/// import { Prisma } from "@prisma/client"; import { BillPayService } from "../src/bill-pay/bill-pay.service"; import { createPrismaMock } from "./utils/mock-prisma"; describe("BillPayService", () => { const userId = "user_1"; let prisma: ReturnType; let notifications: { notifyUser: jest.Mock }; let service: BillPayService; beforeEach(() => { prisma = createPrismaMock(); notifications = { notifyUser: jest.fn().mockResolvedValue({}) }; service = new BillPayService(prisma as never, notifications as never); prisma.auditLog.create.mockResolvedValue({}); }); it("creates a bill and sends a notification", async () => { const dueDate = new Date("2026-08-01T00:00:00.000Z"); prisma.bill.create.mockResolvedValue({ id: "bill_1", userId, name: "Electric", amount: new Prisma.Decimal(125), currency: "USD", dueDate, status: "pending", recurrence: "monthly", autopay: false, reminderDays: 3, payee: null, payments: [], createdAt: dueDate, updatedAt: dueDate, }); const bill = await service.createBill(userId, { name: "Electric", amount: 125, dueDate: "2026-08-01", recurrence: "monthly", }); expect(prisma.bill.create).toHaveBeenCalledWith(expect.objectContaining({ data: expect.objectContaining({ userId, name: "Electric", amount: expect.any(Prisma.Decimal), recurrence: "monthly", }), })); expect(notifications.notifyUser).toHaveBeenCalledWith(userId, expect.objectContaining({ type: "bill.created", title: "Bill added: Electric", })); expect(bill.computedStatus).toBe("pending"); }); it("marks a bill paid and records a payment", async () => { const dueDate = new Date("2026-08-01T00:00:00.000Z"); prisma.bill.findFirst.mockResolvedValue({ id: "bill_1", userId, name: "Internet", amount: new Prisma.Decimal(80), currency: "USD", dueDate, status: "pending", }); prisma.billPayment.create.mockResolvedValue({ id: "payment_1", billId: "bill_1", userId, amount: new Prisma.Decimal(80), method: "bank_bill_pay", }); prisma.bill.update.mockResolvedValue({ id: "bill_1", userId, name: "Internet", amount: new Prisma.Decimal(80), currency: "USD", dueDate, status: "paid", paidAt: new Date("2026-08-02T00:00:00.000Z"), payee: null, payments: [], }); const result = await service.markPaid(userId, "bill_1", { method: "bank_bill_pay", confirmationNumber: "ABC123", paidAt: "2026-08-02", }); expect(prisma.billPayment.create).toHaveBeenCalledWith(expect.objectContaining({ data: expect.objectContaining({ billId: "bill_1", method: "bank_bill_pay", confirmationNumber: "ABC123", }), })); expect(prisma.bill.update).toHaveBeenCalledWith(expect.objectContaining({ data: expect.objectContaining({ status: "paid" }), })); expect(result.bill.computedStatus).toBe("paid"); }); it("summarizes upcoming and overdue bills", async () => { const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000); const nextWeek = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); prisma.bill.findMany.mockResolvedValue([ { id: "b1", userId, name: "Overdue", amount: new Prisma.Decimal(10), dueDate: yesterday, status: "pending", payments: [] }, { id: "b2", userId, name: "Soon", amount: new Prisma.Decimal(20), dueDate: nextWeek, status: "scheduled", payments: [] }, ]); const result = await service.summary(userId); expect(result.activeCount).toBe(2); expect(result.overdueCount).toBe(1); expect(result.totalDueNext30).toBe("30"); }); });