mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 20:35:39 -06:00
test(voice-call): cover Twilio provider state cleanup
This commit is contained in:
@@ -28,6 +28,50 @@ function createProvider(): TwilioProvider {
|
||||
);
|
||||
}
|
||||
|
||||
type TwilioPrivateCallState = {
|
||||
callWebhookUrls: Map<string, string>;
|
||||
callStreamMap: Map<string, string>;
|
||||
streamAuthTokens: Map<string, string>;
|
||||
twimlStorage: Map<string, string>;
|
||||
notifyCalls: Set<string>;
|
||||
activeStreamCalls: Set<string>;
|
||||
};
|
||||
|
||||
function getTwilioPrivateCallState(provider: TwilioProvider): TwilioPrivateCallState {
|
||||
return provider as unknown as TwilioPrivateCallState;
|
||||
}
|
||||
|
||||
function seedTwilioPrivateCallState(params: {
|
||||
provider: TwilioProvider;
|
||||
callId: string;
|
||||
providerCallId: string;
|
||||
}): void {
|
||||
const state = getTwilioPrivateCallState(params.provider);
|
||||
state.callWebhookUrls.set(
|
||||
params.providerCallId,
|
||||
`https://example.ngrok.app/voice/twilio?callId=${params.callId}`,
|
||||
);
|
||||
state.callStreamMap.set(params.providerCallId, "MZ-private-state");
|
||||
state.streamAuthTokens.set(params.providerCallId, "stream-token");
|
||||
state.twimlStorage.set(params.callId, "<Response><Say>Hello</Say></Response>");
|
||||
state.notifyCalls.add(params.callId);
|
||||
state.activeStreamCalls.add(params.providerCallId);
|
||||
}
|
||||
|
||||
function expectTwilioPrivateCallStateReleased(params: {
|
||||
provider: TwilioProvider;
|
||||
callId: string;
|
||||
providerCallId: string;
|
||||
}): void {
|
||||
const state = getTwilioPrivateCallState(params.provider);
|
||||
expect(state.callWebhookUrls.has(params.providerCallId)).toBe(false);
|
||||
expect(state.callStreamMap.has(params.providerCallId)).toBe(false);
|
||||
expect(state.streamAuthTokens.has(params.providerCallId)).toBe(false);
|
||||
expect(state.twimlStorage.has(params.callId)).toBe(false);
|
||||
expect(state.notifyCalls.has(params.callId)).toBe(false);
|
||||
expect(state.activeStreamCalls.has(params.providerCallId)).toBe(false);
|
||||
}
|
||||
|
||||
function createContext(rawBody: string, query?: WebhookContext["query"]): WebhookContext {
|
||||
return {
|
||||
headers: {},
|
||||
@@ -366,6 +410,55 @@ describe("TwilioProvider", () => {
|
||||
expect(nextBody).not.toContain("hold-queue");
|
||||
});
|
||||
|
||||
it("releases all provider call state on terminal callbacks and late replays", () => {
|
||||
const provider = createProvider();
|
||||
const callId = "call-terminal";
|
||||
const providerCallId = "CA-terminal";
|
||||
seedTwilioPrivateCallState({ provider, callId, providerCallId });
|
||||
const terminal = createContext(
|
||||
`CallStatus=completed&Direction=outbound-api&CallSid=${providerCallId}`,
|
||||
{ callId, type: "status" },
|
||||
);
|
||||
|
||||
const first = provider.parseWebhookEvent(terminal).events[0];
|
||||
expect(first).toMatchObject({
|
||||
type: "call.ended",
|
||||
callId,
|
||||
providerCallId,
|
||||
reason: "completed",
|
||||
});
|
||||
expectTwilioPrivateCallStateReleased({ provider, callId, providerCallId });
|
||||
|
||||
const lateReplay = provider.parseWebhookEvent(terminal).events[0];
|
||||
expect(lateReplay).toMatchObject({
|
||||
type: "call.ended",
|
||||
callId,
|
||||
providerCallId,
|
||||
reason: "completed",
|
||||
});
|
||||
expectTwilioPrivateCallStateReleased({ provider, callId, providerCallId });
|
||||
});
|
||||
|
||||
it("releases all provider call state before repeated explicit hangups", async () => {
|
||||
const provider = createProvider();
|
||||
const callId = "call-hangup";
|
||||
const providerCallId = "CA-hangup";
|
||||
seedTwilioPrivateCallState({ provider, callId, providerCallId });
|
||||
const apiRequest = createApiRequestMock();
|
||||
(
|
||||
provider as unknown as {
|
||||
apiRequest: TwilioApiRequest;
|
||||
}
|
||||
).apiRequest = apiRequest;
|
||||
const input = { callId, providerCallId, reason: "hangup-bot" as const };
|
||||
|
||||
await provider.hangupCall(input);
|
||||
expectTwilioPrivateCallStateReleased({ provider, callId, providerCallId });
|
||||
await provider.hangupCall(input);
|
||||
expectTwilioPrivateCallStateReleased({ provider, callId, providerCallId });
|
||||
expect(apiRequest).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("QUEUE_TWIML references /voice/hold-music waitUrl", () => {
|
||||
const provider = createProvider();
|
||||
const firstInbound = createContext("CallStatus=ringing&Direction=inbound&CallSid=CA611");
|
||||
|
||||
Reference in New Issue
Block a user