diff --git a/api/tests/unit_tests/controllers/service_api/app/test_conversation.py b/api/tests/unit_tests/controllers/service_api/app/test_conversation.py index 3197812bc31..09f0d430eff 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_conversation.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_conversation.py @@ -22,6 +22,8 @@ from unittest.mock import Mock, patch import pytest from flask import Flask +from sqlalchemy.engine import Engine +from sqlalchemy.orm import Session from werkzeug.exceptions import BadRequest, NotFound import services @@ -39,19 +41,45 @@ from controllers.service_api.app.conversation import ( ConversationVariableUpdatePayload, ) from controllers.service_api.app.error import NotChatAppError +from core.app.entities.app_invoke_entities import InvokeFrom from fields._value_type_serializer import serialize_value_type from graphon.variables import StringSegment from graphon.variables.types import SegmentType -from models.model import App, AppMode, EndUser +from models.enums import ConversationFromSource +from models.model import App, AppMode, Conversation, EndUser from services.conversation_service import ConversationService from services.errors.conversation import ( ConversationNotExistsError, ConversationVariableNotExistsError, ConversationVariableTypeMismatchError, - LastConversationNotExistsError, ) +def _end_user(user_id: str = "end-user-1") -> EndUser: + end_user = EndUser() + end_user.id = user_id + return end_user + + +def _conversation( + *, + conversation_id: str, + app_id: str = "app-1", + end_user_id: str = "end-user-1", +) -> Conversation: + conversation = Conversation( + app_id=app_id, + mode=AppMode.CHAT, + name="Original Name", + from_source=ConversationFromSource.API, + from_end_user_id=end_user_id, + invoke_from=InvokeFrom.SERVICE_API, + ) + conversation.id = conversation_id + conversation.inputs = {} + return conversation + + class TestConversationListQuery: """Test suite for ConversationListQuery Pydantic model.""" @@ -462,23 +490,30 @@ class TestConversationService: assert hasattr(result, "limit") assert hasattr(result, "has_more") - @patch.object(ConversationService, "rename") - def test_rename_returns_conversation(self, mock_rename): + @pytest.mark.parametrize("sqlite_session", [(Conversation,)], indirect=True) + def test_rename_returns_conversation(self, sqlite_session: Session): """Test rename returns updated conversation.""" - mock_conversation = Mock() - mock_conversation.name = "New Name" - mock_rename.return_value = mock_conversation + conversation_id = "00000000-0000-0000-0000-000000000001" + conversation = _conversation(conversation_id=conversation_id) + sqlite_session.add(conversation) + sqlite_session.commit() + + app_model = App() + app_model.id = "app-1" + end_user = _end_user() result = ConversationService.rename( - app_model=Mock(spec=App), - conversation_id="conv_123", - user=Mock(spec=EndUser), + app_model=app_model, + conversation_id=conversation_id, + user=end_user, name="New Name", auto_generate=False, - session=Mock(), + session=sqlite_session, ) assert result.name == "New Name" + sqlite_session.refresh(conversation) + assert conversation.name == "New Name" class TestConversationPayloadsController: @@ -502,37 +537,29 @@ class TestConversationApiController: with pytest.raises(NotChatAppError): handler(api, app_model=app_model, end_user=end_user) - def test_list_last_not_found(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: - class _BeginStub: - def __enter__(self): - return SimpleNamespace() + @pytest.mark.parametrize("sqlite_session", [(Conversation,)], indirect=True) + def test_list_last_not_found( + self, + app: Flask, + monkeypatch: pytest.MonkeyPatch, + sqlite_engine: Engine, + sqlite_session: Session, + ) -> None: + last_id = "00000000-0000-0000-0000-000000000001" + # The id exists for a different app, proving pagination cannot cross app boundaries. + sqlite_session.add(_conversation(conversation_id=last_id, app_id="other-app")) + sqlite_session.commit() - def __exit__(self, exc_type, exc, tb): - return False - - class _SessionMakerStub: - def __init__(self, *args, **kwargs): - pass - - def begin(self): - return _BeginStub() - - monkeypatch.setattr( - ConversationService, - "pagination_by_last_id", - lambda *_args, **_kwargs: (_ for _ in ()).throw(LastConversationNotExistsError()), - ) conversation_module = sys.modules["controllers.service_api.app.conversation"] - monkeypatch.setattr(conversation_module, "db", SimpleNamespace(engine=object())) - monkeypatch.setattr(conversation_module, "sessionmaker", _SessionMakerStub) + monkeypatch.setattr(conversation_module, "db", SimpleNamespace(engine=sqlite_engine)) api = ConversationApi() handler = unwrap(api.get) - app_model = SimpleNamespace(mode=AppMode.CHAT) - end_user = SimpleNamespace() + app_model = SimpleNamespace(id="app-1", mode=AppMode.CHAT) + end_user = _end_user() with app.test_request_context( - "/conversations?last_id=00000000-0000-0000-0000-000000000001&limit=20", + f"/conversations?last_id={last_id}&limit=20", method="GET", ): with pytest.raises(NotFound):