fix(economy): handle db_error in commands, dedupe cooldowns, single leaderboard scan
Three robustness/perf fixes from the review:
1. db_error UX: economy core returns {"ok": False, "reason": "db_error"} on a
PocketBase outage, but no handler expected it - deferred commands hung on
"thinking..." and others showed a misleading "you're broke". Added a shared
reply_db_error helper (commands/_replies.py) + S.ERR["db_error"], and wired a
db_error branch into every handler that can receive it (daily/work/beg/crime/
rob/give/buy/roulette/slots/blackjack/heist/fish/prestige/vanity/consumables/
request-funding). Also fixed a latent KeyError in vs-bot RPS that read
res["balance"] without checking res["ok"].
2. Deduped the item->cooldown mapping that was copied in do_daily/do_work/do_beg,
do_fish_start, _maybe_remind and _restore_reminders. Single source of truth:
store.ITEM_COOLDOWNS + effective_cooldown(cmd, items).
3. /leaderboard did six full-collection scans (one per tab). Added
get_all_leaderboards() which scans once and builds all six views in memory.
Tests: effective_cooldown cases, and get_all_leaderboards matches the individual
queries + scans the collection exactly once.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VbAVsrZuYesea99mPMmPT
This commit is contained in:
49
tests/test_leaderboards.py
Normal file
49
tests/test_leaderboards.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""Tests for leaderboard queries, incl. the single-scan combined builder."""
|
||||
|
||||
from core import economy, pb_client
|
||||
|
||||
from conftest import run
|
||||
|
||||
|
||||
def _seed(fake_pb, n: int) -> None:
|
||||
for i in range(1, n + 1):
|
||||
run(economy.get_user(1000 + i))
|
||||
rec = fake_pb.record_for(1000 + i)
|
||||
rec["balance"] = i * 100
|
||||
rec["exp"] = i * 50
|
||||
rec["season_total_exp"] = i * 10
|
||||
rec["prestige_level"] = i % 3
|
||||
rec["prestige_points"] = i
|
||||
rec["total_wagered"] = i * 7
|
||||
rec["total_fish_caught"] = n - i # inverse order, to catch sort mistakes
|
||||
|
||||
|
||||
class TestGetAllLeaderboards:
|
||||
def test_matches_individual_queries(self, fake_pb):
|
||||
_seed(fake_pb, 5)
|
||||
combined = run(economy.get_all_leaderboards())
|
||||
assert combined["coins"] == run(economy.get_leaderboard(top_n=None))
|
||||
assert combined["exp"] == run(economy.get_leaderboard_exp(top_n=None))
|
||||
assert combined["season"] == run(economy.get_leaderboard_season_exp(top_n=None))
|
||||
assert combined["prestige"] == run(economy.get_leaderboard_prestige(top_n=None))
|
||||
assert combined["wagered"] == run(economy.get_leaderboard_wagered(top_n=None))
|
||||
assert combined["fish"] == run(economy.get_leaderboard_fish(top_n=None))
|
||||
|
||||
def test_scans_collection_once(self, fake_pb, monkeypatch):
|
||||
_seed(fake_pb, 3)
|
||||
calls = {"n": 0}
|
||||
real = pb_client.list_all_records
|
||||
|
||||
async def counting():
|
||||
calls["n"] += 1
|
||||
return await real()
|
||||
|
||||
monkeypatch.setattr(pb_client, "list_all_records", counting)
|
||||
run(economy.get_all_leaderboards())
|
||||
assert calls["n"] == 1 # not six
|
||||
|
||||
def test_fish_sorted_descending(self, fake_pb):
|
||||
_seed(fake_pb, 4)
|
||||
fish = run(economy.get_all_leaderboards())["fish"]
|
||||
counts = [c for _, c in fish]
|
||||
assert counts == sorted(counts, reverse=True)
|
||||
Reference in New Issue
Block a user