Test your speech integration without spending quota
Mock the API in unit tests, and run one tiny live smoke test that reads the key from the environment and never prints it.
Two layers: fast tests with a fake API, and one live check you run on demand.
1. Unit test with a fake fetch (Node 18+)
say.mjs
export async function say(text, fetchImpl = fetch, key = process.env.SPEAKVORA_KEY) {
const r = await fetchImpl("https://speakvora.com/api/v1/speak", {
method: "POST",
headers: { "x-api-key": key, "content-type": "application/json" },
body: JSON.stringify({ text, voice: "af_heart", lang: "en" }),
});
const j = await r.json();
if (!r.ok) throw new Error(j.error);
return j.url;
}say.test.mjs
import test from "node:test";
import assert from "node:assert/strict";
import { say } from "./say.mjs";
test("returns the audio url", async () => {
const fake = async () => ({ ok: true, json: async () => ({ url: "https://example.test/a.m4a" }) });
assert.equal(await say("Hello.", fake, "test-key"), "https://example.test/a.m4a");
});
test("throws the API error code", async () => {
const fake = async () => ({ ok: false, json: async () => ({ error: "free_allowance_used" }) });
await assert.rejects(() => say("Hello.", fake, "test-key"), /free_allowance_used/);
});Run
node --test2. One live smoke test
smoke.mjs
import { say } from "./say.mjs";
if (!process.env.SPEAKVORA_KEY) { console.error("Set SPEAKVORA_KEY first"); process.exit(1); }
const url = await say("Smoke test.");
const head = await fetch(url, { method: "HEAD" });
console.log(head.ok ? "ok: audio link reachable" : "FAIL: link returned " + head.status);Run it only when you change the integration. It uses a few characters of your free allowance and never prints the key. In CI, store the key as a secret and run the smoke test on a schedule, not on every commit.