Build and Push / build (release) Successful in 1m28s
- Dashboard domain with Quick Links CRUD + reorder endpoints - Dashboard widgets management endpoints - Database migrations for quick_links and dashboard_widgets tables - Static file controller for Organizr widgets - Default local user when OIDC is disabled - Domain-based architecture refactor (src/domains/, src/shared/) - Test suite updated for new structure (285 tests passing) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
656 lines
25 KiB
Python
656 lines
25 KiB
Python
"""Tests for housekeeping (home automation) endpoints."""
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
from unittest.mock import patch, AsyncMock
|
|
|
|
from src.main import app
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
"""Create a test client."""
|
|
return TestClient(app)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_ha_client():
|
|
"""Create a mock Home Assistant client."""
|
|
mock = AsyncMock()
|
|
return mock
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_states():
|
|
"""Sample Home Assistant states for testing."""
|
|
return [
|
|
{
|
|
"entity_id": "light.living_room",
|
|
"state": "on",
|
|
"attributes": {
|
|
"friendly_name": "Living Room Light",
|
|
"brightness": 255,
|
|
"area_id": "living_room"
|
|
},
|
|
"last_changed": "2025-01-01T12:00:00Z"
|
|
},
|
|
{
|
|
"entity_id": "light.bedroom",
|
|
"state": "off",
|
|
"attributes": {
|
|
"friendly_name": "Bedroom Light",
|
|
"area_id": "bedroom"
|
|
},
|
|
"last_changed": "2025-01-01T11:00:00Z"
|
|
},
|
|
{
|
|
"entity_id": "switch.garage",
|
|
"state": "off",
|
|
"attributes": {
|
|
"friendly_name": "Garage Switch"
|
|
},
|
|
"last_changed": "2025-01-01T10:00:00Z"
|
|
},
|
|
{
|
|
"entity_id": "scene.movie_night",
|
|
"state": "scening",
|
|
"attributes": {
|
|
"friendly_name": "Movie Night"
|
|
},
|
|
"last_changed": "2025-01-01T09:00:00Z"
|
|
},
|
|
{
|
|
"entity_id": "script.bedtime",
|
|
"state": "off",
|
|
"attributes": {
|
|
"friendly_name": "Bedtime Routine"
|
|
},
|
|
"last_changed": "2025-01-01T08:00:00Z"
|
|
},
|
|
{
|
|
"entity_id": "automation.motion_lights",
|
|
"state": "on",
|
|
"attributes": {
|
|
"friendly_name": "Motion Lights"
|
|
},
|
|
"last_changed": "2025-01-01T07:00:00Z"
|
|
},
|
|
{
|
|
"entity_id": "sensor.temperature",
|
|
"state": "22.5",
|
|
"attributes": {
|
|
"friendly_name": "Temperature",
|
|
"unit_of_measurement": "°C"
|
|
},
|
|
"last_changed": "2025-01-01T06:00:00Z"
|
|
}
|
|
]
|
|
|
|
|
|
class TestHousekeepingHealth:
|
|
"""Test /housekeeping/health endpoint."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_health_returns_200(self, mock_get_ha, client):
|
|
"""Health endpoint should return 200."""
|
|
mock_client = AsyncMock()
|
|
mock_client.health_check.return_value = {
|
|
"status": "healthy",
|
|
"connected": True,
|
|
"platform": "home_assistant",
|
|
"version": "2024.12.0"
|
|
}
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/health")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_health_returns_connection_status(self, mock_get_ha, client):
|
|
"""Health endpoint should return connection status."""
|
|
mock_client = AsyncMock()
|
|
mock_client.health_check.return_value = {
|
|
"status": "healthy",
|
|
"connected": True,
|
|
"platform": "home_assistant",
|
|
"version": "2024.12.0"
|
|
}
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/health")
|
|
data = response.json()
|
|
|
|
assert "status" in data
|
|
assert "connected" in data
|
|
assert "platform" in data
|
|
assert data["platform"] == "home_assistant"
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_health_returns_unhealthy_when_disconnected(self, mock_get_ha, client):
|
|
"""Health should report unhealthy when HA is disconnected."""
|
|
mock_client = AsyncMock()
|
|
mock_client.health_check.return_value = {
|
|
"status": "unhealthy",
|
|
"connected": False,
|
|
"platform": "home_assistant",
|
|
"error": "Connection refused"
|
|
}
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/health")
|
|
data = response.json()
|
|
|
|
assert data["connected"] is False
|
|
assert data["status"] == "unhealthy"
|
|
|
|
|
|
class TestHousekeepingDevices:
|
|
"""Test /housekeeping/devices endpoints."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_devices_returns_200(self, mock_get_ha, client, sample_states):
|
|
"""List devices should return 200."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/devices")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_devices_returns_controllable_only(self, mock_get_ha, client, sample_states):
|
|
"""List devices should filter out non-controllable entities."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/devices")
|
|
data = response.json()
|
|
|
|
# Should include lights, switches, scenes, scripts, automations
|
|
# Should NOT include sensors
|
|
entity_ids = [d["entity_id"] for d in data["devices"]]
|
|
assert "light.living_room" in entity_ids
|
|
assert "switch.garage" in entity_ids
|
|
assert "sensor.temperature" not in entity_ids
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_devices_filter_by_domain(self, mock_get_ha, client, sample_states):
|
|
"""List devices should filter by domain parameter."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/devices?domain=light")
|
|
data = response.json()
|
|
|
|
# Should only return lights
|
|
assert len(data["devices"]) == 2
|
|
for device in data["devices"]:
|
|
assert device["domain"] == "light"
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_devices_includes_attributes(self, mock_get_ha, client, sample_states):
|
|
"""List devices should include device attributes."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/devices")
|
|
data = response.json()
|
|
|
|
# Find the living room light
|
|
living_room = next(d for d in data["devices"] if d["entity_id"] == "light.living_room")
|
|
assert living_room["name"] == "Living Room Light"
|
|
assert living_room["state"] == "on"
|
|
assert "brightness" in living_room["attributes"]
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_get_device_returns_200(self, mock_get_ha, client):
|
|
"""Get device should return 200 for existing device."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_state.return_value = {
|
|
"entity_id": "light.living_room",
|
|
"state": "on",
|
|
"attributes": {
|
|
"friendly_name": "Living Room Light",
|
|
"brightness": 255
|
|
},
|
|
"last_changed": "2025-01-01T12:00:00Z"
|
|
}
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/devices/light.living_room")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_get_device_returns_404_for_missing(self, mock_get_ha, client):
|
|
"""Get device should return 404 for non-existent device."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_state.return_value = None
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/devices/light.nonexistent")
|
|
assert response.status_code == 404
|
|
|
|
data = response.json()
|
|
assert data["detail"]["code"] == "DEVICE_NOT_FOUND"
|
|
|
|
|
|
class TestHousekeepingAreas:
|
|
"""Test /housekeeping/areas endpoint."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_areas_returns_200(self, mock_get_ha, client):
|
|
"""List areas should return 200."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_areas.return_value = [
|
|
{"id": "living_room", "name": "Living Room"},
|
|
{"id": "bedroom", "name": "Bedroom"}
|
|
]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/areas")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_areas_returns_area_data(self, mock_get_ha, client):
|
|
"""List areas should return area id and name."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_areas.return_value = [
|
|
{"id": "living_room", "name": "Living Room"},
|
|
{"id": "bedroom", "name": "Bedroom"}
|
|
]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/areas")
|
|
data = response.json()
|
|
|
|
assert "areas" in data
|
|
assert len(data["areas"]) == 2
|
|
assert data["areas"][0]["id"] == "living_room"
|
|
assert data["areas"][0]["name"] == "Living Room"
|
|
|
|
|
|
class TestHousekeepingScenes:
|
|
"""Test /housekeeping/scenes endpoints."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_scenes_returns_200(self, mock_get_ha, client, sample_states):
|
|
"""List scenes should return 200."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/scenes")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_scenes_returns_only_scenes(self, mock_get_ha, client, sample_states):
|
|
"""List scenes should only return scene entities."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/scenes")
|
|
data = response.json()
|
|
|
|
assert "scenes" in data
|
|
assert len(data["scenes"]) == 1
|
|
assert data["scenes"][0]["id"] == "scene.movie_night"
|
|
assert data["scenes"][0]["name"] == "Movie Night"
|
|
|
|
|
|
class TestHousekeepingScripts:
|
|
"""Test /housekeeping/scripts endpoints."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_scripts_returns_200(self, mock_get_ha, client, sample_states):
|
|
"""List scripts should return 200."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/scripts")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_scripts_returns_only_scripts(self, mock_get_ha, client, sample_states):
|
|
"""List scripts should only return script entities."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/scripts")
|
|
data = response.json()
|
|
|
|
assert "scripts" in data
|
|
assert len(data["scripts"]) == 1
|
|
assert data["scripts"][0]["id"] == "script.bedtime"
|
|
|
|
|
|
class TestHousekeepingAutomations:
|
|
"""Test /housekeeping/automations endpoints."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_automations_returns_200(self, mock_get_ha, client, sample_states):
|
|
"""List automations should return 200."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/automations")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_list_automations_includes_enabled_status(self, mock_get_ha, client, sample_states):
|
|
"""List automations should include enabled status."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_states.return_value = sample_states
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/automations")
|
|
data = response.json()
|
|
|
|
assert "automations" in data
|
|
assert len(data["automations"]) == 1
|
|
assert data["automations"][0]["id"] == "automation.motion_lights"
|
|
assert data["automations"][0]["enabled"] is True
|
|
|
|
|
|
class TestHousekeepingHistory:
|
|
"""Test /housekeeping/history endpoint."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_history_returns_200(self, mock_get_ha, client):
|
|
"""History endpoint should return 200."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_history.return_value = [[
|
|
{"state": "on", "last_changed": "2025-01-01T12:00:00Z", "attributes": {}},
|
|
{"state": "off", "last_changed": "2025-01-01T11:00:00Z", "attributes": {}}
|
|
]]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/history?entity_id=light.living_room")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_history_returns_entries(self, mock_get_ha, client):
|
|
"""History endpoint should return history entries."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_history.return_value = [[
|
|
{"state": "on", "last_changed": "2025-01-01T12:00:00Z", "attributes": {"brightness": 255}},
|
|
{"state": "off", "last_changed": "2025-01-01T11:00:00Z", "attributes": {}}
|
|
]]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.get("/housekeeping/history?entity_id=light.living_room&hours=24")
|
|
data = response.json()
|
|
|
|
assert "entity_id" in data
|
|
assert "history" in data
|
|
assert data["entity_id"] == "light.living_room"
|
|
assert len(data["history"]) == 2
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_history_requires_entity_id(self, mock_get_ha, client):
|
|
"""History endpoint should require entity_id parameter."""
|
|
response = client.get("/housekeeping/history")
|
|
assert response.status_code == 422 # Validation error
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_history_validates_hours_range(self, mock_get_ha, client):
|
|
"""History endpoint should validate hours range (1-168)."""
|
|
mock_client = AsyncMock()
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
# Too high
|
|
response = client.get("/housekeeping/history?entity_id=light.test&hours=200")
|
|
assert response.status_code == 422
|
|
|
|
# Too low
|
|
response = client.get("/housekeeping/history?entity_id=light.test&hours=0")
|
|
assert response.status_code == 422
|
|
|
|
|
|
class TestHousekeepingDeviceControl:
|
|
"""Test /housekeeping/devices/{entity_id}/control endpoint."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_control_device_turn_on(self, mock_get_ha, client):
|
|
"""Control should turn on device."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_state.return_value = {
|
|
"entity_id": "light.living_room",
|
|
"state": "on",
|
|
"attributes": {"brightness": 255}
|
|
}
|
|
mock_client.turn_on.return_value = [{"entity_id": "light.living_room"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/devices/light.living_room/control",
|
|
json={"action": "turn_on"}
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_control_device_turn_off(self, mock_get_ha, client):
|
|
"""Control should turn off device."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_state.return_value = {
|
|
"entity_id": "light.living_room",
|
|
"state": "off",
|
|
"attributes": {}
|
|
}
|
|
mock_client.turn_off.return_value = [{"entity_id": "light.living_room"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/devices/light.living_room/control",
|
|
json={"action": "turn_off"}
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_control_device_toggle(self, mock_get_ha, client):
|
|
"""Control should toggle device."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_state.return_value = {
|
|
"entity_id": "light.living_room",
|
|
"state": "on",
|
|
"attributes": {}
|
|
}
|
|
mock_client.toggle.return_value = [{"entity_id": "light.living_room"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/devices/light.living_room/control",
|
|
json={"action": "toggle"}
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_control_device_with_brightness(self, mock_get_ha, client):
|
|
"""Control should set brightness."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_state.return_value = {
|
|
"entity_id": "light.living_room",
|
|
"state": "on",
|
|
"attributes": {"brightness": 128}
|
|
}
|
|
mock_client.turn_on.return_value = [{"entity_id": "light.living_room"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/devices/light.living_room/control",
|
|
json={"action": "turn_on", "brightness": 128}
|
|
)
|
|
assert response.status_code == 200
|
|
mock_client.turn_on.assert_called_once()
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_control_device_returns_404_for_missing(self, mock_get_ha, client):
|
|
"""Control should return 404 for non-existent device."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_state.return_value = None
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/devices/light.nonexistent/control",
|
|
json={"action": "turn_on"}
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_control_device_returns_error_response(self, mock_get_ha, client):
|
|
"""Control should return proper error response."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_state.return_value = {
|
|
"entity_id": "light.living_room",
|
|
"state": "on",
|
|
"attributes": {}
|
|
}
|
|
mock_client.turn_on.side_effect = Exception("Service unavailable")
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/devices/light.living_room/control",
|
|
json={"action": "turn_on"}
|
|
)
|
|
assert response.status_code == 500
|
|
|
|
|
|
class TestHousekeepingSceneActivation:
|
|
"""Test /housekeeping/scenes/{scene_id}/activate endpoint."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_activate_scene_returns_200(self, mock_get_ha, client):
|
|
"""Activate scene should return 200."""
|
|
mock_client = AsyncMock()
|
|
mock_client.activate_scene.return_value = [{"entity_id": "scene.movie_night"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post("/housekeeping/scenes/scene.movie_night/activate")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_activate_scene_returns_success_response(self, mock_get_ha, client):
|
|
"""Activate scene should return success response."""
|
|
mock_client = AsyncMock()
|
|
mock_client.activate_scene.return_value = [{"entity_id": "scene.movie_night"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post("/housekeeping/scenes/scene.movie_night/activate")
|
|
data = response.json()
|
|
|
|
assert data["success"] is True
|
|
assert data["scene_id"] == "scene.movie_night"
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_activate_scene_handles_error(self, mock_get_ha, client):
|
|
"""Activate scene should handle errors."""
|
|
mock_client = AsyncMock()
|
|
mock_client.activate_scene.side_effect = Exception("Service error")
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post("/housekeeping/scenes/scene.movie_night/activate")
|
|
assert response.status_code == 500
|
|
|
|
|
|
class TestHousekeepingScriptRun:
|
|
"""Test /housekeeping/scripts/{script_id}/run endpoint."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_run_script_returns_200(self, mock_get_ha, client):
|
|
"""Run script should return 200."""
|
|
mock_client = AsyncMock()
|
|
mock_client.run_script.return_value = [{"entity_id": "script.bedtime"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post("/housekeeping/scripts/script.bedtime/run")
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_run_script_returns_success_response(self, mock_get_ha, client):
|
|
"""Run script should return success response."""
|
|
mock_client = AsyncMock()
|
|
mock_client.run_script.return_value = [{"entity_id": "script.bedtime"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post("/housekeeping/scripts/script.bedtime/run")
|
|
data = response.json()
|
|
|
|
assert data["success"] is True
|
|
assert data["script_id"] == "script.bedtime"
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_run_script_handles_error(self, mock_get_ha, client):
|
|
"""Run script should handle errors."""
|
|
mock_client = AsyncMock()
|
|
mock_client.run_script.side_effect = Exception("Script error")
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post("/housekeeping/scripts/script.bedtime/run")
|
|
assert response.status_code == 500
|
|
|
|
|
|
class TestHousekeepingAutomationToggle:
|
|
"""Test /housekeeping/automations/{automation_id}/toggle endpoint."""
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_toggle_automation_enable(self, mock_get_ha, client):
|
|
"""Toggle automation should enable when requested."""
|
|
mock_client = AsyncMock()
|
|
mock_client.enable_automation.return_value = [{"entity_id": "automation.motion_lights"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/automations/automation.motion_lights/toggle",
|
|
json={"enabled": True}
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_toggle_automation_disable(self, mock_get_ha, client):
|
|
"""Toggle automation should disable when requested."""
|
|
mock_client = AsyncMock()
|
|
mock_client.disable_automation.return_value = [{"entity_id": "automation.motion_lights"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/automations/automation.motion_lights/toggle",
|
|
json={"enabled": False}
|
|
)
|
|
assert response.status_code == 200
|
|
mock_client.disable_automation.assert_called_once()
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_toggle_automation_returns_new_state(self, mock_get_ha, client):
|
|
"""Toggle automation should return new enabled state."""
|
|
mock_client = AsyncMock()
|
|
mock_client.enable_automation.return_value = [{"entity_id": "automation.motion_lights"}]
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/automations/automation.motion_lights/toggle",
|
|
json={"enabled": True}
|
|
)
|
|
data = response.json()
|
|
|
|
assert data["success"] is True
|
|
assert data["automation_id"] == "automation.motion_lights"
|
|
assert data["enabled"] is True
|
|
|
|
@patch("src.domains.housekeeping.controller.get_homeassistant_client")
|
|
def test_toggle_automation_handles_error(self, mock_get_ha, client):
|
|
"""Toggle automation should handle errors."""
|
|
mock_client = AsyncMock()
|
|
mock_client.enable_automation.side_effect = Exception("Automation error")
|
|
mock_get_ha.return_value = mock_client
|
|
|
|
response = client.post(
|
|
"/housekeeping/automations/automation.motion_lights/toggle",
|
|
json={"enabled": True}
|
|
)
|
|
assert response.status_code == 500
|