# #region Test.Maintenance.Orchestrators [C:4] [TYPE Module] [SEMANTICS test, maintenance, orchestration, coverage, edge] # @BRIEF Tests for maintenance orchestrators — start_maintenance, end_maintenance, end_all_maintenance. # @RELATION BINDS_TO -> [MaintenanceOrchestrators] import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src")) from unittest.mock import AsyncMock, MagicMock, patch, PropertyMock import pytest from src.models.maintenance import ( MaintenanceDashboardState, MaintenanceEvent, MaintenanceEventStatus, MaintenanceSettings, ) # ── start_maintenance coverage ── class TestStartMaintenance: """start_maintenance — event start orchestration.""" @pytest.mark.asyncio async def test_dashboard_discovery_failure(self): """find_affected_dashboards raises → FAILED status.""" from src.services.maintenance._orchestrators import start_maintenance db = MagicMock() event = MaintenanceEvent( id="evt1", status=MaintenanceEventStatus.PENDING, tables=["table_a"], environment_id="env1", ) # Chain for MaintenanceEvent query db.query.return_value.filter.return_value.with_for_update.return_value.first.return_value = event # Chain for MaintenanceSettings query db.query.return_value.filter.return_value.first.return_value = MaintenanceSettings( id="default", target_environment_id="env1", banner_template="Tpl", display_timezone="UTC" ) superset = AsyncMock() with patch("src.services.maintenance._orchestrators.find_affected_dashboards", new_callable=AsyncMock, side_effect=Exception("Discovery failed")): result = await start_maintenance("evt1", db, superset) assert result["status"] == "failed" assert "Discovery failed" in result["error"] @pytest.mark.asyncio async def test_partial_success_some_failures(self): """Some dashboards fail → PARTIAL status.""" from src.services.maintenance._orchestrators import start_maintenance db = MagicMock() event = MaintenanceEvent( id="evt1", status=MaintenanceEventStatus.PENDING, tables=["table_a"], environment_id="env1", ) db.query.return_value.filter.return_value.with_for_update.return_value.first.return_value = event db.query.return_value.filter.return_value.first.return_value = MaintenanceSettings( id="default", target_environment_id="env1", banner_template="Tpl", display_timezone="UTC" ) superset = AsyncMock() with patch("src.services.maintenance._orchestrators.find_affected_dashboards", new_callable=AsyncMock, return_value=["dash1", "dash2"]), \ patch("src.services.maintenance._orchestrators._process_dashboards_for_start", new_callable=AsyncMock, return_value=( ["dash1"], # successful ["dash2"], # failed [], # unmatched )): result = await start_maintenance("evt1", db, superset) assert result["status"] == "partial" assert result["affected_dashboards"] == 1 assert len(result["failed_dashboards"]) == 1 @pytest.mark.asyncio async def test_all_failures(self): """All dashboards fail → FAILED.""" from src.services.maintenance._orchestrators import start_maintenance db = MagicMock() event = MaintenanceEvent( id="evt1", status=MaintenanceEventStatus.PENDING, tables=["table_a"], environment_id="env1", ) db.query.return_value.filter.return_value.with_for_update.return_value.first.return_value = event db.query.return_value.filter.return_value.first.return_value = MaintenanceSettings( id="default", target_environment_id="env1", banner_template="Tpl", display_timezone="UTC" ) superset = AsyncMock() with patch("src.services.maintenance._orchestrators.find_affected_dashboards", new_callable=AsyncMock, return_value=["dash1"]), \ patch("src.services.maintenance._orchestrators._process_dashboards_for_start", new_callable=AsyncMock, return_value=( [], # successful ["dash1"], # failed [], # unmatched )): result = await start_maintenance("evt1", db, superset) assert result["status"] == "failed" assert result["affected_dashboards"] == 0 # ── end_maintenance ── class TestEndMaintenance: """end_maintenance — event end orchestration.""" @pytest.mark.asyncio async def test_event_not_found(self): """Event missing → failed.""" from src.services.maintenance._orchestrators import end_maintenance db = MagicMock() db.query.return_value.filter.return_value.with_for_update.return_value.first.return_value = None result = await end_maintenance("nonexistent", db, AsyncMock()) assert result["status"] == "failed" @pytest.mark.asyncio async def test_already_completed(self): """Already completed → idempotent status.""" from src.services.maintenance._orchestrators import end_maintenance db = MagicMock() event = MaintenanceEvent(id="evt1", status=MaintenanceEventStatus.COMPLETED) db.query.return_value.filter.return_value.with_for_update.return_value.first.return_value = event result = await end_maintenance("evt1", db, AsyncMock()) assert result["status"] == "already_completed" @pytest.mark.asyncio async def test_success(self): """Successful end → completed status.""" from src.services.maintenance._orchestrators import end_maintenance db = MagicMock() event = MaintenanceEvent(id="evt1", status=MaintenanceEventStatus.ACTIVE) db.query.return_value.filter.return_value.with_for_update.return_value.first.return_value = event db.query.return_value.filter.return_value.all.return_value = [] with patch("src.services.maintenance._orchestrators._process_states_for_end", new_callable=AsyncMock, return_value=(0, [])): result = await end_maintenance("evt1", db, AsyncMock()) assert result["status"] == "completed" @pytest.mark.asyncio async def test_partial_failed_removals(self): """Some removals fail → partial status.""" from src.services.maintenance._orchestrators import end_maintenance db = MagicMock() event = MaintenanceEvent(id="evt1", status=MaintenanceEventStatus.ACTIVE) db.query.return_value.filter.return_value.with_for_update.return_value.first.return_value = event db.query.return_value.filter.return_value.all.return_value = [] with patch("src.services.maintenance._orchestrators._process_states_for_end", new_callable=AsyncMock, return_value=(1, [{"id": 5, "title": "dash5", "error": "removal failed"}])): result = await end_maintenance("evt1", db, AsyncMock()) assert result["status"] == "partial" assert len(result["failed_removals"]) == 1 # ── end_all_maintenance ── class TestEndAllMaintenance: """end_all_maintenance — bulk end.""" @pytest.mark.asyncio async def test_no_active_events(self): """No active events → immediate completion.""" from src.services.maintenance._orchestrators import end_all_maintenance db = MagicMock() db.query.return_value.filter.return_value.in_.return_value.all.return_value = [] result = await end_all_maintenance(db, AsyncMock()) assert result["status"] == "completed" assert result["closed_events"] == 0 @pytest.mark.asyncio async def test_one_event_ends_cleanly(self): """One active event → ended cleanly.""" from src.services.maintenance._orchestrators import end_all_maintenance db = MagicMock() event = MaintenanceEvent(id="evt1", status=MaintenanceEventStatus.ACTIVE) db.query.return_value.filter.return_value.all.return_value = [event] with patch("src.services.maintenance._orchestrators.end_maintenance", new_callable=AsyncMock, return_value={"removed_from": 3, "failed_removals": []}): result = await end_all_maintenance(db, AsyncMock()) assert result["status"] == "completed" assert result["closed_events"] == 1 assert result["removed_from"] == 3 @pytest.mark.asyncio async def test_end_maintenance_raises_exception(self): """end_maintenance raises → event skipped with failed_removals entry.""" from src.services.maintenance._orchestrators import end_all_maintenance db = MagicMock() event = MaintenanceEvent(id="evt_fail", status=MaintenanceEventStatus.ACTIVE) db.query.return_value.filter.return_value.all.return_value = [event] with patch("src.services.maintenance._orchestrators.end_maintenance", new_callable=AsyncMock, side_effect=Exception("end failure")): result = await end_all_maintenance(db, AsyncMock()) assert result["status"] == "partial" assert result["closed_events"] == 0 assert len(result["failed_removals"]) == 1 assert result["failed_removals"][0]["title"] == "evt_fail" assert "end failure" in result["failed_removals"][0]["error"] @pytest.mark.asyncio async def test_mixed_success_failure(self): """Multiple events: some succeed, some fail.""" from src.services.maintenance._orchestrators import end_all_maintenance db = MagicMock() evt_ok = MaintenanceEvent(id="evt_ok", status=MaintenanceEventStatus.ACTIVE) evt_fail = MaintenanceEvent(id="evt_fail", status=MaintenanceEventStatus.PARTIAL) db.query.return_value.filter.return_value.all.return_value = [evt_ok, evt_fail] call_count = [0] async def mock_end_maintenance(event_id, db_session, superset): call_count[0] += 1 if call_count[0] == 1: return {"removed_from": 2, "failed_removals": []} raise Exception("second failed") with patch("src.services.maintenance._orchestrators.end_maintenance", new_callable=AsyncMock, side_effect=mock_end_maintenance): result = await end_all_maintenance(db, AsyncMock()) assert result["status"] == "partial" assert result["closed_events"] == 1 assert result["removed_from"] == 2 assert len(result["failed_removals"]) == 1 # #endregion Test.Maintenance.Orchestrators