"""Unit tests for the quest + item system (rune_goblin.quests).""" import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src")) from rune_goblin import quests # noqa: E402 def _types(res): return [a["type"] for a in res["world_actions"]] def _player(**kw): base = {"level": 1, "xp": 0, "items": {}, "quests": {}, "weapon": "clerk_wand"} base.update(kw) return base def test_monster_drops_always_include_a_trophy(): assert "monster_trophy" in quests.monster_drops("toll_wisp", "Toll Wisp") def test_fungus_drops_a_spore_by_id_and_name(): by_id = quests.monster_drops("fungus_a", "Mirror Fungus") assert "fungus_spore" in by_id and "monster_trophy" in by_id def test_quest_giver_lookup(): assert quests.is_quest_giver("watch_archer") assert not quests.is_quest_giver("tourist") def test_first_talk_offers_and_accepts_quest(): res = quests.resolve_quest_talk(_player(), "watch_archer") assert res["quest_state"] == "offered" assert "set_quest" in _types(res) setq = next(a for a in res["world_actions"] if a["type"] == "set_quest") assert setq["quest"] == "road_patrol" and setq["state"] == "active" def test_active_quest_without_items_shows_progress(): p = _player(quests={"road_patrol": "active"}, items={"monster_trophy": 1}) res = quests.resolve_quest_talk(p, "watch_archer") assert res["quest_state"] == "progress" assert res["world_actions"] == [] assert "1/2" in res["line"] def test_turn_in_consumes_items_and_grants_reward(): p = _player(quests={"road_patrol": "active"}, items={"monster_trophy": 2}) res = quests.resolve_quest_talk(p, "watch_archer") assert res["quest_state"] == "turned_in" rm = next(a for a in res["world_actions"] if a["type"] == "remove_item") assert rm["item"] == "monster_trophy" and rm["qty"] == 2 # two health potions + gold + xp + quest marked done adds = [a for a in res["world_actions"] if a["type"] == "add_item"] assert sum(a["qty"] for a in adds if a["item"] == "health_potion") == 2 assert any(a["type"] == "add_gold" for a in res["world_actions"]) assert any(a["type"] == "add_xp" for a in res["world_actions"]) setq = next(a for a in res["world_actions"] if a["type"] == "set_quest") assert setq["state"] == "done" def test_weapon_reward_quest_grants_equipment(): p = _player(quests={"spore_sample": "active"}, items={"fungus_spore": 1}) res = quests.resolve_quest_talk(p, "road_druid") assert res["quest_state"] == "turned_in" assert any(a["type"] == "add_weapon" and a["weapon"] == "river_thread" for a in res["world_actions"]) def test_completed_quartermaster_offers_repeatable_exchange(): # done quest + enough trophies -> trade trophies for a potion p = _player(quests={"quartermaster_kit": "done"}, items={"monster_trophy": 2}) res = quests.resolve_quest_talk(p, "quartermaster") assert res["quest_state"] == "exchange" assert any(a["type"] == "remove_item" for a in res["world_actions"]) assert any(a["type"] == "add_item" and a["item"] == "health_potion" for a in res["world_actions"]) def test_completed_quest_without_trophies_just_thanks(): p = _player(quests={"quartermaster_kit": "done"}, items={}) res = quests.resolve_quest_talk(p, "quartermaster") assert res["quest_state"] == "done" assert res["world_actions"] == [] def test_non_giver_returns_none_state(): res = quests.resolve_quest_talk(_player(), "tourist") assert res["quest_state"] == "none" assert res["world_actions"] == []