176 lines
5.3 KiB
Rust
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"));
|
|
}
|