"""Tests for smart alert profiles.""" from vigilar.alerts.profiles import match_profile, is_in_time_window, get_action_for_event from vigilar.config import AlertProfileConfig, AlertProfileRule from vigilar.constants import HouseholdState def _make_profile(name, states, time_window="", rules=None): return AlertProfileConfig( name=name, presence_states=states, time_window=time_window, rules=rules or [], ) class TestTimeWindow: def test_no_window_always_matches(self): assert is_in_time_window("", "14:30") is True def test_inside_window(self): assert is_in_time_window("23:00-06:00", "02:30") is True def test_outside_window(self): assert is_in_time_window("23:00-06:00", "14:30") is False def test_at_start(self): assert is_in_time_window("23:00-06:00", "23:00") is True def test_daytime_window(self): assert is_in_time_window("06:00-23:00", "14:30") is True class TestProfileMatching: def test_match_by_presence(self): profiles = [ _make_profile("Away", ["EMPTY"]), _make_profile("Home", ["ADULTS_HOME", "ALL_HOME"]), ] result = match_profile(profiles, HouseholdState.EMPTY, "14:00") assert result is not None assert result.name == "Away" def test_no_match(self): profiles = [_make_profile("Away", ["EMPTY"])] result = match_profile(profiles, HouseholdState.ALL_HOME, "14:00") assert result is None def test_time_window_narrows(self): profiles = [ _make_profile("Night", ["ALL_HOME"], "23:00-06:00"), _make_profile("Day", ["ALL_HOME"], "06:00-23:00"), ] result = match_profile(profiles, HouseholdState.ALL_HOME, "02:00") assert result is not None assert result.name == "Night" class TestActionForEvent: def test_person_gets_push(self): rules = [AlertProfileRule(detection_type="person", action="push_and_record", recipients="all")] profile = _make_profile("Away", ["EMPTY"], rules=rules) action, recipients = get_action_for_event(profile, "person", "front_door") assert action == "push_and_record" assert recipients == "all" def test_motion_gets_record_only(self): rules = [AlertProfileRule(detection_type="motion", action="record_only", recipients="none")] profile = _make_profile("Home", ["ALL_HOME"], rules=rules) action, recipients = get_action_for_event(profile, "motion", "front_door") assert action == "record_only" def test_no_matching_rule_defaults_quiet(self): profile = _make_profile("Home", ["ALL_HOME"], rules=[]) action, recipients = get_action_for_event(profile, "person", "front_door") assert action == "quiet_log" assert recipients == "none" class TestPetAlertRouting: def test_known_pet_exterior_gets_push(self): rules = [ AlertProfileRule(detection_type="known_pet", camera_location="EXTERIOR", action="push_and_record", recipients="all"), AlertProfileRule(detection_type="known_pet", camera_location="INTERIOR", action="quiet_log", recipients="none"), ] profile = _make_profile("Away", ["EMPTY"], rules=rules) action, recipients = get_action_for_event(profile, "known_pet", "front_entrance", camera_location="EXTERIOR") assert action == "push_and_record" def test_known_pet_interior_gets_quiet_log(self): rules = [ AlertProfileRule(detection_type="known_pet", camera_location="EXTERIOR", action="push_and_record", recipients="all"), AlertProfileRule(detection_type="known_pet", camera_location="INTERIOR", action="quiet_log", recipients="none"), ] profile = _make_profile("Home", ["ALL_HOME"], rules=rules) action, recipients = get_action_for_event(profile, "known_pet", "kitchen", camera_location="INTERIOR") assert action == "quiet_log" def test_wildlife_predator_gets_urgent(self): rules = [ AlertProfileRule(detection_type="wildlife_predator", action="push_and_record", recipients="all"), ] profile = _make_profile("Always", ["EMPTY", "ALL_HOME"], rules=rules) action, _ = get_action_for_event(profile, "wildlife_predator", "front", camera_location="EXTERIOR") assert action == "push_and_record" def test_camera_location_any_matches_all(self): rules = [ AlertProfileRule(detection_type="wildlife_predator", camera_location="any", action="push_and_record", recipients="all"), ] profile = _make_profile("Always", ["EMPTY"], rules=rules) action, _ = get_action_for_event(profile, "wildlife_predator", "kitchen", camera_location="INTERIOR") assert action == "push_and_record" def test_backward_compatible_without_camera_location(self): """Existing calls without camera_location still work.""" rules = [AlertProfileRule(detection_type="person", action="push_and_record", recipients="all")] profile = _make_profile("Away", ["EMPTY"], rules=rules) action, _ = get_action_for_event(profile, "person", "front_door") assert action == "push_and_record"