Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

38 lines · 2.7 KBRaw History
1import { describe, expect, it } from "vitest";
2import { COMPUTE_RETENTION_DAYS, NOTIFICATION_RETENTION_DAYS, pruneExpired } from "../../src/lib/retention.js";
3import { testD1 } from "./stubs/d1.js";
4
5describe("retention", () => {
6 it("deletes expired sessions, old notifications and old compute records, and keeps the rest", async () => {
7 const DB = testD1();
8 const at = new Date("2026-10-14T10:00:00Z");
9 const ago = (d: number) => new Date(at.getTime() - d * 86400_000).toISOString();
10 DB.sqlite.prepare("INSERT INTO accounts (id, login, kind, status, created_at) VALUES ('u', 'u', 'user', 'active', ?)").run(ago(1000));
11 const session = DB.sqlite.prepare("INSERT INTO sessions (id, account_id, method, created_at, last_seen_at, expires_at) VALUES (?, 'u', 'passkey', ?, ?, ?)");
12 session.run("expired", ago(40), ago(10), ago(2));
13 session.run("just-expired", ago(40), ago(1), ago(0.5));
14 session.run("live", ago(1), ago(0), ago(-29));
15 const note = DB.sqlite.prepare("INSERT INTO notifications (id, account_id, kind, title, url, created_at) VALUES (?, 'u', 'issues', 't', 'u', ?)");
16 note.run("old", ago(NOTIFICATION_RETENTION_DAYS + 1));
17 note.run("recent", ago(NOTIFICATION_RETENTION_DAYS - 1));
18 const usage = DB.sqlite.prepare("INSERT INTO compute_usage (id, account_id, repo_id, kind, started_at, ended_at) VALUES (?, 'u', 'r', 'job', ?, ?)");
19 usage.run("old-job", ago(COMPUTE_RETENTION_DAYS + 2), ago(COMPUTE_RETENTION_DAYS + 1));
20 usage.run("recent-job", ago(30), ago(30));
21 usage.run("running", ago(0), null);
22 expect(await pruneExpired(DB, at)).toEqual({ sessions: 1, notifications: 1, compute_usage: 1, early_access: 0, early_access_attempts: 0, contact_messages: 0, contact_attempts: 0 });
23 const ids = (t: string) => (DB.sqlite.prepare(`SELECT id FROM ${t} ORDER BY id`).all() as Array<{ id: string }>).map((r) => r.id);
24 expect(ids("sessions")).toEqual(["just-expired", "live"]);
25 expect(ids("notifications")).toEqual(["recent"]);
26 expect(ids("compute_usage")).toEqual(["recent-job", "running"]);
27 });
28
29 it("deletes a bounded batch at a time", async () => {
30 const DB = testD1();
31 const at = new Date("2026-10-14T10:00:00Z");
32 DB.sqlite.prepare("INSERT INTO accounts (id, login, kind, status, created_at) VALUES ('u', 'u', 'user', 'active', '2020-01-01T00:00:00Z')").run();
33 const note = DB.sqlite.prepare("INSERT INTO notifications (id, account_id, kind, title, url, created_at) VALUES (?, 'u', 'issues', 't', 'u', '2020-01-01T00:00:00Z')");
34 for (let i = 0; i < 5; i++) note.run(`n${i}`);
35 expect((await pruneExpired(DB, at, 3)).notifications).toBe(3);
36 expect((await pruneExpired(DB, at, 3)).notifications).toBe(2);
37 });
38});