dify/sdks/python-client/tests/test_httpx_migration.py
lyzno1 a9b3539b90
feat: migrate Python SDK to httpx with async/await support (#26726)
Signed-off-by: lyzno1 <yuanyouhuilyz@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-10-11 17:45:42 +08:00

332 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Test suite for httpx migration in the Python SDK.
This test validates that the migration from requests to httpx maintains
backward compatibility and proper resource management.
"""
import unittest
from unittest.mock import Mock, patch
from dify_client import (
DifyClient,
ChatClient,
CompletionClient,
WorkflowClient,
WorkspaceClient,
KnowledgeBaseClient,
)
class TestHttpxMigrationMocked(unittest.TestCase):
"""Test cases for httpx migration with mocked requests."""
def setUp(self):
"""Set up test fixtures."""
self.api_key = "test-api-key"
self.base_url = "https://api.dify.ai/v1"
@patch("dify_client.client.httpx.Client")
def test_client_initialization(self, mock_httpx_client):
"""Test that client initializes with httpx.Client."""
mock_client_instance = Mock()
mock_httpx_client.return_value = mock_client_instance
client = DifyClient(self.api_key, self.base_url)
# Verify httpx.Client was called with correct parameters
mock_httpx_client.assert_called_once()
call_kwargs = mock_httpx_client.call_args[1]
self.assertEqual(call_kwargs["base_url"], self.base_url)
# Verify client properties
self.assertEqual(client.api_key, self.api_key)
self.assertEqual(client.base_url, self.base_url)
client.close()
@patch("dify_client.client.httpx.Client")
def test_context_manager_support(self, mock_httpx_client):
"""Test that client works as context manager."""
mock_client_instance = Mock()
mock_httpx_client.return_value = mock_client_instance
with DifyClient(self.api_key, self.base_url) as client:
self.assertEqual(client.api_key, self.api_key)
# Verify close was called
mock_client_instance.close.assert_called_once()
@patch("dify_client.client.httpx.Client")
def test_manual_close(self, mock_httpx_client):
"""Test manual close() method."""
mock_client_instance = Mock()
mock_httpx_client.return_value = mock_client_instance
client = DifyClient(self.api_key, self.base_url)
client.close()
# Verify close was called
mock_client_instance.close.assert_called_once()
@patch("dify_client.client.httpx.Client")
def test_send_request_httpx_compatibility(self, mock_httpx_client):
"""Test _send_request uses httpx.Client.request properly."""
mock_response = Mock()
mock_response.json.return_value = {"result": "success"}
mock_response.status_code = 200
mock_client_instance = Mock()
mock_client_instance.request.return_value = mock_response
mock_httpx_client.return_value = mock_client_instance
client = DifyClient(self.api_key, self.base_url)
response = client._send_request("GET", "/test-endpoint")
# Verify httpx.Client.request was called correctly
mock_client_instance.request.assert_called_once()
call_args = mock_client_instance.request.call_args
# Verify method and endpoint
self.assertEqual(call_args[0][0], "GET")
self.assertEqual(call_args[0][1], "/test-endpoint")
# Verify headers contain authorization
headers = call_args[1]["headers"]
self.assertEqual(headers["Authorization"], f"Bearer {self.api_key}")
self.assertEqual(headers["Content-Type"], "application/json")
client.close()
@patch("dify_client.client.httpx.Client")
def test_response_compatibility(self, mock_httpx_client):
"""Test httpx.Response is compatible with requests.Response API."""
mock_response = Mock()
mock_response.json.return_value = {"key": "value"}
mock_response.text = '{"key": "value"}'
mock_response.content = b'{"key": "value"}'
mock_response.status_code = 200
mock_response.headers = {"Content-Type": "application/json"}
mock_client_instance = Mock()
mock_client_instance.request.return_value = mock_response
mock_httpx_client.return_value = mock_client_instance
client = DifyClient(self.api_key, self.base_url)
response = client._send_request("GET", "/test")
# Verify all common response methods work
self.assertEqual(response.json(), {"key": "value"})
self.assertEqual(response.text, '{"key": "value"}')
self.assertEqual(response.content, b'{"key": "value"}')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.headers["Content-Type"], "application/json")
client.close()
@patch("dify_client.client.httpx.Client")
def test_all_client_classes_use_httpx(self, mock_httpx_client):
"""Test that all client classes properly use httpx."""
mock_client_instance = Mock()
mock_httpx_client.return_value = mock_client_instance
clients = [
DifyClient(self.api_key, self.base_url),
ChatClient(self.api_key, self.base_url),
CompletionClient(self.api_key, self.base_url),
WorkflowClient(self.api_key, self.base_url),
WorkspaceClient(self.api_key, self.base_url),
KnowledgeBaseClient(self.api_key, self.base_url),
]
# Verify httpx.Client was called for each client
self.assertEqual(mock_httpx_client.call_count, 6)
# Clean up
for client in clients:
client.close()
@patch("dify_client.client.httpx.Client")
def test_json_parameter_handling(self, mock_httpx_client):
"""Test that json parameter is passed correctly."""
mock_response = Mock()
mock_response.json.return_value = {"result": "success"}
mock_client_instance = Mock()
mock_client_instance.request.return_value = mock_response
mock_httpx_client.return_value = mock_client_instance
client = DifyClient(self.api_key, self.base_url)
test_data = {"key": "value", "number": 123}
client._send_request("POST", "/test", json=test_data)
# Verify json parameter was passed
call_args = mock_client_instance.request.call_args
self.assertEqual(call_args[1]["json"], test_data)
client.close()
@patch("dify_client.client.httpx.Client")
def test_params_parameter_handling(self, mock_httpx_client):
"""Test that params parameter is passed correctly."""
mock_response = Mock()
mock_response.json.return_value = {"result": "success"}
mock_client_instance = Mock()
mock_client_instance.request.return_value = mock_response
mock_httpx_client.return_value = mock_client_instance
client = DifyClient(self.api_key, self.base_url)
test_params = {"page": 1, "limit": 20}
client._send_request("GET", "/test", params=test_params)
# Verify params parameter was passed
call_args = mock_client_instance.request.call_args
self.assertEqual(call_args[1]["params"], test_params)
client.close()
@patch("dify_client.client.httpx.Client")
def test_inheritance_chain(self, mock_httpx_client):
"""Test that inheritance chain is maintained."""
mock_client_instance = Mock()
mock_httpx_client.return_value = mock_client_instance
# ChatClient inherits from DifyClient
chat_client = ChatClient(self.api_key, self.base_url)
self.assertIsInstance(chat_client, DifyClient)
# CompletionClient inherits from DifyClient
completion_client = CompletionClient(self.api_key, self.base_url)
self.assertIsInstance(completion_client, DifyClient)
# WorkflowClient inherits from DifyClient
workflow_client = WorkflowClient(self.api_key, self.base_url)
self.assertIsInstance(workflow_client, DifyClient)
# Clean up
chat_client.close()
completion_client.close()
workflow_client.close()
@patch("dify_client.client.httpx.Client")
def test_nested_context_managers(self, mock_httpx_client):
"""Test nested context managers work correctly."""
mock_client_instance = Mock()
mock_httpx_client.return_value = mock_client_instance
with DifyClient(self.api_key, self.base_url) as client1:
with ChatClient(self.api_key, self.base_url) as client2:
self.assertEqual(client1.api_key, self.api_key)
self.assertEqual(client2.api_key, self.api_key)
# Both close methods should have been called
self.assertEqual(mock_client_instance.close.call_count, 2)
class TestChatClientHttpx(unittest.TestCase):
"""Test ChatClient specific httpx integration."""
@patch("dify_client.client.httpx.Client")
def test_create_chat_message_httpx(self, mock_httpx_client):
"""Test create_chat_message works with httpx."""
mock_response = Mock()
mock_response.text = '{"answer": "Hello!"}'
mock_response.json.return_value = {"answer": "Hello!"}
mock_response.status_code = 200
mock_client_instance = Mock()
mock_client_instance.request.return_value = mock_response
mock_httpx_client.return_value = mock_client_instance
with ChatClient("test-key") as client:
response = client.create_chat_message({}, "Hi", "user123")
self.assertIn("answer", response.text)
self.assertEqual(response.json()["answer"], "Hello!")
class TestCompletionClientHttpx(unittest.TestCase):
"""Test CompletionClient specific httpx integration."""
@patch("dify_client.client.httpx.Client")
def test_create_completion_message_httpx(self, mock_httpx_client):
"""Test create_completion_message works with httpx."""
mock_response = Mock()
mock_response.text = '{"answer": "Response"}'
mock_response.json.return_value = {"answer": "Response"}
mock_response.status_code = 200
mock_client_instance = Mock()
mock_client_instance.request.return_value = mock_response
mock_httpx_client.return_value = mock_client_instance
with CompletionClient("test-key") as client:
response = client.create_completion_message({"query": "test"}, "blocking", "user123")
self.assertIn("answer", response.text)
class TestKnowledgeBaseClientHttpx(unittest.TestCase):
"""Test KnowledgeBaseClient specific httpx integration."""
@patch("dify_client.client.httpx.Client")
def test_list_datasets_httpx(self, mock_httpx_client):
"""Test list_datasets works with httpx."""
mock_response = Mock()
mock_response.json.return_value = {"data": [], "total": 0}
mock_response.status_code = 200
mock_client_instance = Mock()
mock_client_instance.request.return_value = mock_response
mock_httpx_client.return_value = mock_client_instance
with KnowledgeBaseClient("test-key") as client:
response = client.list_datasets()
data = response.json()
self.assertIn("data", data)
self.assertIn("total", data)
class TestWorkflowClientHttpx(unittest.TestCase):
"""Test WorkflowClient specific httpx integration."""
@patch("dify_client.client.httpx.Client")
def test_run_workflow_httpx(self, mock_httpx_client):
"""Test run workflow works with httpx."""
mock_response = Mock()
mock_response.json.return_value = {"result": "success"}
mock_response.status_code = 200
mock_client_instance = Mock()
mock_client_instance.request.return_value = mock_response
mock_httpx_client.return_value = mock_client_instance
with WorkflowClient("test-key") as client:
response = client.run({"input": "test"}, "blocking", "user123")
self.assertEqual(response.json()["result"], "success")
class TestWorkspaceClientHttpx(unittest.TestCase):
"""Test WorkspaceClient specific httpx integration."""
@patch("dify_client.client.httpx.Client")
def test_get_available_models_httpx(self, mock_httpx_client):
"""Test get_available_models works with httpx."""
mock_response = Mock()
mock_response.json.return_value = {"data": []}
mock_response.status_code = 200
mock_client_instance = Mock()
mock_client_instance.request.return_value = mock_response
mock_httpx_client.return_value = mock_client_instance
with WorkspaceClient("test-key") as client:
response = client.get_available_models("llm")
self.assertIn("data", response.json())
if __name__ == "__main__":
unittest.main()