import { content } from "@deadhead/content"; import { boardPeriodIndex, boardPeriodMs } from "@deadhead/sim"; import { describe, expect, it } from "vitest"; import { acceptedContracts, contractBoards } from "../src/db/schema"; import { BOARD_RETENTION_PERIODS, cleanupExpiredContracts } from "../src/jobs/cleanup"; import { appJobs, CLEANUP_CONTRACTS_JOB } from "../src/jobs/definitions"; import { makeOwner, TEST_NOW, useTestDatabase } from "./db"; import { insertDispatch, putBoard, signUp, testContract, useTestApp } from "./game"; const database = useTestDatabase(); const testApp = useTestApp(database); describe("expired contract cleanup", () => { it("deletes old boards and accepted contracts and keeps current ones", async () => { const { db } = database(); const guest = await signUp(testApp().app); const { shipId } = await makeOwner(db, guest.playerId, guest.avatarId); const period = boardPeriodMs(content.config); const current = boardPeriodIndex(TEST_NOW, content.config); const old = current - BOARD_RETENTION_PERIODS - 1; // A board from long ago and one from the current period. await putBoard(db, guest.playerId, [testContract()], TEST_NOW - (current - old) * period); await putBoard(db, guest.playerId, [testContract()], TEST_NOW); // An accepted contract from each period, both pointing at real dispatches. const record = await insertDispatch(db, { playerId: guest.playerId, shipId, pilotId: guest.avatarId, departAt: TEST_NOW, }); await db.insert(acceptedContracts).values([ { playerId: guest.playerId, contractId: "old", periodIndex: old, dispatchId: record.id, acceptedAt: TEST_NOW, }, { playerId: guest.playerId, contractId: "new", periodIndex: current, dispatchId: record.id, acceptedAt: TEST_NOW, }, ]); expect(await cleanupExpiredContracts(db, TEST_NOW)).toEqual({ boards: 1, acceptedContracts: 1, }); expect((await db.select().from(contractBoards)).map((b) => b.periodIndex)).toEqual([current]); expect((await db.select().from(acceptedContracts)).map((a) => a.contractId)).toEqual(["new"]); }); it("keeps boards inside the retention window", async () => { const { db } = database(); const guest = await signUp(testApp().app); const period = boardPeriodMs(content.config); await putBoard( db, guest.playerId, [testContract()], TEST_NOW - BOARD_RETENTION_PERIODS * period, ); expect(await cleanupExpiredContracts(db, TEST_NOW)).toEqual({ boards: 0, acceptedContracts: 0, }); }); it("runs as an hourly job", () => { const job = appJobs({ db: database().db, clock: () => TEST_NOW }).find( (j) => j.name === CLEANUP_CONTRACTS_JOB, ); expect(job?.cron).toBe("0 * * * *"); }); });