"""对话落库返回人/助 message id,供 segment lineage 配对。""" from __future__ import annotations from contextlib import asynccontextmanager from unittest.mock import AsyncMock, MagicMock, patch import pytest from sqlalchemy.ext.asyncio import AsyncSession from app.features.conversation.history_store import HumanAiTurnIds @asynccontextmanager async def _capture_transactional(db): yield db await db.commit() @pytest.mark.asyncio async def test_record_human_ai_turn_returns_both_message_ids() -> None: conv_id = "conv-1" captured: list[object] = [] class FakeMsg: def __init__(self, **kwargs) -> None: for k, v in kwargs.items(): setattr(self, k, v) class _FakeRepo: @staticmethod def add_conversation_message(msg: object, db) -> None: captured.append(msg) db = MagicMock(spec=AsyncSession) db.commit = AsyncMock() with patch( "app.features.conversation.history_store.transactional", _capture_transactional, ), patch( "app.features.conversation.history_store.ConversationMessage", FakeMsg, ), patch( "app.features.conversation.history_store.repo", _FakeRepo, ): from app.features.conversation import history_store as hs store = hs.ConversationHistoryStore(db) store._sync_redis_best_effort = AsyncMock() # type: ignore[method-assign] store._touch_conversation = AsyncMock() # type: ignore[method-assign] out = await store.record_human_ai_turn( conv_id, "hello", ["reply a", "reply b"], user_message_timestamp=None, is_from_voice=False, voice_session_id=None, audio_duration_seconds=None, tts_audio_urls=None, segment_id="seg-1", memory_retrieval_trace=None, ) assert isinstance(out, HumanAiTurnIds) assert len(captured) == 2 assert captured[0].role == "human" assert captured[1].role == "ai" assert captured[0].segment_id == "seg-1" assert out.human_message_id == captured[0].id assert out.assistant_message_id == captured[1].id db.commit.assert_awaited_once()