fix(api): tolerate legacy incident decision chains
All checks were successful
Code Review / ai-code-review (push) Successful in 10s
CD Pipeline / tests (push) Successful in 1m23s
CD Pipeline / build-and-deploy (push) Successful in 3m37s
CD Pipeline / post-deploy-checks (push) Successful in 1m34s

This commit is contained in:
Your Name
2026-05-19 11:55:42 +08:00
parent ab2862a214
commit 6da0c3969b
2 changed files with 70 additions and 6 deletions

View File

@@ -12,13 +12,18 @@ test_incident_service_resolve_idempotency
重新放大「resolve_incident 重複觸發 postmortem 洗版」的舊風險。
"""
from datetime import UTC, datetime
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from src.models.incident import IncidentStatus
from src.services.incident_service import IncidentService, normalize_status
from src.services.incident_service import (
IncidentService,
normalize_status,
parse_decision_chain,
)
@pytest.mark.asyncio
@@ -95,3 +100,37 @@ def test_normalize_status_accepts_db_enum_name() -> None:
"""PostgreSQL SQLEnum 會存 Enum name讀回時必須正規化成 Pydantic value。"""
assert normalize_status("INVESTIGATING") == "investigating"
assert normalize_status(IncidentStatus.CLOSED) == "closed"
def test_parse_decision_chain_skips_legacy_list_payload() -> None:
"""舊資料 decision_chain 可能是 list不應阻斷 incident hydrate / resolve。"""
assert parse_decision_chain([{"stage": "router"}], "INC-LEGACY-CHAIN") is None
def test_record_to_incident_tolerates_legacy_decision_chain_list() -> None:
"""DB fallback 必須能讀回舊 incident即使 decision_chain 不是新 schema。"""
now = datetime.now(UTC)
record = SimpleNamespace(
incident_id="INC-LEGACY-CHAIN",
status="INVESTIGATING",
severity="P2",
signals=[],
affected_services=[],
decision_chain=[{"stage": "router"}],
proposal_ids=[],
outcome=None,
created_at=now,
updated_at=now,
resolved_at=None,
closed_at=None,
ttl_days=7,
vectorized=False,
notification_type="TYPE-3",
alert_category="host_resource",
)
incident = IncidentService()._record_to_incident(record)
assert incident.incident_id == "INC-LEGACY-CHAIN"
assert incident.status == IncidentStatus.INVESTIGATING
assert incident.decision_chain is None