from types import SimpleNamespace import pytest from fastapi import HTTPException from app.api.sstc_api import charters_ai_alignment as charters_ai_alignment_api from app.api.sstc_api import releases_status_generate as releases_status_generate_api @pytest.mark.asyncio async def test_confirm_alignment_blocks_when_charter_readiness_is_not_ready(monkeypatch) -> None: async def _fake_ensure_project(service, project_id): _ = (service, project_id) return SimpleNamespace(id=6) async def _fake_ensure_charter(db, *, tenant_id: int, project_id: int, charter_id: int): _ = (db, tenant_id, project_id, charter_id) return SimpleNamespace( id=55, title="内部知识库升级宪章", status="aligning", content={ "readiness": { "ready_for_confirm": False, "blocking_fields": ["module_1.kpis", "module_2.alignment_qa"], "blocking_reasons": ["关键 KPI 缺少证据", "固定对齐问答未完成"], } }, ) class _FakeResult: def scalar_one_or_none(self): return None def scalars(self): return self def all(self): return [] class _FakeDB: async def execute(self, stmt): _ = stmt return _FakeResult() def add(self, value): _ = value return None async def commit(self): raise AssertionError("readiness blocked charters must not commit confirmation") monkeypatch.setattr(charters_ai_alignment_api, "_ensure_project", _fake_ensure_project) monkeypatch.setattr(charters_ai_alignment_api, "_ensure_charter", _fake_ensure_charter) with pytest.raises(HTTPException) as exc_info: await charters_ai_alignment_api.confirm_alignment( project_id="6", charter_id="55", body=SimpleNamespace(score=90, confirmed=True, comments="已确认对齐"), db=_FakeDB(), current_user=SimpleNamespace(tenant_id=1, id=7), permission_user=SimpleNamespace(), ) assert exc_info.value.status_code == 409 assert "关键 KPI 缺少证据" in str(exc_info.value.detail) assert "module_1.kpis" in str(exc_info.value.detail) @pytest.mark.asyncio async def test_generate_release_verification_uses_charter_verification_metrics(monkeypatch) -> None: async def _fake_ensure_project(service, project_id): _ = (service, project_id) return SimpleNamespace(id=6) async def _fake_ensure_release(db, *, tenant_id: int, project_id: int, release_node_id: int): _ = (db, tenant_id, project_id, release_node_id) return SimpleNamespace( id=88, status="pending", content={"charter_id": 55}, ) async def _fake_ensure_charter(db, *, tenant_id: int, project_id: int, charter_id: int): _ = (db, tenant_id, project_id, charter_id) return SimpleNamespace( id=55, content={ "derived_artifacts": { "verification_metrics": [ {"name": "首响低于 3 秒", "expected": "P95 <= 3s"}, {"name": "重复提问率下降", "expected": "下降 20%+"}, ] }, "success_metrics": ["不应回退到这里"], }, ) class _FakeDB: def __init__(self): self.committed = False async def commit(self): self.committed = True monkeypatch.setattr(releases_status_generate_api, "_ensure_project", _fake_ensure_project) monkeypatch.setattr(releases_status_generate_api, "_ensure_release", _fake_ensure_release) monkeypatch.setattr(releases_status_generate_api, "_ensure_charter", _fake_ensure_charter) db = _FakeDB() response = await releases_status_generate_api.generate_release_verification( project_id="6", release_node_id="88", db=db, current_user=SimpleNamespace(tenant_id=1, id=7, role="REVIEWER"), permission_user=SimpleNamespace(), ) payload = response["data"] assert db.committed is True assert payload["verification_items"][0]["title"] == "首响低于 3 秒" assert payload["verification_items"][0]["expected"] == "P95 <= 3s" assert payload["verification_items"][0]["source"] == "charter_verification_metric" @pytest.mark.asyncio async def test_generate_release_verification_fails_without_charter_metrics(monkeypatch) -> None: async def _fake_ensure_project(service, project_id): _ = (service, project_id) return SimpleNamespace(id=6) async def _fake_ensure_release(db, *, tenant_id: int, project_id: int, release_node_id: int): _ = (db, tenant_id, project_id, release_node_id) return SimpleNamespace( id=88, status="pending", content={"charter_id": 55}, ) async def _fake_ensure_charter(db, *, tenant_id: int, project_id: int, charter_id: int): _ = (db, tenant_id, project_id, charter_id) return SimpleNamespace( id=55, content={ "derived_artifacts": {"verification_metrics": []}, "success_metrics": [], "core_metrics": [], }, ) class _FakeDB: async def commit(self): raise AssertionError("missing metrics must not commit verification items") monkeypatch.setattr(releases_status_generate_api, "_ensure_project", _fake_ensure_project) monkeypatch.setattr(releases_status_generate_api, "_ensure_release", _fake_ensure_release) monkeypatch.setattr(releases_status_generate_api, "_ensure_charter", _fake_ensure_charter) with pytest.raises(HTTPException) as exc_info: await releases_status_generate_api.generate_release_verification( project_id="6", release_node_id="88", db=_FakeDB(), current_user=SimpleNamespace(tenant_id=1, id=7, role="REVIEWER"), permission_user=SimpleNamespace(), ) assert exc_info.value.status_code == 409 assert "verification metrics" in str(exc_info.value.detail).lower()