use super::support::{Fixture, fixture, hermetic, remove, repository, service_command, service_executable, spawn_owned, wrapped};
use std::path::{Path, PathBuf};
use std::process::{Child, Output, Stdio};
use std::time::{Duration, Instant};
const PATIENCE: Duration = Duration::from_secs(60);
const SETTLE: Duration = Duration::from_millis(500);
fn finished(mut child: Child) -> Output {
let began: Instant = Instant::now();
loop {
if child.try_wait().expect("the child's state").is_some() {
return child.wait_with_output().expect("the child's output");
}
if began.elapsed() >= PATIENCE {
let _killed = child.kill();
let _waited = child.wait();
panic!("the start was still running after {PATIENCE:?}: a refusal ends at once");
}
std::thread::sleep(Duration::from_millis(10));
}
}
#[test]
fn a_start_with_no_socket_and_no_address_is_refused_and_creates_nothing() {
let fixture: Fixture = fixture("service-mode-no-socket");
let journal: PathBuf = fixture.root.join("journal-not-made");
for (label, listen_pid, listen_fds) in [("no variables", None, None), ("a socket for another process", Some("1"), Some("1")), ("a count of none", Some("1"), Some("0"))] {
let mut command = wrapped(&fixture);
command.args(["monochromatic-git-policy-cli", "service", "run", "--journal"]).arg(&journal);
if let (Some(pid), Some(fds)) = (listen_pid.as_ref(), listen_fds.as_ref()) {
command.env("LISTEN_PID", pid).env("LISTEN_FDS", fds);
}
let output: Output = finished(command.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().expect("the wrapper starts"));
let stderr: String = String::from_utf8_lossy(&output.stderr).into_owned();
assert_eq!(output.status.code(), Some(git_policy_cli::service_main::EXIT_NOT_LISTENING), "{label}: {stderr}");
assert!(stderr.contains("started by the service manager") && stderr.contains("--path <socket>"), "{label}: {stderr}");
assert!(!journal.exists(), "{label}: a refused start left a directory");
}
let bare: Output = finished(wrapped(&fixture).args(["monochromatic-git-policy-cli", "service", "run"]).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().expect("the wrapper starts"));
assert!(String::from_utf8_lossy(&bare.stderr).contains("started by the service manager"), "{}", String::from_utf8_lossy(&bare.stderr));
remove(&fixture);
}
#[test]
fn the_words_behind_a_global_option_start_no_server() {
let fixture: Fixture = fixture("service-mode-behind-option");
let repo: PathBuf = repository(&fixture, std::ffi::OsStr::new("repo"));
let socket: PathBuf = fixture.root.join("stray.sock");
let output: Output = finished(
wrapped(&fixture)
.arg("-C")
.arg(&repo)
.args(["monochromatic-git-policy-cli", "service", "run", "--journal"])
.arg(fixture.root.join("journal-not-made"))
.arg("--path")
.arg(&socket)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("the wrapper starts"),
);
assert!(!output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
assert!(String::from_utf8_lossy(&output.stderr).contains("Usage: git monochromatic-git-policy-cli"), "{}", String::from_utf8_lossy(&output.stderr));
std::thread::sleep(SETTLE);
assert!(!socket.exists(), "a socket appeared");
assert!(!fixture.root.join("journal-not-made").exists());
remove(&fixture);
}
fn run_with_git(fixture: &Fixture, git: &Path, label: &str) -> (Output, PathBuf) {
let socket: PathBuf = fixture.root.join(format!("{label}.sock"));
let output: Output = finished(
service_command()
.arg("--journal")
.arg(fixture.root.join(format!("journal-{label}")))
.arg("--git")
.arg(git)
.arg("--path")
.arg(&socket)
.env_clear()
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.expect("the service mode starts"),
);
return (output, socket);
}
#[test]
fn a_git_that_is_the_wrapper_is_refused() {
let fixture: Fixture = fixture("service-mode-git-is-wrapper");
let copy: PathBuf = fixture.root.join("copy-of-the-wrapper");
std::fs::copy(service_executable(), ©).expect("a byte copy of the executable");
for (label, git) in [
("built", PathBuf::from(service_executable())),
("linked", fixture.wrapper.clone()),
("git-link", fixture.root.join("bin/git")),
("copy", copy),
] {
let (output, socket) = run_with_git(&fixture, git.as_path(), label);
let stderr: String = String::from_utf8_lossy(&output.stderr).into_owned();
assert_eq!(output.status.code(), Some(git_policy_cli::service_main::EXIT_REFUSED), "{label}: {stderr}");
assert!(stderr.contains("would start monochromatic-git-policy-cli again") && stderr.contains("/usr/bin/git"), "{label}: {stderr}");
assert!(!socket.exists(), "{label}: a refused start listened");
}
remove(&fixture);
}
#[test]
fn the_same_start_with_the_real_git_listens() {
let fixture: Fixture = fixture("service-mode-real-git");
let socket: PathBuf = fixture.root.join("real.sock");
let mut command = service_command();
hermetic(&mut command, &fixture, std::ffi::OsStr::new("/usr/bin:/bin"));
let mut child: Child = spawn_owned(
command
.arg("--journal")
.arg(fixture.root.join("journal-real"))
.args(["--git", "/usr/bin/git", "--path"])
.arg(&socket)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null()),
)
.expect("the service mode starts");
let began: Instant = Instant::now();
while std::os::unix::net::UnixStream::connect(&socket).is_err() {
assert!(child.try_wait().expect("state").is_none(), "the service ended before it listened");
assert!(began.elapsed() < PATIENCE, "the service never listened");
std::thread::sleep(Duration::from_millis(10));
}
let _killed = child.kill();
let _waited = child.wait();
remove(&fixture);
}