Files

176 lines
5.3 KiB
Rust

//! 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 <PATH>"))
.stderr(predicate::str::contains("await [OPTIONS] port <TARGET>"))
.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 <PATH>"))
.stdout(predicate::str::contains("port <TARGET>"))
.stdout(predicate::str::contains("http <URL>"))
.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"));
}