Files
xianyan/docs/toolsapi/scripts/test_full_e2e.py
Developer 6f5400ec4b feat: 5.4.0版本大更新,新增多端桌面小组件与多项功能优化
- 重构「灵感」模块为「发现」模块,统一页面命名与文案
- 新增flutter_tts语音朗读依赖与鸿蒙Nearby配对方式
- 添加Android/iOS/鸿蒙全平台桌面小组件支持(7种类型)
- 完善文件传输模块,新增画布邀请消息与删除会话功能
- 优化协作画布光标广播节流逻辑,修复已知bug
- 更新应用英文名与隐私政策入口,新增翻译API抽象层
- 移除用户中心多余的加号按钮,完善空状态组件类型
2026-05-19 05:39:50 +08:00

184 lines
6.7 KiB
Python

"""
闲言APP - 文件传输全流程E2E测试脚本
创建时间: 2026-05-19
作用: 模拟两个设备从发现→配对→消息→文件→画布→屏幕共享完整流程
"""
import asyncio
import json
import time
WS_URL = 'wss://tools.wktyl.com:9443'
async def ws_connect():
import websockets
return await websockets.connect(WS_URL, ping_interval=None)
async def register_device(ws, device_id, alias, fingerprint, user_id=None, ip_city=None, ip_range=None):
msg = {
'type': 'register',
'data': {'deviceId': device_id, 'alias': alias, 'deviceType': 'mobile', 'fingerprint': fingerprint, 'protocol': 'xianyan-v1'}
}
if user_id:
msg['data']['userId'] = user_id
if ip_city:
msg['data']['ipCity'] = ip_city
if ip_range:
msg['data']['ipRange'] = ip_range
await ws.send(json.dumps(msg))
for _ in range(5):
resp = json.loads(await asyncio.wait_for(ws.recv(), timeout=10))
if resp.get('type') == 'registered':
return resp
return None
async def drain_messages(ws, count=3, timeout=5):
messages = []
for _ in range(count):
try:
msg = json.loads(await asyncio.wait_for(ws.recv(), timeout=timeout))
messages.append(msg)
except asyncio.TimeoutError:
break
return messages
async def test_full_e2e():
"""全流程: 注册→配对码配对→文本消息→文件元数据→画布同步→屏幕共享"""
ts = int(time.time())
ws_a = await ws_connect()
ws_b = await ws_connect()
print(' [1/7] 注册设备...')
reg_a = await register_device(ws_a, f'e2e-a-{ts}', 'iPhone 16 Pro', f'fp_e2e_a_{ts}', user_id=f'user-e2e-{ts}', ip_city='北京', ip_range='110.123.45.0/24')
reg_b = await register_device(ws_b, f'e2e-b-{ts}', 'MacBook Pro', f'fp_e2e_b_{ts}', user_id=f'user-e2e-{ts}', ip_city='北京', ip_range='110.123.45.0/24')
assert reg_a is not None and reg_b is not None, 'Registration failed'
print(' [PASS] 设备注册成功')
print(' [2/7] 配对码配对...')
await ws_a.send(json.dumps({'type': 'pairing-code-create', 'data': {'alias': 'iPhone 16 Pro'}}))
code = None
for _ in range(5):
msg = json.loads(await asyncio.wait_for(ws_a.recv(), timeout=10))
if msg.get('type') == 'pairing-code-created':
code = msg['pairingCode']
break
assert code is not None, 'Failed to create pairing code'
await ws_b.send(json.dumps({'type': 'pairing-code-join', 'data': {'pairingCode': code}}))
matched = False
for _ in range(5):
msg_b = json.loads(await asyncio.wait_for(ws_b.recv(), timeout=10))
if msg_b.get('type') == 'pairing-matched' and msg_b.get('success'):
matched = True
break
for _ in range(5):
msg_a = json.loads(await asyncio.wait_for(ws_a.recv(), timeout=10))
if msg_a.get('type') == 'pairing-matched' and msg_a.get('success'):
break
assert matched, 'Pairing code match failed'
print(f' [PASS] 配对码配对成功: {code}')
print(' [3/7] 文本消息...')
await ws_a.send(json.dumps({
'type': 'textMessage',
'to': f'fp_e2e_b_{ts}',
'message': {'text': 'Hello from iPhone!'},
'timestamp': int(time.time() * 1000)
}))
received = False
for _ in range(5):
msg = json.loads(await asyncio.wait_for(ws_b.recv(), timeout=10))
if msg.get('type') == 'textMessage':
received = True
assert msg.get('from') == f'fp_e2e_a_{ts}', f'Wrong from: {msg}'
break
assert received, 'Text message not received'
print(' [PASS] 文本消息收发成功')
print(' [4/7] 文件元数据...')
await ws_a.send(json.dumps({
'type': 'fileMeta',
'to': f'fp_e2e_b_{ts}',
'file': {'fileName': 'photo.jpg', 'fileSize': 1024000, 'mimeType': 'image/jpeg'},
'timestamp': int(time.time() * 1000)
}))
received = False
for _ in range(5):
msg = json.loads(await asyncio.wait_for(ws_b.recv(), timeout=10))
if msg.get('type') == 'fileMeta':
received = True
break
assert received, 'File meta not received'
print(' [PASS] 文件元数据传输成功')
print(' [5/7] 画布同步...')
canvas_id = f'e2e-canvas-{ts}'
await ws_a.send(json.dumps({'type': 'canvas-join', 'payload': {'canvasId': canvas_id}}))
await drain_messages(ws_a, 3)
await ws_b.send(json.dumps({'type': 'canvas-join', 'payload': {'canvasId': canvas_id}}))
await drain_messages(ws_a, 3)
await drain_messages(ws_b, 3)
await ws_a.send(json.dumps({
'type': 'canvas-stroke',
'payload': {'canvasId': canvas_id, 'stroke': {'strokeId': 'e2e-s1', 'points': [[50, 60]], 'color': '#000', 'width': 2, 'tool': 'pen'}}
}))
received = False
for _ in range(5):
msg = json.loads(await asyncio.wait_for(ws_b.recv(), timeout=10))
if msg.get('type') == 'canvas-stroke':
received = True
break
assert received, 'Canvas stroke not received'
print(' [PASS] 画布同步成功')
print(' [6/7] 屏幕共享请求...')
await ws_a.send(json.dumps({
'type': 'screen-share-request',
'to': f'fp_e2e_b_{ts}',
'payload': {'direction': 'view'}
}))
received = False
for _ in range(5):
msg = json.loads(await asyncio.wait_for(ws_b.recv(), timeout=10))
if msg.get('type') == 'screen-share-request':
received = True
break
assert received, 'Screen share request not received'
await ws_b.send(json.dumps({
'type': 'screen-share-accept',
'to': f'fp_e2e_a_{ts}',
'payload': {'direction': 'view'}
}))
received = False
for _ in range(5):
msg = json.loads(await asyncio.wait_for(ws_a.recv(), timeout=10))
if msg.get('type') == 'screen-share-accept':
received = True
break
assert received, 'Screen share accept not received'
print(' [PASS] 屏幕共享请求/接受成功')
print(' [7/7] 心跳保活...')
await ws_a.send(json.dumps({'type': 'heartbeat', 'data': {'timestamp': int(time.time() * 1000)}}))
got_ack = False
for _ in range(5):
msg = json.loads(await asyncio.wait_for(ws_a.recv(), timeout=10))
if msg.get('type') == 'heartbeat_ack':
got_ack = True
break
assert got_ack, 'Heartbeat ack not received'
print(' [PASS] 心跳保活成功')
await ws_a.close()
await ws_b.close()
print('\n ===== 全流程E2E测试通过! =====')
async def run_all_tests():
print('\n===== 全流程E2E测试 =====')
await test_full_e2e()
if __name__ == '__main__':
asyncio.run(run_all_tests())