mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 20:51:18 +00:00
135 lines
4.5 KiB
TypeScript
135 lines
4.5 KiB
TypeScript
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",
|
||
});
|
||
});
|
||
});
|