use assert_cmd::prelude::*;
use std::process::Command;
#[test]
fn cordance_init_creates_config() {
let dir = tempfile::tempdir().expect("tempdir");
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("init");
cmd.assert().success();
assert!(dir.path().join("cordance.toml").exists());
}
#[test]
fn cordance_init_skips_if_exists() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("cordance.toml"), "[project]\n").expect("write");
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("init");
cmd.assert().success();
let content = std::fs::read_to_string(dir.path().join("cordance.toml")).expect("read");
assert_eq!(content, "[project]\n");
}
#[test]
fn cordance_scan_json_succeeds() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target").arg(".").arg("scan").arg("--json");
cmd.assert().success();
}
#[test]
fn cordance_scan_plain_succeeds() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target").arg(".").arg("scan");
cmd.assert().success();
}
#[test]
fn cordance_pack_dry_run_succeeds() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(".")
.arg("pack")
.arg("--output-mode")
.arg("dry-run");
cmd.assert().success();
}
#[test]
fn cordance_pack_diff_mode_succeeds() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(".")
.arg("pack")
.arg("--output-mode")
.arg("diff");
cmd.assert().success();
}
#[test]
fn cordance_advise_succeeds() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target").arg(".").arg("advise");
cmd.assert().success();
}
#[test]
fn cordance_advise_json_succeeds() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target").arg(".").arg("advise").arg("--json");
cmd.assert().success();
}
#[test]
fn cordance_check_on_empty_target_exits_gracefully() {
let dir = tempfile::tempdir().expect("tempdir");
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("check");
let status = cmd.status().expect("run");
assert!(status.code().is_some());
}
#[test]
fn cordance_status_reports_operator_summary_without_writes() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::create_dir_all(dir.path().join("doctrine-root")).expect("mkdir doctrine root");
std::fs::write(
dir.path().join("cordance.toml"),
r#"
[doctrine]
source = "doctrine-root"
fallback_repo = "https://nonexistent.example.invalid/doctrine"
pin_commit = "auto"
[axiom]
source = "axiom-root"
algorithm_latest = "v-test-status"
"#,
)
.expect("write cordance.toml");
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("status");
let output = cmd.output().expect("run cordance status");
assert!(
output.status.success(),
"status failed: stdout={}, stderr={}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8_lossy(&output.stdout);
for needle in [
"cordance status",
"config: cordance.toml found",
"source lock: missing",
"advise:",
"doctrine pin: none",
"axiom pin: v-test-status",
"readiness: attention",
] {
assert!(
stdout.contains(needle),
"expected stdout to contain {needle:?}; got: {stdout}"
);
}
assert!(
!dir.path().join(".cordance").exists(),
"status must be read-only and must not create .cordance"
);
}
#[test]
fn cordance_explain_unknown_rule_exits_zero() {
let dir = tempfile::tempdir().expect("tempdir");
let mut pack_cmd = Command::cargo_bin("cordance").expect("bin");
pack_cmd
.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("pack")
.arg("--output-mode")
.arg("write");
let _ = pack_cmd.output();
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("explain")
.arg("nonexistent-rule-xyz-abc");
cmd.assert().success();
}
#[test]
fn cordance_explain_finds_rule_after_pack() {
let dir = tempfile::tempdir().expect("tempdir");
let mut pack_cmd = Command::cargo_bin("cordance").expect("bin");
pack_cmd
.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("pack")
.arg("--output-mode")
.arg("write");
let _ = pack_cmd.output();
let mut explain_cmd = Command::cargo_bin("cordance").expect("bin");
explain_cmd
.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("explain")
.arg("R-build-1");
let _ = explain_cmd.output();
}
#[test]
fn cordance_cortex_push_dry_run_succeeds() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(".")
.arg("cortex")
.arg("push")
.arg("--dry-run");
cmd.assert().success();
}
#[test]
fn cortex_push_dry_run_writes_file_but_omits_handoff() {
let dir = tempfile::tempdir().expect("tempdir");
let mut pack_cmd = Command::cargo_bin("cordance").expect("bin");
pack_cmd
.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("pack")
.arg("--output-mode")
.arg("write");
let _ = pack_cmd.output();
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("cortex")
.arg("push")
.arg("--dry-run");
let output = cmd.output().expect("run cordance");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Cortex receipt:"),
"expected receipt path in stdout; got: {stdout}"
);
assert!(
!stdout.contains("To submit:"),
"dry-run leaked the operator handoff text into stdout: {stdout}"
);
assert!(
dir.path().join(".cordance/cortex-receipt.json").exists(),
"dry-run failed to write the receipt file"
);
}
#[test]
fn cortex_push_receipt_omits_pack_cortex_receipt_noop_warning() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::create_dir_all(dir.path().join("doctrine-root")).expect("mkdir doctrine root");
std::fs::write(
dir.path().join("cordance.toml"),
r#"
[doctrine]
source = "doctrine-root"
fallback_repo = "https://nonexistent.example.invalid/doctrine"
pin_commit = "auto"
"#,
)
.expect("write cordance.toml");
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("cortex")
.arg("push")
.arg("--dry-run");
cmd.assert().success();
let receipt_path = dir.path().join(".cordance/cortex-receipt.json");
let receipt_json = std::fs::read_to_string(&receipt_path).expect("read receipt");
let receipt: serde_json::Value = serde_json::from_str(&receipt_json).expect("parse receipt");
let body = &receipt["cordance_execution_receipt_v1"];
let forbidden = "cortex-receipt requested via --targets but pack emits no receipt";
for field in ["allowed_claim_language", "residual_risk"] {
let values = body[field]
.as_array()
.unwrap_or_else(|| panic!("{field} must be an array in {receipt_path:?}"));
assert!(
!values
.iter()
.any(|value| value.as_str().is_some_and(|text| text.contains(forbidden))),
"{field} was polluted by pack CLI no-op warning: {values:?}"
);
}
}
#[test]
fn cortex_push_without_dry_run_prints_handoff() {
let dir = tempfile::tempdir().expect("tempdir");
let mut pack_cmd = Command::cargo_bin("cordance").expect("bin");
pack_cmd
.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("pack")
.arg("--output-mode")
.arg("write");
let _ = pack_cmd.output();
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("cortex")
.arg("push");
let output = cmd.output().expect("run cordance");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Cortex receipt:"),
"expected receipt path in stdout; got: {stdout}"
);
assert!(
stdout.contains("To submit:"),
"non-dry-run must print the operator handoff text; got: {stdout}"
);
assert!(
dir.path().join(".cordance/cortex-receipt.json").exists(),
"non-dry-run failed to write the receipt file"
);
}
#[test]
fn cordance_pack_with_llm_none_works() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(".")
.arg("pack")
.arg("--output-mode")
.arg("dry-run")
.arg("--llm")
.arg("none");
cmd.assert().success();
}
#[test]
fn cordance_pack_llm_flag_accepts_ollama_model_override() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(".")
.arg("pack")
.arg("--output-mode")
.arg("dry-run")
.arg("--llm")
.arg("none")
.arg("--ollama-model")
.arg("qwen2.5-coder:14b");
cmd.assert().success();
}
#[test]
#[ignore = "depends on port 11434 being free; run with --ignored locally"]
fn cordance_pack_with_llm_ollama_unavailable_warns_but_succeeds() {
let dir = tempfile::tempdir().expect("tempdir");
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("pack")
.arg("--output-mode")
.arg("dry-run")
.arg("--llm")
.arg("ollama");
cmd.assert().success();
}
#[test]
fn cli_rejects_target_outside_cwd() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
let outside = if cfg!(windows) { "C:\\" } else { "/" };
cmd.arg("--target").arg(outside).arg("scan");
cmd.assert()
.failure()
.stderr(predicates::str::contains("outside"));
}
#[test]
fn cli_accepts_target_inside_cwd() {
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target").arg(".").arg("scan").arg("--json");
cmd.assert().success();
}
#[test]
fn cli_allow_outside_cwd_overrides_guard() {
let tmp = tempfile::tempdir().expect("tempdir");
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(tmp.path())
.arg("--allow-outside-cwd")
.arg("scan")
.arg("--json");
cmd.assert().success();
}
#[test]
#[cfg(unix)]
fn cli_rejects_relative_symlink_escaping_cwd() {
use std::os::unix::fs::symlink;
let dir = tempfile::tempdir().expect("tempdir");
let link = dir.path().join("trap");
symlink(
"../../../../../../../../../../../etc/passwd-nonexistent",
&link,
)
.expect("symlink");
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target").arg(&link).arg("scan").arg("--json");
cmd.assert()
.failure()
.stderr(predicates::str::contains("symlink").or(predicates::str::contains("outside")));
}
#[test]
fn pack_uses_hardened_doctrine_loader_and_records_failure() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(
dir.path().join("cordance.toml"),
r#"
[doctrine]
source = "definitely-does-not-exist-engineering-doctrine"
fallback_repo = "https://nonexistent.example.invalid/doctrine"
pin_commit = "auto"
"#,
)
.expect("write cordance.toml");
let mut cmd = Command::cargo_bin("cordance").expect("bin");
cmd.arg("--target")
.arg(dir.path())
.arg("--allow-outside-cwd")
.arg("pack")
.arg("--output-mode")
.arg("write");
cmd.assert().success();
let pack_path = dir.path().join(".cordance/pack.json");
assert!(
pack_path.exists(),
"pack.json was not written; hardened doctrine loader may have aborted"
);
let pack_json = std::fs::read_to_string(&pack_path).expect("read pack.json");
assert!(
pack_json.contains("doctrine load failed"),
"residual_risk did not record the doctrine load failure (pack.json snippet: {})",
&pack_json[..pack_json.len().min(1024)]
);
}