|
|
@@ -0,0 +1,101 @@
|
|
|
+# tests/test_network_stubs.py
|
|
|
+"""NetClientStub + NetworkCtlStub: 真实环回 socket 模拟网关。"""
|
|
|
+import socket
|
|
|
+import struct
|
|
|
+import sys
|
|
|
+import threading
|
|
|
+
|
|
|
+import lupa.luajit21 as lj
|
|
|
+
|
|
|
+sys.path.insert(0, "protocol_bot")
|
|
|
+from encode6bitbuf_final import encode6 # type: ignore # noqa: E402
|
|
|
+
|
|
|
+from apilot.stubs.netclient import NetClientStub # noqa: E402
|
|
|
+from apilot.stubs.networkctl import NetworkCtlStub, dispatch_frame # noqa: E402
|
|
|
+
|
|
|
+
|
|
|
+def _build_frame(msg_id: int, body: bytes) -> bytes:
|
|
|
+ """encode6(9×i32 header) + body。字段序对齐 wire.pack_frame。"""
|
|
|
+ head = struct.pack("<9i", 0, msg_id, 1, 0, 0, 0, 0, len(body), 1)
|
|
|
+ return encode6(head) + body
|
|
|
+
|
|
|
+
|
|
|
+def _spawn_gateway(handler):
|
|
|
+ """启一个临时监听,handler(server_sock) 在调用线程同步完成一次会话。"""
|
|
|
+ srv = socket.socket()
|
|
|
+ srv.bind(("127.0.0.1", 0))
|
|
|
+ srv.listen(1)
|
|
|
+ port = srv.getsockname()[1]
|
|
|
+ threading.Thread(target=handler, args=(srv,), daemon=True).start()
|
|
|
+ return port
|
|
|
+
|
|
|
+
|
|
|
+def _fake_gateway_echo529(server_sock):
|
|
|
+ """收一帧, 回一个 529 帧。body 在线缆上是 raw bytes(仅 header 编码)。"""
|
|
|
+ conn, _ = server_sock.accept()
|
|
|
+ try:
|
|
|
+ conn.recv(65536) # drain client frame
|
|
|
+ conn.sendall(_build_frame(529, b"key32/session123"))
|
|
|
+ finally:
|
|
|
+ conn.close()
|
|
|
+ server_sock.close()
|
|
|
+
|
|
|
+
|
|
|
+def test_sendmsg_roundtrip_and_dispatch():
|
|
|
+ port = _spawn_gateway(_fake_gateway_echo529)
|
|
|
+
|
|
|
+ nc = NetClientStub()
|
|
|
+ nc.connect("127.0.0.1", port)
|
|
|
+ ctl = NetworkCtlStub(nc)
|
|
|
+ ctl.send_msg(2001, 123456, 1, 2, 3, b"uid/key/ch/1/0", 19)
|
|
|
+
|
|
|
+ frames = nc.tick(1.0)
|
|
|
+ assert any(f.msg_id == 529 for f in frames), f"expected 529 in {frames}"
|
|
|
+
|
|
|
+ rt = lj.LuaRuntime()
|
|
|
+ rt.execute("""
|
|
|
+got = nil
|
|
|
+function on_529(msg)
|
|
|
+ local h = msg:GetHeader()
|
|
|
+ local data = msg:GetData()
|
|
|
+ got = h.msgId .. ":" .. data:ReadString(5)
|
|
|
+end
|
|
|
+""")
|
|
|
+ ctl.register_lua_handler(529, rt.eval("on_529"))
|
|
|
+ target = next(f for f in frames if f.msg_id == 529)
|
|
|
+ dispatch_frame(rt, ctl, target)
|
|
|
+ assert rt.eval("got") == "529:key32"
|
|
|
+ nc.disconnect()
|
|
|
+
|
|
|
+
|
|
|
+def test_register_unregister():
|
|
|
+ """register_lua_handler 覆盖, unregister_all 清空。"""
|
|
|
+ nc = NetClientStub()
|
|
|
+ ctl = NetworkCtlStub(nc)
|
|
|
+ rt = lj.LuaRuntime()
|
|
|
+ rt.execute("function h(msg) end")
|
|
|
+ cb = rt.eval("h")
|
|
|
+ ctl.register_lua_handler(10, cb)
|
|
|
+ ctl.register_lua_handler(10, cb) # 覆盖
|
|
|
+ assert ctl.handlers[10] is cb
|
|
|
+ ctl.unregister_all()
|
|
|
+ assert ctl.handlers == {}
|
|
|
+
|
|
|
+
|
|
|
+def test_dispatch_no_handler_is_noop():
|
|
|
+ """无 handler 的帧不抛异常。"""
|
|
|
+ nc = NetClientStub()
|
|
|
+ ctl = NetworkCtlStub(nc)
|
|
|
+ from apilot.wire import Frame
|
|
|
+ rt = lj.LuaRuntime()
|
|
|
+ dispatch_frame(rt, ctl, Frame(recog=0, msg_id=99999, p1=0, p2=0, p3=0, tail=1, body=b""))
|
|
|
+
|
|
|
+
|
|
|
+def test_is_connected_and_disconnect():
|
|
|
+ nc = NetClientStub()
|
|
|
+ assert not nc.is_connected()
|
|
|
+ port = _spawn_gateway(_fake_gateway_echo529)
|
|
|
+ nc.connect("127.0.0.1", port)
|
|
|
+ assert nc.is_connected()
|
|
|
+ nc.disconnect()
|
|
|
+ assert not nc.is_connected()
|