// @vitest-environment node import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("server-only", () => ({})); import { deleteGateFile, GateFileError, getGateFileContent, uploadGateFile, } from "@/lib/chat/gate-files"; beforeEach(() => { vi.stubEnv("AEGIDA_GATE_URL", "https://gate.example.test"); }); afterEach(() => { vi.unstubAllEnvs(); vi.unstubAllGlobals(); }); describe("Aegida Gate Files client", () => { it("streams the original File with purpose user_data and the exact user token", async () => { const file = new File(["brief"], "brief.txt", { type: "text/plain" }); const arrayBuffer = vi.spyOn(file, "arrayBuffer").mockRejectedValue( new Error("whole-file-arrayBuffer-must-not-be-used"), ); const fetchMock = vi.fn().mockResolvedValue( Response.json({ id: "file-opaque_123", object: "file", bytes: 5, created_at: 1_785_758_400, filename: "brief.txt", purpose: "user_data", }), ); vi.stubGlobal("fetch", fetchMock); const signal = new AbortController().signal; await expect(uploadGateFile(file, "Bearer exact-user-jwt", signal)).resolves.toEqual({ id: "file-opaque_123", filename: "brief.txt", bytes: 5, }); const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; expect(url).toBe("https://gate.example.test/v1/files"); expect(init).toMatchObject({ method: "POST", headers: { Authorization: "Bearer exact-user-jwt" }, cache: "no-store", redirect: "error", signal, }); expect(init.headers).not.toHaveProperty("Content-Type"); expect(init.body).toBeInstanceOf(FormData); expect((init.body as FormData).get("purpose")).toBe("user_data"); expect((init.body as FormData).get("file")).toBe(file); expect(arrayBuffer).not.toHaveBeenCalled(); }); it.each([ null, {}, { id: "not-a-file", object: "file", bytes: 5, created_at: 1, filename: "a.txt", purpose: "user_data" }, { id: "file-good", object: "other", bytes: 5, created_at: 1, filename: "a.txt", purpose: "user_data" }, { id: "file-good", object: "file", bytes: -1, created_at: 1, filename: "a.txt", purpose: "user_data" }, { id: "file-good", object: "file", bytes: 5, created_at: 1, filename: "", purpose: "user_data" }, { id: "file-good", object: "file", bytes: 5, created_at: 1, filename: "a.txt", purpose: "assistants" }, ])("rejects a malformed FileObject %#", async (value) => { vi.stubGlobal("fetch", vi.fn().mockResolvedValue(Response.json(value))); await expect( uploadGateFile( new File(["brief"], "brief.txt", { type: "text/plain" }), "Bearer user", new AbortController().signal, ), ).rejects.toBeInstanceOf(GateFileError); }); it("uses authenticated standard content and delete paths", async () => { const fetchMock = vi .fn() .mockResolvedValueOnce(new Response("private file contents")) .mockResolvedValueOnce(new Response(null, { status: 204 })); vi.stubGlobal("fetch", fetchMock); const signal = new AbortController().signal; const content = await getGateFileContent("file-opaque_123", "Bearer user", signal); expect(await new Response(content).text()).toBe("private file contents"); await deleteGateFile("file-opaque_123", "Bearer user", signal); expect(fetchMock).toHaveBeenNthCalledWith(1, "https://gate.example.test/v1/files/file-opaque_123/content", { method: "GET", headers: { Authorization: "Bearer user" }, cache: "no-store", redirect: "error", signal, }); expect(fetchMock).toHaveBeenNthCalledWith(2, "https://gate.example.test/v1/files/file-opaque_123", { method: "DELETE", headers: { Authorization: "Bearer user" }, cache: "no-store", redirect: "error", signal, }); }); it("sanitizes non-2xx and oversized JSON responses", async () => { vi.stubGlobal( "fetch", vi .fn() .mockResolvedValueOnce(new Response("private-dependency-sentinel", { status: 500 })) .mockResolvedValueOnce(new Response("x".repeat(1_048_577))), ); const file = new File(["brief"], "brief.txt", { type: "text/plain" }); for (let attempt = 0; attempt < 2; attempt += 1) { let error: unknown; try { await uploadGateFile(file, "Bearer user", new AbortController().signal); } catch (caught) { error = caught; } expect(error).toBeInstanceOf(GateFileError); expect(String(error)).not.toContain("private-dependency-sentinel"); expect(String(error)).not.toContain("brief"); } }); });