Files
aurum/backend/tests/test_cash_flow.py
ZProger 8e46726b4d test: real backend pytest + E2E Playwright suites, both isolated from prod data
backend/tests/: 33 pytest tests against a real Postgres database
(aurum_test, created/migrated/dropped per run) exercised through the actual
HTTP API — not sqlite, not mocks, and the real `aurum` database is never
opened (app lifespan is skipped, get_session is overridden per test).
Covers transaction category-kind validation, year/month/date-range
filtering and pagination, dashboard summary arithmetic, budget status, cash
flow, and the /transactions/years picker.

e2e/: 6 Playwright specs driving a real browser against a fully separate
throwaway docker-compose stack (run.sh: separate project name, database,
port, and volume, torn down with `down -v` on exit) — regression coverage
for the Recent Transactions month bug, the Card grid-overflow bug, the
frozen-year bug, the Net Worth default range, and the Dashboard->
Transactions deep link, all fixed earlier this session.

Also synced the backend's APP_VERSION constant (stale at 0.15.2, embedded
in exported backups) with the frontend version.
2026-08-16 22:56:53 +03:00

85 lines
3.6 KiB
Python

"""Cash flow: month-by-month income vs. expense across an explicit date
range, or the full transaction history when no range is given.
"""
from decimal import Decimal
from httpx import AsyncClient
from tests.helpers import money, txn_payload
async def test_explicit_range_excludes_transactions_outside_it(client: AsyncClient, account_id, categories):
category_id = categories["Groceries"]["id"]
salary_id = categories["Salary"]["id"]
await client.post(
"/transactions", json=txn_payload(account_id, amount="1000.00", type="income", category_id=salary_id, date="2022-05-01")
)
await client.post(
"/transactions", json=txn_payload(account_id, amount="9999.00", type="income", category_id=salary_id, date="2021-01-01")
)
await client.post(
"/transactions", json=txn_payload(account_id, amount="9999.00", type="income", category_id=salary_id, date="2023-01-01")
)
resp = await client.get("/cash-flow", params={"start_date": "2022-01-01", "end_date": "2022-12-31"})
body = resp.json()
assert money(body["total_income"]) == Decimal("1000.00")
assert len(body["points"]) == 12 # every month of 2022, including zero months
assert body["start_date"] == "2022-01-01"
assert body["end_date"] == "2022-12-31"
async def test_transfers_are_excluded_from_totals(client: AsyncClient, account_id):
other = await client.post("/accounts", json={"name": "Savings", "type": "savings", "currency": "USD"})
other_id = other.json()["id"]
await client.post(
"/transactions",
json=txn_payload(account_id, type="transfer", amount="500.00", transfer_account_id=other_id, date="2022-03-01"),
)
resp = await client.get("/cash-flow", params={"start_date": "2022-01-01", "end_date": "2022-12-31"})
body = resp.json()
assert money(body["total_income"]) == 0
assert money(body["total_expense"]) == 0
async def test_no_range_falls_back_to_earliest_and_latest_transaction_dates(
client: AsyncClient, account_id, categories
):
category_id = categories["Groceries"]["id"]
await client.post("/transactions", json=txn_payload(account_id, category_id=category_id, date="2021-06-15"))
await client.post("/transactions", json=txn_payload(account_id, category_id=category_id, date="2024-02-10"))
resp = await client.get("/cash-flow")
body = resp.json()
assert body["start_date"] == "2021-06-15"
assert body["end_date"] == "2024-02-10"
async def test_no_transactions_returns_empty_points_and_null_bounds(client: AsyncClient):
resp = await client.get("/cash-flow")
body = resp.json()
assert body["points"] == []
assert body["start_date"] is None
assert body["end_date"] is None
assert money(body["total_net"]) == 0
async def test_monthly_points_split_income_and_expense_correctly(client: AsyncClient, account_id, categories):
category_id = categories["Groceries"]["id"]
salary_id = categories["Salary"]["id"]
await client.post(
"/transactions", json=txn_payload(account_id, type="income", amount="2000.00", category_id=salary_id, date="2022-04-01")
)
await client.post(
"/transactions", json=txn_payload(account_id, type="expense", amount="300.00", category_id=category_id, date="2022-04-15")
)
resp = await client.get("/cash-flow", params={"start_date": "2022-04-01", "end_date": "2022-04-30"})
point = resp.json()["points"][0]
assert point["year"] == 2022
assert point["month"] == 4
assert money(point["income"]) == Decimal("2000.00")
assert money(point["expense"]) == Decimal("300.00")
assert money(point["net"]) == Decimal("1700.00")