import { describe, it, expect, beforeAll } from "vitest"; import PocketBase from "pocketbase"; import { APP_URL, DEMO, adminPb, requireServices, signIn, userPb } from "./helpers"; let admin: PocketBase; let managerCookie: string; let projectId: string; let inboxId: string; beforeAll(async () => { await requireServices(); admin = await adminPb(); managerCookie = await signIn(DEMO.managerEmail, DEMO.managerPassword); const project = await admin .collection("projects") .getFirstListItem(`slug = "${DEMO.projectSlug}"`); projectId = project.id; inboxId = ( await admin .collection("columns") .getFirstListItem(`project = "${projectId}" && is_inbox = true`) ).id; }); describe("a client manager cannot write card fields the owner owns", () => { it("is refused by PocketBase on a direct subtask create", async () => { const card = await admin.collection("cards").getFirstListItem( `project = "${projectId}"` ); const pb = await userPb(DEMO.managerEmail, DEMO.managerPassword); await expect( pb.collection("subtasks").create({ card: card.id, title: "injected", done: false, position: 1000, }) ).rejects.toThrow(); }); it("is refused by PocketBase on a direct activity insert", async () => { // activity.createRule is superuser-only. If this ever succeeds, someone // relaxed the rule and the audit log can be forged. const pb = await userPb(DEMO.managerEmail, DEMO.managerPassword); await expect( pb.collection("activity").create({ project: projectId, actor: "x", action: "card_accepted", }) ).rejects.toThrow(); }); }); describe("a client request carries the flags the accept flow depends on", () => { it("lands unaccepted, client-authored, with no start date", async () => { const response = await fetch(`${APP_URL}/api/client/cards`, { method: "POST", headers: { "content-type": "application/json", cookie: managerCookie }, body: JSON.stringify({ project: projectId, title: "Accept-flow probe" }), }); expect(response.status).toBe(201); const { card } = await response.json(); const created = await admin.collection("cards").getOne(card.id); expect(created.column).toBe(inboxId); expect(created.accepted).toBe(false); expect(created.created_by_role).toBe("client"); // start_date must be empty until acceptance — Phase 2 uses it to bound // which commits this card may claim. expect(created.start_date).toBeFalsy(); await admin.collection("cards").delete(card.id); }); });