import { describe, expect, it, vi } from "vitest"; import * as knotmirror from "./knotmirror"; const jsonResponse = (body: unknown): Response => new Response(JSON.stringify(body), { status: 200, headers: { "content-type": "application/json" } }); describe("knotmirror.tree", () => { it("fetches the readme blob", async () => { const fetchMock = vi.fn().mockImplementation(async (input) => { const url = new URL(String(input)); if (url.pathname.endsWith("getTree")) { return jsonResponse({ ref: "main", files: [ { name: "README.md", mode: "100644", size: 12, last_commit: { hash: "abc", message: "docs", when: "2026-07-01T00:00:00Z" } } ] }); } return new Response("# hello", { status: 200, headers: { "content-type": "text/plain" } }); }); const ctx = knotmirror.createKnotMirrorClient("https://mirror.test", fetchMock); const tree = await knotmirror.tree(ctx, { repo: "did:plc:repo", ref: "main", path: "docs" }); expect(tree.readme).toEqual({ filename: "README.md", contents: "# hello" }); expect(tree.files[0]).toMatchObject({ name: "README.md", mode: "100644" }); expect(fetchMock).toHaveBeenCalledTimes(2); const blobUrl = new URL(String(fetchMock.mock.calls[1][0])); expect(blobUrl.pathname).toBe("/xrpc/sh.tangled.git.temp.getBlob"); expect(blobUrl.searchParams.get("path")).toBe("docs/README.md"); }); }); describe("knotmirror.languages", () => { it("uses the languages endpoint", async () => { const fetchMock = vi .fn() .mockResolvedValue( jsonResponse({ ref: "main", languages: [{ name: "TypeScript", size: 10 }] }) ); const ctx = knotmirror.createKnotMirrorClient("https://mirror.test", fetchMock); await knotmirror.languages(ctx, { repo: "did:plc:repo", ref: "main" }); const url = new URL(String(fetchMock.mock.calls[0][0])); expect(url.pathname).toBe("/xrpc/sh.tangled.git.temp.listLanguages"); }); }); describe("knotmirror.blob", () => { const entryBody = { name: "hello.txt", mode: "0100644", size: 5, lastCommit: { hash: "abc", message: "hi", author: { name: "dawn", email: "dawn@tangled.org", when: "2026-07-01T00:00:00Z" } } }; const blobFetch = (content: Uint8Array, entry: unknown = entryBody) => vi.fn().mockImplementation(async (input) => { const url = new URL(String(input)); return url.pathname.endsWith("getEntry") ? jsonResponse(entry) : new Response(content.slice().buffer, { status: 200 }); }); it("creates the repo blob shape from getEntry plus raw content", async () => { const fetchMock = blobFetch(new TextEncoder().encode("hello")); const ctx = knotmirror.createKnotMirrorClient("https://mirror.test", fetchMock); const output = await knotmirror.blob( ctx, { repo: "did:plc:repo", ref: "main", path: "hello.txt" }, 1024 ); expect(output).toMatchObject({ path: "hello.txt", size: 5, isBinary: false, encoding: "utf-8", content: "hello", lastCommit: { hash: "abc", when: "2026-07-01T00:00:00Z" } }); }); it("skips the content fetch when the entry is over the inline cap", async () => { const fetchMock = blobFetch(new TextEncoder().encode("hello"), { ...entryBody, size: 2048 }); const ctx = knotmirror.createKnotMirrorClient("https://mirror.test", fetchMock); const output = await knotmirror.blob( ctx, { repo: "did:plc:repo", ref: "main", path: "hello.txt" }, 1024 ); expect(output.fileTooLarge).toBe(true); expect(output.content).toBeUndefined(); expect(fetchMock).toHaveBeenCalledTimes(1); }); it("marks content with null bytes as binary", async () => { const fetchMock = blobFetch(new Uint8Array([0x50, 0x4b, 0x00, 0x00])); const ctx = knotmirror.createKnotMirrorClient("https://mirror.test", fetchMock); const output = await knotmirror.blob( ctx, { repo: "did:plc:repo", ref: "main", path: "hello.txt" }, 1024 ); expect(output.isBinary).toBe(true); expect(output.content).toBeUndefined(); }); it("treats a gitlink mode as a submodule", async () => { const fetchMock = blobFetch(new Uint8Array(), { ...entryBody, mode: "0160000" }); const ctx = knotmirror.createKnotMirrorClient("https://mirror.test", fetchMock); const output = await knotmirror.blob( ctx, { repo: "did:plc:repo", ref: "main", path: "hello.txt" }, 1024 ); expect(output.submodule).toEqual({ name: "hello.txt", url: "" }); }); });