You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

273 lines
8.8 KiB

"""
星耀数智(AmazingData)集成测试
测试覆盖:
- API端点测试
- 数据一致性检查
- 多数据源对比
- 完整工作流测试
"""
import unittest
import asyncio
import sys
import os
import json
from datetime import datetime, timedelta
from unittest.mock import Mock, patch, AsyncMock
# 添加项目根目录到路径
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
class TestAPIEndpoints(unittest.TestCase):
"""测试API端点"""
def setUp(self):
"""测试前准备"""
self.base_url = 'http://localhost:8080/v1'
self.headers = {'X-Admin-Token': 'demo-api-key-2024'}
@patch('urllib.request.urlopen')
def test_adapters_endpoint(self, mock_urlopen):
"""测试适配器列表端点"""
# Mock响应
mock_response = Mock()
mock_response.read.return_value = json.dumps({
'code': 200,
'message': 'success',
'data': {
'adapters': [
{'name': 'amazingdata', 'status': 'active', 'type': 'stock'},
{'name': 'akshare', 'status': 'active', 'type': 'stock'}
]
}
}).encode()
mock_urlopen.return_value = mock_response
import urllib.request
req = urllib.request.Request(
f'{self.base_url}/admin/adapters',
headers=self.headers
)
response = urllib.request.urlopen(req, timeout=10)
data = json.loads(response.read().decode())
self.assertEqual(data['code'], 200)
self.assertIn('adapters', data['data'])
# 检查是否有amazingdata适配器
adapter_names = [a['name'] for a in data['data']['adapters']]
self.assertIn('amazingdata', adapter_names)
@patch('urllib.request.urlopen')
def test_source_status_endpoint(self, mock_urlopen):
"""测试数据源状态端点"""
mock_response = Mock()
mock_response.read.return_value = json.dumps({
'code': 200,
'message': 'success',
'data': {
'sources': [
{'id': 'amazingdata', 'name': '星耀数智', 'status': 'healthy'}
]
}
}).encode()
mock_urlopen.return_value = mock_response
import urllib.request
req = urllib.request.Request(
f'{self.base_url}/admin/source/status',
headers={'X-API-Key': 'demo-api-key-2024'}
)
response = urllib.request.urlopen(req, timeout=10)
data = json.loads(response.read().decode())
self.assertEqual(data['code'], 200)
class TestDataConsistency(unittest.TestCase):
"""测试数据一致性"""
def test_date_format_consistency(self):
"""测试日期格式一致性"""
from app.adapters.amazingdata_adapter import AmazingDataAdapter
adapter = AmazingDataAdapter()
# 测试不同格式的日期转换为统一格式
test_cases = [
('2024-01-01', 20240101),
('2024/01/01', 20240101),
('20240101', 20240101),
(20240101, 20240101),
]
for input_date, expected in test_cases:
result = adapter._format_date(input_date)
self.assertEqual(result, expected, f"Failed for {input_date}")
def test_symbol_format_consistency(self):
"""测试代码格式一致性"""
# 股票代码应该包含交易所后缀
valid_stock_codes = [
'000001.SZ',
'600000.SH',
'688001.SH',
'430001.BJ'
]
for code in valid_stock_codes:
# 检查格式: 代码.交易所
parts = code.split('.')
self.assertEqual(len(parts), 2, f"Invalid code format: {code}")
self.assertIn(parts[1], ['SH', 'SZ', 'BJ'], f"Invalid exchange: {parts[1]}")
class TestXYSZSpecificFeatures(unittest.TestCase):
"""测试星耀数智特有功能"""
def setUp(self):
"""测试前准备"""
self.adapter = None
try:
from app.adapters.amazingdata_adapter import AmazingDataAdapter
self.adapter_class = AmazingDataAdapter
except ImportError:
self.skipTest("AmazingData adapter not available")
def test_futures_exchange_mapping(self):
"""测试期货交易所映射"""
adapter = self.adapter_class()
# 测试上海期货交易所品种
self.assertEqual(adapter._get_futures_exchange('CU'), 'SHFE')
self.assertEqual(adapter._get_futures_exchange('AU'), 'SHFE')
# 测试大连商品交易所品种
self.assertEqual(adapter._get_futures_exchange('A'), 'DCE')
self.assertEqual(adapter._get_futures_exchange('M'), 'DCE')
# 测试郑州商品交易所品种
self.assertEqual(adapter._get_futures_exchange('CF'), 'CZCE')
self.assertEqual(adapter._get_futures_exchange('SR'), 'CZCE')
# 测试中金所品种
self.assertEqual(adapter._get_futures_exchange('IF'), 'CFFEX')
self.assertEqual(adapter._get_futures_exchange('IC'), 'CFFEX')
# 测试能源中心品种
self.assertEqual(adapter._get_futures_exchange('SC'), 'INE')
class TestWorkflow(unittest.IsolatedAsyncioTestCase):
"""测试完整工作流"""
async def asyncSetUp(self):
"""异步测试前准备"""
from app.adapters.amazingdata_adapter import AmazingDataAdapter
self.adapter = AmazingDataAdapter()
async def test_complete_workflow_mocked(self):
"""测试完整工作流Mock版本"""
# Mock所有依赖
self.adapter._ad = Mock()
self.adapter._base_data = Mock()
self.adapter._market_data = Mock()
self.adapter._info_data = Mock()
self.adapter._calendar = Mock()
self.adapter._is_logged_in = True
self.adapter._connected = True
# 1. 获取股票列表
self.adapter._base_data.get_code_list.return_value = [
'000001.SZ', '600000.SH'
]
import pandas as pd
self.adapter._base_data.get_code_info.return_value = pd.DataFrame({
'symbol': ['平安银行', '浦发银行']
}, index=['000001.SZ', '600000.SH'])
symbols = await self.adapter.fetch_symbols('stock')
self.assertEqual(len(symbols), 2)
# 2. 获取K线数据
kline_df = pd.DataFrame({
'open': [10.0], 'high': [11.0], 'low': [9.0],
'close': [10.5], 'volume': [10000], 'amount': [105000]
}, index=pd.to_datetime(['2024-01-01']))
self.adapter._market_data.query_kline.return_value = {
'000001.SZ': kline_df
}
klines = await self.adapter.fetch_klines(
symbol='000001.SZ',
start='20240101',
end='20240101',
freq='1d'
)
self.assertEqual(len(klines), 1)
# 3. 健康检查
self.adapter._base_data.get_code_list.return_value = ['000001.SZ']
health = await self.adapter.health_check()
self.assertTrue(health)
class TestErrorHandling(unittest.TestCase):
"""测试错误处理"""
def test_connection_error(self):
"""测试连接错误处理"""
from app.adapters.amazingdata_adapter import AmazingDataAdapter
adapter = AmazingDataAdapter()
# 测试未登录时调用方法
with self.assertRaises(RuntimeError) as context:
adapter._check_login()
self.assertIn('未连接到数据源', str(context.exception))
def test_invalid_date_format(self):
"""测试无效日期格式处理"""
from app.adapters.amazingdata_adapter import AmazingDataAdapter
adapter = AmazingDataAdapter()
# 测试无效输入
with self.assertRaises(ValueError):
adapter._format_date(None)
with self.assertRaises(ValueError):
adapter._format_date([])
with self.assertRaises(ValueError):
adapter._format_date({'date': '2024-01-01'})
class TestPerformanceRequirements(unittest.TestCase):
"""测试性能要求"""
def test_large_symbol_list_handling(self):
"""测试大批量代码处理"""
from app.adapters.amazingdata_adapter import AmazingDataAdapter
adapter = AmazingDataAdapter()
# 模拟5000只股票
large_code_list = [f'{i:06d}.SZ' for i in range(1, 5001)]
# 分批处理测试
batch_size = 50
batches = [large_code_list[i:i+batch_size] for i in range(0, len(large_code_list), batch_size)]
self.assertEqual(len(batches), 100)
self.assertEqual(len(batches[0]), 50)
if __name__ == '__main__':
unittest.main(verbosity=2)