test(actions): cover audit-log immunity and request flags
This commit is contained in:
parent
043d8ddc8c
commit
c966d7dcc9
1 changed files with 81 additions and 0 deletions
81
tests/integration/owner-actions.test.ts
Normal file
81
tests/integration/owner-actions.test.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
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);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue