
























@@ -2,7 +2,7 @@
22import * as fs from "node:fs";
33import * as path from "node:path";
44import { 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";
6677const ssrfRuntimeMocks = vi.hoisted(() => ({
88fetchWithSsrFGuard: vi.fn(),
@@ -19,8 +19,6 @@ afterAll(() => {
19192020import { resolveSTTConfig, transcribeAudio } from "./stt.js";
212122-const tempDirs = createTempDirTracker();
23-2422function cancelTrackedResponse(
2523text: string,
2624init: ResponseInit,
@@ -71,7 +69,6 @@ describe("engine/utils/stt", () => {
7169});
72707371afterEach(() => {
74-tempDirs.cleanup();
7572ssrfRuntimeMocks.fetchWithSsrFGuard.mockReset();
7673vi.unstubAllGlobals();
7774});
@@ -135,70 +132,19 @@ describe("engine/utils/stt", () => {
135132});
136133137134it("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+});
198146199-let error: unknown;
200-try {
201-await transcribeAudio(audioPath, {
147+const transcript = await transcribeAudio(audioPath, {
202148channels: {
203149qqbot: {
204150stt: {
@@ -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});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。