import type { SQL } from "drizzle-orm"; import { PgDialect } from "drizzle-orm/pg-core"; import { beforeEach, describe, expect, it, vi } from "vitest"; const candidates = vi.fn(); const returning = vi.fn(); const deleteWhere = vi.fn((condition: SQL) => { void condition; return { returning }; }); const deleteRows = vi.fn(() => ({ where: deleteWhere })); const select = vi.fn(() => ({ from: () => ({ leftJoin: () => ({ where: () => ({ orderBy: () => ({ limit: candidates }) }) }), where: () => ({}), }), })); const set = vi.fn(); const evalRedis = vi.fn().mockResolvedValue(1); vi.mock("server-only", () => ({})); vi.mock("@/db/client", () => ({ getDb: () => ({ select, delete: deleteRows }) })); vi.mock("@/lib/redis/client", () => ({ getRedisClient: async () => ({ set, eval: evalRedis }) })); const { removeExpiredUnpaidCheckouts } = await import("./cleanup"); describe("expired checkout cleanup", () => { beforeEach(() => { vi.clearAllMocks(); candidates.mockResolvedValue([{ id: "checkout-1" }, { id: "checkout-2" }]); set.mockResolvedValueOnce("OK").mockResolvedValueOnce(null); returning.mockResolvedValue([{ id: "checkout-1" }]); }); it("deletes only an unlocked expired checkout with no ticket", async () => { expect(await removeExpiredUnpaidCheckouts()).toBe(1); expect(deleteRows).toHaveBeenCalledOnce(); expect(set).toHaveBeenCalledWith(expect.stringContaining("checkout-1"), expect.any(String), "EX", 60, "NX"); expect(evalRedis).toHaveBeenCalledOnce(); const query = new PgDialect().sqlToQuery(deleteWhere.mock.calls[0][0]); expect(query.sql).toContain("not exists"); expect(query.sql).toContain('"created_at"'); expect(query.params).toContain("checkout-1"); }); });