import { test, expect } from "@playwright/test"; import { PrismaClient } from "@prisma/client"; import { bookOneAdult } from "../../fixtures/booking-flow"; const prisma = new PrismaClient(); test.afterAll(async () => { await prisma.$disconnect(); }); /** * UA-13 ✅ — client-forged booking total (matrix A1 / ISSUES C-1), guarded through the REAL browser. * We intercept the outgoing POST /bookings and rewrite reviewedTotalMinor (and every per-seat * seatFareMinor) to 1. The server has two trust branches — sum-of-seatFareMinor when all are present, * else reviewedTotalMinor — so the forge targets both. The server must recompute the authoritative * fare and REJECT the mismatched client amount with a 4xx, persisting nothing. */ test("UA-13: server rejects a client-forged reviewedTotalMinor=1 (C-1)", async ({ page }) => { const r = await bookOneAdult(page, { nationality: "Ethiopian", paymentMethod: "WALLET", tolerateBookingError: true, // Forge both the per-seat fares and the reviewed total → 1. mutateBookingBody: (body) => ({ ...body, reviewedTotalMinor: 1, passengers: (body.passengers ?? []).map((p: any) => ({ ...p, seatFareMinor: 1 })), }), }); // The real fare the engine computed is far above 1… expect(r.cardFareMinor).toBeGreaterThan(1000); // …the browser forced reviewedTotalMinor=1, and the server must REFUSE it with a 4xx. expect(r.reviewedTotalMinor).toBe(1); expect(r.bookingStatus).toBeGreaterThanOrEqual(400); expect(r.bookingStatus).toBeLessThan(500); // No booking id was returned, and no 1-minor booking was persisted. expect(r.bookingId).toBeFalsy(); const forged = await prisma.booking.findFirst({ where: { totalMinor: 1 } }); expect(forged).toBeNull(); });