test: tighten tts command result assertions

This commit is contained in:
Shakker
2026-05-08 17:44:25 +01:00
parent 127fdb6120
commit 6f2ada1723

View File

@@ -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<ReturnType<typeof handleTtsCommands>>;
function buildTtsParams(
commandBodyNormalized: string,
@@ -62,6 +63,22 @@ function buildTtsParams(
} as unknown as Parameters<typeof handleTtsCommands>[0];
}
function expectHandled(result: TtsCommandResult): NonNullable<TtsCommandResult> {
if (!result) {
throw new Error("Expected TTS command to be handled");
}
expect(result.shouldContinue).toBe(false);
return result;
}
function expectReply(result: TtsCommandResult): NonNullable<TtsCommandResult>["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();
});
});