""" Tests for the trigger system. """ import unittest import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from src.engine.triggers import ( TriggerRegistry, TriggerType, TriggerCondition, Trigger, MANDATORY_TRIGGERS, STANDARD_TRIGGERS ) class TestTriggerType(unittest.TestCase): """Tests for TriggerType enum.""" def test_mandatory_triggers_defined(self): self.assertIn(TriggerType.DISCUSSION_START, MANDATORY_TRIGGERS) self.assertIn(TriggerType.VOTE_START, MANDATORY_TRIGGERS) self.assertIn(TriggerType.GAME_START, MANDATORY_TRIGGERS) def test_standard_triggers_defined(self): self.assertIn(TriggerType.BODY_IN_FOV, STANDARD_TRIGGERS) self.assertIn(TriggerType.PLAYER_ENTERS_FOV, STANDARD_TRIGGERS) class TestTriggerRegistry(unittest.TestCase): """Tests for TriggerRegistry class.""" def setUp(self): self.registry = TriggerRegistry() self.registry.register_agent("agent1") self.registry.register_agent("agent2") def test_register_agent(self): # Agents should have standard triggers subscribed self.assertIn(TriggerType.BODY_IN_FOV, self.registry._subscriptions["agent1"]) self.assertIn(TriggerType.PLAYER_ENTERS_FOV, self.registry._subscriptions["agent1"]) def test_subscribe(self): self.registry.subscribe("agent1", TriggerType.INTERSECTION) self.assertIn(TriggerType.INTERSECTION, self.registry._subscriptions["agent1"]) def test_unsubscribe(self): self.registry.unsubscribe("agent1", TriggerType.BODY_IN_FOV) self.assertNotIn(TriggerType.BODY_IN_FOV, self.registry._subscriptions["agent1"]) def test_cannot_unsubscribe_mandatory(self): self.registry.unsubscribe("agent1", TriggerType.DISCUSSION_START) # Should still fire because mandatory self.assertTrue( self.registry.should_fire("agent1", TriggerType.DISCUSSION_START, 0.0) ) def test_should_fire_standard(self): # Standard triggers should fire by default self.assertTrue( self.registry.should_fire("agent1", TriggerType.BODY_IN_FOV, 0.0) ) def test_should_fire_optional_not_subscribed(self): # Optional triggers don't fire unless subscribed self.assertFalse( self.registry.should_fire("agent1", TriggerType.INTERSECTION, 0.0) ) def test_should_fire_mandatory_always(self): # Mandatory triggers always fire self.assertTrue( self.registry.should_fire("agent1", TriggerType.GAME_START, 0.0) ) def test_mute_trigger(self): condition = TriggerCondition( trigger_type=TriggerType.PLAYER_ENTERS_FOV, until_time=10.0 ) self.registry.mute("agent1", condition) # Should be muted before time expires self.assertTrue( self.registry.is_muted("agent1", TriggerType.PLAYER_ENTERS_FOV, 5.0) ) # Should not fire while muted self.assertFalse( self.registry.should_fire("agent1", TriggerType.PLAYER_ENTERS_FOV, 5.0) ) def test_mute_expires(self): condition = TriggerCondition( trigger_type=TriggerType.PLAYER_ENTERS_FOV, until_time=10.0 ) self.registry.mute("agent1", condition) # Should not be muted after time expires self.assertFalse( self.registry.is_muted("agent1", TriggerType.PLAYER_ENTERS_FOV, 15.0) ) def test_cannot_mute_mandatory(self): condition = TriggerCondition( trigger_type=TriggerType.DISCUSSION_START, until_time=100.0 ) self.registry.mute("agent1", condition) # Mandatory should still fire self.assertTrue( self.registry.should_fire("agent1", TriggerType.DISCUSSION_START, 50.0) ) def test_target_specific_mute(self): condition = TriggerCondition( trigger_type=TriggerType.PLAYER_ENTERS_FOV, until_time=10.0, target_id="player_blue" ) self.registry.mute("agent1", condition) # Should be muted for specific target self.assertTrue( self.registry.is_muted("agent1", TriggerType.PLAYER_ENTERS_FOV, 5.0, "player_blue") ) # Should NOT be muted for different target self.assertFalse( self.registry.is_muted("agent1", TriggerType.PLAYER_ENTERS_FOV, 5.0, "player_red") ) def test_clear_expired_mutes(self): cond1 = TriggerCondition(trigger_type=TriggerType.PLAYER_ENTERS_FOV, until_time=5.0) cond2 = TriggerCondition(trigger_type=TriggerType.PLAYER_EXITS_FOV, until_time=15.0) self.registry.mute("agent1", cond1) self.registry.mute("agent1", cond2) self.assertEqual(len(self.registry._mutes["agent1"]), 2) self.registry.clear_expired_mutes("agent1", 10.0) self.assertEqual(len(self.registry._mutes["agent1"]), 1) def test_get_agents_for_trigger(self): # Add a third agent self.registry.register_agent("agent3") # Mute agent2 self.registry.mute("agent2", TriggerCondition( trigger_type=TriggerType.BODY_IN_FOV, until_time=10.0 )) agents = self.registry.get_agents_for_trigger(TriggerType.BODY_IN_FOV, 5.0) self.assertIn("agent1", agents) self.assertNotIn("agent2", agents) # muted self.assertIn("agent3", agents) def test_get_agents_with_exclude(self): agents = self.registry.get_agents_for_trigger( TriggerType.BODY_IN_FOV, 0.0, exclude={"agent1"} ) self.assertNotIn("agent1", agents) self.assertIn("agent2", agents) class TestTrigger(unittest.TestCase): """Tests for Trigger dataclass.""" def test_trigger_creation(self): trigger = Trigger( trigger_type=TriggerType.BODY_IN_FOV, target_agent_id="agent1", time=10.5, data={"victim": "Blue"} ) self.assertEqual(trigger.trigger_type, TriggerType.BODY_IN_FOV) self.assertEqual(trigger.target_agent_id, "agent1") self.assertEqual(trigger.time, 10.5) if __name__ == "__main__": unittest.main()