feat: Airwallex 发卡管理后台完整实现
- 后端: FastAPI + SQLAlchemy + SQLite, JWT认证, 代理支持的AirwallexClient - 前端: React 18 + Vite + Ant Design 5, 中文界面 - 功能: 卡片管理, 持卡人管理, 交易记录, API令牌, 系统设置, 审计日志 - 第三方API: X-API-Key认证, 权限控制 - Docker部署: docker-compose编排前后端
This commit is contained in:
249
airwallex-sdk/tests/test_exceptions.py
Normal file
249
airwallex-sdk/tests/test_exceptions.py
Normal file
@@ -0,0 +1,249 @@
|
||||
"""
|
||||
Tests for the Airwallex SDK exception handling.
|
||||
"""
|
||||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
import json
|
||||
|
||||
from airwallex.exceptions import (
|
||||
AirwallexAPIError,
|
||||
AuthenticationError,
|
||||
RateLimitError,
|
||||
ResourceNotFoundError,
|
||||
ValidationError,
|
||||
ServerError,
|
||||
ResourceExistsError,
|
||||
AmountLimitError,
|
||||
EditForbiddenError,
|
||||
CurrencyError,
|
||||
DateError,
|
||||
TransferMethodError,
|
||||
ConversionError,
|
||||
ServiceUnavailableError,
|
||||
create_exception_from_response
|
||||
)
|
||||
|
||||
|
||||
class TestExceptions(unittest.TestCase):
|
||||
"""Tests for the exception handling system."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.mock_response = MagicMock()
|
||||
self.method = "GET"
|
||||
self.url = "/api/v1/test"
|
||||
self.kwargs = {"headers": {"Authorization": "Bearer test_token"}}
|
||||
|
||||
def test_exception_from_status_code(self):
|
||||
"""Test creating exceptions based only on status code."""
|
||||
# 401 should create AuthenticationError
|
||||
self.mock_response.status_code = 401
|
||||
self.mock_response.json.side_effect = ValueError("Invalid JSON")
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, AuthenticationError)
|
||||
self.assertEqual(exception.status_code, 401)
|
||||
|
||||
# 404 should create ResourceNotFoundError
|
||||
self.mock_response.status_code = 404
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, ResourceNotFoundError)
|
||||
self.assertEqual(exception.status_code, 404)
|
||||
|
||||
# 429 should create RateLimitError
|
||||
self.mock_response.status_code = 429
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, RateLimitError)
|
||||
self.assertEqual(exception.status_code, 429)
|
||||
|
||||
# 400 should create ValidationError
|
||||
self.mock_response.status_code = 400
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, ValidationError)
|
||||
self.assertEqual(exception.status_code, 400)
|
||||
|
||||
# 500 should create ServerError
|
||||
self.mock_response.status_code = 500
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, ServerError)
|
||||
self.assertEqual(exception.status_code, 500)
|
||||
|
||||
def test_exception_from_error_code(self):
|
||||
"""Test creating exceptions based on error code."""
|
||||
self.mock_response.status_code = 400
|
||||
|
||||
# Test credentials_invalid (should be AuthenticationError)
|
||||
self.mock_response.json.return_value = {
|
||||
"code": "credentials_invalid",
|
||||
"message": "Invalid credentials",
|
||||
"source": "api_key"
|
||||
}
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, AuthenticationError)
|
||||
self.assertEqual(exception.error_code, "credentials_invalid")
|
||||
self.assertEqual(exception.error_source, "api_key")
|
||||
|
||||
# Test already_exists (should be ResourceExistsError)
|
||||
self.mock_response.json.return_value = {
|
||||
"code": "already_exists",
|
||||
"message": "Resource already exists",
|
||||
"source": "id"
|
||||
}
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, ResourceExistsError)
|
||||
self.assertEqual(exception.error_code, "already_exists")
|
||||
|
||||
# Test amount_above_limit (should be AmountLimitError)
|
||||
self.mock_response.json.return_value = {
|
||||
"code": "amount_above_limit",
|
||||
"message": "Amount exceeds the maximum limit",
|
||||
"source": "amount"
|
||||
}
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, AmountLimitError)
|
||||
self.assertEqual(exception.error_code, "amount_above_limit")
|
||||
|
||||
# Test invalid_currency_pair (should be CurrencyError)
|
||||
self.mock_response.json.return_value = {
|
||||
"code": "invalid_currency_pair",
|
||||
"message": "The currency pair is invalid",
|
||||
"source": "currency_pair"
|
||||
}
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, CurrencyError)
|
||||
self.assertEqual(exception.error_code, "invalid_currency_pair")
|
||||
|
||||
# Test invalid_transfer_date (should be DateError)
|
||||
self.mock_response.json.return_value = {
|
||||
"code": "invalid_transfer_date",
|
||||
"message": "The transfer date is invalid",
|
||||
"source": "transfer_date"
|
||||
}
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, DateError)
|
||||
self.assertEqual(exception.error_code, "invalid_transfer_date")
|
||||
|
||||
# Test service_unavailable (should be ServiceUnavailableError)
|
||||
self.mock_response.status_code = 503
|
||||
self.mock_response.json.return_value = {
|
||||
"code": "service_unavailable",
|
||||
"message": "The service is currently unavailable",
|
||||
}
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
self.assertIsInstance(exception, ServiceUnavailableError)
|
||||
self.assertEqual(exception.error_code, "service_unavailable")
|
||||
|
||||
def test_exception_string_representation(self):
|
||||
"""Test the string representation of exceptions."""
|
||||
self.mock_response.status_code = 400
|
||||
self.mock_response.json.return_value = {
|
||||
"code": "invalid_argument",
|
||||
"message": "The argument is invalid",
|
||||
"source": "amount"
|
||||
}
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
expected_str = "Airwallex API Error (HTTP 400): [invalid_argument] The argument is invalid (source: amount) for GET /api/v1/test"
|
||||
self.assertEqual(str(exception), expected_str)
|
||||
|
||||
# Test without source
|
||||
self.mock_response.json.return_value = {
|
||||
"code": "invalid_argument",
|
||||
"message": "The argument is invalid"
|
||||
}
|
||||
|
||||
exception = create_exception_from_response(
|
||||
response=self.mock_response,
|
||||
method=self.method,
|
||||
url=self.url,
|
||||
kwargs=self.kwargs
|
||||
)
|
||||
|
||||
expected_str = "Airwallex API Error (HTTP 400): [invalid_argument] The argument is invalid for GET /api/v1/test"
|
||||
self.assertEqual(str(exception), expected_str)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user