forked from sass/tipibot
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
110 lines
4.0 KiB
Python
110 lines
4.0 KiB
Python
"""Tests for the pure (no-database) economy math."""
|
|
|
|
import random
|
|
from datetime import timedelta
|
|
|
|
from core import economy
|
|
|
|
|
|
class TestLevels:
|
|
def test_milestones(self):
|
|
assert economy.get_level(0) == 1
|
|
assert economy.get_level(249) == 4
|
|
assert economy.get_level(250) == 5
|
|
assert economy.get_level(1000) == 10
|
|
assert economy.get_level(4000) == 20
|
|
assert economy.get_level(9000) == 30
|
|
|
|
def test_exp_for_level_is_inverse(self):
|
|
for level in range(1, 41):
|
|
exp = economy.exp_for_level(level)
|
|
assert economy.get_level(exp) == level
|
|
if level > 1:
|
|
assert economy.get_level(exp - 1) == level - 1
|
|
|
|
def test_negative_exp_clamps_to_level_1(self):
|
|
assert economy.get_level(-500) == 1
|
|
|
|
def test_role_names(self):
|
|
assert economy.level_role_name(1) == "TipiNOOB"
|
|
assert economy.level_role_name(4) == "TipiNOOB"
|
|
assert economy.level_role_name(5) == "TipiGRINDER"
|
|
assert economy.level_role_name(10) == "TipiHUSTLER"
|
|
assert economy.level_role_name(20) == "TipiCHAD"
|
|
assert economy.level_role_name(30) == "TipiLEGEND"
|
|
assert economy.level_role_name(99) == "TipiLEGEND"
|
|
|
|
|
|
class TestEffectiveCooldown:
|
|
def test_default_when_no_item(self):
|
|
assert economy.effective_cooldown("work", []) == economy.COOLDOWNS["work"]
|
|
assert economy.effective_cooldown("daily", []) == economy.COOLDOWNS["daily"]
|
|
|
|
def test_item_reduces_cooldown(self):
|
|
assert economy.effective_cooldown("work", ["monitor"]) == timedelta(minutes=40)
|
|
assert economy.effective_cooldown("beg", ["hiirematt"]) == timedelta(minutes=3)
|
|
assert economy.effective_cooldown("daily", ["korvaklapid"]) == timedelta(hours=18)
|
|
assert economy.effective_cooldown("fish", ["ussipurk"]) == timedelta(seconds=90)
|
|
|
|
def test_unrelated_item_does_not_reduce(self):
|
|
assert economy.effective_cooldown("work", ["hiirematt"]) == economy.COOLDOWNS["work"]
|
|
|
|
def test_command_without_cooldown_returns_none(self):
|
|
assert economy.effective_cooldown("unknown", []) is None
|
|
|
|
def test_every_item_cooldown_beats_its_base(self):
|
|
# An item-reduced cooldown must always be shorter than the base.
|
|
for cmd, (item, reduced) in economy.store.ITEM_COOLDOWNS.items():
|
|
assert reduced < economy.COOLDOWNS[cmd]
|
|
|
|
|
|
class TestGambleExp:
|
|
def test_tiers(self):
|
|
assert economy.gamble_exp(0) == 0
|
|
assert economy.gamble_exp(9) == 0
|
|
assert economy.gamble_exp(10) == 5
|
|
assert economy.gamble_exp(99) == 5
|
|
assert economy.gamble_exp(100) == 10
|
|
assert economy.gamble_exp(999) == 10
|
|
assert economy.gamble_exp(1_000) == 15
|
|
assert economy.gamble_exp(9_999) == 15
|
|
assert economy.gamble_exp(10_000) == 20
|
|
assert economy.gamble_exp(99_999) == 20
|
|
assert economy.gamble_exp(100_000) == 25
|
|
|
|
def test_cap(self):
|
|
assert economy.gamble_exp(10_000_000) == 25
|
|
|
|
|
|
class TestFormatTd:
|
|
def test_hours(self):
|
|
assert economy.format_td(timedelta(hours=1, minutes=23, seconds=45)) == "1t 23m"
|
|
|
|
def test_minutes(self):
|
|
assert economy.format_td(timedelta(minutes=45, seconds=12)) == "45m 12s"
|
|
|
|
def test_seconds(self):
|
|
assert economy.format_td(timedelta(seconds=8)) == "8s"
|
|
|
|
|
|
class TestRollFish:
|
|
def test_rolls_are_valid(self):
|
|
random.seed(42)
|
|
for _ in range(500):
|
|
fish_id, weight = economy.roll_fish()
|
|
if fish_id == "junk":
|
|
assert weight == 0
|
|
else:
|
|
fish = economy.FISH_CATALOGUE[fish_id]
|
|
assert fish["weight"][0] <= weight <= fish["weight"][1]
|
|
|
|
def test_rarity_bump_shifts_every_catch_up_a_tier(self):
|
|
random.seed(7)
|
|
rarities = {
|
|
economy.FISH_CATALOGUE[fid]["rarity"]
|
|
for fid, _ in (economy.roll_fish(rarity_bump=True) for _ in range(1000))
|
|
if fid != "junk"
|
|
}
|
|
assert "common" not in rarities
|
|
assert "legendary" in rarities
|