From 94fe57a59603ffb12c0057ffe25122f6493e3a2d Mon Sep 17 00:00:00 2001 From: Rene Arumetsa Date: Sun, 26 Jul 2026 21:36:26 +0300 Subject: [PATCH] Add startup scheme --- bot.py | 15 +++++++++++++++ core/economy.py | 9 +++++++++ core/pb_client.py | 6 ++++++ tests/conftest.py | 7 ++++++- tests/test_quests.py | 6 ++++++ 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 29158d3..2fd659d 100644 --- a/bot.py +++ b/bot.py @@ -383,6 +383,21 @@ async def on_ready(): log.info("Logged in as %s (ID: %s)", bot.user, bot.user.id) economy.set_house(bot.user.id) + # PocketBase silently drops writes to fields missing from the collection + # schema, so surface any drift loudly instead of letting features no-op. + try: + missing = await economy.missing_schema_fields() + if missing: + log.error( + "PocketBase collection '%s' is missing %d schema field(s): %s " + "- writes to them are silently dropped! Run scripts/sync_pb_schema.py.", + config.PB_ECONOMY_COLLECTION, len(missing), ", ".join(missing), + ) + else: + log.info("PocketBase schema check passed (%s)", config.PB_ECONOMY_COLLECTION) + except Exception as e: + log.warning("Could not verify PocketBase schema: %s", e) + _apply_profile_command_filters() # Pull sheet data into cache diff --git a/core/economy.py b/core/economy.py index 6ec6619..fee1aea 100644 --- a/core/economy.py +++ b/core/economy.py @@ -573,6 +573,15 @@ async def do_spam_jail(user_id: int) -> None: # --------------------------------------------------------------------------- # Public helpers # --------------------------------------------------------------------------- +async def missing_schema_fields() -> list[str]: + """Compare the live PocketBase collection schema against every field the + bot persists. PocketBase silently drops writes to undeclared fields, so + any name returned here means broken features without error messages.""" + live = await pb_client.get_collection_fields() + expected = set(_default_user()) | {"user_id"} + return sorted(expected - live) + + async def get_all_users_raw() -> dict[str, "UserData"]: """Return a snapshot of all user records.""" records = await pb_client.list_all_records() diff --git a/core/pb_client.py b/core/pb_client.py index b15a495..78dba61 100644 --- a/core/pb_client.py +++ b/core/pb_client.py @@ -147,6 +147,12 @@ async def update_record(record_id: str, data: dict[str, Any]) -> dict[str, Any]: ) +async def get_collection_fields() -> set[str]: + """Return the field names defined on the economy collection's schema.""" + data = await _request("GET", f"{PB_URL}/api/collections/{ECONOMY_COLLECTION}") + return {f["name"] for f in data.get("fields", [])} + + async def count_records() -> int: """Return the total number of records in the collection (single cheap request).""" data = await _request( diff --git a/tests/conftest.py b/tests/conftest.py index 350e825..1a7636a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -73,6 +73,11 @@ class FakePocketBase: async def count_records(self) -> int: return len(self.records) + async def get_collection_fields(self) -> set[str]: + if self.schema_fields is None: + return set(economy._default_user()) | {"user_id"} + return set(self.schema_fields) + # -- test helpers ------------------------------------------------------- def record_for(self, user_id: int) -> dict: for record in self.records.values(): @@ -83,7 +88,7 @@ class FakePocketBase: def _install(monkeypatch, fake: FakePocketBase) -> FakePocketBase: for name in ("get_record", "create_record", "update_record", - "list_all_records", "count_records"): + "list_all_records", "count_records", "get_collection_fields"): monkeypatch.setattr(pb_client, name, getattr(fake, name)) monkeypatch.setattr(economy, "HOUSE_ID", None) monkeypatch.setattr(economy, "_house_pb_id", None) diff --git a/tests/test_quests.py b/tests/test_quests.py index d702c20..a261e92 100644 --- a/tests/test_quests.py +++ b/tests/test_quests.py @@ -100,6 +100,12 @@ class TestClaim: class TestSchemaDetection: + def test_missing_fields_reported(self, fake_pb_without_quest_fields): + assert run(economy.missing_schema_fields()) == ["quest_daily", "quest_weekly"] + + def test_healthy_schema_reports_nothing(self, fake_pb): + assert run(economy.missing_schema_fields()) == [] + def test_missing_quest_fields_warn_and_zero_progress( self, fake_pb_without_quest_fields, caplog ):