import { installPinnedHostnameTestHooks } from
"openclaw/plugin-sdk/testing";
import { afterEach, describe, expect, it, vi } from
"vitest";
import { buildGradiumSpeechProvider } from
"./speech-provider.js";
describe(
"gradium speech provider", () => {
installPinnedHostnameTestHooks();
const provider = buildGradiumSpeechProvider();
afterEach(() => {
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
it(
"reports configured when GRADIUM_API_KEY is set", () => {
const original = process.env.GRADIUM_API_KEY;
try {
process.env.GRADIUM_API_KEY =
"gsk_test";
expect(provider.isConfigured({ providerConfig: {}, timeoutMs:
5_
000 })).toBe(
true);
}
finally {
if (original === undefined) {
delete process.env.GRADIUM_API_KEY;
}
else {
process.env.GRADIUM_API_KEY = original;
}
}
});
it(
"reports not configured when no key is available", () => {
const original = process.env.GRADIUM_API_KEY;
try {
delete process.env.GRADIUM_API_KEY;
expect(provider.isConfigured({ providerConfig: {}, timeoutMs:
5_
000 })).toBe(
false);
}
finally {
if (original !== undefined) {
process.env.GRADIUM_API_KEY = original;
}
}
});
it(
"synthesizes audio via the Gradium TTS endpoint", async () => {
const audioData = Buffer.from(
"wav-audio-data");
const fetchMock = vi.fn().mockResolvedValue(
new Response(audioData, { status:
200 }));
vi.stubGlobal(
"fetch", fetchMock);
const result = await provider.synthesize({
text:
"OpenClaw test",
cfg: {} as never,
providerConfig: { apiKey:
"gsk_test123" },
target:
"audio-file",
timeoutMs:
30_
000,
});
expect(fetchMock).toHaveBeenCalledOnce();
const [url, init] = fetchMock.mock.calls[
0] as [string, RequestInit];
expect(url).toBe(
"https://api.gradium.ai/api/post/speech/tts");
const headers =
new Headers(init.headers);
expect(headers.get(
"x-api-key")).toBe(
"gsk_test123");
expect(JSON.parse(init.body as string)).toEqual({
text:
"OpenClaw test",
voice_id:
"YTpq7expH9539ERJ",
only_audio:
true,
output_format:
"wav",
json_config:
'{"padding_bonus":0}',
});
expect(result.outputFormat).toBe(
"wav");
expect(result.fileExtension).toBe(
".wav");
expect(result.voiceCompatible).toBe(
false);
expect(result.audioBuffer).toEqual(audioData);
});
it(
"uses opus and voiceCompatible for voice-note target", async () => {
const audioData = Buffer.from(
"opus-audio-data");
const fetchMock = vi.fn().mockResolvedValue(
new Response(audioData, { status:
200 }));
vi.stubGlobal(
"fetch", fetchMock);
const result = await provider.synthesize({
text:
"Voice note test",
cfg: {} as never,
providerConfig: { apiKey:
"gsk_test123" },
target:
"voice-note",
timeoutMs:
30_
000,
});
const [, init] = fetchMock.mock.calls[
0] as [string, RequestInit];
expect(JSON.parse(init.body as string).output_format).toBe(
"opus");
expect(result.outputFormat).toBe(
"opus");
expect(result.fileExtension).toBe(
".opus");
expect(result.voiceCompatible).toBe(
true);
expect(result.audioBuffer).toEqual(audioData);
});
it(
"uses ulaw_8000 for telephony synthesis", async () => {
const audioData = Buffer.from(
"ulaw-audio-data");
const fetchMock = vi.fn().mockResolvedValue(
new Response(audioData, { status:
200 }));
vi.stubGlobal(
"fetch", fetchMock);
const result = await provider.synthesizeTelephony!({
text:
"Telephony test",
cfg: {} as never,
providerConfig: { apiKey:
"gsk_test123" },
timeoutMs:
30_
000,
});
const [, init] = fetchMock.mock.calls[
0] as [string, RequestInit];
expect(JSON.parse(init.body as string).output_format).toBe(
"ulaw_8000");
expect(result.outputFormat).toBe(
"ulaw_8000");
expect(result.sampleRate).toBe(
8_
000);
expect(result.audioBuffer).toEqual(audioData);
});
it(
"throws when no API key is available", async () => {
const original = process.env.GRADIUM_API_KEY;
try {
delete process.env.GRADIUM_API_KEY;
await expect(
provider.synthesize({
text:
"test",
cfg: {} as never,
providerConfig: {},
target:
"audio-file",
timeoutMs:
5_
000,
}),
).rejects.toThrow(
"Gradium API key missing");
}
finally {
if (original !== undefined) {
process.env.GRADIUM_API_KEY = original;
}
}
});
});