feat(expenses): add edit amount and due date for pending expenses

This commit is contained in:
Jose Selesan
2026-06-04 11:00:36 -03:00
parent 5b6ccf0fa6
commit 55098a7b95
10 changed files with 366 additions and 11 deletions

View File

@@ -11,6 +11,7 @@ const mockPrisma = {
expense: {
findMany: mock(),
findFirst: mock(),
findUniqueOrThrow: mock(),
create: mock(),
update: mock(),
count: mock(),
@@ -36,6 +37,7 @@ const {
createPeriodicExpenseSchema,
createNonPeriodicExpenseSchema,
payExpenseSchema,
updateExpenseSchema,
buildDueDate,
getCurrentYearMonthUTC,
listPeriodicExpenses,
@@ -45,6 +47,7 @@ const {
listExpenses,
createNonPeriodicExpense,
payExpense,
updateExpense,
generateExpensesForCurrentMonth,
generateMonthlyExpense,
} = await import("../src/modules/expenses/expenses.service");
@@ -179,6 +182,35 @@ describe("schemas", () => {
expect(() => payExpenseSchema.parse({ amountPayed: 0 })).toThrow();
});
});
describe("updateExpenseSchema", () => {
test("accepts valid input", () => {
const result = updateExpenseSchema.parse({
amount: 2500,
dueDate: "2026-07-15T00:00:00.000Z",
});
expect(result.amount).toBe(2500);
expect(result.dueDate).toBe("2026-07-15T00:00:00.000Z");
});
test("rejects non-positive amount", () => {
expect(() =>
updateExpenseSchema.parse({
amount: 0,
dueDate: "2026-07-15T00:00:00.000Z",
}),
).toThrow();
});
test("rejects invalid dueDate", () => {
expect(() =>
updateExpenseSchema.parse({
amount: 100,
dueDate: "not-a-date",
}),
).toThrow();
});
});
});
describe("buildDueDate", () => {
@@ -430,6 +462,45 @@ describe("payExpense", () => {
});
});
describe("updateExpense", () => {
test("updates amount and dueDate for a PENDING expense", async () => {
const existing = {
id: 1,
status: PaymentStatus.PENDING,
amount: 1500,
dueDate: new Date("2026-06-15T00:00:00.000Z"),
year: 2026,
month: 6,
};
mockPrisma.expense.findUniqueOrThrow.mockResolvedValueOnce(existing);
mockPrisma.expense.update.mockImplementationOnce(
async ({ where, data }) => ({ id: where.id, ...existing, ...data }),
);
const result = await updateExpense(1, {
amount: 2000,
dueDate: "2026-07-20T00:00:00.000Z",
});
expect(result.amount).toBe(2000);
expect(result.year).toBe(2026);
expect(result.month).toBe(7);
});
test("rejects update for PAYED expense", async () => {
mockPrisma.expense.findUniqueOrThrow.mockResolvedValueOnce({
id: 1,
status: PaymentStatus.PAYED,
});
expect(
updateExpense(1, {
amount: 100,
dueDate: "2026-07-15T00:00:00.000Z",
}),
).rejects.toThrow("Solo se pueden editar gastos pendientes");
});
});
describe("generateExpensesForCurrentMonth", () => {
test("skips existing expenses to avoid duplicates", async () => {
const { month } = getCurrentYearMonthUTC();