Files
aurum/backend/tests
technosexual 949d2df37f fix: enforce transfer invariants when updating a transaction (#3)
The transfer rules — destination required, destination different from the
source account, no category, and no destination on a non-transfer — were
enforced by TransactionBase's validator on create, but the PATCH route only
re-checked category-vs-type.

So `PATCH /api/transactions/{id} {"type": "transfer"}` on an ordinary
expense committed a transfer row with transfer_account_id = NULL, and only
then failed serializing the response (TransactionRead inherits the same
validator). The caller got a 500 and would reasonably assume nothing was
saved — but from that point on every `GET /api/transactions` failed the same
validation, so the transactions list, the app's main screen, returned 500
permanently, with no way left in the UI to delete the offending row.

The rules now live in one place, transfer_rule_violation(), used by both the
create schema and the update route, so the two paths can't drift apart
again. The update path validates the row as it would look after the patch
rather than only the fields sent, since switching type alone invalidates
fields that were left untouched.

The frontend already sends a complete payload on edit (explicit nulls for
category_id / transfer_account_id) and does these same checks client-side,
so this only closes the hole for direct API calls — no UI change needed.


Claude-Session: https://claude.ai/code/session_01UmuMDFrwdvQkce885VaaTh

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 02:42:37 +03:00
..

Backend tests

Real integration tests against a real Postgres database — a dedicated aurum_test database created fresh, migrated, and dropped again on every run. They never touch the real aurum database or its data: the app's lifespan startup (which seeds against the real DB) is never triggered, and every route's DB dependency is overridden to point at aurum_test instead.

Running

The stack must already be up (docker compose up -d). Test dependencies aren't baked into the production image, so install them once per container lifetime, then run pytest inside the running backend container — it's the only place with network access to Postgres:

docker compose exec backend pip install -r requirements-dev.txt
docker compose exec backend pytest -v

Re-run just pytest -v for subsequent runs; the pip install only needs repeating after a container restart/rebuild.

Adding a test

  • Use the client fixture (an httpx.AsyncClient wired to the app) to hit the API the same way the frontend does — prefer this over reaching into services/models directly, so tests keep verifying the actual contract.
  • account_id and categories fixtures give you the same default account/categories a real fresh install seeds.
  • Every table is truncated and reseeded before each test (see conftest.py::_clean_database), so tests don't need to worry about leftover state or guess at IDs from a previous test.