





















@@ -41,6 +41,7 @@ vi.mock("../../tts/tts.js", () => ttsMocks);
4141const { handleTtsCommands } = await import("./commands-tts.js");
4242const PRIMARY_TTS_PROVIDER = "acme-speech";
4343const FALLBACK_TTS_PROVIDER = "backup-speech";
44+type TtsCommandResult = Awaited<ReturnType<typeof handleTtsCommands>>;
44454546function buildTtsParams(
4647commandBodyNormalized: string,
@@ -62,6 +63,22 @@ function buildTtsParams(
6263} as unknown as Parameters<typeof handleTtsCommands>[0];
6364}
646566+function expectHandled(result: TtsCommandResult): NonNullable<TtsCommandResult> {
67+if (!result) {
68+throw new Error("Expected TTS command to be handled");
69+}
70+expect(result.shouldContinue).toBe(false);
71+return result;
72+}
73+74+function expectReply(result: TtsCommandResult): NonNullable<TtsCommandResult>["reply"] {
75+const handled = expectHandled(result);
76+if (!handled.reply) {
77+throw new Error("Expected TTS command to return a reply");
78+}
79+return handled.reply;
80+}
81+6582describe("handleTtsCommands status fallback reporting", () => {
6683beforeEach(() => {
6784vi.clearAllMocks();
@@ -104,14 +121,10 @@ describe("handleTtsCommands status fallback reporting", () => {
104121});
105122106123const result = await handleTtsCommands(buildTtsParams("/tts status"), true);
107-expect(result?.shouldContinue).toBe(false);
108-expect(result?.reply?.text).toContain(
109-`Fallback: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`,
110-);
111-expect(result?.reply?.text).toContain(
112-`Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`,
113-);
114-expect(result?.reply?.text).toContain(
124+const reply = expectReply(result);
125+expect(reply.text).toContain(`Fallback: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`);
126+expect(reply.text).toContain(`Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`);
127+expect(reply.text).toContain(
115128`Attempt details: ${PRIMARY_TTS_PROVIDER}:failed(provider_error) 73ms, ${FALLBACK_TTS_PROVIDER}:success(ok) 420ms`,
116129);
117130});
@@ -136,14 +149,10 @@ describe("handleTtsCommands status fallback reporting", () => {
136149});
137150138151const result = await handleTtsCommands(buildTtsParams("/tts status"), true);
139-expect(result?.shouldContinue).toBe(false);
140-expect(result?.reply?.text).toContain("Error: TTS conversion failed");
141-expect(result?.reply?.text).toContain(
142-`Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`,
143-);
144-expect(result?.reply?.text).toContain(
145-`Attempt details: ${PRIMARY_TTS_PROVIDER}:failed(timeout) 999ms`,
146-);
152+const reply = expectReply(result);
153+expect(reply.text).toContain("Error: TTS conversion failed");
154+expect(reply.text).toContain(`Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`);
155+expect(reply.text).toContain(`Attempt details: ${PRIMARY_TTS_PROVIDER}:failed(timeout) 999ms`);
147156});
148157149158it("persists fallback metadata from /tts audio and renders it in /tts status", async () => {
@@ -177,19 +186,19 @@ describe("handleTtsCommands status fallback reporting", () => {
177186});
178187179188const audioResult = await handleTtsCommands(buildTtsParams("/tts audio hello world"), true);
180-expect(audioResult?.shouldContinue).toBe(false);
181-expect(audioResult?.reply?.mediaUrl).toBe("/tmp/fallback.ogg");
189+const audioReply = expectReply(audioResult);
190+expect(audioReply.mediaUrl).toBe("/tmp/fallback.ogg");
182191183192const statusResult = await handleTtsCommands(buildTtsParams("/tts status"), true);
184-expect(statusResult?.shouldContinue).toBe(false);
185-expect(statusResult?.reply?.text).toContain(`Provider: ${FALLBACK_TTS_PROVIDER}`);
186-expect(statusResult?.reply?.text).toContain(
193+const statusReply = expectReply(statusResult);
194+expect(statusReply.text).toContain(`Provider: ${FALLBACK_TTS_PROVIDER}`);
195+expect(statusReply.text).toContain(
187196`Fallback: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`,
188197);
189-expect(statusResult?.reply?.text).toContain(
198+expect(statusReply.text).toContain(
190199`Attempts: ${PRIMARY_TTS_PROVIDER} -> ${FALLBACK_TTS_PROVIDER}`,
191200);
192-expect(statusResult?.reply?.text).toContain(
201+expect(statusReply.text).toContain(
193202`Attempt details: ${PRIMARY_TTS_PROVIDER}:failed(provider_error) 65ms, ${FALLBACK_TTS_PROVIDER}:success(ok) 175ms`,
194203);
195204});
@@ -201,8 +210,8 @@ describe("handleTtsCommands status fallback reporting", () => {
201210} as OpenClawConfig),
202211true,
203212);
204-expect(result?.shouldContinue).toBe(false);
205-expect(result?.reply?.text).toContain("TTS status");
213+const reply = expectReply(result);
214+expect(reply.text).toContain("TTS status");
206215});
207216208217it("resolves status config for the active agent", async () => {
@@ -212,7 +221,7 @@ describe("handleTtsCommands status fallback reporting", () => {
212221213222const result = await handleTtsCommands(buildTtsParams("/tts status", cfg, "reader"), true);
214223215-expect(result?.shouldContinue).toBe(false);
224+expectHandled(result);
216225expect(ttsMocks.resolveTtsConfig).toHaveBeenCalledWith(
217226cfg,
218227expect.objectContaining({ agentId: "reader", channelId: "forum" }),
@@ -237,7 +246,7 @@ describe("handleTtsCommands status fallback reporting", () => {
237246true,
238247);
239248240-expect(result?.shouldContinue).toBe(false);
249+expectHandled(result);
241250expect(ttsMocks.textToSpeech).toHaveBeenCalledWith(
242251expect.objectContaining({
243252text: "hello",
@@ -258,11 +267,11 @@ describe("handleTtsCommands status fallback reporting", () => {
258267]);
259268260269const listResult = await handleTtsCommands(buildTtsParams("/tts persona"), true);
261-expect(listResult?.shouldContinue).toBe(false);
262-expect(listResult?.reply?.text).toContain("alfred (Alfred) provider=google");
270+const listReply = expectReply(listResult);
271+expect(listReply.text).toContain("alfred (Alfred) provider=google");
263272264273const setResult = await handleTtsCommands(buildTtsParams("/tts persona alfred"), true);
265-expect(setResult?.shouldContinue).toBe(false);
274+expectHandled(setResult);
266275expect(ttsMocks.setTtsPersona).toHaveBeenCalledWith("/tmp/tts-prefs.json", "alfred");
267276});
268277@@ -321,8 +330,8 @@ describe("handleTtsCommands status fallback reporting", () => {
321330true,
322331);
323332324-expect(result?.shouldContinue).toBe(false);
325-expect(result?.reply).toMatchObject({
333+const reply = expectReply(result);
334+expect(reply).toMatchObject({
326335mediaUrl: "/tmp/latest.ogg",
327336audioAsVoice: true,
328337spokenText: "latest visible reply",
@@ -359,12 +368,14 @@ describe("handleTtsCommands status fallback reporting", () => {
359368const params = buildTtsParams("/tts latest", {}, undefined, { sessionEntry, sessionStore });
360369361370const first = await handleTtsCommands(params, true);
362-expect(first?.reply?.mediaUrl).toBe("/tmp/latest.ogg");
371+const firstReply = expectReply(first);
372+expect(firstReply.mediaUrl).toBe("/tmp/latest.ogg");
363373ttsMocks.textToSpeech.mockClear();
364374365375const second = await handleTtsCommands(params, true);
366376367-expect(second?.reply?.text).toContain("already sent");
377+const secondReply = expectReply(second);
378+expect(secondReply.text).toContain("already sent");
368379expect(ttsMocks.textToSpeech).not.toHaveBeenCalled();
369380});
370381@@ -376,21 +387,24 @@ describe("handleTtsCommands status fallback reporting", () => {
376387buildTtsParams("/tts chat on", {}, undefined, { sessionEntry, sessionStore }),
377388true,
378389);
379-expect(onResult?.reply?.text).toContain("enabled for this chat");
390+const onReply = expectReply(onResult);
391+expect(onReply.text).toContain("enabled for this chat");
380392expect(sessionEntry.ttsAuto).toBe("always");
381393382394const offResult = await handleTtsCommands(
383395buildTtsParams("/tts chat off", {}, undefined, { sessionEntry, sessionStore }),
384396true,
385397);
386-expect(offResult?.reply?.text).toContain("disabled for this chat");
398+const offReply = expectReply(offResult);
399+expect(offReply.text).toContain("disabled for this chat");
387400expect(sessionEntry.ttsAuto).toBe("off");
388401389402const clearResult = await handleTtsCommands(
390403buildTtsParams("/tts chat default", {}, undefined, { sessionEntry, sessionStore }),
391404true,
392405);
393-expect(clearResult?.reply?.text).toContain("override cleared");
406+const clearReply = expectReply(clearResult);
407+expect(clearReply.text).toContain("override cleared");
394408expect(sessionEntry.ttsAuto).toBeUndefined();
395409});
396410});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。