diff --git a/src/app/api/models/test/ping.js b/src/app/api/models/test/ping.js index 2c3439c5..5b7ee8a0 100644 --- a/src/app/api/models/test/ping.js +++ b/src/app/api/models/test/ping.js @@ -34,7 +34,7 @@ function createSilentWavFile() { writeAscii(36, "data"); view.setUint32(40, dataSize, true); - return new File([buffer], "test.wav", { type: "audio/wav" }); + return new Blob([buffer], { type: "audio/wav" }); } async function getInternalHeaders() { @@ -104,7 +104,7 @@ export async function pingModelByKind(model, kind, baseUrl = `http://127.0.0.1:$ if (kind === "stt") { const form = new FormData(); const sampleAudio = createSilentWavFile(); - form.append("file", sampleAudio); + form.append("file", sampleAudio, "test.wav"); form.append("model", model); const res = await fetch(`${baseUrl}/api/v1/audio/transcriptions`, { diff --git a/tests/unit/model-test-routing.test.js b/tests/unit/model-test-routing.test.js index 20c87eba..3eaa5723 100644 --- a/tests/unit/model-test-routing.test.js +++ b/tests/unit/model-test-routing.test.js @@ -72,6 +72,67 @@ describe("model test route kind routing", () => { ); }); + it("routes embedding model tests to /api/v1/embeddings", async () => { + global.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ + data: [{ embedding: [0.1, 0.2] }], + }), { + status: 200, + headers: { "Content-Type": "application/json" }, + })); + + const { POST } = await import("../../src/app/api/models/test/route.js"); + + const req = new Request("http://localhost/api/models/test", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + model: "voyage/voyage-3-large", + kind: "embedding", + }), + }); + + const res = await POST(req); + const body = await res.json(); + + expect(body.ok).toBe(true); + expect(global.fetch).toHaveBeenCalledWith( + expect.stringContaining("/api/v1/embeddings"), + expect.objectContaining({ + method: "POST", + body: JSON.stringify({ + model: "voyage/voyage-3-large", + input: "test", + }), + }) + ); + }); + + it("fails embedding model tests when provider returns no embedding data", async () => { + global.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ + data: [{ embedding: null }], + }), { + status: 200, + headers: { "Content-Type": "application/json" }, + })); + + const { POST } = await import("../../src/app/api/models/test/route.js"); + + const req = new Request("http://localhost/api/models/test", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + model: "voyage/voyage-3-large", + kind: "embedding", + }), + }); + + const res = await POST(req); + const body = await res.json(); + + expect(body.ok).toBe(false); + expect(body.error).toBe("Provider returned no embedding data"); + }); + it("routes stt model tests to /api/v1/audio/transcriptions", async () => { global.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ text: "test", @@ -103,4 +164,31 @@ describe("model test route kind routing", () => { }) ); }); + + it("returns formatted HTTP errors for non-2xx embedding responses", async () => { + global.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ + error: { message: "bad upstream" }, + }), { + status: 502, + headers: { "Content-Type": "application/json" }, + })); + + const { POST } = await import("../../src/app/api/models/test/route.js"); + + const req = new Request("http://localhost/api/models/test", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + model: "voyage/voyage-3-large", + kind: "embedding", + }), + }); + + const res = await POST(req); + const body = await res.json(); + + expect(body.ok).toBe(false); + expect(body.status).toBe(502); + expect(body.error).toBe("HTTP 502: bad upstream"); + }); });