fix dashboard validation fallback and semantic relation parsing

This commit is contained in:
2026-03-15 16:32:39 +03:00
parent 6b66f2fb49
commit eba0fab091
4 changed files with 145 additions and 26 deletions

View File

@@ -308,6 +308,91 @@ async def test_get_dashboards_with_status_handles_mixed_naive_and_aware_task_dat
# [/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]
# @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"):
from src.services.resource_service import ResourceService
service = ResourceService()
mock_client.return_value.get_dashboards_summary.return_value = [
{"id": 4, "title": "Dashboard 4", "slug": "deck"}
]
task_warn = MagicMock()
task_warn.id = "task-warn"
task_warn.plugin_id = "llm_dashboard_validation"
task_warn.status = "SUCCESS"
task_warn.params = {"dashboard_id": "4", "environment_id": "prod"}
task_warn.result = {"status": "WARN"}
task_warn.started_at = datetime(2024, 1, 1, 11, 0, 0)
task_unknown = MagicMock()
task_unknown.id = "task-unknown"
task_unknown.plugin_id = "llm_dashboard_validation"
task_unknown.status = "RUNNING"
task_unknown.params = {"dashboard_id": "4", "environment_id": "prod"}
task_unknown.result = {"status": "UNKNOWN"}
task_unknown.started_at = datetime(2024, 1, 1, 12, 0, 0)
env = MagicMock()
env.id = "prod"
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]
# @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"):
from src.services.resource_service import ResourceService
service = ResourceService()
mock_client.return_value.get_dashboards_summary.return_value = [
{"id": 5, "title": "Dashboard 5", "slug": "ops"}
]
task_unknown_old = MagicMock()
task_unknown_old.id = "task-unknown-old"
task_unknown_old.plugin_id = "llm_dashboard_validation"
task_unknown_old.status = "SUCCESS"
task_unknown_old.params = {"dashboard_id": "5", "environment_id": "prod"}
task_unknown_old.result = {"status": "UNKNOWN"}
task_unknown_old.started_at = datetime(2024, 1, 1, 11, 0, 0)
task_unknown_new = MagicMock()
task_unknown_new.id = "task-unknown-new"
task_unknown_new.plugin_id = "llm_dashboard_validation"
task_unknown_new.status = "SUCCESS"
task_unknown_new.params = {"dashboard_id": "5", "environment_id": "prod"}
task_unknown_new.result = {"status": "UNKNOWN"}
task_unknown_new.started_at = datetime(2024, 1, 1, 12, 0, 0)
env = MagicMock()
env.id = "prod"
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]
# @TEST: _get_last_task_for_resource handles mixed naive/aware created_at values.
# @PRE: Matching tasks include naive and aware created_at timestamps.

View File

@@ -189,15 +189,36 @@ class ResourceService:
)
return self._normalize_datetime_for_compare(raw_time)
last_task = max(matched_tasks, key=_task_time)
raw_result = getattr(last_task, "result", None)
validation_status = None
if isinstance(raw_result, dict):
validation_status = self._normalize_validation_status(raw_result.get("status"))
projected_tasks = []
for task in matched_tasks:
raw_result = getattr(task, "result", None)
validation_status = None
if isinstance(raw_result, dict):
validation_status = self._normalize_validation_status(raw_result.get("status"))
projected_tasks.append(
(
task,
validation_status,
_task_time(task),
)
)
projected_tasks.sort(key=lambda item: item[2], reverse=True)
latest_task, latest_validation_status, _ = projected_tasks[0]
decisive_task = next(
(
item for item in projected_tasks
if item[1] in {"PASS", "WARN", "FAIL"}
),
None,
)
validation_status = latest_validation_status
if validation_status == "UNKNOWN" and decisive_task is not None:
validation_status = decisive_task[1]
return {
"task_id": str(getattr(last_task, "id", "")),
"status": self._normalize_task_status(getattr(last_task, "status", "")),
"task_id": str(getattr(latest_task, "id", "")),
"status": self._normalize_task_status(getattr(latest_task, "status", "")),
"validation_status": validation_status,
}
# [/DEF:_get_last_llm_task_for_dashboard:Function]