from __future__ import annotations import os import tempfile from blokus_ui.session import GameSession from blokus_ui.store import RunStore def make_session(**kwargs): """Create a GameSession with sensible defaults for testing.""" defaults = { "board_size": 7, "num_players": 2, "human_player": 0, "bot_type": "random", "bot_seed": 42, } defaults.update(kwargs) return GameSession(**defaults) class TestGameSession: def test_creation(self): session = make_session() assert session.board_size == 7 assert session.num_players == 2 assert session.human_player == 0 assert session.bot_type_name == "random" def test_initial_state(self): session = make_session() state = session.get_state() assert state["board_size"] == 7 assert state["human_player"] == 0 assert state["game_over"] is False assert len(state["players"]) == 2 assert len(state["available_pieces"]) > 0 def test_valid_placements(self): session = make_session() placements = session.get_valid_placements(0) assert len(placements) > 0 for p in placements: assert "action" in p assert "cells" in p assert "x" in p assert "y" in p def test_apply_human_action(self): session = make_session() placements = session.get_valid_placements(0) action = placements[0]["action"] result = session.apply_human_action(action) assert result["success"] is True def test_invalid_action(self): session = make_session() result = session.apply_human_action(999999) assert result["success"] is False def test_move_history(self): session = make_session() placements = session.get_valid_placements(0) action = placements[0]["action"] session.apply_human_action(action) assert len(session.move_history) > 0 move = session.move_history[0] assert move["player_idx"] == 0 assert "piece_name" in move assert "x" in move assert "y" in move def test_game_over_detection(self): session = make_session(board_size=7) steps = 0 while not session.game_over and steps < 200: state = session.get_state() moved = False for piece in state["available_pieces"]: placements = session.get_valid_placements(piece["id"]) if placements: session.apply_human_action(placements[0]["action"]) moved = True break if not moved: break steps += 1 assert session.game_over is True def test_serialization(self): session = make_session() placements = session.get_valid_placements(0) session.apply_human_action(placements[0]["action"]) data = session.to_dict() assert "config" in data assert "move_history" in data assert "game_over" in data assert data["config"]["board_size"] == 7 assert data["config"]["num_players"] == 2 def test_deserialization(self): session = make_session() placements = session.get_valid_placements(0) session.apply_human_action(placements[0]["action"]) data = session.to_dict() restored = GameSession.from_dict(data) assert restored.board_size == session.board_size assert restored.num_players == session.num_players assert len(restored.move_history) == len(session.move_history) assert restored.game_over == session.game_over def test_statistics(self): session = make_session() placements = session.get_valid_placements(0) session.apply_human_action(placements[0]["action"]) stats = session.get_statistics() assert "coverage" in stats assert "pieces_placed" in stats assert "squares_placed" in stats assert "total_moves" in stats assert len(stats["pieces_placed"]) == 2 assert len(stats["squares_placed"]) == 2 def test_four_player_game(self): session = make_session(num_players=4) state = session.get_state() assert len(state["players"]) == 4 def test_greedy_bot(self): session = make_session(bot_type="greedy") placements = session.get_valid_placements(0) assert len(placements) > 0 def test_minimax_bot(self): session = make_session(bot_type="minimax") placements = session.get_valid_placements(0) assert len(placements) > 0 class TestRunStore: def test_save_and_load(self): with tempfile.TemporaryDirectory() as tmpdir: store = RunStore(runs_dir=tmpdir) session = make_session() placements = session.get_valid_placements(0) session.apply_human_action(placements[0]["action"]) run_id = store.save(session, name="test_run") assert run_id is not None run_data = store.load(run_id) assert run_data is not None assert run_data["name"] == "test_run" assert run_data["data"]["config"]["board_size"] == 7 def test_list_runs(self): with tempfile.TemporaryDirectory() as tmpdir: store = RunStore(runs_dir=tmpdir) session = make_session() store.save(session, name="run1") session2 = make_session(bot_type="greedy", bot_seed=43) store.save(session2, name="run2") runs = store.list_runs() assert len(runs) == 2 assert runs[0]["name"] in ("run1", "run2") assert runs[0]["board_size"] == 7 def test_delete_run(self): with tempfile.TemporaryDirectory() as tmpdir: store = RunStore(runs_dir=tmpdir) session = make_session() run_id = store.save(session, name="test_run") assert store.delete(run_id) is True assert store.load(run_id) is None assert store.delete(run_id) is False def test_load_nonexistent(self): with tempfile.TemporaryDirectory() as tmpdir: store = RunStore(runs_dir=tmpdir) assert store.load("nonexistent") is None def test_list_empty(self): with tempfile.TemporaryDirectory() as tmpdir: store = RunStore(runs_dir=tmpdir) assert store.list_runs() == [] def test_runs_dir_creation(self): with tempfile.TemporaryDirectory() as tmpdir: runs_dir = os.path.join(tmpdir, "new_dir") RunStore(runs_dir=runs_dir) assert os.path.exists(runs_dir)