test(orders): clean statement fixtures before ingest, not after
ci / lint-test (push) Failing after 40s

These tests insert a Westpac statement and a `DD *DOORDASH ...` charge, and
only removed them at the end of the test — so they survived into the next run,
where `reconcileCardLeg` could match one at ingest time and resolve an order
that was meant to park `awaiting_card_statement`.

That is a real ordering bug in the fixtures regardless. It is my best
explanation for the intermittent failure in "parks an unresolvable split",
but I could not reproduce it: seeding the exact leftover row and running the
old code passed anyway. So this is hygiene with a plausible mechanism, not a
confirmed fix — if that test fails again, this was not the cause.
This commit is contained in:
2026-07-27 11:01:23 +10:00
parent b6cd62f7b5
commit 4febf38292
@@ -142,6 +142,19 @@ describe("Order ingestion — invariants", () => {
beforeEach(async () => { beforeEach(async () => {
await queryRaw(`DELETE FROM expense_metadata WHERE source = 'email'`); await queryRaw(`DELETE FROM expense_metadata WHERE source = 'email'`);
await queryRaw(`DELETE FROM transactions WHERE description LIKE 'Order - %'`); await queryRaw(`DELETE FROM transactions WHERE description LIKE 'Order - %'`);
// The statement fixtures these tests insert survived into the next run, and
// reconcileCardLeg matched a leftover charge at ingest time — so an order
// meant to park "awaiting_card_statement" resolved immediately instead.
// That is the whole story behind the intermittent failure in "parks an
// unresolvable split": not a race, just fixtures that were never cleaned.
// Must happen BEFORE ingest, which is why cleaning up at the end of the
// test was not enough.
await queryRaw(
`DELETE FROM statements WHERE filename IN ('test-westpac-2026-03.pdf', 'panel-cba.pdf', 'panel-plain.pdf')`
);
await queryRaw(
`DELETE FROM transactions WHERE description IN ('DD *DOORDASH WOOLWORTHS MELBOURNE AUS', 'UBER *EATS ZURICH')`
);
}); });
it("I6: a credits order creates one transaction at face value", async () => { it("I6: a credits order creates one transaction at face value", async () => {
@@ -223,12 +236,6 @@ describe("Order ingestion — invariants", () => {
expect(res.transactionId).toBeNull(); expect(res.transactionId).toBeNull();
expect(res.flags).toContain("awaiting_card_statement"); expect(res.flags).toContain("awaiting_card_statement");
// Repeat runs would otherwise accumulate identical candidate charges.
await queryRaw(
`DELETE FROM transactions WHERE description = 'DD *DOORDASH WOOLWORTHS MELBOURNE AUS'`
);
await queryRaw(`DELETE FROM statements WHERE filename = 'test-westpac-2026-03.pdf'`);
// Statement arrives: card 8032 took 40.93 of the 60.93 order. // Statement arrives: card 8032 took 40.93 of the 60.93 order.
const st = await queryRow<{ id: number }>( const st = await queryRow<{ id: number }>(
`INSERT INTO statements (bank_name, account_number, filename) `INSERT INTO statements (bank_name, account_number, filename)