use predicates::prelude::PredicateBooleanExt;
fn alba() -> assert_cmd::Command {
assert_cmd::Command::cargo_bin("alba").unwrap()
}
#[test]
fn check_reports_success() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("Beamfile"),
"beam build { run \"echo ok\" }",
)
.unwrap();
alba()
.current_dir(&dir)
.arg("check")
.assert()
.success()
.stdout(predicates::str::contains("1 beam"));
}
#[test]
fn check_renders_parse_error_with_exit_2() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("Beamfile"), "beam x { descriptoin \"t\" }").unwrap();
alba()
.current_dir(&dir)
.arg("check")
.assert()
.code(2)
.stderr(predicates::str::contains("did you mean `description`?"));
}
#[test]
fn check_rejects_invalid_embedded_shell_syntax() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("Beamfile"),
"beam bad { run \"echo 'unclosed\" }",
)
.unwrap();
alba()
.current_dir(&dir)
.arg("check")
.assert()
.code(2)
.stderr(predicates::str::contains("beam `bad`"))
.stderr(predicates::str::contains("unclosed single quote"));
}
#[test]
fn check_skips_parameterized_and_system_shell_beams() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("Beamfile"),
"beam deploy(target) { run \"echo {target} 'x\" }\n\
beam legacy { executor system_shell run \"if [ 1 ]; then echo y; fi\" }\n\
beam untouched(unused) { run \"echo 'unclosed\" }\n\
beam ship { executor docker { image \"x\" } run \"echo 'unclosed\" }\n",
)
.unwrap();
alba()
.current_dir(&dir)
.arg("check")
.assert()
.success()
.stdout(predicates::str::contains("4 beams"));
}
#[test]
fn check_accepts_valid_embedded_commands() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("Beamfile"),
"beam ok { run [\"echo one\", \"cat a.txt | cat\"] }",
)
.unwrap();
alba()
.current_dir(&dir)
.arg("check")
.assert()
.success()
.stdout(predicates::str::contains("\u{2713} Beamfile: 1 beam"));
}
#[test]
fn bare_alba_lists_beams_without_default() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("Beamfile"),
"beam test { description \"Run the test suite\" run \"echo ok\" }\n\
beam build { description \"Compile the workspace\" run \"echo ok\" }\n",
)
.unwrap();
let output = alba().current_dir(&dir).assert().success();
let stdout = String::from_utf8(output.get_output().stdout.clone()).unwrap();
assert!(stdout.contains("build"));
assert!(stdout.contains("Compile the workspace"));
assert!(stdout.contains("test"));
assert!(stdout.contains("Run the test suite"));
assert!(stdout.find("build").unwrap() < stdout.find("test").unwrap());
}
#[test]
fn missing_beamfile_is_exit_2() {
alba()
.current_dir(tempfile::tempdir().unwrap().path())
.assert()
.code(2)
.stderr(predicates::str::contains("no Beamfile found"));
}
#[test]
fn bare_alba_shows_placeholder_for_missing_description() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("Beamfile"),
"beam build { run \"echo ok\" }",
)
.unwrap();
alba()
.current_dir(&dir)
.assert()
.success()
.stdout(predicates::str::contains("(no description)"))
.stdout(predicates::str::contains("\u{1b}[").not());
}
#[test]
fn file_flag_points_at_a_beamfile_elsewhere() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("Other.beam"),
"beam build { run \"echo ok\" }",
)
.unwrap();
alba()
.current_dir(&dir)
.args(["--file", "Other.beam", "check"])
.assert()
.success()
.stdout(predicates::str::contains("1 beam"));
}
#[test]
fn cache_clean_removes_the_store_and_is_idempotent() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("Beamfile"),
"beam gen { inputs [\"data.txt\"] run \"echo generated\" }\n",
)
.unwrap();
std::fs::write(dir.path().join("data.txt"), "v1").unwrap();
alba()
.current_dir(&dir)
.args(["run", "gen"])
.assert()
.success();
assert!(dir.path().join(".alba/cache").is_dir());
let clean = || {
alba()
.current_dir(&dir)
.args(["cache", "clean"])
.assert()
.success()
};
clean();
assert!(!dir.path().join(".alba/cache").exists());
clean(); }
#[test]
fn cache_clean_works_with_a_broken_beamfile() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("Beamfile"), "beam { this is not valid").unwrap();
alba()
.current_dir(&dir)
.args(["cache", "clean"])
.assert()
.success();
}
#[test]
fn file_flag_missing_target_is_exit_2() {
let dir = tempfile::tempdir().unwrap();
alba()
.current_dir(&dir)
.args(["--file", "some/where/Other.beam", "check"])
.assert()
.code(2)
.stderr(predicates::str::contains("no Beamfile found"));
}
#[test]
fn bare_alba_survives_its_reader_closing_the_pipe_early() {
let dir = tempfile::tempdir().unwrap();
let beamfile: String = (0..20_000)
.map(|i| {
format!(
"beam b{i} {{ description \"a fairly long description, number {i}, so the total listing is large\" run \"echo ok\" }}\n"
)
})
.collect();
std::fs::write(dir.path().join("Beamfile"), beamfile).unwrap();
let mut child = std::process::Command::new(env!("CARGO_BIN_EXE_alba"))
.current_dir(&dir)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.unwrap();
let mut stdout = child.stdout.take().unwrap();
let mut buf = [0u8; 16];
std::io::Read::read_exact(&mut stdout, &mut buf).unwrap();
drop(stdout);
let output = child.wait_with_output().unwrap();
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("panicked"),
"listing must not panic when its reader closes the pipe early; stderr: {stderr}"
);
assert!(
output.status.success(),
"listing must still finish and exit 0 even though its reader went away; status: {:?}",
output.status
);
}