presenton/servers/fastapi/tests/test_chat_conversation_store.py
sudipnext 4c271170b5 feat: Add chat history management and asset generation features
- Introduced a new `ChatHistoryMessageModel` to persist chat messages in the database.
- Implemented a migration script to create the `chat_history_messages` table.
- Enhanced chat services to support storing and retrieving chat history.
- Added functionality for generating multiple media assets in a single API call.
- Updated the chat UI to display assistant activities and tool usage more effectively.
- Refactored API calls to use absolute URLs for better reliability.
2026-04-26 13:12:50 +05:45

137 lines
5 KiB
Python

import asyncio
import uuid
from unittest.mock import AsyncMock, MagicMock, patch
from services.chat.conversation_store import ChatConversationStore
class TestChatConversationStore:
def test_load_history_reads_sql_first(self):
presentation_id = uuid.uuid4()
conversation_id = uuid.uuid4()
expected_history = [
{"role": "user", "content": "Hi"},
{"role": "assistant", "content": "Hello"},
]
sql_session = MagicMock()
with patch(
"services.chat.conversation_store.sql_chat_history.load_messages",
new=AsyncMock(return_value=expected_history),
) as load_sql, patch(
"services.chat.conversation_store.CHAT_MEMORY_STORE.load_history",
new=AsyncMock(),
) as load_mem0:
store = ChatConversationStore(sql_session)
history = asyncio.run(
store.load_history(
presentation_id=presentation_id,
conversation_id=conversation_id,
)
)
load_sql.assert_awaited_once_with(
sql_session,
presentation_id=presentation_id,
conversation_id=conversation_id,
)
load_mem0.assert_not_called()
assert history == expected_history
def test_load_history_falls_back_to_mem0_and_backfills_sql(self):
presentation_id = uuid.uuid4()
conversation_id = uuid.uuid4()
legacy = [
{"role": "user", "content": "old"},
{"role": "assistant", "content": "from mem0"},
]
sql_session = MagicMock()
with patch(
"services.chat.conversation_store.sql_chat_history.load_messages",
new=AsyncMock(return_value=[]),
) as load_sql, patch(
"services.chat.conversation_store.CHAT_MEMORY_STORE.load_history",
new=AsyncMock(return_value=legacy),
) as load_mem0, patch(
"services.chat.conversation_store.sql_chat_history.replace_messages",
new=AsyncMock(),
) as replace_messages:
store = ChatConversationStore(sql_session)
history = asyncio.run(
store.load_history(
presentation_id=presentation_id,
conversation_id=conversation_id,
)
)
load_sql.assert_awaited_once()
load_mem0.assert_awaited_once()
replace_messages.assert_awaited_once_with(
sql_session,
presentation_id=presentation_id,
conversation_id=conversation_id,
messages=legacy,
)
assert history == legacy
def test_append_turn_persists_sql_and_mem0(self):
sql_session = MagicMock()
store = ChatConversationStore(sql_session)
presentation_id = uuid.uuid4()
conversation_id = uuid.uuid4()
with patch(
"services.chat.conversation_store.sql_chat_history.append_turn",
new=AsyncMock(),
) as append_sql, patch(
"services.chat.conversation_store.CHAT_MEMORY_STORE.store_chat_turn",
new=AsyncMock(),
) as store_mem0:
asyncio.run(
store.append_turn(
presentation_id=presentation_id,
conversation_id=conversation_id,
user_message="Can you improve slide 2?",
assistant_message="Yes, I will tighten the bullet points.",
)
)
append_sql.assert_awaited_once_with(
sql_session,
presentation_id=presentation_id,
conversation_id=conversation_id,
user_message="Can you improve slide 2?",
assistant_message="Yes, I will tighten the bullet points.",
tool_calls=None,
)
store_mem0.assert_awaited_once_with(
presentation_id=presentation_id,
conversation_id=conversation_id,
user_message="Can you improve slide 2?",
assistant_message="Yes, I will tighten the bullet points.",
)
def test_retrieve_semantic_context_delegates_to_chat_memory_store(self):
store = ChatConversationStore(MagicMock())
presentation_id = uuid.uuid4()
conversation_id = uuid.uuid4()
with patch(
"services.chat.conversation_store.CHAT_MEMORY_STORE.retrieve_context",
new=AsyncMock(return_value="conversation-scoped context"),
) as retrieve_context:
context = asyncio.run(
store.retrieve_semantic_context(
presentation_id=presentation_id,
conversation_id=conversation_id,
query="What did we decide?",
)
)
retrieve_context.assert_awaited_once_with(
presentation_id=presentation_id,
conversation_id=conversation_id,
query="What did we decide?",
)
assert context == "conversation-scoped context"