mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 21:41:17 +00:00
127 lines
4.6 KiB
TypeScript
127 lines
4.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
vi.mock("@/src/lib/prisma", () => ({
|
|
prisma: {
|
|
customerPass: { findUnique: vi.fn(), create: vi.fn() },
|
|
product: { findUnique: vi.fn() },
|
|
user: { findUnique: vi.fn() },
|
|
},
|
|
}));
|
|
vi.mock("@/src/lib/stripe", () => ({
|
|
stripe: { paymentIntents: { retrieve: vi.fn() } },
|
|
onAccount: (id: string | null) => (id ? { stripeAccount: id } : undefined),
|
|
}));
|
|
vi.mock("@/src/lib/stripe-fee", () => ({ recordStripeFee: vi.fn() }));
|
|
vi.mock("@/src/lib/stripe-refund", () => ({ refundStripePayment: vi.fn() }));
|
|
vi.mock("@/src/lib/mail/resend", () => ({ sendMail: vi.fn() }));
|
|
vi.mock("@/src/modules/notifications/lib/create-notification", () => ({
|
|
notifyBusinessManager: vi.fn(),
|
|
}));
|
|
|
|
import {
|
|
handlePassCheckoutCompleted,
|
|
isPassCheckout,
|
|
} from "@/src/modules/products/lib/pass-webhook";
|
|
import { prisma } from "@/src/lib/prisma";
|
|
import { stripe } from "@/src/lib/stripe";
|
|
import { recordStripeFee } from "@/src/lib/stripe-fee";
|
|
import { refundStripePayment } from "@/src/lib/stripe-refund";
|
|
import { sendMail } from "@/src/lib/mail/resend";
|
|
import { notifyBusinessManager } from "@/src/modules/notifications/lib/create-notification";
|
|
|
|
const session = {
|
|
id: "cs_1",
|
|
payment_status: "paid",
|
|
payment_intent: "pi_1",
|
|
amount_total: 12500,
|
|
customer_details: null,
|
|
metadata: { kind: "pass", productId: "prod-1", userId: "user-1" },
|
|
};
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.mocked(prisma.customerPass.findUnique).mockResolvedValue(null);
|
|
vi.mocked(prisma.customerPass.create).mockResolvedValue({ id: "pass-1" } as never);
|
|
vi.mocked(prisma.product.findUnique).mockResolvedValue({
|
|
id: "prod-1",
|
|
name: "10er-Karte",
|
|
credits: 10,
|
|
validityMonths: 6,
|
|
sports: ["pilates"],
|
|
businessId: "biz-1",
|
|
business: { name: "Studio", branding: null },
|
|
} as never);
|
|
vi.mocked(prisma.user.findUnique).mockResolvedValue({
|
|
id: "user-1",
|
|
email: "Kunde@Example.com",
|
|
name: "Anna",
|
|
} as never);
|
|
vi.mocked(stripe.paymentIntents.retrieve).mockResolvedValue({
|
|
application_fee_amount: 500,
|
|
} as never);
|
|
vi.mocked(recordStripeFee).mockResolvedValue(undefined);
|
|
vi.mocked(sendMail).mockResolvedValue(undefined as never);
|
|
vi.mocked(notifyBusinessManager).mockResolvedValue(undefined as never);
|
|
});
|
|
|
|
describe("isPassCheckout", () => {
|
|
it("erkennt Kartenkäufe an den Metadaten", () => {
|
|
expect(isPassCheckout(session as never)).toBe(true);
|
|
expect(isPassCheckout({ metadata: { courseId: "c1" } } as never)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("handlePassCheckoutCompleted", () => {
|
|
it("legt die Karte mit Momentaufnahme des Produkts und Ablaufdatum an", async () => {
|
|
await handlePassCheckoutCompleted(session as never, "acct_studio");
|
|
|
|
const { data } = vi.mocked(prisma.customerPass.create).mock.calls[0][0];
|
|
expect(data).toMatchObject({
|
|
productId: "prod-1",
|
|
businessId: "biz-1",
|
|
userId: "user-1",
|
|
email: "kunde@example.com",
|
|
name: "10er-Karte",
|
|
sports: ["pilates"],
|
|
creditsTotal: 10,
|
|
creditsRemaining: 10,
|
|
source: "purchase",
|
|
amountPaid: 12500,
|
|
platformFee: 500,
|
|
priceCents: 12000,
|
|
stripeAccountId: "acct_studio",
|
|
stripeSessionId: "cs_1",
|
|
});
|
|
expect(data.validUntil).toBeInstanceOf(Date);
|
|
expect(recordStripeFee).toHaveBeenCalledWith("pi_1", "acct_studio");
|
|
expect(sendMail).toHaveBeenCalled();
|
|
expect(vi.mocked(notifyBusinessManager).mock.calls[0][1].type).toBe("pass.sold");
|
|
});
|
|
|
|
it("legt bei erneuter Zustellung derselben Session keine zweite Karte an", async () => {
|
|
vi.mocked(prisma.customerPass.findUnique).mockResolvedValue({ id: "pass-1" } as never);
|
|
await handlePassCheckoutCompleted(session as never, "acct_studio");
|
|
expect(prisma.customerPass.create).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("wartet bei SEPA, bis die Zahlung angekommen ist", async () => {
|
|
await handlePassCheckoutCompleted({ ...session, payment_status: "unpaid" } as never, "acct");
|
|
expect(prisma.customerPass.create).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("erstattet, wenn das Produkt inzwischen gelöscht wurde", async () => {
|
|
vi.mocked(prisma.product.findUnique).mockResolvedValue(null);
|
|
vi.mocked(refundStripePayment).mockResolvedValue({
|
|
success: true,
|
|
stripeRefundId: "re_1",
|
|
amountRefundedCents: 12500,
|
|
});
|
|
|
|
await handlePassCheckoutCompleted(session as never, "acct_studio");
|
|
|
|
expect(prisma.customerPass.create).not.toHaveBeenCalled();
|
|
expect(refundStripePayment).toHaveBeenCalledWith(
|
|
expect.objectContaining({ paymentIntentId: "pi_1", refundApplicationFee: true })
|
|
);
|
|
});
|
|
});
|