From 6f2ada17234b384f73829ddd2a732041fbe073b2 Mon Sep 17 00:00:00 2001 From: Shakker Date: Fri, 8 May 2026 17:44:25 +0100 Subject: [PATCH] test: tighten tts command result assertions --- src/auto-reply/reply/commands-tts.test.ts | 88 +++++++++++++---------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/src/auto-reply/reply/commands-tts.test.ts b/src/auto-reply/reply/commands-tts.test.ts index b6d6b6946ac..f576b5aa557 100644 --- a/src/auto-reply/reply/commands-tts.test.ts +++ b/src/auto-reply/reply/commands-tts.test.ts @@ -41,6 +41,7 @@ vi.mock("../../tts/tts.js", () => ttsMocks); const { handleTtsCommands } = await import("./commands-tts.js"); const PRIMARY_TTS_PROVIDER = "acme-speech"; const FALLBACK_TTS_PROVIDER = "backup-speech"; +type TtsCommandResult = Awaited>; function buildTtsParams( commandBodyNormalized: string, @@ -62,6 +63,22 @@ function buildTtsParams( } as unknown as Parameters[0]; } +function expectHandled(result: TtsCommandResult): NonNullable { + if (!result) { + throw new Error("Expected TTS command to be handled"); + } + expect(result.shouldContinue).toBe(false); + return result; +} + +function expectReply(result: TtsCommandResult): NonNullable["reply"] { + const handled = expectHandled(result); + if (!handled.reply) { + throw new Error("Expected TTS command to return a reply"); + } + return handled.reply; +} + describe("handleTtsCommands status fallback reporting", () => { beforeEach(() => { vi.clearAllMocks(); @@ -104,14 +121,10 @@ describe("handleTtsCommands status fallback reporting", () => { }); const result = await handleTtsCommands(buildTtsParams("/tts status"), true); - expect(result?.shouldContinue).toBe(false); - expect(result?.reply?.text).toContain( - `Fallback: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`, - ); - expect(result?.reply?.text).toContain( - `Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`, - ); - expect(result?.reply?.text).toContain( + const reply = expectReply(result); + expect(reply.text).toContain(`Fallback: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`); + expect(reply.text).toContain(`Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`); + expect(reply.text).toContain( `Attempt details: ${PRIMARY_TTS_PROVIDER}:failed(provider_error) 73ms, ${FALLBACK_TTS_PROVIDER}:success(ok) 420ms`, ); }); @@ -136,14 +149,10 @@ describe("handleTtsCommands status fallback reporting", () => { }); const result = await handleTtsCommands(buildTtsParams("/tts status"), true); - expect(result?.shouldContinue).toBe(false); - expect(result?.reply?.text).toContain("Error: TTS conversion failed"); - expect(result?.reply?.text).toContain( - `Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`, - ); - expect(result?.reply?.text).toContain( - `Attempt details: ${PRIMARY_TTS_PROVIDER}:failed(timeout) 999ms`, - ); + const reply = expectReply(result); + expect(reply.text).toContain("Error: TTS conversion failed"); + expect(reply.text).toContain(`Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`); + expect(reply.text).toContain(`Attempt details: ${PRIMARY_TTS_PROVIDER}:failed(timeout) 999ms`); }); it("persists fallback metadata from /tts audio and renders it in /tts status", async () => { @@ -177,19 +186,19 @@ describe("handleTtsCommands status fallback reporting", () => { }); const audioResult = await handleTtsCommands(buildTtsParams("/tts audio hello world"), true); - expect(audioResult?.shouldContinue).toBe(false); - expect(audioResult?.reply?.mediaUrl).toBe("/tmp/fallback.ogg"); + const audioReply = expectReply(audioResult); + expect(audioReply.mediaUrl).toBe("/tmp/fallback.ogg"); const statusResult = await handleTtsCommands(buildTtsParams("/tts status"), true); - expect(statusResult?.shouldContinue).toBe(false); - expect(statusResult?.reply?.text).toContain(`Provider: ${FALLBACK_TTS_PROVIDER}`); - expect(statusResult?.reply?.text).toContain( + const statusReply = expectReply(statusResult); + expect(statusReply.text).toContain(`Provider: ${FALLBACK_TTS_PROVIDER}`); + expect(statusReply.text).toContain( `Fallback: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`, ); - expect(statusResult?.reply?.text).toContain( + expect(statusReply.text).toContain( `Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`, ); - expect(statusResult?.reply?.text).toContain( + expect(statusReply.text).toContain( `Attempt details: ${PRIMARY_TTS_PROVIDER}:failed(provider_error) 65ms, ${FALLBACK_TTS_PROVIDER}:success(ok) 175ms`, ); }); @@ -201,8 +210,8 @@ describe("handleTtsCommands status fallback reporting", () => { } as OpenClawConfig), true, ); - expect(result?.shouldContinue).toBe(false); - expect(result?.reply?.text).toContain("TTS status"); + const reply = expectReply(result); + expect(reply.text).toContain("TTS status"); }); it("resolves status config for the active agent", async () => { @@ -212,7 +221,7 @@ describe("handleTtsCommands status fallback reporting", () => { const result = await handleTtsCommands(buildTtsParams("/tts status", cfg, "reader"), true); - expect(result?.shouldContinue).toBe(false); + expectHandled(result); expect(ttsMocks.resolveTtsConfig).toHaveBeenCalledWith( cfg, expect.objectContaining({ agentId: "reader", channelId: "forum" }), @@ -237,7 +246,7 @@ describe("handleTtsCommands status fallback reporting", () => { true, ); - expect(result?.shouldContinue).toBe(false); + expectHandled(result); expect(ttsMocks.textToSpeech).toHaveBeenCalledWith( expect.objectContaining({ text: "hello", @@ -258,11 +267,11 @@ describe("handleTtsCommands status fallback reporting", () => { ]); const listResult = await handleTtsCommands(buildTtsParams("/tts persona"), true); - expect(listResult?.shouldContinue).toBe(false); - expect(listResult?.reply?.text).toContain("alfred (Alfred) provider=google"); + const listReply = expectReply(listResult); + expect(listReply.text).toContain("alfred (Alfred) provider=google"); const setResult = await handleTtsCommands(buildTtsParams("/tts persona alfred"), true); - expect(setResult?.shouldContinue).toBe(false); + expectHandled(setResult); expect(ttsMocks.setTtsPersona).toHaveBeenCalledWith("/tmp/tts-prefs.json", "alfred"); }); @@ -321,8 +330,8 @@ describe("handleTtsCommands status fallback reporting", () => { true, ); - expect(result?.shouldContinue).toBe(false); - expect(result?.reply).toMatchObject({ + const reply = expectReply(result); + expect(reply).toMatchObject({ mediaUrl: "/tmp/latest.ogg", audioAsVoice: true, spokenText: "latest visible reply", @@ -359,12 +368,14 @@ describe("handleTtsCommands status fallback reporting", () => { const params = buildTtsParams("/tts latest", {}, undefined, { sessionEntry, sessionStore }); const first = await handleTtsCommands(params, true); - expect(first?.reply?.mediaUrl).toBe("/tmp/latest.ogg"); + const firstReply = expectReply(first); + expect(firstReply.mediaUrl).toBe("/tmp/latest.ogg"); ttsMocks.textToSpeech.mockClear(); const second = await handleTtsCommands(params, true); - expect(second?.reply?.text).toContain("already sent"); + const secondReply = expectReply(second); + expect(secondReply.text).toContain("already sent"); expect(ttsMocks.textToSpeech).not.toHaveBeenCalled(); }); @@ -376,21 +387,24 @@ describe("handleTtsCommands status fallback reporting", () => { buildTtsParams("/tts chat on", {}, undefined, { sessionEntry, sessionStore }), true, ); - expect(onResult?.reply?.text).toContain("enabled for this chat"); + const onReply = expectReply(onResult); + expect(onReply.text).toContain("enabled for this chat"); expect(sessionEntry.ttsAuto).toBe("always"); const offResult = await handleTtsCommands( buildTtsParams("/tts chat off", {}, undefined, { sessionEntry, sessionStore }), true, ); - expect(offResult?.reply?.text).toContain("disabled for this chat"); + const offReply = expectReply(offResult); + expect(offReply.text).toContain("disabled for this chat"); expect(sessionEntry.ttsAuto).toBe("off"); const clearResult = await handleTtsCommands( buildTtsParams("/tts chat default", {}, undefined, { sessionEntry, sessionStore }), true, ); - expect(clearResult?.reply?.text).toContain("override cleared"); + const clearReply = expectReply(clearResult); + expect(clearReply.text).toContain("override cleared"); expect(sessionEntry.ttsAuto).toBeUndefined(); }); });