Skip to content

Commit 37962aa

Browse files
committed
test(qqbot): keep stt temp helper on sdk surface
1 parent a876f8d commit 37962aa

1 file changed

Lines changed: 75 additions & 76 deletions

File tree

extensions/qqbot/src/engine/utils/stt.test.ts

Lines changed: 75 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import * as fs from "node:fs";
33
import * as path from "node:path";
44
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5-
import { createTempDirTracker } from "../../../../../test/helpers/temp-dir.js";
5+
import { withTempDir } from "openclaw/plugin-sdk/test-env";
66

77
const ssrfRuntimeMocks = vi.hoisted(() => ({
88
fetchWithSsrFGuard: vi.fn(),
@@ -19,8 +19,6 @@ afterAll(() => {
1919

2020
import { resolveSTTConfig, transcribeAudio } from "./stt.js";
2121

22-
const tempDirs = createTempDirTracker();
23-
2422
function cancelTrackedResponse(
2523
text: string,
2624
init: ResponseInit,
@@ -71,7 +69,6 @@ describe("engine/utils/stt", () => {
7169
});
7270

7371
afterEach(() => {
74-
tempDirs.cleanup();
7572
ssrfRuntimeMocks.fetchWithSsrFGuard.mockReset();
7673
vi.unstubAllGlobals();
7774
});
@@ -135,70 +132,19 @@ describe("engine/utils/stt", () => {
135132
});
136133

137134
it("posts audio to OpenAI-compatible transcription endpoint", async () => {
138-
const tmpDir = tempDirs.make("openclaw-qqbot-stt-");
139-
const audioPath = path.join(tmpDir, "voice.wav");
140-
fs.writeFileSync(audioPath, Buffer.from([1, 2, 3, 4]));
141-
142-
const release = vi.fn(async () => {});
143-
ssrfRuntimeMocks.fetchWithSsrFGuard.mockResolvedValueOnce({
144-
response: Response.json({
145-
text: "hello from audio",
146-
}),
147-
release,
148-
});
149-
150-
const transcript = await transcribeAudio(audioPath, {
151-
channels: {
152-
qqbot: {
153-
stt: {
154-
baseUrl: "https://api.example.test/v1/",
155-
apiKey: "secret",
156-
model: "whisper-1",
157-
},
158-
},
159-
},
160-
});
161-
162-
expect(transcript).toBe("hello from audio");
163-
expect(ssrfRuntimeMocks.fetchWithSsrFGuard).toHaveBeenCalledTimes(1);
164-
const request = requireFirstSsrfRequest();
165-
expect(request.url).toBe("https://api.example.test/v1/audio/transcriptions");
166-
expect(request.auditContext).toBe("qqbot-stt");
167-
expect(request.init?.method).toBe("POST");
168-
expect(request.init?.headers).toEqual({ Authorization: "Bearer secret" });
169-
expect(request.init?.body).toBeInstanceOf(FormData);
170-
const body = request.init?.body as FormData;
171-
expect(body.get("model")).toBe("whisper-1");
172-
const file = body.get("file");
173-
expect(file).toBeInstanceOf(File);
174-
expect((file as File).name).toBe("voice.wav");
175-
expect((file as File).type).toBe("audio/wav");
176-
expect(new Uint8Array(await (file as File).arrayBuffer())).toEqual(
177-
new Uint8Array([1, 2, 3, 4]),
178-
);
179-
expect(release).toHaveBeenCalledTimes(1);
180-
});
181-
182-
it("bounds STT error bodies without using response.text()", async () => {
183-
const tmpDir = tempDirs.make("openclaw-qqbot-stt-error-");
184-
const audioPath = path.join(tmpDir, "voice.wav");
185-
fs.writeFileSync(audioPath, Buffer.from([1, 2, 3, 4]));
186-
187-
const release = vi.fn(async () => {});
188-
const tracked = cancelTrackedResponse(`${"stt provider unavailable ".repeat(1024)}tail`, {
189-
status: 503,
190-
statusText: "Service Unavailable",
191-
headers: { "content-type": "text/plain" },
192-
});
193-
const textSpy = vi.spyOn(tracked.response, "text").mockRejectedValue(new Error("unbounded"));
194-
ssrfRuntimeMocks.fetchWithSsrFGuard.mockResolvedValueOnce({
195-
response: tracked.response,
196-
release,
197-
});
135+
await withTempDir("openclaw-qqbot-stt-", async (tmpDir) => {
136+
const audioPath = path.join(tmpDir, "voice.wav");
137+
fs.writeFileSync(audioPath, Buffer.from([1, 2, 3, 4]));
138+
139+
const release = vi.fn(async () => {});
140+
ssrfRuntimeMocks.fetchWithSsrFGuard.mockResolvedValueOnce({
141+
response: Response.json({
142+
text: "hello from audio",
143+
}),
144+
release,
145+
});
198146

199-
let error: unknown;
200-
try {
201-
await transcribeAudio(audioPath, {
147+
const transcript = await transcribeAudio(audioPath, {
202148
channels: {
203149
qqbot: {
204150
stt: {
@@ -209,14 +155,67 @@ describe("engine/utils/stt", () => {
209155
},
210156
},
211157
});
212-
} catch (caught) {
213-
error = caught;
214-
}
215-
216-
expect(String(error)).toContain("STT failed (HTTP 503): stt provider unavailable");
217-
expect(String(error)).not.toContain("tail");
218-
expect(tracked.wasCanceled()).toBe(true);
219-
expect(textSpy).not.toHaveBeenCalled();
220-
expect(release).toHaveBeenCalledTimes(1);
158+
159+
expect(transcript).toBe("hello from audio");
160+
expect(ssrfRuntimeMocks.fetchWithSsrFGuard).toHaveBeenCalledTimes(1);
161+
const request = requireFirstSsrfRequest();
162+
expect(request.url).toBe("https://api.example.test/v1/audio/transcriptions");
163+
expect(request.auditContext).toBe("qqbot-stt");
164+
expect(request.init?.method).toBe("POST");
165+
expect(request.init?.headers).toEqual({ Authorization: "Bearer secret" });
166+
expect(request.init?.body).toBeInstanceOf(FormData);
167+
const body = request.init?.body as FormData;
168+
expect(body.get("model")).toBe("whisper-1");
169+
const file = body.get("file");
170+
expect(file).toBeInstanceOf(File);
171+
expect((file as File).name).toBe("voice.wav");
172+
expect((file as File).type).toBe("audio/wav");
173+
expect(new Uint8Array(await (file as File).arrayBuffer())).toEqual(
174+
new Uint8Array([1, 2, 3, 4]),
175+
);
176+
expect(release).toHaveBeenCalledTimes(1);
177+
});
178+
});
179+
180+
it("bounds STT error bodies without using response.text()", async () => {
181+
await withTempDir("openclaw-qqbot-stt-error-", async (tmpDir) => {
182+
const audioPath = path.join(tmpDir, "voice.wav");
183+
fs.writeFileSync(audioPath, Buffer.from([1, 2, 3, 4]));
184+
185+
const release = vi.fn(async () => {});
186+
const tracked = cancelTrackedResponse(`${"stt provider unavailable ".repeat(1024)}tail`, {
187+
status: 503,
188+
statusText: "Service Unavailable",
189+
headers: { "content-type": "text/plain" },
190+
});
191+
const textSpy = vi.spyOn(tracked.response, "text").mockRejectedValue(new Error("unbounded"));
192+
ssrfRuntimeMocks.fetchWithSsrFGuard.mockResolvedValueOnce({
193+
response: tracked.response,
194+
release,
195+
});
196+
197+
let error: unknown;
198+
try {
199+
await transcribeAudio(audioPath, {
200+
channels: {
201+
qqbot: {
202+
stt: {
203+
baseUrl: "https://api.example.test/v1/",
204+
apiKey: "secret",
205+
model: "whisper-1",
206+
},
207+
},
208+
},
209+
});
210+
} catch (caught) {
211+
error = caught;
212+
}
213+
214+
expect(String(error)).toContain("STT failed (HTTP 503): stt provider unavailable");
215+
expect(String(error)).not.toContain("tail");
216+
expect(tracked.wasCanceled()).toBe(true);
217+
expect(textSpy).not.toHaveBeenCalled();
218+
expect(release).toHaveBeenCalledTimes(1);
219+
});
221220
});
222221
});

0 commit comments

Comments
 (0)