console.test.ts

5.4 KB · raw

1import { afterAll, beforeAll, describe, expect, setSystemTime, test } from "bun:test";2import { fileIssue } from "../src/issues/store.ts";3import { Browser, type Instance, instance } from "./browser.ts";4import { makeRepos, writeRelease } from "./fixture.ts";56let clock = Date.parse("2026-09-26T12:00:00Z");7const tick = (seconds: number) => {8	clock += seconds * 1000;9	setSystemTime(new Date(clock));10};1112describe("repos and visibility", () => {13	let inst: Instance;14	let browser: Browser;15	let bucketDir: string;16	let save: string;17	const publicPaths = () => [18		"/app",19		"/app/files/README.md",20		"/app/raw/README.md",21		`/app/saves/${save}`,22		`/app/archive/${save}.tar.gz`,23		"/app/archive/HEAD.tar.gz",24		"/app/archives",25		"/app/releases",26		"/app/releases/latest",27		"/app/releases/latest.json",28		"/app/releases/history.json",29		"/app/releases/latest/version.txt",30		"/app/releases/latest/linux-x86_64",31		"/app/releases/1.0.0/checksums.txt",32		"/app/releases/1.0.0/linux-x86_64",33		"/app/install.sh",34		"/api/app/releases/latest",35		"/api/app/releases/history",36		"/app/update-info",37		"/app/meta",38		"/app/issues",39		"/app/issues/new",40		"/app/issues/1",41	];42	const statuses = async () => {43		const out: string[] = [];44		for (const path of publicPaths()) out.push(`${path} ${(await inst.app.request(path)).status}`);45		return out;46	};47	const text = async (path: string) => (await inst.app.request(path)).text();48	const publish = (repo: string, fields: Record<string, string>) =>49		browser.post(`/admin/repos/${repo}`, { sort_order: "0", ...fields });5051	beforeAll(async () => {52		setSystemTime(new Date(clock));53		const repos = await makeRepos(["app", "other"]);54		bucketDir = repos.bucketDir;55		save = repos.saves.app as string;56		await writeRelease(bucketDir, "app", {57			version: "1.0.0",58			save,59			files: [{ platform: "linux-x86_64", name: "app", bytes: new Uint8Array([1]) }],60		});61		inst = await instance(bucketDir);62		inst.kepr.run("INSERT INTO settings (key, value) VALUES ('handle', 'june')");63		fileIssue(inst.kepr, {64			repo: "app",65			title: "It broke",66			body: "b",67			type: "bug",68			version: "",69			reporterName: "",70			contactEmail: "",71			ipHash: "x",72			filedAt: 1,73		});74		browser = new Browser(inst.app);75		await browser.signIn();76	});77	afterAll(() => setSystemTime());7879	test("every repo the sync found is listed, private, with its last sync", async () => {80		const page = await (await browser.get("/admin/repos")).text();81		expect(page).toContain('id="repo-app"');82		expect(page).toContain('id="repo-other"');83		expect(page.match(/<span>private<\/span>/g)?.length).toBe(2);84		expect(page).toContain("last sync 2026-09-26 12:00 UTC");85		expect((await statuses()).every((line) => line.endsWith(" 404"))).toBe(true);86	});8788	test("bad settings are refused and change nothing", async () => {89		for (const fields of <Record<string, string>[]>[90			{ homepage: "javascript:alert(1)" },91			{ sort_order: "first" },92			{ install1: "one\ntwo" },93			{ category: "x".repeat(501) },94		]) {95			expect((await publish("app", { public: "1", ...fields })).status).toBe(400);96		}97		expect((await inst.app.request("/app")).status).toBe(404);98		expect((await browser.post("/admin/repos/nowhere", { public: "1" })).status).toBe(404);99	});100101	test("publishing opens every public surface, with the settings shown", async () => {102		await publish("other", { public: "1" });103		const response = await publish("app", {104			public: "1",105			category: "tools",106			homepage: "https://app.example",107			install1: "nix profile add app",108		});109		expect(response.status).toBe(303);110		const lines = await statuses();111		expect(lines.filter((line) => line.endsWith(" 404"))).toEqual([]);112		expect(await text("/app")).toContain("https://app.example");113		expect(await text("/app")).toContain("nix profile add app");114		expect(await text("/")).toContain('href="/app"');115		expect(await text("/browse")).toContain('href="/app"');116		expect(await text("/@june")).toContain('href="/app"');117		expect(await text("/sitemap.xml")).toContain("/app</loc>");118	});119120	test("flipping a repo private 404s every surface and drops it from lists and counts", async () => {121		const stat = async () =>122			[...(await text("/@june")).matchAll(/profile-stat-number">([^<]*)</g)].map((m) => m[1]);123		// Both download routes above counted; the issue is the repo's one.124		expect(await stat()).toEqual(["2", "2", "1", "2"]);125		await publish("app", { category: "tools" });126		expect((await statuses()).filter((line) => !line.endsWith(" 404"))).toEqual([]);127		for (const path of ["/", "/browse", "/@june"]) expect(await text(path)).not.toContain("/app");128		expect(await text("/sitemap.xml")).not.toContain("/app<");129		expect(await text("/")).not.toContain("tools");130		expect(await stat()).toEqual(["1", "1", "0", "0"]);131		// Settings survive the flip.132		expect(await (await browser.get("/admin/repos")).text()).toContain('value="tools"');133	});134135	test("sync now reads the bucket at once and records the time", async () => {136		await writeRelease(bucketDir, "other", {137			version: "2.0.0",138			save: save,139			files: [{ platform: "linux-x86_64", name: "other", bytes: new Uint8Array([2]) }],140		});141		tick(60);142		expect((await inst.app.request("/other/releases/latest/version.txt")).status).toBe(404);143		const response = await browser.post("/admin/sync", {});144		expect(response.status).toBe(303);145		expect(await text("/other/releases/latest/version.txt")).toBe("2.0.0");146		expect(await (await browser.get("/admin/repos")).text()).toContain(147			"last sync 2026-09-26 12:01 UTC",148		);149	});150});