ledgerone_backend/test/transactions.service.spec.ts
2026-02-24 21:45:18 +00:00

93 lines
3.3 KiB
TypeScript

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();
});
});