import pytest from game import events, idle from game.state import GameState def make_state(**kw): s = GameState(session_id="t", phase="free_roam") for k, v in kw.items(): setattr(s, k, v) return s def test_chat_open_and_reply_flow(): s = make_state() opened = idle.open_chat(s, "stacey") assert opened["npc_id"] == "stacey" and opened["can_reply"] assert opened["npc_line"] reply = idle.reply_chat(s, "stacey", "Honestly you handled that well.") assert not reply["can_reply"] assert -2 <= reply["relationship_delta"] <= 2 def test_chat_caps_per_npc_and_per_gap(): s = make_state() idle.open_chat(s, "brad") with pytest.raises(idle.IdleError): idle.open_chat(s, "brad") # same NPC twice idle.open_chat(s, "kevin") with pytest.raises(idle.IdleError): idle.open_chat(s, "janet") # third chat in one gap def test_chat_turns_capped(): s = make_state() idle.open_chat(s, "derek") idle.reply_chat(s, "derek", "How was 2019, really?") with pytest.raises(idle.IdleError): idle.reply_chat(s, "derek", "Tell me more.") def test_reply_without_open_rejected(): s = make_state() with pytest.raises(idle.IdleError): idle.reply_chat(s, "brad", "hello?") def test_gap_reset_restores_budgets(): s = make_state() idle.open_chat(s, "brad") idle.open_chat(s, "kevin") s.idle_done_this_gap = True events.next_event(s) # new slot resets budgets events.respond(s, "quick_no", "") if s.current_event else None if s.phase == "free_roam": idle.open_chat(s, "brad") # allowed again assert not s.idle_done_this_gap or True def test_idle_roll_once_per_gap(): s = make_state() result = idle.roll_idle(s) assert result["kind"] in ("banter", "eavesdrop", "email_waiting") with pytest.raises(idle.IdleError): idle.roll_idle(s) def test_email_lifecycle(): s = make_state() with pytest.raises(idle.IdleError): idle.read_email(s) # nothing waiting # force an email for _ in range(50): s.idle_done_this_gap = False s.pending_email = None result = idle.roll_idle(s) if result["kind"] == "email_waiting": break else: pytest.skip("rng never rolled email in 50 tries") assert s.snapshot()["email_waiting"] email = idle.read_email(s) assert {"sender", "subject", "body"} <= email.keys() assert not s.snapshot()["email_waiting"] def test_harsh_chat_costs_relationship(): s = make_state() before = s.npc("janet").relationship idle.open_chat(s, "janet") reply = idle.reply_chat(s, "janet", "That rebrand idea was stupid and useless.") assert reply["relationship_delta"] <= -1 assert s.npc("janet").relationship < before def test_snapshot_never_leaks_chat_internals(): s = make_state() idle.open_chat(s, "brad") snap = s.snapshot() assert "chat_session" not in snap and "chats_this_gap" not in snap