import { TransactionsService } from "../src/transactions/transactions.service"; import { createPrismaMock } from "./utils/mock-prisma"; const createService = () => { const prisma = createPrismaMock(); const plaid = { syncTransactionsForUser: jest.fn() }; const service = new TransactionsService(prisma as any, plaid as any); return { service, prisma, plaid }; }; describe("TransactionsService", () => { it("calculates summary income/expense/net", async () => { const { service, prisma } = createService(); prisma.transactionRaw.findMany.mockResolvedValue([ { amount: -120.5, date: new Date("2025-01-01") }, { amount: 40, date: new Date("2025-01-02") }, { amount: -10, date: new Date("2025-01-03") } ]); const result = await service.summary("user_1", "2025-01-01", "2025-01-31"); expect(result.total).toBe("-90.50"); expect(result.income).toBe("130.50"); expect(result.expense).toBe("40.00"); expect(result.net).toBe("90.50"); expect(result.count).toBe(3); }); it("builds cashflow buckets for requested months", async () => { const { service, prisma } = createService(); prisma.transactionRaw.findMany.mockResolvedValue([ { amount: -200, date: new Date("2025-01-12") }, { amount: 50, date: new Date("2025-02-03") } ]); const result = await service.cashflow("user_1", 3); expect(result).toHaveLength(3); expect(result.some((row) => row.month.endsWith("-01"))).toBe(true); }); it("returns merchant insights sorted by spend", async () => { const { service, prisma } = createService(); prisma.transactionRaw.findMany.mockResolvedValue([ { description: "Coffee Bar", amount: 12.5 }, { description: "Coffee Bar", amount: 7.5 }, { description: "Grocer", amount: 40 }, { description: "Refund", amount: -10 } ]); const result = await service.merchantInsights("user_1", 2); expect(result[0].merchant).toBe("Grocer"); expect(result[0].total).toBe("40.00"); expect(result[1].merchant).toBe("Coffee Bar"); expect(result[1].count).toBe(2); }); it("creates manual transaction and derived fields", async () => { const { service, prisma } = createService(); prisma.account.findFirst.mockResolvedValue({ id: "acct_1", userId: "user_1" }); prisma.transactionRaw.create.mockResolvedValue({ id: "tx_1" }); prisma.transactionDerived.create.mockResolvedValue({ id: "derived_1" }); const result = await service.createManualTransaction({ userId: "user_1", accountId: "acct_1", date: "2025-01-15", description: "Manual payment", amount: 123.45, category: "Operations", note: "Test note", hidden: false }); expect(result).toEqual({ id: "tx_1" }); expect(prisma.transactionRaw.create).toHaveBeenCalled(); expect(prisma.transactionDerived.create).toHaveBeenCalled(); }); it("returns null when no account is available for manual transaction", async () => { const { service, prisma } = createService(); prisma.account.findFirst.mockResolvedValue(null); const result = await service.createManualTransaction({ userId: "user_1", date: "2025-01-15", description: "Manual payment", amount: 10 }); expect(result).toBeNull(); expect(prisma.transactionRaw.create).not.toHaveBeenCalled(); }); });