import { beforeEach, describe, expect, it, vi } from "vitest"; import type { RuntimeEnv } from "../runtime-api.js"; const verificationMocks = vi.hoisted(() => ({ bootstrapMatrixVerification: vi.fn(), })); vi.mock("./matrix/actions/verification.js", () => ({ bootstrapMatrixVerification: verificationMocks.bootstrapMatrixVerification, })); import { matrixPlugin } from "./channel.js"; import { matrixSetupAdapter } from "./setup-core.js"; import { installMatrixTestRuntime } from "./test-runtime.js"; import type { CoreConfig } from "./types.js"; let runMatrixSetupBootstrapAfterConfigWrite: typeof import("./setup-bootstrap.js").runMatrixSetupBootstrapAfterConfigWrite; describe("matrix setup post-write bootstrap", () => { const log = vi.fn(); const error = vi.fn(); const exit = vi.fn((code: number): never => { throw new Error(`exit ${code}`); }); const encryptedDefaultCfg = { channels: { matrix: { encryption: true, }, }, } as CoreConfig; const defaultPasswordInput = { homeserver: "https://matrix.example.org", userId: "@flurry:example.org", password: "secret", // pragma: allowlist secret } as const; const runtime: RuntimeEnv = { log, error, exit, }; function applyAccountConfig(params: { previousCfg: CoreConfig; accountId: string; input: Record; }) { return { previousCfg: params.previousCfg, accountId: params.accountId, input: params.input, nextCfg: matrixSetupAdapter.applyAccountConfig({ cfg: params.previousCfg, accountId: params.accountId, input: params.input, }) as CoreConfig, }; } function applyDefaultAccountConfig(input: Record = defaultPasswordInput) { return applyAccountConfig({ previousCfg: encryptedDefaultCfg, accountId: "default", input, }); } function mockBootstrapResult(params: { success: boolean; backupVersion?: string | null; error?: string; }) { verificationMocks.bootstrapMatrixVerification.mockResolvedValue({ success: params.success, ...(params.error ? { error: params.error } : {}), verification: { backupVersion: params.backupVersion ?? null, }, crossSigning: {}, pendingVerifications: 0, cryptoBootstrap: null, }); } async function runAfterAccountConfigWritten(params: { previousCfg: CoreConfig; nextCfg: CoreConfig; accountId: string; input: Record; }) { await runMatrixSetupBootstrapAfterConfigWrite({ previousCfg: params.previousCfg, cfg: params.nextCfg, accountId: params.accountId, runtime, }); } async function withSavedEnv( values: Record, run: () => Promise | T, ) { const previousEnv = Object.fromEntries( Object.keys(values).map((key) => [key, process.env[key]]), ) as Record; for (const [key, value] of Object.entries(values)) { if (value === undefined) { delete process.env[key]; } else { process.env[key] = value; } } try { return await run(); } finally { for (const [key, value] of Object.entries(previousEnv)) { if (value === undefined) { delete process.env[key]; } else { process.env[key] = value; } } } } beforeEach(async () => { vi.resetModules(); ({ runMatrixSetupBootstrapAfterConfigWrite } = await import("./setup-bootstrap.js")); verificationMocks.bootstrapMatrixVerification.mockReset(); log.mockClear(); error.mockClear(); exit.mockClear(); installMatrixTestRuntime(); }); it("bootstraps verification for newly added encrypted accounts", async () => { const { previousCfg, nextCfg, accountId, input } = applyDefaultAccountConfig(); mockBootstrapResult({ success: true, backupVersion: "7" }); await runAfterAccountConfigWritten({ previousCfg, nextCfg, accountId, input }); expect(verificationMocks.bootstrapMatrixVerification).toHaveBeenCalledWith({ accountId: "default", }); expect(log).toHaveBeenCalledWith('Matrix verification bootstrap: complete for "default".'); expect(log).toHaveBeenCalledWith('Matrix backup version for "default": 7'); expect(error).not.toHaveBeenCalled(); }); it("does not bootstrap verification for already configured accounts", async () => { const previousCfg = { channels: { matrix: { accounts: { flurry: { encryption: true, homeserver: "https://matrix.example.org", userId: "@flurry:example.org", accessToken: "token", }, }, }, }, } as CoreConfig; const input = { homeserver: "https://matrix.example.org", userId: "@flurry:example.org", accessToken: "new-token", }; const { nextCfg, accountId } = applyAccountConfig({ previousCfg, accountId: "flurry", input, }); await runAfterAccountConfigWritten({ previousCfg, nextCfg, accountId, input }); expect(verificationMocks.bootstrapMatrixVerification).not.toHaveBeenCalled(); expect(log).not.toHaveBeenCalled(); expect(error).not.toHaveBeenCalled(); }); it("logs a warning when verification bootstrap fails", async () => { const { previousCfg, nextCfg, accountId, input } = applyDefaultAccountConfig(); mockBootstrapResult({ success: false, error: "no room-key backup exists on the homeserver", }); await runAfterAccountConfigWritten({ previousCfg, nextCfg, accountId, input }); expect(error).toHaveBeenCalledWith( 'Matrix verification bootstrap warning for "default": no room-key backup exists on the homeserver', ); }); it("bootstraps a newly added env-backed default account when encryption is already enabled", async () => { await withSavedEnv( { MATRIX_HOMESERVER: "https://matrix.example.org", MATRIX_ACCESS_TOKEN: "env-token", }, async () => { const { previousCfg, nextCfg, accountId, input } = applyDefaultAccountConfig({ useEnv: true, }); mockBootstrapResult({ success: true, backupVersion: "9" }); await runAfterAccountConfigWritten({ previousCfg, nextCfg, accountId, input }); expect(verificationMocks.bootstrapMatrixVerification).toHaveBeenCalledWith({ accountId: "default", }); expect(log).toHaveBeenCalledWith('Matrix verification bootstrap: complete for "default".'); }, ); }); it("rejects default useEnv setup when no Matrix auth env vars are available", () => { return withSavedEnv( { MATRIX_HOMESERVER: undefined, MATRIX_USER_ID: undefined, MATRIX_ACCESS_TOKEN: undefined, MATRIX_PASSWORD: undefined, MATRIX_DEFAULT_HOMESERVER: undefined, MATRIX_DEFAULT_USER_ID: undefined, MATRIX_DEFAULT_ACCESS_TOKEN: undefined, MATRIX_DEFAULT_PASSWORD: undefined, }, () => { expect( matrixSetupAdapter.validateInput?.({ cfg: {} as CoreConfig, accountId: "default", input: { useEnv: true }, }), ).toContain("Set Matrix env vars for the default account"); }, ); }); it("clears allowPrivateNetwork when deleting the default Matrix account config", () => { const updated = matrixPlugin.config.deleteAccount?.({ cfg: { channels: { matrix: { homeserver: "http://localhost.localdomain:8008", allowPrivateNetwork: true, accounts: { ops: { enabled: true, }, }, }, }, } as CoreConfig, accountId: "default", }) as CoreConfig; expect(updated.channels?.matrix).toEqual({ accounts: { ops: { enabled: true, }, }, }); }); });