diff --git a/api/tests/unit_tests/core/agent/test_plugin_entities.py b/api/tests/unit_tests/core/agent/test_plugin_entities.py index aa3098a2a1f..edcf8f492a5 100644 --- a/api/tests/unit_tests/core/agent/test_plugin_entities.py +++ b/api/tests/unit_tests/core/agent/test_plugin_entities.py @@ -29,13 +29,24 @@ from core.tools.entities.tool_entities import ToolIdentity, ToolProviderIdentity @pytest.fixture -def mock_identity(mocker: MockerFixture): - return mocker.MagicMock(spec=AgentStrategyIdentity) +def strategy_identity() -> AgentStrategyIdentity: + return AgentStrategyIdentity( + author="test", + name="test-strategy", + label=I18nObject(en_US="Test strategy"), + provider="test-provider", + ) @pytest.fixture -def mock_provider_identity(mocker: MockerFixture): - return mocker.MagicMock(spec=AgentStrategyProviderIdentity) +def provider_identity() -> AgentStrategyProviderIdentity: + return AgentStrategyProviderIdentity( + author="test", + name="test-provider", + description=I18nObject(en_US="Test provider description"), + icon="icon.svg", + label=I18nObject(en_US="Test provider"), + ) # ========================================================= @@ -176,22 +187,22 @@ class TestAgentStrategyParameter: class TestAgentStrategyProviderEntity: - def test_creation_with_plugin_id(self, mock_provider_identity) -> None: + def test_creation_with_plugin_id(self, provider_identity) -> None: entity = AgentStrategyProviderEntity( - identity=mock_provider_identity, + identity=provider_identity, plugin_id="plugin-123", ) assert entity.plugin_id == "plugin-123" - def test_creation_with_empty_plugin_id(self, mock_provider_identity) -> None: + def test_creation_with_empty_plugin_id(self, provider_identity) -> None: entity = AgentStrategyProviderEntity( - identity=mock_provider_identity, + identity=provider_identity, plugin_id="", ) assert entity.plugin_id == "" - def test_creation_without_plugin_id(self, mock_provider_identity) -> None: - entity = AgentStrategyProviderEntity(identity=mock_provider_identity) + def test_creation_without_plugin_id(self, provider_identity) -> None: + entity = AgentStrategyProviderEntity(identity=provider_identity) assert entity.plugin_id is None def test_invalid_identity_raises(self) -> None: @@ -205,22 +216,22 @@ class TestAgentStrategyProviderEntity: class TestAgentStrategyEntity: - def test_parameters_default_empty(self, mock_identity) -> None: + def test_parameters_default_empty(self, strategy_identity) -> None: entity = AgentStrategyEntity( - identity=mock_identity, + identity=strategy_identity, description=I18nObject(en_US="test"), ) assert entity.parameters == [] - def test_parameters_none_converted_to_empty(self, mock_identity) -> None: + def test_parameters_none_converted_to_empty(self, strategy_identity) -> None: entity = AgentStrategyEntity( - identity=mock_identity, + identity=strategy_identity, description=I18nObject(en_US="test"), parameters=None, ) assert entity.parameters == [] - def test_parameters_preserved(self, mock_identity) -> None: + def test_parameters_preserved(self, strategy_identity) -> None: param = AgentStrategyParameter.model_construct( type=AgentStrategyParameter.AgentStrategyParameterType.STRING, name="test", @@ -228,16 +239,16 @@ class TestAgentStrategyEntity: ) entity = AgentStrategyEntity( - identity=mock_identity, + identity=strategy_identity, description=I18nObject(en_US="test"), parameters=[param], ) assert entity.parameters == [param] - def test_invalid_parameters_type_raises(self, mock_identity) -> None: + def test_invalid_parameters_type_raises(self, strategy_identity) -> None: with pytest.raises(ValidationError): AgentStrategyEntity( - identity=mock_identity, + identity=strategy_identity, description=I18nObject(en_US="test"), parameters="invalid", ) @@ -250,25 +261,25 @@ class TestAgentStrategyEntity: [AgentFeature.HISTORY_MESSAGES], ], ) - def test_features_valid(self, mock_identity, features) -> None: + def test_features_valid(self, strategy_identity, features) -> None: entity = AgentStrategyEntity( - identity=mock_identity, + identity=strategy_identity, description=I18nObject(en_US="test"), features=features, ) assert entity.features == features - def test_invalid_features_type_raises(self, mock_identity) -> None: + def test_invalid_features_type_raises(self, strategy_identity) -> None: with pytest.raises(ValidationError): AgentStrategyEntity( - identity=mock_identity, + identity=strategy_identity, description=I18nObject(en_US="test"), features="invalid", ) - def test_output_schema_and_meta_version(self, mock_identity) -> None: + def test_output_schema_and_meta_version(self, strategy_identity) -> None: entity = AgentStrategyEntity( - identity=mock_identity, + identity=strategy_identity, description=I18nObject(en_US="test"), output_schema={"type": "object"}, meta_version="v1", @@ -276,9 +287,9 @@ class TestAgentStrategyEntity: assert entity.output_schema == {"type": "object"} assert entity.meta_version == "v1" - def test_missing_required_fields_raise(self, mock_identity) -> None: + def test_missing_required_fields_raise(self, strategy_identity) -> None: with pytest.raises(ValidationError): - AgentStrategyEntity(identity=mock_identity) + AgentStrategyEntity(identity=strategy_identity) # ========================================================= @@ -287,27 +298,27 @@ class TestAgentStrategyEntity: class TestAgentProviderEntityWithPlugin: - def test_default_strategies_empty(self, mock_provider_identity) -> None: - entity = AgentProviderEntityWithPlugin(identity=mock_provider_identity) + def test_default_strategies_empty(self, provider_identity) -> None: + entity = AgentProviderEntityWithPlugin(identity=provider_identity) assert entity.strategies == [] - def test_strategies_assignment(self, mock_provider_identity, mock_identity) -> None: + def test_strategies_assignment(self, provider_identity, strategy_identity) -> None: strategy = AgentStrategyEntity.model_construct( - identity=mock_identity, + identity=strategy_identity, description=I18nObject(en_US="test"), parameters=[], ) entity = AgentProviderEntityWithPlugin( - identity=mock_provider_identity, + identity=provider_identity, strategies=[strategy], ) assert entity.strategies == [strategy] - def test_invalid_strategies_type_raises(self, mock_provider_identity) -> None: + def test_invalid_strategies_type_raises(self, provider_identity) -> None: with pytest.raises(ValidationError): AgentProviderEntityWithPlugin( - identity=mock_provider_identity, + identity=provider_identity, strategies="invalid", ) diff --git a/api/tests/unit_tests/core/mcp/test_entities.py b/api/tests/unit_tests/core/mcp/test_entities.py index e99c38285ca..d591a42ae20 100644 --- a/api/tests/unit_tests/core/mcp/test_entities.py +++ b/api/tests/unit_tests/core/mcp/test_entities.py @@ -1,13 +1,22 @@ """Unit tests for MCP entities module.""" -from unittest.mock import Mock +from queue import Queue from core.mcp.entities import ( SUPPORTED_PROTOCOL_VERSIONS, RequestContext, ) from core.mcp.session.base_session import BaseSession -from core.mcp.types import LATEST_PROTOCOL_VERSION, RequestParams +from core.mcp.types import LATEST_PROTOCOL_VERSION, RequestParams, ServerNotification, ServerRequest + + +def _session() -> BaseSession: + return BaseSession( + read_stream=Queue(), + write_stream=Queue(), + receive_request_type=ServerRequest, + receive_notification_type=ServerNotification, + ) class TestProtocolVersions: @@ -31,46 +40,46 @@ class TestRequestContext: def test_request_context_creation(self): """Test creating a RequestContext instance.""" - mock_session = Mock(spec=BaseSession) - mock_lifespan = {"key": "value"} - mock_meta = RequestParams.Meta(progressToken="test-token") + session = _session() + lifespan = {"key": "value"} + meta = RequestParams.Meta(progressToken="test-token") context = RequestContext( request_id="test-request-123", - meta=mock_meta, - session=mock_session, - lifespan_context=mock_lifespan, + meta=meta, + session=session, + lifespan_context=lifespan, ) assert context.request_id == "test-request-123" - assert context.meta == mock_meta - assert context.session == mock_session - assert context.lifespan_context == mock_lifespan + assert context.meta == meta + assert context.session == session + assert context.lifespan_context == lifespan def test_request_context_with_none_meta(self): """Test creating RequestContext with None meta.""" - mock_session = Mock(spec=BaseSession) + session = _session() context = RequestContext( request_id=42, # Can be int or string meta=None, - session=mock_session, + session=session, lifespan_context=None, ) assert context.request_id == 42 assert context.meta is None - assert context.session == mock_session + assert context.session == session assert context.lifespan_context is None def test_request_context_attributes(self): """Test RequestContext attributes are accessible.""" - mock_session = Mock(spec=BaseSession) + session = _session() context = RequestContext( request_id="test-123", meta=None, - session=mock_session, + session=session, lifespan_context=None, ) @@ -83,19 +92,19 @@ class TestRequestContext: # Verify values assert context.request_id == "test-123" assert context.meta is None - assert context.session == mock_session + assert context.session == session assert context.lifespan_context is None def test_request_context_generic_typing(self): """Test RequestContext with different generic types.""" - # Create a mock session with specific type - mock_session = Mock(spec=BaseSession) + # Create a real session without starting its receive loop. + session = _session() # Create context with string lifespan context context_str = RequestContext[BaseSession, str]( request_id="test-1", meta=None, - session=mock_session, + session=session, lifespan_context="string-context", ) assert isinstance(context_str.lifespan_context, str) @@ -104,7 +113,7 @@ class TestRequestContext: context_dict = RequestContext[BaseSession, dict]( request_id="test-2", meta=None, - session=mock_session, + session=session, lifespan_context={"key": "value"}, ) assert isinstance(context_dict.lifespan_context, dict) @@ -118,7 +127,7 @@ class TestRequestContext: context_custom = RequestContext[BaseSession, CustomLifespan]( request_id="test-3", meta=None, - session=mock_session, + session=session, lifespan_context=custom_lifespan, ) assert isinstance(context_custom.lifespan_context, CustomLifespan) @@ -126,13 +135,13 @@ class TestRequestContext: def test_request_context_with_progress_meta(self): """Test RequestContext with progress metadata.""" - mock_session = Mock(spec=BaseSession) + session = _session() progress_meta = RequestParams.Meta(progressToken="progress-123") context = RequestContext( request_id="req-456", meta=progress_meta, - session=mock_session, + session=session, lifespan_context=None, ) @@ -141,27 +150,27 @@ class TestRequestContext: def test_request_context_equality(self): """Test RequestContext equality comparison.""" - mock_session1 = Mock(spec=BaseSession) - mock_session2 = Mock(spec=BaseSession) + session1 = _session() + session2 = _session() context1 = RequestContext( request_id="test-123", meta=None, - session=mock_session1, + session=session1, lifespan_context="context", ) context2 = RequestContext( request_id="test-123", meta=None, - session=mock_session1, + session=session1, lifespan_context="context", ) context3 = RequestContext( request_id="test-456", meta=None, - session=mock_session1, + session=session1, lifespan_context="context", ) @@ -175,24 +184,23 @@ class TestRequestContext: context4 = RequestContext( request_id="test-123", meta=None, - session=mock_session2, + session=session2, lifespan_context="context", ) assert context1 != context4 def test_request_context_repr(self): """Test RequestContext string representation.""" - mock_session = Mock(spec=BaseSession) - mock_session.__repr__ = Mock(return_value="") + session = _session() context = RequestContext( request_id="test-123", meta=None, - session=mock_session, + session=session, lifespan_context={"data": "test"}, ) repr_str = repr(context) assert "RequestContext" in repr_str assert "test-123" in repr_str - assert "MockSession" in repr_str + assert repr(session) in repr_str diff --git a/api/tests/unit_tests/libs/test_email_i18n.py b/api/tests/unit_tests/libs/test_email_i18n.py index 0fc37bb016e..c8d94c13c73 100644 --- a/api/tests/unit_tests/libs/test_email_i18n.py +++ b/api/tests/unit_tests/libs/test_email_i18n.py @@ -44,11 +44,8 @@ class MockBrandingService: self.application_title = application_title def get_branding_config(self) -> BrandingModel: - """Return mock branding configuration""" - branding_model = MagicMock(spec=BrandingModel) - branding_model.enabled = self.enabled - branding_model.application_title = self.application_title - return branding_model + """Return branding configuration with the requested test settings.""" + return BrandingModel(enabled=self.enabled, application_title=self.application_title) class MockEmailSender: diff --git a/api/tests/unit_tests/tools/test_api_tool.py b/api/tests/unit_tests/tools/test_api_tool.py index 0c5c75121a3..cc639db27ea 100644 --- a/api/tests/unit_tests/tools/test_api_tool.py +++ b/api/tests/unit_tests/tools/test_api_tool.py @@ -54,15 +54,13 @@ class TestApiToolInvoke: author="test_author", ) - # Create a mock runtime - self.mock_runtime = Mock(spec=ToolRuntime) - self.mock_runtime.credentials = {"auth_type": "none"} + self.runtime = ToolRuntime(tenant_id="test_tenant", credentials={"auth_type": "none"}) # Create the ApiTool instance self.api_tool = ApiTool( entity=self.mock_tool_entity, api_bundle=self.mock_api_bundle, - runtime=self.mock_runtime, + runtime=self.runtime, provider_id="test_provider", ) @@ -71,19 +69,13 @@ class TestApiToolInvoke: self, mock_get: Mock, database_session: Session ) -> None: """Test that when upstream returns JSON, the output Text message contains JSON-serialized string.""" - # Setup mock response with JSON content + # Use the response's real JSON parsing and content decoding. json_response_data = { "key": "value", "number": 123, "nested": {"inner": "data"}, } - mock_response = Mock(spec=httpx.Response) - mock_response.status_code = 200 - mock_response.content = json.dumps(json_response_data).encode("utf-8") - mock_response.json.return_value = json_response_data - mock_response.text = json.dumps(json_response_data) - mock_response.headers = {"content-type": "application/json"} - mock_get.return_value = mock_response + mock_get.return_value = httpx.Response(200, json=json_response_data) # Invoke the tool result_generator = self.api_tool._invoke(session=database_session, user_id="test_user", tool_parameters={}) @@ -150,15 +142,11 @@ class TestApiToolInvoke: self, mock_get: Mock, test_case, database_session: Session ) -> None: """Test that when upstream returns a non-dict JSON, the output Text message contains JSON-serialized string.""" - # Setup mock response with non-dict JSON content + # Explicit content also covers JSON null, which is distinct from no body. _, json_value = test_case - mock_response = Mock(spec=httpx.Response) - mock_response.status_code = 200 - mock_response.content = json.dumps(json_value).encode("utf-8") - mock_response.json.return_value = json_value - mock_response.text = json.dumps(json_value) - mock_response.headers = {"content-type": "application/json"} - mock_get.return_value = mock_response + mock_get.return_value = httpx.Response( + 200, content=json.dumps(json_value), headers={"content-type": "application/json"} + ) # Invoke the tool result_generator = self.api_tool._invoke(session=database_session, user_id="test_user", tool_parameters={}) @@ -189,15 +177,9 @@ class TestApiToolInvoke: self, mock_get: Mock, database_session: Session ) -> None: """Test that when upstream returns plain text, the output Text message contains the original text.""" - # Setup mock response with plain text content + # Plain text exercises the response's real JSON decoding failure. text_response_data = "This is a plain text response" - mock_response = Mock(spec=httpx.Response) - mock_response.status_code = 200 - mock_response.content = text_response_data.encode("utf-8") - mock_response.json.side_effect = json.JSONDecodeError("Expecting value", "doc", 0) - mock_response.text = text_response_data - mock_response.headers = {"content-type": "text/plain"} - mock_get.return_value = mock_response + mock_get.return_value = httpx.Response(200, text=text_response_data) # Invoke the tool result_generator = self.api_tool._invoke(session=database_session, user_id="test_user", tool_parameters={}) @@ -218,12 +200,7 @@ class TestApiToolInvoke: @patch("core.tools.custom_tool.tool.ssrf_proxy.get") def test_invoke_with_empty_response(self, mock_get: Mock, database_session: Session) -> None: """Test that empty responses are handled correctly.""" - # Setup mock response with empty content - mock_response = Mock(spec=httpx.Response) - mock_response.status_code = 200 - mock_response.content = b"" - mock_response.headers = {"content-type": "application/json"} - mock_get.return_value = mock_response + mock_get.return_value = httpx.Response(200, content=b"", headers={"content-type": "application/json"}) # Invoke the tool result_generator = self.api_tool._invoke(session=database_session, user_id="test_user", tool_parameters={}) @@ -244,11 +221,7 @@ class TestApiToolInvoke: @patch("core.tools.custom_tool.tool.ssrf_proxy.get") def test_invoke_with_error_response(self, mock_get: Mock, database_session: Session) -> None: """Test that error responses are handled correctly.""" - # Setup mock response with error status code - mock_response = Mock(spec=httpx.Response) - mock_response.status_code = 404 - mock_response.text = "Not Found" - mock_get.return_value = mock_response + mock_get.return_value = httpx.Response(404, text="Not Found") result_generator = self.api_tool._invoke(session=database_session, user_id="test_user", tool_parameters={}) diff --git a/api/tests/unit_tests/tools/test_mcp_tool.py b/api/tests/unit_tests/tools/test_mcp_tool.py index 0b6f80edcb4..e841bd03c50 100644 --- a/api/tests/unit_tests/tools/test_mcp_tool.py +++ b/api/tests/unit_tests/tools/test_mcp_tool.py @@ -2,7 +2,7 @@ import base64 from collections.abc import Iterator from decimal import Decimal from typing import Any -from unittest.mock import Mock, patch +from unittest.mock import patch import pytest from sqlalchemy.engine import Engine @@ -39,8 +39,7 @@ def _make_mcp_tool(output_schema: dict[str, Any] | None = None) -> MCPTool: provider="test_provider", ) entity = ToolEntity(identity=identity, output_schema=output_schema or {}) - runtime = Mock(spec=ToolRuntime) - runtime.credentials = {} + runtime = ToolRuntime(tenant_id="test_tenant") return MCPTool( entity=entity, runtime=runtime,