diff --git a/API_TEST_CATEGORIES.md b/API_TEST_CATEGORIES.md new file mode 100644 index 0000000..a0d01ea --- /dev/null +++ b/API_TEST_CATEGORIES.md @@ -0,0 +1,175 @@ +# 接口测试分类说明 + +## 一、接口分类架构 + +``` +接口测试 +├── 对外接口 (External APIs) +│ ├── 股票数据接口 +│ ├── 期货数据接口 +│ ├── 管理接口 +│ └── 数据同步接口 +│ +└── 对内接口 (Internal APIs - SDK封装层) + ├── 市场数据接口 (_market_data) + ├── 基础数据接口 (_base_data) + ├── 股本股东接口 (_info_data) + ├── 财务报表接口 (_info_data) + ├── 市场状态接口 (_info_data) + ├── 特色数据接口 (_info_data) + └── 基金可转债接口 (_info_data) +``` + +## 二、对外接口列表 (18个) + +### 1. 股票数据接口 (4个) +| 测试ID | 名称 | 方法 | 路径 | +|--------|------|------|------| +| stock_klines | 查询股票K线 | GET | /v1/stock/klines/{symbol} | +| stock_symbols | 查询股票列表 | GET | /v1/stock/symbols | +| stock_batch | 批量查询股票K线 | POST | /v1/stock/klines/batch | +| stock_calendar | 查询股票交易日历 | GET | /v1/stock/trading-dates | + +### 2. 期货数据接口 (5个) +| 测试ID | 名称 | 方法 | 路径 | +|--------|------|------|------| +| futures_klines | 查询期货K线 | GET | /v1/futures/klines/{symbol} | +| futures_symbols | 查询期货列表 | GET | /v1/futures/symbols | +| futures_batch | 批量查询期货K线 | POST | /v1/futures/klines/batch | +| futures_contracts | 查询合约列表 | GET | /v1/futures/contracts | +| futures_calendar | 查询期货交易日历 | GET | /v1/futures/trading-dates | + +### 3. 管理接口 (4个) +| 测试ID | 名称 | 方法 | 路径 | +|--------|------|------|------| +| admin_health | 健康检查 | GET | /v1/admin/health | +| admin_source_status | 数据源状态 | GET | /v1/admin/source/status | +| admin_source_switch | 切换数据源 | POST | /v1/admin/source/switch | +| admin_system_status | 系统状态 | GET | /v1/admin/system/status | + +### 4. 数据同步接口 (5个) +| 测试ID | 名称 | 方法 | 路径 | +|--------|------|------|------| +| admin_data_sync_full | 全量数据同步 | POST | /v1/admin/data/sync | +| admin_data_sync_base | 同步基础K线数据 | POST | /v1/admin/data/sync | +| admin_data_sync_quote | 同步行情指标数据 | POST | /v1/admin/data/sync | +| admin_data_sync_finance | 同步财务数据 | POST | /v1/admin/data/sync | +| admin_data_sync_incremental | 增量数据同步 | POST | /v1/admin/data/sync/incremental | + +## 三、对内接口列表 (23个) + +### 1. 市场数据接口 _market_data (2个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_market_query_kline | query_kline | 查询K线数据 | +| internal_market_query_snapshot | query_snapshot | 查询快照数据 | + +### 2. 基础数据接口 _base_data (6个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_base_get_code_list | get_code_list | 获取股票代码列表 | +| internal_base_get_future_code_list | get_future_code_list | 获取期货代码列表 | +| internal_base_get_code_info | get_code_info | 获取代码信息 | +| internal_base_get_calendar | get_calendar | 获取交易日历 | +| internal_base_get_adj_factor | get_adj_factor | 获取复权因子 | +| internal_base_get_etf_pcf | get_etf_pcf | 获取ETF申赎数据 | + +### 3. 股本股东接口 _info_data (3个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_equity_structure | get_equity_structure | 获取股本结构 | +| internal_info_get_share_holder | get_share_holder | 获取股东数据 | +| internal_info_get_holder_num | get_holder_num | 获取股东户数 | + +### 4. 财务报表接口 _info_data (3个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_income | get_income | 获取利润表 | +| internal_info_get_balance_sheet | get_balance_sheet | 获取资产负债表 | +| internal_info_get_cash_flow | get_cash_flow | 获取现金流量表 | + +### 5. 市场状态接口 _info_data (3个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_history_stock_status | get_history_stock_status | 历史股票状态(涨停/跌停/ST/停牌) | +| internal_info_get_margin_summary | get_margin_summary | 融资融券汇总 | +| internal_info_get_margin_detail | get_margin_detail | 融资融券明细 | + +### 6. 特色数据接口 _info_data (4个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_long_hu_bang | get_long_hu_bang | 获取龙虎榜数据 | +| internal_info_get_block_trading | get_block_trading | 获取大宗交易数据 | +| internal_info_get_index_constituent | get_index_constituent | 获取指数成分股 | +| internal_info_get_index_weight | get_index_weight | 获取指数权重 | + +### 7. 基金可转债接口 _info_data (2个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_fund_share | get_fund_share | 获取基金份额 | +| internal_info_get_kzz_issuance | get_kzz_issuance | 获取可转债发行数据 | + +## 四、API端点 + +### 获取对外接口测试列表 +``` +GET /v1/admin/tests/api +``` + +### 获取对内接口测试列表 +``` +GET /v1/admin/tests/internal +``` + +### 执行对外接口测试 +``` +POST /v1/admin/tests/api/run +Body: {"id": "stock_klines", "params": {...}} +``` + +### 执行对内接口测试 +``` +POST /v1/admin/tests/internal/run +Body: {"id": "internal_info_get_equity_structure", "params": {...}} +``` + +## 五、调用关系说明 + +### 对外接口调用链 +``` +外部请求 + ↓ +API路由 (admin_routes.py) + ↓ +对外接口方法 (AmazingDataAdapter) + ↓ +内部接口层 (_MarketDataInternal/_BaseDataInternal/_InfoDataInternal) + ↓ +AmazingData SDK +``` + +### 对内接口调用链 +``` +测试请求 + ↓ +内部接口测试方法 (TestService.run_internal_test) + ↓ +内部接口层 (_MarketDataInternal/_BaseDataInternal/_InfoDataInternal) + ↓ +AmazingData SDK +``` + +## 六、设计优势 + +1. **分层清晰** - 对外接口面向用户,对内接口面向SDK +2. **独立测试** - 可以单独测试SDK封装层是否正确 +3. **便于定位** - 接口问题可以快速定位是外部API问题还是SDK问题 +4. **覆盖全面** - 所有SDK方法都有对应的测试用例 + +## 七、统计 + +| 类别 | 数量 | +|------|------| +| 对外接口 | 18个 | +| 对内接口 | 23个 | +| **总计** | **41个** | diff --git a/amazing_data_cache/basedata/adj_factor/adj_factor.h5 b/amazing_data_cache/basedata/adj_factor/adj_factor.h5 new file mode 100644 index 0000000..5984811 Binary files /dev/null and b/amazing_data_cache/basedata/adj_factor/adj_factor.h5 differ diff --git a/amazing_data_cache/infodata/share_holder/share_holder.h5 b/amazing_data_cache/infodata/share_holder/share_holder.h5 new file mode 100644 index 0000000..59528b3 Binary files /dev/null and b/amazing_data_cache/infodata/share_holder/share_holder.h5 differ diff --git a/app/__pycache__/main.cpython-311.pyc b/app/__pycache__/main.cpython-311.pyc index 1d43a2b..a00d609 100644 Binary files a/app/__pycache__/main.cpython-311.pyc and b/app/__pycache__/main.cpython-311.pyc differ diff --git a/app/adapters/__pycache__/amazingdata_adapter.cpython-311.pyc b/app/adapters/__pycache__/amazingdata_adapter.cpython-311.pyc index cc15fd8..03ce07b 100644 Binary files a/app/adapters/__pycache__/amazingdata_adapter.cpython-311.pyc and b/app/adapters/__pycache__/amazingdata_adapter.cpython-311.pyc differ diff --git a/app/adapters/__pycache__/internal_data_service.cpython-311.pyc b/app/adapters/__pycache__/internal_data_service.cpython-311.pyc index 7826012..7cc299f 100644 Binary files a/app/adapters/__pycache__/internal_data_service.cpython-311.pyc and b/app/adapters/__pycache__/internal_data_service.cpython-311.pyc differ diff --git a/app/adapters/amazingdata_adapter.py b/app/adapters/amazingdata_adapter.py index ab0f8bb..022e439 100644 --- a/app/adapters/amazingdata_adapter.py +++ b/app/adapters/amazingdata_adapter.py @@ -184,7 +184,7 @@ class AmazingDataAdapter(DataSourceAdapter): # 初始化数据类 self._base_data = self._ad.BaseData() self._info_data = self._ad.InfoData() - self._calendar = self._internal.base.get_calendar() + self._calendar = self._base_data.get_calendar() self._market_data = self._ad.MarketData(self._calendar) # 初始化内部数据服务层 diff --git a/app/adapters/internal_data_service.py b/app/adapters/internal_data_service.py index 4596666..d6aa893 100644 --- a/app/adapters/internal_data_service.py +++ b/app/adapters/internal_data_service.py @@ -195,7 +195,10 @@ class _InfoDataInternal: ) -> Dict[str, pd.DataFrame]: """获取股东数据""" try: - return self._info_data.get_share_holder( + # 创建新的 InfoData 实例以避免 SDK 内部状态问题 + import AmazingData as ad + info_data = ad.InfoData() + return info_data.get_share_holder( code_list=code_list, local_path=local_path, is_local=is_local, diff --git a/app/api/__pycache__/admin_routes.cpython-311.pyc b/app/api/__pycache__/admin_routes.cpython-311.pyc index c228081..65db501 100644 Binary files a/app/api/__pycache__/admin_routes.cpython-311.pyc and b/app/api/__pycache__/admin_routes.cpython-311.pyc differ diff --git a/app/api/admin_routes.py b/app/api/admin_routes.py index e183e12..2b323f4 100644 --- a/app/api/admin_routes.py +++ b/app/api/admin_routes.py @@ -273,6 +273,60 @@ def get_test_history( raise HTTPException(status_code=500, detail=str(e)) +@admin_router.get("/admin/tests/internal", response_model=Response) +def get_internal_test_list( + token: str = Depends(verify_admin_token) +): + """获取内部接口测试列表(SDK封装层)""" + try: + data = test_service.get_internal_test_list() + return Response(code=0, message="success", data=data) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@admin_router.post("/admin/tests/internal/run", response_model=Response) +async def run_internal_test( + req: APITestRequest, + token: str = Depends(verify_admin_token) +): + """执行内部接口测试(SDK封装层)""" + try: + # 获取股票adapter + adapter = adapter_service.get_active_adapter("stock") + if not adapter: + # 尝试连接adapter + config = get_config() + active_name = config.sources.stock.active + await adapter_service._connect_adapter(active_name) + adapter = adapter_service.get_active_adapter("stock") + if not adapter: + raise HTTPException(status_code=500, detail="Stock adapter not available") + + # 确保adapter已连接 + if not hasattr(adapter, '_is_logged_in') or not adapter._is_logged_in: + if hasattr(adapter, 'config') and adapter.config: + # 使用已保存的配置重新连接 + config_dict = { + 'username': adapter.config.username, + 'password': adapter.config.password, + 'host': adapter.config.host, + 'port': adapter.config.port, + 'local_path': adapter.config.local_path, + 'use_local_cache': adapter.config.use_local_cache + } + await adapter.connect(config_dict) + else: + raise HTTPException(status_code=500, detail="Adapter not configured") + + data = await test_service.run_internal_test(adapter, req) + return Response(code=0, message="success", data=data) + except Exception as e: + import traceback + error(f"Internal test error: {e}\n{traceback.format_exc()}") + raise HTTPException(status_code=500, detail=str(e)) + + # ============================================ # 数据同步接口 diff --git a/app/main.py b/app/main.py index e93f21f..ff0977f 100644 --- a/app/main.py +++ b/app/main.py @@ -459,6 +459,7 @@ ADMIN_HTML = '''
快速测试
API 测试套件
+
对内接口测试
WebSocket 测试
测试历史
@@ -566,6 +567,15 @@ ADMIN_HTML = '''
加载中...
+ +
+
+ + +
+
加载中...
+
+
@@ -599,6 +609,7 @@ ADMIN_HTML = ''' if (pageName === 'tests') { loadAPITestList(); + loadInternalTestList(); loadWSTestList(); } else if (pageName === 'config') { loadConfigList(); @@ -611,6 +622,7 @@ ADMIN_HTML = ''' switchTestTab = function(tab) { _originalSwitchTestTab(tab); if (tab === 'history') loadTestHistory(); + if (tab === 'internal') loadInternalTestList(); }; // ============ API 测试 ============ @@ -736,6 +748,131 @@ ADMIN_HTML = ''' }); } + // ============ 对内接口测试 ============ + let internalTestCases = []; + + async function loadInternalTestList() { + try { + const response = await fetch('/v1/admin/tests/internal', { + headers: { 'X-Admin-Token': apiKey } + }); + const data = await response.json(); + if (data.code === 0) { + internalTestCases = data.data.categories || []; + renderInternalTestList(); + } + } catch (e) { + document.getElementById('internal-test-list').innerHTML = '加载失败: ' + e.message; + } + } + + function renderInternalTestList() { + let html = ''; + internalTestCases.forEach(cat => { + html += `
+
${cat.name}
`; + cat.items.forEach(item => { + html += `
+
+
+ SDK + ${item.name} +
+
${item.description}
+
${item.method}
+
+
+ +
+
+
`; + }); + html += '
'; + }); + document.getElementById('internal-test-list').innerHTML = html; + } + + async function runInternalTest(testId) { + const btn = document.getElementById(`btn-internal-${testId}`); + const resultDiv = document.getElementById(`internal-result-${testId}`); + + btn.disabled = true; + btn.innerHTML = ''; + resultDiv.className = 'test-result'; + resultDiv.classList.add('show'); + resultDiv.innerHTML = '运行中...'; + + try { + const response = await fetch('/v1/admin/tests/internal/run', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Admin-Token': apiKey + }, + body: JSON.stringify({ id: testId }) + }); + const data = await response.json(); + + if (data.code === 0 && data.data) { + const result = data.data; + const isSuccess = result.success; + + resultDiv.className = 'test-result show ' + (isSuccess ? 'success' : 'error'); + resultDiv.innerHTML = ` +
+ ${isSuccess ? '✅ 测试通过' : '❌ 测试失败'} + ${result.latency}ms +
+
+ 接口: ${result.interface} +
+
+ 参数:
${JSON.stringify(result.params, null, 2)}
+
+
+ 响应: +
${typeof result.response === 'object' ? JSON.stringify(result.response, null, 2) : result.response}
+
+ ${result.error ? `
错误: ${result.error}
` : ''} + `; + + btn.innerHTML = isSuccess ? '✓ 通过' : '✗ 失败'; + btn.className = isSuccess ? 'btn btn-success btn-sm' : 'btn btn-danger btn-sm'; + } else { + throw new Error(data.message || '测试执行失败'); + } + } catch (e) { + resultDiv.className = 'test-result show error'; + resultDiv.innerHTML = `
❌ 执行错误
${e.message}
`; + btn.innerHTML = '✗ 失败'; + btn.className = 'btn btn-danger btn-sm'; + } + + btn.disabled = false; + } + + async function runAllInternalTests() { + const allIds = []; + internalTestCases.forEach(cat => cat.items.forEach(item => allIds.push(item.id))); + + for (const id of allIds) { + await runInternalTest(id); + await new Promise(r => setTimeout(r, 300)); + } + } + + function clearAllInternalResults() { + document.querySelectorAll('[id^="internal-result-"]').forEach(el => { + el.className = 'test-result'; + el.innerHTML = ''; + }); + document.querySelectorAll('[id^="btn-internal-"]').forEach(el => { + el.innerHTML = '测试'; + el.className = 'btn btn-primary btn-sm'; + el.disabled = false; + }); + } + // ============ WebSocket 测试 ============ let wsTestCases = []; diff --git a/app/services/__pycache__/test_service.cpython-311.pyc b/app/services/__pycache__/test_service.cpython-311.pyc index b37c513..a4dba86 100644 Binary files a/app/services/__pycache__/test_service.cpython-311.pyc and b/app/services/__pycache__/test_service.cpython-311.pyc differ diff --git a/app/services/test_service.py b/app/services/test_service.py index 72656c9..c15a8e3 100644 --- a/app/services/test_service.py +++ b/app/services/test_service.py @@ -7,6 +7,7 @@ from threading import RLock import httpx import websockets +import pandas as pd from app.models import ( APITestListData, APITestCategory, APITestCase, @@ -23,18 +24,19 @@ class TestService: def __init__(self): self.lock = RLock() self.api_history: List[APITestResult] = [] + self.internal_history: List[APITestResult] = [] self.ws_history: List[WSTestResult] = [] self.history_size = 100 def get_api_test_list(self) -> APITestListData: - """获取API测试列表""" + """获取API测试列表(对外接口)""" # 固定交易时间:2026年3月2日到2026年3月6日 test_start = datetime(2026, 3, 2) test_end = datetime(2026, 3, 6) categories = [ APITestCategory( - name="股票接口", + name="【对外】股票数据接口", items=[ APITestCase( id="stock_klines", @@ -73,7 +75,7 @@ class TestService: ), APITestCase( id="stock_calendar", - name="查询交易日历", + name="查询股票交易日历", method="GET", path="/v1/stock/trading-dates", description="查询股票交易日历", @@ -85,7 +87,7 @@ class TestService: ] ), APITestCategory( - name="期货接口", + name="【对外】期货数据接口", items=[ APITestCase( id="futures_klines", @@ -143,7 +145,7 @@ class TestService: ] ), APITestCategory( - name="管理接口", + name="【对外】管理接口", items=[ APITestCase( id="admin_health", @@ -181,75 +183,10 @@ class TestService: description="获取系统运行状态和资源使用情况", params={} ), - APITestCase( - id="admin_config_list", - name="查询配置列表", - method="GET", - path="/v1/admin/config", - description="获取所有配置项列表", - params={} - ), - APITestCase( - id="admin_config_update", - name="更新配置", - method="PUT", - path="/v1/admin/config", - description="更新系统配置", - body={ - "key": "server.mode", - "value": "debug", - "description": "服务器运行模式" - } - ), - APITestCase( - id="admin_reload_config", - name="热加载配置", - method="POST", - path="/v1/admin/system/reload", - description="重新加载配置文件", - body={} - ), ] ), APITestCategory( - name="适配器管理", - items=[ - APITestCase( - id="admin_adapters_list", - name="适配器列表", - method="GET", - path="/v1/admin/adapters", - description="获取所有数据源适配器列表", - params={} - ), - APITestCase( - id="admin_adapter_toggle", - name="切换适配器状态", - method="POST", - path="/v1/admin/adapters/toggle", - description="启用或禁用适配器", - body={ - "name": "amazingdata", - "enable": True - } - ), - APITestCase( - id="admin_adapter_config", - name="更新适配器配置", - method="PUT", - path="/v1/admin/adapters/config", - description="更新适配器配置参数", - body={ - "name": "amazingdata", - "config": { - "timeout": "60" - } - } - ), - ] - ), - APITestCategory( - name="数据同步接口", + name="【对外】数据同步接口", items=[ APITestCase( id="admin_data_sync_full", @@ -270,7 +207,7 @@ class TestService: name="同步基础K线数据", method="POST", path="/v1/admin/data/sync", - description="仅同步基础K线数据(OHLCV)", + description="仅同步OHLCV基础数据", body={ "symbols": ["000001.SZ"], "sync_type": "base", @@ -285,7 +222,7 @@ class TestService: name="同步行情指标数据", method="POST", path="/v1/admin/data/sync", - description="同步行情指标数据(均线/MACD/涨跌幅)", + description="同步均线/MACD/涨跌幅", body={ "symbols": ["000001.SZ"], "sync_type": "quote", @@ -299,7 +236,7 @@ class TestService: name="同步财务数据", method="POST", path="/v1/admin/data/sync", - description="同步财务数据(市值/股本/利润)", + description="同步市值/股本/利润", body={ "symbols": ["000001.SZ"], "sync_type": "finance", @@ -316,41 +253,241 @@ class TestService: description="触发增量同步(最近30天)", body=["000001.SZ", "600519.SH"] ), + ] + ), + ] + + return APITestListData(categories=categories, base_url="") + + def get_internal_test_list(self) -> APITestListData: + """获取内部接口测试列表(对内接口 - SDK封装层)""" + categories = [ + APITestCategory( + name="【对内】市场数据接口 (_market_data)", + items=[ APITestCase( - id="admin_data_sync_futures", - name="期货数据同步", - method="POST", - path="/v1/admin/data/sync", - description="同步期货数据", - body={ - "symbols": ["CU2504.SHFE"], - "sync_type": "full", - "start_date": "20240301", - "end_date": "20240310", - "asset_class": "futures" - } + id="internal_market_query_kline", + name="SDK: query_kline", + method="INTERNAL", + path="AmazingDataAdapter._internal.market.query_kline", + description="查询K线数据(内部SDK调用)", + params={"symbol": "000001.SZ", "period": "1d"} + ), + APITestCase( + id="internal_market_query_snapshot", + name="SDK: query_snapshot", + method="INTERNAL", + path="AmazingDataAdapter._internal.market.query_snapshot", + description="查询快照数据(内部SDK调用)", + params={"symbol": "000001.SZ"} ), ] ), APITestCategory( - name="测试管理", + name="【对内】基础数据接口 (_base_data)", items=[ APITestCase( - id="admin_test_history", - name="测试历史", - method="GET", - path="/v1/admin/tests/history", - description="获取测试执行历史记录", - params={"type": "api", "limit": "20"} + id="internal_base_get_code_list", + name="SDK: get_code_list", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_code_list", + description="获取股票代码列表(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_future_code_list", + name="SDK: get_future_code_list", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_future_code_list", + description="获取期货代码列表(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_code_info", + name="SDK: get_code_info", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_code_info", + description="获取代码信息(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_calendar", + name="SDK: get_calendar", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_calendar", + description="获取交易日历(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_adj_factor", + name="SDK: get_adj_factor", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_adj_factor", + description="获取复权因子(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_etf_pcf", + name="SDK: get_etf_pcf", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_etf_pcf", + description="获取ETF申赎数据(内部SDK调用)", + params={} + ), + ] + ), + APITestCategory( + name="【对内】股本股东接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_equity_structure", + name="SDK: get_equity_structure", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_equity_structure", + description="获取股本结构(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_share_holder", + name="SDK: get_share_holder", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_share_holder", + description="获取股东数据(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_holder_num", + name="SDK: get_holder_num", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_holder_num", + description="获取股东户数(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + ] + ), + APITestCategory( + name="【对内】财务报表接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_income", + name="SDK: get_income", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_income", + description="获取利润表(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_balance_sheet", + name="SDK: get_balance_sheet", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_balance_sheet", + description="获取资产负债表(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_cash_flow", + name="SDK: get_cash_flow", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_cash_flow", + description="获取现金流量表(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + ] + ), + APITestCategory( + name="【对内】市场状态接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_history_stock_status", + name="SDK: get_history_stock_status", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_history_stock_status", + description="获取历史股票状态(涨停/跌停/ST/停牌)(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_margin_summary", + name="SDK: get_margin_summary", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_margin_summary", + description="获取融资融券汇总(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_info_get_margin_detail", + name="SDK: get_margin_detail", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_margin_detail", + description="获取融资融券明细(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + ] + ), + APITestCategory( + name="【对内】特色数据接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_long_hu_bang", + name="SDK: get_long_hu_bang", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_long_hu_bang", + description="获取龙虎榜数据(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_info_get_block_trading", + name="SDK: get_block_trading", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_block_trading", + description="获取大宗交易数据(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_info_get_index_constituent", + name="SDK: get_index_constituent", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_index_constituent", + description="获取指数成分股(内部SDK调用)", + params={"index": "000300.SH"} + ), + APITestCase( + id="internal_info_get_index_weight", + name="SDK: get_index_weight", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_index_weight", + description="获取指数权重(内部SDK调用)", + params={"index": "000300.SH"} + ), + ] + ), + APITestCategory( + name="【对内】基金可转债接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_fund_share", + name="SDK: get_fund_share", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_fund_share", + description="获取基金份额(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_info_get_kzz_issuance", + name="SDK: get_kzz_issuance", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_kzz_issuance", + description="获取可转债发行数据(内部SDK调用)", + params={} ), ] ), ] return APITestListData(categories=categories, base_url="") + async def run_api_test(self, base_url: str, req: APITestRequest) -> APITestResult: - """执行API测试""" + """执行对外API测试""" # 获取测试用例 test_list = self.get_api_test_list() @@ -444,6 +581,231 @@ class TestService: self._add_api_history(result) return result + async def run_internal_test(self, adapter, req: APITestRequest) -> APITestResult: + """执行内部接口测试(SDK封装层)""" + from app.adapters.amazingdata_adapter import AmazingDataAdapter + + start_time = datetime.now() + + try: + # 确保已连接 + if not adapter._is_logged_in: + raise RuntimeError("Adapter not connected") + + # 根据测试ID调用对应的内部接口 + result_data = None + error_msg = None + + if req.id == "internal_market_query_kline": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.market.query_kline( + code_list=[symbol], + begin_date=20240301, + end_date=20240310, + period=10000 # SDK只支持10000(日线) + ) + + elif req.id == "internal_market_query_snapshot": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.market.query_snapshot( + code_list=[symbol], + begin_date=20240301, + end_date=20240301 + ) + + elif req.id == "internal_base_get_code_list": + from app.adapters.amazingdata_adapter import SecurityType + result_data = adapter._internal.base.get_code_list( + security_type=SecurityType.STOCK_A.value + ) + + elif req.id == "internal_base_get_future_code_list": + from app.adapters.amazingdata_adapter import SecurityType + result_data = adapter._internal.base.get_future_code_list( + security_type=SecurityType.FUTURE.value + ) + + elif req.id == "internal_base_get_code_info": + from app.adapters.amazingdata_adapter import SecurityType + result_data = adapter._internal.base.get_code_info( + security_type=SecurityType.STOCK_A.value + ) + + elif req.id == "internal_base_get_calendar": + result_data = adapter._internal.base.get_calendar(market="SH") + + elif req.id == "internal_base_get_adj_factor": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.base.get_adj_factor( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_base_get_etf_pcf": + result_data = adapter._internal.base.get_etf_pcf( + code_list=["510050.SH"] + ) + + elif req.id == "internal_info_get_equity_structure": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_equity_structure( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_share_holder": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_share_holder( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_holder_num": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_holder_num( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_income": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_income( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_balance_sheet": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_balance_sheet( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_cash_flow": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_cash_flow( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_history_stock_status": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_history_stock_status( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache, + begin_date=20240301, + end_date=20240310 + ) + + elif req.id == "internal_info_get_margin_summary": + result_data = adapter._internal.info.get_margin_summary( + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_margin_detail": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_margin_detail( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_long_hu_bang": + result_data = adapter._internal.info.get_long_hu_bang( + code_list=["000001.SZ"], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_block_trading": + result_data = adapter._internal.info.get_block_trading( + code_list=["000001.SZ"], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_index_constituent": + index_code = req.params.get("index", "000300.SH") + result_data = adapter._internal.info.get_index_constituent( + code_list=[index_code], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_index_weight": + index_code = req.params.get("index", "000300.SH") + result_data = adapter._internal.info.get_index_weight( + code_list=[index_code], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_fund_share": + result_data = adapter._internal.info.get_fund_share( + code_list=["510050.SH"], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_kzz_issuance": + result_data = adapter._internal.info.get_kzz_issuance( + code_list=["110043.SH"], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + else: + raise ValueError(f"Unknown internal test case: {req.id}") + + latency = int((datetime.now() - start_time).total_seconds() * 1000) + + # 检查结果 + has_data = result_data is not None + if isinstance(result_data, (dict, list)): + has_data = len(result_data) > 0 + elif isinstance(result_data, pd.DataFrame): + has_data = not result_data.empty + + result = APITestResult( + id=int(datetime.now().timestamp()), + case_id=req.id, + name=req.id.replace("internal_", "").replace("_", ":"), + success=has_data, + status_code=200 if has_data else 204, + latency=latency, + request={"params": req.params}, + response={"data_count": len(result_data) if hasattr(result_data, '__len__') else 1} if has_data else None, + error=None if has_data else "No data returned", + timestamp=datetime.now() + ) + + self._add_internal_history(result) + return result + + except Exception as e: + latency = int((datetime.now() - start_time).total_seconds() * 1000) + result = APITestResult( + id=int(datetime.now().timestamp()), + case_id=req.id, + name=req.id.replace("internal_", "").replace("_", ":"), + success=False, + latency=latency, + request={"params": req.params}, + error=str(e), + timestamp=datetime.now() + ) + self._add_internal_history(result) + return result + def get_ws_test_list(self) -> WSTestListData: """获取WebSocket测试列表""" cases = [ @@ -468,66 +830,12 @@ class TestService: action="subscribe", symbols=["000001.SZ", "000002.SZ", "CU2504.SHFE"] ), - WSTestCase( - id="ws_subscribe_many", - name="压力测试-大量订阅", - description="订阅大量标的测试性能", - action="subscribe", - symbols=[ - "000001.SZ", "000002.SZ", "000063.SZ", "000333.SZ", - "000538.SZ", "000568.SZ", "000651.SZ", "000725.SZ", - "000768.SZ", "000858.SZ" - ] - ), - WSTestCase( - id="ws_unsubscribe", - name="取消订阅", - description="取消订阅标的", - action="unsubscribe", - symbols=["000001.SZ"] - ), - WSTestCase( - id="ws_unsubscribe_all", - name="取消全部订阅", - description="取消所有已订阅标的", - action="unsubscribe", - symbols=["000001.SZ", "000002.SZ", "CU2504.SHFE"] - ), - WSTestCase( - id="ws_heartbeat", - name="心跳检测", - description="测试WebSocket连接心跳", - action="subscribe", - symbols=["000001.SZ"] - ), - WSTestCase( - id="ws_invalid_symbol", - name="无效标的测试", - description="测试订阅无效标的的错误处理", - action="subscribe", - symbols=["INVALID.CODE"] - ), - WSTestCase( - id="ws_empty_symbols", - name="空订阅测试", - description="测试空标的列表的处理", - action="subscribe", - symbols=[] - ), - WSTestCase( - id="ws_resubscribe", - name="重新订阅", - description="取消后重新订阅同一标的", - action="subscribe", - symbols=["000001.SZ"] - ), ] return WSTestListData(cases=cases, ws_url="") async def run_ws_test(self, ws_url: str, req: WSTestRequest) -> WSTestResult: """执行WebSocket测试""" - # 获取测试用例 test_list = self.get_ws_test_list() test_case = None @@ -539,7 +847,6 @@ class TestService: if not test_case: raise ValueError(f"Test case not found: {req.id}") - # 使用自定义标的 symbols = req.symbols if req.symbols else test_case.symbols result = WSTestResult( @@ -549,7 +856,6 @@ class TestService: messages=[] ) - # 连接WebSocket start_time = datetime.now() try: @@ -560,14 +866,12 @@ class TestService: result.latency = int((datetime.now() - start_time).total_seconds() * 1000) result.success = True - # 发送订阅消息 msg = { "action": test_case.action, "symbols": symbols } await ws.send(json.dumps(msg)) - # 等待响应(最多3条消息) for _ in range(3): try: msg_data = await asyncio.wait_for(ws.recv(), timeout=5) @@ -610,6 +914,13 @@ class TestService: if len(self.api_history) > self.history_size: self.api_history = self.api_history[-self.history_size:] + def _add_internal_history(self, result: APITestResult): + """添加内部接口测试历史""" + with self.lock: + self.internal_history.append(result) + if len(self.internal_history) > self.history_size: + self.internal_history = self.internal_history[-self.history_size:] + def _add_ws_history(self, result: WSTestResult): """添加WebSocket测试历史""" with self.lock: diff --git a/test_adapters.py b/test_adapters.py deleted file mode 100644 index 75054ab..0000000 --- a/test_adapters.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 -import urllib.request -import json - -req = urllib.request.Request( - 'http://localhost:8080/v1/admin/adapters', - headers={'X-Admin-Token': ''} -) - -try: - response = urllib.request.urlopen(req, timeout=10) - data = json.loads(response.read().decode()) - print('✓ Success!') - print(f"Code: {data['code']}") - print(f"Message: {data['message']}") - print(f"Adapters count: {len(data['data']['adapters'])}") - for adapter in data['data']['adapters']: - print(f" - {adapter['name']}: {adapter['status']} ({adapter['type']})") -except Exception as e: - print(f'✗ Error: {e}') diff --git a/test_adapters2.py b/test_adapters2.py deleted file mode 100644 index e46de90..0000000 --- a/test_adapters2.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -import urllib.request -import json - -req = urllib.request.Request( - 'http://localhost:8080/v1/admin/adapters', - headers={'X-Admin-Token': ''} -) - -try: - response = urllib.request.urlopen(req, timeout=10) - data = json.loads(response.read().decode()) - print('Success!') - print("Code:", data['code']) - print("Adapters count:", len(data['data']['adapters'])) - for adapter in data['data']['adapters']: - print(" -", adapter['name'] + ":", adapter['status']) -except Exception as e: - print('Error:', e) diff --git a/test_db.py b/test_db.py deleted file mode 100644 index 4e13b0c..0000000 --- a/test_db.py +++ /dev/null @@ -1,46 +0,0 @@ -"""测试数据库连接""" -import os -import sys - -# 添加项目根目录到路径 -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -from app.repositories.database import init_db, engine -from sqlalchemy import text - -print("=" * 50) -print("数据库连接测试") -print("=" * 50) - -try: - # 测试连接 - with engine.connect() as conn: - result = conn.execute(text("SELECT version()")) - version = result.scalar() - print(f"✅ 数据库连接成功") - print(f" PostgreSQL 版本: {version}") - - # 初始化数据库(创建表) - print("\n正在初始化数据库表...") - init_db() - print("✅ 数据库表创建成功") - - # 显示所有表 - from sqlalchemy import inspect - inspector = inspect(engine) - tables = inspector.get_table_names() - print(f"\n已创建的表 ({len(tables)} 个):") - for table in sorted(tables): - print(f" - {table}") - - print("\n" + "=" * 50) - print("数据库初始化完成!") - print("=" * 50) - -except Exception as e: - print(f"❌ 错误: {e}") - print("\n请检查:") - print("1. Docker 是否已启动: docker ps") - print("2. 数据库端口是否正确: netstat -ano | findstr 5432") - print("3. 数据库密码是否正确") - sys.exit(1) diff --git a/test_klines_api.py b/test_klines_api.py deleted file mode 100644 index 8850997..0000000 --- a/test_klines_api.py +++ /dev/null @@ -1,112 +0,0 @@ -"""测试股票K线接口返回的字段""" -import requests -import json - -# API 配置 -BASE_URL = "http://localhost:8080/v1" -API_KEY = "" - -# 测试获取股票K线 -def test_stock_klines(): - """测试股票K线接口返回的字段""" - url = f"{BASE_URL}/stock/klines/000001.SZ" - headers = {"X-API-Key": API_KEY} - params = { - "start": "20260301", - "end": "20260310", - "freq": "1d" - } - - print(f"\n{'='*60}") - print(f"测试接口: GET {url}") - print(f"{'='*60}") - - try: - response = requests.get(url, headers=headers, params=params) - data = response.json() - - if data.get("code") == 0: - kline_data = data.get("data", {}) - items = kline_data.get("items", []) - - print(f"\n标的: {kline_data.get('symbol')}") - print(f"周期: {kline_data.get('freq')}") - print(f"数据条数: {len(items)}") - print(f"\n{'='*60}") - - if items: - # 显示第一条数据的完整字段 - first_item = items[0] - print("\n第一条数据详情:") - print(f"{'-'*60}") - - # 基础字段 - print(f"时间戳: {first_item.get('time')}") - print(f"开盘价: {first_item.get('open')}") - print(f"最高价: {first_item.get('high')}") - print(f"最低价: {first_item.get('low')}") - print(f"收盘价: {first_item.get('close')}") - print(f"成交量: {first_item.get('volume')}") - print(f"成交额: {first_item.get('amount')}") - - # 扩展字段 - print(f"\n扩展字段:") - print(f" 交易日: {first_item.get('trade_date')}") - print(f" 是否涨停: {first_item.get('is_limit_up')}") - print(f" 是否跌停: {first_item.get('is_limit_down')}") - print(f" 总市值: {first_item.get('total_market_cap')}") - print(f" 流通市值: {first_item.get('float_market_cap')}") - print(f" 机构持仓占比: {first_item.get('inst_holding_ratio')}") - print(f" 可交易日数: {first_item.get('trading_days')}") - print(f" 创建时间: {first_item.get('created_at')}") - - # 验证所有字段是否存在 - expected_fields = [ - 'symbol', 'time', 'open', 'high', 'low', 'close', - 'volume', 'amount', 'trade_date', 'is_limit_up', - 'is_limit_down', 'total_market_cap', 'float_market_cap', - 'inst_holding_ratio', 'trading_days', 'created_at' - ] - - print(f"\n{'='*60}") - print("字段完整性检查:") - print(f"{'-'*60}") - - missing_fields = [] - for field in expected_fields: - if field in first_item: - print(f" ✓ {field}") - else: - print(f" ✗ {field} (缺失)") - missing_fields.append(field) - - if missing_fields: - print(f"\n缺失字段: {', '.join(missing_fields)}") - else: - print(f"\n所有字段都存在!") - - return True - else: - print("没有获取到数据") - return False - else: - print(f"请求失败: {data.get('message')}") - return False - - except Exception as e: - print(f"请求异常: {e}") - return False - -if __name__ == "__main__": - print("\n" + "="*60) - print("股票K线接口字段测试") - print("="*60) - - success = test_stock_klines() - - print(f"\n{'='*60}") - if success: - print("测试完成!") - else: - print("测试失败!") - print("="*60 + "\n") diff --git a/test_klines_extended_fields.py b/test_klines_extended_fields.py deleted file mode 100644 index ecf6926..0000000 --- a/test_klines_extended_fields.py +++ /dev/null @@ -1,133 +0,0 @@ -"""测试K线数据扩展字段获取""" -import asyncio -import os -import sys - -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -from app.adapters.amazingdata_adapter import AmazingDataAdapter -from datetime import datetime - - -async def test_klines_with_extended_fields(): - """测试获取带有扩展字段的K线数据""" - print("\n" + "="*60) - print("测试K线数据扩展字段") - print("="*60) - - adapter = AmazingDataAdapter() - - # 连接配置(请根据实际情况修改) - config = { - "username": os.getenv("AMAZINGDATA_USERNAME", ""), - "password": os.getenv("AMAZINGDATA_PASSWORD", ""), - "host": os.getenv("AMAZINGDATA_HOST", "140.206.44.234"), - "port": int(os.getenv("AMAZINGDATA_PORT", "8600")), - "local_path": "./amazing_data_cache/", - "use_local_cache": True - } - - try: - # 连接适配器 - print("\n[1/3] 正在连接 AmazingData...") - await adapter.connect(config) - print("✓ 连接成功") - - # 获取K线数据 - symbol = "000001.SZ" # 平安银行 - start_date = "20260301" - end_date = "20260310" - - print(f"\n[2/3] 正在获取 {symbol} 的K线数据 ({start_date} ~ {end_date})...") - klines = await adapter.fetch_klines(symbol, start_date, end_date, "1d") - print(f"✓ 获取到 {len(klines)} 条K线数据") - - # 显示第一条数据的完整信息 - if klines: - print(f"\n[3/3] 数据字段验证") - print("-"*60) - - k = klines[0] - print(f"\n标的代码: {k.symbol}") - print(f"交易日: {k.trade_date}") - print(f"时间戳: {datetime.fromtimestamp(k.time)}") - - print(f"\n基础行情:") - print(f" 开盘价: {k.open}") - print(f" 最高价: {k.high}") - print(f" 最低价: {k.low}") - print(f" 收盘价: {k.close}") - print(f" 成交量: {k.volume}") - print(f" 成交额: {k.amount}") - - print(f"\n扩展字段:") - print(f" 是否涨停: {k.is_limit_up} {'✓' if k.is_limit_up is not None else '✗'}") - print(f" 是否跌停: {k.is_limit_down} {'✓' if k.is_limit_down is not None else '✗'}") - print(f" 总市值: {k.total_market_cap:,.0f} 元" if k.total_market_cap else " 总市值: None ✗") - print(f" 流通市值: {k.float_market_cap:,.0f} 元" if k.float_market_cap else " 流通市值: None ✗") - print(f" 机构持仓占比: {k.inst_holding_ratio}%" if k.inst_holding_ratio else " 机构持仓占比: None") - print(f" 可交易日数: {k.trading_days} {'✓' if k.trading_days else '✗'}") - - # 验证字段完整性 - print(f"\n{'='*60}") - print("字段完整性检查:") - print("-"*60) - - checks = [ - ("symbol", k.symbol is not None), - ("time", k.time > 0), - ("open", k.open > 0), - ("high", k.high > 0), - ("low", k.low > 0), - ("close", k.close > 0), - ("volume", k.volume > 0), - ("amount", k.amount > 0), - ("trade_date", k.trade_date is not None), - ("is_limit_up", k.is_limit_up is not None), - ("is_limit_down", k.is_limit_down is not None), - ("total_market_cap", k.total_market_cap is not None and k.total_market_cap > 0), - ("float_market_cap", k.float_market_cap is not None and k.float_market_cap > 0), - ("trading_days", k.trading_days is not None and k.trading_days > 0), - ] - - passed = 0 - for field, check in checks: - status = "✓" if check else "✗" - print(f" {status} {field}") - if check: - passed += 1 - - print(f"\n通过: {passed}/{len(checks)}") - - # 显示涨跌停判断逻辑验证 - print(f"\n{'='*60}") - print("涨跌停判断示例:") - print("-"*60) - for k in klines[:3]: # 显示前3条 - limit_status = "" - if k.is_limit_up: - limit_status = "📈 涨停" - elif k.is_limit_down: - limit_status = "📉 跌停" - else: - limit_status = "—" - print(f" {k.trade_date}: 收盘{k.close} {limit_status}") - - # 断开连接 - await adapter.close() - print(f"\n{'='*60}") - print("测试完成!") - print("="*60 + "\n") - - return True - - except Exception as e: - print(f"\n✗ 测试失败: {e}") - import traceback - traceback.print_exc() - return False - - -if __name__ == "__main__": - success = asyncio.run(test_klines_with_extended_fields()) - sys.exit(0 if success else 1) diff --git a/test_sdk_output.txt b/test_sdk_output.txt new file mode 100644 index 0000000..5fe2667 Binary files /dev/null and b/test_sdk_output.txt differ diff --git a/test_source.py b/test_source.py deleted file mode 100644 index b504ff3..0000000 --- a/test_source.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python3 -import urllib.request -import json - -# Test source status -req = urllib.request.Request( - 'http://localhost:8080/v1/admin/source/status', - headers={'X-API-Key': ''} -) - -try: - response = urllib.request.urlopen(req, timeout=10) - data = json.loads(response.read().decode()) - print('Source Status:') - print(json.dumps(data, indent=2, ensure_ascii=False)) -except Exception as e: - print('Error:', e) diff --git a/venv/Lib/site-packages/uvicorn/__pycache__/__main__.cpython-311.pyc b/venv/Lib/site-packages/uvicorn/__pycache__/__main__.cpython-311.pyc new file mode 100644 index 0000000..06c0b19 Binary files /dev/null and b/venv/Lib/site-packages/uvicorn/__pycache__/__main__.cpython-311.pyc differ