Files
lmvpn_server/pytest/websocket_test.py
T
2026-07-02 20:45:53 +08:00

143 lines
4.5 KiB
Python

import json
import asyncio
import pytest
from websockets import ConnectionClosed
pytestmark = pytest.mark.asyncio
# ============================================================
# 认证测试
# ============================================================
async def test_auth_success(raw_ws):
"""正确凭据应返回 auth_ok"""
msg = json.dumps({"type": "auth", "username": "admin", "password": "admin123"})
await raw_ws.send(msg)
resp = await raw_ws.recv()
data = json.loads(resp)
assert data["type"] == "auth_ok"
async def test_auth_wrong_password(raw_ws):
"""错误密码应返回 auth_err"""
msg = json.dumps({"type": "auth", "username": "admin", "password": "wrong"})
await raw_ws.send(msg)
resp = await raw_ws.recv()
data = json.loads(resp)
assert data["type"] == "auth_err"
async def test_auth_nonexistent_user(raw_ws):
"""不存在的用户名应返回 auth_err"""
msg = json.dumps({"type": "auth", "username": "no_such_user", "password": "123456"})
await raw_ws.send(msg)
resp = await raw_ws.recv()
data = json.loads(resp)
assert data["type"] == "auth_err"
async def test_auth_non_json(raw_ws):
"""首条消息不是 JSON,服务端应关闭连接"""
await raw_ws.send("not json")
with pytest.raises(ConnectionClosed):
await raw_ws.recv()
async def test_auth_missing_type(raw_ws):
"""缺少 type 字段,服务端应关闭连接"""
msg = json.dumps({"username": "admin", "password": "admin123"})
await raw_ws.send(msg)
with pytest.raises(ConnectionClosed):
await raw_ws.recv()
# ============================================================
# 消息回显测试
# ============================================================
async def test_echo_text(authenticated_ws):
"""发送文本消息,验证回显一致"""
await authenticated_ws.send("hello world")
resp = await authenticated_ws.recv()
assert resp == "hello world"
async def test_echo_binary(authenticated_ws):
"""发送二进制消息,验证回显一致"""
data = b"\x00\x01\x02\xff\xfe\xfd"
await authenticated_ws.send(data)
resp = await authenticated_ws.recv()
assert resp == data
async def test_echo_multiple_messages(authenticated_ws):
"""连续发送多条消息,逐一验证回显"""
messages = ["msg1", "msg2", "msg3", "hello", "世界"]
for m in messages:
await authenticated_ws.send(m)
resp = await authenticated_ws.recv()
assert resp == m
async def test_echo_json_message(authenticated_ws):
"""发送 JSON 消息,验证回显一致"""
payload = json.dumps({"cmd": "ping", "seq": 1, "data": "test"})
await authenticated_ws.send(payload)
resp = await authenticated_ws.recv()
assert resp == payload
async def test_echo_large_message(authenticated_ws):
"""发送大消息(64KB),验证回显一致"""
large_msg = "A" * 65536
await authenticated_ws.send(large_msg)
resp = await authenticated_ws.recv()
assert resp == large_msg
# ============================================================
# 并发测试
# ============================================================
async def test_concurrent_connections():
"""同时建立 5 个认证连接,各自发送消息验证回显"""
from websockets.asyncio.client import connect
import os
ws_url = os.environ.get("LMVPN_WS_URL", "ws://localhost:8080/ws")
async def one_session(uid):
async with connect(ws_url, ping_interval=None) as ws:
auth = json.dumps({"type": "auth", "username": "admin", "password": "admin123"})
await ws.send(auth)
resp = await ws.recv()
assert json.loads(resp)["type"] == "auth_ok"
msg = f"msg_from_{uid}"
await ws.send(msg)
resp = await ws.recv()
assert resp == msg
tasks = [one_session(i) for i in range(5)]
await asyncio.gather(*tasks)
# ============================================================
# 协议测试
# ============================================================
async def test_connection_close_after_auth_failure(raw_ws):
"""认证失败后服务端应关闭连接"""
msg = json.dumps({"type": "auth", "username": "admin", "password": "wrong"})
await raw_ws.send(msg)
resp = await raw_ws.recv()
assert json.loads(resp)["type"] == "auth_err"
with pytest.raises(ConnectionClosed):
await raw_ws.recv()
async def test_ping_pong(authenticated_ws):
"""发送 ping,服务端应在超时前回应 pong"""
await authenticated_ws.ping()