from pathlib import Path from fastapi.testclient import TestClient from app.automations.store import AutomationStore from app.main import app def _payload() -> dict[str, object]: return { "alias": "Licht bei Dunkelheit", "description": "Schaltet das Flurlicht unter dem Helligkeitsgrenzwert ein.", "trigger": {"entity_id": "sensor.hall_illuminance", "below": 10}, "action": { "service": "light.turn_on", "entity_id": "light.hall", "data": {"brightness_pct": 40}, }, } def test_proposal_requires_explicit_approval_before_yaml(tmp_path: Path) -> None: with TestClient(app) as client: app.state.automation_store = AutomationStore(tmp_path) created = client.post("/v1/automations/proposals", json=_payload()) proposal_id = created.json()["proposal_id"] blocked = client.get(f"/v1/automations/proposals/{proposal_id}/yaml") approved = client.post( f"/v1/automations/proposals/{proposal_id}/approve", json={"expected_revision": 1}, ) exported = client.get(f"/v1/automations/proposals/{proposal_id}/yaml") assert created.status_code == 201 assert created.json()["status"] == "draft" assert blocked.status_code == 409 assert approved.json()["status"] == "approved" assert "service: light.turn_on" in exported.text def test_proposal_rejects_unsafe_service_domain(tmp_path: Path) -> None: payload = _payload() payload["action"] = { "service": "shell_command.run", "entity_id": "light.hall", "data": {}, } with TestClient(app) as client: app.state.automation_store = AutomationStore(tmp_path) response = client.post("/v1/automations/proposals", json=payload) assert response.status_code == 422 def test_proposal_requires_numeric_threshold(tmp_path: Path) -> None: payload = _payload() payload["trigger"] = {"entity_id": "sensor.hall_illuminance"} with TestClient(app) as client: app.state.automation_store = AutomationStore(tmp_path) response = client.post("/v1/automations/proposals", json=payload) assert response.status_code == 422