Kuma/tests/zahlungen/zahlungen-actions.test.ts
2026-10-02 14:30:54 +02:00

135 lines
4.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, expect, vi, beforeEach } from "vitest";
vi.mock("@/src/lib/auth/getUser", () => ({ requireManager: vi.fn() }));
vi.mock("@/src/lib/prisma", () => ({
prisma: {
courseBooking: { findMany: vi.fn() },
customerPass: { findMany: vi.fn() },
},
}));
import { getZahlungen } from "@/src/modules/zahlungen/actions/zahlungen-actions";
import { requireManager } from "@/src/lib/auth/getUser";
import { prisma } from "@/src/lib/prisma";
const booking = (id: string, createdAt: string) => ({
id,
createdAt: new Date(createdAt),
paymentStatus: "paid",
amountPaid: 2950,
platformFee: 50,
platformFeeOnTop: true,
stripeFee: 69,
stripeAccountId: "acct_studio",
refundedAmount: null,
refundedFeeAmount: null,
seatsPurchased: 1,
invoiceNumber: null,
course: { name: "Yoga", business: { id: "biz-1", name: "Studio" } },
user: { name: "Anna", email: "anna@example.com" },
guestLead: null,
});
const pass = (id: string, createdAt: string, status = "active") => ({
id,
createdAt: new Date(createdAt),
name: "10er-Karte",
email: "ben@example.com",
status,
amountPaid: 18500,
platformFee: 500,
stripeFee: 302,
invoiceNumber: null,
user: { name: "Ben" },
business: { id: "biz-1", name: "Studio" },
});
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(requireManager).mockResolvedValue({ businesses: [{ id: "biz-1" }] } as never);
});
describe("getZahlungen – Kursbuchungen und Kartenkäufe", () => {
it("mischt beide nach Datum und zeigt den Kartenpreis ohne Servicegebühr", async () => {
vi.mocked(prisma.courseBooking.findMany).mockResolvedValue([
booking("b2", "2026-10-03T10:00:00Z"),
booking("b1", "2026-10-01T10:00:00Z"),
] as never);
vi.mocked(prisma.customerPass.findMany).mockResolvedValue([
pass("p1", "2026-10-02T10:00:00Z"),
] as never);
const result = await getZahlungen({});
expect(result.success).toBe(true);
if (!result.success) return;
expect(result.zahlungen.map((z) => z.id)).toEqual(["b2", "p1", "b1"]);
expect(result.zahlungen[1]).toMatchObject({
kind: "pass",
courseName: "10er-Karte",
amountPaid: 18000,
stripeFeeCents: 302,
payoutCents: 17698,
paymentStatus: "paid",
});
expect(result.nextCursor).toBeNull();
});
it("blättert über beide Quellen mit Zeitpunkt und ID der letzten Zeile", async () => {
vi.mocked(prisma.courseBooking.findMany).mockResolvedValue([
booking("b2", "2026-10-03T10:00:00Z"),
booking("b1", "2026-10-01T10:00:00Z"),
] as never);
vi.mocked(prisma.customerPass.findMany).mockResolvedValue([
pass("p1", "2026-10-02T10:00:00Z"),
] as never);
const first = await getZahlungen({ limit: 2 });
if (!first.success) throw new Error(first.error);
expect(first.zahlungen.map((z) => z.id)).toEqual(["b2", "p1"]);
expect(first.nextCursor).toBe("2026-10-02T10:00:00.000Z_p1");
vi.mocked(prisma.courseBooking.findMany).mockResolvedValue([
booking("b1", "2026-10-01T10:00:00Z"),
] as never);
vi.mocked(prisma.customerPass.findMany).mockResolvedValue([] as never);
await getZahlungen({ limit: 2, cursor: first.nextCursor! });
const after = {
OR: [
{ createdAt: { lt: new Date("2026-10-02T10:00:00Z") } },
{ createdAt: new Date("2026-10-02T10:00:00Z"), id: { lt: "p1" } },
],
};
expect(vi.mocked(prisma.courseBooking.findMany).mock.calls[1][0]?.where).toMatchObject({
AND: [expect.anything(), after],
});
expect(vi.mocked(prisma.customerPass.findMany).mock.calls[1][0]?.where).toMatchObject({
AND: [expect.anything(), after],
});
});
it("lässt Kartenkäufe weg, wenn nach einem Status gefiltert wird, den Karten nicht haben", async () => {
vi.mocked(prisma.courseBooking.findMany).mockResolvedValue([] as never);
await getZahlungen({ status: "partially_refunded" });
expect(prisma.customerPass.findMany).not.toHaveBeenCalled();
});
it("zeigt erstattete Kartenkäufe als erstattet, ohne Rechnung", async () => {
vi.mocked(prisma.courseBooking.findMany).mockResolvedValue([] as never);
vi.mocked(prisma.customerPass.findMany).mockResolvedValue([
pass("p1", "2026-10-02T10:00:00Z", "refunded"),
] as never);
const result = await getZahlungen({ status: "refunded" });
if (!result.success) throw new Error(result.error);
expect(result.zahlungen[0].paymentStatus).toBe("pass_refunded");
expect(vi.mocked(prisma.customerPass.findMany).mock.calls[0][0]?.where).toMatchObject({
status: "refunded",
source: "purchase",
});
});
});