55 lines
3.1 KiB
TypeScript
55 lines
3.1 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
limit: vi.fn(), verifyCommissionSlip: vi.fn(), write: vi.fn(), delete: vi.fn(),
|
|
paymentValues: vi.fn(), ticketReturning: vi.fn(), notify: vi.fn(), discord: vi.fn(),
|
|
}));
|
|
vi.mock("server-only", () => ({}));
|
|
vi.mock("@/lib/redis/client", () => ({ getRedisClient: async () => ({ set: async () => "OK", eval: async () => 0 }) }));
|
|
vi.mock("@/lib/media/inspect", () => ({ inspectImage: vi.fn() }));
|
|
vi.mock("@/lib/media/storage", () => ({ getMediaStorage: async () => ({ write: mocks.write, delete: mocks.delete }) }));
|
|
vi.mock("@/lib/commission/payment", () => ({ verifyCommissionSlip: mocks.verifyCommissionSlip }));
|
|
vi.mock("@/lib/commission/server", () => ({ notifyCommission: mocks.notify }));
|
|
vi.mock("@/lib/commission/discord", () => ({ notifyPaidTicketDiscord: mocks.discord }));
|
|
vi.mock("@/db", () => ({ getDb: () => ({
|
|
select: () => ({ from: () => ({ where: () => ({ limit: mocks.limit }) }) }),
|
|
transaction: async (fn: (tx: unknown) => Promise<unknown>) => {
|
|
let inserts = 0;
|
|
return fn({ insert: () => ++inserts === 1
|
|
? { values: () => ({ returning: mocks.ticketReturning }) }
|
|
: { values: mocks.paymentValues } });
|
|
},
|
|
}) }));
|
|
import { verifyAndCreateTicket } from "./slip-upload";
|
|
import type { Checkout } from "./slip-upload";
|
|
|
|
const checkout = { id: "checkout", userId: "customer", amountBaht: 100, createdAt: new Date() } as Checkout;
|
|
const file = new File(["image"], "slip.png", { type: "image/png" });
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mocks.limit.mockResolvedValue([]);
|
|
mocks.ticketReturning.mockResolvedValue([{ id: "ticket" }]);
|
|
mocks.paymentValues.mockResolvedValue(undefined);
|
|
mocks.write.mockResolvedValue(undefined);
|
|
mocks.delete.mockResolvedValue(undefined);
|
|
});
|
|
|
|
describe("verified commission payment persistence", () => {
|
|
it.each(["slip2go", "slipok"])("persists the %s provider without changing the bank transaction key", async (provider) => {
|
|
mocks.verifyCommissionSlip.mockResolvedValue({ provider, transRef: "bank-ref", referenceId: `${provider}:ref`,
|
|
amountBaht: 100, transferredAt: new Date(), providerData: { success: true } });
|
|
expect(await verifyAndCreateTicket(checkout, file)).toEqual({ ticketId: "ticket", created: true });
|
|
expect(mocks.paymentValues).toHaveBeenCalledWith(expect.objectContaining({ provider, transRef: "bank-ref",
|
|
checkoutId: "checkout", ticketId: "ticket" }));
|
|
});
|
|
it("rejects a bank transaction already paid through another provider and cleans up the upload", async () => {
|
|
mocks.verifyCommissionSlip.mockResolvedValue({ provider: "slipok", transRef: "bank-ref",
|
|
referenceId: "slipok:bank-ref", amountBaht: 100, transferredAt: new Date(), providerData: {} });
|
|
mocks.paymentValues.mockRejectedValueOnce(Object.assign(new Error("duplicate bank reference"), { code: "23505" }));
|
|
await expect(verifyAndCreateTicket(checkout, file)).rejects.toMatchObject({ status: 409, message: "payment-already-used" });
|
|
expect(mocks.delete).toHaveBeenCalledOnce();
|
|
expect(mocks.notify).not.toHaveBeenCalled();
|
|
expect(mocks.discord).not.toHaveBeenCalled();
|
|
});
|
|
});
|