import { ATTACHMENT_MAX_BYTES } from "@rakazo/contracts"; import { afterEach, describe, expect, it, vi } from "vitest"; vi.mock("@lingui/core/macro", () => ({ t: (parts: TemplateStringsArray, ...values: unknown[]) => parts.reduce( (text, part, index) => text + part + (index < values.length ? values[index] : ""), "", ), })); const create = vi.hoisted(() => vi.fn()); vi.mock("./rpc", () => ({ rpc: { artifacts: { create } }, withSpaceHeaders: (init: HeadersInit) => new Headers(init), })); import { attachmentMaxBytes, uploadArtifactFile, uploadAttachment, usesStreamingUpload, } from "./artifact-upload"; const artifact = { id: "art-1", botId: "bot-1", groupId: null, runId: null, name: "clip.mp4", mimeType: "video/mp4", size: 4, createdAt: "2026-10-03T12:00:00.000Z", }; afterEach(() => { vi.unstubAllGlobals(); create.mockReset(); }); describe("artifact uploads", () => { it("streams media and large files and keeps small files on the inline RPC", () => { expect(usesStreamingUpload(10, "video/mp4")).toBe(true); expect(usesStreamingUpload(10, "audio/mpeg")).toBe(true); expect(usesStreamingUpload(10, "image/png")).toBe(false); expect(usesStreamingUpload(ATTACHMENT_MAX_BYTES + 1, "image/png")).toBe(true); expect(attachmentMaxBytes("video/mp4")).toBe(500 * 1024 * 1024); expect(attachmentMaxBytes("text/plain")).toBe(25 * 1024 * 1024); }); it("posts the raw file with its target, name and type", async () => { const fetch = vi.fn(async () => Response.json(artifact, { status: 201 })); vi.stubGlobal("fetch", fetch); const file = new File(["data"], "clip.mp4", { type: "video/mp4" }); await expect(uploadAttachment({ groupId: "group-1" }, file, "video/mp4")).resolves.toEqual( artifact, ); const [url, init] = fetch.mock.calls[0] as unknown as [string, RequestInit]; expect(url).toBe("/api/artifacts?groupId=group-1&name=clip.mp4"); expect(init.method).toBe("POST"); expect(init.credentials).toBe("include"); expect(new Headers(init.headers).get("content-type")).toBe("video/mp4"); expect(init.body).toBe(file); expect(create).not.toHaveBeenCalled(); }); it("surfaces the server's reason, or a generic one", async () => { vi.stubGlobal( "fetch", vi.fn(async () => Response.json({ error: "File is too large." }, { status: 413 })), ); await expect( uploadArtifactFile({ botId: "bot-1" }, new Blob(["x"]), "big.mp4", "video/mp4"), ).rejects.toThrow("File is too large."); vi.stubGlobal( "fetch", vi.fn(async () => new Response("bad gateway", { status: 502 })), ); await expect( uploadArtifactFile({ botId: "bot-1" }, new Blob(["x"]), "big.mp4", "video/mp4"), ).rejects.toThrow("Could not upload big.mp4. Try again."); }); });