import { createHash } from "node:crypto"; import { beforeEach, describe, expect, it, vi } from "vitest"; const limit = vi.fn(); const where = vi.fn(() => ({ limit })); const from = vi.fn(() => ({ where })); const select = vi.fn(() => ({ from })); const get = vi.fn(); const set = vi.fn(); const redis = { get, set }; vi.mock("server-only", () => ({})); vi.mock("@/db", () => ({ getDb: () => ({ select }) })); vi.mock("@/lib/redis/client", () => ({ getRedisClient: async () => redis })); const { authorizeMobileSlip, createMobileSlipLink } = await import("./mobile-slip"); const checkout = { id: "checkout-1", userId: "customer-1", amountBaht: 150 }; describe("commission mobile slip links", () => { beforeEach(() => { vi.clearAllMocks(); set.mockResolvedValue("OK"); }); it("requires checkout ownership before issuing a link", async () => { limit.mockResolvedValueOnce([]); await expect(createMobileSlipLink("checkout-1", "wrong-user")).rejects.toMatchObject({ status: 404 }); expect(set).not.toHaveBeenCalled(); }); it("issues a ten-minute link and rejects an expired or replaced token", async () => { limit.mockResolvedValueOnce([{ id: checkout.id }]).mockResolvedValueOnce([]); const { token, digest } = await createMobileSlipLink(checkout.id, checkout.userId); expect(token).toMatch(/^[A-Za-z0-9_-]{43}$/); expect(digest).toBe(createHash("sha256").update(token).digest("hex")); expect(set).toHaveBeenCalledWith(expect.stringContaining(":token:"), JSON.stringify({ checkoutId: checkout.id, userId: checkout.userId }), "EX", 600); expect(set).toHaveBeenCalledWith(expect.stringContaining(":active:"), digest, "EX", 600); get.mockResolvedValueOnce(null); await expect(authorizeMobileSlip(token)).rejects.toMatchObject({ status: 404 }); get.mockResolvedValueOnce(JSON.stringify({ checkoutId: checkout.id, userId: checkout.userId })) .mockResolvedValueOnce("another-digest"); await expect(authorizeMobileSlip(token)).rejects.toMatchObject({ status: 404 }); }); it("accepts the active token only for its stored checkout", async () => { const token = "x".repeat(43); const digest = createHash("sha256").update(token).digest("hex"); get.mockResolvedValueOnce(JSON.stringify({ checkoutId: checkout.id, userId: checkout.userId })) .mockResolvedValueOnce(digest); limit.mockResolvedValueOnce([checkout]).mockResolvedValueOnce([]); expect(await authorizeMobileSlip(token)).toEqual({ checkout, ticketId: null, digest }); }); });