Files
buzz-sheet/lib/commission/mobile-slip.test.ts
T
gunshiz 2517982a27
CI / Verify (push) Successful in 2m4s
CI / Build immutable images and deploy (push) Successful in 3m8s
feat : also update
2026-10-01 19:47:02 +07:00

53 lines
2.5 KiB
TypeScript

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 });
});
});