use std::collections::HashSet;
use cordance_core::lock::SourceLock;
fn fixture(name: &str) -> SourceLock {
let manifest = env!("CARGO_MANIFEST_DIR");
let path = format!("{manifest}/../../fixtures/check/{name}");
let json = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("failed to read fixture {name}: {e}"));
serde_json::from_str(&json).unwrap_or_else(|e| panic!("failed to parse fixture {name}: {e}"))
}
#[test]
fn clean_diff_is_clean() {
let lock = fixture("clean.lock.json");
let fenced: HashSet<String> = HashSet::new();
let report = lock.diff(&lock, &fenced);
assert!(report.is_clean(), "self-diff must be clean");
assert_eq!(report.exit_code(), 0);
}
#[test]
fn source_drift_detected() {
let prev = fixture("clean.lock.json");
let curr = fixture("source-drifted.lock.json");
let fenced: HashSet<String> = HashSet::new();
let report = curr.diff(&prev, &fenced);
assert_eq!(
report.source_drifts.len(),
1,
"expected one source drift entry"
);
assert_eq!(report.source_drifts[0].id, "project_readme:README.md");
assert_eq!(report.exit_code(), 1);
}
#[test]
fn compute_from_empty_pack_stable_id() {
use cordance_core::advise::AdviseReport;
use cordance_core::pack::{CordancePack, PackTargets, ProjectIdentity};
use cordance_core::schema;
let pack = CordancePack {
schema: schema::CORDANCE_PACK_V1.into(),
project: ProjectIdentity {
name: "t".into(),
repo_root: ".".into(),
kind: "test".into(),
host_os: "linux".into(),
axiom_pin: None,
},
sources: vec![],
doctrine_pins: vec![],
targets: PackTargets::default(),
outputs: vec![],
source_lock: SourceLock::empty(),
advise: AdviseReport::empty(),
residual_risk: vec!["x".into()],
};
let lock1 = SourceLock::compute_from_pack(&pack);
let lock2 = SourceLock::compute_from_pack(&pack);
assert_eq!(
lock1.pack_id, lock2.pack_id,
"pack_id must be deterministic"
);
}
#[test]
fn check_after_pack_reports_clean() {
use assert_cmd::prelude::*;
use std::process::Command;
let dir = tempfile::tempdir().expect("tempdir");
let project = dir.path();
std::fs::write(project.join("README.md"), "# initial cordance check fixture\n")
.expect("write README");
let mut pack_cmd = Command::cargo_bin("cordance").expect("bin");
pack_cmd
.arg("--target")
.arg(project)
.arg("--allow-outside-cwd")
.arg("pack")
.arg("--output-mode")
.arg("write");
pack_cmd.assert().success();
assert!(
project.join(".cordance").join("sources.lock").exists(),
"pack must produce .cordance/sources.lock"
);
let mut check_cmd = Command::cargo_bin("cordance").expect("bin");
check_cmd
.arg("--target")
.arg(project)
.arg("--allow-outside-cwd")
.arg("check");
check_cmd.assert().code(0);
}
#[test]
fn check_detects_modified_source_file() {
use assert_cmd::prelude::*;
use std::process::Command;
let dir = tempfile::tempdir().expect("tempdir");
let project = dir.path();
std::fs::write(project.join("README.md"), "# Initial\n").expect("write README");
let mut pack_cmd = Command::cargo_bin("cordance").expect("bin");
pack_cmd
.arg("--target")
.arg(project)
.arg("--allow-outside-cwd")
.arg("pack")
.arg("--output-mode")
.arg("write");
pack_cmd.assert().success();
std::fs::write(project.join("README.md"), "# Modified\n").expect("mutate README");
let mut check_cmd = Command::cargo_bin("cordance").expect("bin");
check_cmd
.arg("--target")
.arg(project)
.arg("--allow-outside-cwd")
.arg("check");
check_cmd.assert().failure();
}
#[test]
fn pack_propagates_axiom_pin_into_lock() {
use assert_cmd::prelude::*;
use std::process::Command;
let dir = tempfile::tempdir().expect("tempdir");
let project = dir.path();
std::fs::write(
project.join("cordance.toml"),
"[axiom]\nsource = \"./pai-axiom\"\nalgorithm_latest = \"v9.9.9-axiom-test\"\n",
)
.expect("write cordance.toml");
std::fs::write(project.join("README.md"), "# axiom pin test\n").expect("write README");
let mut pack_cmd = Command::cargo_bin("cordance").expect("bin");
pack_cmd
.arg("--target")
.arg(project)
.arg("--allow-outside-cwd")
.arg("pack")
.arg("--output-mode")
.arg("write");
pack_cmd.assert().success();
let lock_path = project.join(".cordance").join("sources.lock");
let lock_json = std::fs::read_to_string(&lock_path).expect("read lock");
let lock: SourceLock = serde_json::from_str(&lock_json).expect("parse lock");
assert_eq!(
lock.axiom_algorithm_pin.as_deref(),
Some("v9.9.9-axiom-test"),
"axiom pin from cordance.toml must round-trip into the lock"
);
}