test(db): raise interloper-db coverage from 87% to 98% - #317
Merged
Conversation
Fills the untested surface of interloper-db, closing 223 of its 256 uncovered statements. Two modules had no tests at all. `engine.py` is straightforward. For `provision.py` — 18% before — the routines are Postgres-specific (an advisory lock, a maintenance connection to `postgres`, `pg_terminate_backend`), and the package unit-tests against in-memory SQLite with no integration marker anywhere, so the engine is faked and the statements it is handed are asserted: the lock brackets the work and is released even when the migration fails, view-backed models are left to Alembic, the maintenance DSN swap happens, and the drop evicts other sessions while sparing its own. The store facets make up the rest. `Store.from_settings` (the canonical constructor every long-lived process uses) is covered both ways round: a configured key attaches a working cipher, and no key logs the warning and leaves the store failing closed. Sessions, membership, invitations, the backfill progression arms, and the component-drift paths follow. Drift is exercised the way it arises rather than by hand: `create` rejects an unknown catalog key, so the row is written by a store whose catalog lists it and read by one whose catalog does not. That reports `disabled` (the class is still importable) rather than `missing`, which is the distinction the status resolver draws. Two guards are reachable only with an orphaned row — a session or token whose profile is gone — which the foreign keys prevent. Those are fabricated with `PRAGMA foreign_keys=OFF` and a comment saying so: the guard exists for a row that should not be there, and the test proves what it does rather than asserting the guard is dead. Three modules gained a test file at their mirror path because they carry behaviour beyond their declarations: `session.py` (the dialect dispatch that keeps every upsert portable), `models/columns.py` (the JSONB/JSON variance the whole schema rests on) and `models/components.py` (`parent_key`, `stamp_state`). `models/auth.py`, `models/quotas.py` and `models/tokens.py` stay without one — they are declarations only, already covered through the stores that write them, following the precedent of `models/test_runs.py`, which exists for real behaviour rather than for field definitions. The 33 statements left are the paths SQLite cannot reach — `EventStore.save` compiles Postgres `ON CONFLICT` — plus guards marked unreachable in the source and `TypeError` arms for a misregistered quota. By Digitl
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
Fills the untested surface of
interloper-db, closing 223 of its 256 uncovered statements (87% → 98%). Tests only: no source file changes.Coverage
provision.pyengine.pystore/auth.pystore/base.pystore/organisations.pystore/components.pystore/runs.pystore/events.pystore/hydration.pystore/relations.py,quotas/base.py,store/tokens.py,store/status.pyReviewer notes
provision.pyis faked, deliberately. Its routines are Postgres-specific — apg_advisory_lock, a maintenance connection topostgres,pg_terminate_backend— and the package unit-tests against in-memory SQLite with nointegrationmarker used anywhere in the repo. So the engine is faked and the statements it is handed are asserted: the lock brackets the work and is released even when the migration raises, view-backed models are left to Alembic (with a guard test so that filter can't silently become a no-op), the maintenance DSN swap happens underAUTOCOMMIT, and the drop evicts other sessions while sparing its own.Drift is produced the way it arises, not by hand.
createrejects an unknown catalog key, so a drifted row can't be written directly. Instead the row is written by a store whose catalog lists the key and read by one whose catalog does not — which reportsdisabled(the class is still importable) rather thanmissing. That distinction is the status resolver's, and my first draft got it wrong.Two guards need a row the schema forbids. A session or token whose profile is gone is unreachable through the store's API — the foreign key blocks it. Those two tests fabricate the orphan with
PRAGMA foreign_keys=OFFand a comment saying why: the guard exists for a row that should not be there, so the test proves what it does rather than asserting the guard is dead code.Store.from_settings— the canonical constructor every long-lived process uses — is covered both ways round: a configured key attaches a cipher that round-trips, and no key logs the warning and leaves the store failing closed rather than writing plaintext.On the test layout
The package already mirrors correctly, and the mechanical check passes in both directions with no orphans. Three modules gained a file at their mirror path because they carry behaviour beyond declarations:
session.py→ the dialect dispatch that keeps every upsert portablemodels/columns.py→ the JSONB-on-Postgres / JSON-elsewhere variance the whole schema rests onmodels/components.py→parent_keyandstamp_statemodels/auth.py,models/quotas.pyandmodels/tokens.pystay without one. They are field declarations only, already exercised through the stores that write them, and this follows the precedent ofmodels/test_runs.py— which exists for real behaviour (event_metadata), not to restate field definitions. Say the word if you'd rather every module carry a file regardless and I'll add them.Deliberately left uncovered
The 33 remaining statements are:
EventStore.save(7) — compiles PostgresON CONFLICT DO NOTHING, so SQLite can't reach it. Needs a live Postgres.# defensive: FKs make this unreachableinhydration.pyandcomponents.py)TypeErrorarms for a quota registered as the wrong typerelations.pyVerification
uv run ruff check,uv run ty check,uv run pytest packages/interloper-db(429 passed) and the full workspace suite (2532 passed, 2 skipped) all green; the db suite is stable across three runs. Pre-commit ran the same three hooks on the commit.By Digitl