> money-tracker@0.1.0 test > node --test 'tests/**/*.test.ts' ✔ the book page shows the March balance and its entries (83.733084ms) ✔ / goes to the latest month with entries (4.290333ms) ✔ recording an expense through the form lowers the balance (9.485042ms) ✔ nonsense is refused with a reason and nothing is stored (5.775625ms) ✔ the JSON API refuses nonsense too (6.955125ms) ✔ correcting and deleting through the web keep every total right (10.064791ms) ✔ 0.10 and 0.20 spent read as 0.30 spent (7.344625ms) ✔ unknown pages and entries answer 404 (4.419125ms) ✔ quantities are exact to eight decimals, in and out (2.581959ms) ✔ buying a fraction: 0.01750000 BTC on 2026-03-02 cost 1071.70 EUR (0.57775ms) ✔ what it is worth today: 1030.75 on 2026-03-20, unrealised -40.95 (0.327ms) ✔ selling settles part of it: 0.01000000 BTC left and -17.55 realised (0.207458ms) ✔ a price that is missing: the last known one, with its date, never zero (0.139458ms) ✔ the pinned tables are what the database seeds (one source, tied by a test) (59.821ms) ✔ through the web: buy, add a satoshi, value, sell, and one net-worth figure (92.9045ms) ✔ an entry keeps the money it really was and reports at its own day's rate (64.74325ms) ✔ home is a choice: every total re-reports in USD and no entry is rewritten (6.431958ms) ✔ a missing rate is said out loud and the amount is shown in its own currency (7.9925ms) ✔ totals never mix: a category spanning two currencies converts each entry at its own date (6.382ms) ✔ money moves between accounts at that day's rate and the transfer remembers it (8.244ms) ✔ rates parse and format exactly (1.745458ms) ✔ 120.00 USD on 2026-03-14 reports as 112.80 EUR (0.899667ms) ✔ 200.00 GBP on 2026-03-02 is 234.00 EUR (0.10325ms) ✔ converting into a non-EUR home divides by that day's rate, rounding half-up once (0.13675ms) ✔ a rate that is not there is missing, not guessed from another day (0.958875ms) ✔ a mixed total converts every item at its own date or refuses with the missing rates (0.184709ms) ✔ the March book balances at 3223.70 (9.507583ms) ✔ money goes out: recording 23.50 on groceries lowers the balance by 23.50 (4.29875ms) ✔ a mistake is corrected: the dinner becomes 54.00, once (24.470042ms) ✔ an entry is removed: deleting the metro top-up raises the balance by 18.90 (23.555ms) ✔ each category shows its own total for March (5.946333ms) ✔ nonsense is refused with a reason and the book is unchanged (9.288916ms) ✔ month arithmetic (0.474333ms) ✔ parses decimal text into exact minor units (3.822667ms) ✔ refuses what is not money (0.348583ms) ✔ 0.10 + 0.20 is exactly 0.30 (2.748583ms) ✔ formats minor units with grouping and a fixed number of decimals (0.112375ms) ✔ the role table is the only place permissions live (3.19975ms) ✔ an even split is exact to the cent and the remainder does not vanish (2.2475ms) ✔ strangers are sent to sign in; the API answers 401 (58.084125ms) ✔ a book is shared: Grace finds it among her books and can record an entry in it (12.505959ms) ✔ private stays private: Ada's own book is invisible to Grace, by page, by API and by guessed link (9.256291ms) ✔ splitting a bill: Ada pays 84.00 split evenly with Grace; Grace owes Ada 42.00 and both can see it (12.131666ms) ✔ a viewer cannot edit, even when the buttons are not on the screen (9.754ms) ✔ leaving the book: Grace loses access and her entries stay where they are (6.603083ms) ✔ assumed-user mode is off by default and explicit when on (7.829208ms) ✔ an emailed receipt is read into a draft and nothing is added to the book (41.579125ms) ✔ a photographed receipt is read the same way (4.414125ms) ✔ the person has the last word: a changed category is what gets saved, and the receipt stays with the entry (6.11ms) ✔ a reading that is not sure says so, invents nothing, and cannot be confirmed as is (4.621459ms) ✔ the lines must add up: a mismatch is reported before a confirm (4.082167ms) ✔ the same receipt twice is recognised and the existing entry offered (4.8355ms) ✔ with no reader able to read a file, every field is left to the person (9.739334ms) ✔ the model reader parses a Claude answer and the composed reader falls through to it (0.40025ms) ✔ the bundled samples all load and replay (0.394666ms) ℹ tests 55 ℹ suites 0 ℹ pass 55 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 605.863208