use std::path::PathBuf;
use std::process::{Command, Output};
fn golden(name: &str) -> PathBuf {
let dir = match std::env::var_os("ANT_CONFORMANCE_GOLDEN") {
Some(d) => PathBuf::from(d),
None => {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../openantares/conformance/golden")
}
};
dir.join(name)
}
fn run(args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_openantares"))
.args(args)
.output()
.expect("spawn openantares")
}
#[test]
fn no_args_is_usage_64() {
let out = run(&[]);
assert_eq!(out.status.code(), Some(64), "stderr: {}", text(&out.stderr));
assert!(text(&out.stderr).contains("usage:"));
}
#[test]
fn unknown_command_is_usage_64() {
let out = run(&["frobnicate", "x.ant"]);
assert_eq!(out.status.code(), Some(64), "stderr: {}", text(&out.stderr));
}
#[test]
fn validate_ok_is_0_with_ok_line() {
let p = golden("basic.ant");
let out = run(&["validate", p.to_str().unwrap()]);
assert_eq!(
out.status.code(),
Some(0),
"stdout: {} stderr: {}",
text(&out.stdout),
text(&out.stderr)
);
let stdout = text(&out.stdout);
assert!(stdout.contains(": OK"), "stdout: {stdout}");
assert!(stdout.contains("version=0.3"), "stdout: {stdout}");
}
#[test]
fn validate_major_version_file_is_65() {
let p = golden("major_version.ant");
let out = run(&["validate", p.to_str().unwrap()]);
assert_eq!(
out.status.code(),
Some(65),
"stdout: {} stderr: {}",
text(&out.stdout),
text(&out.stderr)
);
assert!(text(&out.stderr).contains("FAIL"));
}
#[test]
fn validate_truncated_stream_is_65() {
let bytes = std::fs::read(golden("basic.ant")).unwrap();
let dir = tempdir();
let p = dir.join("truncated.ant");
std::fs::write(&p, &bytes[..bytes.len() / 2]).unwrap();
let out = run(&["validate", p.to_str().unwrap()]);
assert_eq!(out.status.code(), Some(65), "stderr: {}", text(&out.stderr));
}
#[test]
fn validate_missing_file_is_66() {
let out = run(&["validate", "/nonexistent/definitely-not-here.ant"]);
assert_eq!(
out.status.code(),
Some(66),
"stdout: {} stderr: {}",
text(&out.stdout),
text(&out.stderr)
);
assert!(text(&out.stderr).contains("FAIL"));
}
#[test]
fn validate_keeps_going_and_reports_most_severe() {
let ok = golden("basic.ant");
let bad = golden("major_version.ant");
let out = run(&[
"validate",
ok.to_str().unwrap(),
bad.to_str().unwrap(),
"/nonexistent/definitely-not-here.ant",
]);
assert_eq!(out.status.code(), Some(66), "stderr: {}", text(&out.stderr));
assert!(text(&out.stdout).contains(": OK"), "ok file still reported");
assert_eq!(
text(&out.stderr).matches("FAIL").count(),
2,
"both failing files reported: {}",
text(&out.stderr)
);
}
#[test]
fn info_reports_manifest_and_counts_matching_expected_json() {
let p = golden("basic.ant");
let out = run(&["info", p.to_str().unwrap()]);
assert_eq!(out.status.code(), Some(0), "stderr: {}", text(&out.stderr));
let stdout = text(&out.stdout);
for needle in [
"format: antares",
"tenant id: 1",
"project id: 1",
"records: 7",
"vertices: 2",
"edges: 1",
"observations: 1",
"evidence: 1",
"beliefs: 1",
"vectors: 1",
"vertex tombstones: 0",
] {
assert!(
needle_in(&stdout, needle),
"missing `{needle}` in:\n{stdout}"
);
}
assert!(stdout.contains("0.3"), "format version shown: {stdout}");
}
#[test]
fn info_takes_exactly_one_file() {
let p = golden("basic.ant");
let out = run(&["info", p.to_str().unwrap(), p.to_str().unwrap()]);
assert_eq!(out.status.code(), Some(64), "stderr: {}", text(&out.stderr));
}
fn text(b: &[u8]) -> String {
String::from_utf8_lossy(b).into_owned()
}
fn needle_in(hay: &str, needle: &str) -> bool {
hay.contains(needle)
}
fn tempdir() -> PathBuf {
let d = std::env::temp_dir().join(format!("openantares-cli-test-{}", std::process::id()));
std::fs::create_dir_all(&d).unwrap();
d
}