use slotgate::config::gate_args::GateArgs;
use slotgate::execution::gate_runner::GateRunner;
use std::env::temp_dir;
use std::fs;
use std::path::PathBuf;
use std::process::ExitCode;
fn args_running(command: &str, jobs: &[&str], log_dir: &str) -> GateArgs {
let mut args = args_without_pre_build();
args.program = String::from("cmd.exe");
args.program_args = vec![String::from("/C"), String::from(command)];
args.jobs = jobs.iter().map(|job| String::from(*job)).collect();
args.timeout_secs = 30;
args.log_dir = temp_log_dir(log_dir);
args
}
fn args_without_pre_build() -> GateArgs {
GateArgs {
max_parallel: 3,
port_range_base: 30_000,
port_range_size: 100,
port_env_base: String::from("PORT_RANGE_BASE"),
port_env_count: String::from("PORT_RANGE_COUNT"),
timeout_secs: 120,
log_dir: PathBuf::from("logs/slotgate"),
program: String::from("cargo"),
program_args: vec![String::from("test"), String::from("{job}")],
jobs: vec![String::from("alpha"), String::from("beta")],
pre_build_program: None,
pre_build_args: Vec::new(),
pre_build_target_name: None,
}
}
fn shows_as(code: ExitCode, expected: ExitCode) -> bool {
format!("{code:?}") == format!("{expected:?}")
}
fn temp_log_dir(test_name: &str) -> PathBuf {
let dir = temp_dir().join(format!("slotgate_gate_runner_tests_{test_name}"));
let _ = fs::remove_dir_all(&dir);
dir
}
#[tokio::test]
async fn resolve_pre_build_reports_an_error_when_the_pre_build_program_cannot_run() {
let mut args = args_without_pre_build();
args.pre_build_program = Some(String::from(
"slotgate-nonexistent-pre-build-program-for-tests",
));
let resolved = GateRunner::resolve_pre_build(&args).await;
assert!(
resolved.is_err(),
"a pre-build program that does not exist must surface as an error"
);
}
#[tokio::test]
async fn resolve_pre_build_without_a_pre_build_program_preserves_every_other_setting() {
let args = args_without_pre_build();
let resolved = GateRunner::resolve_pre_build(&args)
.await
.expect("no pre-build program means nothing can fail");
assert_eq!(resolved.max_parallel, args.max_parallel);
assert_eq!(resolved.port_range_base, args.port_range_base);
assert_eq!(resolved.port_range_size, args.port_range_size);
assert_eq!(resolved.port_env_base, args.port_env_base);
assert_eq!(resolved.port_env_count, args.port_env_count);
assert_eq!(resolved.timeout_secs, args.timeout_secs);
assert_eq!(resolved.log_dir, args.log_dir);
assert_eq!(resolved.jobs, args.jobs);
}
#[tokio::test]
async fn resolve_pre_build_without_a_pre_build_program_returns_the_arguments_unchanged() {
let args = args_without_pre_build();
let resolved = GateRunner::resolve_pre_build(&args)
.await
.expect("no pre-build program means nothing can fail");
assert_eq!(resolved.program, args.program);
assert_eq!(resolved.program_args, args.program_args);
}
#[tokio::test]
async fn run_a_gate_whose_jobs_all_pass_returns_success() {
let args = args_running("exit 0", &["alpha", "beta"], "all_pass");
let code = GateRunner::run(args).await;
assert!(
shows_as(code, ExitCode::SUCCESS),
"every job passed, so the gate must succeed"
);
}
#[tokio::test]
async fn run_a_gate_with_one_failing_job_returns_failure() {
let args = args_running("exit 1", &["alpha"], "one_fails");
let code = GateRunner::run(args).await;
assert!(
shows_as(code, ExitCode::FAILURE),
"a failing job must fail the gate"
);
}