kepr2.test.ts

3.4 KB · raw

1import { expect, test } from "bun:test";2import { cp, mkdtemp } from "node:fs/promises";3import { tmpdir } from "node:os";4import { join } from "node:path";5import { createApp } from "../src/server/app.ts";6import { openKeprDb } from "../src/server/db.ts";7import { Browser, instance } from "./browser.ts";8import { makeKohBucket } from "./fixture.ts";910async function app() {11	const dataDir = await mkdtemp(join(tmpdir(), "kepr-kepr2-"));12	const config = {13		domain: "kepr.example",14		dataDir,15		adminPasswordHash: "$argon2id$x",16		bucket: { kind: "folder" as const, path: dataDir },17	};18	return createApp(config, openKeprDb(dataDir));19}2021test("§28: Kepr 2's moved addresses answer 301 with the new one", async () => {22	const kepr = await app();23	const moves: [string, string][] = [24		["/demo?panel=issues", "/demo/issues"],25		["/demo?panel=builds", "/demo/releases"],26		["/demo?panel=archives", "/demo/archives"],27		["/demo?panel=trust", "/demo"],28		["/demo/releases/2.1.0", "/demo/releases#2.1.0"],29		["/demo/file?path=src/main.ts", "/demo/files/src/main.ts"],30		["/demo/file?path=docs/a%20b.md", "/demo/files/docs/a%20b.md"],31		["/demo/bundle", "/demo/archive/HEAD.tar.gz"],32		["/demo/bundle.blake3", "/demo/archive/HEAD.tar.gz"],33		["/source", "/kepr"],34	];35	for (const [from, to] of moves) {36		const response = await kepr.request(from);37		expect([from, response.status, response.headers.get("location")]).toEqual([from, 301, to]);38	}39});4041test("§28: Kepr 2's retired addresses answer 410 Gone, for any method", async () => {42	const kepr = await app();43	const gone = [44		"/tickets",45		"/tickets/new",46		"/tickets/3",47		"/drafts",48		"/drafts/abc",49		"/join",50		"/cli-auth",51		"/api/cli-auth/exchange",52		"/api/cli-auth/session",53		"/api/repos",54		"/api/repos/demo/offer/negotiate",55		"/api/repos/demo/offer/complete",56		"/api/repos/demo/snapshot",57		"/api/releases/upload",58		"/protocol/v1/protocol.zig",59		"/feed",60		"/demo/feed",61	];62	for (const path of gone) {63		expect([path, (await kepr.request(path)).status]).toEqual([path, 410]);64		const post = await kepr.request(path, { method: "POST" });65		expect([path, post.status]).toEqual([path, 410]);66	}67});6869test("the redirects shadow none of §22.1's routes, and refuse unsafe input", async () => {70	const kepr = await app();71	for (const path of [72		"/demo/releases/latest",73		"/demo/releases/latest.json",74		"/demo/releases/history.json",75		"/demo",76		"/demo?panel=nope",77		"/demo/file",78		"/demo/file?path=../etc/passwd",79		"/demo/file?path=a//b",80		"/demo/releases/bad_version",81	]) {82		expect([path, (await kepr.request(path)).status]).toEqual([path, 404]);83	}84});8586test("a bucket repo named like one of Kepr's own routes is not synced, and the console says why", async () => {87	const fx = await makeKohBucket();88	await cp(join(fx.bucketDir, fx.repo), join(fx.bucketDir, "browse"), { recursive: true });89	const inst = await instance(fx.bucketDir);90	const rows = inst.kepr.query("SELECT name, last_sync_error FROM repos ORDER BY name").all();91	const clash = `not synced: the name "browse" clashes with Kepr's own /browse route; rename the repo`;92	expect(rows).toEqual([93		{ name: "browse", last_sync_error: clash },94		{ name: "demo", last_sync_error: null },95	]);96	expect(await Bun.file(join(inst.dataDir, "mirror/browse.db")).exists()).toBe(false);97	const browser = new Browser(inst.app);98	await browser.signIn();99	expect(await (await browser.get("/admin/repos")).text()).toContain(100		`sync error: ${clash.replaceAll('"', """).replaceAll("'", "'")}`,101	);102});