




















@@ -74,6 +74,21 @@ const startWsServer = async (
7474},
7575});
767677+const requireRecord = (value: unknown, label: string): Record<string, unknown> => {
78+if (value === null || typeof value !== "object" || Array.isArray(value)) {
79+throw new Error(`Expected ${label} to be a record`);
80+}
81+return value as Record<string, unknown>;
82+};
83+84+const requireTalkEvent = (events: TalkEvent[], type: TalkEvent["type"]) => {
85+const event = events.find((candidate) => candidate.type === type);
86+if (!event) {
87+throw new Error(`Expected ${type} Talk event`);
88+}
89+return requireRecord(event, `${type} Talk event`);
90+};
91+7792describe("MediaStreamHandler TTS queue", () => {
7893it("serializes TTS playback and resolves in order", async () => {
7994const handler = new MediaStreamHandler({
@@ -260,30 +275,26 @@ describe("MediaStreamHandler security hardening", () => {
260275"turn.cancelled",
261276"session.closed",
262277]);
263-expect(talkEvents[0]).toEqual(
264-expect.objectContaining({
265-sessionId: "voice-call:CA-talk:MZ-talk",
266-mode: "stt-tts",
267-transport: "gateway-relay",
268-brain: "agent-consult",
269-provider: "openai",
270-seq: 1,
271-}),
272-);
273-expect(talkEvents.find((event) => event.type === "transcript.done")).toEqual(
274-expect.objectContaining({
275-final: true,
276-turnId: "MZ-talk:turn-1",
277-payload: expect.objectContaining({ text: "hello there", role: "user" }),
278-}),
279-);
280-expect(talkEvents.find((event) => event.type === "turn.cancelled")).toEqual(
281-expect.objectContaining({
282-final: true,
283-turnId: "MZ-talk:turn-2",
284-payload: expect.objectContaining({ reason: "barge-in" }),
285-}),
286-);
278+const startedEvent = requireRecord(talkEvents[0], "session started Talk event");
279+expect(startedEvent.sessionId).toBe("voice-call:CA-talk:MZ-talk");
280+expect(startedEvent.mode).toBe("stt-tts");
281+expect(startedEvent.transport).toBe("gateway-relay");
282+expect(startedEvent.brain).toBe("agent-consult");
283+expect(startedEvent.provider).toBe("openai");
284+expect(startedEvent.seq).toBe(1);
285+286+const transcriptDone = requireTalkEvent(talkEvents, "transcript.done");
287+expect(transcriptDone.final).toBe(true);
288+expect(transcriptDone.turnId).toBe("MZ-talk:turn-1");
289+const transcriptPayload = requireRecord(transcriptDone.payload, "transcript payload");
290+expect(transcriptPayload.text).toBe("hello there");
291+expect(transcriptPayload.role).toBe("user");
292+293+const cancelled = requireTalkEvent(talkEvents, "turn.cancelled");
294+expect(cancelled.final).toBe(true);
295+expect(cancelled.turnId).toBe("MZ-talk:turn-2");
296+const cancelledPayload = requireRecord(cancelled.payload, "cancelled payload");
297+expect(cancelledPayload.reason).toBe("barge-in");
287298} finally {
288299await server.close();
289300}
@@ -568,11 +579,16 @@ describe("MediaStreamHandler security hardening", () => {
568579throw new Error("Expected upgrade callback to be registered");
569580}
570581completeUpgrade({} as WebSocket);
571-expect(fakeWss.emit).toHaveBeenCalledWith(
572-"connection",
573-expect.anything(),
574-expect.objectContaining({ socket: { remoteAddress: "127.0.0.1" } }),
575-);
582+expect(fakeWss.emit).toHaveBeenCalledOnce();
583+const emitCall = fakeWss.emit.mock.calls[0];
584+if (emitCall === undefined) {
585+throw new Error("Expected websocket connection emit call");
586+}
587+expect(emitCall[0]).toBe("connection");
588+expect(emitCall[1]).toBeDefined();
589+const request = requireRecord(emitCall[2], "connection request");
590+const socket = requireRecord(request.socket, "connection request socket");
591+expect(socket.remoteAddress).toBe("127.0.0.1");
576592});
577593578594it("releases in-flight reservations when ws rejects a malformed upgrade before the callback", async () => {
@@ -624,7 +640,9 @@ describe("MediaStreamHandler security hardening", () => {
624640});
625641626642it("clears pending state after valid start", async () => {
627-const shouldAcceptStream = vi.fn(() => true);
643+const shouldAcceptStream = vi.fn(
644+(_params: { callId: string; streamSid: string; token?: string }) => true,
645+);
628646const handler = new MediaStreamHandler({
629647transcriptionProvider: createStubSttProvider(),
630648providerConfig: {},
@@ -646,14 +664,15 @@ describe("MediaStreamHandler security hardening", () => {
646664);
647665648666await vi.waitFor(() => {
649-expect(shouldAcceptStream).toHaveBeenCalledWith(
650-expect.objectContaining({
651-callId: "CA123",
652-streamSid: "MZ123",
653-token: "token-123",
654-}),
655-);
667+expect(shouldAcceptStream).toHaveBeenCalledOnce();
656668});
669+const acceptedStream = requireRecord(
670+shouldAcceptStream.mock.calls[0]?.[0],
671+"accepted stream params",
672+);
673+expect(acceptedStream.callId).toBe("CA123");
674+expect(acceptedStream.streamSid).toBe("MZ123");
675+expect(acceptedStream.token).toBe("token-123");
657676expect(ws.readyState).toBe(WebSocket.OPEN);
658677659678const second = await connectWs(server.url);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。