import { describe, expect, it } from "vitest";
import {
archiveUrl,
defaultArchivePrefix,
immutableArchiveUrl,
parseArchiveRequest
} from "#lib/api/archive.ts";
// kit hands the route its rest param decoded
const parse = (target: string, userAgent = "") => {
const url = new URL(`/${target}`, "https://tangled.test");
return parseArchiveRequest(
decodeURIComponent(url.pathname.slice(1)),
url.searchParams,
userAgent
);
};
describe("parseArchiveRequest", () => {
const windows = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)";
it.each([
[
"a short ref",
"v1.0.0?format=tar.gz",
"",
{ ref: "v1.0.0", format: "tar.gz", prefix: "", guessed: false }
],
[
"a full ref",
"refs/tags/v1.0.0?prefix=did:plc:boltless",
"",
{ ref: "refs/tags/v1.0.0", format: "tar.gz", prefix: "did:plc:boltless", guessed: true }
],
[
"an escaped ref with a format suffix",
"refs%2Ftags%2Fv1.0.0.tar.gz",
"",
{ ref: "refs/tags/v1.0.0", format: "tar.gz", prefix: "", guessed: false }
],
[
"the zip suffix",
"refs/tags/v1.0.0.zip",
"",
{ ref: "refs/tags/v1.0.0", format: "zip", prefix: "", guessed: false }
],
[
"a tag ending in a format suffix",
"refs/tags/v1.0.0.zip.tar.gz",
"",
{ ref: "refs/tags/v1.0.0.zip", format: "tar.gz", prefix: "", guessed: false }
],
[
"an unknown format query over a zip suffix",
"v1.zip?format=tar.xz",
"",
{ ref: "v1", format: "zip", prefix: "", guessed: false }
],
[
"a format query over the suffix",
"v1.zip?format=tar.gz",
"",
{ ref: "v1", format: "tar.gz", prefix: "", guessed: false }
],
[
"zip for a windows user agent",
"main?format=tar.xz",
windows,
{ ref: "main", format: "zip", prefix: "", guessed: true }
],
[
"a percent in the ref itself",
"refs/tags/a%252Fb",
"",
{ ref: "refs/tags/a%2Fb", format: "tar.gz", prefix: "", guessed: true }
],
[
"a prefix wrapped in slashes",
"main?prefix=/kelp/./reef/",
"",
{ ref: "main", format: "tar.gz", prefix: "kelp/reef", guessed: true }
]
])("reads %s", (_, path, userAgent, want) => {
expect(parse(path, userAgent)).toEqual(want);
});
it.each([
["an empty ref", ""],
["only a suffix", ".tar.gz"],
["an escaped space", "refs/tags/a%20b"],
["a ref git would read as an option", "--output=%2Ftmp%2Fevil"],
["a range", "main..evil"],
["a prefix escaping the root", "main?prefix=a/../../etc"],
["a prefix with a backslash", "main?prefix=a%5Cb"],
["a prefix over 255 bytes", `main?prefix=${"a".repeat(256)}`]
])("refuses %s", (_, path) => {
expect(parse(path)).toBeNull();
});
});
describe("defaultArchivePrefix", () => {
it.each([
["squid", "main", "squid-main"],
["squid", "refs/heads/feat/uni", "squid-feat-uni"],
["squid", "refs/tags/v1.0.0", "squid-v1.0.0"],
["squid", "refs/remotes/origin/main", "squid-origin-main"],
["squid", "refs/notes/x", "squid-notes-x"]
])("names %s at %s %s", (repo, ref, want) => {
expect(defaultArchivePrefix(repo, ref)).toBe(want);
});
it("cuts a long stem at 255 bytes without splitting a character", () => {
const prefix = defaultArchivePrefix("squid", "é".repeat(200));
expect(new TextEncoder().encode(prefix).length).toBe(254);
expect(prefix.endsWith("é")).toBe(true);
});
});
describe("archive urls", () => {
const served = {
ref: "6f1d3a2b4c5d6e7f8091a2b3c4d5e6f708192a3b",
format: "zip",
prefix: "kelp&format=zip#uni"
} as const;
it("pins the immutable url to the commit with every parameter spelled out", () => {
const url = immutableArchiveUrl("https://tangled.test", "did:plc:limpet", served);
expect(url).toBe(
"https://tangled.test/did:plc:limpet/archive/6f1d3a2b4c5d6e7f8091a2b3c4d5e6f708192a3b.zip?prefix=kelp%26format%3Dzip%23uni"
);
const parsed = new URL(url);
const ref = decodeURIComponent(parsed.pathname.replace("/did:plc:limpet/archive/", ""));
expect(parseArchiveRequest(ref, parsed.searchParams, "")).toEqual({
...served,
guessed: false
});
});
it("asks bobbin for the same archive", () => {
const url = new URL(
archiveUrl("https://bobbin.test", { repo: "did:plc:limpet", ...served })
);
expect(url.pathname).toBe("/xrpc/org.tangled.temp.git.getArchive");
expect(Object.fromEntries(url.searchParams)).toEqual({ repo: "did:plc:limpet", ...served });
});
});