import { describe, expect, it } from "vitest"; import { createTranscriptClient, type FetchLike } from "./client.js"; function jsonResponse(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" }, }); } interface CapturedCall { url: string; method?: string | undefined; } /** Builds a fake fetch that returns scripted responses in order, capturing each call. */ function makeFetch(responses: Response[]): { fetchFn: FetchLike; calls: CapturedCall[] } { const calls: CapturedCall[] = []; let i = 0; const fetchFn: FetchLike = (async (input: string | URL | Request, init?: RequestInit) => { const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url; calls.push({ url, method: init?.method }); return responses[i++] ?? jsonResponse({}); }) as unknown as FetchLike; return { fetchFn, calls }; } const BASE = "http://test-transcriber.local"; const signal = (): AbortSignal => new AbortController().signal; describe("createTranscriptClient.getTranscript", () => { it("sends GET /api/transcript?url=...", async () => { const { fetchFn, calls } = makeFetch([ jsonResponse({ status: "completed", video_id: "v1", full_text: "", segments: [], }), ]); const client = createTranscriptClient({ baseUrl: BASE, fetchFn }); await client.getTranscript("https://youtu.be/v1", signal()); const call = calls[0]; if (!call) throw new Error("no call captured"); expect(call.url).toBe( `${BASE}/api/transcript?url=${encodeURIComponent("https://youtu.be/v1")}`, ); expect(call.method).toBe("GET"); }); it("returns completed response", async () => { const body = { status: "completed" as const, video_id: "v1", full_text: "hi", segments: [{ text: "hi", start: 0, duration: 1 }], }; const { fetchFn } = makeFetch([jsonResponse(body)]); const client = createTranscriptClient({ baseUrl: BASE, fetchFn }); const result = await client.getTranscript("https://youtu.be/v1", signal()); expect(result).toEqual(body); }); it("returns queued response", async () => { const body = { status: "queued" as const, video_id: "v1", position: 2, estimated_seconds: 60, }; const { fetchFn } = makeFetch([jsonResponse(body)]); const client = createTranscriptClient({ baseUrl: BASE, fetchFn }); const result = await client.getTranscript("https://youtu.be/v1", signal()); expect(result).toEqual(body); }); it("returns failed response", async () => { const body = { status: "failed" as const, video_id: "v1", error: "boom", error_type: "DownloadError", }; const { fetchFn } = makeFetch([jsonResponse(body)]); const client = createTranscriptClient({ baseUrl: BASE, fetchFn }); const result = await client.getTranscript("https://youtu.be/v1", signal()); expect(result).toEqual(body); }); it("throws on HTTP error", async () => { const { fetchFn } = makeFetch([ new Response("not found", { status: 404, statusText: "Not Found" }), ]); const client = createTranscriptClient({ baseUrl: BASE, fetchFn }); await expect(client.getTranscript("https://youtu.be/v1", signal())).rejects.toThrow("HTTP 404"); }); it("throws on timeout", async () => { const fetchFn: FetchLike = ((_input: string | URL | Request, init?: RequestInit) => new Promise((_resolve, reject) => { const sig = init?.signal; if (!sig) return; sig.addEventListener("abort", () => { const err = new Error("aborted"); err.name = "AbortError"; reject(err); }); })) as unknown as FetchLike; const client = createTranscriptClient({ baseUrl: BASE, fetchFn, timeoutMs: 10 }); await expect(client.getTranscript("https://youtu.be/v1", signal())).rejects.toThrow( "timed out", ); }); it("respects abort signal", async () => { const controller = new AbortController(); const fetchFn: FetchLike = ((_input: string | URL | Request, init?: RequestInit) => new Promise((_resolve, reject) => { const sig = init?.signal; if (!sig) return; sig.addEventListener("abort", () => { const err = new Error("aborted"); err.name = "AbortError"; reject(err); }); })) as unknown as FetchLike; const client = createTranscriptClient({ baseUrl: BASE, fetchFn, timeoutMs: 30_000 }); const promise = client.getTranscript("https://youtu.be/v1", controller.signal); controller.abort(); await expect(promise).rejects.toThrow("aborted"); }); });