131 lines
5.5 KiB
Python
131 lines
5.5 KiB
Python
"""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"
|