# auth_peer_close_bridge_test —— 40250 CAccountConnector::OnRemoteDisconnect # 到 M2Client LoginPhase owner 的回归。运行时需要 MT_PROTOCOL=classic。 extends SceneTree var _fail := 0 var _login_failures := 0 var _connect_failures := 0 func _ck(ok: bool, message: String) -> void: if not ok: _fail += 1 printerr("FAIL: " + message) func _init() -> void: call_deferred("_run") func _run() -> void: if not ClassDB.class_exists("M2Client"): _ck(false, "M2Client extension is registered") _finish() return var server := TCPServer.new() var listen_error := server.listen(0, "127.0.0.1") _ck(listen_error == OK, "local auth peer-close fixture listens") if listen_error != OK: _finish() return var client: Node = ClassDB.instantiate("M2Client") get_root().add_child(client) client.connect("login_failed", Callable(self, "_on_login_failure")) if client.has_signal("connect_failed"): client.connect("connect_failed", Callable(self, "_on_connect_failure")) else: _ck(false, "M2Client exposes connect_failed") client.connect_to_server("127.0.0.1", server.get_local_port(), "127.0.0.1", 1, "admin", "secret") for _i in range(30): if server.is_connection_available(): break await process_frame _ck(server.is_connection_available(), "auth peer-close fixture accepts the connection") if server.is_connection_available(): var peer := server.take_connection() _ck(peer != null, "auth peer-close fixture gets the accepted peer") if peer != null: peer.disconnect_from_host() # Let M2Client observe EOF and run the same pump that the real LoginWindow # receives after CAccountConnector::OnRemoteDisconnect. for _i in range(30): await process_frame _ck(_login_failures == 0, "auth peer close is not reported as credential login failure") _ck(_connect_failures == 0, "auth peer close is not reported as connect failure") _ck(String(client.get_stage()) != "failed", "auth peer close retains a non-terminal LoginPhase owner") # The Offline owner must remain usable: a second LoginWindow.Connect replaces # only the auth transport, then the same peer-close rule applies again. var retry_server := TCPServer.new() var retry_error := retry_server.listen(0, "127.0.0.1") _ck(retry_error == OK, "auth peer-close retry fixture listens") if retry_error == OK: client.retry_login("127.0.0.1", retry_server.get_local_port(), "127.0.0.1", 1, "admin", "secret") for _i in range(30): if retry_server.is_connection_available(): break await process_frame _ck(retry_server.is_connection_available(), "auth peer-close retry replaces the old auth transport") if retry_server.is_connection_available(): var retry_peer := retry_server.take_connection() if retry_peer != null: retry_peer.disconnect_from_host() for _i in range(30): await process_frame _ck(_login_failures == 0 and _connect_failures == 0, "auth peer-close retry remains separate from both failure callbacks") _ck(String(client.get_stage()) != "failed", "auth peer-close retry keeps the LoginPhase owner non-terminal") client.disconnect_from_server() client.queue_free() server.stop() retry_server.stop() _finish() func _finish() -> void: if _fail == 0: print("PASS: auth_peer_close_bridge_test") else: printerr("%d check(s) failed" % _fail) quit(0 if _fail == 0 else 1) func _on_login_failure(_reason: String) -> void: _login_failures += 1 func _on_connect_failure(_reason: String) -> void: _connect_failures += 1