issues.test.ts

2.7 KB · raw

1import { describe, expect, test } from "bun:test";2import { mkdtemp } from "node:fs/promises";3import { tmpdir } from "node:os";4import { join } from "node:path";5import {6	addEntry,7	fileIssue,8	filingsSince,9	findIssue,10	hashToken,11	issueCounts,12	listIssues,13	type NewIssue,14	threadEntries,15} from "../src/issues/store.ts";16import { openKeprDb } from "../src/server/db.ts";1718const newIssue = (repo: string, title: string, filedAt = 1000): NewIssue => ({19	repo,20	title,21	body: "It broke.",22	type: "bug",23	version: "1.0.0",24	reporterName: "",25	contactEmail: "[email protected]",26	ipHash: "ip-a",27	filedAt,28});2930describe("issue data layer", async () => {31	const db = openKeprDb(await mkdtemp(join(tmpdir(), "kepr-issues-")));3233	test("numbers are per repo and continue after an imported number", () => {34		expect(fileIssue(db, newIssue("app", "one")).number).toBe(1);35		expect(fileIssue(db, newIssue("app", "two")).number).toBe(2);36		expect(fileIssue(db, newIssue("lib", "first")).number).toBe(1);37		db.run(38			"INSERT INTO issues (repo, number, title, body, type, filed_at) VALUES ('lib', 41, 'from kepr 2', 'x', 'idea', 5)",39		);40		expect(fileIssue(db, newIssue("lib", "next")).number).toBe(42);41	});4243	test("only the token's hash is stored, and public reads leave out contact and IP", () => {44		const { number, token } = fileIssue(db, newIssue("app", "secret"));45		expect(token).toMatch(/^[0-9a-f]{64}$/);46		const issue = findIssue(db, "app", number);47		expect(issue?.token_hash).toBe(hashToken(token));48		expect(JSON.stringify(issue)).not.toContain(token);49		expect(Object.keys(issue ?? {})).not.toContain("contact_email");50		expect(Object.keys(issue ?? {})).not.toContain("ip_hash");51	});5253	test("hidden issues vanish from lists, counts and lookups but still count against the IP", () => {54		const before = filingsSince(db, "ip-a", 0);55		const { number } = fileIssue(db, newIssue("app", "spam"));56		db.run("UPDATE issues SET hidden = 1 WHERE repo = 'app' AND number = ?", [number]);57		expect(findIssue(db, "app", number)).toBeNull();58		expect(listIssues(db, "app", "open").map((i) => i.title)).not.toContain("spam");59		expect(issueCounts(db, "app")).toEqual({ open: 3, closed: 0 });60		expect(filingsSince(db, "ip-a", 0)).toBe(before + 1);61		expect(filingsSince(db, "ip-a", 1000)).toBe(0);62	});6364	test("the thread keeps entries in order with their author kind", () => {65		const issue = findIssue(db, "app", 1);66		if (!issue) throw new Error("issue 1 missing");67		addEntry(db, issue.id, "reporter", "More detail.", 2000);68		addEntry(db, issue.id, "operator", "Thanks.", 3000);69		expect(threadEntries(db, issue.id).map((e) => [e.author, e.body])).toEqual([70			["reporter", "More detail."],71			["operator", "Thanks."],72		]);73	});74});