fix: commit verified semantic repair changes
This commit is contained in:
@@ -7,6 +7,8 @@ from src.models.llm import ValidationRecord
|
||||
# [DEF:test_health_service:Module]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Unit tests for HealthService aggregation logic.
|
||||
# @RELATION: VERIFIES ->[src.services.health_service.HealthService]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_health_summary_aggregation():
|
||||
@@ -15,9 +17,9 @@ async def test_get_health_summary_aggregation():
|
||||
"""
|
||||
# Setup: Mock DB session
|
||||
db = MagicMock()
|
||||
|
||||
|
||||
now = datetime.utcnow()
|
||||
|
||||
|
||||
# Dashboard 1: Old FAIL, New PASS
|
||||
rec1_old = ValidationRecord(
|
||||
id="rec-old",
|
||||
@@ -26,7 +28,7 @@ async def test_get_health_summary_aggregation():
|
||||
status="FAIL",
|
||||
timestamp=now - timedelta(hours=1),
|
||||
summary="Old failure",
|
||||
issues=[]
|
||||
issues=[],
|
||||
)
|
||||
rec1_new = ValidationRecord(
|
||||
id="rec-new",
|
||||
@@ -35,9 +37,9 @@ async def test_get_health_summary_aggregation():
|
||||
status="PASS",
|
||||
timestamp=now,
|
||||
summary="New pass",
|
||||
issues=[]
|
||||
issues=[],
|
||||
)
|
||||
|
||||
|
||||
# Dashboard 2: Single WARN
|
||||
rec2 = ValidationRecord(
|
||||
id="rec-warn",
|
||||
@@ -46,28 +48,28 @@ async def test_get_health_summary_aggregation():
|
||||
status="WARN",
|
||||
timestamp=now,
|
||||
summary="Warning",
|
||||
issues=[]
|
||||
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"
|
||||
@@ -75,6 +77,7 @@ async def test_get_health_summary_aggregation():
|
||||
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():
|
||||
"""
|
||||
@@ -82,10 +85,10 @@ async def test_get_health_summary_empty():
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -159,6 +162,8 @@ async def test_get_health_summary_reuses_dashboard_metadata_cache_across_service
|
||||
HealthService._dashboard_summary_cache.clear()
|
||||
|
||||
|
||||
# [DEF:test_delete_validation_report_deletes_dashboard_scope_and_linked_tasks:Function]
|
||||
# @RELATION: BINDS_TO ->[test_health_service]
|
||||
def test_delete_validation_report_deletes_dashboard_scope_and_linked_tasks():
|
||||
db = MagicMock()
|
||||
config_manager = MagicMock()
|
||||
@@ -222,12 +227,17 @@ def test_delete_validation_report_deletes_dashboard_scope_and_linked_tasks():
|
||||
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 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_deletes_dashboard_scope_and_linked_tasks:Function]
|
||||
|
||||
|
||||
# [DEF:test_delete_validation_report_returns_false_for_unknown_record:Function]
|
||||
# @RELATION: BINDS_TO ->[test_health_service]
|
||||
def test_delete_validation_report_returns_false_for_unknown_record():
|
||||
db = MagicMock()
|
||||
db.query.return_value.filter.return_value.first.return_value = None
|
||||
@@ -237,6 +247,11 @@ def test_delete_validation_report_returns_false_for_unknown_record():
|
||||
assert service.delete_validation_report("missing") is False
|
||||
|
||||
|
||||
# [/DEF:test_delete_validation_report_returns_false_for_unknown_record:Function]
|
||||
|
||||
|
||||
# [DEF:test_delete_validation_report_swallows_linked_task_cleanup_failure:Function]
|
||||
# @RELATION: BINDS_TO ->[test_health_service]
|
||||
def test_delete_validation_report_swallows_linked_task_cleanup_failure():
|
||||
db = MagicMock()
|
||||
config_manager = MagicMock()
|
||||
@@ -264,11 +279,14 @@ def test_delete_validation_report_swallows_linked_task_cleanup_failure():
|
||||
|
||||
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:
|
||||
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_instance.delete_task_with_logs.side_effect = RuntimeError(
|
||||
"cleanup exploded"
|
||||
)
|
||||
cleanup_cls.return_value = cleanup_instance
|
||||
|
||||
service = HealthService(db, config_manager=config_manager)
|
||||
@@ -282,4 +300,5 @@ def test_delete_validation_report_swallows_linked_task_cleanup_failure():
|
||||
assert "task-1" not in task_manager.tasks
|
||||
|
||||
|
||||
# [/DEF:test_delete_validation_report_swallows_linked_task_cleanup_failure:Function]
|
||||
# [/DEF:test_health_service:Module]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# [DEF:backend.src.services.__tests__.test_llm_plugin_persistence:Module]
|
||||
# [DEF:test_llm_plugin_persistence:Module]
|
||||
# @RELATION: VERIFIES ->[src.plugins.llm_analysis.plugin.DashboardValidationPlugin]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Regression test for ValidationRecord persistence fields populated from task context.
|
||||
|
||||
@@ -9,6 +10,7 @@ from src.plugins.llm_analysis import plugin as plugin_module
|
||||
|
||||
|
||||
# [DEF:_DummyLogger:Class]
|
||||
# @RELATION: BINDS_TO ->[test_llm_plugin_persistence]
|
||||
# @PURPOSE: Minimal logger shim for TaskContext-like objects used in tests.
|
||||
class _DummyLogger:
|
||||
def with_source(self, _source: str):
|
||||
@@ -25,10 +27,13 @@ class _DummyLogger:
|
||||
|
||||
def error(self, *_args, **_kwargs):
|
||||
return None
|
||||
|
||||
|
||||
# [/DEF:_DummyLogger:Class]
|
||||
|
||||
|
||||
# [DEF:_FakeDBSession:Class]
|
||||
# @RELATION: BINDS_TO ->[test_llm_plugin_persistence]
|
||||
# @PURPOSE: Captures persisted records for assertion and mimics SQLAlchemy session methods used by plugin.
|
||||
class _FakeDBSession:
|
||||
def __init__(self):
|
||||
@@ -44,13 +49,18 @@ class _FakeDBSession:
|
||||
|
||||
def close(self):
|
||||
self.closed = True
|
||||
|
||||
|
||||
# [/DEF:_FakeDBSession:Class]
|
||||
|
||||
|
||||
# [DEF:test_dashboard_validation_plugin_persists_task_and_environment_ids:Function]
|
||||
# @RELATION: BINDS_TO ->[test_llm_plugin_persistence]
|
||||
# @PURPOSE: Ensure db ValidationRecord includes context.task_id and params.environment_id.
|
||||
@pytest.mark.asyncio
|
||||
async def test_dashboard_validation_plugin_persists_task_and_environment_ids(tmp_path, monkeypatch):
|
||||
async def test_dashboard_validation_plugin_persists_task_and_environment_ids(
|
||||
tmp_path, monkeypatch
|
||||
):
|
||||
fake_db = _FakeDBSession()
|
||||
|
||||
env = types.SimpleNamespace(id="env-42")
|
||||
@@ -112,7 +122,9 @@ async def test_dashboard_validation_plugin_persists_task_and_environment_ids(tmp
|
||||
|
||||
class _FakeSupersetClient:
|
||||
def __init__(self, _env):
|
||||
self.network = types.SimpleNamespace(request=lambda **_kwargs: {"result": []})
|
||||
self.network = types.SimpleNamespace(
|
||||
request=lambda **_kwargs: {"result": []}
|
||||
)
|
||||
|
||||
monkeypatch.setattr(plugin_module, "SessionLocal", lambda: fake_db)
|
||||
monkeypatch.setattr(plugin_module, "LLMProviderService", _FakeProviderService)
|
||||
@@ -120,7 +132,9 @@ async def test_dashboard_validation_plugin_persists_task_and_environment_ids(tmp
|
||||
monkeypatch.setattr(plugin_module, "LLMClient", _FakeLLMClient)
|
||||
monkeypatch.setattr(plugin_module, "NotificationService", _FakeNotificationService)
|
||||
monkeypatch.setattr(plugin_module, "SupersetClient", _FakeSupersetClient)
|
||||
monkeypatch.setattr("src.dependencies.get_config_manager", lambda: _FakeConfigManager())
|
||||
monkeypatch.setattr(
|
||||
"src.dependencies.get_config_manager", lambda: _FakeConfigManager()
|
||||
)
|
||||
|
||||
context = types.SimpleNamespace(
|
||||
task_id="task-999",
|
||||
@@ -144,7 +158,9 @@ async def test_dashboard_validation_plugin_persists_task_and_environment_ids(tmp
|
||||
assert fake_db.added is not None
|
||||
assert fake_db.added.task_id == "task-999"
|
||||
assert fake_db.added.environment_id == "env-42"
|
||||
|
||||
|
||||
# [/DEF:test_dashboard_validation_plugin_persists_task_and_environment_ids:Function]
|
||||
|
||||
|
||||
# [/DEF:backend.src.services.__tests__.test_llm_plugin_persistence:Module]
|
||||
# [/DEF:test_llm_plugin_persistence:Module]
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# [DEF:backend.src.services.__tests__.test_resource_service:Module]
|
||||
# [DEF:TestResourceService:Module]
|
||||
# @COMPLEXITY: 3
|
||||
# @SEMANTICS: resource-service, tests, dashboards, datasets, activity
|
||||
# @PURPOSE: Unit tests for ResourceService
|
||||
# @LAYER: Service
|
||||
# @RELATION: TESTS -> backend.src.services.resource_service
|
||||
# @RELATION: VERIFIES -> ResourceService
|
||||
# @RELATION: VERIFIES ->[src.services.resource_service.ResourceService]
|
||||
# @INVARIANT: Resource summaries preserve task linkage and status projection behavior.
|
||||
|
||||
import pytest
|
||||
@@ -13,25 +12,27 @@ from datetime import datetime, timezone
|
||||
|
||||
|
||||
# [DEF:test_get_dashboards_with_status:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @PURPOSE: Validate dashboard enrichment includes git/task status projections.
|
||||
# @TEST: get_dashboards_with_status returns dashboards with git and task status
|
||||
# @PRE: SupersetClient returns dashboard list
|
||||
# @POST: Each dashboard has git_status and last_task fields
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_dashboards_with_status():
|
||||
with patch("src.services.resource_service.SupersetClient") as mock_client, \
|
||||
patch("src.services.resource_service.GitService"):
|
||||
|
||||
with (
|
||||
patch("src.services.resource_service.SupersetClient") as mock_client,
|
||||
patch("src.services.resource_service.GitService"),
|
||||
):
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
|
||||
service = ResourceService()
|
||||
|
||||
|
||||
# Mock Superset response
|
||||
mock_client.return_value.get_dashboards_summary.return_value = [
|
||||
{"id": 1, "title": "Dashboard 1", "slug": "dash-1"},
|
||||
{"id": 2, "title": "Dashboard 2", "slug": "dash-2"}
|
||||
{"id": 2, "title": "Dashboard 2", "slug": "dash-2"},
|
||||
]
|
||||
|
||||
|
||||
# Mock tasks
|
||||
task_prod_old = MagicMock()
|
||||
task_prod_old.id = "task-123"
|
||||
@@ -62,7 +63,7 @@ async def test_get_dashboards_with_status():
|
||||
env,
|
||||
[task_prod_old, task_prod_new, task_other_env],
|
||||
)
|
||||
|
||||
|
||||
assert len(result) == 2
|
||||
assert result[0]["id"] == 1
|
||||
assert "git_status" in result[0]
|
||||
@@ -76,35 +77,35 @@ async def test_get_dashboards_with_status():
|
||||
|
||||
|
||||
# [DEF:test_get_datasets_with_status:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: get_datasets_with_status returns datasets with task status
|
||||
# @PRE: SupersetClient returns dataset list
|
||||
# @POST: Each dataset has last_task field
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_datasets_with_status():
|
||||
with patch("src.services.resource_service.SupersetClient") as mock_client:
|
||||
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
|
||||
service = ResourceService()
|
||||
|
||||
|
||||
# Mock Superset response
|
||||
mock_client.return_value.get_datasets_summary.return_value = [
|
||||
{"id": 1, "table_name": "users", "schema": "public", "database": "app"},
|
||||
{"id": 2, "table_name": "orders", "schema": "public", "database": "app"}
|
||||
{"id": 2, "table_name": "orders", "schema": "public", "database": "app"},
|
||||
]
|
||||
|
||||
|
||||
# Mock tasks
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = "task-456"
|
||||
mock_task.status = "RUNNING"
|
||||
mock_task.params = {"resource_id": "dataset-1"}
|
||||
mock_task.created_at = datetime.now()
|
||||
|
||||
|
||||
env = MagicMock()
|
||||
env.id = "prod"
|
||||
|
||||
|
||||
result = await service.get_datasets_with_status(env, [mock_task])
|
||||
|
||||
|
||||
assert len(result) == 2
|
||||
assert result[0]["table_name"] == "users"
|
||||
assert "last_task" in result[0]
|
||||
@@ -116,35 +117,36 @@ async def test_get_datasets_with_status():
|
||||
|
||||
|
||||
# [DEF:test_get_activity_summary:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: get_activity_summary returns active count and recent tasks
|
||||
# @PRE: tasks list provided
|
||||
# @POST: Returns dict with active_count and recent_tasks
|
||||
def test_get_activity_summary():
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
|
||||
service = ResourceService()
|
||||
|
||||
|
||||
# Create mock tasks
|
||||
task1 = MagicMock()
|
||||
task1.id = "task-1"
|
||||
task1.status = "RUNNING"
|
||||
task1.params = {"resource_name": "Dashboard 1", "resource_type": "dashboard"}
|
||||
task1.created_at = datetime(2024, 1, 1, 10, 0, 0)
|
||||
|
||||
|
||||
task2 = MagicMock()
|
||||
task2.id = "task-2"
|
||||
task2.status = "SUCCESS"
|
||||
task2.params = {"resource_name": "Dataset 1", "resource_type": "dataset"}
|
||||
task2.created_at = datetime(2024, 1, 1, 9, 0, 0)
|
||||
|
||||
|
||||
task3 = MagicMock()
|
||||
task3.id = "task-3"
|
||||
task3.status = "WAITING_INPUT"
|
||||
task3.params = {"resource_name": "Dashboard 2", "resource_type": "dashboard"}
|
||||
task3.created_at = datetime(2024, 1, 1, 8, 0, 0)
|
||||
|
||||
|
||||
result = service.get_activity_summary([task1, task2, task3])
|
||||
|
||||
|
||||
assert result["active_count"] == 2 # RUNNING + WAITING_INPUT
|
||||
assert len(result["recent_tasks"]) == 3
|
||||
|
||||
@@ -153,51 +155,52 @@ def test_get_activity_summary():
|
||||
|
||||
|
||||
# [DEF:test_get_git_status_for_dashboard_no_repo:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: _get_git_status_for_dashboard returns None when no repo exists
|
||||
# @PRE: GitService returns None for repo
|
||||
# @POST: Returns None
|
||||
def test_get_git_status_for_dashboard_no_repo():
|
||||
with patch("src.services.resource_service.GitService") as mock_git:
|
||||
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
|
||||
service = ResourceService()
|
||||
mock_git.return_value.get_repo.return_value = None
|
||||
|
||||
|
||||
result = service._get_git_status_for_dashboard(123)
|
||||
|
||||
|
||||
assert result is not None
|
||||
assert result['sync_status'] == 'NO_REPO'
|
||||
assert result['has_repo'] is False
|
||||
assert result["sync_status"] == "NO_REPO"
|
||||
assert result["has_repo"] is False
|
||||
|
||||
|
||||
# [/DEF:test_get_git_status_for_dashboard_no_repo:Function]
|
||||
|
||||
|
||||
# [DEF:test_get_last_task_for_resource:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: _get_last_task_for_resource returns most recent task for resource
|
||||
# @PRE: tasks list with matching resource_id
|
||||
# @POST: Returns task summary with task_id and status
|
||||
def test_get_last_task_for_resource():
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
|
||||
service = ResourceService()
|
||||
|
||||
|
||||
# Create mock tasks
|
||||
task1 = MagicMock()
|
||||
task1.id = "task-old"
|
||||
task1.status = "SUCCESS"
|
||||
task1.params = {"resource_id": "dashboard-1"}
|
||||
task1.created_at = datetime(2024, 1, 1, 10, 0, 0)
|
||||
|
||||
|
||||
task2 = MagicMock()
|
||||
task2.id = "task-new"
|
||||
task2.status = "RUNNING"
|
||||
task2.params = {"resource_id": "dashboard-1"}
|
||||
task2.created_at = datetime(2024, 1, 1, 12, 0, 0)
|
||||
|
||||
|
||||
result = service._get_last_task_for_resource("dashboard-1", [task1, task2])
|
||||
|
||||
|
||||
assert result is not None
|
||||
assert result["task_id"] == "task-new" # Most recent
|
||||
assert result["status"] == "RUNNING"
|
||||
@@ -207,27 +210,28 @@ def test_get_last_task_for_resource():
|
||||
|
||||
|
||||
# [DEF:test_extract_resource_name_from_task:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: _extract_resource_name_from_task extracts name from params
|
||||
# @PRE: task has resource_name in params
|
||||
# @POST: Returns resource name or fallback
|
||||
def test_extract_resource_name_from_task():
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
|
||||
service = ResourceService()
|
||||
|
||||
|
||||
# Task with resource_name
|
||||
task = MagicMock()
|
||||
task.id = "task-123"
|
||||
task.params = {"resource_name": "My Dashboard"}
|
||||
|
||||
|
||||
result = service._extract_resource_name_from_task(task)
|
||||
assert result == "My Dashboard"
|
||||
|
||||
|
||||
# Task without resource_name
|
||||
task2 = MagicMock()
|
||||
task2.id = "task-456"
|
||||
task2.params = {}
|
||||
|
||||
|
||||
result2 = service._extract_resource_name_from_task(task2)
|
||||
assert "task-456" in result2
|
||||
|
||||
@@ -236,48 +240,56 @@ def test_extract_resource_name_from_task():
|
||||
|
||||
|
||||
# [DEF:test_get_last_task_for_resource_empty_tasks:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: _get_last_task_for_resource returns None for empty tasks list
|
||||
# @PRE: tasks is empty list
|
||||
# @POST: Returns None
|
||||
def test_get_last_task_for_resource_empty_tasks():
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
|
||||
service = ResourceService()
|
||||
|
||||
|
||||
result = service._get_last_task_for_resource("dashboard-1", [])
|
||||
assert result is None
|
||||
|
||||
|
||||
# [/DEF:test_get_last_task_for_resource_empty_tasks:Function]
|
||||
|
||||
|
||||
# [DEF:test_get_last_task_for_resource_no_match:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: _get_last_task_for_resource returns None when no tasks match resource_id
|
||||
# @PRE: tasks list has no matching resource_id
|
||||
# @POST: Returns None
|
||||
def test_get_last_task_for_resource_no_match():
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
|
||||
service = ResourceService()
|
||||
|
||||
|
||||
task = MagicMock()
|
||||
task.id = "task-999"
|
||||
task.status = "SUCCESS"
|
||||
task.params = {"resource_id": "dashboard-99"}
|
||||
task.created_at = datetime(2024, 1, 1, 10, 0, 0)
|
||||
|
||||
|
||||
result = service._get_last_task_for_resource("dashboard-1", [task])
|
||||
assert result is None
|
||||
|
||||
|
||||
# [/DEF:test_get_last_task_for_resource_no_match:Function]
|
||||
|
||||
|
||||
# [DEF:test_get_dashboards_with_status_handles_mixed_naive_and_aware_task_datetimes:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: get_dashboards_with_status handles mixed naive/aware datetimes without comparison errors.
|
||||
# @PRE: Task list includes both timezone-aware and timezone-naive timestamps.
|
||||
# @POST: Latest task is selected deterministically and no exception is raised.
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_dashboards_with_status_handles_mixed_naive_and_aware_task_datetimes():
|
||||
with patch("src.services.resource_service.SupersetClient") as mock_client, \
|
||||
patch("src.services.resource_service.GitService"):
|
||||
|
||||
with (
|
||||
patch("src.services.resource_service.SupersetClient") as mock_client,
|
||||
patch("src.services.resource_service.GitService"),
|
||||
):
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
service = ResourceService()
|
||||
@@ -305,18 +317,22 @@ async def test_get_dashboards_with_status_handles_mixed_naive_and_aware_task_dat
|
||||
result = await service.get_dashboards_with_status(env, [task_naive, task_aware])
|
||||
|
||||
assert result[0]["last_task"]["task_id"] == "task-aware"
|
||||
|
||||
|
||||
# [/DEF:test_get_dashboards_with_status_handles_mixed_naive_and_aware_task_datetimes:Function]
|
||||
|
||||
|
||||
# [DEF:test_get_dashboards_with_status_prefers_latest_decisive_validation_status_over_newer_unknown:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: get_dashboards_with_status keeps latest task identity while falling back to older decisive validation status.
|
||||
# @PRE: Same dashboard has older WARN and newer UNKNOWN validation tasks.
|
||||
# @POST: Returned last_task points to newest task but preserves WARN as last meaningful validation state.
|
||||
@pytest.mark.anyio
|
||||
async def test_get_dashboards_with_status_prefers_latest_decisive_validation_status_over_newer_unknown():
|
||||
with patch("src.services.resource_service.SupersetClient") as mock_client, \
|
||||
patch("src.services.resource_service.GitService"):
|
||||
|
||||
with (
|
||||
patch("src.services.resource_service.SupersetClient") as mock_client,
|
||||
patch("src.services.resource_service.GitService"),
|
||||
):
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
service = ResourceService()
|
||||
@@ -343,23 +359,29 @@ async def test_get_dashboards_with_status_prefers_latest_decisive_validation_sta
|
||||
env = MagicMock()
|
||||
env.id = "prod"
|
||||
|
||||
result = await service.get_dashboards_with_status(env, [task_warn, task_unknown])
|
||||
result = await service.get_dashboards_with_status(
|
||||
env, [task_warn, task_unknown]
|
||||
)
|
||||
|
||||
assert result[0]["last_task"]["task_id"] == "task-unknown"
|
||||
assert result[0]["last_task"]["status"] == "RUNNING"
|
||||
assert result[0]["last_task"]["validation_status"] == "WARN"
|
||||
|
||||
|
||||
# [/DEF:test_get_dashboards_with_status_prefers_latest_decisive_validation_status_over_newer_unknown:Function]
|
||||
|
||||
|
||||
# [DEF:test_get_dashboards_with_status_falls_back_to_latest_unknown_without_decisive_history:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: get_dashboards_with_status still returns newest UNKNOWN when no decisive validation exists.
|
||||
# @PRE: Same dashboard has only UNKNOWN validation tasks.
|
||||
# @POST: Returned last_task keeps newest UNKNOWN task.
|
||||
@pytest.mark.anyio
|
||||
async def test_get_dashboards_with_status_falls_back_to_latest_unknown_without_decisive_history():
|
||||
with patch("src.services.resource_service.SupersetClient") as mock_client, \
|
||||
patch("src.services.resource_service.GitService"):
|
||||
|
||||
with (
|
||||
patch("src.services.resource_service.SupersetClient") as mock_client,
|
||||
patch("src.services.resource_service.GitService"),
|
||||
):
|
||||
from src.services.resource_service import ResourceService
|
||||
|
||||
service = ResourceService()
|
||||
@@ -386,14 +408,19 @@ async def test_get_dashboards_with_status_falls_back_to_latest_unknown_without_d
|
||||
env = MagicMock()
|
||||
env.id = "prod"
|
||||
|
||||
result = await service.get_dashboards_with_status(env, [task_unknown_old, task_unknown_new])
|
||||
result = await service.get_dashboards_with_status(
|
||||
env, [task_unknown_old, task_unknown_new]
|
||||
)
|
||||
|
||||
assert result[0]["last_task"]["task_id"] == "task-unknown-new"
|
||||
assert result[0]["last_task"]["validation_status"] == "UNKNOWN"
|
||||
|
||||
|
||||
# [/DEF:test_get_dashboards_with_status_falls_back_to_latest_unknown_without_decisive_history:Function]
|
||||
|
||||
|
||||
# [DEF:test_get_last_task_for_resource_handles_mixed_naive_and_aware_created_at:Function]
|
||||
# @RELATION: BINDS_TO ->[TestResourceService]
|
||||
# @TEST: _get_last_task_for_resource handles mixed naive/aware created_at values.
|
||||
# @PRE: Matching tasks include naive and aware created_at timestamps.
|
||||
# @POST: Latest task is returned without raising datetime comparison errors.
|
||||
@@ -414,11 +441,15 @@ def test_get_last_task_for_resource_handles_mixed_naive_and_aware_created_at():
|
||||
task_aware.params = {"resource_id": "dashboard-1"}
|
||||
task_aware.created_at = datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
result = service._get_last_task_for_resource("dashboard-1", [task_naive, task_aware])
|
||||
result = service._get_last_task_for_resource(
|
||||
"dashboard-1", [task_naive, task_aware]
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result["task_id"] == "task-new"
|
||||
|
||||
|
||||
# [/DEF:test_get_last_task_for_resource_handles_mixed_naive_and_aware_created_at:Function]
|
||||
|
||||
|
||||
# [/DEF:backend.src.services.__tests__.test_resource_service:Module]
|
||||
# [/DEF:TestResourceService:Module]
|
||||
|
||||
Reference in New Issue
Block a user