import inspect import time from types import SimpleNamespace import pytest import app.services.pmflow_ai_turn_runtime_service as runtime_module from app.schemas.rag import RAGAskRequest from app.services.customer_answer_presentation_service import CustomerAnswerPresentationService from app.services.pmflow_ai_turn_runtime_service import PMFlowAITurnRuntimeService def _service() -> PMFlowAITurnRuntimeService: return PMFlowAITurnRuntimeService(object(), tenant_id=1) def _open_judgement_state(): return _service().build_initial_turn_state( request=RAGAskRequest(question="这个方向怎么样"), route_plan={}, turn_hypothesis={ "normalized_goal": "判断方向", "desired_outcome": "基于当前状态给出受控判断", "answer_shape": "strategic_judgement", "can_answer_with_assumptions": True, "goal_frames": [ { "frame_id": "open-direction", "operation_kind": "judge_direction", "target_scope": {"scope_type": "current_space", "binding_strength": "surface"}, "target_object_requirement": "none", "source_policy": "internal", "answer_shape": "strategic_judgement", "confidence": 0.72, } ], "operation_gaps": [], }, ) def test_packet_ap_removes_legacy_answer_sanitizer_from_renderer() -> None: source = inspect.getsource(PMFlowAITurnRuntimeService._execute_answer_renderer_candidate) assert "_sanitize_state_answer" not in source assert not hasattr(runtime_module, "_sanitize_state_answer") @pytest.mark.asyncio async def test_packet_ap_quality_gate_fail_closed_when_revision_still_fails(monkeypatch) -> None: service = _service() state = _open_judgement_state() async def bad_render(*, request, turn_state): _ = request, turn_state return "TurnState evidence_findings 仍然被直接展示。" def bad_fallback(*, question, turn_state): _ = question, turn_state return "PMFlowAITurnState runtime_steps 又泄漏了一次。" monkeypatch.setattr(service, "_render_answer_from_registry", bad_render) monkeypatch.setattr(runtime_module, "_render_state_answer_fallback", bad_fallback) payload = await service._run_state_answer_synthesis( request=RAGAskRequest(question="这个方向怎么样"), current_user=SimpleNamespace(id=1, tenant_id=1), route_plan={}, turn_state=state, presentation_service=CustomerAnswerPresentationService(object(), tenant_id=1), started_at=time.perf_counter(), ) finding_types = {item["finding_type"] for item in payload["answer_runtime_packet"]["quality_findings"]} assert "internal_runtime_leak" in finding_types assert "post_revision_quality_failed" in finding_types assert "TurnState" not in payload["answer"] assert "evidence_findings" not in payload["answer"] assert "PMFlowAITurnState" not in payload["answer"] assert "可直接展示" in payload["answer"] assert any(step.key == "block_answer_quality" for step in state.runtime_steps)