Files
ss-tools/backend/src/services/__tests__/test_health_service.py

286 lines
9.3 KiB
Python

import pytest
from datetime import datetime, timedelta
from unittest.mock import MagicMock, patch
from src.services.health_service import HealthService
from src.models.llm import ValidationRecord
# [DEF:test_health_service:Module]
# @TIER: STANDARD
# @PURPOSE: Unit tests for HealthService aggregation logic.
@pytest.mark.asyncio
async def test_get_health_summary_aggregation():
"""
@TEST_SCENARIO: Verify that HealthService correctly aggregates the latest record per dashboard.
"""
# Setup: Mock DB session
db = MagicMock()
now = datetime.utcnow()
# Dashboard 1: Old FAIL, New PASS
rec1_old = ValidationRecord(
id="rec-old",
dashboard_id="dash_1",
environment_id="env_1",
status="FAIL",
timestamp=now - timedelta(hours=1),
summary="Old failure",
issues=[]
)
rec1_new = ValidationRecord(
id="rec-new",
dashboard_id="dash_1",
environment_id="env_1",
status="PASS",
timestamp=now,
summary="New pass",
issues=[]
)
# Dashboard 2: Single WARN
rec2 = ValidationRecord(
id="rec-warn",
dashboard_id="dash_2",
environment_id="env_1",
status="WARN",
timestamp=now,
summary="Warning",
issues=[]
)
# Mock the query chain
# subquery = self.db.query(...).filter(...).group_by(...).subquery()
# query = self.db.query(ValidationRecord).join(subquery, ...).all()
mock_query = db.query.return_value
mock_query.filter.return_value = mock_query
mock_query.group_by.return_value = mock_query
mock_query.subquery.return_value = MagicMock()
db.query.return_value.join.return_value.all.return_value = [rec1_new, rec2]
service = HealthService(db)
summary = await service.get_health_summary(environment_id="env_1")
assert summary.pass_count == 1
assert summary.warn_count == 1
assert summary.fail_count == 0
assert len(summary.items) == 2
# Verify dash_1 has the latest status (PASS)
dash_1_item = next(item for item in summary.items if item.dashboard_id == "dash_1")
assert dash_1_item.status == "PASS"
assert dash_1_item.summary == "New pass"
assert dash_1_item.record_id == rec1_new.id
assert dash_1_item.dashboard_slug == "dash_1"
@pytest.mark.asyncio
async def test_get_health_summary_empty():
"""
@TEST_SCENARIO: Verify behavior with no records.
"""
db = MagicMock()
db.query.return_value.join.return_value.all.return_value = []
service = HealthService(db)
summary = await service.get_health_summary(environment_id="env_none")
assert summary.pass_count == 0
assert len(summary.items) == 0
@pytest.mark.asyncio
async def test_get_health_summary_resolves_slug_and_title_from_superset():
db = MagicMock()
config_manager = MagicMock()
config_manager.get_environments.return_value = [MagicMock(id="env_1")]
record = ValidationRecord(
id="rec-1",
dashboard_id="42",
environment_id="env_1",
status="PASS",
timestamp=datetime.utcnow(),
summary="Healthy",
issues=[],
)
db.query.return_value.join.return_value.all.return_value = [record]
with patch("src.services.health_service.SupersetClient") as mock_client_cls:
mock_client = MagicMock()
mock_client.get_dashboards_summary.return_value = [
{"id": 42, "slug": "ops-overview", "title": "Ops Overview"}
]
mock_client_cls.return_value = mock_client
service = HealthService(db, config_manager=config_manager)
summary = await service.get_health_summary(environment_id="env_1")
assert summary.items[0].dashboard_slug == "ops-overview"
assert summary.items[0].dashboard_title == "Ops Overview"
mock_client.get_dashboards_summary.assert_called_once_with()
@pytest.mark.anyio
async def test_get_health_summary_reuses_dashboard_metadata_cache_across_service_instances():
HealthService._dashboard_summary_cache.clear()
db = MagicMock()
config_manager = MagicMock()
config_manager.get_environments.return_value = [MagicMock(id="env_1")]
record = ValidationRecord(
id="rec-1",
dashboard_id="42",
environment_id="env_1",
status="PASS",
timestamp=datetime.utcnow(),
summary="Healthy",
issues=[],
)
db.query.return_value.join.return_value.all.return_value = [record]
with patch("src.services.health_service.SupersetClient") as mock_client_cls:
mock_client = MagicMock()
mock_client.get_dashboards_summary.return_value = [
{"id": 42, "slug": "ops-overview", "title": "Ops Overview"}
]
mock_client_cls.return_value = mock_client
first_service = HealthService(db, config_manager=config_manager)
second_service = HealthService(db, config_manager=config_manager)
first_summary = await first_service.get_health_summary(environment_id="env_1")
second_summary = await second_service.get_health_summary(environment_id="env_1")
assert first_summary.items[0].dashboard_slug == "ops-overview"
assert second_summary.items[0].dashboard_slug == "ops-overview"
mock_client.get_dashboards_summary.assert_called_once_with()
HealthService._dashboard_summary_cache.clear()
def test_delete_validation_report_deletes_dashboard_scope_and_linked_tasks():
db = MagicMock()
config_manager = MagicMock()
task_manager = MagicMock()
task_manager.tasks = {"task-1": object(), "task-2": object(), "task-3": object()}
target_record = ValidationRecord(
id="rec-1",
task_id="task-1",
dashboard_id="42",
environment_id="env_1",
status="PASS",
timestamp=datetime.utcnow(),
summary="Healthy",
issues=[],
screenshot_path=None,
)
older_peer = ValidationRecord(
id="rec-2",
task_id="task-2",
dashboard_id="42",
environment_id="env_1",
status="FAIL",
timestamp=datetime.utcnow() - timedelta(hours=1),
summary="Older",
issues=[],
screenshot_path=None,
)
other_environment = ValidationRecord(
id="rec-3",
task_id="task-3",
dashboard_id="42",
environment_id="env_2",
status="WARN",
timestamp=datetime.utcnow(),
summary="Other environment",
issues=[],
screenshot_path=None,
)
first_query = MagicMock()
first_query.first.return_value = target_record
peer_query = MagicMock()
peer_query.filter.return_value = peer_query
peer_query.all.return_value = [target_record, older_peer]
db.query.side_effect = [first_query, peer_query]
with patch("src.services.health_service.TaskCleanupService") as cleanup_cls:
cleanup_instance = MagicMock()
cleanup_cls.return_value = cleanup_instance
service = HealthService(db, config_manager=config_manager)
deleted = service.delete_validation_report("rec-1", task_manager=task_manager)
assert deleted is True
assert db.delete.call_count == 2
db.delete.assert_any_call(target_record)
db.delete.assert_any_call(older_peer)
db.commit.assert_called_once()
cleanup_instance.delete_task_with_logs.assert_any_call("task-1")
cleanup_instance.delete_task_with_logs.assert_any_call("task-2")
cleanup_instance.delete_task_with_logs.call_count == 2
assert "task-1" not in task_manager.tasks
assert "task-2" not in task_manager.tasks
assert "task-3" in task_manager.tasks
def test_delete_validation_report_returns_false_for_unknown_record():
db = MagicMock()
db.query.return_value.filter.return_value.first.return_value = None
service = HealthService(db, config_manager=MagicMock())
assert service.delete_validation_report("missing") is False
def test_delete_validation_report_swallows_linked_task_cleanup_failure():
db = MagicMock()
config_manager = MagicMock()
task_manager = MagicMock()
task_manager.tasks = {"task-1": object()}
record = ValidationRecord(
id="rec-1",
task_id="task-1",
dashboard_id="42",
environment_id="env_1",
status="PASS",
timestamp=datetime.utcnow(),
summary="Healthy",
issues=[],
screenshot_path=None,
)
first_query = MagicMock()
first_query.first.return_value = record
peer_query = MagicMock()
peer_query.filter.return_value = peer_query
peer_query.all.return_value = [record]
db.query.side_effect = [first_query, peer_query]
with patch("src.services.health_service.TaskCleanupService") as cleanup_cls, patch(
"src.services.health_service.logger"
) as mock_logger:
cleanup_instance = MagicMock()
cleanup_instance.delete_task_with_logs.side_effect = RuntimeError("cleanup exploded")
cleanup_cls.return_value = cleanup_instance
service = HealthService(db, config_manager=config_manager)
deleted = service.delete_validation_report("rec-1", task_manager=task_manager)
assert deleted is True
db.delete.assert_called_once_with(record)
db.commit.assert_called_once()
cleanup_instance.delete_task_with_logs.assert_called_once_with("task-1")
mock_logger.warning.assert_called_once()
assert "task-1" not in task_manager.tasks
# [/DEF:test_health_service:Module]