//! Acceptance test for `SessionTask`: a fake WS server sends `hello\r\n` as a //! binary frame, and we assert the `vt100` screen sees it within the //! interval. use std::time::Duration; use axum::extract::ws::{Message, WebSocketUpgrade}; use axum::response::Response; use axum::routing::any; use axum::Router; use maestro_tui::session_task::{SessionState, SessionTask}; async fn attach_handler(ws: WebSocketUpgrade) -> Response { ws.on_upgrade(|mut socket| async move { // Push initial bytes immediately. if socket .send(Message::Binary(b"hello\r\n".to_vec())) .await .is_err() { return; } // Keep the connection open so the client doesn't enter reconnect. tokio::time::sleep(Duration::from_secs(2)).await; }) } #[tokio::test(flavor = "multi_thread")] async fn vt100_screen_receives_pty_bytes() { let app = Router::new().route("/api/attach/x", any(attach_handler)); let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = listener.local_addr().unwrap(); tokio::spawn(async move { axum::serve(listener, app).await.unwrap() }); let url = format!("ws://{addr}/api/attach/x"); let handle = SessionTask::spawn(url, 24, 80); // Poll for up to 500 ms looking for "hello" in row 0. for _ in 0..50 { tokio::time::sleep(Duration::from_millis(10)).await; if handle.state() != SessionState::Live { continue; } let parser = handle.screen.lock().expect("vt100 parser mutex"); let screen = parser.screen(); let row: String = (0..5) .map(|c| screen.cell(0, c).unwrap().contents()) .collect(); if row.starts_with("hello") { return; } } panic!( "vt100 screen never contained 'hello'; state={:?}", handle.state() ); }