compliance/tests/test_api_caller.py
2025-05-16 15:18:02 +08:00

219 lines
7.4 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
API Caller Test Module
Unit tests for ddms_compliance_suite.api_caller.caller module,
validates API request and response handling.
"""
import os
import sys
import unittest
import json
from unittest import mock
from pathlib import Path
# Add project root to Python path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from ddms_compliance_suite.api_caller.caller import APICaller, APIRequest, APIResponse
class MockResponse:
"""Mock class for requests response"""
def __init__(self, json_data, status_code, headers=None, content=None, elapsed_seconds=0.1):
self.json_data = json_data
self.status_code = status_code
self.headers = headers or {"Content-Type": "application/json"}
self.content = content or json.dumps(json_data).encode('utf-8')
self.elapsed = mock.Mock()
self.elapsed.total_seconds.return_value = elapsed_seconds
def json(self):
return self.json_data
def raise_for_status(self):
if self.status_code >= 400:
from requests.exceptions import HTTPError
raise HTTPError(f"HTTP Error: {self.status_code}")
class TestAPICaller(unittest.TestCase):
"""API Caller Test Class"""
def setUp(self):
"""Setup before tests"""
self.api_caller = APICaller(
default_timeout=30, # Match the default timeout in the actual implementation
default_headers={"X-Test": "test-value"}
)
@mock.patch('requests.request')
def test_successful_get_request(self, mock_request):
"""Test successful GET request"""
# Set mock return value
mock_response = MockResponse(
json_data={"message": "success", "data": {"id": 1, "name": "Test Project"}},
status_code=200
)
mock_request.return_value = mock_response
# Create request object
request = APIRequest(
method="GET",
url="https://api.example.com/test",
params={"id": 1}
)
# Execute request
response = self.api_caller.call_api(request)
# Validate
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json_content["message"], "success")
self.assertEqual(response.json_content["data"]["name"], "Test Project")
# Validate mock call with proper timeout
mock_request.assert_called_once_with(
method="GET",
url="https://api.example.com/test",
headers={"X-Test": "test-value"},
params={"id": 1},
json=None,
data=None,
timeout=30 # Default timeout from the implementation
)
@mock.patch('requests.request')
def test_successful_post_request(self, mock_request):
"""Test successful POST request"""
# Set mock return value
mock_response = MockResponse(
json_data={"message": "created", "id": 123},
status_code=201
)
mock_request.return_value = mock_response
# Create request object
request = APIRequest(
method="POST",
url="https://api.example.com/create",
json_data={"name": "New Test Project", "description": "Test Description"},
headers={"Content-Type": "application/json"}
)
# Execute request
response = self.api_caller.call_api(request)
# Validate
self.assertEqual(response.status_code, 201)
self.assertEqual(response.json_content["message"], "created")
self.assertEqual(response.json_content["id"], 123)
# Validate mock call with proper timeout
mock_request.assert_called_once_with(
method="POST",
url="https://api.example.com/create",
headers={"X-Test": "test-value", "Content-Type": "application/json"},
params=None,
json={"name": "New Test Project", "description": "Test Description"},
data=None,
timeout=30 # Default timeout from the implementation
)
@mock.patch('requests.request')
def test_error_request(self, mock_request):
"""Test request failure case"""
# Set mock to raise exception
from requests.exceptions import HTTPError
mock_response = mock.Mock()
mock_response.status_code = 404
mock_response.headers = {"Content-Type": "application/json"}
mock_response.content = b"Not Found"
# Create a proper HTTPError with response attached
http_error = HTTPError("404 Client Error")
http_error.response = mock_response
mock_request.side_effect = http_error
# Create request object
request = APIRequest(
method="GET",
url="https://api.example.com/nonexistent"
)
# Execute request
response = self.api_caller.call_api(request)
# Validate
self.assertEqual(response.status_code, 404)
self.assertIsNone(response.json_content)
@mock.patch('requests.request')
def test_non_json_response(self, mock_request):
"""Test non-JSON response"""
# Create a real mock for requests.request
content = b"Non-JSON content"
mock_response = mock.Mock()
mock_response.status_code = 200
mock_response.headers = {"Content-Type": "text/plain"}
mock_response.content = content
mock_response.elapsed.total_seconds.return_value = 0.1
# Setup the JSON decode error when json() is called
from requests.exceptions import JSONDecodeError
mock_response.json.side_effect = JSONDecodeError("Invalid JSON", "", 0)
# Set the mock response for the request
mock_request.return_value = mock_response
# Create request object
request = APIRequest(
method="GET",
url="https://api.example.com/text"
)
# Execute request
response = self.api_caller.call_api(request)
# Validate
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Non-JSON content")
self.assertIsNone(response.json_content)
@mock.patch('requests.request')
def test_custom_timeout(self, mock_request):
"""Test custom timeout setting"""
# Set mock return value
mock_response = MockResponse(
json_data={"message": "success"},
status_code=200
)
mock_request.return_value = mock_response
# Create request object with custom timeout
request = APIRequest(
method="GET",
url="https://api.example.com/slow",
timeout=10
)
# Execute request
response = self.api_caller.call_api(request)
# Validate
self.assertEqual(response.status_code, 200)
# Validate mock call used custom timeout
mock_request.assert_called_once_with(
method="GET",
url="https://api.example.com/slow",
headers={"X-Test": "test-value"},
params=None,
json=None,
data=None,
timeout=10 # Custom timeout specified in the request
)
if __name__ == "__main__":
unittest.main()