diff --git a/src/commands/doctor-config-flow.test.ts b/src/commands/doctor-config-flow.test.ts index d5c109fa08d..2fa4cf84731 100644 --- a/src/commands/doctor-config-flow.test.ts +++ b/src/commands/doctor-config-flow.test.ts @@ -2017,4 +2017,54 @@ describe("doctor config flow", () => { expect(cfg.channels.googlechat.dm.allowFrom).toEqual(["*"]); expect(cfg.channels.googlechat.allowFrom).toEqual(["*"]); }); + + it("does not report repeat talk provider normalization on consecutive repair runs", async () => { + await withTempHome(async (home) => { + const configDir = path.join(home, ".openclaw"); + await fs.mkdir(configDir, { recursive: true }); + await fs.writeFile( + path.join(configDir, "openclaw.json"), + JSON.stringify( + { + talk: { + interruptOnSpeech: true, + silenceTimeoutMs: 1500, + provider: "elevenlabs", + providers: { + elevenlabs: { + apiKey: "secret-key", + voiceId: "voice-123", + modelId: "eleven_v3", + }, + }, + }, + }, + null, + 2, + ), + "utf-8", + ); + + const noteSpy = vi.spyOn(noteModule, "note").mockImplementation(() => {}); + try { + await loadAndMaybeMigrateDoctorConfig({ + options: { nonInteractive: true, repair: true }, + confirm: async () => false, + }); + noteSpy.mockClear(); + + await loadAndMaybeMigrateDoctorConfig({ + options: { nonInteractive: true, repair: true }, + confirm: async () => false, + }); + const secondRunTalkNormalizationLines = noteSpy.mock.calls + .filter((call) => call[1] === "Doctor changes") + .map((call) => String(call[0])) + .filter((line) => line.includes("Normalized talk.provider/providers shape")); + expect(secondRunTalkNormalizationLines).toEqual([]); + } finally { + noteSpy.mockRestore(); + } + }); + }); });