F-11 (Medium): Test the production repository against real SQLite#10
Merged
Conversation
Every invariant the audit cares about — atomic commit/rollback, no partial writes, unique-constraint mapping, idempotency dedup, SQL date-range semantics — lives in SqlStorageRepository, which had zero tests in this repo: the suite ran only against the in-memory double, whose divergence was demonstrable (the audit mutated a returned entry's arrays and the tampering appeared in subsequent reads — the double aliased internal state; production re-hydrates per query and is immune). A refactor breaking the transaction block would have shipped with all unit tests green. - test/helpers/node-sql-storage.ts: a DurableObjectStorage stand-in over Node's built-in node:sqlite exposing exactly the surface the repository uses (exec with toArray()/one() cursors, transactionSync via SAVEPOINT). Foreign keys enforced by default, matching DO storage. - sqlite-repository.spec.ts drives the real repository: account uniqueness via the DB index, posting + balances + range filtering, trial balance with contra accounts, entry hydration, idempotent retries, and an atomicity proof — a balanced payload whose second amount row violates the FK leaves zero rows behind. - InMemoryRepository.toEntry now copies its line arrays, with a regression test, so the double matches production aliasing semantics. Audit finding F-11 (Medium). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
adamburmister
added a commit
that referenced
this pull request
Jul 18, 2026
…g test Main merged F-11 (repository test suite, PR #10) and F-16 (frozen entry arrays, PR #11) independently; combined, the in-memory aliasing test's push onto entry.debitAmounts now throws TypeError on the frozen array, failing on main itself and on every PR's merge CI. The freeze strengthens the invariant the test checks, so it now asserts the push throws and that later reads still come back clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finding
F-11 (Medium) from the ledger audit: the production
SqlStorageRepository— where atomicity (transactionSync), rollback, unique-constraint mapping, dedup-race recovery, and SQL date-range semantics actually live — had zero tests in this repo. The 78-test suite ran exclusively against the in-memory double, and the double demonstrably diverged: audit probe P7 pushed onto a returnedentry.debitAmounts(runtime arrays;readonlyis compile-time only) and the tampered line appeared in subsequentallEntries()reads — the double aliased its internal arrays, while production re-hydrates per query and is immune.Why it matters: a refactor that, say, moved the idempotency-key insert outside the transaction block would ship with every unit test green. Trusting an external demo app to exercise rollback paths is not a control.
Fix
test/helpers/node-sql-storage.ts: aDurableObjectStoragestand-in over Node's built-innode:sqlite(no new dependencies), exposing exactly the surface the repository uses —sql.exec(...)withtoArray()/one()cursors andtransactionSyncimplemented via SAVEPOINT.node:sqliteenforces foreign keys by default, matching DO storage.sqlite-repository.spec.tsdrives the real repository through theLedgerfacade and directly:balanceByTypegetEntry,entriesForAccount,amountsForAccount,getEntryByKey)RepositoryErrorand zero rows inpluts_entries/pluts_amounts— the rollback the design promises, now verified on every CI runInMemoryRepository.toEntrycopies its line arrays (regression test included), so the double matches production semantics.All six production-repository tests passed on first run — the existing transaction code is correct; it just had no in-repo proof.
Testing
Full suite: 85 passed (78 base + 7 new).
tsc --noEmitandbiome checkclean. The aliasing test was written first and watched fail against the unfixed double.Note: shares
test/types/node-sqlite.d.tswith #4/#5/#7/#8 (identical content — merges cleanly in any order).🤖 Generated with Claude Code