ledgerone_backend/test/bill-pay.service.spec.ts

126 lines
3.8 KiB
TypeScript

/// <reference types="jest" />
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<typeof createPrismaMock>;
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");
});
});