Files
tyx_AI_xhs/tests/worker/wp1-05-retention-cleanup.test.ts
T

73 lines
3.5 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { RetentionCleanup } from "../../apps/worker/src/retention-cleanup.js";
import { RegistrationService } from "../../apps/api/src/registration.js";
import { MockResendAdapter } from "../../apps/api/src/resend-adapter.js";
import { registrationNotice } from "../../packages/shared-contracts/src/registration-notice.js";
const baseNow = Date.parse("2026-07-28T11:00:00.000Z");
const roots: string[] = [];
const services: RegistrationService[] = [];
function writeEvidence(value: unknown) {
const directory = process.env.DADA_EVIDENCE_DIR_RETENTION;
if (!directory) return;
mkdirSync(directory, { recursive: true });
writeFileSync(resolve(directory, "retention-events.json"), `${JSON.stringify(value, null, 2)}\n`);
}
afterEach(() => {
for (const service of services.splice(0)) service.close();
for (const root of roots.splice(0)) rmSync(root, { force: true, recursive: true });
});
describe("TDD-WP1-DEL-002 retention worker", () => {
it("refuses early/manual deletion and removes each row only at its original expiry", () => {
const root = mkdtempSync(join(tmpdir(), "dada-wp1-05-retention-"));
roots.push(root);
const databasePath = join(root, "dada.sqlite3");
const service = new RegistrationService({
challengePepper: Buffer.alloc(32, 0x61), clock: () => baseNow, codeGenerator: () => "120984",
currentPrivacyNoticeVersion: registrationNotice.version, databasePath,
invitePepper: Buffer.alloc(32, 0x62), resend: new MockResendAdapter(), sessionPepper: Buffer.alloc(32, 0x63),
});
services.push(service);
const accessExpiry = baseNow + 1_000;
const anonymousExpiry = baseNow + 2_000;
service.database.prepare(`
INSERT INTO private_content_access_logs (log_id, actor_ref, subject_ref, target_ref, content_type, occurred_at, expires_at)
VALUES (?, 'admin-fixture', ?, ?, 'prompt', ?, ?)
`).run(randomUUID(), randomUUID(), randomUUID(), baseNow - 10_000, accessExpiry);
service.database.prepare(`
INSERT INTO anonymous_retained_events (
event_id, anonymous_subject_id, event_type, model_id, outcome, error_category,
credit_delta, occurred_at, expires_at
) VALUES (?, ?, 'generation_commit', 'model-a', 'succeeded', NULL, -1, ?, ?)
`).run(randomUUID(), randomUUID(), baseNow - 10_000, anonymousExpiry);
expect(() => service.database.prepare("DELETE FROM anonymous_retained_events").run()).toThrow();
expect(() => service.database.prepare("DELETE FROM private_content_access_logs").run()).toThrow();
const early = new RetentionCleanup({ clock: () => baseNow, databasePath });
expect(early.purgeExpired()).toEqual({ anonymous_events: 0, private_access_logs: 0 });
early.close();
const firstExpiry = new RetentionCleanup({ clock: () => accessExpiry, databasePath });
expect(firstExpiry.purgeExpired()).toEqual({ anonymous_events: 0, private_access_logs: 1 });
firstExpiry.close();
const secondExpiry = new RetentionCleanup({ clock: () => anonymousExpiry, databasePath });
expect(secondExpiry.purgeExpired()).toEqual({ anonymous_events: 1, private_access_logs: 0 });
secondExpiry.close();
writeEvidence({
anonymous_deleted_at_expiry: true,
early_delete_blocked: true,
private_access_deleted_at_original_expiry: true,
});
});
});