//! Integration tests for the `await` command. use std::fs; use std::io::{Read, Write}; use std::net::TcpListener; use std::thread; use assert_cmd::Command; use predicates::prelude::*; use tempfile::TempDir; fn cargo_command() -> Command { Command::cargo_bin("await").expect("binary") } #[test] fn no_args_prints_quick_help_card() { let mut command = cargo_command(); command .assert() .code(2) .stdout(predicate::str::is_empty()) .stderr(predicate::str::contains( "error: provide a subcommand: path, port, http, or run", )) .stderr(predicate::str::contains("await - Mercury Toolbox")) .stderr(predicate::str::contains("Usage:")) .stderr(predicate::str::contains("await [OPTIONS] path ")) .stderr(predicate::str::contains("await [OPTIONS] port ")) .stderr(predicate::str::contains( "Type 'await --help' for the full command reference.", )); } fn spawn_http_server(response: &'static [u8]) -> u16 { let listener = TcpListener::bind(("127.0.0.1", 0)).expect("listener"); let port = listener.local_addr().expect("address").port(); thread::spawn(move || { if let Ok((mut stream, _)) = listener.accept() { let mut buffer = [0_u8; 1024]; let _ = stream.read(&mut buffer); let _ = stream.write_all(response); } }); port } #[test] fn help_mentions_v3_modes() { let mut command = cargo_command(); command .arg("--help") .assert() .success() .stdout(predicate::str::contains("path ")) .stdout(predicate::str::contains("port ")) .stdout(predicate::str::contains("http ")) .stdout(predicate::str::contains("--state exists|missing")); } #[test] fn path_mode_supports_missing_state() { let temp = TempDir::new().expect("temp dir"); let missing = temp.path().join("missing.flag"); let mut command = cargo_command(); command .arg("path") .arg(missing) .arg("--json") .arg("--state") .arg("missing") .assert() .success() .stdout(predicate::str::contains("\"ok\":true")) .stdout(predicate::str::contains("\"kind\":\"path\"")) .stdout(predicate::str::contains("\"state\":\"missing\"")); } #[test] fn port_mode_uses_tcp_scheme_target() { let listener = TcpListener::bind(("127.0.0.1", 0)).expect("listener"); let address = listener.local_addr().expect("address"); let mut command = cargo_command(); command .arg("--json") .arg("--timeout") .arg("800ms") .arg("--interval") .arg("50ms") .arg("port") .arg(format!("tcp://127.0.0.1:{}", address.port())) .assert() .success() .stdout(predicate::str::contains("\"ok\":true")) .stdout(predicate::str::contains("\"kind\":\"port\"")) .stdout(predicate::str::contains("\"target\":\"tcp://127.0.0.1:")); drop(listener); } #[test] fn http_mode_supports_status_matching() { let port = spawn_http_server(b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\n\r\n"); let mut command = cargo_command(); command .arg("--json") .arg("--timeout") .arg("800ms") .arg("--interval") .arg("50ms") .arg("http") .arg(format!("http://127.0.0.1:{port}/health")) .arg("--status") .arg("204") .assert() .success() .stdout(predicate::str::contains("\"ok\":true")) .stdout(predicate::str::contains("\"kind\":\"http\"")) .stdout(predicate::str::contains("\"status_code\":204")); } #[cfg(windows)] #[test] fn run_mode_retries_until_exit_code_matches() { let temp = TempDir::new().expect("temp dir"); let marker = temp.path().join("ready.flag"); let script = temp.path().join("flip.ps1"); fs::write( &script, format!( "if (Test-Path '{}') {{ exit 0 }}\nNew-Item -ItemType File -Path '{}' | Out-Null\nexit 9\n", marker.display(), marker.display() ), ) .expect("script"); let mut command = cargo_command(); command .arg("--json") .arg("--timeout") .arg("20s") .arg("--interval") .arg("100ms") .arg("run") .arg("--shell") .arg("pwsh") .arg("--exit-code") .arg("0") .arg("--") .arg(script) .assert() .success() .stdout(predicate::str::contains("\"ok\":true")) .stdout(predicate::str::contains("\"attempts\":2")) .stdout(predicate::str::contains("\"kind\":\"run\"")) .stdout(predicate::str::contains("\"exit_code\":0")); } #[test] fn path_mode_times_out_when_requested_state_never_arrives() { let temp = TempDir::new().expect("temp dir"); let existing = temp.path().join("ready.flag"); fs::write(&existing, "ready").expect("flag"); let mut command = cargo_command(); command .args(["--timeout", "150ms", "--interval", "50ms", "path"]) .arg(&existing) .arg("--state") .arg("missing") .assert() .code(1) .stdout(predicate::str::contains("timeout condition=path:missing:")) .stdout(predicate::str::contains("last=path:ok=false:exists=true")); }