import { Command } from "commander"; import { formatZonedTimestamp } from "openclaw/plugin-sdk/matrix-runtime-shared"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { registerMatrixCli, resetMatrixCliStateForTests } from "./cli.js"; const bootstrapMatrixVerificationMock = vi.fn(); const acceptMatrixVerificationMock = vi.fn(); const cancelMatrixVerificationMock = vi.fn(); const confirmMatrixVerificationSasMock = vi.fn(); const getMatrixRoomKeyBackupStatusMock = vi.fn(); const getMatrixVerificationSasMock = vi.fn(); const getMatrixVerificationStatusMock = vi.fn(); const listMatrixOwnDevicesMock = vi.fn(); const listMatrixVerificationsMock = vi.fn(); const mismatchMatrixVerificationSasMock = vi.fn(); const pruneMatrixStaleGatewayDevicesMock = vi.fn(); const requestMatrixVerificationMock = vi.fn(); const resolveMatrixAccountConfigMock = vi.fn(); const resolveMatrixAccountMock = vi.fn(); const resolveMatrixAuthContextMock = vi.fn(); const matrixSetupApplyAccountConfigMock = vi.fn(); const matrixSetupValidateInputMock = vi.fn(); const matrixRuntimeLoadConfigMock = vi.fn(); const matrixRuntimeWriteConfigFileMock = vi.fn(); const resetMatrixRoomKeyBackupMock = vi.fn(); const restoreMatrixRoomKeyBackupMock = vi.fn(); const runMatrixSelfVerificationMock = vi.fn(); const setMatrixSdkConsoleLoggingMock = vi.fn(); const setMatrixSdkLogModeMock = vi.fn(); const startMatrixVerificationMock = vi.fn(); const updateMatrixOwnProfileMock = vi.fn(); const verifyMatrixRecoveryKeyMock = vi.fn(); const consoleLogMock = vi.fn(); const consoleErrorMock = vi.fn(); const stdoutWriteMock = vi.fn(); vi.mock("./matrix/actions/verification.js", () => ({ acceptMatrixVerification: (...args: unknown[]) => acceptMatrixVerificationMock(...args), bootstrapMatrixVerification: (...args: unknown[]) => bootstrapMatrixVerificationMock(...args), cancelMatrixVerification: (...args: unknown[]) => cancelMatrixVerificationMock(...args), confirmMatrixVerificationSas: (...args: unknown[]) => confirmMatrixVerificationSasMock(...args), getMatrixRoomKeyBackupStatus: (...args: unknown[]) => getMatrixRoomKeyBackupStatusMock(...args), getMatrixVerificationSas: (...args: unknown[]) => getMatrixVerificationSasMock(...args), getMatrixVerificationStatus: (...args: unknown[]) => getMatrixVerificationStatusMock(...args), listMatrixVerifications: (...args: unknown[]) => listMatrixVerificationsMock(...args), mismatchMatrixVerificationSas: (...args: unknown[]) => mismatchMatrixVerificationSasMock(...args), requestMatrixVerification: (...args: unknown[]) => requestMatrixVerificationMock(...args), resetMatrixRoomKeyBackup: (...args: unknown[]) => resetMatrixRoomKeyBackupMock(...args), restoreMatrixRoomKeyBackup: (...args: unknown[]) => restoreMatrixRoomKeyBackupMock(...args), runMatrixSelfVerification: (...args: unknown[]) => runMatrixSelfVerificationMock(...args), startMatrixVerification: (...args: unknown[]) => startMatrixVerificationMock(...args), verifyMatrixRecoveryKey: (...args: unknown[]) => verifyMatrixRecoveryKeyMock(...args), })); vi.mock("./matrix/actions/devices.js", () => ({ listMatrixOwnDevices: (...args: unknown[]) => listMatrixOwnDevicesMock(...args), pruneMatrixStaleGatewayDevices: (...args: unknown[]) => pruneMatrixStaleGatewayDevicesMock(...args), })); vi.mock("./matrix/client/logging.js", () => ({ setMatrixSdkConsoleLogging: (...args: unknown[]) => setMatrixSdkConsoleLoggingMock(...args), setMatrixSdkLogMode: (...args: unknown[]) => setMatrixSdkLogModeMock(...args), })); vi.mock("./matrix/actions/profile.js", () => ({ updateMatrixOwnProfile: (...args: unknown[]) => updateMatrixOwnProfileMock(...args), })); vi.mock("./matrix/accounts.js", () => ({ resolveMatrixAccount: (...args: unknown[]) => resolveMatrixAccountMock(...args), resolveMatrixAccountConfig: (...args: unknown[]) => resolveMatrixAccountConfigMock(...args), })); vi.mock("./matrix/client.js", () => ({ resolveMatrixAuthContext: (...args: unknown[]) => resolveMatrixAuthContextMock(...args), })); vi.mock("./setup-core.js", () => ({ matrixSetupAdapter: { applyAccountConfig: (...args: unknown[]) => matrixSetupApplyAccountConfigMock(...args), validateInput: (...args: unknown[]) => matrixSetupValidateInputMock(...args), }, })); vi.mock("./runtime.js", () => ({ getMatrixRuntime: () => ({ config: { loadConfig: (...args: unknown[]) => matrixRuntimeLoadConfigMock(...args), writeConfigFile: (...args: unknown[]) => matrixRuntimeWriteConfigFileMock(...args), }, }), })); function buildProgram(): Command { const program = new Command(); registerMatrixCli({ program }); return program; } function formatExpectedLocalTimestamp(value: string): string { return formatZonedTimestamp(new Date(value), { displaySeconds: true }) ?? value; } function mockMatrixVerificationStatus(params: { recoveryKeyCreatedAt: string | null; verifiedAt?: string; }) { getMatrixVerificationStatusMock.mockResolvedValue({ encryptionEnabled: true, verified: true, localVerified: true, crossSigningVerified: true, signedByOwner: true, userId: "@bot:example.org", deviceId: "DEVICE123", backupVersion: "1", backup: { serverVersion: "1", activeVersion: "1", trusted: true, matchesDecryptionKey: true, decryptionKeyCached: true, }, recoveryKeyStored: true, recoveryKeyCreatedAt: params.recoveryKeyCreatedAt, pendingVerifications: 0, verifiedAt: params.verifiedAt, }); } function mockMatrixVerificationSummary(overrides: Record = {}) { return { id: "self-1", transactionId: "txn-1", otherUserId: "@bot:example.org", otherDeviceId: "PHONE123", isSelfVerification: true, initiatedByMe: true, phaseName: "started", pending: true, methods: ["m.sas.v1"], chosenMethod: "m.sas.v1", hasSas: true, sas: { decimal: [1234, 5678, 9012], }, completed: false, ...overrides, }; } describe("matrix CLI verification commands", () => { beforeEach(() => { resetMatrixCliStateForTests(); vi.clearAllMocks(); process.exitCode = undefined; vi.spyOn(console, "log").mockImplementation((...args: unknown[]) => consoleLogMock(...args)); vi.spyOn(console, "error").mockImplementation((...args: unknown[]) => consoleErrorMock(...args), ); vi.spyOn(process.stdout, "write").mockImplementation(((chunk: string | Uint8Array) => { stdoutWriteMock(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8")); return true; }) as typeof process.stdout.write); consoleLogMock.mockReset(); consoleErrorMock.mockReset(); stdoutWriteMock.mockReset(); matrixSetupValidateInputMock.mockReturnValue(null); matrixSetupApplyAccountConfigMock.mockImplementation(({ cfg }: { cfg: unknown }) => cfg); matrixRuntimeLoadConfigMock.mockReturnValue({}); matrixRuntimeWriteConfigFileMock.mockResolvedValue(undefined); resolveMatrixAuthContextMock.mockImplementation( ({ cfg, accountId }: { cfg: unknown; accountId?: string | null }) => ({ cfg, env: process.env, accountId: accountId ?? "default", resolved: {}, }), ); resolveMatrixAccountMock.mockReturnValue({ configured: false, }); resolveMatrixAccountConfigMock.mockReturnValue({ encryption: false, }); bootstrapMatrixVerificationMock.mockResolvedValue({ success: true, verification: { recoveryKeyCreatedAt: null, backupVersion: null, }, crossSigning: {}, pendingVerifications: 0, cryptoBootstrap: {}, }); resetMatrixRoomKeyBackupMock.mockResolvedValue({ success: true, previousVersion: "1", deletedVersion: "1", createdVersion: "2", backup: { serverVersion: "2", activeVersion: "2", trusted: true, matchesDecryptionKey: true, decryptionKeyCached: true, keyLoadAttempted: false, keyLoadError: null, }, }); updateMatrixOwnProfileMock.mockResolvedValue({ skipped: false, displayNameUpdated: true, avatarUpdated: false, resolvedAvatarUrl: null, convertedAvatarFromHttp: false, }); listMatrixOwnDevicesMock.mockResolvedValue([]); pruneMatrixStaleGatewayDevicesMock.mockResolvedValue({ before: [], staleGatewayDeviceIds: [], currentDeviceId: null, deletedDeviceIds: [], remainingDevices: [], }); }); afterEach(() => { vi.restoreAllMocks(); process.exitCode = undefined; }); it("sets non-zero exit code for device verification failures in JSON mode", async () => { verifyMatrixRecoveryKeyMock.mockResolvedValue({ success: false, error: "invalid key", }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "device", "bad-key", "--json"], { from: "user", }); expect(process.exitCode).toBe(1); }); it("prints recovery-key and identity-trust diagnostics for device verification failures", async () => { verifyMatrixRecoveryKeyMock.mockResolvedValue({ success: false, error: "Matrix recovery key was applied, but this device still lacks full Matrix identity trust.", encryptionEnabled: true, userId: "@bot:example.org", deviceId: "DEVICE123", backupVersion: "7", backup: { serverVersion: "7", activeVersion: "7", trusted: true, matchesDecryptionKey: true, decryptionKeyCached: true, keyLoadAttempted: true, keyLoadError: null, }, verified: false, localVerified: true, crossSigningVerified: false, signedByOwner: false, recoveryKeyAccepted: true, backupUsable: true, deviceOwnerVerified: false, recoveryKeyStored: true, recoveryKeyCreatedAt: "2026-02-25T20:10:11.000Z", }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "device", "valid-key"], { from: "user", }); expect(process.exitCode).toBe(1); expect(consoleErrorMock).toHaveBeenCalledWith( "Verification failed: Matrix recovery key was applied, but this device still lacks full Matrix identity trust.", ); expect(consoleLogMock).toHaveBeenCalledWith("Recovery key accepted: yes"); expect(consoleLogMock).toHaveBeenCalledWith("Backup usable: yes"); expect(consoleLogMock).toHaveBeenCalledWith("Device verified by owner: no"); expect(consoleLogMock).toHaveBeenCalledWith("Backup: active and trusted on this device"); expect(consoleLogMock).toHaveBeenCalledWith( "- Recovery key can unlock the room-key backup, but full Matrix identity trust is still incomplete. Run openclaw matrix verify self and follow the prompts from another Matrix client.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- If you intend to replace the current cross-signing identity, run openclaw matrix verify bootstrap --recovery-key '' --force-reset-cross-signing.", ); }); it("runs interactive Matrix self-verification in one CLI flow", async () => { runMatrixSelfVerificationMock.mockResolvedValue( mockMatrixVerificationSummary({ completed: true, deviceOwnerVerified: true, ownerVerification: { backup: { activeVersion: "1", decryptionKeyCached: true, keyLoadAttempted: false, keyLoadError: null, matchesDecryptionKey: true, serverVersion: "1", trusted: true, }, backupVersion: "1", crossSigningVerified: true, deviceId: "DEVICE123", localVerified: true, recoveryKeyCreatedAt: null, recoveryKeyId: null, recoveryKeyStored: true, signedByOwner: true, userId: "@bot:example.org", verified: true, }, pending: false, phaseName: "done", }), ); const program = buildProgram(); await program.parseAsync( ["matrix", "verify", "self", "--account", "ops", "--timeout-ms", "5000"], { from: "user", }, ); expect(runMatrixSelfVerificationMock).toHaveBeenCalledWith({ accountId: "ops", cfg: {}, timeoutMs: 5000, onRequested: expect.any(Function), onReady: expect.any(Function), onSas: expect.any(Function), confirmSas: expect.any(Function), }); expect(consoleLogMock).toHaveBeenCalledWith("Self-verification complete."); expect(consoleLogMock).toHaveBeenCalledWith("Device verified by owner: yes"); expect(consoleLogMock).toHaveBeenCalledWith("Cross-signing verified: yes"); expect(consoleLogMock).toHaveBeenCalledWith("Signed by owner: yes"); expect(consoleLogMock).toHaveBeenCalledWith("Backup: active and trusted on this device"); }); it("requests Matrix self-verification and prints the follow-up SAS commands", async () => { requestMatrixVerificationMock.mockResolvedValue( mockMatrixVerificationSummary({ id: "self-verify-1", hasSas: false, sas: undefined, }), ); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "request", "--own-user", "--account", "ops"], { from: "user", }); expect(requestMatrixVerificationMock).toHaveBeenCalledWith({ accountId: "ops", cfg: {}, ownUser: true, userId: undefined, deviceId: undefined, roomId: undefined, }); expect(consoleLogMock).toHaveBeenCalledWith("Verification id: self-verify-1"); expect(consoleLogMock).toHaveBeenCalledWith("Transaction id: txn-1"); expect(consoleLogMock).toHaveBeenCalledWith( "- Accept the verification request in another Matrix client for this account.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- Then run openclaw matrix verify start --account ops -- txn-1 to start SAS verification.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- Run openclaw matrix verify sas --account ops -- txn-1 to display the SAS emoji or decimals.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- When the SAS matches, run openclaw matrix verify confirm-sas --account ops -- txn-1.", ); }); it("prints DM lookup details in Matrix verification follow-up commands", async () => { requestMatrixVerificationMock.mockResolvedValue( mockMatrixVerificationSummary({ id: "dm-verify-1", transactionId: "txn-dm", roomId: "!room-'$(x):example.org", otherUserId: "@alice:example.org", isSelfVerification: false, hasSas: false, sas: undefined, }), ); const program = buildProgram(); await program.parseAsync( [ "matrix", "verify", "request", "--user-id", "@alice:example.org", "--room-id", "!room-'$(x):example.org", ], { from: "user" }, ); expect(requestMatrixVerificationMock).toHaveBeenCalledWith({ accountId: "default", cfg: {}, ownUser: undefined, userId: "@alice:example.org", deviceId: undefined, roomId: "!room-'$(x):example.org", }); expect(consoleLogMock).toHaveBeenCalledWith("Room id: !room-'$(x):example.org"); expect(consoleLogMock).toHaveBeenCalledWith( "- Then run openclaw matrix verify start --user-id @alice:example.org --room-id '!room-'\\''$(x):example.org' -- txn-dm to start SAS verification.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- Run openclaw matrix verify sas --user-id @alice:example.org --room-id '!room-'\\''$(x):example.org' -- txn-dm to display the SAS emoji or decimals.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- When the SAS matches, run openclaw matrix verify confirm-sas --user-id @alice:example.org --room-id '!room-'\\''$(x):example.org' -- txn-dm.", ); }); it("terminates options before remote Matrix verification ids in follow-up commands", async () => { requestMatrixVerificationMock.mockResolvedValue( mockMatrixVerificationSummary({ id: "local-id", transactionId: "--account=evil", hasSas: false, sas: undefined, }), ); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "request", "--own-user", "--account", "ops"], { from: "user", }); expect(consoleLogMock).toHaveBeenCalledWith( "- Then run openclaw matrix verify start --account ops -- --account=evil to start SAS verification.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- Run openclaw matrix verify sas --account ops -- --account=evil to display the SAS emoji or decimals.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- When the SAS matches, run openclaw matrix verify confirm-sas --account ops -- --account=evil.", ); }); it("rejects ambiguous Matrix verification request targets", async () => { const program = buildProgram(); await program.parseAsync( ["matrix", "verify", "request", "--own-user", "--user-id", "@other:example.org"], { from: "user" }, ); expect(process.exitCode).toBe(1); expect(requestMatrixVerificationMock).not.toHaveBeenCalled(); expect(consoleErrorMock).toHaveBeenCalledWith( "Verification request failed: --own-user cannot be combined with --user-id, --device-id, or --room-id", ); }); it("lists Matrix verification requests", async () => { listMatrixVerificationsMock.mockResolvedValue([ mockMatrixVerificationSummary({ id: "incoming-1", initiatedByMe: false }), ]); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "list"], { from: "user" }); expect(listMatrixVerificationsMock).toHaveBeenCalledWith({ accountId: "default", cfg: {} }); expect(consoleLogMock).toHaveBeenCalledWith("Verification id: incoming-1"); expect(consoleLogMock).toHaveBeenCalledWith("Initiated by OpenClaw: no"); }); it("sanitizes remote Matrix verification metadata before printing it", async () => { listMatrixVerificationsMock.mockResolvedValue([ mockMatrixVerificationSummary({ id: "self-\u001B[31m1", transactionId: "txn-\n\u009B31m1", otherUserId: "@bot\u001B[2J\u009Dspoof\u0007:example.org", otherDeviceId: "PHONE\r\u009B2J123", phaseName: "started\u001B[0m", methods: ["m.sas.v1\n\u009B31mspoof"], chosenMethod: "m.sas.v1\u001B[1m", sas: { emoji: [ ["🐶", "Dog\u001B[31m\u009B2J"], ["🐱", "Cat\n\u009B31mspoof"], ], }, error: "Remote\u001B[31m cancelled\n\u009B31mforged", }), ]); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "list"], { from: "user" }); expect(consoleLogMock).toHaveBeenCalledWith("Verification id: self-1"); expect(consoleLogMock).toHaveBeenCalledWith("Transaction id: txn-1"); expect(consoleLogMock).toHaveBeenCalledWith("Other user: @bot:example.org"); expect(consoleLogMock).toHaveBeenCalledWith("Other device: PHONE123"); expect(consoleLogMock).toHaveBeenCalledWith("Phase: started"); expect(consoleLogMock).toHaveBeenCalledWith("Methods: m.sas.v1spoof"); expect(consoleLogMock).toHaveBeenCalledWith("Chosen method: m.sas.v1"); expect(consoleLogMock).toHaveBeenCalledWith("SAS emoji: 🐶 Dog | 🐱 Catspoof"); expect(consoleLogMock).toHaveBeenCalledWith("Verification error: Remote cancelledforged"); }); it("sanitizes remote Matrix status metadata before printing diagnostics", async () => { getMatrixVerificationStatusMock.mockResolvedValue({ encryptionEnabled: true, verified: false, localVerified: false, crossSigningVerified: false, signedByOwner: false, userId: "@bot\u001B[2J:example.org", deviceId: "PHONE\r\u009B2J123", backupVersion: "1\u001B[31m", backup: { serverVersion: "2\u001B[31m", activeVersion: "1\u009B2J", trusted: false, matchesDecryptionKey: false, decryptionKeyCached: false, keyLoadAttempted: true, keyLoadError: "Remote\n\u009B31mforged", }, recoveryKeyStored: false, recoveryKeyCreatedAt: null, pendingVerifications: 0, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "status", "--verbose"], { from: "user" }); expect(consoleLogMock).toHaveBeenCalledWith("User: @bot:example.org"); expect(consoleLogMock).toHaveBeenCalledWith("Device: PHONE123"); expect(consoleLogMock).toHaveBeenCalledWith("Backup server version: 2"); expect(consoleLogMock).toHaveBeenCalledWith("Backup active on this device: 1"); expect(consoleLogMock).toHaveBeenCalledWith("Backup key load error: Remoteforged"); }); it("shell-quotes Matrix verification ids in follow-up command guidance", async () => { requestMatrixVerificationMock.mockResolvedValue( mockMatrixVerificationSummary({ id: "self-verify-1", transactionId: "txn-'$(touch /tmp/pwn)", }), ); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "request", "--own-user"], { from: "user", }); expect(consoleLogMock).toHaveBeenCalledWith( "- Then run openclaw matrix verify start -- 'txn-'\\''$(touch /tmp/pwn)' to start SAS verification.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- Run openclaw matrix verify sas -- 'txn-'\\''$(touch /tmp/pwn)' to display the SAS emoji or decimals.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- When the SAS matches, run openclaw matrix verify confirm-sas -- 'txn-'\\''$(touch /tmp/pwn)'.", ); }); it("shows Matrix SAS diagnostics and confirm/mismatch guidance", async () => { getMatrixVerificationSasMock.mockResolvedValue({ decimal: [1234, 5678, 9012], }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "sas", "self-1"], { from: "user" }); expect(getMatrixVerificationSasMock).toHaveBeenCalledWith("self-1", { accountId: "default", cfg: {}, }); expect(consoleLogMock).toHaveBeenCalledWith("SAS decimals: 1234 5678 9012"); expect(consoleLogMock).toHaveBeenCalledWith( "- If they match, run openclaw matrix verify confirm-sas -- self-1.", ); expect(consoleLogMock).toHaveBeenCalledWith( "- If they do not match, run openclaw matrix verify mismatch-sas -- self-1.", ); }); it("passes DM lookup details through Matrix verification follow-up commands", async () => { startMatrixVerificationMock.mockResolvedValue( mockMatrixVerificationSummary({ id: "dm-verify-1", transactionId: "txn-dm", roomId: "!dm:example.org", otherUserId: "@alice:example.org", }), ); const program = buildProgram(); await program.parseAsync( [ "matrix", "verify", "start", "txn-dm", "--user-id", "@alice:example.org", "--room-id", "!dm:example.org", "--account", "ops", ], { from: "user" }, ); expect(startMatrixVerificationMock).toHaveBeenCalledWith("txn-dm", { accountId: "ops", cfg: {}, method: "sas", verificationDmUserId: "@alice:example.org", verificationDmRoomId: "!dm:example.org", }); expect(consoleLogMock).toHaveBeenCalledWith( "- If they match, run openclaw matrix verify confirm-sas --user-id @alice:example.org --room-id '!dm:example.org' --account ops -- txn-dm.", ); }); it("prints stable transaction ids in follow-up commands after accepting verification", async () => { acceptMatrixVerificationMock.mockResolvedValue( mockMatrixVerificationSummary({ id: "verification-1", transactionId: "txn-stable", }), ); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "accept", "verification-1"], { from: "user" }); expect(consoleLogMock).toHaveBeenCalledWith( "- Run openclaw matrix verify start -- txn-stable to start SAS verification.", ); }); it("confirms, rejects, accepts, starts, and cancels Matrix verification requests", async () => { acceptMatrixVerificationMock.mockResolvedValue(mockMatrixVerificationSummary({ id: "in-1" })); startMatrixVerificationMock.mockResolvedValue(mockMatrixVerificationSummary({ id: "in-1" })); confirmMatrixVerificationSasMock.mockResolvedValue( mockMatrixVerificationSummary({ id: "in-1", completed: true, pending: false }), ); mismatchMatrixVerificationSasMock.mockResolvedValue( mockMatrixVerificationSummary({ id: "in-1", phaseName: "cancelled", pending: false }), ); cancelMatrixVerificationMock.mockResolvedValue( mockMatrixVerificationSummary({ id: "in-1", phaseName: "cancelled", pending: false }), ); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "accept", "in-1"], { from: "user" }); await program.parseAsync(["matrix", "verify", "start", "in-1"], { from: "user" }); await program.parseAsync(["matrix", "verify", "confirm-sas", "in-1"], { from: "user" }); await program.parseAsync(["matrix", "verify", "mismatch-sas", "in-1"], { from: "user" }); await program.parseAsync( ["matrix", "verify", "cancel", "in-1", "--reason", "changed my mind"], { from: "user" }, ); expect(acceptMatrixVerificationMock).toHaveBeenCalledWith("in-1", { accountId: "default", cfg: {}, }); expect(startMatrixVerificationMock).toHaveBeenCalledWith("in-1", { accountId: "default", cfg: {}, method: "sas", }); expect(confirmMatrixVerificationSasMock).toHaveBeenCalledWith("in-1", { accountId: "default", cfg: {}, }); expect(mismatchMatrixVerificationSasMock).toHaveBeenCalledWith("in-1", { accountId: "default", cfg: {}, }); expect(cancelMatrixVerificationMock).toHaveBeenCalledWith("in-1", { accountId: "default", cfg: {}, reason: "changed my mind", code: undefined, }); }); it("sets non-zero exit code for bootstrap failures in JSON mode", async () => { bootstrapMatrixVerificationMock.mockResolvedValue({ success: false, error: "bootstrap failed", verification: {}, crossSigning: {}, pendingVerifications: 0, cryptoBootstrap: null, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "bootstrap", "--json"], { from: "user", }); expect(process.exitCode).toBe(1); }); it("sets non-zero exit code for backup restore failures in JSON mode", async () => { restoreMatrixRoomKeyBackupMock.mockResolvedValue({ success: false, error: "missing backup key", backupVersion: null, imported: 0, total: 0, loadedFromSecretStorage: false, backup: { serverVersion: "1", activeVersion: null, trusted: true, matchesDecryptionKey: false, decryptionKeyCached: false, }, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "backup", "restore", "--json"], { from: "user", }); expect(process.exitCode).toBe(1); }); it("sets non-zero exit code for backup reset failures in JSON mode", async () => { resetMatrixRoomKeyBackupMock.mockResolvedValue({ success: false, error: "reset failed", previousVersion: "1", deletedVersion: "1", createdVersion: null, backup: { serverVersion: null, activeVersion: null, trusted: null, matchesDecryptionKey: null, decryptionKeyCached: null, keyLoadAttempted: false, keyLoadError: null, }, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "backup", "reset", "--yes", "--json"], { from: "user", }); expect(process.exitCode).toBe(1); }); it("passes loaded cfg to verify status action", async () => { const fakeCfg = { channels: { matrix: {} } }; matrixRuntimeLoadConfigMock.mockReturnValue(fakeCfg); mockMatrixVerificationStatus({ recoveryKeyCreatedAt: null }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "status"], { from: "user" }); expect(getMatrixVerificationStatusMock).toHaveBeenCalledWith( expect.objectContaining({ cfg: fakeCfg }), ); }); it("passes loaded cfg to all verify subcommands", async () => { const fakeCfg = { channels: { matrix: {} } }; matrixRuntimeLoadConfigMock.mockReturnValue(fakeCfg); // verify bootstrap const program1 = buildProgram(); await program1.parseAsync(["matrix", "verify", "bootstrap"], { from: "user", }); expect(bootstrapMatrixVerificationMock).toHaveBeenCalledWith( expect.objectContaining({ cfg: fakeCfg }), ); // verify device verifyMatrixRecoveryKeyMock.mockResolvedValue({ success: true }); const program2 = buildProgram(); await program2.parseAsync(["matrix", "verify", "device", "test-key"], { from: "user", }); expect(verifyMatrixRecoveryKeyMock).toHaveBeenCalledWith( "test-key", expect.objectContaining({ cfg: fakeCfg }), ); // verify backup status getMatrixRoomKeyBackupStatusMock.mockResolvedValue({}); const program3 = buildProgram(); await program3.parseAsync(["matrix", "verify", "backup", "status"], { from: "user", }); expect(getMatrixRoomKeyBackupStatusMock).toHaveBeenCalledWith( expect.objectContaining({ cfg: fakeCfg }), ); // verify backup reset const program4 = buildProgram(); await program4.parseAsync(["matrix", "verify", "backup", "reset", "--yes"], { from: "user" }); expect(resetMatrixRoomKeyBackupMock).toHaveBeenCalledWith( expect.objectContaining({ cfg: fakeCfg }), ); // verify backup restore restoreMatrixRoomKeyBackupMock.mockResolvedValue({ success: true, imported: 0, total: 0, backup: {}, }); const program5 = buildProgram(); await program5.parseAsync(["matrix", "verify", "backup", "restore"], { from: "user", }); expect(restoreMatrixRoomKeyBackupMock).toHaveBeenCalledWith( expect.objectContaining({ cfg: fakeCfg }), ); }); it("lists matrix devices", async () => { listMatrixOwnDevicesMock.mockResolvedValue([ { deviceId: "A7hWr\u001B[31mQ70ea", displayName: "OpenClaw\u001B[2J Gateway", lastSeenIp: "127.0.0.1\u009B2J", lastSeenTs: 1_741_507_200_000, current: true, }, { deviceId: "BritdXC6iL", displayName: "OpenClaw Gateway", lastSeenIp: null, lastSeenTs: null, current: false, }, ]); const program = buildProgram(); await program.parseAsync(["matrix", "devices", "list", "--account", "poe"], { from: "user" }); expect(listMatrixOwnDevicesMock).toHaveBeenCalledWith({ accountId: "poe", cfg: {} }); expect(console.log).toHaveBeenCalledWith("Account: poe"); expect(console.log).toHaveBeenCalledWith("- A7hWrQ70ea (current, OpenClaw Gateway)"); expect(console.log).toHaveBeenCalledWith(" Last IP: 127.0.0.1"); expect(console.log).toHaveBeenCalledWith("- BritdXC6iL (OpenClaw Gateway)"); }); it("prunes stale matrix gateway devices", async () => { pruneMatrixStaleGatewayDevicesMock.mockResolvedValue({ before: [ { deviceId: "A7hWrQ70ea", displayName: "OpenClaw Gateway", lastSeenIp: "127.0.0.1", lastSeenTs: 1_741_507_200_000, current: true, }, { deviceId: "BritdXC6iL", displayName: "OpenClaw Gateway", lastSeenIp: null, lastSeenTs: null, current: false, }, ], staleGatewayDeviceIds: ["BritdXC6iL"], currentDeviceId: "A7hWrQ70ea", deletedDeviceIds: ["BritdXC6iL"], remainingDevices: [ { deviceId: "A7hWrQ70ea", displayName: "OpenClaw Gateway", lastSeenIp: "127.0.0.1", lastSeenTs: 1_741_507_200_000, current: true, }, ], }); const program = buildProgram(); await program.parseAsync(["matrix", "devices", "prune-stale", "--account", "poe"], { from: "user", }); expect(pruneMatrixStaleGatewayDevicesMock).toHaveBeenCalledWith({ accountId: "poe", cfg: {}, }); expect(console.log).toHaveBeenCalledWith("Deleted stale OpenClaw devices: BritdXC6iL"); expect(console.log).toHaveBeenCalledWith("Current device: A7hWrQ70ea"); expect(console.log).toHaveBeenCalledWith("Remaining devices: 1"); }); it("adds a matrix account and prints a binding hint", async () => { matrixRuntimeLoadConfigMock.mockReturnValue({ channels: {} }); matrixSetupApplyAccountConfigMock.mockImplementation( ({ cfg, accountId }: { cfg: Record; accountId: string }) => ({ ...cfg, channels: { ...(cfg.channels as Record | undefined), matrix: { accounts: { [accountId]: { homeserver: "https://matrix.example.org", }, }, }, }, }), ); const program = buildProgram(); await program.parseAsync( [ "matrix", "account", "add", "--account", "Ops", "--homeserver", "https://matrix.example.org", "--user-id", "@ops:example.org", "--password", "secret", ], { from: "user" }, ); expect(matrixSetupValidateInputMock).toHaveBeenCalledWith( expect.objectContaining({ accountId: "ops", input: expect.objectContaining({ homeserver: "https://matrix.example.org", userId: "@ops:example.org", password: "secret", // pragma: allowlist secret }), }), ); expect(matrixRuntimeWriteConfigFileMock).toHaveBeenCalledWith( expect.objectContaining({ channels: { matrix: { accounts: { ops: expect.objectContaining({ homeserver: "https://matrix.example.org", }), }, }, }, }), ); expect(console.log).toHaveBeenCalledWith("Saved matrix account: ops"); expect(console.log).toHaveBeenCalledWith( "Bind this account to an agent: openclaw agents bind --agent --bind matrix:ops", ); }); it("bootstraps verification for newly added encrypted accounts", async () => { resolveMatrixAccountConfigMock.mockReturnValue({ encryption: true, }); listMatrixOwnDevicesMock.mockResolvedValue([ { deviceId: "BritdXC6iL", displayName: "OpenClaw Gateway", lastSeenIp: null, lastSeenTs: null, current: false, }, { deviceId: "du314Zpw3A", displayName: "OpenClaw Gateway", lastSeenIp: null, lastSeenTs: null, current: true, }, ]); bootstrapMatrixVerificationMock.mockResolvedValue({ success: true, verification: { recoveryKeyCreatedAt: "2026-03-09T06:00:00.000Z", backupVersion: "7", }, crossSigning: {}, pendingVerifications: 0, cryptoBootstrap: {}, }); const program = buildProgram(); await program.parseAsync( [ "matrix", "account", "add", "--account", "ops", "--homeserver", "https://matrix.example.org", "--user-id", "@ops:example.org", "--password", "secret", ], { from: "user" }, ); expect(bootstrapMatrixVerificationMock).toHaveBeenCalledWith({ accountId: "ops", }); expect(console.log).toHaveBeenCalledWith("Matrix verification bootstrap: complete"); expect(console.log).toHaveBeenCalledWith( `Recovery key created at: ${formatExpectedLocalTimestamp("2026-03-09T06:00:00.000Z")}`, ); expect(console.log).toHaveBeenCalledWith("Backup version: 7"); expect(console.log).toHaveBeenCalledWith( "Matrix device hygiene warning: stale OpenClaw devices detected (BritdXC6iL). Run openclaw matrix devices prune-stale --account ops.", ); }); it("does not bootstrap verification when updating an already configured account", async () => { matrixRuntimeLoadConfigMock.mockReturnValue({ channels: { matrix: { accounts: { ops: { enabled: true, homeserver: "https://matrix.example.org", }, }, }, }, }); resolveMatrixAccountConfigMock.mockReturnValue({ encryption: true, }); const program = buildProgram(); await program.parseAsync( [ "matrix", "account", "add", "--account", "ops", "--homeserver", "https://matrix.example.org", "--user-id", "@ops:example.org", "--password", "secret", ], { from: "user" }, ); expect(bootstrapMatrixVerificationMock).not.toHaveBeenCalled(); }); it("warns instead of failing when device-health probing fails after saving the account", async () => { listMatrixOwnDevicesMock.mockRejectedValue(new Error("homeserver unavailable")); const program = buildProgram(); await program.parseAsync( [ "matrix", "account", "add", "--account", "ops", "--homeserver", "https://matrix.example.org", "--user-id", "@ops:example.org", "--password", "secret", ], { from: "user" }, ); expect(matrixRuntimeWriteConfigFileMock).toHaveBeenCalled(); expect(process.exitCode).toBeUndefined(); expect(console.log).toHaveBeenCalledWith("Saved matrix account: ops"); expect(console.error).toHaveBeenCalledWith( "Matrix device health warning: homeserver unavailable", ); }); it("returns device-health warnings in JSON mode without failing the account add command", async () => { listMatrixOwnDevicesMock.mockRejectedValue(new Error("homeserver unavailable")); const program = buildProgram(); await program.parseAsync( [ "matrix", "account", "add", "--account", "ops", "--homeserver", "https://matrix.example.org", "--user-id", "@ops:example.org", "--password", "secret", "--json", ], { from: "user" }, ); expect(matrixRuntimeWriteConfigFileMock).toHaveBeenCalled(); expect(process.exitCode).toBeUndefined(); const jsonOutput = stdoutWriteMock.mock.calls.at(-1)?.[0]; expect(typeof jsonOutput).toBe("string"); expect(JSON.parse(String(jsonOutput))).toEqual( expect.objectContaining({ accountId: "ops", deviceHealth: expect.objectContaining({ currentDeviceId: null, staleOpenClawDeviceIds: [], error: "homeserver unavailable", }), }), ); }); it("uses --name as fallback account id and prints account-scoped config path", async () => { matrixRuntimeLoadConfigMock.mockReturnValue({ channels: {} }); const program = buildProgram(); await program.parseAsync( [ "matrix", "account", "add", "--name", "Main Bot", "--homeserver", "https://matrix.example.org", "--user-id", "@main:example.org", "--password", "secret", ], { from: "user" }, ); expect(matrixSetupValidateInputMock).toHaveBeenCalledWith( expect.objectContaining({ accountId: "main-bot", }), ); expect(console.log).toHaveBeenCalledWith("Saved matrix account: main-bot"); expect(console.log).toHaveBeenCalledWith("Config path: channels.matrix.accounts.main-bot"); expect(updateMatrixOwnProfileMock).toHaveBeenCalledWith( expect.objectContaining({ accountId: "main-bot", displayName: "Main Bot", }), ); expect(console.log).toHaveBeenCalledWith( "Bind this account to an agent: openclaw agents bind --agent --bind matrix:main-bot", ); }); it("forwards --avatar-url through account add setup and profile sync", async () => { matrixRuntimeLoadConfigMock.mockReturnValue({ channels: {} }); const program = buildProgram(); await program.parseAsync( [ "matrix", "account", "add", "--name", "Ops Bot", "--homeserver", "https://matrix.example.org", "--access-token", "ops-token", "--avatar-url", "mxc://example/ops-avatar", ], { from: "user" }, ); expect(matrixSetupApplyAccountConfigMock).toHaveBeenCalledWith( expect.objectContaining({ accountId: "ops-bot", input: expect.objectContaining({ name: "Ops Bot", homeserver: "https://matrix.example.org", accessToken: "ops-token", avatarUrl: "mxc://example/ops-avatar", }), }), ); expect(updateMatrixOwnProfileMock).toHaveBeenCalledWith( expect.objectContaining({ accountId: "ops-bot", displayName: "Ops Bot", avatarUrl: "mxc://example/ops-avatar", }), ); expect(console.log).toHaveBeenCalledWith("Saved matrix account: ops-bot"); expect(console.log).toHaveBeenCalledWith("Config path: channels.matrix.accounts.ops-bot"); }); it("sets profile name and avatar via profile set command", async () => { const program = buildProgram(); await program.parseAsync( [ "matrix", "profile", "set", "--account", "alerts", "--name", "Alerts Bot", "--avatar-url", "mxc://example/avatar", ], { from: "user" }, ); expect(updateMatrixOwnProfileMock).toHaveBeenCalledWith( expect.objectContaining({ accountId: "alerts", displayName: "Alerts Bot", avatarUrl: "mxc://example/avatar", }), ); expect(matrixRuntimeWriteConfigFileMock).toHaveBeenCalled(); expect(console.log).toHaveBeenCalledWith("Account: alerts"); expect(console.log).toHaveBeenCalledWith("Config path: channels.matrix.accounts.alerts"); }); it("returns JSON errors for invalid account setup input", async () => { matrixSetupValidateInputMock.mockReturnValue("Matrix requires --homeserver"); const program = buildProgram(); await program.parseAsync(["matrix", "account", "add", "--json"], { from: "user", }); expect(process.exitCode).toBe(1); expect(stdoutWriteMock).toHaveBeenCalledWith( expect.stringContaining('"error": "Matrix requires --homeserver"'), ); }); it("keeps zero exit code for successful bootstrap in JSON mode", async () => { process.exitCode = 0; bootstrapMatrixVerificationMock.mockResolvedValue({ success: true, verification: {}, crossSigning: {}, pendingVerifications: 0, cryptoBootstrap: {}, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "bootstrap", "--json"], { from: "user", }); expect(process.exitCode).toBe(0); }); it("prints local timezone timestamps for verify status output in verbose mode", async () => { const recoveryCreatedAt = "2026-02-25T20:10:11.000Z"; mockMatrixVerificationStatus({ recoveryKeyCreatedAt: recoveryCreatedAt }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "status", "--verbose"], { from: "user", }); expect(console.log).toHaveBeenCalledWith( `Recovery key created at: ${formatExpectedLocalTimestamp(recoveryCreatedAt)}`, ); expect(console.log).toHaveBeenCalledWith("Diagnostics:"); expect(console.log).toHaveBeenCalledWith("Locally trusted: yes"); expect(console.log).toHaveBeenCalledWith("Signed by owner: yes"); expect(setMatrixSdkLogModeMock).toHaveBeenCalledWith("default"); }); it("prints local timezone timestamps for verify bootstrap and device output in verbose mode", async () => { const recoveryCreatedAt = "2026-02-25T20:10:11.000Z"; const verifiedAt = "2026-02-25T20:14:00.000Z"; bootstrapMatrixVerificationMock.mockResolvedValue({ success: true, verification: { encryptionEnabled: true, verified: true, userId: "@bot:example.org", deviceId: "DEVICE123", backupVersion: "1", backup: { serverVersion: "1", activeVersion: "1", trusted: true, matchesDecryptionKey: true, decryptionKeyCached: true, }, recoveryKeyStored: true, recoveryKeyId: "SSSS", recoveryKeyCreatedAt: recoveryCreatedAt, localVerified: true, crossSigningVerified: true, signedByOwner: true, }, crossSigning: { published: true, masterKeyPublished: true, selfSigningKeyPublished: true, userSigningKeyPublished: true, }, pendingVerifications: 0, cryptoBootstrap: {}, }); verifyMatrixRecoveryKeyMock.mockResolvedValue({ success: true, encryptionEnabled: true, userId: "@bot:example.org", deviceId: "DEVICE123", backupVersion: "1", backup: { serverVersion: "1", activeVersion: "1", trusted: true, matchesDecryptionKey: true, decryptionKeyCached: true, }, verified: true, localVerified: true, crossSigningVerified: true, signedByOwner: true, recoveryKeyStored: true, recoveryKeyId: "SSSS", recoveryKeyCreatedAt: recoveryCreatedAt, verifiedAt, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "bootstrap", "--verbose"], { from: "user", }); await program.parseAsync(["matrix", "verify", "device", "valid-key", "--verbose"], { from: "user", }); expect(console.log).toHaveBeenCalledWith( `Recovery key created at: ${formatExpectedLocalTimestamp(recoveryCreatedAt)}`, ); expect(console.log).toHaveBeenCalledWith( `Verified at: ${formatExpectedLocalTimestamp(verifiedAt)}`, ); }); it("keeps default output concise when verbose is not provided", async () => { const recoveryCreatedAt = "2026-02-25T20:10:11.000Z"; mockMatrixVerificationStatus({ recoveryKeyCreatedAt: recoveryCreatedAt }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "status"], { from: "user" }); expect(console.log).not.toHaveBeenCalledWith( `Recovery key created at: ${formatExpectedLocalTimestamp(recoveryCreatedAt)}`, ); expect(console.log).not.toHaveBeenCalledWith("Pending verifications: 0"); expect(console.log).not.toHaveBeenCalledWith("Diagnostics:"); expect(console.log).toHaveBeenCalledWith("Backup: active and trusted on this device"); expect(setMatrixSdkLogModeMock).toHaveBeenCalledWith("quiet"); }); it("shows explicit backup issue in default status output", async () => { getMatrixVerificationStatusMock.mockResolvedValue({ encryptionEnabled: true, verified: true, localVerified: true, crossSigningVerified: true, signedByOwner: true, userId: "@bot:example.org", deviceId: "DEVICE123", backupVersion: "5256", backup: { serverVersion: "5256", activeVersion: null, trusted: true, matchesDecryptionKey: false, decryptionKeyCached: false, keyLoadAttempted: true, keyLoadError: null, }, recoveryKeyStored: true, recoveryKeyCreatedAt: "2026-02-25T20:10:11.000Z", pendingVerifications: 0, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "status"], { from: "user" }); expect(console.log).toHaveBeenCalledWith( "Backup issue: backup decryption key is not loaded on this device (secret storage did not return a key)", ); expect(console.log).toHaveBeenCalledWith( "- Backup key is not loaded on this device. Run openclaw matrix verify backup restore to load it and restore old room keys.", ); expect(console.log).not.toHaveBeenCalledWith( "- Backup is present but not trusted for this device. Re-run 'openclaw matrix verify device '.", ); }); it("includes key load failure details in status output", async () => { getMatrixVerificationStatusMock.mockResolvedValue({ encryptionEnabled: true, verified: true, localVerified: true, crossSigningVerified: true, signedByOwner: true, userId: "@bot:example.org", deviceId: "DEVICE123", backupVersion: "5256", backup: { serverVersion: "5256", activeVersion: null, trusted: true, matchesDecryptionKey: false, decryptionKeyCached: false, keyLoadAttempted: true, keyLoadError: "secret storage key is not available", }, recoveryKeyStored: true, recoveryKeyCreatedAt: "2026-02-25T20:10:11.000Z", pendingVerifications: 0, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "status"], { from: "user" }); expect(console.log).toHaveBeenCalledWith( "Backup issue: backup decryption key could not be loaded from secret storage (secret storage key is not available)", ); }); it("includes backup reset guidance when the backup key does not match this device", async () => { getMatrixVerificationStatusMock.mockResolvedValue({ encryptionEnabled: true, verified: true, localVerified: true, crossSigningVerified: true, signedByOwner: true, userId: "@bot:example.org", deviceId: "DEVICE123", backupVersion: "21868", backup: { serverVersion: "21868", activeVersion: "21868", trusted: true, matchesDecryptionKey: false, decryptionKeyCached: true, keyLoadAttempted: false, keyLoadError: null, }, recoveryKeyStored: true, recoveryKeyCreatedAt: "2026-03-09T14:40:00.000Z", pendingVerifications: 0, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "status"], { from: "user" }); expect(console.log).toHaveBeenCalledWith( "- If you want a fresh backup baseline and accept losing unrecoverable history, run openclaw matrix verify backup reset --yes. This may also repair secret storage so the new backup key can be loaded after restart.", ); }); it("requires --yes before resetting the Matrix room-key backup", async () => { const program = buildProgram(); await program.parseAsync(["matrix", "verify", "backup", "reset"], { from: "user", }); expect(process.exitCode).toBe(1); expect(resetMatrixRoomKeyBackupMock).not.toHaveBeenCalled(); expect(console.error).toHaveBeenCalledWith( "Backup reset failed: Refusing to reset Matrix room-key backup without --yes", ); }); it("resets the Matrix room-key backup when confirmed", async () => { const program = buildProgram(); await program.parseAsync(["matrix", "verify", "backup", "reset", "--yes"], { from: "user", }); expect(resetMatrixRoomKeyBackupMock).toHaveBeenCalledWith({ accountId: "default", cfg: {}, }); expect(console.log).toHaveBeenCalledWith("Reset success: yes"); expect(console.log).toHaveBeenCalledWith("Previous backup version: 1"); expect(console.log).toHaveBeenCalledWith("Deleted backup version: 1"); expect(console.log).toHaveBeenCalledWith("Current backup version: 2"); expect(console.log).toHaveBeenCalledWith("Backup: active and trusted on this device"); }); it("prints resolved account-aware guidance when a named Matrix account is selected implicitly", async () => { resolveMatrixAuthContextMock.mockImplementation( ({ cfg, accountId }: { cfg: unknown; accountId?: string | null }) => ({ cfg, env: process.env, accountId: accountId ?? "assistant", resolved: {}, }), ); getMatrixVerificationStatusMock.mockResolvedValue({ encryptionEnabled: true, verified: false, localVerified: false, crossSigningVerified: false, signedByOwner: false, userId: "@bot:example.org", deviceId: "DEVICE123", backupVersion: null, backup: { serverVersion: null, activeVersion: null, trusted: null, matchesDecryptionKey: null, decryptionKeyCached: null, keyLoadAttempted: false, keyLoadError: null, }, recoveryKeyStored: false, recoveryKeyCreatedAt: null, pendingVerifications: 0, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "status"], { from: "user" }); expect(getMatrixVerificationStatusMock).toHaveBeenCalledWith({ accountId: "assistant", cfg: {}, includeRecoveryKey: false, }); expect(console.log).toHaveBeenCalledWith("Account: assistant"); expect(console.log).toHaveBeenCalledWith( "- Run openclaw matrix verify device '' --account assistant to verify this device.", ); expect(console.log).toHaveBeenCalledWith( "- Run openclaw matrix verify bootstrap --account assistant to create a room key backup.", ); }); it("prints backup health lines for verify backup status in verbose mode", async () => { getMatrixRoomKeyBackupStatusMock.mockResolvedValue({ serverVersion: "2", activeVersion: null, trusted: true, matchesDecryptionKey: false, decryptionKeyCached: false, keyLoadAttempted: true, keyLoadError: null, }); const program = buildProgram(); await program.parseAsync(["matrix", "verify", "backup", "status", "--verbose"], { from: "user", }); expect(console.log).toHaveBeenCalledWith("Backup server version: 2"); expect(console.log).toHaveBeenCalledWith("Backup active on this device: no"); expect(console.log).toHaveBeenCalledWith("Backup trusted by this device: yes"); }); });