""" Sanguo VeighNa Web API 测试套件 测试所有 REST API 端点 """ import pytest import asyncio from httpx import AsyncClient, ASGITransport from fastapi import status # 导入应用 import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from sanguo_web.api import app # ============================================ # Fixtures # ============================================ @pytest.fixture async def client(): """创建测试客户端""" async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac: yield ac @pytest.fixture async def auth_token(client): """获取认证 Token""" response = await client.post( "/api/v1/auth/login", json={"username": "admin", "password": "admin123"} ) assert response.status_code == 200 data = response.json() return data.get("access_token") @pytest.fixture def auth_headers(auth_token): """获取认证请求头""" return {"Authorization": f"Bearer {auth_token}"} # ============================================ # 系统端点测试 # ============================================ class TestSystemEndpoints: """系统端点测试""" @pytest.mark.asyncio async def test_root(self, client): """测试根路径""" response = await client.get("/") assert response.status_code == 200 @pytest.mark.asyncio async def test_health_check(self, client): """测试健康检查""" response = await client.get("/health") assert response.status_code == 200 data = response.json() assert "status" in data @pytest.mark.asyncio async def test_api_root(self, client): """测试 API 根路径""" response = await client.get("/api") assert response.status_code == 200 data = response.json() assert "name" in data assert "version" in data @pytest.mark.asyncio async def test_system_info(self, client, auth_headers): """测试系统信息""" response = await client.get( "/api/v1/system/info", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert "version" in data or "status" in data # ============================================ # 认证端点测试 # ============================================ class TestAuthEndpoints: """认证端点测试""" @pytest.mark.asyncio async def test_login_success(self, client): """测试成功登录""" response = await client.post( "/api/v1/auth/login", json={"username": "admin", "password": "admin123"} ) assert response.status_code == 200 data = response.json() assert "access_token" in data assert "token_type" in data assert data["token_type"] == "bearer" @pytest.mark.asyncio async def test_login_wrong_password(self, client): """测试错误密码""" response = await client.post( "/api/v1/auth/login", json={"username": "admin", "password": "wrong_password"} ) assert response.status_code == 401 @pytest.mark.asyncio async def test_login_invalid_user(self, client): """测试无效用户""" response = await client.post( "/api/v1/auth/login", json={"username": "invalid_user", "password": "admin123"} ) assert response.status_code == 401 @pytest.mark.asyncio async def test_verify_token_valid(self, client, auth_token): """测试有效 Token 验证""" response = await client.post( "/api/v1/auth/verify", json={"token": auth_token} ) assert response.status_code == 200 data = response.json() assert data["valid"] is True assert "user_info" in data @pytest.mark.asyncio async def test_verify_token_invalid(self, client): """测试无效 Token 验证""" response = await client.post( "/api/v1/auth/verify", json={"token": "invalid_token"} ) assert response.status_code == 200 data = response.json() assert data["valid"] is False @pytest.mark.asyncio async def test_me(self, client, auth_headers): """测试获取当前用户信息""" response = await client.get( "/api/v1/auth/me", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert "username" in data @pytest.mark.asyncio async def test_logout(self, client, auth_headers): """测试登出""" response = await client.post( "/api/v1/auth/logout", headers=auth_headers ) assert response.status_code == 200 # ============================================ # 网关端点测试 # ============================================ class TestGatewayEndpoints: """网关端点测试""" @pytest.mark.asyncio async def test_get_available_gateways(self, client, auth_headers): """测试获取可用网关列表""" response = await client.get( "/api/v1/gateway/available", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert isinstance(data, list) @pytest.mark.asyncio async def test_get_connected_gateways(self, client, auth_headers): """测试获取已连接网关列表""" response = await client.get( "/api/v1/gateway/connected", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert isinstance(data, list) @pytest.mark.asyncio async def test_get_gateway_setting(self, client, auth_headers): """测试获取网关配置模板""" response = await client.get( "/api/v1/gateway/setting/CTP", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert isinstance(data, dict) @pytest.mark.asyncio async def test_connect_gateway(self, client, auth_headers): """测试连接网关(模拟)""" response = await client.post( "/api/v1/gateway/connect", headers=auth_headers, json={ "gateway_name": "CTP_TEST", "gateway_type": "ctp", "setting": { "用户名": "test_user", "密码": "test_pass", "经纪商代码": "9999", "交易服务器": "tcp://test服务器:41205", "行情服务器": "tcp://test服务器:41213", } } ) # 在 Mock 模式下可能返回 200 或 500 assert response.status_code in [200, 500] @pytest.mark.asyncio async def test_unauthorized_access(self, client): """测试未授权访问""" response = await client.get("/api/v1/gateway/available") assert response.status_code == 401 # ============================================ # 行情端点测试 # ============================================ class TestMarketEndpoints: """行情端点测试""" @pytest.mark.asyncio async def test_get_ticks(self, client, auth_headers): """测试获取行情数据""" response = await client.get( "/api/v1/market/ticks", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert "ticks" in data @pytest.mark.asyncio async def test_subscribe(self, client, auth_headers): """测试订阅行情""" response = await client.post( "/api/v1/market/subscribe", headers=auth_headers, json={ "symbol": "IF2501", "exchange": "CFFEX" } ) assert response.status_code in [200, 202] @pytest.mark.asyncio async def test_unsubscribe(self, client, auth_headers): """测试取消订阅""" response = await client.post( "/api/v1/market/unsubscribe", headers=auth_headers, json={ "symbol": "IF2501", "exchange": "CFFEX" } ) assert response.status_code in [200, 202] @pytest.mark.asyncio async def test_get_contracts(self, client, auth_headers): """测试获取合约列表""" response = await client.get( "/api/v1/market/contracts", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert "contracts" in data # ============================================ # 交易端点测试 # ============================================ class TestTradingEndpoints: """交易端点测试""" @pytest.mark.asyncio async def test_get_accounts(self, client, auth_headers): """测试获取账户信息""" response = await client.get( "/api/v1/trading/accounts", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert isinstance(data, list) @pytest.mark.asyncio async def test_get_positions(self, client, auth_headers): """测试获取持仓信息""" response = await client.get( "/api/v1/trading/positions", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert isinstance(data, list) @pytest.mark.asyncio async def test_get_orders(self, client, auth_headers): """测试获取委托列表""" response = await client.get( "/api/v1/trading/orders", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert isinstance(data, list) @pytest.mark.asyncio async def test_get_active_orders(self, client, auth_headers): """测试获取活动委托""" response = await client.get( "/api/v1/trading/orders/active", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert isinstance(data, list) @pytest.mark.asyncio async def test_send_order(self, client, auth_headers): """测试发送订单(模拟)""" response = await client.post( "/api/v1/trading/orders", headers=auth_headers, json={ "symbol": "IF2501", "exchange": "CFFEX", "direction": "buy", "order_type": "limit", "volume": 1, "price": 3500.0 } ) # 在没有连接网关的情况下可能返回错误 assert response.status_code in [200, 500] @pytest.mark.asyncio async def test_get_trades(self, client, auth_headers): """测试获取成交记录""" response = await client.get( "/api/v1/trading/trades", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert "trades" in data assert "total" in data @pytest.mark.asyncio async def test_get_account_summary(self, client, auth_headers): """测试获取账户综合信息""" response = await client.get( "/api/v1/trading/account", headers=auth_headers ) assert response.status_code in [200, 404] # 可能没有账户数据 # ============================================ # 策略端点测试 # ============================================ class TestStrategyEndpoints: """策略端点测试""" @pytest.mark.asyncio async def test_get_strategies(self, client, auth_headers): """测试获取策略列表""" response = await client.get( "/api/v1/strategy/list", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert "strategies" in data @pytest.mark.asyncio async def test_init_strategy(self, client, auth_headers): """测试初始化策略""" response = await client.post( "/api/v1/strategy/test_strategy/init", headers=auth_headers ) assert response.status_code in [200, 404] @pytest.mark.asyncio async def test_start_strategy(self, client, auth_headers): """测试启动策略""" response = await client.post( "/api/v1/strategy/test_strategy/start", headers=auth_headers ) assert response.status_code in [200, 404] @pytest.mark.asyncio async def test_stop_strategy(self, client, auth_headers): """测试停止策略""" response = await client.post( "/api/v1/strategy/test_strategy/stop", headers=auth_headers ) assert response.status_code in [200, 404] # ============================================ # 运行测试 # ============================================ if __name__ == "__main__": pytest.main([__file__, "-v", "--tb=short"])