From 77a301f8ff0db8764370aa68c20d0770eaab3c26 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Sat, 3 Oct 2026 02:11:11 +0900 Subject: [PATCH] test: use real plugin runtime HTTP and stream responses --- .../core/plugin/test_plugin_runtime.py | 336 +++++++----------- 1 file changed, 124 insertions(+), 212 deletions(-) diff --git a/api/tests/unit_tests/core/plugin/test_plugin_runtime.py b/api/tests/unit_tests/core/plugin/test_plugin_runtime.py index 6f2455f2d5c..b72856395f9 100644 --- a/api/tests/unit_tests/core/plugin/test_plugin_runtime.py +++ b/api/tests/unit_tests/core/plugin/test_plugin_runtime.py @@ -13,7 +13,7 @@ Tests follow the Arrange-Act-Assert pattern for clarity. import json from typing import Any -from unittest.mock import MagicMock, patch +from unittest.mock import patch import httpx import pytest @@ -54,8 +54,17 @@ def _patch_shared_httpx_client(): After refactor, code uses core.plugin.impl.base._httpx_client directly. Patch its request/stream to route through module-level httpx so existing mocks still apply. """ + + def request[**P](*args: P.args, **kwargs: P.kwargs) -> httpx.Response: + response = httpx.request(*args, **kwargs) + method, url = kwargs["method"], kwargs["url"] + assert isinstance(method, str) + assert isinstance(url, str) + response.request = httpx.Request(method, url) + return response + with ( - patch("core.plugin.impl.base._httpx_client.request", side_effect=lambda **kw: httpx.request(**kw)), + patch("core.plugin.impl.base._httpx_client.request", side_effect=request), patch("core.plugin.impl.base._httpx_client.stream", side_effect=lambda **kw: httpx.stream(**kw)), ): yield @@ -124,9 +133,7 @@ class TestPluginRuntimeExecution: def test_successful_request_execution(self, plugin_client, mock_config): """Test successful HTTP request execution.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"result": "success"} + mock_response = httpx.Response(200, json={"result": "success"}) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -143,8 +150,7 @@ class TestPluginRuntimeExecution: def test_request_with_timeout_configuration(self, plugin_client, mock_config): """Test that timeout configuration is properly applied.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 + mock_response = httpx.Response(200) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -155,7 +161,7 @@ class TestPluginRuntimeExecution: assert "timeout" in call_kwargs def test_request_timeout_can_be_scoped_to_current_context(self, plugin_client, mock_config): - mock_response = MagicMock(status_code=200) + mock_response = httpx.Response(200) with ( patch("core.plugin.impl.base.plugin_daemon_request_timeout", httpx.Timeout(600.0)), @@ -215,9 +221,7 @@ class TestPluginRuntimeSandboxIsolation: def test_api_key_authentication(self, plugin_client, mock_config): """Test that all requests include API key for authentication.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": True} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": True}) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -234,9 +238,7 @@ class TestPluginRuntimeSandboxIsolation: class TestResponse(BaseModel): result: str - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": {"result": "isolated_execution"}} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": {"result": "isolated_execution"}}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -250,10 +252,8 @@ class TestPluginRuntimeSandboxIsolation: def test_plugin_daemon_unauthorized_error(self, plugin_client, mock_config): """Test handling of unauthorized access to plugin daemon.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps({"error_type": "PluginDaemonUnauthorizedError", "message": "Unauthorized access"}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -264,12 +264,10 @@ class TestPluginRuntimeSandboxIsolation: def test_plugin_permission_denied(self, plugin_client, mock_config): """Test handling of permission denied errors.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps( {"error_type": "PluginPermissionDeniedError", "message": "Permission denied for this operation"} ) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -306,8 +304,7 @@ class TestPluginRuntimeResourceLimits: def test_timeout_configuration_applied(self, plugin_client, mock_config): """Test that timeout configuration is properly applied to requests.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 + mock_response = httpx.Response(200) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -338,12 +335,10 @@ class TestPluginRuntimeResourceLimits: def test_resource_limit_error_from_daemon(self, plugin_client, mock_config): """Test handling of resource limit errors from plugin daemon.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps( {"error_type": "PluginDaemonInternalServerError", "message": "Resource limit exceeded"} ) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -378,15 +373,13 @@ class TestPluginRuntimeErrorHandling: def test_plugin_invoke_rate_limit_error(self, plugin_client, mock_config): """Test handling of rate limit errors during plugin invocation.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 invoke_error = { "error_type": "InvokeRateLimitError", "message": "Rate limit exceeded", "args": {"description": "Rate limit exceeded"}, } error_message = json.dumps({"error_type": "PluginInvokeError", "message": json.dumps(invoke_error)}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -397,15 +390,13 @@ class TestPluginRuntimeErrorHandling: def test_plugin_invoke_authorization_error(self, plugin_client, mock_config): """Test handling of authorization errors during plugin invocation.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 invoke_error = { "error_type": "InvokeAuthorizationError", "message": "Invalid credentials", "args": {"description": "Invalid credentials"}, } error_message = json.dumps({"error_type": "PluginInvokeError", "message": json.dumps(invoke_error)}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -416,15 +407,13 @@ class TestPluginRuntimeErrorHandling: def test_plugin_invoke_bad_request_error(self, plugin_client, mock_config): """Test handling of bad request errors during plugin invocation.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 invoke_error = { "error_type": "InvokeBadRequestError", "message": "Invalid parameters", "args": {"description": "Invalid parameters"}, } error_message = json.dumps({"error_type": "PluginInvokeError", "message": json.dumps(invoke_error)}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -435,15 +424,13 @@ class TestPluginRuntimeErrorHandling: def test_plugin_invoke_connection_error(self, plugin_client, mock_config): """Test handling of connection errors during plugin invocation.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 invoke_error = { "error_type": "InvokeConnectionError", "message": "Connection to external service failed", "args": {"description": "Connection to external service failed"}, } error_message = json.dumps({"error_type": "PluginInvokeError", "message": json.dumps(invoke_error)}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -454,15 +441,13 @@ class TestPluginRuntimeErrorHandling: def test_plugin_invoke_server_unavailable_error(self, plugin_client, mock_config): """Test handling of server unavailable errors during plugin invocation.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 invoke_error = { "error_type": "InvokeServerUnavailableError", "message": "Service temporarily unavailable", "args": {"description": "Service temporarily unavailable"}, } error_message = json.dumps({"error_type": "PluginInvokeError", "message": json.dumps(invoke_error)}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -473,14 +458,12 @@ class TestPluginRuntimeErrorHandling: def test_credentials_validation_error(self, plugin_client, mock_config): """Test handling of credential validation errors.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 invoke_error = { "error_type": "CredentialsValidateFailedError", "message": "Invalid API key format", } error_message = json.dumps({"error_type": "PluginInvokeError", "message": json.dumps(invoke_error)}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -491,12 +474,10 @@ class TestPluginRuntimeErrorHandling: def test_plugin_not_found_error(self, plugin_client, mock_config): """Test handling of plugin not found errors.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps( {"error_type": "PluginNotFoundError", "message": "Plugin with ID 'test-plugin' not found"} ) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -507,12 +488,10 @@ class TestPluginRuntimeErrorHandling: def test_plugin_unique_identifier_error(self, plugin_client, mock_config): """Test handling of unique identifier errors.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps( {"error_type": "PluginUniqueIdentifierError", "message": "Invalid plugin identifier format"} ) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -523,12 +502,10 @@ class TestPluginRuntimeErrorHandling: def test_daemon_bad_request_error(self, plugin_client, mock_config): """Test handling of daemon bad request errors.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps( {"error_type": "PluginDaemonBadRequestError", "message": "Missing required parameter"} ) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -539,10 +516,8 @@ class TestPluginRuntimeErrorHandling: def test_daemon_not_found_error(self, plugin_client, mock_config): """Test handling of daemon not found errors.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps({"error_type": "PluginDaemonNotFoundError", "message": "Resource not found"}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -553,14 +528,12 @@ class TestPluginRuntimeErrorHandling: def test_generic_plugin_invoke_error(self, plugin_client, mock_config): """Test handling of generic plugin invoke errors.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 # Create a proper nested JSON structure for PluginInvokeError invoke_error_message = json.dumps( {"error_type": "UnknownInvokeError", "message": "Generic plugin execution error"} ) error_message = json.dumps({"error_type": "PluginInvokeError", "message": invoke_error_message}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -571,10 +544,8 @@ class TestPluginRuntimeErrorHandling: def test_unknown_error_type(self, plugin_client, mock_config): """Test handling of unknown error types.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps({"error_type": "UnknownErrorType", "message": "Unknown error occurred"}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -585,10 +556,8 @@ class TestPluginRuntimeErrorHandling: def test_http_status_error_handling(self, plugin_client, mock_config): """Test handling of HTTP status errors.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 500 - mock_response.raise_for_status.side_effect = httpx.HTTPStatusError( - "Server Error", request=MagicMock(), response=mock_response + mock_response = httpx.Response( + 500, request=httpx.Request("GET", "http://127.0.0.1:5002/plugin/test-tenant/test") ) with patch("httpx.request", return_value=mock_response, autospec=True): @@ -599,9 +568,7 @@ class TestPluginRuntimeErrorHandling: def test_empty_data_response_error(self, plugin_client, mock_config): """Test handling of empty data in successful response.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": None} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -641,9 +608,7 @@ class TestPluginRuntimeCommunication: value: str count: int - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": {"value": "test", "count": 42}} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": {"value": "test", "count": 42}}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -669,8 +634,7 @@ class TestPluginRuntimeCommunication: 'data: {"code": 0, "message": "", "data": {"chunk": "third"}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -699,8 +663,7 @@ class TestPluginRuntimeCommunication: f'data: {{"code": -500, "message": {json.dumps(error_obj)}, "data": null}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -738,9 +701,7 @@ class TestPluginRuntimeCommunication: status: str data: dict[str, Any] - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"status": "success", "data": {"key": "value"}} + mock_response = httpx.Response(200, json={"status": "success", "data": {"key": "value"}}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -764,8 +725,7 @@ class TestPluginRuntimeCommunication: '{"id": 2, "text": "second"}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -796,8 +756,7 @@ class TestPluginRuntimeCommunication: "", ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -845,8 +804,7 @@ class TestPluginToolManagerIntegration: 'data: {"code": 0, "message": "", "data": {"type": "text", "message": {"text": "Result"}}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -875,8 +833,7 @@ class TestPluginToolManagerIntegration: 'data: {"code": 0, "message": "", "data": {"result": true}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -899,8 +856,7 @@ class TestPluginToolManagerIntegration: 'data: {"code": 0, "message": "", "data": {"result": false}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -923,8 +879,7 @@ class TestPluginToolManagerIntegration: 'data: {"code": 0, "message": "", "data": {"result": true}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -967,16 +922,17 @@ class TestPluginInstallerIntegration: def test_list_plugins_success(self, installer, mock_config): """Test successful plugin listing.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = { - "code": 0, - "message": "", - "data": { - "list": [], - "total": 0, + mock_response = httpx.Response( + 200, + json={ + "code": 0, + "message": "", + "data": { + "list": [], + "total": 0, + }, }, - } + ) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -988,9 +944,7 @@ class TestPluginInstallerIntegration: def test_uninstall_plugin_success(self, installer, mock_config): """Test successful plugin uninstallation.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": True} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": True}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -1002,9 +956,7 @@ class TestPluginInstallerIntegration: def test_fetch_plugin_by_identifier_success(self, installer, mock_config): """Test successful plugin fetch by identifier.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": True} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": True}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -1040,9 +992,7 @@ class TestPluginRuntimeEdgeCases: def test_malformed_json_response(self, plugin_client, mock_config): """Test handling of malformed JSON responses.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.side_effect = json.JSONDecodeError("Invalid JSON", "", 0) + mock_response = httpx.Response(200, content=b"invalid JSON") with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -1052,10 +1002,8 @@ class TestPluginRuntimeEdgeCases: def test_invalid_response_structure(self, plugin_client, mock_config): """Test handling of invalid response structure.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 # Missing required fields in response - mock_response.json.return_value = {"invalid": "structure"} + mock_response = httpx.Response(200, json={"invalid": "structure"}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -1070,8 +1018,7 @@ class TestPluginRuntimeEdgeCases: "data: {invalid json}", ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1094,8 +1041,7 @@ class TestPluginRuntimeEdgeCases: def test_request_with_bytes_data(self, plugin_client, mock_config): """Test request with bytes data.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 + mock_response = httpx.Response(200) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -1108,8 +1054,7 @@ class TestPluginRuntimeEdgeCases: def test_request_with_files(self, plugin_client, mock_config): """Test request with file upload.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 + mock_response = httpx.Response(200) files = {"file": ("test.txt", b"file content", "text/plain")} @@ -1124,8 +1069,7 @@ class TestPluginRuntimeEdgeCases: def test_streaming_empty_response(self, plugin_client, mock_config): """Test streaming with empty response.""" # Arrange - mock_response = MagicMock() - mock_response.iter_lines.return_value = [] + mock_response = httpx.Response(200, content=b"") with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1144,8 +1088,7 @@ class TestPluginRuntimeEdgeCases: f'data: {{"code": -500, "message": {json.dumps(error_obj)}, "data": null}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1164,9 +1107,7 @@ class TestPluginRuntimeEdgeCases: def test_non_json_error_message(self, plugin_client, mock_config): """Test handling of non-JSON error message.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": -1, "message": "Plain text error message", "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": "Plain text error message", "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -1201,9 +1142,7 @@ class TestPluginRuntimeAdvancedScenarios: def test_multiple_sequential_requests(self, plugin_client, mock_config): """Test multiple sequential requests to the same endpoint.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": True} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": True}) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -1230,9 +1169,7 @@ class TestPluginRuntimeAdvancedScenarios: ], } - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": complex_data} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": complex_data}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -1259,8 +1196,7 @@ class TestPluginRuntimeAdvancedScenarios: '{"code": 0, "message": "", "data": {"type": "complete", "data": {"result": "success"}}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1289,8 +1225,7 @@ class TestPluginRuntimeAdvancedScenarios: call_count += 1 if call_count < 3: raise httpx.RequestError("Temporary failure") - mock_response = MagicMock() - mock_response.status_code = 200 + mock_response = httpx.Response(200) return mock_response with patch("httpx.request", side_effect=side_effect, autospec=True): @@ -1314,8 +1249,7 @@ class TestPluginRuntimeAdvancedScenarios: "X-Tenant-ID": "tenant-456", } - mock_response = MagicMock() - mock_response.status_code = 200 + mock_response = httpx.Response(200) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -1340,8 +1274,7 @@ class TestPluginRuntimeAdvancedScenarios: f'{{"code": 0, "message": "", "data": {{"chunk_id": {i}, "data": "{large_data}"}}}}' for i in range(10) ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1386,8 +1319,7 @@ class TestPluginRuntimeSecurityAndValidation: def test_api_key_header_always_present(self, plugin_client, mock_config): """Test that API key header is always included in requests.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 + mock_response = httpx.Response(200) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -1407,9 +1339,7 @@ class TestPluginRuntimeSecurityAndValidation: "credentials": {"token": "secret-token"}, } - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": True} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": True}) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -1428,10 +1358,8 @@ class TestPluginRuntimeSecurityAndValidation: def test_unauthorized_access_with_invalid_key(self, plugin_client, mock_config): """Test handling of unauthorized access with invalid API key.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps({"error_type": "PluginDaemonUnauthorizedError", "message": "Invalid API key"}) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -1447,12 +1375,10 @@ class TestPluginRuntimeSecurityAndValidation: "limit": 0, # Invalid zero limit } - mock_response = MagicMock() - mock_response.status_code = 200 error_message = json.dumps( {"error_type": "PluginDaemonBadRequestError", "message": "Invalid parameters: page must be positive"} ) - mock_response.json.return_value = {"code": -1, "message": error_message, "data": None} + mock_response = httpx.Response(200, json={"code": -1, "message": error_message, "data": None}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert @@ -1465,8 +1391,7 @@ class TestPluginRuntimeSecurityAndValidation: def test_content_type_header_validation(self, plugin_client, mock_config): """Test that Content-Type header is properly set for JSON requests.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 + mock_response = httpx.Response(200) with patch("httpx.request", return_value=mock_response, autospec=True) as mock_request: # Act @@ -1515,8 +1440,7 @@ class TestPluginRuntimePerformanceScenarios: f'{{"code": 0, "message": "", "data": {{"index": {i}, "value": "chunk_{i}"}}}}' for i in range(100) ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1550,8 +1474,7 @@ class TestPluginRuntimePerformanceScenarios: stream_data = [f'{{"code": 0, "message": "", "data": {{"data": "chunk_{i}"}}}}' for i in range(10)] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1577,9 +1500,7 @@ class TestPluginRuntimePerformanceScenarios: def test_concurrent_request_simulation(self, plugin_client, mock_config): """Test simulation of concurrent requests (sequential execution in test).""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": True} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": True}) request_results = [] @@ -1637,8 +1558,7 @@ class TestPluginToolManagerAdvanced: ), ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1666,8 +1586,7 @@ class TestPluginToolManagerAdvanced: 'data: {"code": 0, "message": "", "data": {"type": "text", "message": {"text": "Context-aware result"}}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1698,8 +1617,7 @@ class TestPluginToolManagerAdvanced: 'data: {"code": 0, "message": "", "data": {"parameters": []}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1729,8 +1647,7 @@ class TestPluginToolManagerAdvanced: 'data: {"code": 0, "message": "", "data": {"result": true}}', ] - mock_response = MagicMock() - mock_response.iter_lines.return_value = [line.encode("utf-8") for line in stream_data] + mock_response = httpx.Response(200, text="\n".join(stream_data)) with patch("httpx.stream", autospec=True) as mock_stream: mock_stream.return_value.__enter__.return_value = mock_response @@ -1774,28 +1691,29 @@ class TestPluginInstallerAdvanced: """Test successful plugin package upload.""" # Arrange plugin_package = b"fake-plugin-package-data" - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = { - "code": 0, - "message": "", - "data": { - "unique_identifier": "test-org/test-plugin", - "manifest": { - "version": "1.0.0", - "author": "test-org", - "name": "test-plugin", - "description": {"en_US": "Test plugin"}, - "icon": "icon.png", - "label": {"en_US": "Test Plugin"}, - "created_at": "2024-01-01T00:00:00Z", - "resource": {"memory": 256}, - "plugins": {}, - "meta": {}, + mock_response = httpx.Response( + 200, + json={ + "code": 0, + "message": "", + "data": { + "unique_identifier": "test-org/test-plugin", + "manifest": { + "version": "1.0.0", + "author": "test-org", + "name": "test-plugin", + "description": {"en_US": "Test plugin"}, + "icon": "icon.png", + "label": {"en_US": "Test Plugin"}, + "created_at": "2024-01-01T00:00:00Z", + "resource": {"memory": 256}, + "plugins": {}, + "meta": {}, + }, + "verification": None, }, - "verification": None, }, - } + ) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -1807,13 +1725,14 @@ class TestPluginInstallerAdvanced: def test_fetch_plugin_readme_success(self, installer, mock_config): """Test successful plugin readme fetch.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = { - "code": 0, - "message": "", - "data": {"content": "# Plugin README\n\nThis is a test plugin.", "language": "en"}, - } + mock_response = httpx.Response( + 200, + json={ + "code": 0, + "message": "", + "data": {"content": "# Plugin README\n\nThis is a test plugin.", "language": "en"}, + }, + ) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -1826,13 +1745,7 @@ class TestPluginInstallerAdvanced: def test_fetch_plugin_readme_not_found(self, installer, mock_config): """Test plugin readme fetch when readme doesn't exist.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 404 - - def raise_for_status(): - raise httpx.HTTPStatusError("Not Found", request=MagicMock(), response=mock_response) - - mock_response.raise_for_status = raise_for_status + mock_response = httpx.Response(404) with patch("httpx.request", return_value=mock_response, autospec=True): # Act & Assert - Should raise PluginDaemonClientSideError for 404 @@ -1842,16 +1755,17 @@ class TestPluginInstallerAdvanced: def test_list_plugins_with_pagination(self, installer, mock_config): """Test plugin listing with pagination.""" # Arrange - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = { - "code": 0, - "message": "", - "data": { - "list": [], - "total": 50, + mock_response = httpx.Response( + 200, + json={ + "code": 0, + "message": "", + "data": { + "list": [], + "total": 50, + }, }, - } + ) with patch("httpx.request", return_value=mock_response, autospec=True): # Act @@ -1871,9 +1785,7 @@ class TestPluginInstallerAdvanced: GenericProviderID("langgenius/plugin2/provider2"), ] - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"code": 0, "message": "", "data": [True, False]} + mock_response = httpx.Response(200, json={"code": 0, "message": "", "data": [True, False]}) with patch("httpx.request", return_value=mock_response, autospec=True): # Act