import { describe, it, expect, beforeAll, afterAll } from "vitest"; import { migrateTest, resetContent, TEST_URL } from "./setup"; import postgres from "postgres"; let sql: ReturnType; let imp: typeof import("@/lib/content/import"); let queue: typeof import("@/lib/jobs/queue"); let dbmod: typeof import("@/db"); beforeAll(async () => { await migrateTest(); await resetContent(); sql = postgres(TEST_URL, { max: 2, onnotice: () => {} }); imp = await import("@/lib/content/import"); queue = await import("@/lib/jobs/queue"); dbmod = await import("@/db"); }, 60_000); afterAll(async () => { await sql.end(); await dbmod.closeDb(); }); describe("content import", () => { it("imports categories, a record, relations and publishes; is idempotent", async () => { const r1 = await imp.importAll({ root: "content", publish: true, only: ["duckdb", "python", "sqlite"] }); expect(r1.categories).toBeGreaterThan(70); expect(r1.tools.created.sort()).toEqual(["duckdb", "python", "sqlite"]); expect(r1.tools.published.length).toBe(3); const r2 = await imp.importAll({ root: "content", publish: true, only: ["duckdb", "python", "sqlite"] }); expect(r2.tools.unchanged.length).toBe(3); expect(r2.tools.created.length).toBe(0); const [rev] = await sql`SELECT count(*)::int AS n FROM tool_revisions`; expect(rev.n).toBe(3); // one revision per create, none for unchanged const [alt] = await sql`SELECT count(*)::int AS n FROM tool_alternatives ta JOIN tools t ON t.id=ta.tool_id WHERE t.slug='duckdb'`; expect(alt.n).toBeGreaterThanOrEqual(1); // duckdb ↔ sqlite const [pub] = await sql`SELECT count(*)::int AS n FROM public_tools`; expect(pub.n).toBe(3); }, 60_000); it("public view hides private and unpublished records", async () => { await sql`UPDATE tools SET visibility='private' WHERE slug='python'`; await sql`UPDATE tools SET editorial_status='reviewed' WHERE slug='sqlite'`; const [pub] = await sql`SELECT array_agg(slug) AS s FROM public_tools`; expect(pub.s).toEqual(["duckdb"]); await sql`UPDATE tools SET visibility='public', editorial_status='published' WHERE slug IN ('python','sqlite')`; }); it("search vector and trigram index answer typo queries", async () => { const rows = await sql`SELECT slug FROM public_tools WHERE similarity(f_unaccent(lower(name)), 'postgress') > 0.3 OR search_vector @@ to_tsquery('english', 'analyt:*') ORDER BY slug`; expect(rows.map((r) => r.slug)).toContain("duckdb"); }); }); describe("database boundary", () => { it("tools_reader can read public views but not base tables or private data", async () => { const [role] = await sql`SELECT rolname FROM pg_roles WHERE rolname='tools_reader'`; if (!role) return; // roles are cluster-level; skip if absent const url = new URL(TEST_URL); url.username = "tools_reader"; url.password = process.env.DATABASE_URL_PUBLIC?.match(/tools_reader:([^@]+)@/)?.[1] ?? ""; url.hostname = "127.0.0.1"; if (!url.password) return; const r = postgres(url.toString(), { max: 1, onnotice: () => {} }); expect((await r`SELECT count(*)::int AS n FROM public_tools`)[0].n).toBeGreaterThanOrEqual(0); await expect(r`SELECT count(*) FROM tools`).rejects.toThrow(/permission denied/); await expect(r`SELECT count(*) FROM owner_notes`).rejects.toThrow(/permission denied/); await expect(r`SELECT count(*) FROM users`).rejects.toThrow(/permission denied/); await expect(r`SELECT count(*) FROM audit_log`).rejects.toThrow(/permission denied/); await expect(r`UPDATE vendors SET name='x'`).rejects.toThrow(/permission denied/); await r.end(); }); it("audit_log and tool_revisions are append-only", async () => { await sql`INSERT INTO audit_log (actor, action) VALUES ('test', 'x')`; await expect(sql`UPDATE audit_log SET action='y' WHERE actor='test'`).rejects.toThrow(/append-only/); await expect(sql`DELETE FROM audit_log WHERE actor='test'`).rejects.toThrow(/append-only/); await expect(sql`DELETE FROM tool_revisions`).rejects.toThrow(/append-only/); }); it("one primary category per tool is enforced", async () => { const [t] = await sql`SELECT id FROM tools WHERE slug='duckdb'`; const [c] = await sql`SELECT id FROM categories WHERE slug='notebooks'`; await expect(sql`INSERT INTO tool_categories (tool_id, category_id, is_primary) VALUES (${t.id}, ${c.id}, true)`).rejects.toThrow(/duplicate key|unique/); }); }); describe("job queue", () => { it("dedupes, claims with lock, retries with backoff and dead-letters", async () => { await sql`DELETE FROM jobs`; const a = await queue.enqueue("link_check", { toolId: "t1" }, { dedupeKey: "k1", maxAttempts: 2 }); const b = await queue.enqueue("link_check", { toolId: "t1" }, { dedupeKey: "k1" }); expect(a).not.toBeNull(); expect(b === null || b === a).toBe(true); expect((await sql`SELECT count(*)::int AS n FROM jobs`)[0].n).toBe(1); const job = await queue.claim("w1"); expect(job?.id).toBe(a); expect(job?.payload).toEqual({ toolId: "t1" }); expect(await queue.claim("w2")).toBeNull(); // locked await queue.fail(job!, "boom"); let [row] = await sql`SELECT status, run_at > now() AS later, attempts FROM jobs WHERE id=${a}`; expect(row.status).toBe("queued"); expect(row.later).toBe(true); expect(row.attempts).toBe(1); await sql`UPDATE jobs SET run_at = now() WHERE id=${a}`; const j2 = await queue.claim("w1"); await queue.fail(j2!, "boom again"); [row] = await sql`SELECT status, last_error FROM jobs WHERE id=${a}`; expect(row.status).toBe("dead"); expect(row.last_error).toContain("boom again"); await queue.retry(a!); [row] = await sql`SELECT status, attempts FROM jobs WHERE id=${a}`; expect(row.status).toBe("queued"); expect(row.attempts).toBe(0); const j3 = await queue.claim("w1"); await queue.succeed(j3!.id, { ok: 1 }); [row] = await sql`SELECT status, result FROM jobs WHERE id=${a}`; expect(row.status).toBe("succeeded"); expect(row.result).toEqual({ ok: 1 }); }); it("reaps stale running jobs", async () => { const id = await queue.enqueue("backup", {}, { dedupeKey: "stale" }); await queue.claim("w9"); await sql`UPDATE jobs SET locked_at = now() - interval '2 hours' WHERE id=${id}`; expect(await queue.reapStale(20)).toBe(1); expect((await sql`SELECT status FROM jobs WHERE id=${id}`)[0].status).toBe("queued"); }); }); describe("auth primitives", () => { it("argon2id hash verifies and policy rejects weak passwords", async () => { const { hashPassword, verifyPassword, passwordPolicy } = await import("@/lib/auth/password"); const h = await hashPassword("Correct-Horse-Battery-9"); expect(h.startsWith("$argon2id$")).toBe(true); expect(await verifyPassword(h, "Correct-Horse-Battery-9")).toBe(true); expect(await verifyPassword(h, "wrong")).toBe(false); expect(passwordPolicy("492592")).toBeTruthy(); expect(passwordPolicy("alllowercaseletters")).toBeTruthy(); expect(passwordPolicy("Correct-Horse-Battery-9")).toBeNull(); }); it("login limiter locks after 5 failures", async () => { const { loginAllowed, recordAttempt } = await import("@/lib/auth/attempts"); for (let i = 0; i < 5; i++) await recordAttempt("9.9.9.9", "x@y.z", false); const g = await loginAllowed("9.9.9.9", "other@y.z"); expect(g.ok).toBe(false); expect((await loginAllowed("8.8.8.8", "fresh@y.z")).ok).toBe(true); }); });