diff --git a/src/cli/program/config-guard.test.ts b/src/cli/program/config-guard.test.ts index 0ec070e3845..f61590ebae3 100644 --- a/src/cli/program/config-guard.test.ts +++ b/src/cli/program/config-guard.test.ts @@ -29,10 +29,26 @@ function makeRuntime() { } describe("ensureConfigReady", () => { - async function runEnsureConfigReady(commandPath: string[]) { + async function loadEnsureConfigReady() { vi.resetModules(); - const { ensureConfigReady } = await import("./config-guard.js"); - await ensureConfigReady({ runtime: makeRuntime() as never, commandPath }); + return await import("./config-guard.js"); + } + + async function runEnsureConfigReady(commandPath: string[]) { + const runtime = makeRuntime(); + const { ensureConfigReady } = await loadEnsureConfigReady(); + await ensureConfigReady({ runtime: runtime as never, commandPath }); + return runtime; + } + + function setInvalidSnapshot(overrides?: Partial>) { + readConfigFileSnapshotMock.mockResolvedValue({ + ...makeSnapshot(), + exists: true, + valid: false, + issues: [{ path: "channels.whatsapp", message: "invalid" }], + ...overrides, + }); } beforeEach(() => { @@ -55,4 +71,33 @@ describe("ensureConfigReady", () => { await runEnsureConfigReady(commandPath); expect(loadAndMaybeMigrateDoctorConfigMock).toHaveBeenCalledTimes(expectedDoctorCalls); }); + + it("exits for invalid config on non-allowlisted commands", async () => { + setInvalidSnapshot(); + const runtime = await runEnsureConfigReady(["message"]); + + expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("Config invalid")); + expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("doctor --fix")); + expect(runtime.exit).toHaveBeenCalledWith(1); + }); + + it("does not exit for invalid config on allowlisted commands", async () => { + setInvalidSnapshot(); + const statusRuntime = await runEnsureConfigReady(["status"]); + expect(statusRuntime.exit).not.toHaveBeenCalled(); + + const gatewayRuntime = await runEnsureConfigReady(["gateway", "health"]); + expect(gatewayRuntime.exit).not.toHaveBeenCalled(); + }); + + it("runs doctor migration flow only once per module instance", async () => { + const runtimeA = makeRuntime(); + const runtimeB = makeRuntime(); + const { ensureConfigReady } = await loadEnsureConfigReady(); + + await ensureConfigReady({ runtime: runtimeA as never, commandPath: ["message"] }); + await ensureConfigReady({ runtime: runtimeB as never, commandPath: ["message"] }); + + expect(loadAndMaybeMigrateDoctorConfigMock).toHaveBeenCalledTimes(1); + }); });