import json from pathlib import Path import pytest from velvet_rope.game import GameService from velvet_rope.model_backends import DeterministicMarloweBackend from velvet_rope.state import GameStatus, Mood REPLAYS_FIXTURE_PATH = Path(__file__).parent / "fixtures" / "transcript_replays.json" def load_replays(): return json.loads(REPLAYS_FIXTURE_PATH.read_text())["replays"] REPLAYS = load_replays() def assert_score_bounds(scores, expected_bounds): if "rapport_min" in expected_bounds: assert scores.rapport >= expected_bounds["rapport_min"] if "suspicion_max" in expected_bounds: assert scores.suspicion <= expected_bounds["suspicion_max"] if "patience_min" in expected_bounds: assert scores.patience >= expected_bounds["patience_min"] if "softspot_progress" in expected_bounds: assert scores.softspot_progress == expected_bounds["softspot_progress"] @pytest.mark.parametrize("replay", REPLAYS, ids=lambda replay: replay["id"]) def test_deterministic_transcript_replay(replay): service = GameService(backend=DeterministicMarloweBackend()) state = service.new_game() mood_path = [] for line in replay["player_lines"]: state = service.play_turn(state, line) mood_path.append(state.mood.value) assert state.status is GameStatus(replay["expect"]["final_status"]) assert state.mood is Mood(replay["expect"]["final_mood"]) assert state.used_tactics == set(replay["expect"]["used_tactics"]) assert_score_bounds(state.scores, replay["expect"].get("score_bounds", {})) if "mood_path" in replay["expect"]: assert mood_path == replay["expect"]["mood_path"] if "assistant_reply_contains" in replay["expect"]: assert replay["expect"]["assistant_reply_contains"].lower() in state.history[-1].content.lower()