import { UnsupportedMediaOperationError } from "@rakazo/adapter-kit"; import { MediaInferenceOperationSchema } from "@rakazo/contracts"; import { describe, expect, it } from "vitest"; import { MediaSidecarError, SidecarMediaInferenceProvider } from "./media-sidecar.js"; const TOKEN = "test-token-0123456789abcdef0123456789abcdef"; const SCENES = { shots: [{ startMs: 0, endMs: 1000, score: 1 }], scenes: [{ startMs: 0, endMs: 1000 }], }; interface Call { url: string; init: RequestInit; } function json(status: number, body: unknown): Response { return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" }, }); } function fakeFetch(responses: Array Promise)>) { const calls: Call[] = []; const impl = (async (input: string | URL | Request, init?: RequestInit) => { calls.push({ url: String(input), init: init ?? {} }); const next = responses.shift(); if (!next) throw new Error("unexpected request"); return typeof next === "function" ? next(init ?? {}) : next; }) as typeof fetch; return { impl, calls }; } function client(fetchImpl: typeof fetch, extra: { retryBaseMs?: number } = {}) { return new SidecarMediaInferenceProvider({ baseUrl: "http://127.0.0.1:8800/", token: TOKEN, fetch: fetchImpl, retryBaseMs: 0, ...extra, }); } describe("SidecarMediaInferenceProvider", () => { it("posts camelCase JSON with bearer auth and without the signal", async () => { const { impl, calls } = fakeFetch([json(200, SCENES)]); const controller = new AbortController(); const result = await client(impl).detectScenes({ path: "/data/media/a.mp4", signal: controller.signal, }); expect(result).toEqual(SCENES); expect(calls).toHaveLength(1); const [call] = calls; expect(call?.url).toBe("http://127.0.0.1:8800/v1/detectScenes"); expect(call?.init.method).toBe("POST"); const headers = call?.init.headers as Record; expect(headers.authorization).toBe(`Bearer ${TOKEN}`); expect(headers["content-type"]).toBe("application/json"); expect(JSON.parse(String(call?.init.body))).toEqual({ path: "/data/media/a.mp4" }); expect(call?.init.signal).toBeInstanceOf(AbortSignal); }); it("reads capabilities with GET", async () => { const capabilities = { configured: true, operations: Object.fromEntries( MediaInferenceOperationSchema.options.map((operation) => [ operation, { available: operation !== "synthesizeIdentity", experimental: false, model: "m", license: "Apache-2.0", commercialUse: true, }, ]), ), accelerator: "cpu", hardwareEncoders: [], }; const { impl, calls } = fakeFetch([json(200, capabilities)]); await expect(client(impl).capabilities()).resolves.toEqual(capabilities); expect(calls[0]?.url).toBe("http://127.0.0.1:8800/v1/capabilities"); expect(calls[0]?.init.method).toBe("GET"); }); it("maps 501 to UnsupportedMediaOperationError", async () => { const { impl } = fakeFetch([ json(501, { error: { code: "unsupported", message: "no licensed model", operation: "inpaintVideo" }, }), ]); const error = await client(impl) .inpaintVideo({ path: "/m/a.mp4", maskDir: "/m/masks", outPath: "/m/out.mp4" }) .catch((caught: unknown) => caught); expect(error).toBeInstanceOf(UnsupportedMediaOperationError); expect((error as UnsupportedMediaOperationError).operation).toBe("inpaintVideo"); expect((error as Error).message).toContain("no licensed model"); }); it("retries 503 busy with backoff, at most three times", async () => { const busy = () => json(503, { error: { code: "busy", message: "queue full" } }); const recovered = fakeFetch([busy(), busy(), json(200, SCENES)]); await expect(client(recovered.impl).detectScenes({ path: "/m/a.mp4" })).resolves.toEqual( SCENES, ); expect(recovered.calls).toHaveLength(3); const exhausted = fakeFetch([busy(), busy(), busy(), busy(), busy()]); const error = await client(exhausted.impl) .detectScenes({ path: "/m/a.mp4" }) .catch((caught: unknown) => caught); expect(exhausted.calls).toHaveLength(4); expect(error).toBeInstanceOf(MediaSidecarError); expect((error as MediaSidecarError).code).toBe("busy"); expect((error as MediaSidecarError).status).toBe(503); }); it("maps typed sidecar errors", async () => { const { impl } = fakeFetch([ json(400, { error: { code: "invalid_path", message: "path is outside MEDIA_ROOT" } }), json(500, "not json-shaped"), ]); const provider = client(impl); await expect(provider.detectScenes({ path: "/etc/passwd" })).rejects.toMatchObject({ name: "MediaSidecarError", code: "invalid_path", status: 400, operation: "detectScenes", }); await expect(provider.detectScenes({ path: "/m/a.mp4" })).rejects.toMatchObject({ code: "http_error", status: 500, }); }); it("passes caller aborts through as AbortError", async () => { const controller = new AbortController(); const { impl } = fakeFetch([ (init) => new Promise((_, reject) => { init.signal?.addEventListener("abort", () => reject(init.signal?.reason)); setTimeout(() => controller.abort(), 10); }), ]); await expect( client(impl).trackFaces({ path: "/m/a.mp4", sampleFps: 5, signal: controller.signal }), ).rejects.toMatchObject({ name: "AbortError" }); const already = new AbortController(); already.abort(); const idle = fakeFetch([]); await expect( client(idle.impl).detectScenes({ path: "/m/a.mp4", signal: already.signal }), ).rejects.toMatchObject({ name: "AbortError" }); expect(idle.calls).toHaveLength(0); }); it("reports a timeout as a typed error", async () => { const { impl } = fakeFetch([ (init) => new Promise((_, reject) => { init.signal?.addEventListener("abort", () => reject(init.signal?.reason)); }), ]); const provider = new SidecarMediaInferenceProvider({ baseUrl: "http://127.0.0.1:8800", token: TOKEN, fetch: impl, timeoutsMs: { detectScenes: 20 }, }); await expect(provider.detectScenes({ path: "/m/a.mp4" })).rejects.toMatchObject({ code: "timeout", }); }); it("rejects malformed responses", async () => { const { impl } = fakeFetch([ json(200, { shots: [{ startMs: -1, endMs: 10, score: 1 }], scenes: [] }), json(200, { frames: [{ tMs: 0, faces: [{ box: { x: 2, y: 0, w: 0.1, h: 0.1 }, confidence: 1 }] }], }), json(200, { model: "m", dim: 3, vectors: [[1, 2]] }), new Response("", { status: 200 }), ]); const provider = client(impl); await expect(provider.detectScenes({ path: "/m/a.mp4" })).rejects.toMatchObject({ code: "malformed_response", }); await expect(provider.detectFaces({ path: "/m/a.mp4", sampleFps: 1 })).rejects.toMatchObject({ code: "malformed_response", }); await expect(provider.embedFrames({ path: "/m/a.mp4", timesMs: [0] })).rejects.toMatchObject({ code: "malformed_response", }); await expect(provider.detectScenes({ path: "/m/a.mp4" })).rejects.toMatchObject({ code: "malformed_response", }); }); it("never puts the token into error messages", async () => { const { impl } = fakeFetch([ json(401, { error: { code: "unauthorized", message: `bad token ${TOKEN}` } }), json(500, { error: { code: "internal", message: `crashed with Bearer ${TOKEN}` } }), async () => { throw new TypeError(`connect ECONNREFUSED (auth ${TOKEN})`); }, ]); const provider = client(impl); for (let i = 0; i < 3; i++) { const error = (await provider .detectScenes({ path: "/m/a.mp4" }) .catch((caught: unknown) => caught)) as Error; expect(error).toBeInstanceOf(MediaSidecarError); expect(error.message).not.toContain(TOKEN); expect(JSON.stringify(error)).not.toContain(TOKEN); expect(String(error.stack)).not.toContain(TOKEN); } }); });