use std::fs;
use std::path::PathBuf;
use std::process::Command;
fn ontoenv_bin() -> PathBuf {
let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("target")
.join("debug")
.join(if cfg!(windows) {
"ontoenv.exe"
} else {
"ontoenv"
});
if !p.exists() {
p = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("target")
.join("release")
.join(if cfg!(windows) {
"ontoenv.exe"
} else {
"ontoenv"
});
}
assert!(p.exists(), "ontoenv binary not found at {:?}", p);
p
}
fn tmp_dir(name: &str) -> PathBuf {
let mut base = std::env::temp_dir();
base.push(format!("ontoenv-cli-{}-{}", name, std::process::id()));
if base.exists() {
let _ = fs::remove_dir_all(&base);
}
fs::create_dir_all(&base).unwrap();
base
}
fn write_ttl(path: &PathBuf, ontology_uri: &str, extra: &str) {
let content = format!(
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\
@prefix owl: <http://www.w3.org/2002/07/owl#> .\n\
<{uri}> a owl:Ontology .\n\
{extra}\n",
uri = ontology_uri,
extra = extra
);
fs::write(path, content).expect("write ttl");
}
#[test]
fn non_init_command_errors_outside_env() {
let exe = ontoenv_bin();
let root = tmp_dir("noenv");
let out = Command::new(&exe)
.current_dir(&root)
.env("ONTOENV_DIR", &root)
.arg("list")
.arg("ontologies")
.output()
.expect("run list");
assert!(!out.status.success(), "expected failure outside env");
}
#[test]
fn discovery_from_subdirectory() {
let exe = ontoenv_bin();
let root = tmp_dir("discover");
let out = Command::new(&exe)
.current_dir(&root)
.arg("init")
.arg(".")
.output()
.expect("run init");
assert!(
out.status.success(),
"init failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let nested = root.join("nested");
fs::create_dir_all(&nested).unwrap();
let out = Command::new(&exe)
.current_dir(&nested)
.arg("list")
.arg("ontologies")
.output()
.expect("run list");
assert!(
out.status.success(),
"list failed in subdir: {}",
String::from_utf8_lossy(&out.stderr)
);
}
#[test]
fn ontoenv_dir_override() {
let exe = ontoenv_bin();
let env_root = tmp_dir("envdir");
let out = Command::new(&exe)
.current_dir(&env_root)
.arg("init")
.arg(".")
.output()
.expect("run init");
assert!(
out.status.success(),
"init failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let elsewhere = tmp_dir("elsewhere");
let out = Command::new(&exe)
.current_dir(&elsewhere)
.env("ONTOENV_DIR", env_root.join(".ontoenv"))
.arg("list")
.arg("ontologies")
.output()
.expect("run list");
assert!(
out.status.success(),
"list failed with ONTOENV_DIR: {}",
String::from_utf8_lossy(&out.stderr)
);
}
#[test]
fn update_from_nested_subdir_uses_root_locations() {
let exe = ontoenv_bin();
let root = tmp_dir("update_nested");
let ont_dir = root.join("ontologies");
fs::create_dir_all(&ont_dir).unwrap();
let ont_path = ont_dir.join("A.ttl");
write_ttl(&ont_path, "http://example.org/ont/A", "");
std::thread::sleep(std::time::Duration::from_millis(1100));
let out = Command::new(&exe)
.current_dir(&root)
.arg("init")
.arg("ontologies")
.output()
.expect("run init");
assert!(
out.status.success(),
"init failed: {}",
String::from_utf8_lossy(&out.stderr)
);
write_ttl(
&ont_path,
"http://example.org/ont/A",
"<http://example.org/ont/A> <http://example.org/p> <http://example.org/o> .",
);
std::thread::sleep(std::time::Duration::from_millis(2000));
let nested = root.join("nested").join("deeper");
fs::create_dir_all(&nested).unwrap();
let out = Command::new(&exe)
.current_dir(&nested)
.arg("update")
.output()
.expect("run update");
assert!(
out.status.success(),
"update failed from nested dir: {}",
String::from_utf8_lossy(&out.stderr)
);
let update_stdout = String::from_utf8_lossy(&out.stdout);
assert!(
update_stdout.contains("http://example.org/ont/A"),
"update output missing ontology: {}",
update_stdout
);
let out = Command::new(&exe)
.current_dir(&nested)
.arg("list")
.arg("locations")
.output()
.expect("run list locations");
assert!(
out.status.success(),
"list locations failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let locations_out = String::from_utf8_lossy(&out.stdout);
assert!(
locations_out.contains("ontologies") && locations_out.contains("A.ttl"),
"expected ontology location in output, got: {}",
locations_out
);
}
#[test]
fn why_lists_importers_paths() {
let exe = ontoenv_bin();
let root = tmp_dir("why");
let a_uri = "http://example.org/ont/A";
let b_uri = "http://example.org/ont/B";
let c_uri = "http://example.org/ont/C";
let a_path = root.join("A.ttl");
let b_path = root.join("B.ttl");
let c_path = root.join("C.ttl");
write_ttl(&b_path, b_uri, "");
write_ttl(
&a_path,
a_uri,
&format!("<{}> owl:imports <{}> .", a_uri, b_uri),
);
write_ttl(
&c_path,
c_uri,
&format!("<{}> owl:imports <{}> .", c_uri, a_uri),
);
let out = Command::new(&exe)
.current_dir(&root)
.arg("init")
.arg(".")
.output()
.expect("run init");
assert!(out.status.success());
let out = Command::new(&exe)
.current_dir(&root)
.arg("why")
.arg(b_uri)
.output()
.expect("run why");
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.contains(&format!("{} -> {}", a_uri, b_uri)));
assert!(stdout.contains(&format!("{} -> {} -> {}", c_uri, a_uri, b_uri)));
}
#[test]
fn get_stdout_turtle() {
let exe = ontoenv_bin();
let root = tmp_dir("get_turtle");
let iri = "http://example.org/ont/Only";
let path = root.join("only.ttl");
write_ttl(&path, iri, "");
let out = Command::new(&exe)
.current_dir(&root)
.arg("init")
.arg(".")
.output()
.expect("run init");
assert!(
out.status.success(),
"init failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let out = Command::new(&exe)
.current_dir(&root)
.arg("get")
.arg(iri)
.output()
.expect("run get");
assert!(
out.status.success(),
"get failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains(iri),
"stdout did not contain IRI: {}",
stdout
);
}
#[test]
fn get_jsonld_output() {
let exe = ontoenv_bin();
let root = tmp_dir("get_jsonld");
let iri = "http://example.org/ont/JL";
let path = root.join("jl.ttl");
write_ttl(&path, iri, "");
let out = Command::new(&exe)
.current_dir(&root)
.arg("init")
.arg(".")
.output()
.expect("run init");
assert!(out.status.success());
let out = Command::new(&exe)
.current_dir(&root)
.arg("get")
.arg(iri)
.arg("--format")
.arg("jsonld")
.output()
.expect("run get jsonld");
assert!(
out.status.success(),
"get jsonld failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains(iri),
"jsonld output missing iri; got: {}",
stdout
);
assert!(
stdout.trim_start().starts_with("{") || stdout.trim_start().starts_with("["),
"not JSON-LD? {}",
stdout
);
}
#[test]
fn get_with_location_disambiguates() {
let exe = ontoenv_bin();
let root = tmp_dir("get_loc");
let iri = "http://example.org/ont/Dup";
let p1 = root.join("dup_v1.ttl");
let p2 = root.join("dup_v2.ttl");
write_ttl(
&p1,
iri,
"<http://example.org/x> <http://example.org/p> \"v1\" .",
);
write_ttl(
&p2,
iri,
"<http://example.org/x> <http://example.org/p> \"v2\" .",
);
let out = Command::new(&exe)
.current_dir(&root)
.arg("init")
.arg(".")
.output()
.expect("run init");
assert!(out.status.success());
let out = Command::new(&exe)
.current_dir(&root)
.arg("get")
.arg(iri)
.arg("--location")
.arg(p1.to_str().unwrap())
.output()
.expect("run get v1");
assert!(
out.status.success(),
"get v1 failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let s1 = String::from_utf8_lossy(&out.stdout);
assert!(s1.contains("\"v1\""), "expected v1 triple, got: {}", s1);
let out = Command::new(&exe)
.current_dir(&root)
.arg("get")
.arg(iri)
.arg("-l")
.arg(p2.to_str().unwrap())
.output()
.expect("run get v2");
assert!(
out.status.success(),
"get v2 failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let s2 = String::from_utf8_lossy(&out.stdout);
assert!(s2.contains("\"v2\""), "expected v2 triple, got: {}", s2);
}