Files
agentic-dev/packages/core-shared/src/payload/retention-purge/task.test.ts
Danijel Martinek 413ac0273c feat(core-shared): grace-purge soft-deleted rows + boot registration
The retention purge job gated its whole body on activeRetention while
every collection declares only postDeletion, and no app ever called
registerRetentionPurgeJobs — retention was dead end to end (audit
findings A2 + A3). The DSR soft delete now stamps a deletedAt tombstone
on postDeletion collections (kept distinct from processingRestrictedAt
so an Art. 18 restriction never feeds the purge), the job grace-purges
tombstoned rows past postDeletion.duration with the declared action,
core-cms injects the tombstone field + Payload task definitions, and
bindAllProduction enqueues the first purge cycle at boot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:25:46 +02:00

62 lines
1.8 KiB
TypeScript

import { describe, it, expect, vi } from "vitest";
import type { Payload } from "payload";
import { buildRetentionPurgeTask } from "@/payload/retention-purge/task";
function makeFakePayload() {
const find = vi.fn().mockResolvedValue({ docs: [] });
const jobsQueue = vi.fn().mockResolvedValue({ id: "job-1" });
const payload = {
config: {
collections: [
{
slug: "users",
custom: {
retention: {
purgeSchedule: "daily",
postDeletion: {
duration: "P30D",
trigger: "after-deletion",
action: "hard-delete",
},
},
},
fields: [],
},
],
},
find,
update: vi.fn(),
delete: vi.fn(),
jobs: { queue: jobsQueue },
} as unknown as Payload;
return { payload, find, jobsQueue };
}
describe("buildRetentionPurgeTask (A3)", () => {
it("uses the retention-purge--<slug> task slug", () => {
expect(buildRetentionPurgeTask("users").slug).toBe(
"retention-purge--users",
);
});
it("runs the purge against req.payload and re-enqueues the next cycle", async () => {
const { payload, find, jobsQueue } = makeFakePayload();
const task = buildRetentionPurgeTask("users");
const result = await task.handler({ req: { payload } });
expect(result).toEqual({ output: {} });
// postDeletion branch queried the tombstone field
expect(find).toHaveBeenCalledWith(
expect.objectContaining({
collection: "users",
where: { deletedAt: expect.anything() },
}),
);
// self-re-enqueue went through the payload job queue
expect(jobsQueue).toHaveBeenCalledWith(
expect.objectContaining({ task: "retention-purge--users" }),
);
});
});