import { describe, it, expect, beforeAll } from "vitest"; import PocketBase from "pocketbase"; import { APP_URL, DEMO, adminPb, requireServices, signIn, userPb } from "./helpers"; let admin: PocketBase; beforeAll(async () => { await requireServices(); admin = await adminPb(); }); describe("a project is never created without a usable board", () => { it("every project has exactly one inbox and one done column", async () => { // A project missing either makes POST /api/client/cards answer 409, which // the client discovers days later when they try to file a request. const projects = await admin.collection("projects").getFullList(); expect(projects.length).toBeGreaterThan(0); for (const project of projects) { const columns = await admin .collection("columns") .getFullList({ filter: `project = "${project.id}"` }); expect( columns.filter((c) => c.is_inbox), `project ${project.slug} inbox columns` ).toHaveLength(1); expect( columns.filter((c) => c.is_done), `project ${project.slug} done columns` ).toHaveLength(1); } }); }); describe("the admin area is owner-only", () => { it("does not let a client manager list other accounts", async () => { const pb = await userPb(DEMO.managerEmail, DEMO.managerPassword); const users = await pb.collection("users").getFullList(); expect(users).toHaveLength(1); }); it("does not let a client manager create a client", async () => { const pb = await userPb(DEMO.managerEmail, DEMO.managerPassword); await expect( pb.collection("clients").create({ name: "Injected", slug: `injected-${Date.now()}`, active: true, }) ).rejects.toThrow(); }); it("does not let a client manager create a project", async () => { const pb = await userPb(DEMO.managerEmail, DEMO.managerPassword); const client = await admin .collection("clients") .getFirstListItem(`slug = "${DEMO.clientSlug}"`); await expect( pb.collection("projects").create({ client: client.id, name: "Injected", slug: `injected-${Date.now()}`, status: "active", }) ).rejects.toThrow(); }); }); describe("deactivated accounts cannot sign in", () => { it("is refused with the same message as a wrong password", async () => { const email = `deactivated-${Date.now()}@example.com`; const password = "probe-password-123"; const client = await admin .collection("clients") .getFirstListItem(`slug = "${DEMO.clientSlug}"`); const user = await admin.collection("users").create({ email, password, passwordConfirm: password, name: "Deactivated probe", role: "client_manager", client: client.id, active: false, verified: true, }); const response = await fetch(`${APP_URL}/api/auth/login`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email, password }), }); expect(response.status).toBe(401); const body = await response.json(); expect(body.error).toBe("Invalid email or password"); await admin.collection("users").delete(user.id); }); it("still lets an active account in", async () => { // Guards against the active check rejecting everyone, which would look // identical to a wrong password and lock the product. const cookie = await signIn(DEMO.managerEmail, DEMO.managerPassword); expect(cookie).toContain("pb_auth="); }); });