fix: 修复登录500错误和移除明文密码提示
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service) - 移除登录页面上的明文密码提示 - 改进前端错误处理,避免数据加载失败导致登录显示错误
This commit is contained in:
@@ -0,0 +1,416 @@
|
||||
"""
|
||||
WebSocket 客户端测试脚本
|
||||
用于测试 WebSocket 实时数据推送功能
|
||||
"""
|
||||
import asyncio
|
||||
import websockets
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class WebSocketTestClient:
|
||||
"""WebSocket 测试客户端"""
|
||||
|
||||
def __init__(self, url: str = "ws://localhost:8000/ws"):
|
||||
"""
|
||||
初始化测试客户端
|
||||
|
||||
- **url**: WebSocket 服务器 URL
|
||||
"""
|
||||
self.url = url
|
||||
self.websocket: Optional[websockets.WebSocketClientProtocol] = None
|
||||
self.connected = False
|
||||
|
||||
async def connect(self, token: Optional[str] = None):
|
||||
"""
|
||||
连接到 WebSocket 服务器
|
||||
|
||||
- **token**: 可选的 JWT Token
|
||||
"""
|
||||
uri = f"{self.url}"
|
||||
if token:
|
||||
uri += f"?token={token}"
|
||||
|
||||
try:
|
||||
self.websocket = await websockets.connect(uri)
|
||||
self.connected = True
|
||||
print(f"[+] Connected to {self.url}")
|
||||
|
||||
# 接收欢迎消息
|
||||
welcome_msg = await self.websocket.recv()
|
||||
print(f"[+] Welcome message: {welcome_msg}")
|
||||
return json.loads(welcome_msg)
|
||||
except Exception as e:
|
||||
print(f"[-] Failed to connect: {e}")
|
||||
raise
|
||||
|
||||
async def disconnect(self):
|
||||
"""断开连接"""
|
||||
if self.websocket:
|
||||
await self.websocket.close()
|
||||
self.connected = False
|
||||
print("[+] Disconnected from server")
|
||||
|
||||
async def subscribe(self, subscription_types: list):
|
||||
"""
|
||||
订阅数据类型
|
||||
|
||||
- **subscription_types**: 订阅类型列表,如 ["tick", "order", "trade"]
|
||||
"""
|
||||
message = {
|
||||
"type": "subscribe",
|
||||
"data": {
|
||||
"subscription": subscription_types
|
||||
}
|
||||
}
|
||||
await self.send(message)
|
||||
print(f"[+] Subscribed to: {subscription_types}")
|
||||
|
||||
async def unsubscribe(self, subscription_types: list):
|
||||
"""
|
||||
取消订阅
|
||||
|
||||
- **subscription_types**: 订阅类型列表
|
||||
"""
|
||||
message = {
|
||||
"type": "unsubscribe",
|
||||
"data": {
|
||||
"subscription": subscription_types
|
||||
}
|
||||
}
|
||||
await self.send(message)
|
||||
print(f"[+] Unsubscribed from: {subscription_types}")
|
||||
|
||||
async def subscribe_symbol(self, symbols: list):
|
||||
"""
|
||||
订阅品种行情
|
||||
|
||||
- **symbols**: 品种代码列表,如 ["IF2501.CFFEX", "IH2501.CFFEX"]
|
||||
"""
|
||||
message = {
|
||||
"type": "subscribe_symbol",
|
||||
"data": {
|
||||
"symbol": symbols
|
||||
}
|
||||
}
|
||||
await self.send(message)
|
||||
print(f"[+] Subscribed to symbols: {symbols}")
|
||||
|
||||
async def unsubscribe_symbol(self, symbols: list):
|
||||
"""
|
||||
取消品种订阅
|
||||
|
||||
- **symbols**: 品种代码列表
|
||||
"""
|
||||
message = {
|
||||
"type": "unsubscribe_symbol",
|
||||
"data": {
|
||||
"symbol": symbols
|
||||
}
|
||||
}
|
||||
await self.send(message)
|
||||
print(f"[+] Unsubscribed from symbols: {symbols}")
|
||||
|
||||
async def send_ping(self):
|
||||
"""发送心跳"""
|
||||
message = {
|
||||
"type": "ping",
|
||||
"data": {}
|
||||
}
|
||||
await self.send(message)
|
||||
print("[+] Ping sent")
|
||||
|
||||
async def send(self, message: dict):
|
||||
"""
|
||||
发送消息
|
||||
|
||||
- **message**: 消息字典
|
||||
"""
|
||||
if not self.websocket or not self.connected:
|
||||
raise Exception("Not connected to WebSocket server")
|
||||
|
||||
await self.websocket.send(json.dumps(message))
|
||||
|
||||
async def receive(self, timeout: Optional[float] = None):
|
||||
"""
|
||||
接收消息
|
||||
|
||||
- **timeout**: 超时时间(秒)
|
||||
"""
|
||||
if not self.websocket or not self.connected:
|
||||
raise Exception("Not connected to WebSocket server")
|
||||
|
||||
try:
|
||||
message = await asyncio.wait_for(self.websocket.recv(), timeout=timeout)
|
||||
return json.loads(message)
|
||||
except asyncio.TimeoutError:
|
||||
return None
|
||||
|
||||
async def listen(self, duration: int = 10, print_messages: bool = True):
|
||||
"""
|
||||
监听消息
|
||||
|
||||
- **duration**: 监听时长(秒)
|
||||
- **print_messages**: 是否打印消息
|
||||
"""
|
||||
print(f"\n[*] Listening for messages ({duration}s)...")
|
||||
messages = []
|
||||
|
||||
try:
|
||||
while True:
|
||||
message = await asyncio.wait_for(self.websocket.recv(), timeout=duration)
|
||||
data = json.loads(message)
|
||||
messages.append(data)
|
||||
|
||||
if print_messages:
|
||||
msg_type = data.get("type", "unknown")
|
||||
print(f"[*] Received {msg_type}: {json.dumps(data, ensure_ascii=False)[:200]}...")
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
print(f"\n[+] Listening finished. Received {len(messages)} messages")
|
||||
return messages
|
||||
|
||||
|
||||
async def test_basic_connection():
|
||||
"""测试基本连接功能"""
|
||||
print("\n" + "="*50)
|
||||
print("TEST 1: Basic Connection")
|
||||
print("="*50)
|
||||
|
||||
client = WebSocketTestClient()
|
||||
|
||||
try:
|
||||
# 连接
|
||||
await client.connect()
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# 测试心跳
|
||||
await client.send_ping()
|
||||
response = await client.receive(timeout=2)
|
||||
if response and response.get("type") == "pong":
|
||||
print("[+] Ping/Pong test passed")
|
||||
|
||||
# 断开
|
||||
await client.disconnect()
|
||||
print("[+] Test passed")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"[-] Test failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_subscription():
|
||||
"""测试订阅功能"""
|
||||
print("\n" + "="*50)
|
||||
print("TEST 2: Subscription")
|
||||
print("="*50)
|
||||
|
||||
client = WebSocketTestClient()
|
||||
|
||||
try:
|
||||
# 连接
|
||||
await client.connect()
|
||||
|
||||
# 订阅多个数据类型
|
||||
await client.subscribe(["tick", "order", "trade", "position", "account", "log"])
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
# 订阅品种
|
||||
await client.subscribe_symbol(["IF2501.CFFEX", "IH2501.CFFEX"])
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
# 取消订阅
|
||||
await client.unsubscribe(["log"])
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
# 断开
|
||||
await client.disconnect()
|
||||
print("[+] Test passed")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"[-] Test failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_message_reception():
|
||||
"""测试消息接收功能"""
|
||||
print("\n" + "="*50)
|
||||
print("TEST 3: Message Reception")
|
||||
print("="*50)
|
||||
|
||||
client = WebSocketTestClient()
|
||||
|
||||
try:
|
||||
# 连接
|
||||
await client.connect()
|
||||
|
||||
# 订阅所有类型
|
||||
await client.subscribe(["tick", "order", "trade", "position", "account", "log", "contract"])
|
||||
|
||||
# 监听消息
|
||||
messages = await client.listen(duration=5)
|
||||
|
||||
print(f"\n[+] Received messages by type:")
|
||||
msg_types = {}
|
||||
for msg in messages:
|
||||
msg_type = msg.get("type", "unknown")
|
||||
msg_types[msg_type] = msg_types.get(msg_type, 0) + 1
|
||||
|
||||
for msg_type, count in msg_types.items():
|
||||
print(f" - {msg_type}: {count}")
|
||||
|
||||
# 断开
|
||||
await client.disconnect()
|
||||
print("[+] Test passed")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"[-] Test failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_heartbeat():
|
||||
"""测试心跳机制"""
|
||||
print("\n" + "="*50)
|
||||
print("TEST 4: Heartbeat")
|
||||
print("="*50)
|
||||
|
||||
client = WebSocketTestClient()
|
||||
|
||||
try:
|
||||
# 连接
|
||||
await client.connect()
|
||||
|
||||
# 发送多次心跳
|
||||
for i in range(5):
|
||||
await client.send_ping()
|
||||
response = await client.receive(timeout=2)
|
||||
if response and response.get("type") == "pong":
|
||||
print(f"[+] Heartbeat {i+1}/5 successful")
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# 断开
|
||||
await client.disconnect()
|
||||
print("[+] Test passed")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"[-] Test failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_error_handling():
|
||||
"""测试错误处理"""
|
||||
print("\n" + "="*50)
|
||||
print("TEST 5: Error Handling")
|
||||
print("="*50)
|
||||
|
||||
client = WebSocketTestClient()
|
||||
|
||||
try:
|
||||
# 连接
|
||||
await client.connect()
|
||||
|
||||
# 发送未知消息类型
|
||||
await client.send({
|
||||
"type": "unknown_type",
|
||||
"data": {}
|
||||
})
|
||||
|
||||
response = await client.receive(timeout=2)
|
||||
if response and response.get("type") == "error":
|
||||
print("[+] Error response received correctly")
|
||||
|
||||
# 断开
|
||||
await client.disconnect()
|
||||
print("[+] Test passed")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"[-] Test failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_authenticated_connection():
|
||||
"""测试认证连接"""
|
||||
print("\n" + "="*50)
|
||||
print("TEST 6: Authenticated Connection")
|
||||
print("="*50)
|
||||
|
||||
# 注意:需要有效的 JWT Token
|
||||
# 这里测试无效 Token 的情况
|
||||
client = WebSocketTestClient()
|
||||
|
||||
try:
|
||||
# 使用无效 Token 连接
|
||||
await client.connect(token="invalid_token")
|
||||
print("[-] Should have failed with invalid token")
|
||||
await client.disconnect()
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"[+] Correctly rejected invalid token: {e}")
|
||||
return True
|
||||
|
||||
|
||||
async def run_all_tests():
|
||||
"""运行所有测试"""
|
||||
print("\n" + "="*50)
|
||||
print("WebSocket Test Suite")
|
||||
print("="*50)
|
||||
|
||||
tests = [
|
||||
("Basic Connection", test_basic_connection),
|
||||
("Subscription", test_subscription),
|
||||
("Message Reception", test_message_reception),
|
||||
("Heartbeat", test_heartbeat),
|
||||
("Error Handling", test_error_handling),
|
||||
("Authenticated Connection", test_authenticated_connection),
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
for name, test_func in tests:
|
||||
try:
|
||||
result = await test_func()
|
||||
results.append((name, result))
|
||||
except Exception as e:
|
||||
print(f"[-] Test '{name}' crashed: {e}")
|
||||
results.append((name, False))
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# 汇总结果
|
||||
print("\n" + "="*50)
|
||||
print("Test Results Summary")
|
||||
print("="*50)
|
||||
|
||||
passed = sum(1 for _, result in results if result)
|
||||
total = len(results)
|
||||
|
||||
for name, result in results:
|
||||
status = "✓ PASS" if result else "✗ FAIL"
|
||||
print(f"{status}: {name}")
|
||||
|
||||
print(f"\nTotal: {passed}/{total} tests passed")
|
||||
|
||||
return passed == total
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 运行测试
|
||||
success = asyncio.run(run_all_tests())
|
||||
|
||||
if success:
|
||||
print("\n[+] All tests passed!")
|
||||
exit(0)
|
||||
else:
|
||||
print("\n[-] Some tests failed")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user