@@ -8,6 +8,8 @@ vi.mock("openclaw/plugin-sdk/media-runtime", () => ({
|
8 | 8 | |
9 | 9 | import { buildXiaomiSpeechProvider } from "./speech-provider.js"; |
10 | 10 | |
| 11 | +const MAX_TIMER_TIMEOUT_MS = 2_147_000_000; |
| 12 | + |
11 | 13 | describe("buildXiaomiSpeechProvider", () => { |
12 | 14 | const provider = buildXiaomiSpeechProvider(); |
13 | 15 | |
@@ -208,6 +210,37 @@ describe("buildXiaomiSpeechProvider", () => {
|
208 | 210 | }); |
209 | 211 | }); |
210 | 212 | |
| 213 | +it("caps oversized TTS request timeouts before scheduling or fetching", async () => { |
| 214 | +const audio = Buffer.from("fake-mp3-audio").toString("base64"); |
| 215 | +const timeoutSpy = vi |
| 216 | +.spyOn(globalThis, "setTimeout") |
| 217 | +.mockImplementation((() => 1) as typeof setTimeout); |
| 218 | +const clearTimeoutSpy = vi |
| 219 | +.spyOn(globalThis, "clearTimeout") |
| 220 | +.mockImplementation(() => undefined); |
| 221 | +vi.mocked(globalThis.fetch).mockResolvedValueOnce( |
| 222 | +new Response(JSON.stringify({ choices: [{ message: { audio: { data: audio } } }] }), { |
| 223 | +status: 200, |
| 224 | +headers: { "Content-Type": "application/json" }, |
| 225 | +}), |
| 226 | +); |
| 227 | + |
| 228 | +try { |
| 229 | +await provider.synthesize({ |
| 230 | +text: "Hello from OpenClaw.", |
| 231 | +cfg: {} as never, |
| 232 | +providerConfig: { apiKey: "sk-test" }, |
| 233 | +target: "audio-file", |
| 234 | +timeoutMs: MAX_TIMER_TIMEOUT_MS + 1_000_000, |
| 235 | +}); |
| 236 | + |
| 237 | +expect(timeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS); |
| 238 | +} finally { |
| 239 | +timeoutSpy.mockRestore(); |
| 240 | +clearTimeoutSpy.mockRestore(); |
| 241 | +} |
| 242 | +}); |
| 243 | + |
211 | 244 | it("throws when API key is missing", async () => { |
212 | 245 | const savedKey = process.env.XIAOMI_API_KEY; |
213 | 246 | delete process.env.XIAOMI_API_KEY; |
|