// Node identity CLI tests: read-only output of the node host device identity. import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { loadOrCreateDeviceIdentity, publicKeyRawBase64UrlFromPem, } from "../../infra/device-identity.js"; import { defaultRuntime } from "../../runtime.js"; import { runNodeIdentityShow } from "./identity.js"; describe("runNodeIdentityShow", () => { let stateDir: string; let prevStateDir: string | undefined; let logSpy: ReturnType; let errorSpy: ReturnType; let exitSpy: ReturnType; beforeEach(() => { stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-node-identity-")); prevStateDir = process.env.OPENCLAW_STATE_DIR; process.env.OPENCLAW_STATE_DIR = stateDir; logSpy = vi.spyOn(defaultRuntime, "log").mockImplementation(() => {}); errorSpy = vi.spyOn(defaultRuntime, "error").mockImplementation(() => {}); exitSpy = vi.spyOn(defaultRuntime, "exit").mockImplementation(() => {}); }); afterEach(() => { if (prevStateDir === undefined) { delete process.env.OPENCLAW_STATE_DIR; } else { process.env.OPENCLAW_STATE_DIR = prevStateDir; } logSpy.mockRestore(); errorSpy.mockRestore(); exitSpy.mockRestore(); fs.rmSync(stateDir, { recursive: true, force: true }); }); it("fails closed when no identity exists (never mints one)", () => { runNodeIdentityShow({}); expect(errorSpy).toHaveBeenCalledOnce(); expect(exitSpy).toHaveBeenCalledWith(1); expect(fs.existsSync(path.join(stateDir, "identity", "device.json"))).toBe(false); }); it("prints deviceId and raw public key as JSON", () => { const identity = loadOrCreateDeviceIdentity(path.join(stateDir, "identity", "device.json")); runNodeIdentityShow({ json: true }); expect(exitSpy).not.toHaveBeenCalled(); expect(logSpy).toHaveBeenCalledOnce(); const parsed = JSON.parse(String(logSpy.mock.calls[0]?.[0])) as { deviceId: string; publicKey: string; }; expect(parsed).toEqual({ deviceId: identity.deviceId, publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem), }); }); it("prints human-readable lines without --json", () => { const identity = loadOrCreateDeviceIdentity(path.join(stateDir, "identity", "device.json")); runNodeIdentityShow({}); expect(exitSpy).not.toHaveBeenCalled(); const output = logSpy.mock.calls.map((call: unknown[]) => String(call[0])).join("\n"); expect(output).toContain(identity.deviceId); expect(output).toContain(publicKeyRawBase64UrlFromPem(identity.publicKeyPem)); }); });