190 lines
5.5 KiB
TypeScript
190 lines
5.5 KiB
TypeScript
/// <reference types="vite/client" />
|
|
|
|
import { convexTest } from "convex-test";
|
|
import { describe, expect, test } from "vitest";
|
|
import { api } from "./_generated/api";
|
|
import type { Id } from "./_generated/dataModel";
|
|
import schema from "./schema";
|
|
|
|
const modules = import.meta.glob("./**/*.ts");
|
|
delete modules["./transactions.test.ts"];
|
|
|
|
describe("transactions.list", () => {
|
|
test("combines search, date range, account, category, and type filters", async () => {
|
|
const t = convexTest(schema, modules);
|
|
|
|
const seeded = await t.run(async (ctx) => {
|
|
const userId = await ctx.db.insert("users", {
|
|
name: "Filter User",
|
|
email: "filter@example.com",
|
|
});
|
|
const giroAccountId = await ctx.db.insert("accounts", {
|
|
userId,
|
|
name: "Girokonto",
|
|
type: "checking",
|
|
openingBalance: 0,
|
|
currency: "EUR",
|
|
isArchived: false,
|
|
});
|
|
const otherAccountId = await ctx.db.insert("accounts", {
|
|
userId,
|
|
name: "Depot",
|
|
type: "investment",
|
|
openingBalance: 0,
|
|
currency: "EUR",
|
|
isArchived: false,
|
|
});
|
|
const groceryCategoryId = await ctx.db.insert("categories", {
|
|
userId,
|
|
name: "Lebensmittel & Supermarkt",
|
|
kind: "ausgabe",
|
|
block: "variabel",
|
|
color: "#ef4444",
|
|
sortOrder: 1,
|
|
isSystem: false,
|
|
});
|
|
const restaurantCategoryId = await ctx.db.insert("categories", {
|
|
userId,
|
|
name: "Restaurant",
|
|
kind: "ausgabe",
|
|
block: "variabel",
|
|
color: "#f97316",
|
|
sortOrder: 2,
|
|
isSystem: false,
|
|
});
|
|
|
|
const matchingId = await ctx.db.insert("transactions", {
|
|
userId,
|
|
accountId: giroAccountId,
|
|
categoryId: groceryCategoryId,
|
|
bookingDate: "2026-06-15",
|
|
description: "LIDL SAGT DANKE",
|
|
amount: -18.37,
|
|
isPending: false,
|
|
effectiveMonth: "2026-06",
|
|
});
|
|
await ctx.db.insert("transactions", {
|
|
userId,
|
|
accountId: giroAccountId,
|
|
categoryId: groceryCategoryId,
|
|
bookingDate: "2026-05-29",
|
|
description: "LIDL OLD MONTH",
|
|
amount: -21.92,
|
|
isPending: false,
|
|
effectiveMonth: "2026-05",
|
|
});
|
|
await ctx.db.insert("transactions", {
|
|
userId,
|
|
accountId: otherAccountId,
|
|
categoryId: groceryCategoryId,
|
|
bookingDate: "2026-06-16",
|
|
description: "LIDL OTHER ACCOUNT",
|
|
amount: -99,
|
|
isPending: false,
|
|
effectiveMonth: "2026-06",
|
|
});
|
|
await ctx.db.insert("transactions", {
|
|
userId,
|
|
accountId: giroAccountId,
|
|
categoryId: restaurantCategoryId,
|
|
bookingDate: "2026-06-17",
|
|
description: "LIDL RESTAURANT",
|
|
amount: -12,
|
|
isPending: false,
|
|
effectiveMonth: "2026-06",
|
|
});
|
|
await ctx.db.insert("transactions", {
|
|
userId,
|
|
accountId: giroAccountId,
|
|
categoryId: groceryCategoryId,
|
|
bookingDate: "2026-06-18",
|
|
description: "LIDL REFUND",
|
|
amount: 5,
|
|
isPending: false,
|
|
effectiveMonth: "2026-06",
|
|
});
|
|
|
|
return { userId, giroAccountId, groceryCategoryId, matchingId };
|
|
});
|
|
|
|
const asUser = t.withIdentity({
|
|
subject: `${seeded.userId}|test-session`,
|
|
tokenIdentifier: `test:${seeded.userId}`,
|
|
});
|
|
|
|
const result = await asUser.query(api.transactions.list, {
|
|
paginationOpts: { cursor: null, numItems: 20 },
|
|
search: "LIDL",
|
|
from: "2026-06-01",
|
|
to: "2026-06-30",
|
|
accountId: seeded.giroAccountId as Id<"accounts">,
|
|
categoryIds: [seeded.groceryCategoryId as Id<"categories">],
|
|
type: "ausgabe",
|
|
basis: "booking",
|
|
});
|
|
|
|
expect(result.page.map((tx) => tx._id)).toEqual([seeded.matchingId]);
|
|
});
|
|
|
|
test("filters by effective month when the global basis is assignment month", async () => {
|
|
const t = convexTest(schema, modules);
|
|
|
|
const seeded = await t.run(async (ctx) => {
|
|
const userId = await ctx.db.insert("users", {
|
|
name: "Basis User",
|
|
email: "basis@example.com",
|
|
});
|
|
const accountId = await ctx.db.insert("accounts", {
|
|
userId,
|
|
name: "Girokonto",
|
|
type: "checking",
|
|
openingBalance: 0,
|
|
currency: "EUR",
|
|
isArchived: false,
|
|
});
|
|
const shiftedId = await ctx.db.insert("transactions", {
|
|
userId,
|
|
accountId,
|
|
bookingDate: "2026-05-29",
|
|
description: "Salary shifted into June",
|
|
amount: 2500,
|
|
isPending: false,
|
|
assignedMonth: "2026-06",
|
|
effectiveMonth: "2026-06",
|
|
});
|
|
await ctx.db.insert("transactions", {
|
|
userId,
|
|
accountId,
|
|
bookingDate: "2026-05-20",
|
|
description: "May only",
|
|
amount: -15,
|
|
isPending: false,
|
|
effectiveMonth: "2026-05",
|
|
});
|
|
|
|
return { userId, shiftedId };
|
|
});
|
|
|
|
const asUser = t.withIdentity({
|
|
subject: `${seeded.userId}|test-session`,
|
|
tokenIdentifier: `test:${seeded.userId}`,
|
|
});
|
|
|
|
const effectiveResult = await asUser.query(api.transactions.list, {
|
|
paginationOpts: { cursor: null, numItems: 20 },
|
|
from: "2026-06-01",
|
|
to: "2026-06-30",
|
|
basis: "effective",
|
|
});
|
|
const bookingResult = await asUser.query(api.transactions.list, {
|
|
paginationOpts: { cursor: null, numItems: 20 },
|
|
from: "2026-06-01",
|
|
to: "2026-06-30",
|
|
basis: "booking",
|
|
});
|
|
|
|
expect(effectiveResult.page.map((tx) => tx._id)).toEqual([seeded.shiftedId]);
|
|
expect(bookingResult.page).toEqual([]);
|
|
});
|
|
});
|