diff options
| -rw-r--r-- | tests/client_mode.rs | 47 | ||||
| -rw-r--r-- | tests/live_handoff.rs | 25 |
2 files changed, 65 insertions, 7 deletions
diff --git a/tests/client_mode.rs b/tests/client_mode.rs index a946af7c..71d3ff8d 100644 --- a/tests/client_mode.rs +++ b/tests/client_mode.rs @@ -716,6 +716,39 @@ fn output_len(output: &SharedOutput) -> usize { output.lock().unwrap_or_else(|p| p.into_inner()).text.len() } +fn sidebar_row_click(screen: &str, label: &str) -> Vec<u8> { + let sidebar_width = screen + .lines() + .find_map(|line| line.chars().position(|character| character == '│')) + .expect("visible sidebar boundary"); + let row = screen + .lines() + .position(|line| { + line.chars() + .take(sidebar_width) + .collect::<String>() + .contains(label) + }) + .unwrap_or_else(|| panic!("sidebar row {label:?} is not visible: {screen}")) + + 1; + format!("\x1b[<0;7;{row}M\x1b[<0;7;{row}m").into_bytes() +} + +#[test] +fn sidebar_row_click_tracks_restored_workspace_count() { + for restored in [false, true] { + let screen = format!( + " machines │\n │\n ▾ Local │local-returned in pane output\n{} · local-returned └─────────────────\n", + if restored { " · restored │\n" } else { "" } + ); + let row = if restored { 5 } else { 4 }; + assert_eq!( + sidebar_row_click(&screen, "local-returned"), + format!("\x1b[<0;7;{row}M\x1b[<0;7;{row}m").into_bytes() + ); + } +} + /// Spawns a server + real thin client under a PTY and waits until the client /// has attached and rendered a frame. Returns the pieces plus a shared buffer /// that keeps accumulating PTY output (including teardown) on a background @@ -1074,20 +1107,22 @@ fn federated_client_starts_without_local_and_survives_its_restart() { local_pane, "printf 'LOCAL_RECOVERED_SURFACE\\n'", ); - let watermark = output_len(&output); // Select the fresh workspace below Local's restored workspace. - input.write_all(b"\x1b[<0;7;5M\x1b[<0;7;5m").unwrap(); + // A fast shutdown may leave no saved workspace, so locate the actual row. + input + .write_all(&sidebar_row_click(&screen_text(), "local-returned")) + .unwrap(); assert!( wait_until(Duration::from_secs(10), Duration::from_millis(20), || { - read_output(&output)[watermark..].contains("LOCAL_RECOVERED_SURFACE") + screen_text().contains("LOCAL_RECOVERED_SURFACE") }), "recovered Local must be selectable: {}", - read_output(&output) + screen_text() ); // A coherent frame precedes the final host-effects fence; input stays gated until then. assert!( wait_until(Duration::from_secs(8), Duration::from_millis(100), || { - if read_output(&output)[watermark..].contains("LOCAL_INPUT_RECOVERED") { + if screen_text().contains("LOCAL_INPUT_RECOVERED") { return true; } input @@ -1096,7 +1131,7 @@ fn federated_client_starts_without_local_and_survives_its_restart() { false }), "recovered Local must accept input: {}", - read_output(&output) + screen_text() ); drop(input); drop(client); diff --git a/tests/live_handoff.rs b/tests/live_handoff.rs index e40f8268..ea4b84df 100644 --- a/tests/live_handoff.rs +++ b/tests/live_handoff.rs @@ -1581,9 +1581,13 @@ fn live_handoff_keeps_agent_started_pane_after_agent_exits() { let api_socket = runtime_dir.join("herdr.sock"); let started_marker = base.join("agent-started"); let exited_marker = base.join("agent-exited"); + let ready_marker = base.join("shell-ready"); let shell_marker = base.join("shell-after-agent"); let bin = base.join("bin"); fs::create_dir_all(&bin).unwrap(); + let delayed_shell = bin.join("delayed-shell"); + fs::write(&delayed_shell, "#!/bin/sh\n/bin/sleep 0.4\nexec /bin/sh\n").unwrap(); + fs::set_permissions(&delayed_shell, fs::Permissions::from_mode(0o755)).unwrap(); let fake_pi = bin.join("pi"); fs::write( &fake_pi, @@ -1601,7 +1605,10 @@ fn live_handoff_keeps_agent_started_pane_after_agent_exits() { &config_home, &runtime_dir, &api_socket, - &[("PATH", path.as_str())], + &[ + ("PATH", path.as_str()), + ("SHELL", delayed_shell.to_str().unwrap()), + ], ); wait_for_socket(&api_socket, Duration::from_secs(10)); register_runtime_dir(&runtime_dir); @@ -1619,6 +1626,22 @@ fn live_handoff_keeps_agent_started_pane_after_agent_exits() { .unwrap() .to_string(); + assert_ok(request( + &api_socket, + serde_json::json!({ + "id": "test:shell-ready", + "method": "pane.send_input", + "params": { + "pane_id": pane_id, + "text": format!("printf ready > {}", ready_marker.display()), + "keys": ["Enter"] + } + }), + )); + // Creation acknowledges the PTY, not an idle interactive shell. A real + // shell command must execute before this raw agent.start request. + support::wait_for_file(&ready_marker, Duration::from_secs(5)); + let started = request( &api_socket, serde_json::json!({ |
