js - ts + fix
This commit is contained in:
154
backend/tests/services/test_run_to_dict.py
Normal file
154
backend/tests/services/test_run_to_dict.py
Normal file
@@ -0,0 +1,154 @@
|
||||
# #region TestRunToDict [C:3] [TYPE Module] [SEMANTICS test,validation,run,dict,duration]
|
||||
# @BRIEF Verify ValidationTaskService._run_to_dict computes duration_ms correctly,
|
||||
# including timezone-naive datetime handling and edge cases.
|
||||
# @RELATION BINDS_TO -> [ValidationTaskService]
|
||||
# @TEST_EDGE: no_finished_at -> duration_ms is None
|
||||
# @TEST_EDGE: no_started_at -> duration_ms is None
|
||||
# @TEST_EDGE: naive_datetime -> Handled correctly (treated as UTC)
|
||||
# @TEST_EDGE: aware_datetime -> Handled correctly
|
||||
# @TEST_INVARIANT: duration_ms_computed -> VERIFIED_BY: test_duration_ms_with_aware_datetimes, test_duration_ms_with_naive_datetimes
|
||||
|
||||
import sys
|
||||
from datetime import UTC, datetime, timezone, timedelta
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
|
||||
|
||||
|
||||
class MockRun:
|
||||
"""Minimal mock matching ValidationRun ORM attributes."""
|
||||
def __init__(self, **kwargs):
|
||||
self.id = kwargs.get("id", "run-1")
|
||||
self.policy_id = kwargs.get("policy_id", "policy-1")
|
||||
self.task_id = kwargs.get("task_id", "task-1")
|
||||
self.status = kwargs.get("status", "completed")
|
||||
self.trigger = kwargs.get("trigger", "manual")
|
||||
self.started_at = kwargs.get("started_at")
|
||||
self.finished_at = kwargs.get("finished_at")
|
||||
self.dashboard_count = kwargs.get("dashboard_count", 3)
|
||||
self.pass_count = kwargs.get("pass_count", 2)
|
||||
self.warn_count = kwargs.get("warn_count", 1)
|
||||
self.fail_count = kwargs.get("fail_count", 0)
|
||||
self.unknown_count = kwargs.get("unknown_count", 0)
|
||||
self.created_at = kwargs.get("created_at", datetime(2025, 1, 15, tzinfo=UTC))
|
||||
|
||||
|
||||
# #region TestRunToDictDuration [C:2] [TYPE Class]
|
||||
# @BRIEF Verify _run_to_dict duration_ms computation.
|
||||
class TestRunToDictDuration:
|
||||
"""Verify _run_to_dict duration_ms field."""
|
||||
|
||||
# #region setup [C:1] [TYPE Function]
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_service(self):
|
||||
from src.services.validation_service import ValidationTaskService
|
||||
self.mock_db = MagicMock()
|
||||
self.service = ValidationTaskService(db=self.mock_db)
|
||||
self.run_to_dict = self.service._run_to_dict
|
||||
# #endregion setup
|
||||
|
||||
# #region test_duration_ms_with_aware_datetimes [C:2] [TYPE Function]
|
||||
# @BRIEF UTC-aware started_at/finished_at → correct duration_ms.
|
||||
def test_duration_ms_with_aware_datetimes(self):
|
||||
started = datetime(2025, 1, 15, 10, 0, 0, tzinfo=UTC)
|
||||
finished = datetime(2025, 1, 15, 10, 0, 5, tzinfo=UTC) # 5 seconds
|
||||
run = MockRun(started_at=started, finished_at=finished)
|
||||
|
||||
result = self.run_to_dict(run)
|
||||
|
||||
assert result["duration_ms"] == 5000
|
||||
# #endregion test_duration_ms_with_aware_datetimes
|
||||
|
||||
# #region test_duration_ms_with_naive_datetimes [C:2] [TYPE Function]
|
||||
# @BRIEF Naive (no timezone) datetimes → treated as UTC, correct duration.
|
||||
def test_duration_ms_with_naive_datetimes(self):
|
||||
started = datetime(2025, 1, 15, 10, 0, 0) # naive
|
||||
finished = datetime(2025, 1, 15, 10, 2, 30) # naive, 2.5 minutes
|
||||
run = MockRun(started_at=started, finished_at=finished)
|
||||
|
||||
result = self.run_to_dict(run)
|
||||
|
||||
assert result["duration_ms"] == 150000 # 2.5 * 60 * 1000
|
||||
# #endregion test_duration_ms_with_naive_datetimes
|
||||
|
||||
# #region test_duration_ms_no_finished_at [C:2] [TYPE Function]
|
||||
# @BRIEF When finished_at is None, duration_ms is None.
|
||||
def test_duration_ms_no_finished_at(self):
|
||||
started = datetime(2025, 1, 15, 10, 0, 0, tzinfo=UTC)
|
||||
run = MockRun(started_at=started, finished_at=None)
|
||||
|
||||
result = self.run_to_dict(run)
|
||||
|
||||
assert result["duration_ms"] is None
|
||||
# #endregion test_duration_ms_no_finished_at
|
||||
|
||||
# #region test_duration_ms_no_started_at [C:2] [TYPE Function]
|
||||
# @BRIEF When started_at is None, duration_ms is None.
|
||||
def test_duration_ms_no_started_at(self):
|
||||
finished = datetime(2025, 1, 15, 10, 0, 0, tzinfo=UTC)
|
||||
run = MockRun(started_at=None, finished_at=finished)
|
||||
|
||||
result = self.run_to_dict(run)
|
||||
|
||||
assert result["duration_ms"] is None
|
||||
# #endregion test_duration_ms_no_started_at
|
||||
|
||||
# #region test_duration_ms_both_none [C:2] [TYPE Function]
|
||||
# @BRIEF When both are None, duration_ms is None.
|
||||
def test_duration_ms_both_none(self):
|
||||
run = MockRun(started_at=None, finished_at=None)
|
||||
|
||||
result = self.run_to_dict(run)
|
||||
|
||||
assert result["duration_ms"] is None
|
||||
# #endregion test_duration_ms_both_none
|
||||
|
||||
# #region test_duration_ms_mixed_aware_naive [C:2] [TYPE Function]
|
||||
# @BRIEF One aware, one naive → still computes correctly (both treated as UTC).
|
||||
def test_duration_ms_mixed_aware_naive(self):
|
||||
started = datetime(2025, 1, 15, 10, 0, 0) # naive
|
||||
finished = datetime(2025, 1, 15, 10, 0, 10, tzinfo=UTC) # aware
|
||||
run = MockRun(started_at=started, finished_at=finished)
|
||||
|
||||
result = self.run_to_dict(run)
|
||||
|
||||
assert result["duration_ms"] == 10000
|
||||
# #endregion test_duration_ms_mixed_aware_naive
|
||||
|
||||
# #region test_other_fields_present [C:2] [TYPE Function]
|
||||
# @BRIEF _run_to_dict returns all expected fields.
|
||||
def test_other_fields_present(self):
|
||||
run = MockRun()
|
||||
result = self.run_to_dict(run)
|
||||
|
||||
assert result["id"] == "run-1"
|
||||
assert result["policy_id"] == "policy-1"
|
||||
assert result["task_id"] == "task-1"
|
||||
assert result["status"] == "completed"
|
||||
assert result["trigger"] == "manual"
|
||||
assert result["dashboard_count"] == 3
|
||||
assert result["pass_count"] == 2
|
||||
assert result["warn_count"] == 1
|
||||
assert result["fail_count"] == 0
|
||||
assert result["unknown_count"] == 0
|
||||
# #endregion test_other_fields_present
|
||||
|
||||
# #region test_isoformat_on_dates [C:2] [TYPE Function]
|
||||
# @BRIEF started_at/finished_at are ISO-formatted strings in output.
|
||||
def test_isoformat_on_dates(self):
|
||||
started = datetime(2025, 1, 15, 10, 0, 0, tzinfo=UTC)
|
||||
run = MockRun(started_at=started, finished_at=started)
|
||||
|
||||
result = self.run_to_dict(run)
|
||||
|
||||
assert isinstance(result["started_at"], str)
|
||||
assert isinstance(result["finished_at"], str)
|
||||
assert "2025-01-15" in result["started_at"]
|
||||
# #endregion test_isoformat_on_dates
|
||||
# #endregion TestRunToDictDuration
|
||||
|
||||
|
||||
# #endregion TestRunToDict
|
||||
Reference in New Issue
Block a user