Kuma/tests/api/stripe/checkout-course.test.ts
2026-09-11 12:06:58 +02:00

129 lines
4.4 KiB
TypeScript
Raw 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";
import { POST } from "@/src/app/api/stripe/checkout/course/route";
vi.mock("@/src/lib/auth/getUser", () => ({
getUserData: vi.fn(),
isUser: vi.fn(),
}));
vi.mock("@/src/lib/auth/account-role-by-email", () => ({
isManagerOrEmployeeEmail: vi.fn(),
}));
vi.mock("@/src/lib/prisma", () => ({
prisma: {
course: { findUnique: vi.fn() },
courseBooking: { findUnique: vi.fn() },
},
}));
vi.mock("@/src/lib/stripe", () => ({
stripe: { checkout: { sessions: { create: vi.fn() } } },
calculatePlatformFeeCents: vi.fn().mockReturnValue(100),
eurosToCents: vi.fn().mockReturnValue(2900),
}));
import { getUserData, isUser } from "@/src/lib/auth/getUser";
import { isManagerOrEmployeeEmail } from "@/src/lib/auth/account-role-by-email";
import { prisma } from "@/src/lib/prisma";
import { stripe } from "@/src/lib/stripe";
let ipCounter = 0;
function makeRequest(body: Record<string, unknown>) {
// Jede Anfrage aus einer eigenen IP, damit der In-Memory-Rate-Limiter
// (10/Minute/IP) die Tests nicht gegenseitig beeinflusst.
ipCounter += 1;
return new Request("http://localhost/api/stripe/checkout/course", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-forwarded-for": `10.0.0.${ipCounter}`,
},
body: JSON.stringify(body),
}) as never;
}
const publishedCourse = {
id: "course-1",
name: "Yoga",
status: "published",
price: 29,
date: new Date("2099-01-01"),
timeFrom: "10:00",
maxParticipants: 20,
_count: { bookings: 0 },
business: {
id: "biz-1",
name: "Studio",
stripeAccountId: "acct_123",
stripeAccountStatus: "active",
},
};
beforeEach(() => {
vi.mocked(getUserData).mockReset();
vi.mocked(isUser).mockReset();
vi.mocked(isManagerOrEmployeeEmail).mockReset();
vi.mocked(prisma.course.findUnique).mockReset();
vi.mocked(prisma.courseBooking.findUnique).mockReset();
vi.mocked(stripe.checkout.sessions.create).mockReset();
vi.mocked(stripe.checkout.sessions.create).mockResolvedValue({
url: "https://checkout.stripe.com/session",
} as never);
vi.mocked(prisma.course.findUnique).mockResolvedValue(publishedCourse as never);
});
describe("POST /api/stripe/checkout/course – Gast mit Team-Account-E-Mail (Ticket 6)", () => {
it("lehnt eine Gast-Buchung mit Manager/Employee-E-Mail mit 403 ab, ohne Stripe-Session", async () => {
vi.mocked(getUserData).mockResolvedValue(null as never);
vi.mocked(isManagerOrEmployeeEmail).mockResolvedValue(true);
const res = await POST(makeRequest({ courseId: "course-1", guestEmail: "Manager@Studio.de" }));
expect(res.status).toBe(403);
const data = await res.json();
expect(data.error).toMatch(/Team-Account/i);
expect(isManagerOrEmployeeEmail).toHaveBeenCalledWith("manager@studio.de");
expect(stripe.checkout.sessions.create).not.toHaveBeenCalled();
});
it("lässt eine normale Gast-Buchung durch, wenn die E-Mail zu keinem Team-Account gehört", async () => {
vi.mocked(getUserData).mockResolvedValue(null as never);
vi.mocked(isManagerOrEmployeeEmail).mockResolvedValue(false);
vi.mocked(prisma.courseBooking.findUnique).mockResolvedValue(null as never);
const res = await POST(makeRequest({ courseId: "course-1", guestEmail: "gast@example.com" }));
expect(res.status).toBe(200);
const data = await res.json();
expect(data.url).toBe("https://checkout.stripe.com/session");
expect(stripe.checkout.sessions.create).toHaveBeenCalledOnce();
});
it("prüft die Team-Account-E-Mail nicht für eingeloggte Kunden-Accounts", async () => {
vi.mocked(getUserData).mockResolvedValue({
id: "user-1",
email: "kunde@example.com",
} as never);
vi.mocked(isUser).mockReturnValue(true);
vi.mocked(prisma.courseBooking.findUnique).mockResolvedValue(null as never);
const res = await POST(makeRequest({ courseId: "course-1" }));
expect(res.status).toBe(200);
expect(isManagerOrEmployeeEmail).not.toHaveBeenCalled();
});
it("weist eine ungültige Gast-E-Mail weiterhin vor der Team-Account-Prüfung mit 400 ab", async () => {
vi.mocked(getUserData).mockResolvedValue(null as never);
const res = await POST(makeRequest({ courseId: "course-1", guestEmail: "keine-email" }));
expect(res.status).toBe(400);
const data = await res.json();
expect(data.isGuestRequired).toBe(true);
expect(isManagerOrEmployeeEmail).not.toHaveBeenCalled();
});
});