refactor(users-db): convert the seven users.db domain modules to store classes#155
Merged
Conversation
…e classes Final PR extending the catalogue architecture (CensusStore #153, ParsesStore #154): - backend/db_catalogue.py: the path-bound identity + dunder surface (repr/bool/eq/hash/fspath) extracts into PathBound; BaseCatalogue inherits it, and a new AsyncStoreBase(PathBound) is the base for the aiosqlite-backed users.db domains (which do NOT own schema — the package-level backend.server.db.init_db orchestrator is unchanged). - Seven domain modules become store classes sharing one users.db: UsersStore, ClaimsStore, FavoritesStore, ItemWatchStore, TokensStore, ServersStore (sync), RaidScheduleStore — each with a shared `store` instance. The 58 `path: Path = DB_PATH` kwargs threaded through every public function are gone; pure helpers (generate_token, hash_token, _server_row, _teams_with_slots) are staticmethods. - backend/server/db/__init__.py facade re-exports each store's BOUND METHODS under the original names, so the `users_db.get_active_claims(...)` API shape (and every from-package import) is preserved with zero route churn. ALL_STORES tuple lets conftest re-point every store in one loop. - Direct domain-module importers (favorites API, raid_schedule API, raid_live, character views pre-warm, server_context.load_registry — which passed DB_PATH positionally) migrate to store instances. - Tests: explicit path= kwargs stripped where they were the redirected default; genuinely-injected temp paths become XStore(tmp) constructions or autouse store-repoint fixtures; DB_PATH monkeypatches gain the instance-path repoint; stale patch strings move onto the store bindings. 1524 tests green, ruff + pyright clean. Live-verified /api/server (registry), raid-schedule and raiding-live endpoints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
VortexUK
added a commit
that referenced
this pull request
Jul 11, 2026
Follow-up review of #153-#155 (same 8-angle adversarial pipeline as #152); this fixes everything it found: Correctness hazards: - backend/server/db/__init__.py init_db now reads DB_PATH at call time (was `path: Path = DB_PATH` — the default-arg-captured-at-import pattern the arc eliminated everywhere else; under the documented early-import race, conftest's argless call would have run schema + migrations against the developer's real users.db). - backend/server/api/zones.py drops the import-time PARSES_DB_PATH value copy for the store's dynamic `parses_db.path` — the last consumer that a per-test store.path re-point couldn't reach. - tests/fixtures/parses_db.py: de-duplicated setattr (the second line now re-points DB_PATH as the docstring promised), docstring honest. Structure / dedup: - AsyncStoreBase gains the shared `_db()` asynccontextmanager — the 46 hand-rolled `aiosqlite.connect(self.path)` (+19 row_factory) sites in the six async domain stores collapse onto it; future connection-level policy (busy_timeout, FK pragma) has one home. - ServersStore (sync) moves off the async-named base onto PathBound. - BaseCatalogue._apply_migrations replaces the four drifted per-store migration loops (census logged, parses/recipes/zones swallowed silently) — skips now log at DEBUG uniformly. - CensusStore aligns with the .sql convention (new store.sql; inline schema/DML constants removed), FOREIGN_KEYS corrected to False (the schema declares no FKs), and sql_loader's stale "DDL stays embedded in the .py" docstring bullet updated to reality. - backend/db_catalogue.py log prefix [eq2db] → [db-catalogue]. Docs & tests: - The seven users.db domain docstrings + parses/db.py no longer teach the removed `path: Path = DB_PATH` convention; the inverted "CRITICAL: pass the explicit path" comments in two test files now describe the store model; orphaned test constants removed. - New tests/fixtures/users_db.py point_users_db_at() replaces the ~9 pasted (and already-forked) ALL_STORES re-point loops. - New tests/server/test_db_facade.py: facade completeness guard (a public store method with no alias fails the build with the name spelled out; intentional omissions live in _FACADE_EXEMPT, itself checked for staleness) + alias-integrity guard (every facade alias must be bound to an ALL_STORES instance). Three dead aliases (get_server_by_subdomain_sync, generate_token, hash_token) removed. 1527 tests green (3 new), ruff + pyright clean; /api/server, raid-schedule and character read paths live-verified. 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.
Summary
Final of the three PRs extending the catalogue architecture beyond eq2db (#153 CensusStore, #154 ParsesStore). This was the biggest win available: 58
path: Path = DB_PATHkwargs threaded through every public function across seven domain modules — the same import-time-binding hazard class we eliminated from the zones models in #152.Base layer
backend/db_catalogue.pysplits: the path-bound identity + dunder surface (__repr__/__bool__/__eq__/__hash__/__fspath__,clear_caches) extracts intoPathBound;BaseCatalogue(PathBound)keeps the sync templateinit_db+ read helpers; newAsyncStoreBase(PathBound)is the base for the aiosqlite-backed users.db domains. These stores deliberately do NOT own schema — the package-levelinit_db()orchestrator (SCHEMA + migrations + assertions) is unchanged, since the whole family shares one file.Domain stores
UsersStore,ClaimsStore,FavoritesStore,ItemWatchStore,TokensStore,ServersStore(sync sqlite3),RaidScheduleStore— one class per existing module, sharedstoreinstance each, per-call connections againstself.pathexactly as before. Pure helpers (generate_token,hash_token,_server_row,_teams_with_slots) are staticmethods per the convention.Facade preserved — zero route churn
backend/server/db/__init__.pyre-exports each store's bound methods under the original names, sousers_db.get_active_claims(...)and everyfrom backend.server.db import Xkeep working; the bound methods read the instance'spathdynamically, so one re-point per store covers every alias.ALL_STORESgives conftest a single loop. The only production call-site changes are the five direct domain-module importers (favorites API, raid-schedule API, raid_live, views pre-warm) andserver_context.load_registry— which was passingDB_PATHpositionally.Tests
Explicit
path=kwargs that were just the conftest-redirected default are stripped; genuinely-injected temp paths becomeXStore(tmp)constructions or autouse store-repoint fixtures;monkeypatch.setattr(db, "DB_PATH", …)sites gain the instance repoint (with write-before-repoint orderings fixed); stale patch strings move onto store bindings.Verification
Full suite 1524 green, ruff + pyright clean. Live-verified
/api/server(registry load through ServersStore), guild raid-schedule, and raiding-live on the dev backend.🤖 Generated with Claude Code