92 lines
No EOL
2.8 KiB
TypeScript
92 lines
No EOL
2.8 KiB
TypeScript
import { describe, it, expect, beforeAll } from "vitest";
|
|
import PocketBase from "pocketbase";
|
|
|
|
import { APP_URL, DEMO, adminPb, requireServices, signIn } from "./helpers";
|
|
|
|
let admin: PocketBase;
|
|
let cookie: string;
|
|
let cardId: string;
|
|
|
|
beforeAll(async () => {
|
|
await requireServices();
|
|
admin = await adminPb();
|
|
cookie = await signIn(DEMO.managerEmail, DEMO.managerPassword);
|
|
|
|
const project = await admin
|
|
.collection("projects")
|
|
.getFirstListItem(`slug = "${DEMO.projectSlug}"`);
|
|
|
|
cardId = (
|
|
await admin.collection("cards").getFirstListItem(`project = "${project.id}"`)
|
|
).id;
|
|
});
|
|
|
|
async function patch(body: Record<string, unknown>) {
|
|
return fetch(`${APP_URL}/api/client/cards/${cardId}`, {
|
|
method: "PATCH",
|
|
headers: { "content-type": "application/json", cookie },
|
|
body: JSON.stringify(body),
|
|
});
|
|
}
|
|
|
|
async function activityCount(): Promise<number> {
|
|
const rows = await admin
|
|
.collection("activity")
|
|
.getFullList({ filter: `card = "${cardId}"` });
|
|
return rows.length;
|
|
}
|
|
|
|
/** Spec §9 test 5 */
|
|
describe("every client-originated change is audited", () => {
|
|
it("writes one row with the correct old and new values", async () => {
|
|
await admin.collection("cards").update(cardId, { priority: "medium" });
|
|
const before = await activityCount();
|
|
|
|
const response = await patch({ priority: "high" });
|
|
expect(response.status).toBe(200);
|
|
|
|
// Exactly one new row overall — not "at least one", which would pass even
|
|
// if the route wrote a row per field it merely looked at.
|
|
expect(await activityCount()).toBe(before + 1);
|
|
|
|
const rows = await admin.collection("activity").getFullList({
|
|
filter: `card = "${cardId}" && field = "priority"`,
|
|
sort: "-created",
|
|
});
|
|
|
|
expect(rows[0].action).toBe("priority_changed");
|
|
expect(rows[0].old_value).toBe("medium");
|
|
expect(rows[0].new_value).toBe("high");
|
|
expect(rows[0].actor).not.toBe("");
|
|
});
|
|
|
|
it("writes no row when the value is unchanged", async () => {
|
|
await admin.collection("cards").update(cardId, { priority: "low" });
|
|
const before = await activityCount();
|
|
|
|
const response = await patch({ priority: "low" });
|
|
expect(response.status).toBe(200);
|
|
|
|
expect(await activityCount()).toBe(before);
|
|
});
|
|
|
|
it("writes one row per field when two change at once", async () => {
|
|
await admin
|
|
.collection("cards")
|
|
.update(cardId, { priority: "medium", target_date: "2026-10-01" });
|
|
const before = await activityCount();
|
|
|
|
const response = await patch({
|
|
priority: "critical",
|
|
target_date: "2026-12-24",
|
|
});
|
|
expect(response.status).toBe(200);
|
|
|
|
expect(await activityCount()).toBe(before + 2);
|
|
});
|
|
|
|
it("returns 400 when the body contains no editable fields", async () => {
|
|
const response = await patch({ title: "nope", column: "whatever" });
|
|
expect(response.status).toBe(400);
|
|
});
|
|
}); |