import { Test, TestingModule } from "@nestjs/testing"; import { INestApplication, ValidationPipe } from "@nestjs/common"; import request from "supertest"; import { AppModule } from "../../app.module"; import { PrismaService } from "../../common/prisma.service"; describe("Payments E2E", () => { let app: INestApplication; let prisma: PrismaService; let authToken: string; let bookingId: string; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); app.useGlobalPipes( new ValidationPipe({ transform: true, whitelist: true }), ); await app.init(); prisma = app.get(PrismaService); const testUser = await prisma.user.create({ data: { email: "payment-test@example.com", phone: "+251911111112", fullName: "Payment Test User", passwordHash: "$2b$10$abcdefghijklmnopqrstuvwxyz", role: "PASSENGER", }, }); const passenger = await prisma.passenger.create({ data: { userId: testUser.id }, }); await prisma.walletAccount.create({ data: { passengerId: passenger.id, balanceMinor: 100000, currency: "ETB", }, }); authToken = "mock-jwt-token"; const station1 = await prisma.station.create({ data: { code: "TST1", name: "Test Station 1", city: "Test City", lat: 9.0, lng: 38.0, }, }); const station2 = await prisma.station.create({ data: { code: "TST2", name: "Test Station 2", city: "Test City 2", lat: 9.5, lng: 38.5, }, }); const train = await prisma.train.create({ data: { number: "TEST-001", name: "Test Train" }, }); const schedule = await prisma.trainSchedule.create({ data: { trainId: train.id, originStationId: station1.id, destinationStationId: station2.id, departureAt: new Date(Date.now() + 86400000), arrivalAt: new Date(Date.now() + 90000000), durationMinutes: 60, }, }); const coachType = await prisma.coachType.create({ data: { name: "Standard", code: "STD" }, }); const seatClass = await prisma.seatClass.create({ data: { name: "Economy Regular", description: "Standard economy seating", baseFareMinor: 45000, isActive: true, coachTypeId: coachType.id, }, }); const coach = await prisma.coach.create({ data: { coachTypeId: coachType.id, number: "TEST-C1", arrangement: "2+2", capacity: 10, status: "ACTIVE", }, }); const seat = await prisma.seat.create({ data: { coachId: coach.id, row: 1, col: "A", seatNumber: "1A", status: "AVAILABLE", }, }); const booking = await prisma.booking.create({ data: { bookingRef: "TEST-BOOK-001", passengerId: passenger.id, scheduleId: schedule.id, status: "PENDING_PAYMENT", totalMinor: 50000, currency: "ETB", }, }); await prisma.bookingSeat.create({ data: { bookingId: booking.id, seatId: seat.id, passengerName: "Test Passenger", }, }); bookingId = booking.id; }); afterAll(async () => { await prisma.$transaction([ prisma.bookingSeat.deleteMany(), prisma.paymentIntent.deleteMany(), prisma.booking.deleteMany(), prisma.coachAssignment.deleteMany(), prisma.seat.deleteMany(), prisma.coach.deleteMany(), prisma.trainSchedule.deleteMany(), prisma.train.deleteMany(), prisma.station.deleteMany({ where: { code: { in: ["TST1", "TST2"] } } }), prisma.walletLedgerEntry.deleteMany(), prisma.walletAccount.deleteMany(), prisma.passenger.deleteMany(), prisma.user.deleteMany({ where: { email: "payment-test@example.com" } }), ]); await app.close(); }); describe("POST /payments/initiate", () => { it("should initiate wallet payment successfully", async () => { const response = await request(app.getHttpServer()) .post("/payments/initiate") .set("Authorization", `Bearer ${authToken}`) .send({ bookingId, method: "WALLET" }) .expect(201); expect(response.body.intentId).toBeDefined(); expect(response.body.status).toBe("SUCCEEDED"); }); it("should return 400 for invalid payment method", async () => { await request(app.getHttpServer()) .post("/payments/initiate") .set("Authorization", `Bearer ${authToken}`) .send({ bookingId, method: "INVALID_METHOD" }) .expect(400); }); it("should return 404 for non-existent booking", async () => { await request(app.getHttpServer()) .post("/payments/initiate") .set("Authorization", `Bearer ${authToken}`) .send({ bookingId: "non-existent-id", method: "WALLET" }) .expect(404); }); }); describe("GET /payments/intents/:bookingId", () => { it("should get payment intent status", async () => { const response = await request(app.getHttpServer()) .get(`/payments/intents/${bookingId}`) .set("Authorization", `Bearer ${authToken}`) .expect(200); expect(response.body.intentId).toBeDefined(); expect(response.body.status).toBeDefined(); }); it("should return 404 for non-existent intent", async () => { await request(app.getHttpServer()) .get("/payments/intents/non-existent-booking") .set("Authorization", `Bearer ${authToken}`) .expect(404); }); }); // Provider webhooks moved to the payment microservice (apps/edr-payment-api /webhooks/*). });