""" Tests for the discrete event simulator. """ import unittest import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from src.engine.simulator import Simulator from src.engine.types import Event, Player, Position, Role, GamePhase class TestEvent(unittest.TestCase): """Tests for Event dataclass.""" def test_event_creation(self): event = Event(time=10.0, event_type="TEST", data={"key": "value"}) self.assertEqual(event.time, 10.0) self.assertEqual(event.event_type, "TEST") self.assertEqual(event.data["key"], "value") def test_event_ordering(self): e1 = Event(time=5.0, event_type="A") e2 = Event(time=10.0, event_type="B") e3 = Event(time=5.0, event_type="C") self.assertTrue(e1 < e2) self.assertFalse(e2 < e1) # Same time - order undefined but should not error self.assertFalse(e1 < e3 and e3 < e1) class TestPlayer(unittest.TestCase): """Tests for Player dataclass.""" def test_player_creation(self): player = Player( id="p1", name="Red", color="red", role=Role.CREWMATE, position=Position(room_id="cafeteria") ) self.assertEqual(player.id, "p1") self.assertEqual(player.role, Role.CREWMATE) self.assertTrue(player.is_alive) def test_position_in_room(self): pos = Position(room_id="cafeteria") self.assertTrue(pos.is_in_room()) self.assertFalse(pos.is_on_edge()) def test_position_on_edge(self): pos = Position(edge_id="ab", progress=0.5) self.assertFalse(pos.is_in_room()) self.assertTrue(pos.is_on_edge()) class TestSimulator(unittest.TestCase): """Tests for the Simulator class.""" def setUp(self): self.sim = Simulator() def test_initial_state(self): self.assertEqual(self.sim.time, 0.0) self.assertEqual(self.sim.phase, GamePhase.LOBBY) self.assertEqual(len(self.sim.players), 0) def test_schedule_event(self): event = Event(time=5.0, event_type="TEST") self.sim.schedule(event) self.assertEqual(self.sim.peek_next_time(), 5.0) def test_schedule_at(self): event = self.sim.schedule_at(10.0, "TEST", {"data": 1}) self.assertEqual(event.time, 10.0) self.assertEqual(self.sim.peek_next_time(), 10.0) def test_schedule_in(self): self.sim.time = 5.0 event = self.sim.schedule_in(3.0, "TEST") self.assertEqual(event.time, 8.0) def test_step_processes_event(self): self.sim.schedule_at(5.0, "TEST") event = self.sim.step() self.assertIsNotNone(event) self.assertEqual(event.event_type, "TEST") self.assertEqual(self.sim.time, 5.0) def test_step_empty_queue(self): event = self.sim.step() self.assertIsNone(event) def test_event_ordering(self): self.sim.schedule_at(10.0, "SECOND") self.sim.schedule_at(5.0, "FIRST") self.sim.schedule_at(15.0, "THIRD") e1 = self.sim.step() e2 = self.sim.step() e3 = self.sim.step() self.assertEqual(e1.event_type, "FIRST") self.assertEqual(e2.event_type, "SECOND") self.assertEqual(e3.event_type, "THIRD") def test_event_handler(self): received = [] def handler(event): received.append(event.data.get("value")) self.sim.on("TEST", handler) self.sim.schedule_at(5.0, "TEST", {"value": 42}) self.sim.step() self.assertEqual(received, [42]) def test_multiple_handlers(self): calls = [] self.sim.on("TEST", lambda e: calls.append("A")) self.sim.on("TEST", lambda e: calls.append("B")) self.sim.schedule_at(5.0, "TEST") self.sim.step() self.assertEqual(calls, ["A", "B"]) def test_wildcard_handler(self): events = [] self.sim.on("*", lambda e: events.append(e.event_type)) self.sim.schedule_at(1.0, "A") self.sim.schedule_at(2.0, "B") self.sim.step() self.sim.step() self.assertEqual(events, ["A", "B"]) def test_run_until(self): self.sim.schedule_at(5.0, "A") self.sim.schedule_at(10.0, "B") self.sim.schedule_at(15.0, "C") self.sim.run_until(10.0) self.assertEqual(self.sim.time, 10.0) self.assertEqual(self.sim.peek_next_time(), 15.0) def test_run_until_empty(self): self.sim.schedule_at(5.0, "A") self.sim.run_until_empty() self.assertEqual(self.sim.time, 5.0) self.assertIsNone(self.sim.peek_next_time()) def test_event_log(self): self.sim.schedule_at(5.0, "TEST", {"foo": "bar"}) self.sim.step() self.assertEqual(len(self.sim.event_log), 1) self.assertEqual(self.sim.event_log[0]["type"], "TEST") self.assertEqual(self.sim.event_log[0]["t"], 5.0) def test_add_player(self): player = Player(id="p1", name="Red", color="red") self.sim.add_player(player) self.assertEqual(len(self.sim.players), 1) self.assertIn("p1", self.sim.players) def test_get_player(self): player = Player(id="p1", name="Red", color="red") self.sim.add_player(player) found = self.sim.get_player("p1") self.assertEqual(found.name, "Red") self.assertIsNone(self.sim.get_player("nonexistent")) def test_get_living_players(self): p1 = Player(id="p1", name="Red", color="red") p2 = Player(id="p2", name="Blue", color="blue", is_alive=False) p3 = Player(id="p3", name="Green", color="green") self.sim.add_player(p1) self.sim.add_player(p2) self.sim.add_player(p3) living = self.sim.get_living_players() self.assertEqual(len(living), 2) def test_players_at(self): p1 = Player(id="p1", name="Red", color="red", position=Position(room_id="cafeteria")) p2 = Player(id="p2", name="Blue", color="blue", position=Position(room_id="cafeteria")) p3 = Player(id="p3", name="Green", color="green", position=Position(room_id="admin")) self.sim.add_player(p1) self.sim.add_player(p2) self.sim.add_player(p3) at_cafe = self.sim.players_at("cafeteria") self.assertEqual(len(at_cafe), 2) if __name__ == "__main__": unittest.main()