use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
#[cfg(feature = "cranelift")]
const ENGINES: &[&str] = &["--vm", "--jit"];
#[cfg(not(feature = "cranelift"))]
const ENGINES: &[&str] = &["--vm"];
const JSON: &str = r#"{"vulnerabilities":[{"cve":{"id":"CVE-1"}}]}"#;
fn run_jpth(engine: &str, path: &str) -> (bool, String, String) {
let src = format!(r#"main j:t>R t t;jpth j "{}""#, path.replace('"', "\\\""));
let out = ilo()
.arg(engine)
.arg(&src)
.arg(JSON)
.output()
.expect("failed to spawn ilo");
let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string();
let stderr = String::from_utf8_lossy(&out.stderr).trim().to_string();
(out.status.success(), stdout, stderr)
}
#[test]
fn jpth_jsonpath_root_selector_diagnoses_dot_path() {
for engine in ENGINES {
let (ok, stdout, stderr) = run_jpth(engine, "$.vulnerabilities[0].cve.id");
assert!(
!ok,
"jsonpath-shape path should fail on {engine}, got stdout={stdout}"
);
assert!(
stderr.contains("jpth is dot-path only"),
"{engine}: stderr lacks dot-path diagnostic, got: {stderr}"
);
}
}
#[test]
fn jpth_jsonpath_wildcard_diagnoses_at_iteration() {
for engine in ENGINES {
let (ok, _stdout, stderr) = run_jpth(engine, "vulnerabilities.*.cve.id");
assert!(!ok, "wildcard path should fail on {engine}");
assert!(
stderr.contains("jpth is dot-path only") && stderr.contains("`*`"),
"{engine}: stderr missing wildcard hint, got: {stderr}"
);
}
}
#[test]
fn jpth_jsonpath_bracket_indexing_diagnoses_dot_index() {
for engine in ENGINES {
let (ok, _stdout, stderr) = run_jpth(engine, "vulnerabilities[0].cve.id");
assert!(!ok, "bracket path should fail on {engine}");
assert!(
stderr.contains("jpth is dot-path only") && stderr.contains("dot before array"),
"{engine}: stderr missing bracket-form hint, got: {stderr}"
);
}
}
#[test]
fn jpth_dot_path_still_works_unchanged() {
for engine in ENGINES {
let (ok, stdout, stderr) = run_jpth(engine, "vulnerabilities.0.cve.id");
assert!(
ok,
"valid dot-path should succeed on {engine}, stderr={stderr}"
);
assert_eq!(stdout, "CVE-1", "{engine}: unexpected stdout {stdout}");
}
}
#[test]
fn jpth_literal_dollar_key_still_resolves() {
let json = r#"{"$":"raw-dollar-key"}"#;
for engine in ENGINES {
let src = r#"main j:t>R t t;jpth j "$""#;
let out = ilo()
.arg(engine)
.arg(src)
.arg(json)
.output()
.expect("failed to spawn ilo");
assert!(
out.status.success(),
"{engine}: bare `$` key should resolve as dot-path, stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string();
assert_eq!(stdout, "raw-dollar-key", "{engine}: stdout={stdout}");
}
}
#[test]
fn jpth_unit_helper_matches_jsonpath_shapes() {
use ilo::builtins::jpth_jsonpath_diagnostic;
assert!(jpth_jsonpath_diagnostic("$.a.b").is_some());
assert!(jpth_jsonpath_diagnostic("$[0].a").is_some());
assert!(jpth_jsonpath_diagnostic("$*").is_some());
assert!(jpth_jsonpath_diagnostic("a.*.b").is_some());
assert!(jpth_jsonpath_diagnostic("a[0].b").is_some());
assert!(jpth_jsonpath_diagnostic("$").is_none());
assert!(jpth_jsonpath_diagnostic("a.b.0.c").is_none());
assert!(jpth_jsonpath_diagnostic("name").is_none());
}