#!/usr/bin/env python3 """End-to-end turn generation tests using the real configured LLM. Tests that generate_turn handles real LLM responses correctly with the actual character sheet and world state. Requires a running LLM. Usage: python3 tools/test_llm_turn.py """ import sys import os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from engine import GameEngine PASS = 0 FAIL = 0 engine = GameEngine() def check(label: str, result, *, expect_error=False, expect_book=True, expect_prompt=True, expect_log=None): global PASS, FAIL ok = True details = [] if expect_error and not result.error: ok = False details.append("expected error but got none") elif not expect_error and result.error: ok = False details.append(f"unexpected error: {result.error}") if expect_book and not result.book_log: ok = False details.append("expected non-empty book_log") if expect_prompt and not result.user_prompt: ok = False details.append("expected non-empty user_prompt") if expect_log is not None: if result.log_entry != expect_log: ok = False details.append(f"expected log_entry={expect_log!r}, got {result.log_entry!r}") status = "✓" if ok else "✗" if ok: PASS += 1 else: FAIL += 1 bl = result.book_log[:80].replace("\n", " ") if result.book_log else "(none)" print(f" {status} {label}") if not ok: for d in details: print(f" {d}") print(f" book_log: {bl}...") if result.user_prompt: print(f" prompt: {result.user_prompt[:60]}...") def section(name: str): print(f"\n{'=' * 60}") print(f" {name}") print(f"{'=' * 60}") def main(): section("First turn — no player action (story opening)") r = engine.generate_turn() check("Story opening", r, expect_error=False, expect_book=True, expect_prompt=False) section("Valid action — buy a drink") r = engine.generate_turn( player_action="I buy a mug of ale at the Splintered Tankard", ) check("Buy ale", r, expect_error=False, expect_book=True, expect_prompt=False) section("Valid action — talk to an NPC") r = engine.generate_turn( player_action="I ask Mistress Otta about recent rumours", ) check("Ask Otta", r, expect_error=False, expect_book=True, expect_prompt=False) section("Valid action — use inventory item") r = engine.generate_turn( player_action="I apply my healing salve to restore HP", ) check("Use healing salve", r, expect_error=False, expect_book=True, expect_prompt=False) section("Valid action — explore") r = engine.generate_turn( player_action="I head to the Market Square to look around", ) check("Visit market", r, expect_error=False, expect_book=True, expect_prompt=False) section("Invalid action — use non-existent item") r = engine.generate_turn( player_action="I drink a potion of invisibility", ) check("Potion of invisibility", r, expect_error=False, expect_book=False) if r.log_entry: print(f" log: {r.log_entry}") section("Invalid action — cast spell (not a weaver)") r = engine.generate_turn( player_action="I cast a fireball at the tavern ceiling", ) check("Fireball spell", r, expect_error=False, expect_book=False) if r.log_entry: print(f" log: {r.log_entry}") section("Invalid action — nonsensical") r = engine.generate_turn( player_action="I fly to the moon", ) check("Fly to moon", r, expect_error=False, expect_book=False) if r.log_entry: print(f" log: {r.log_entry}") section("Resume scene (no player action)") r = engine.generate_turn() check("Resume scene", r, expect_error=False, expect_book=True, expect_prompt=False) print(f"\n{'=' * 60}") print(f" Results: {PASS} passed, {FAIL} failed") print(f"{'=' * 60}") return 0 if FAIL == 0 else 1 if __name__ == "__main__": sys.exit(main())