Skip to content

Commit 0fe22fb

Browse files
committed
test: add non-speech audio generation test suites
6 new/modified test files covering ElevenLabs sound/music, fal.ai queue lifecycle, Gemini HTTP audio (streaming + non-streaming), Gemini WebSocket audio, audio recording/replay, and multimedia type guard + endpoint filtering.
1 parent 4787781 commit 0fe22fb

6 files changed

Lines changed: 1282 additions & 1 deletion
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import { describe, test, expect, afterEach } from "vitest";
2+
import { LLMock } from "../llmock.js";
3+
4+
describe("ElevenLabs sound generation", () => {
5+
let mock: LLMock;
6+
7+
afterEach(async () => {
8+
await mock?.stop();
9+
});
10+
11+
test("sound generation with string-form audio returns binary", async () => {
12+
mock = new LLMock({ port: 0 });
13+
mock.addFixture({
14+
match: { userMessage: "castle door opening", endpoint: "audio-gen" },
15+
response: { audio: "SGVsbG8=", format: "mp3" },
16+
});
17+
await mock.start();
18+
19+
const res = await fetch(`${mock.url}/v1/sound-generation`, {
20+
method: "POST",
21+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
22+
body: JSON.stringify({ text: "castle door opening" }),
23+
});
24+
25+
expect(res.status).toBe(200);
26+
expect(res.headers.get("content-type")).toBe("audio/mpeg");
27+
const buffer = await res.arrayBuffer();
28+
expect(buffer.byteLength).toBeGreaterThan(0);
29+
// "SGVsbG8=" decodes to "Hello" (5 bytes)
30+
expect(buffer.byteLength).toBe(5);
31+
});
32+
33+
test("sound generation with object-form audio", async () => {
34+
mock = new LLMock({ port: 0 });
35+
mock.addFixture({
36+
match: { userMessage: "explosion", endpoint: "audio-gen" },
37+
response: { audio: { b64Json: "SGVsbG8=", contentType: "audio/wav" } },
38+
});
39+
await mock.start();
40+
41+
const res = await fetch(`${mock.url}/v1/sound-generation`, {
42+
method: "POST",
43+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
44+
body: JSON.stringify({ text: "explosion" }),
45+
});
46+
47+
expect(res.status).toBe(200);
48+
expect(res.headers.get("content-type")).toBe("audio/wav");
49+
const buffer = await res.arrayBuffer();
50+
expect(buffer.byteLength).toBe(5);
51+
});
52+
53+
test("missing text field returns 400", async () => {
54+
mock = new LLMock({ port: 0 });
55+
await mock.start();
56+
57+
const res = await fetch(`${mock.url}/v1/sound-generation`, {
58+
method: "POST",
59+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
60+
body: JSON.stringify({}),
61+
});
62+
63+
expect(res.status).toBe(400);
64+
const data = await res.json();
65+
expect(data.error.message).toContain("text");
66+
});
67+
68+
test("no matching fixture returns 404", async () => {
69+
mock = new LLMock({ port: 0 });
70+
mock.addFixture({
71+
match: { userMessage: "specific sound", endpoint: "audio-gen" },
72+
response: { audio: "SGVsbG8=" },
73+
});
74+
await mock.start();
75+
76+
const res = await fetch(`${mock.url}/v1/sound-generation`, {
77+
method: "POST",
78+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
79+
body: JSON.stringify({ text: "completely different sound" }),
80+
});
81+
82+
expect(res.status).toBe(404);
83+
});
84+
85+
test("error fixture returns error status", async () => {
86+
mock = new LLMock({ port: 0 });
87+
mock.addFixture({
88+
match: { userMessage: "rate limited", endpoint: "audio-gen" },
89+
response: { error: { message: "rate limit", type: "rate_limit_error" }, status: 429 },
90+
});
91+
await mock.start();
92+
93+
const res = await fetch(`${mock.url}/v1/sound-generation`, {
94+
method: "POST",
95+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
96+
body: JSON.stringify({ text: "rate limited" }),
97+
});
98+
99+
expect(res.status).toBe(429);
100+
const data = await res.json();
101+
expect(data.error.message).toBe("rate limit");
102+
});
103+
});
104+
105+
describe("ElevenLabs music", () => {
106+
let mock: LLMock;
107+
108+
afterEach(async () => {
109+
await mock?.stop();
110+
});
111+
112+
test("music compose returns binary audio with song-id header", async () => {
113+
mock = new LLMock({ port: 0 });
114+
mock.addFixture({
115+
match: { userMessage: "upbeat piano", endpoint: "audio-gen" },
116+
response: { audio: "SGVsbG8=", format: "mp3" },
117+
});
118+
await mock.start();
119+
120+
const res = await fetch(`${mock.url}/v1/music`, {
121+
method: "POST",
122+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
123+
body: JSON.stringify({ prompt: "upbeat piano" }),
124+
});
125+
126+
expect(res.status).toBe(200);
127+
expect(res.headers.get("content-type")).toBe("audio/mpeg");
128+
expect(res.headers.get("song-id")).toBeTruthy();
129+
expect(res.headers.get("song-id")).toMatch(/^mock-song-/);
130+
const buffer = await res.arrayBuffer();
131+
expect(buffer.byteLength).toBe(5);
132+
});
133+
134+
test("music stream returns binary audio", async () => {
135+
mock = new LLMock({ port: 0 });
136+
mock.addFixture({
137+
match: { userMessage: "ambient drone", endpoint: "audio-gen" },
138+
response: { audio: "SGVsbG8=" },
139+
});
140+
await mock.start();
141+
142+
const res = await fetch(`${mock.url}/v1/music/stream`, {
143+
method: "POST",
144+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
145+
body: JSON.stringify({ prompt: "ambient drone" }),
146+
});
147+
148+
expect(res.status).toBe(200);
149+
const buffer = await res.arrayBuffer();
150+
expect(buffer.byteLength).toBe(5);
151+
});
152+
153+
test("music plan returns JSON text", async () => {
154+
mock = new LLMock({ port: 0 });
155+
const compositionPlan = JSON.stringify({ sections: ["intro", "verse", "chorus"] });
156+
mock.addFixture({
157+
match: { userMessage: "jazz song", endpoint: "audio-gen" },
158+
response: { content: compositionPlan },
159+
});
160+
await mock.start();
161+
162+
const res = await fetch(`${mock.url}/v1/music/plan`, {
163+
method: "POST",
164+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
165+
body: JSON.stringify({ prompt: "jazz song" }),
166+
});
167+
168+
expect(res.status).toBe(200);
169+
expect(res.headers.get("content-type")).toBe("application/json");
170+
const data = await res.json();
171+
expect(data.sections).toEqual(["intro", "verse", "chorus"]);
172+
});
173+
174+
test("missing prompt returns 400 for music", async () => {
175+
mock = new LLMock({ port: 0 });
176+
await mock.start();
177+
178+
const res = await fetch(`${mock.url}/v1/music`, {
179+
method: "POST",
180+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
181+
body: JSON.stringify({}),
182+
});
183+
184+
expect(res.status).toBe(400);
185+
const data = await res.json();
186+
expect(data.error.message).toContain("prompt");
187+
});
188+
});
189+
190+
describe("ElevenLabs convenience methods", () => {
191+
let mock: LLMock;
192+
193+
afterEach(async () => {
194+
await mock?.stop();
195+
});
196+
197+
test("onSoundEffect creates fixture with correct endpoint", async () => {
198+
mock = new LLMock({ port: 0 });
199+
mock.onSoundEffect("door", { audio: "SGVsbG8=" });
200+
await mock.start();
201+
202+
const res = await fetch(`${mock.url}/v1/sound-generation`, {
203+
method: "POST",
204+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
205+
body: JSON.stringify({ text: "door" }),
206+
});
207+
208+
expect(res.status).toBe(200);
209+
const buffer = await res.arrayBuffer();
210+
expect(buffer.byteLength).toBe(5);
211+
});
212+
213+
test("onMusic creates fixture with correct endpoint", async () => {
214+
mock = new LLMock({ port: 0 });
215+
mock.onMusic("piano", { audio: "SGVsbG8=" });
216+
await mock.start();
217+
218+
const res = await fetch(`${mock.url}/v1/music`, {
219+
method: "POST",
220+
headers: { "Content-Type": "application/json", Authorization: "Bearer test" },
221+
body: JSON.stringify({ prompt: "piano" }),
222+
});
223+
224+
expect(res.status).toBe(200);
225+
expect(res.headers.get("song-id")).toBeTruthy();
226+
const buffer = await res.arrayBuffer();
227+
expect(buffer.byteLength).toBe(5);
228+
});
229+
});

0 commit comments

Comments
 (0)