import { beforeEach, describe, expect, it, vi } from "vitest"; import { clearConfigCache, loadConfig } from "./config.js"; import { withTempHomeConfig } from "./test-helpers.js"; describe("talk config validation fail-closed behavior", () => { beforeEach(() => { clearConfigCache(); vi.restoreAllMocks(); }); async function expectInvalidTalkConfig(config: unknown, messagePattern: RegExp) { await withTempHomeConfig(config, async () => { const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); let thrown: unknown; try { loadConfig(); } catch (error) { thrown = error; } expect(thrown).toBeInstanceOf(Error); expect((thrown as { code?: string } | undefined)?.code).toBe("INVALID_CONFIG"); expect((thrown as Error).message).toMatch(messagePattern); expect(consoleSpy).toHaveBeenCalled(); }); } it.each([ ["boolean", true], ["string", "1500"], ["float", 1500.5], ])("rejects %s talk.silenceTimeoutMs during config load", async (_label, value) => { await expectInvalidTalkConfig( { agents: { list: [{ id: "main" }] }, talk: { silenceTimeoutMs: value, }, }, /silenceTimeoutMs|talk/i, ); }); it("rejects talk.provider when it does not match talk.providers during config load", async () => { await expectInvalidTalkConfig( { agents: { list: [{ id: "main" }] }, talk: { provider: "acme", providers: { elevenlabs: { voiceId: "voice-123", }, }, }, }, /talk\.provider|talk\.providers|acme/i, ); }); it("rejects multi-provider talk config without talk.provider during config load", async () => { await expectInvalidTalkConfig( { agents: { list: [{ id: "main" }] }, talk: { providers: { acme: { voiceId: "voice-acme", }, elevenlabs: { voiceId: "voice-eleven", }, }, }, }, /talk\.provider|required/i, ); }); });