//! df-tunnel ↔ u-relay 端到端联调(Agent 侧握手 + 存活 + 断开) //! //! 起 WsTunnelClient 连本机 u-relay :19180 /ws/agent: //! 1. Hello{kind:"agent", channel_id, token} → u-relay 立即回 hello_ack(3s PROBE 内收到) //! 2. send_raw_event 发一条事件(relay 无 commander 在线 → 静默丢弃,不报错) //! 3. 存活 2s(收发循环稳定,无 panic) //! 4. 优雅 disconnect //! //! 运行:先起 u-relay(U_RELAY_TOKEN=devflow-relay-default-token U_RELAY_ADDR=127.0.0.1:19180), //! 再 `cargo run -p df-tunnel --example e2e_u_relay`。 use std::sync::Arc; use std::time::Duration; use df_tunnel::tunnel::CommandHandler; use df_tunnel::{TunnelClient, WsTunnelClient}; use tokio::sync::Mutex; #[tokio::main] async fn main() -> anyhow::Result<()> { let url = "ws://127.0.0.1:19180/ws/agent"; let channel_id = "test-channel-e2e-01"; let token = "devflow-relay-default-token"; let client = Arc::new(WsTunnelClient::new()); // 注册 command 回调(收 commander 指令;本测试预计不触发) let got_cmd = Arc::new(Mutex::new(0usize)); let counter = got_cmd.clone(); let on_command: CommandHandler = Arc::new(move |_payload| { let counter = counter.clone(); Box::pin(async move { *counter.lock().await += 1; }) }); println!("[1] connect {url} channel={channel_id}"); client .connect(url, channel_id, token, on_command) .await .map_err(|e| anyhow::anyhow!("connect 失败: {e}"))?; println!(" connect Ok"); assert!(client.is_connected(), "is_connected 应为 true"); println!("[2] is_connected == true ✓(u-relay 已回 hello_ack,握手成功)"); // 发一条原始事件(relay 无 commander 在线 → 静默丢弃,不应报错) client .send_raw_event(serde_json::json!({"type": "e2e_probe", "note": "df-tunnel e2e"})) .await .map_err(|e| anyhow::anyhow!("send_raw_event 失败: {e}"))?; println!("[3] send_raw_event Ok(事件出站;relay 无对端静默丢弃符合预期)"); // 保持 2s 让心跳/收发循环稳定跑(无 panic 即可) tokio::time::sleep(Duration::from_secs(2)).await; assert!(client.is_connected(), "2s 后仍应 connected(心跳保活)"); println!("[4] 2s 存活 ✓"); // 优雅断开 client.disconnect().await?; assert!(!client.is_connected(), "disconnect 后 is_connected 应为 false"); println!("[5] disconnect Ok,is_connected == false ✓"); let got = *got_cmd.lock().await; println!("───"); println!("df-tunnel ↔ u-relay E2E: 5/5 通过(command 收到 {got} 条,预期 0)"); Ok(()) }