mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
* fix(talk): avoid full plugin imports during cold catalog discovery Use optional capability catalog entries backed by the same provider factories and native host operations as full registration. Preserve prepared generations, active descriptors, installed-plugin registration, and explicit empty-family semantics. Account for exactly three approved public catalog type exports. * fix(plugins): complete cold catalog boundary integration Keep catalog descriptors separate from native host-operation types, and move DeepInfra shared model types and constants to their leaf owner. Recognize manifest-loaded catalog entrypoints and remove the unused xAI transcription wrapper while retaining the production factory tests. Preserve real module exports under existing test overrides. Keep all assertions, deadlines, public SDK exports, and approved budgets unchanged.
157 lines
5.8 KiB
TypeScript
157 lines
5.8 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { openAIRealtimeHost } from "./realtime-host.js";
|
|
import {
|
|
buildOpenAIRealtimeSidebandUrl,
|
|
createOpenAIQuicksilverCall,
|
|
hangupOpenAIRealtimeCall,
|
|
} from "./realtime-quicksilver-wire.js";
|
|
|
|
describe("OpenAI Realtime sideband call wire", () => {
|
|
it("posts bounded server-owned session config and returns the fixed sideband URL", async () => {
|
|
const onCallAllocated = vi.fn();
|
|
const fetchImpl = vi.fn(async (_url: string | URL | Request, init?: RequestInit) => {
|
|
expect(init?.method).toBe("POST");
|
|
expect(init?.headers).toMatchObject({ Authorization: "Bearer sk-platform" }); // pragma: allowlist secret
|
|
expect(init?.headers).toMatchObject({
|
|
"Content-Type": expect.stringMatching(/^multipart\/form-data; boundary=/),
|
|
});
|
|
if (typeof init?.body !== "string") {
|
|
throw new Error("Expected string multipart body");
|
|
}
|
|
const form = init.body;
|
|
expect(form).toContain('name="sdp"');
|
|
expect(form).toContain("v=0\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\n");
|
|
expect(form).toContain('name="session"');
|
|
expect(form).toContain('"type":"realtime","model":"gpt-realtime-2.1"');
|
|
return new Response("v=0\r\na=answer\r\n", {
|
|
status: 201,
|
|
headers: { Location: "/v1/realtime/calls/call_test-123" },
|
|
});
|
|
});
|
|
|
|
await expect(
|
|
createOpenAIQuicksilverCall(
|
|
{
|
|
auth: { type: "api-key", token: "sk-platform" }, // pragma: allowlist secret
|
|
requestIds: {
|
|
realtimeSessionId: "realtime-test",
|
|
sessionId: "session-test",
|
|
threadId: "thread-test",
|
|
},
|
|
sdp: "v=0\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\n",
|
|
session: { type: "realtime", model: "gpt-realtime-2.1" },
|
|
gaSideband: true,
|
|
onCallAllocated,
|
|
fetchImpl: fetchImpl as typeof fetch,
|
|
},
|
|
openAIRealtimeHost,
|
|
),
|
|
).resolves.toEqual({
|
|
kind: "ga-sideband",
|
|
answerSdp: "v=0\r\na=answer\r\n",
|
|
callId: "call_test-123",
|
|
sidebandUrl: "wss://api.openai.com/v1/realtime?call_id=call_test-123",
|
|
status: 201,
|
|
});
|
|
expect(onCallAllocated).toHaveBeenCalledExactlyOnceWith("call_test-123");
|
|
});
|
|
|
|
it.each([
|
|
[null, "missing"],
|
|
["https://attacker.example/v1/realtime/calls/rtc_test", "unexpected target"],
|
|
["/v1/files/rtc_test", "no valid call id"],
|
|
["/v1/realtime/calls/bad.id", "no valid call id"],
|
|
[`/v1/realtime/calls/${"x".repeat(129)}`, "no valid call id"],
|
|
[`/v1/realtime/calls/rtc_test?${"x".repeat(600)}`, "too large"],
|
|
])("rejects an untrusted Location header", async (location, message) => {
|
|
const onCallAllocated = vi.fn();
|
|
await expect(
|
|
createOpenAIQuicksilverCall(
|
|
{
|
|
auth: { type: "api-key", token: "sk-platform" }, // pragma: allowlist secret
|
|
requestIds: {
|
|
realtimeSessionId: "realtime-location",
|
|
sessionId: "session-location",
|
|
threadId: "thread-location",
|
|
},
|
|
sdp: "v=0\r\n",
|
|
session: { type: "realtime", model: "gpt-realtime-2.1" },
|
|
gaSideband: true,
|
|
onCallAllocated,
|
|
fetchImpl: vi.fn(
|
|
async () =>
|
|
new Response("v=answer\r\n", {
|
|
status: 201,
|
|
...(location === null ? {} : { headers: { Location: location } }),
|
|
}),
|
|
) as typeof fetch,
|
|
},
|
|
openAIRealtimeHost,
|
|
),
|
|
).rejects.toThrow(message);
|
|
expect(onCallAllocated).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("constructs and validates the sideband URL locally", () => {
|
|
expect(buildOpenAIRealtimeSidebandUrl("call-a_b_1")).toBe(
|
|
"wss://api.openai.com/v1/realtime?call_id=call-a_b_1",
|
|
);
|
|
expect(() => buildOpenAIRealtimeSidebandUrl("https://attacker.example")).toThrow("invalid");
|
|
});
|
|
|
|
it.each([200, 404, 503])("releases the hangup response body for HTTP %s", async (status) => {
|
|
const cancel = vi.fn();
|
|
const fetchImpl = vi.fn(async () => new Response(new ReadableStream({ cancel }), { status }));
|
|
const hangingUp = hangupOpenAIRealtimeCall(
|
|
{
|
|
apiKey: "sk-platform", // pragma: allowlist secret
|
|
callId: "call_test-123",
|
|
fetchImpl: fetchImpl as typeof fetch,
|
|
},
|
|
openAIRealtimeHost,
|
|
);
|
|
if (status === 503) {
|
|
await expect(hangingUp).rejects.toThrow("hangup failed (503)");
|
|
} else {
|
|
await hangingUp;
|
|
}
|
|
expect(cancel).toHaveBeenCalledOnce();
|
|
expect(fetchImpl).toHaveBeenCalledWith(
|
|
"https://api.openai.com/v1/realtime/calls/call_test-123/hangup",
|
|
expect.objectContaining({ method: "POST" }),
|
|
);
|
|
});
|
|
|
|
it("redacts bounded provider error details", async () => {
|
|
const onCallAllocated = vi.fn();
|
|
const leaked = "sk-proj-abcdefghijklmnopqrstuvwxyz1234567890";
|
|
const fetchImpl = vi.fn(
|
|
async () => new Response(`request rejected Authorization: Bearer ${leaked}`, { status: 403 }),
|
|
);
|
|
let error: unknown;
|
|
try {
|
|
await createOpenAIQuicksilverCall(
|
|
{
|
|
auth: { type: "api-key", token: "sk-platform" }, // pragma: allowlist secret
|
|
requestIds: {
|
|
realtimeSessionId: "realtime-error",
|
|
sessionId: "session-error",
|
|
threadId: "thread-error",
|
|
},
|
|
sdp: "v=0\r\n",
|
|
session: { type: "realtime", model: "gpt-realtime-2.1" },
|
|
gaSideband: true,
|
|
onCallAllocated,
|
|
fetchImpl: fetchImpl as typeof fetch,
|
|
},
|
|
openAIRealtimeHost,
|
|
);
|
|
} catch (caught) {
|
|
error = caught;
|
|
}
|
|
expect(error).toBeInstanceOf(Error);
|
|
expect((error as Error).message).toContain("OpenAI Realtime call creation failed (403)");
|
|
expect((error as Error).message).not.toContain(leaked);
|
|
expect(onCallAllocated).not.toHaveBeenCalled();
|
|
});
|
|
});
|