171 lines
6.2 KiB
Python
171 lines
6.2 KiB
Python
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="ADMIN"),
|
|
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="ADMIN"),
|
|
permission_user=SimpleNamespace(),
|
|
)
|
|
|
|
assert exc_info.value.status_code == 409
|
|
assert "verification metrics" in str(exc_info.value.detail).lower()
|