use futures_util::{SinkExt, StreamExt}; use serde_json::json; use tokio_tungstenite::connect_async; use std::time::Duration; async fn ws_available(url: &str) -> bool { match connect_async(url).await { Ok((_ws, _)) => true, Err(_) => false, } } async fn http_healthy() -> bool { let base = std::env::var("SDK_RELAY_HTTP").unwrap_or_else(|_| "http://localhost:8091".to_string()); let client = match reqwest::Client::builder().timeout(Duration::from_millis(500)).build() { Ok(c) => c, Err(_) => return false, }; match client.get(format!("{}/health", base)).send().await { Ok(resp) => resp.status().is_success(), Err(_) => false, } } #[tokio::test] async fn websocket_ping_pong_should_work() { let url = std::env::var("SDK_RELAY_WS").unwrap_or_else(|_| "ws://localhost:8090".to_string()); if !http_healthy().await || !ws_available(&url).await { eprintln!("sdk_relay WS indisponible, test ping/pong ignoré"); return; } let (mut ws, _) = connect_async(url).await.expect("connect ws"); let ping = json!({"type":"ping","client_id":"functional-test","timestamp":1703001600u64}).to_string(); ws.send(tokio_tungstenite::tungstenite::Message::Text(ping)) .await .expect("send ping"); let msg = ws.next().await.expect("no response").expect("ws err"); let txt = msg.into_text().expect("not text"); let json: serde_json::Value = serde_json::from_str(&txt).expect("invalid json"); assert_eq!(json.get("type").and_then(|v| v.as_str()).unwrap_or(""), "pong"); } #[tokio::test] async fn websocket_subscribe_should_ack() { let url = std::env::var("SDK_RELAY_WS").unwrap_or_else(|_| "ws://localhost:8090".to_string()); if !http_healthy().await || !ws_available(&url).await { eprintln!("sdk_relay WS indisponible, test subscribe ignoré"); return; } let (mut ws, _) = connect_async(url).await.expect("connect ws"); let subscribe = json!({ "type":"subscribe", "subscriptions":["notifications","health","metrics"], "client_id":"functional-test", "timestamp":1703001600u64 }).to_string(); ws.send(tokio_tungstenite::tungstenite::Message::Text(subscribe)) .await .expect("send subscribe"); let msg = ws.next().await.expect("no response").expect("ws err"); let txt = msg.into_text().expect("not text"); let json: serde_json::Value = serde_json::from_str(&txt).expect("invalid json"); assert_eq!(json.get("type").and_then(|v| v.as_str()).unwrap_or(""), "subscribe_response"); assert_eq!(json.get("status").and_then(|v| v.as_str()).unwrap_or(""), "subscribed"); }