- has now ErrorResponse return values instead of None on errors - changed parameter cafile to ca_file and its position in the init method - Proxy has ProxyConfig Typed Dict format Tests updates to reflect those changes
848 lines
31 KiB
Python
848 lines
31 KiB
Python
"""
|
|
PyTest: requests_handling/caller
|
|
"""
|
|
|
|
from unittest.mock import Mock, patch
|
|
import pytest
|
|
import requests
|
|
from corelibs.requests_handling.caller import Caller, ErrorResponse, ProxyConfig
|
|
|
|
|
|
class TestCallerInit:
|
|
"""Tests for Caller initialization"""
|
|
|
|
def test_init_with_required_params_only(self):
|
|
"""Test Caller initialization with only required parameters"""
|
|
header = {"Authorization": "Bearer token"}
|
|
caller = Caller(header=header)
|
|
|
|
assert caller.headers == header
|
|
assert caller.timeout == 20
|
|
assert caller.verify is True
|
|
assert caller.proxy is None
|
|
assert caller.ca_file is None
|
|
|
|
def test_init_with_all_params(self):
|
|
"""Test Caller initialization with all parameters"""
|
|
header = {"Authorization": "Bearer token", "Content-Type": "application/json"}
|
|
proxy: ProxyConfig = {
|
|
"type": "socks5",
|
|
"host": "proxy.example.com:8080",
|
|
"port": "8080"
|
|
}
|
|
caller = Caller(header=header, timeout=30, proxy=proxy, verify=False)
|
|
|
|
assert caller.headers == header
|
|
assert caller.timeout == 30
|
|
assert caller.verify is False
|
|
assert caller.proxy == proxy
|
|
|
|
def test_init_with_empty_header(self):
|
|
"""Test Caller initialization with empty header"""
|
|
caller = Caller(header={})
|
|
|
|
assert caller.headers == {}
|
|
assert caller.timeout == 20
|
|
|
|
def test_init_custom_timeout(self):
|
|
"""Test Caller initialization with custom timeout"""
|
|
caller = Caller(header={}, timeout=60)
|
|
|
|
assert caller.timeout == 60
|
|
|
|
def test_init_verify_false(self):
|
|
"""Test Caller initialization with verify=False"""
|
|
caller = Caller(header={}, verify=False)
|
|
|
|
assert caller.verify is False
|
|
|
|
def test_init_with_ca_file(self):
|
|
"""Test Caller initialization with ca_file parameter"""
|
|
ca_file_path = "/path/to/ca/cert.pem"
|
|
caller = Caller(header={}, ca_file=ca_file_path)
|
|
|
|
assert caller.ca_file == ca_file_path
|
|
|
|
|
|
class TestCallerGet:
|
|
"""Tests for Caller.get method"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_get_basic(self, mock_get: Mock):
|
|
"""Test basic GET request"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_response.status_code = 200
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={"Authorization": "Bearer token"})
|
|
response = caller.get("https://api.example.com/data")
|
|
|
|
assert response == mock_response
|
|
mock_get.assert_called_once_with(
|
|
"https://api.example.com/data",
|
|
params=None,
|
|
headers={"Authorization": "Bearer token"},
|
|
timeout=20,
|
|
verify=True,
|
|
proxies=None,
|
|
cert=None
|
|
)
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_get_with_params(self, mock_get: Mock):
|
|
"""Test GET request with query parameters"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
params = {"page": 1, "limit": 10}
|
|
response = caller.get("https://api.example.com/data", params=params)
|
|
|
|
assert response == mock_response
|
|
mock_get.assert_called_once_with(
|
|
"https://api.example.com/data",
|
|
params=params,
|
|
headers={},
|
|
timeout=20,
|
|
verify=True,
|
|
proxies=None,
|
|
cert=None
|
|
)
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_get_with_custom_timeout(self, mock_get: Mock):
|
|
"""Test GET request uses default timeout from instance"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={}, timeout=45)
|
|
caller.get("https://api.example.com/data")
|
|
|
|
mock_get.assert_called_once()
|
|
assert mock_get.call_args[1]["timeout"] == 45
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_get_with_verify_false(self, mock_get: Mock):
|
|
"""Test GET request with verify=False"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={}, verify=False)
|
|
caller.get("https://api.example.com/data")
|
|
|
|
mock_get.assert_called_once()
|
|
assert mock_get.call_args[1]["verify"] is False
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_get_with_proxy(self, mock_get: Mock):
|
|
"""Test GET request with proxy"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
proxy: ProxyConfig = {
|
|
"type": "socks5",
|
|
"host": "proxy.example.com:8080",
|
|
"port": "8080"
|
|
}
|
|
caller = Caller(header={}, proxy=proxy)
|
|
caller.get("https://api.example.com/data")
|
|
|
|
mock_get.assert_called_once()
|
|
assert mock_get.call_args[1]["proxies"] == proxy
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_get_invalid_schema_returns_none(self, mock_get: Mock):
|
|
"""Test GET request with invalid URL schema returns ErrorResponse"""
|
|
mock_get.side_effect = requests.exceptions.InvalidSchema("Invalid URL")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.get("invalid://example.com")
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert response.code == 200
|
|
assert "Invalid URL during 'get'" in response.message
|
|
assert response.action == "get"
|
|
assert response.url == "invalid://example.com"
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_get_timeout_returns_none(self, mock_get: Mock):
|
|
"""Test GET request timeout returns ErrorResponse"""
|
|
mock_get.side_effect = requests.exceptions.ReadTimeout("Timeout")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.get("https://api.example.com/data")
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert response.code == 300
|
|
assert "Timeout (20s) during 'get'" in response.message
|
|
assert response.action == "get"
|
|
assert response.url == "https://api.example.com/data"
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_get_connection_error_returns_none(self, mock_get: Mock):
|
|
"""Test GET request connection error returns ErrorResponse"""
|
|
mock_get.side_effect = requests.exceptions.ConnectionError("Connection failed")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.get("https://api.example.com/data")
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert response.code == 400
|
|
assert "Connection error during 'get'" in response.message
|
|
assert response.action == "get"
|
|
assert response.url == "https://api.example.com/data"
|
|
|
|
|
|
class TestCallerPost:
|
|
"""Tests for Caller.post method"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_post_basic(self, mock_post: Mock):
|
|
"""Test basic POST request"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_response.status_code = 201
|
|
mock_post.return_value = mock_response
|
|
|
|
caller = Caller(header={"Content-Type": "application/json"})
|
|
data = {"name": "test", "value": 123}
|
|
response = caller.post("https://api.example.com/data", data=data)
|
|
|
|
assert response == mock_response
|
|
mock_post.assert_called_once_with(
|
|
"https://api.example.com/data",
|
|
params=None,
|
|
json=data,
|
|
headers={"Content-Type": "application/json"},
|
|
timeout=20,
|
|
verify=True,
|
|
proxies=None,
|
|
cert=None
|
|
)
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_post_without_data(self, mock_post: Mock):
|
|
"""Test POST request without data"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_post.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
response = caller.post("https://api.example.com/data")
|
|
|
|
assert response == mock_response
|
|
mock_post.assert_called_once()
|
|
# Data defaults to None, which becomes {} in __call
|
|
assert mock_post.call_args[1]["json"] == {}
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_post_with_params(self, mock_post: Mock):
|
|
"""Test POST request with query parameters"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_post.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
data = {"key": "value"}
|
|
params = {"version": "v1"}
|
|
response = caller.post("https://api.example.com/data", data=data, params=params)
|
|
|
|
assert response == mock_response
|
|
mock_post.assert_called_once()
|
|
assert mock_post.call_args[1]["params"] == params
|
|
assert mock_post.call_args[1]["json"] == data
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_post_invalid_schema_returns_none(self, mock_post: Mock):
|
|
"""Test POST request with invalid URL schema returns ErrorResponse"""
|
|
mock_post.side_effect = requests.exceptions.InvalidSchema("Invalid URL")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.post("invalid://example.com", data={"test": "data"})
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert response.code == 200
|
|
assert "Invalid URL during 'post'" in response.message
|
|
assert response.action == "post"
|
|
assert response.url == "invalid://example.com"
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_post_timeout_returns_none(self, mock_post: Mock):
|
|
"""Test POST request timeout returns ErrorResponse"""
|
|
mock_post.side_effect = requests.exceptions.ReadTimeout("Timeout")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.post("https://api.example.com/data", data={"test": "data"})
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert response.code == 300
|
|
assert "Timeout (20s) during 'post'" in response.message
|
|
assert response.action == "post"
|
|
assert response.url == "https://api.example.com/data"
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_post_connection_error_returns_none(self, mock_post: Mock):
|
|
"""Test POST request connection error returns ErrorResponse"""
|
|
mock_post.side_effect = requests.exceptions.ConnectionError("Connection failed")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.post("https://api.example.com/data", data={"test": "data"})
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert response.code == 400
|
|
assert "Connection error during 'post'" in response.message
|
|
assert response.action == "post"
|
|
assert response.url == "https://api.example.com/data"
|
|
|
|
|
|
class TestCallerPut:
|
|
"""Tests for Caller.put method"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.put')
|
|
def test_put_basic(self, mock_put: Mock):
|
|
"""Test basic PUT request"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_response.status_code = 200
|
|
mock_put.return_value = mock_response
|
|
|
|
caller = Caller(header={"Content-Type": "application/json"})
|
|
data = {"id": 1, "name": "updated"}
|
|
response = caller.put("https://api.example.com/data/1", data=data)
|
|
|
|
assert response == mock_response
|
|
mock_put.assert_called_once_with(
|
|
"https://api.example.com/data/1",
|
|
params=None,
|
|
json=data,
|
|
headers={"Content-Type": "application/json"},
|
|
timeout=20,
|
|
verify=True,
|
|
proxies=None,
|
|
cert=None
|
|
)
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.put')
|
|
def test_put_with_params(self, mock_put: Mock):
|
|
"""Test PUT request with query parameters"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_put.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
data = {"name": "test"}
|
|
params = {"force": "true"}
|
|
response = caller.put("https://api.example.com/data/1", data=data, params=params)
|
|
|
|
assert response == mock_response
|
|
mock_put.assert_called_once()
|
|
assert mock_put.call_args[1]["params"] == params
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.put')
|
|
def test_put_timeout_returns_none(self, mock_put: Mock):
|
|
"""Test PUT request timeout returns ErrorResponse"""
|
|
mock_put.side_effect = requests.exceptions.ReadTimeout("Timeout")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.put("https://api.example.com/data/1", data={"test": "data"})
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert response.code == 300
|
|
assert "Timeout (20s) during 'put'" in response.message
|
|
assert response.action == "put"
|
|
assert response.url == "https://api.example.com/data/1"
|
|
|
|
|
|
class TestCallerPatch:
|
|
"""Tests for Caller.patch method"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.patch')
|
|
def test_patch_basic(self, mock_patch: Mock):
|
|
"""Test basic PATCH request"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_response.status_code = 200
|
|
mock_patch.return_value = mock_response
|
|
|
|
caller = Caller(header={"Content-Type": "application/json"})
|
|
data = {"status": "active"}
|
|
response = caller.patch("https://api.example.com/data/1", data=data)
|
|
|
|
assert response == mock_response
|
|
mock_patch.assert_called_once_with(
|
|
"https://api.example.com/data/1",
|
|
params=None,
|
|
json=data,
|
|
headers={"Content-Type": "application/json"},
|
|
timeout=20,
|
|
verify=True,
|
|
proxies=None,
|
|
cert=None
|
|
)
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.patch')
|
|
def test_patch_with_params(self, mock_patch: Mock):
|
|
"""Test PATCH request with query parameters"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_patch.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
data = {"field": "value"}
|
|
params = {"notify": "false"}
|
|
response = caller.patch("https://api.example.com/data/1", data=data, params=params)
|
|
|
|
assert response == mock_response
|
|
mock_patch.assert_called_once()
|
|
assert mock_patch.call_args[1]["params"] == params
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.patch')
|
|
def test_patch_connection_error_returns_none(self, mock_patch: Mock):
|
|
"""Test PATCH request connection error returns ErrorResponse"""
|
|
mock_patch.side_effect = requests.exceptions.ConnectionError("Connection failed")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.patch("https://api.example.com/data/1", data={"test": "data"})
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert response.code == 400
|
|
assert "Connection error during 'patch'" in response.message
|
|
assert response.action == "patch"
|
|
assert response.url == "https://api.example.com/data/1"
|
|
|
|
|
|
class TestCallerDelete:
|
|
"""Tests for Caller.delete method"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.delete')
|
|
def test_delete_basic(self, mock_delete: Mock):
|
|
"""Test basic DELETE request"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_response.status_code = 204
|
|
mock_delete.return_value = mock_response
|
|
|
|
caller = Caller(header={"Authorization": "Bearer token"})
|
|
response = caller.delete("https://api.example.com/data/1")
|
|
|
|
assert response == mock_response
|
|
mock_delete.assert_called_once_with(
|
|
"https://api.example.com/data/1",
|
|
params=None,
|
|
headers={"Authorization": "Bearer token"},
|
|
timeout=20,
|
|
verify=True,
|
|
proxies=None,
|
|
cert=None
|
|
)
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.delete')
|
|
def test_delete_with_params(self, mock_delete: Mock):
|
|
"""Test DELETE request with query parameters"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_delete.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
params = {"force": "true"}
|
|
response = caller.delete("https://api.example.com/data/1", params=params)
|
|
|
|
assert response == mock_response
|
|
mock_delete.assert_called_once()
|
|
assert mock_delete.call_args[1]["params"] == params
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.delete')
|
|
def test_delete_invalid_schema_returns_none(self, mock_delete: Mock):
|
|
"""Test DELETE request with invalid URL schema returns ErrorResponse"""
|
|
mock_delete.side_effect = requests.exceptions.InvalidSchema("Invalid URL")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.delete("invalid://example.com/data/1")
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert response.code == 200
|
|
assert "Invalid URL during 'delete'" in response.message
|
|
assert response.action == "delete"
|
|
assert response.url == "invalid://example.com/data/1"
|
|
|
|
|
|
class TestCallerParametrized:
|
|
"""Parametrized tests for all HTTP methods"""
|
|
|
|
@pytest.mark.parametrize("method,http_method", [
|
|
("get", "get"),
|
|
("post", "post"),
|
|
("put", "put"),
|
|
("patch", "patch"),
|
|
("delete", "delete"),
|
|
])
|
|
@patch('corelibs.requests_handling.caller.requests')
|
|
def test_all_methods_use_correct_headers(self, mock_requests: Mock, method: str, http_method: str):
|
|
"""Test that all HTTP methods use the headers correctly"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_http_method = getattr(mock_requests, http_method)
|
|
mock_http_method.return_value = mock_response
|
|
|
|
headers = {"Authorization": "Bearer token", "X-Custom": "value"}
|
|
caller = Caller(header=headers)
|
|
|
|
# Call the method
|
|
caller_method = getattr(caller, method)
|
|
if method in ["get", "delete"]:
|
|
caller_method("https://api.example.com/data")
|
|
else:
|
|
caller_method("https://api.example.com/data", data={"key": "value"})
|
|
|
|
# Verify headers were passed
|
|
mock_http_method.assert_called_once()
|
|
assert mock_http_method.call_args[1]["headers"] == headers
|
|
|
|
@pytest.mark.parametrize("method,http_method", [
|
|
("get", "get"),
|
|
("post", "post"),
|
|
("put", "put"),
|
|
("patch", "patch"),
|
|
("delete", "delete"),
|
|
])
|
|
@patch('corelibs.requests_handling.caller.requests')
|
|
def test_all_methods_use_timeout(self, mock_requests: Mock, method: str, http_method: str):
|
|
"""Test that all HTTP methods use the timeout correctly"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_http_method = getattr(mock_requests, http_method)
|
|
mock_http_method.return_value = mock_response
|
|
|
|
timeout = 45
|
|
caller = Caller(header={}, timeout=timeout)
|
|
|
|
# Call the method
|
|
caller_method = getattr(caller, method)
|
|
if method in ["get", "delete"]:
|
|
caller_method("https://api.example.com/data")
|
|
else:
|
|
caller_method("https://api.example.com/data", data={"key": "value"})
|
|
|
|
# Verify timeout was passed
|
|
mock_http_method.assert_called_once()
|
|
assert mock_http_method.call_args[1]["timeout"] == timeout
|
|
|
|
@pytest.mark.parametrize("exception_class,expected_message", [
|
|
(requests.exceptions.InvalidSchema, "Invalid URL during"),
|
|
(requests.exceptions.ReadTimeout, "Timeout"),
|
|
(requests.exceptions.ConnectionError, "Connection error during"),
|
|
])
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_exception_handling(
|
|
self, mock_get: Mock, exception_class: type, expected_message: str
|
|
):
|
|
"""Test exception handling for all exception types"""
|
|
mock_get.side_effect = exception_class("Test error")
|
|
|
|
caller = Caller(header={})
|
|
response = caller.get("https://api.example.com/data")
|
|
|
|
assert isinstance(response, ErrorResponse)
|
|
assert expected_message in response.message
|
|
|
|
|
|
class TestCallerIntegration:
|
|
"""Integration tests for Caller"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests')
|
|
def test_multiple_requests_maintain_state(self, mock_requests: Mock):
|
|
"""Test that multiple requests maintain caller state"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_requests.get.return_value = mock_response
|
|
mock_requests.post.return_value = mock_response
|
|
|
|
headers = {"Authorization": "Bearer token"}
|
|
caller = Caller(header=headers, timeout=30, verify=False)
|
|
|
|
# Make multiple requests
|
|
caller.get("https://api.example.com/data1")
|
|
caller.post("https://api.example.com/data2", data={"key": "value"})
|
|
|
|
# Verify both used same configuration
|
|
assert mock_requests.get.call_args[1]["headers"] == headers
|
|
assert mock_requests.get.call_args[1]["timeout"] == 30
|
|
assert mock_requests.get.call_args[1]["verify"] is False
|
|
|
|
assert mock_requests.post.call_args[1]["headers"] == headers
|
|
assert mock_requests.post.call_args[1]["timeout"] == 30
|
|
assert mock_requests.post.call_args[1]["verify"] is False
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_post_with_complex_data(self, mock_post: Mock):
|
|
"""Test POST request with complex nested data"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_post.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
complex_data = {
|
|
"user": {
|
|
"name": "John Doe",
|
|
"email": "john@example.com",
|
|
"preferences": {
|
|
"notifications": True,
|
|
"theme": "dark"
|
|
}
|
|
},
|
|
"tags": ["important", "urgent"],
|
|
"count": 42
|
|
}
|
|
response = caller.post("https://api.example.com/users", data=complex_data)
|
|
|
|
assert response == mock_response
|
|
mock_post.assert_called_once()
|
|
assert mock_post.call_args[1]["json"] == complex_data
|
|
|
|
@patch('corelibs.requests_handling.caller.requests')
|
|
def test_all_http_methods_work_together(self, mock_requests: Mock):
|
|
"""Test that all HTTP methods can be used with the same Caller instance"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
for method in ['get', 'post', 'put', 'patch', 'delete']:
|
|
getattr(mock_requests, method).return_value = mock_response
|
|
|
|
caller = Caller(header={"Authorization": "Bearer token"})
|
|
|
|
# Test all methods
|
|
caller.get("https://api.example.com/data")
|
|
caller.post("https://api.example.com/data", data={"new": "data"})
|
|
caller.put("https://api.example.com/data/1", data={"updated": "data"})
|
|
caller.patch("https://api.example.com/data/1", data={"field": "value"})
|
|
caller.delete("https://api.example.com/data/1")
|
|
|
|
# Verify all were called
|
|
mock_requests.get.assert_called_once()
|
|
mock_requests.post.assert_called_once()
|
|
mock_requests.put.assert_called_once()
|
|
mock_requests.patch.assert_called_once()
|
|
mock_requests.delete.assert_called_once()
|
|
|
|
|
|
class TestCallerEdgeCases:
|
|
"""Edge case tests for Caller"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_empty_url(self, mock_get: Mock):
|
|
"""Test with empty URL"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
response = caller.get("")
|
|
|
|
assert response == mock_response
|
|
mock_get.assert_called_once_with(
|
|
"",
|
|
params=None,
|
|
headers={},
|
|
timeout=20,
|
|
verify=True,
|
|
proxies=None,
|
|
cert=None
|
|
)
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_post_with_empty_data(self, mock_post: Mock):
|
|
"""Test POST with explicitly empty data dict"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_post.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
response = caller.post("https://api.example.com/data", data={})
|
|
|
|
assert response == mock_response
|
|
mock_post.assert_called_once()
|
|
assert mock_post.call_args[1]["json"] == {}
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_get_with_empty_params(self, mock_get: Mock):
|
|
"""Test GET with explicitly empty params dict"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
response = caller.get("https://api.example.com/data", params={})
|
|
|
|
assert response == mock_response
|
|
mock_get.assert_called_once()
|
|
assert mock_get.call_args[1]["params"] == {}
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_post_with_none_values_in_data(self, mock_post: Mock):
|
|
"""Test POST with None values in data"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_post.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
data = {"key1": None, "key2": "value", "key3": None}
|
|
response = caller.post("https://api.example.com/data", data=data)
|
|
|
|
assert response == mock_response
|
|
mock_post.assert_called_once()
|
|
assert mock_post.call_args[1]["json"] == data
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_very_long_url(self, mock_get: Mock):
|
|
"""Test with very long URL"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
long_url = "https://api.example.com/" + "a" * 1000
|
|
response = caller.get(long_url)
|
|
|
|
assert response == mock_response
|
|
mock_get.assert_called_once_with(
|
|
long_url,
|
|
params=None,
|
|
headers={},
|
|
timeout=20,
|
|
verify=True,
|
|
proxies=None,
|
|
cert=None
|
|
)
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_special_characters_in_url(self, mock_get: Mock):
|
|
"""Test URL with special characters"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
url = "https://api.example.com/data?query=test%20value&id=123"
|
|
response = caller.get(url)
|
|
|
|
assert response == mock_response
|
|
mock_get.assert_called_once_with(
|
|
url,
|
|
params=None,
|
|
headers={},
|
|
timeout=20,
|
|
verify=True,
|
|
proxies=None,
|
|
cert=None
|
|
)
|
|
|
|
def test_timeout_zero(self):
|
|
"""Test Caller with timeout of 0"""
|
|
caller = Caller(header={}, timeout=0)
|
|
assert caller.timeout == 0
|
|
|
|
def test_negative_timeout(self):
|
|
"""Test Caller with negative timeout"""
|
|
caller = Caller(header={}, timeout=-1)
|
|
assert caller.timeout == -1
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_unicode_in_headers(self, mock_get: Mock):
|
|
"""Test headers with unicode characters"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
headers = {"X-Custom": "测试", "Authorization": "Bearer token"}
|
|
caller = Caller(header=headers)
|
|
response = caller.get("https://api.example.com/data")
|
|
|
|
assert response == mock_response
|
|
mock_get.assert_called_once()
|
|
assert mock_get.call_args[1]["headers"] == headers
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_unicode_in_data(self, mock_post: Mock):
|
|
"""Test data with unicode characters"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_post.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
data = {"name": "用户", "message": "こんにちは", "emoji": "🚀"}
|
|
response = caller.post("https://api.example.com/data", data=data)
|
|
|
|
assert response == mock_response
|
|
mock_post.assert_called_once()
|
|
assert mock_post.call_args[1]["json"] == data
|
|
|
|
|
|
class TestCallerProxyHandling:
|
|
"""Tests for proxy handling"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_proxy_configuration(self, mock_get: Mock):
|
|
"""Test that proxy configuration is passed to requests"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
proxy: ProxyConfig = {
|
|
"type": "socks5",
|
|
"host": "proxy.example.com:8080",
|
|
"port": "8080"
|
|
}
|
|
caller = Caller(header={}, proxy=proxy)
|
|
caller.get("https://api.example.com/data")
|
|
|
|
mock_get.assert_called_once()
|
|
assert mock_get.call_args[1]["proxies"] == proxy
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.post')
|
|
def test_proxy_with_auth(self, mock_post: Mock):
|
|
"""Test proxy with authentication"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_post.return_value = mock_response
|
|
|
|
proxy: ProxyConfig = {
|
|
"type": "socks5",
|
|
"host": "proxy.example.com:8080",
|
|
"port": "8080"
|
|
}
|
|
caller = Caller(header={}, proxy=proxy)
|
|
caller.post("https://api.example.com/data", data={"test": "data"})
|
|
|
|
mock_post.assert_called_once()
|
|
assert mock_post.call_args[1]["proxies"] == proxy
|
|
|
|
|
|
class TestCallerTimeoutHandling:
|
|
"""Tests for timeout parameter handling"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_timeout_parameter_none_uses_default(self, mock_get: Mock):
|
|
"""Test that None timeout uses the instance default"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={}, timeout=30)
|
|
# The private __timeout method is called internally
|
|
caller.get("https://api.example.com/data")
|
|
|
|
mock_get.assert_called_once()
|
|
assert mock_get.call_args[1]["timeout"] == 30
|
|
|
|
|
|
class TestCallerResponseHandling:
|
|
"""Tests for response handling"""
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_response_object_returned_correctly(self, mock_get: Mock):
|
|
"""Test that response object is returned correctly"""
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_response.status_code = 200
|
|
mock_response.text = "Success"
|
|
mock_response.json.return_value = {"status": "ok"}
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
response = caller.get("https://api.example.com/data")
|
|
|
|
assert not isinstance(response, ErrorResponse)
|
|
assert response.status_code == 200
|
|
assert response.text == "Success"
|
|
assert response.json() == {"status": "ok"}
|
|
|
|
@patch('corelibs.requests_handling.caller.requests.get')
|
|
def test_response_with_different_status_codes(self, mock_get: Mock):
|
|
"""Test response handling with different status codes"""
|
|
for status_code in [200, 201, 204, 400, 401, 404, 500]:
|
|
mock_response = Mock(spec=requests.Response)
|
|
mock_response.status_code = status_code
|
|
mock_get.return_value = mock_response
|
|
|
|
caller = Caller(header={})
|
|
response = caller.get("https://api.example.com/data")
|
|
|
|
assert not isinstance(response, ErrorResponse)
|
|
assert response.status_code == status_code
|
|
|
|
|
|
# __END__
|