test: use real plugin runtime HTTP and stream responses

This commit is contained in:
Asuka Minato committed 2026-10-04 00:53:17 +09:00
1 parent eb80de9280
commit 77a301f8ff
1 file changed
+124 -212
@@ -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