test_network_stubs.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. # tests/test_network_stubs.py
  2. """NetClientStub + NetworkCtlStub: 真实环回 socket 模拟网关。"""
  3. import socket
  4. import struct
  5. import sys
  6. import threading
  7. import lupa.luajit21 as lj
  8. sys.path.insert(0, "protocol_bot")
  9. from encode6bitbuf_final import encode6 # type: ignore # noqa: E402
  10. from apilot.stubs.netclient import NetClientStub # noqa: E402
  11. from apilot.stubs.networkctl import NetworkCtlStub, dispatch_frame # noqa: E402
  12. def _build_frame(msg_id: int, body: bytes) -> bytes:
  13. """encode6(9×i32 header) + body。字段序对齐 wire.pack_frame。"""
  14. head = struct.pack("<9i", 0, msg_id, 1, 0, 0, 0, 0, len(body), 1)
  15. return encode6(head) + body
  16. def _spawn_gateway(handler):
  17. """启一个临时监听,handler(server_sock) 在调用线程同步完成一次会话。"""
  18. srv = socket.socket()
  19. srv.bind(("127.0.0.1", 0))
  20. srv.listen(1)
  21. port = srv.getsockname()[1]
  22. threading.Thread(target=handler, args=(srv,), daemon=True).start()
  23. return port
  24. def _fake_gateway_echo529(server_sock):
  25. """收一帧, 回一个 529 帧。body 在线缆上是 raw bytes(仅 header 编码)。"""
  26. conn, _ = server_sock.accept()
  27. try:
  28. conn.recv(65536) # drain client frame
  29. conn.sendall(_build_frame(529, b"key32/session123"))
  30. finally:
  31. conn.close()
  32. server_sock.close()
  33. def test_sendmsg_roundtrip_and_dispatch():
  34. port = _spawn_gateway(_fake_gateway_echo529)
  35. nc = NetClientStub()
  36. nc.connect("127.0.0.1", port)
  37. ctl = NetworkCtlStub(nc)
  38. ctl.send_msg(2001, 123456, 1, 2, 3, b"uid/key/ch/1/0", 19)
  39. frames = nc.tick(1.0)
  40. assert any(f.msg_id == 529 for f in frames), f"expected 529 in {frames}"
  41. rt = lj.LuaRuntime()
  42. rt.execute("""
  43. got = nil
  44. function on_529(msg)
  45. local h = msg:GetHeader()
  46. local data = msg:GetData()
  47. got = h.msgId .. ":" .. data:ReadString(5)
  48. end
  49. """)
  50. ctl.register_lua_handler(529, rt.eval("on_529"))
  51. target = next(f for f in frames if f.msg_id == 529)
  52. dispatch_frame(rt, ctl, target)
  53. assert rt.eval("got") == "529:key32"
  54. nc.disconnect()
  55. def test_register_unregister():
  56. """register_lua_handler 覆盖, unregister_all 清空。"""
  57. nc = NetClientStub()
  58. ctl = NetworkCtlStub(nc)
  59. rt = lj.LuaRuntime()
  60. rt.execute("function h(msg) end")
  61. cb = rt.eval("h")
  62. ctl.register_lua_handler(10, cb)
  63. ctl.register_lua_handler(10, cb) # 覆盖
  64. assert ctl.handlers[10] is cb
  65. ctl.unregister_all()
  66. assert ctl.handlers == {}
  67. def test_dispatch_no_handler_is_noop():
  68. """无 handler 的帧不抛异常。"""
  69. nc = NetClientStub()
  70. ctl = NetworkCtlStub(nc)
  71. from apilot.wire import Frame
  72. rt = lj.LuaRuntime()
  73. dispatch_frame(rt, ctl, Frame(recog=0, msg_id=99999, p1=0, p2=0, p3=0, tail=1, body=b""))
  74. def test_is_connected_and_disconnect():
  75. nc = NetClientStub()
  76. assert not nc.is_connected()
  77. port = _spawn_gateway(_fake_gateway_echo529)
  78. nc.connect("127.0.0.1", port)
  79. assert nc.is_connected()
  80. nc.disconnect()
  81. assert not nc.is_connected()