import json from contextlib import contextmanager from datetime import datetime from unittest.mock import MagicMock, patch import pytest from openhands.server.routes.manage_conversations import ( delete_conversation, get_conversation, search_conversations, update_conversation, ) from openhands.storage.data_models.conversation_info import ConversationInfo from openhands.storage.data_models.conversation_info_result_set import ( ConversationInfoResultSet, ) from openhands.storage.data_models.conversation_status import ConversationStatus from openhands.storage.memory import InMemoryFileStore @contextmanager def _patch_store(): file_store = InMemoryFileStore() file_store.write( 'sessions/some_conversation_id/metadata.json', json.dumps( { 'title': 'Some Conversation', 'selected_repository': 'foobar', 'conversation_id': 'some_conversation_id', 'github_user_id': '12345', 'created_at': '2025-01-01T00:00:00', 'last_updated_at': '2025-01-01T00:01:00', } ), ) with patch( 'openhands.storage.conversation.file_conversation_store.get_file_store', MagicMock(return_value=file_store), ): with patch( 'openhands.server.routes.manage_conversations.session_manager.file_store', file_store, ): yield @pytest.mark.asyncio async def test_search_conversations(): with _patch_store(): result_set = await search_conversations( MagicMock(state=MagicMock(github_token='')) ) expected = ConversationInfoResultSet( results=[ ConversationInfo( conversation_id='some_conversation_id', title='Some Conversation', created_at=datetime.fromisoformat('2025-01-01T00:00:00'), last_updated_at=datetime.fromisoformat('2025-01-01T00:01:00'), status=ConversationStatus.STOPPED, selected_repository='foobar', ) ] ) assert result_set == expected @pytest.mark.asyncio async def test_get_conversation(): with _patch_store(): conversation = await get_conversation( 'some_conversation_id', MagicMock(state=MagicMock(github_token='')) ) expected = ConversationInfo( conversation_id='some_conversation_id', title='Some Conversation', created_at=datetime.fromisoformat('2025-01-01T00:00:00'), last_updated_at=datetime.fromisoformat('2025-01-01T00:01:00'), status=ConversationStatus.STOPPED, selected_repository='foobar', ) assert conversation == expected @pytest.mark.asyncio async def test_get_missing_conversation(): with _patch_store(): assert ( await get_conversation( 'no_such_conversation', MagicMock(state=MagicMock(github_token='')) ) is None ) @pytest.mark.asyncio async def test_update_conversation(): with _patch_store(): await update_conversation( MagicMock(state=MagicMock(github_token='')), 'some_conversation_id', 'New Title', ) conversation = await get_conversation( 'some_conversation_id', MagicMock(state=MagicMock(github_token='')) ) expected = ConversationInfo( conversation_id='some_conversation_id', title='New Title', created_at=datetime.fromisoformat('2025-01-01T00:00:00'), last_updated_at=datetime.fromisoformat('2025-01-01T00:01:00'), status=ConversationStatus.STOPPED, selected_repository='foobar', ) assert conversation == expected @pytest.mark.asyncio async def test_delete_conversation(): with _patch_store(): await delete_conversation( 'some_conversation_id', MagicMock(state=MagicMock(github_token='')), ) conversation = await get_conversation( 'some_conversation_id', MagicMock(state=MagicMock(github_token='')) ) assert conversation is None