"""replay/conversation 响应携带服务端 elapsed 字段。""" from __future__ import annotations from datetime import datetime, timezone import pytest from httpx import ASGITransport, AsyncClient from app.features.evaluation.internal_auth import get_internal_eval_principal from app.features.evaluation.replay_service import ReplayServerTiming @pytest.mark.asyncio async def test_replay_conversation_includes_server_elapsed_ms( monkeypatch: pytest.MonkeyPatch, ) -> None: from fastapi import FastAPI monkeypatch.setattr( "app.core.config.settings.internal_eval_api_key", "secret", raising=False, ) from app.features.evaluation.deps import get_replay_conversation_service from app.features.evaluation.router import router t0 = datetime(2026, 4, 9, 10, 0, 0, tzinfo=timezone.utc) t1 = datetime(2026, 4, 9, 10, 0, 1, tzinfo=timezone.utc) class _FakeReplay: async def replay_utterances(self, **kwargs): return ( 1, ["seg-a"], ReplayServerTiming( started_at_utc=t0, finished_at_utc=t1, elapsed_ms=150, ), ) app = FastAPI() app.include_router(router, prefix="/internal/api/evaluation") async def _override_auth(): from app.features.evaluation.internal_auth import InternalEvalPrincipal return InternalEvalPrincipal() app.dependency_overrides[get_internal_eval_principal] = _override_auth app.dependency_overrides[get_replay_conversation_service] = lambda: _FakeReplay() transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://t") as client: r = await client.post( "/internal/api/evaluation/replay/conversation", headers={"X-Internal-Eval-Key": "secret"}, json={ "conversation_id": "00000000-0000-0000-0000-000000000099", "user_utterances": ["hi"], "flush_memoir_after": False, "skip_memoir": True, "skip_tts": True, }, ) assert r.status_code == 200 body = r.json() assert body["elapsed_ms"] == 150 assert body["started_at_utc"] is not None assert body["finished_at_utc"] is not None