import { afterEach, describe, expect, it, rs } from "@rstest/core"; import { ARTIFACT_PREVIEW_MAX_BYTES, loadArtifactContent, } from "@/core/artifacts/loader"; describe("loadArtifactContent", () => { afterEach(() => { rs.restoreAllMocks(); rs.unstubAllGlobals(); }); it("uses the server content revision when available", async () => { rs.spyOn(globalThis, "fetch").mockResolvedValue( new Response("content", { status: 200, headers: { ETag: `"${"a".repeat(64)}"` }, }), ); const loaded = await loadArtifactContent({ filepath: "/mnt/user-data/outputs/report.md", threadId: "thread-1", }); expect(loaded.sha256).toBe("a".repeat(64)); }); it("computes a revision for a complete response without a SHA-256 ETag", async () => { rs.spyOn(globalThis, "fetch").mockResolvedValue( new Response("content", { status: 200, headers: { ETag: '"starlette-file-etag"' }, }), ); const loaded = await loadArtifactContent({ filepath: "/mnt/user-data/outputs/page.html", threadId: "thread-1", }); expect(loaded.sha256).toBe( "ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73", ); }); it("requests only the preview byte budget and reports truncation", async () => { const bytes = new TextEncoder().encode("preview"); const fetchMock = rs.fn(async (_url: string, init?: RequestInit) => { expect(new Headers(init?.headers).get("Range")).toBe( `bytes=0-${ARTIFACT_PREVIEW_MAX_BYTES - 1}`, ); return new Response(bytes, { status: 206, headers: { "Content-Range": `bytes 0-${bytes.length - 1}/2000000`, }, }); }); rs.stubGlobal("fetch", fetchMock); const result = await loadArtifactContent({ filepath: "/mnt/user-data/outputs/large.txt", threadId: "thread-1", }); expect(result.content).toBe("preview"); expect(result.truncated).toBe(true); expect(result.totalBytes).toBe(2_000_000); expect(result.sha256).toBeUndefined(); }); it("loads and revisions the full file only when explicitly requested", async () => { const fetchMock = rs.fn(async (_url: string, init?: RequestInit) => { expect(new Headers(init?.headers).has("Range")).toBe(false); return new Response("complete", { status: 200, headers: { "Content-Length": "8" }, }); }); rs.stubGlobal("fetch", fetchMock); const result = await loadArtifactContent({ filepath: "/mnt/user-data/outputs/large.txt", threadId: "thread-1", full: true, }); expect(result).toMatchObject({ content: "complete", truncated: false, totalBytes: 8, sha256: "eebbf6457e46a7f63acdf9b97390f790ba443d60cfa44b607da7e5c40aa1cc1d", }); }); it("does not render a replacement character for a split UTF-8 code point", async () => { const emojiBytes = new TextEncoder().encode("abc😀"); const partial = emojiBytes.slice(0, -2); rs.stubGlobal( "fetch", rs.fn( async () => new Response(partial, { status: 206, headers: { "Content-Range": `bytes 0-${partial.length - 1}/${emojiBytes.length + 10}`, }, }), ), ); const result = await loadArtifactContent({ filepath: "/mnt/user-data/outputs/unicode.txt", threadId: "thread-1", }); expect(result.content).toBe("abc"); }); it("treats an unsatisfied range on an empty file as empty content", async () => { rs.stubGlobal( "fetch", rs.fn( async () => new Response(null, { status: 416, headers: { "Content-Range": "bytes */0" }, }), ), ); const result = await loadArtifactContent({ filepath: "/mnt/user-data/outputs/empty.txt", threadId: "thread-1", }); expect(result).toMatchObject({ content: "", truncated: false, totalBytes: 0, sha256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", }); }); });