js - ts + fix

This commit is contained in:
2026-06-01 14:40:17 +03:00
parent 1a7f368324
commit b4b0deb856
19 changed files with 1337 additions and 473 deletions

View File

@@ -0,0 +1,256 @@
# #region TestUpdateRunStatus [C:3] [TYPE Module] [SEMANTICS test,validation,run,status,counters]
# @BRIEF Verify _update_run_status computes counters correctly, sets status to 'completed',
# and handles edge cases (missing run, partial records, exception handling).
# @RELATION BINDS_TO -> [LLMAnalysisPlugin]
# @TEST_EDGE: missing_run_id -> Returns immediately without DB query
# @TEST_EDGE: no_records_found -> Logs warning and returns
# @TEST_EDGE: partial_records -> Does NOT mark complete when records < dashboard_count
# @TEST_EDGE: exception_in_query -> Logs error, does not crash
# @TEST_INVARIANT: run_status_always_completed -> VERIFIED_BY: test_sets_status_completed_with_all_records
# @TEST_INVARIANT: counters_computed_from_records -> VERIFIED_BY: test_computed_counters_mixed_statuses, test_computed_counters_all_pass
# @TEST_INVARIANT: finished_at_set_on_completion -> VERIFIED_BY: test_sets_finished_at_utc
import sys
from datetime import UTC, datetime, timezone
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
# Ensure src is importable (same pattern as test_llm_dashboard_validation_v2.py)
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
from src.plugins.llm_analysis.plugin import _update_run_status
# Minimal mock models matching the ORM shape
class MockValidationRecord:
def __init__(self, status, run_id="run-1"):
self.status = status
self.run_id = run_id
class MockValidationRun:
def __init__(self, id="run-1", dashboard_count=3, status="running"):
self.id = id
self.dashboard_count = dashboard_count
self.status = status
self.finished_at = None
self.pass_count = 0
self.warn_count = 0
self.fail_count = 0
self.unknown_count = 0
def _make_mock_db(records=None, run=None):
"""Create a mock DB session that returns the given records and run."""
mock_db = MagicMock()
mock_query = MagicMock()
mock_db.query.return_value = mock_query
mock_query.filter.return_value = mock_query
mock_query.all.return_value = records or []
mock_query.first.return_value = run
return mock_db
# #region TestUpdateRunStatusNoop [C:2] [TYPE Class]
# @BRIEF Verify _update_run_status early-exit conditions.
class TestUpdateRunStatusNoop:
"""Verify _update_run_status no-op paths."""
# #region test_none_run_id_returns_early [C:2] [TYPE Function]
# @BRIEF When run_id is None, function returns without DB access.
def test_none_run_id_returns_early(self):
mock_db = MagicMock()
_update_run_status(mock_db, None)
mock_db.query.assert_not_called()
# #endregion test_none_run_id_returns_early
# #region test_empty_run_id_returns_early [C:2] [TYPE Function]
# @BRIEF When run_id is empty string, function returns without DB access.
def test_empty_run_id_returns_early(self):
mock_db = MagicMock()
_update_run_status(mock_db, "")
mock_db.query.assert_not_called()
# #endregion test_empty_run_id_returns_early
# #region test_no_records_logs_warning [C:2] [TYPE Function]
# @BRIEF When no records exist for the run, logs warning and returns.
def test_no_records_logs_warning(self):
mock_db = _make_mock_db(records=[], run=MockValidationRun())
_update_run_status(mock_db, "run-1")
mock_db.commit.assert_not_called()
# #endregion test_no_records_logs_warning
# #region test_no_run_found_logs_warning [C:2] [TYPE Function]
# @BRIEF When run record not found, logs warning and returns.
def test_no_run_found_logs_warning(self):
mock_db = _make_mock_db(records=[MockValidationRecord("PASS")], run=None)
_update_run_status(mock_db, "run-1")
mock_db.commit.assert_not_called()
# #endregion test_no_run_found_logs_warning
# #endregion TestUpdateRunStatusNoop
# #region TestUpdateRunStatusPartial [C:2] [TYPE Class]
# @BRIEF Verify _update_run_status does NOT mark complete when records < dashboard_count.
class TestUpdateRunStatusPartial:
"""Verify partial completion guard."""
# #region test_partial_records_not_marked_complete [C:2] [TYPE Function]
# @BRIEF When len(records) < dashboard_count, run stays 'running'.
def test_partial_records_not_marked_complete(self):
run = MockValidationRun(dashboard_count=3, status="running")
records = [MockValidationRecord("PASS"), MockValidationRecord("WARN")] # 2 < 3
mock_db = _make_mock_db(records=records, run=run)
_update_run_status(mock_db, "run-1")
assert run.status == "running" # NOT changed
mock_db.commit.assert_not_called()
# #endregion test_partial_records_not_marked_complete
# #region test_zero_dashboard_count_skips_guard [C:2] [TYPE Function]
# @BRIEF When dashboard_count is 0 or None, the partial guard is skipped and run completes.
def test_zero_dashboard_count_skips_guard(self):
run = MockValidationRun(dashboard_count=0, status="running")
records = [MockValidationRecord("PASS")]
mock_db = _make_mock_db(records=records, run=run)
_update_run_status(mock_db, "run-1")
assert run.status == "completed"
mock_db.commit.assert_called_once()
# #endregion test_zero_dashboard_count_skips_guard
# #endregion TestUpdateRunStatusPartial
# #region TestUpdateRunStatusCompletion [C:2] [TYPE Class]
# @BRIEF Verify _update_run_status correctly computes counters and sets status.
class TestUpdateRunStatusCompletion:
"""Verify counter computation and status assignment."""
# #region test_sets_status_completed_with_all_records [C:2] [TYPE Function]
# @BRIEF When all expected records exist, run.status is set to 'completed'.
def test_sets_status_completed_with_all_records(self):
run = MockValidationRun(dashboard_count=2, status="running")
records = [MockValidationRecord("PASS"), MockValidationRecord("FAIL")]
mock_db = _make_mock_db(records=records, run=run)
_update_run_status(mock_db, "run-1")
assert run.status == "completed"
# #endregion test_sets_status_completed_with_all_records
# #region test_computed_counters_all_pass [C:2] [TYPE Function]
# @BRIEF All PASS records → pass_count=2, others=0.
def test_computed_counters_all_pass(self):
run = MockValidationRun(dashboard_count=2)
records = [MockValidationRecord("PASS"), MockValidationRecord("PASS")]
mock_db = _make_mock_db(records=records, run=run)
_update_run_status(mock_db, "run-1")
assert run.pass_count == 2
assert run.warn_count == 0
assert run.fail_count == 0
assert run.unknown_count == 0
# #endregion test_computed_counters_all_pass
# #region test_computed_counters_mixed_statuses [C:2] [TYPE Function]
# @BRIEF Mixed statuses → correct per-status counters.
def test_computed_counters_mixed_statuses(self):
run = MockValidationRun(dashboard_count=4)
records = [
MockValidationRecord("PASS"),
MockValidationRecord("PASS"),
MockValidationRecord("WARN"),
MockValidationRecord("FAIL"),
]
mock_db = _make_mock_db(records=records, run=run)
_update_run_status(mock_db, "run-1")
assert run.pass_count == 2
assert run.warn_count == 1
assert run.fail_count == 1
assert run.unknown_count == 0
# #endregion test_computed_counters_mixed_statuses
# #region test_computed_counters_unknown_status [C:2] [TYPE Function]
# @BRIEF UNKNOWN status records are counted correctly.
def test_computed_counters_unknown_status(self):
run = MockValidationRun(dashboard_count=2)
records = [MockValidationRecord("UNKNOWN"), MockValidationRecord("PASS")]
mock_db = _make_mock_db(records=records, run=run)
_update_run_status(mock_db, "run-1")
assert run.unknown_count == 1
assert run.pass_count == 1
# #endregion test_computed_counters_unknown_status
# #region test_sets_finished_at [C:2] [TYPE Function]
# @BRIEF finished_at is set to a datetime on completion.
def test_sets_finished_at(self):
run = MockValidationRun(dashboard_count=1, status="running")
records = [MockValidationRecord("PASS")]
mock_db = _make_mock_db(records=records, run=run)
_update_run_status(mock_db, "run-1")
assert run.finished_at is not None
assert isinstance(run.finished_at, datetime)
# #endregion test_sets_finished_at
# #region test_db_exception_logged_not_raised [C:2] [TYPE Function]
# @BRIEF DB exception during finalization is logged but not re-raised.
def test_db_exception_logged_not_raised(self):
mock_db = MagicMock()
mock_query = MagicMock()
mock_db.query.return_value = mock_query
mock_query.filter.return_value = mock_query
mock_query.all.side_effect = RuntimeError("DB connection lost")
# Should NOT raise
_update_run_status(mock_db, "run-1")
# #endregion test_db_exception_logged_not_raised
# #endregion TestUpdateRunStatusCompletion
# #region TestExecuteErrorHandling [C:2] [TYPE Class]
# @BRIEF Verify the always-finalize pattern used in execute().
class TestExecuteErrorHandling:
"""Verify _update_run_status is always callable after the loop."""
# #region test_update_run_status_called_on_success [C:2] [TYPE Function]
# @BRIEF _update_run_status is called after normal execution with all records.
def test_update_run_status_called_on_success(self):
run = MockValidationRun(dashboard_count=1, status="running")
records = [MockValidationRecord("PASS")]
mock_db = _make_mock_db(records=records, run=run)
_update_run_status(mock_db, "run-1")
mock_db.commit.assert_called_once()
assert run.status == "completed"
# #endregion test_update_run_status_called_on_success
# #region test_update_run_status_handles_zero_records [C:2] [TYPE Function]
# @BRIEF When all dashboards failed (0 records), _update_run_status still runs
# and does not mark the run as complete (partial guard).
def test_update_run_status_handles_zero_records(self):
run = MockValidationRun(dashboard_count=2, status="running")
mock_db = _make_mock_db(records=[], run=run)
_update_run_status(mock_db, "run-1")
# With 0 records and dashboard_count=2, the partial guard prevents completion
assert run.status == "running"
mock_db.commit.assert_not_called()
# #endregion test_update_run_status_handles_zero_records
# #endregion TestExecuteErrorHandling
# #endregion TestUpdateRunStatus