forked from sass/tipibot
Added tests, fix README.
This commit is contained in:
116
tests/test_quests.py
Normal file
116
tests/test_quests.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""Tests for the quest system: rotation, progress, claiming, schema detection."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from core import economy
|
||||
|
||||
from conftest import run
|
||||
|
||||
UID = 111
|
||||
|
||||
|
||||
def _fixed_now(monkeypatch, dt: datetime):
|
||||
monkeypatch.setattr(economy, "_now", lambda: dt)
|
||||
return dt
|
||||
|
||||
|
||||
def _complete_all_active_quests(fake_pb, user_id: int) -> tuple[int, int]:
|
||||
"""Push every active quest's tracked counter past its goal directly in the
|
||||
store. Returns (expected_coins, expected_exp)."""
|
||||
rec = fake_pb.record_for(user_id)
|
||||
coins = exp = 0
|
||||
for pool, block in (
|
||||
(economy.QUESTS_DAILY, rec["quest_daily"]),
|
||||
(economy.QUESTS_WEEKLY, rec["quest_weekly"]),
|
||||
):
|
||||
for qid, state in block["quests"].items():
|
||||
stat = pool[qid]["stat"]
|
||||
rec[stat] = state["snap"] + pool[qid]["goal"]
|
||||
coins += pool[qid]["coins"]
|
||||
exp += pool[qid]["exp"]
|
||||
return coins, exp
|
||||
|
||||
|
||||
class TestRotation:
|
||||
def test_deterministic_per_seed(self):
|
||||
a = economy._pick_quests(economy.QUESTS_DAILY, 3, "1:date:2026-07-26")
|
||||
b = economy._pick_quests(economy.QUESTS_DAILY, 3, "1:date:2026-07-26")
|
||||
assert a == b
|
||||
|
||||
def test_users_get_different_sets(self):
|
||||
sets = {
|
||||
tuple(economy._pick_quests(economy.QUESTS_DAILY, 3, f"{uid}:date:2026-07-26"))
|
||||
for uid in range(50)
|
||||
}
|
||||
assert len(sets) > 1
|
||||
|
||||
def test_counts(self, fake_pb):
|
||||
data = run(economy.get_quests(UID))
|
||||
assert len(data["daily"]) == economy.DAILY_QUEST_COUNT
|
||||
assert len(data["weekly"]) == economy.WEEKLY_QUEST_COUNT
|
||||
|
||||
def test_daily_rolls_over_weekly_stays(self, fake_pb, monkeypatch):
|
||||
# a Tuesday, so day+1 stays inside the same ISO week
|
||||
t0 = _fixed_now(monkeypatch, datetime(2026, 7, 21, 12, tzinfo=timezone.utc))
|
||||
run(economy.get_quests(UID))
|
||||
rec = fake_pb.record_for(UID)
|
||||
daily_before, weekly_before = dict(rec["quest_daily"]), dict(rec["quest_weekly"])
|
||||
_fixed_now(monkeypatch, t0 + timedelta(days=1))
|
||||
run(economy.get_quests(UID))
|
||||
rec = fake_pb.record_for(UID)
|
||||
assert rec["quest_daily"]["date"] != daily_before["date"]
|
||||
assert rec["quest_weekly"] == weekly_before
|
||||
|
||||
|
||||
class TestProgress:
|
||||
def test_progress_tracks_counter_delta(self, fake_pb):
|
||||
run(economy.get_quests(UID))
|
||||
rec = fake_pb.record_for(UID)
|
||||
qid, state = next(iter(rec["quest_daily"]["quests"].items()))
|
||||
stat = economy.QUESTS_DAILY[qid]["stat"]
|
||||
rec[stat] = state["snap"] + 1
|
||||
data = run(economy.get_quests(UID))
|
||||
quest = next(q for q in data["daily"] if q["id"] == qid)
|
||||
assert quest["progress"] == 1
|
||||
|
||||
def test_pre_roll_stats_do_not_count(self, fake_pb):
|
||||
run(economy.get_user(UID))
|
||||
fake_pb.record_for(UID)["work_count"] = 500
|
||||
data = run(economy.get_quests(UID))
|
||||
assert all(q["progress"] == 0 for q in data["daily"] + data["weekly"])
|
||||
|
||||
|
||||
class TestClaim:
|
||||
def test_claim_pays_and_is_idempotent(self, fake_pb):
|
||||
run(economy.get_quests(UID))
|
||||
coins, exp = _complete_all_active_quests(fake_pb, UID)
|
||||
res = run(economy.claim_quests(UID))
|
||||
assert res["ok"]
|
||||
assert res["claimed"] == economy.DAILY_QUEST_COUNT + economy.WEEKLY_QUEST_COUNT
|
||||
assert res["coins"] == coins and res["exp"] == exp
|
||||
assert fake_pb.record_for(UID)["balance"] == coins
|
||||
res = run(economy.claim_quests(UID))
|
||||
assert not res["ok"] and res["reason"] == "nothing"
|
||||
|
||||
def test_claim_with_nothing_done(self, fake_pb):
|
||||
run(economy.get_quests(UID))
|
||||
res = run(economy.claim_quests(UID))
|
||||
assert not res["ok"]
|
||||
|
||||
|
||||
class TestSchemaDetection:
|
||||
def test_missing_quest_fields_warn_and_zero_progress(
|
||||
self, fake_pb_without_quest_fields, caplog
|
||||
):
|
||||
"""Reproduces the live 'quests never progress' symptom: when the
|
||||
collection schema lacks quest_daily/quest_weekly, PocketBase drops the
|
||||
rolled quest block, so every call re-rolls with a fresh snapshot."""
|
||||
fake = fake_pb_without_quest_fields
|
||||
with caplog.at_level(logging.WARNING):
|
||||
run(economy.get_quests(UID))
|
||||
assert any("quest fields" in r.message for r in caplog.records)
|
||||
# counters advance, but progress stays 0 because the snapshot re-rolls
|
||||
fake.record_for(UID)["work_count"] = 500
|
||||
data = run(economy.get_quests(UID))
|
||||
assert all(q["progress"] == 0 for q in data["daily"] + data["weekly"])
|
||||
Reference in New Issue
Block a user