from __future__ import annotations from pathlib import Path from app.core.event_core import EventCore from app.core.handoff import HandoffMatrix from app.core.models import ( BehaviorPatternV2, ControlProfile, EntityState, HandoffMode, LearningProfile, LearningState, RuntimeState, SafetyStage, StateEvent, ) from app.core.stores import FutureStores def test_event_core_routes_state_event_to_decision(tmp_path: Path) -> None: stores = FutureStores(tmp_path) stores.save_runtime( RuntimeState( entities={ "light.storage": EntityState( entity_id="light.storage", domain="light", state="off", ) } ) ) stores.save_learning( LearningState( profiles={ "light.storage": LearningProfile( actuator_entity_id="light.storage", patterns=[ BehaviorPatternV2( actuator_entity_id="light.storage", target_state="on", trigger_entity_id="binary_sensor.storage_door", trigger_state="on", support=3, confidence=0.95, ) ], ) } ) ) stores.save_control( stores.control().model_copy( update={ "profiles": { "light.storage": ControlProfile( actuator_entity_id="light.storage", stage=SafetyStage.DRY_RUN, min_confidence=0.8, ) } } ) ) core = EventCore(stores) audit = core.process_state_event( StateEvent(entity_id="binary_sensor.storage_door", new_state="on") ) decisions = [item for item in audit if item.kind == "decision"] assert decisions assert decisions[0].decision is not None assert decisions[0].decision.target_state == "on" assert decisions[0].decision.allowed is True assert decisions[0].decision.dry_run is True def test_handoff_matrix_detects_conflict_and_rollback() -> None: matrix = HandoffMatrix() profile = ControlProfile( actuator_entity_id="light.storage", handoff_mode=HandoffMode.CONTROLLED, related_automation_ids=["automation.storage"], ) assert matrix.classify(profile) is HandoffMode.CONFLICT controlled = matrix.assume_control(profile) assert controlled.paused_automation_ids == ["automation.storage"] rolled_back = matrix.rollback(controlled) assert rolled_back.handoff_mode is HandoffMode.ROLLBACK assert rolled_back.paused_automation_ids == []