use super::*;
#[test]
fn embedded_agents_contract_matches_the_repo_doc() {
let embedded = include_str!("../AGENTS.md");
match std::fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/../../AGENTS.md")) {
Ok(root) if root.contains("instructions for an AI coding agent") => {
assert_eq!(embedded, root, "crate AGENTS.md drifted from the repo root — re-copy it");
}
_ => { }
}
}
#[test]
fn rewire_flags_dropped_edges_not_added_ones() {
let cg = |pairs: &[(&str, &[&str])]| -> BTreeMap<String, Vec<String>> {
pairs.iter().map(|(k, v)| (k.to_string(), v.iter().map(|s| s.to_string()).collect())).collect()
};
let base = cg(&[("api::handle", &["service::place_order"]), ("cart::total", &["pricing::quote"])]);
let cur = cg(&[("cart::total", &["pricing::quote"]), ("main", &["api::handle"])]);
let d = dropped_edges(&cur, &base);
assert_eq!(d.get("api::handle"), Some(&vec!["service::place_order"])); assert!(!d.contains_key("cart::total")); assert!(!d.contains_key("main")); assert!(dropped_edges(&base, &base).is_empty());
}
#[test]
fn gains_flags_a_supply_chain_capability_gain() {
let set = |xs: &[&str]| xs.iter().map(|s| s.to_string()).collect::<BTreeSet<String>>();
assert_eq!(gained_effects(&set(&["Fs", "Net"]), &set(&["Fs"])), vec!["Net"]); assert!(gained_effects(&set(&["Fs"]), &set(&["Fs"])).is_empty()); assert!(gained_effects(&set(&["Fs"]), &set(&["Fs", "Net"])).is_empty()); }
#[test]
fn match_tier_ladder() {
assert_eq!(match_tier("pricing::Pricing::quote", "pricing::Pricing::quote"), 3);
assert_eq!(match_tier("pricing::Pricing::quote", "Pricing::quote"), 2);
assert_eq!(match_tier("pricing::Pricing::quote", "quote"), 2);
assert_eq!(match_tier("pricing::Pricing::quote_bulk", "quote"), 1); assert_eq!(match_tier("pricing::Pricing::quote", "ricing::quote"), 1); assert_eq!(match_tier("pricing::Pricing::quote", "zzz"), 0);
let names = ["pricing::Pricing::quote", "pricing::Pricing::quote_bulk"];
let t = best_tier(names.iter().copied(), "Pricing::quote");
assert_eq!(t, 2);
assert!(q_match(names[0], "Pricing::quote", t));
assert!(!q_match(names[1], "Pricing::quote", t));
let t2 = best_tier(names.iter().copied(), "ricing");
assert_eq!(t2, 1);
assert!(q_match(names[0], "ricing", t2) && q_match(names[1], "ricing", t2));
}
#[test]
fn whatif_scope_and_policy_parse() {
use candor_classify::policy::{parse_policy, scope_matches};
assert!(scope_matches("app::domain::handle", "domain"));
assert!(scope_matches("crate::domain_logic", "domain")); assert!(!scope_matches("app::subdomain::handle", "domain")); assert!(scope_matches("api::handle", "api"));
let p = parse_policy("deny Net Db api\npure parse\ndeny Exec\nallow Net in billing x\nforbid a -> b\n# c");
let rules = &p.rules;
assert_eq!(rules.len(), 3);
assert_eq!(rules[0].effects.iter().copied().collect::<Vec<_>>(), vec!["Db", "Net"]);
assert_eq!(rules[0].scope.as_deref(), Some("api"));
assert!(rules[1].effects.is_empty()); assert_eq!(rules[1].scope.as_deref(), Some("parse"));
assert_eq!(rules[2].scope, None); assert_eq!(p.allow_rules.len(), 1);
assert_eq!(p.layer_rules.len(), 1);
}
#[test]
fn containment_layer_derivation() {
let names: Vec<String> = ["pgman::conn::connect", "pgman::query::run", "pgman::main"]
.iter()
.map(|s| s.to_string())
.collect();
let refs: Vec<&String> = names.iter().collect();
let pl = common_prefix_len(&refs);
assert_eq!(pl, 1, "shared root is `pgman`");
assert_eq!(layer_of("pgman::conn::connect", pl), "conn");
assert_eq!(layer_of("pgman::query::Q::run", pl), "query");
assert_eq!(layer_of("pgman::main", pl), "(root)");
assert_eq!(norm_name("<pgman::conn::Conn as std::fmt::Debug>::fmt"), "pgman::conn::Conn");
assert_eq!(layer_of("<pgman::conn::Conn as std::fmt::Debug>::fmt", pl), "conn");
let multi: Vec<String> =
["a::x::f".to_string(), "b::y::g".to_string()].to_vec();
let mrefs: Vec<&String> = multi.iter().collect();
assert_eq!(common_prefix_len(&mrefs), 0);
assert_eq!(layer_of("a::x::f", 0), "a");
}
#[test]
fn is_scan_artifact_discriminates() {
assert!(is_scan_artifact("report", "report.mycrate.scan.json"));
assert!(is_scan_artifact("report", "report.mycrate.scan.callgraph.json"));
assert!(!is_scan_artifact("report", "report.mycrate.Rlib.json"));
assert!(!is_scan_artifact("report", "report.mycrate.Executable.callgraph.json"));
assert!(!is_scan_artifact("report", "report.calibrated.json"));
assert!(!is_scan_artifact("report", "other.mycrate.scan.json"));
}
#[test]
fn report_backend_and_clear_other() {
let dir = std::env::temp_dir().join("candor-query-backend-test");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let pre = dir.join("report");
let prefix = pre.to_string_lossy().to_string();
let w = |name: &str| std::fs::write(dir.join(name), b"{}").unwrap();
assert_eq!(report_backend(&prefix), "none");
w("report.c.scan.json");
w("report.c.scan.callgraph.json");
assert_eq!(report_backend(&prefix), "scan");
w("report.c.Rlib.json");
w("report.c.Rlib.callgraph.json");
w("report.calibrated.json");
assert_eq!(report_backend(&prefix), "scan"); let removed = clear_other_reports(&prefix, "lint"); assert_eq!(removed, 2); assert!(!dir.join("report.c.scan.json").exists());
assert!(dir.join("report.c.Rlib.json").exists());
assert_eq!(report_backend(&prefix), "lint");
w("report.c.scan.json");
let removed = clear_other_reports(&prefix, "scan");
assert!(removed >= 3); assert!(dir.join("report.c.scan.json").exists());
assert_eq!(report_backend(&prefix), "scan");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn locate_picks_newest_by_mtime() {
use std::time::{Duration, SystemTime};
let dir = std::env::temp_dir().join("candor-query-locate-test");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let old = dir.join("libcandor@nightly-2025-01-01-x.dylib");
let new = dir.join("libcandor@nightly-2026-01-01-x.dylib");
std::fs::write(&old, b"x").unwrap();
std::fs::write(&new, b"x").unwrap();
let f_old = std::fs::OpenOptions::new().write(true).open(&old).unwrap();
f_old.set_modified(SystemTime::now() - Duration::from_secs(100)).unwrap();
let f_new = std::fs::OpenOptions::new().write(true).open(&new).unwrap();
f_new.set_modified(SystemTime::now()).unwrap();
let out = locate_newest("lib", &[dir.to_string_lossy().to_string()]);
assert_eq!(out, Some(new)); let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn merge_hook_is_nondestructive_and_idempotent() {
let dir = std::env::temp_dir().join("candor-query-merge-test");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let cmd = "X/stop-hook.sh".to_string();
let arg = |p: &std::path::Path| vec![p.to_string_lossy().to_string(), cmd.clone()];
let fresh = dir.join("fresh.json");
assert_eq!(cmd_merge_hook(&arg(&fresh)), 0);
let v: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(&fresh).unwrap()).unwrap();
assert_eq!(v["hooks"]["Stop"][0]["hooks"][0]["command"], cmd.as_str());
let keep = dir.join("keep.json");
std::fs::write(&keep, r#"{"model":"opus","permissions":{"allow":["Bash"]}}"#).unwrap();
assert_eq!(cmd_merge_hook(&arg(&keep)), 0);
let v: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(&keep).unwrap()).unwrap();
assert_eq!(v["model"], "opus");
assert_eq!(v["permissions"]["allow"][0], "Bash");
assert_eq!(v["hooks"]["Stop"][0]["hooks"][0]["command"], cmd.as_str());
assert_eq!(cmd_merge_hook(&arg(&keep)), 0);
let v: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(&keep).unwrap()).unwrap();
assert_eq!(v["hooks"]["Stop"].as_array().unwrap().len(), 1);
let bad = dir.join("bad.json");
let original = "{ // comment\n \"model\": \"x\",\n}";
std::fs::write(&bad, original).unwrap();
assert_eq!(cmd_merge_hook(&arg(&bad)), 0);
assert_eq!(std::fs::read_to_string(&bad).unwrap(), original, "must not touch a non-JSON file");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn state_hash_is_deterministic_and_scoped() {
let dir = std::env::temp_dir().join("candor-query-state-test");
let _ = std::fs::remove_dir_all(&dir);
for d in ["src", "sub", "target/x", ".git"] {
std::fs::create_dir_all(dir.join(d)).unwrap();
}
std::fs::write(dir.join("src/a.rs"), "fn a(){}").unwrap();
std::fs::write(dir.join("sub/b.rs"), "fn b(){}").unwrap();
std::fs::write(dir.join("target/x/c.rs"), "fn c(){}").unwrap(); std::fs::write(dir.join(".git/d.rs"), "fn d(){}").unwrap(); std::fs::write(dir.join("src/notrust.txt"), "ignored").unwrap();
let hash = |root: &Path| -> u64 {
let mut files = Vec::new();
collect_rs(root, &mut files);
files.sort();
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for f in &files {
for &b in f.strip_prefix(root).unwrap_or(f).to_string_lossy().as_bytes() {
h ^= b as u64;
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
for &b in std::fs::read(f).unwrap_or_default().iter() {
h ^= b as u64;
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
}
h
};
let mut files = Vec::new();
collect_rs(&dir, &mut files);
assert_eq!(files.len(), 2, "must collect exactly src/a.rs + sub/b.rs");
let h1 = hash(&dir);
assert_eq!(h1, hash(&dir), "deterministic");
std::fs::write(dir.join("target/x/c.rs"), "fn c(){ let _=9; }").unwrap();
std::fs::write(dir.join(".git/d.rs"), "fn d(){ let _=9; }").unwrap();
assert_eq!(h1, hash(&dir), "target/ and .git/ edits are ignored");
std::fs::write(dir.join("src/a.rs"), "fn a(){ let _=1; }").unwrap();
assert_ne!(h1, hash(&dir), "a real .rs edit changes the hash");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn globs_discriminate_reports_from_sidecars() {
let dir = std::env::temp_dir().join("candor-query-glob-test");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
for f in [
"report.mycrate.lib.json", "report.mycrate.Executable.json", "report.calibrated.json", "report.encountered-mycrate.json", "report.single.json", "other.a.b.json", ] {
std::fs::write(dir.join(f), "[]").unwrap();
}
let prefix = dir.join("report");
let prefix = prefix.to_str().unwrap();
let reports: Vec<String> =
glob_reports(prefix).iter().map(|p| p.file_name().unwrap().to_str().unwrap().to_string()).collect();
assert_eq!(reports, vec!["report.mycrate.Executable.json", "report.mycrate.lib.json"]);
let enc: Vec<String> =
glob_encountered(prefix).iter().map(|p| p.file_name().unwrap().to_str().unwrap().to_string()).collect();
assert_eq!(enc, vec!["report.encountered-mycrate.json"]);
assert_eq!(prefix_base(prefix), "report");
let _ = std::fs::remove_dir_all(&dir);
}