test: guard messaging provider mock calls

This commit is contained in:
Peter Steinberger
2026-05-12 00:00:42 +01:00
parent 25e241e3e2
commit 1f167178f2
4 changed files with 84 additions and 26 deletions

View File

@@ -25,6 +25,28 @@ import * as ttsModule from "./tts.js";
const TEST_CFG = {} as OpenClawConfig;
function requireFirstEdgeTtsCall(edgeSpy: ReturnType<typeof vi.spyOn>): {
config?: unknown;
outputPath: string;
text?: string;
timeoutMs?: number;
} {
const [call] = edgeSpy.mock.calls;
if (!call) {
throw new Error("expected Microsoft Edge TTS call");
}
const [edgeCall] = call;
if (!edgeCall || typeof edgeCall !== "object" || Array.isArray(edgeCall)) {
throw new Error("expected Microsoft Edge TTS call");
}
return edgeCall as {
config?: unknown;
outputPath: string;
text?: string;
timeoutMs?: number;
};
}
describe("listMicrosoftVoices", () => {
const proxyReset = installDebugProxyTestResetHooks();
@@ -213,10 +235,7 @@ describe("buildMicrosoftSpeechProvider", () => {
});
expect(edgeSpy).toHaveBeenCalledOnce();
const edgeCall = edgeSpy.mock.calls[0]?.[0];
if (!edgeCall) {
throw new Error("expected Microsoft Edge TTS call");
}
const edgeCall = requireFirstEdgeTtsCall(edgeSpy);
expect(edgeCall.text).toBe("你好,这是一个测试 hello");
expect(path.basename(edgeCall.outputPath)).toBe("speech.mp3");
expect(edgeCall.timeoutMs).toBe(1000);
@@ -258,10 +277,7 @@ describe("buildMicrosoftSpeechProvider", () => {
});
expect(edgeSpy).toHaveBeenCalledOnce();
const edgeCall = edgeSpy.mock.calls[0]?.[0];
if (!edgeCall) {
throw new Error("expected Microsoft Edge TTS call");
}
const edgeCall = requireFirstEdgeTtsCall(edgeSpy);
expect(edgeCall.text).toBe("你好,这是一个测试 hello");
expect(path.basename(edgeCall.outputPath)).toBe("speech.mp3");
expect(edgeCall.timeoutMs).toBe(1000);

View File

@@ -33,6 +33,14 @@ async function makeTempDir(): Promise<string> {
return dir;
}
function requireFirstTimingSafeEqualCall(mock: ReturnType<typeof vi.fn>): [unknown, unknown] {
const [call] = mock.mock.calls;
if (!call) {
throw new Error("expected timingSafeEqual call");
}
return call as [unknown, unknown];
}
describe("nextcloud talk core", () => {
it("builds an outbound session route for normalized room targets", () => {
const route = resolveNextcloudTalkOutboundSessionRoute({
@@ -216,7 +224,7 @@ describe("nextcloud talk core", () => {
).toBe(false);
expect(timingSafeEqualMock).toHaveBeenCalledOnce();
const [leftBuffer, rightBuffer] = timingSafeEqualMock.mock.calls[0] ?? [];
const [leftBuffer, rightBuffer] = requireFirstTimingSafeEqualCall(timingSafeEqualMock);
expect(Buffer.isBuffer(leftBuffer)).toBe(true);
expect(Buffer.isBuffer(rightBuffer)).toBe(true);
if (!Buffer.isBuffer(leftBuffer) || !Buffer.isBuffer(rightBuffer)) {

View File

@@ -84,6 +84,22 @@ function createRuntimeEnv() {
} as unknown as RuntimeEnv;
}
function requireFirstMockArg(mock: ReturnType<typeof vi.fn>, label: string): unknown {
const [call] = mock.mock.calls;
if (!call) {
throw new Error(`expected ${label}`);
}
return call[0];
}
function requireFirstSendMessageCall(): [unknown, unknown, unknown] {
const [call] = sendMessageNextcloudTalkMock.mock.calls;
if (!call) {
throw new Error("expected Nextcloud Talk send call");
}
return call as [unknown, unknown, unknown];
}
function createAccount(
overrides?: Partial<ResolvedNextcloudTalkAccount>,
): ResolvedNextcloudTalkAccount {
@@ -154,17 +170,22 @@ describe("nextcloud-talk inbound behavior", () => {
statusSink,
});
const challengeParams = issueChallenge.mock.calls[0]?.[0] as
| { meta?: { name?: string }; senderId?: string; senderIdLine?: string }
| undefined;
expect(challengeParams?.senderId).toBe("user-1");
expect(challengeParams?.senderIdLine).toBe("Your Nextcloud user id: user-1");
expect(challengeParams?.meta).toEqual({ name: "Alice" });
const challengeParams = requireFirstMockArg(
issueChallenge,
"Nextcloud Talk pairing challenge",
) as {
meta?: { name?: string };
senderId?: string;
senderIdLine?: string;
};
expect(challengeParams.senderId).toBe("user-1");
expect(challengeParams.senderIdLine).toBe("Your Nextcloud user id: user-1");
expect(challengeParams.meta).toEqual({ name: "Alice" });
expect(sendMessageNextcloudTalkMock).toHaveBeenCalledTimes(1);
const sendArgs = sendMessageNextcloudTalkMock.mock.calls[0];
expect(sendArgs?.[0]).toBe("room-1");
expect(sendArgs?.[1]).toBe("Pair with code 123456");
expect(sendArgs?.[2]).toEqual({
const sendArgs = requireFirstSendMessageCall();
expect(sendArgs[0]).toBe("room-1");
expect(sendArgs[1]).toBe("Pair with code 123456");
expect(sendArgs[2]).toEqual({
cfg: { channels: { "nextcloud-talk": {} } },
accountId: "default",
});
@@ -279,9 +300,10 @@ describe("nextcloud-talk inbound behavior", () => {
runtime: createRuntimeEnv(),
});
const assembledRequest = (
coreRuntime.channel.turn.runAssembled as unknown as { mock: { calls: unknown[][] } }
).mock.calls[0]?.[0] as { replyPipeline?: unknown } | undefined;
expect(assembledRequest?.replyPipeline).toEqual({});
const assembledRequest = requireFirstMockArg(
coreRuntime.channel.turn.runAssembled as ReturnType<typeof vi.fn>,
"Nextcloud Talk assembled request",
) as { replyPipeline?: unknown };
expect(assembledRequest.replyPipeline).toEqual({});
});
});

View File

@@ -26,6 +26,18 @@ afterEach(() => {
__testing.resetRoomCache();
});
function requireFirstFetchParams(): { auditContext?: string; url?: string } {
const [call] = fetchWithSsrFGuard.mock.calls;
if (!call) {
throw new Error("expected Nextcloud Talk room info fetch call");
}
const [fetchParams] = call;
if (!fetchParams || typeof fetchParams !== "object" || Array.isArray(fetchParams)) {
throw new Error("expected Nextcloud Talk room info fetch call");
}
return fetchParams as { auditContext?: string; url?: string };
}
describe("nextcloud talk room info", () => {
it("resolves direct rooms from the room info endpoint", async () => {
const release = vi.fn(async () => {});
@@ -56,11 +68,11 @@ describe("nextcloud talk room info", () => {
});
expect(kind).toBe("direct");
const fetchParams = fetchWithSsrFGuard.mock.calls[0]?.[0];
expect(fetchParams?.url).toBe(
const fetchParams = requireFirstFetchParams();
expect(fetchParams.url).toBe(
"https://nc.example.com/ocs/v2.php/apps/spreed/api/v4/room/room-direct",
);
expect(fetchParams?.auditContext).toBe("nextcloud-talk.room-info");
expect(fetchParams.auditContext).toBe("nextcloud-talk.room-info");
expect(release).toHaveBeenCalledTimes(1);
});