use super::*;
use crate::findings::{
executable_finding, executable_findings_from_git_stage, executable_findings_from_paths,
generated_finding, generated_findings_from_gitattributes_text,
};
use crate::test_support::*;
use allow_core::FindingKind;
use std::path::{Path, PathBuf};
#[test]
fn migrates_generated_allowlist_to_canonical_policy() {
let policy = generated_policy_fixture_path();
let cfg = load_legacy_or_canonical(&policy)
.unwrap_or_else(|err| std::panic::panic_any(format!("generated policy migrates: {err}")));
assert_eq!(cfg.policy, "cargo-allow");
assert_eq!(cfg.allow.len(), 1);
let entry = cfg
.allow
.first()
.unwrap_or_else(|| std::panic::panic_any("expected generated allow entry"));
assert_eq!(entry.kind, FindingKind::GeneratedCode);
assert_eq!(entry.family.as_deref(), Some("generated_code"));
assert_eq!(
entry.path.as_deref(),
Some(Path::new("policy/no-panic-baseline.toml"))
);
assert_eq!(entry.lifecycle.expires.as_deref(), Some("never"));
assert_eq!(entry.lifecycle.review_after.as_deref(), Some("2026-05-10"));
assert!(
entry
.evidence
.iter()
.any(|item| item == "legacy-policy:generated-no-panic-baseline")
);
assert!(entry.evidence.iter().any(|item| item.starts_with("cargo:")));
}
#[test]
fn generated_findings_read_linguist_generated_paths() {
let root = generated_fixture_root();
let findings = generated_findings_from_gitattributes(&root)
.unwrap_or_else(|err| std::panic::panic_any(format!("generated findings load: {err}")));
assert_eq!(findings.len(), 1);
let finding = findings
.first()
.unwrap_or_else(|| std::panic::panic_any("expected generated finding"));
assert_eq!(finding.kind, FindingKind::GeneratedCode);
assert_eq!(finding.path, PathBuf::from("policy/no-panic-baseline.toml"));
}
#[test]
fn generated_findings_can_read_gitattributes_text() {
let findings = generated_findings_from_gitattributes_text(
"generated/schema.json linguist-generated=true\nREADME.md linguist-documentation=true\n",
);
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].kind, FindingKind::GeneratedCode);
assert_eq!(findings[0].path, PathBuf::from("generated/schema.json"));
}
#[test]
fn generated_compat_preserves_missing_and_stale_drift() {
let policy = generated_policy_fixture_path();
let cfg = load_generated_compat_config(&policy).unwrap_or_else(|err| {
std::panic::panic_any(format!("generated compat config loads: {err}"))
});
let matched = allow_match::evaluate(
&cfg,
&[generated_finding(PathBuf::from(
"policy/no-panic-baseline.toml",
))],
allow_match::CheckMode::NoNew,
);
assert!(matched.iter().any(|outcome| {
outcome.finding_index.is_some()
&& outcome.allow_id.as_deref() == Some("generated-no-panic-baseline")
&& matches!(
outcome.status,
allow_core::MatchStatus::Matched | allow_core::MatchStatus::ReviewDue
)
}));
let missing_allow = allow_match::evaluate(
&cfg,
&[generated_finding(PathBuf::from(
"policy/extra-baseline.toml",
))],
allow_match::CheckMode::NoNew,
);
assert!(
missing_allow
.iter()
.any(|outcome| outcome.status == allow_core::MatchStatus::New)
);
let stale_allow = allow_match::evaluate(&cfg, &[], allow_match::CheckMode::Audit);
assert!(stale_allow.iter().any(|outcome| {
outcome.finding_index.is_none()
&& matches!(
outcome.status,
allow_core::MatchStatus::Stale | allow_core::MatchStatus::ReviewDue
)
}));
}
#[test]
fn generated_migration_preserves_legacy_evidence_when_present() {
let path = fixture_dir().join("generated-allowlist.toml");
std::fs::write(
&path,
r#"schema_version = 1
policy = "generated-allowlist"
owner = "EffortlessMetrics"
status = "advisory"
[[allow]]
id = "generated-schema"
path = "docs/generated/schema.json"
generator = "cargo xtask schema"
regenerate_command = "cargo xtask schema"
owner = "policy"
reason = "Generated schema fixture."
evidence = ["doc:docs/schemas/README.md", "issue:#123"]
created = "2026-05-10"
expires = "permanent"
"#,
)
.unwrap_or_else(|err| std::panic::panic_any(format!("fixture write: {err}")));
let cfg = load_legacy_or_canonical(&path).unwrap_or_else(|err| {
std::panic::panic_any(format!("generated policy with evidence migrates: {err}"))
});
let entry = cfg
.allow
.first()
.unwrap_or_else(|| std::panic::panic_any("expected generated allow entry"));
assert!(
entry
.evidence
.iter()
.any(|item| item == "doc:docs/schemas/README.md")
);
assert!(entry.evidence.iter().any(|item| item == "issue:#123"));
assert!(
entry
.evidence
.iter()
.any(|item| item == "legacy-policy:generated-schema")
);
assert!(
entry
.evidence
.iter()
.any(|item| item == "generator:cargo xtask schema")
);
assert!(
entry
.evidence
.iter()
.any(|item| item == "cargo:cargo xtask schema")
);
}
#[test]
fn migrates_executable_allowlist_to_policy_exception_entries() {
let policy = executable_policy_fixture_path();
let cfg = load_legacy_or_canonical(&policy)
.unwrap_or_else(|err| std::panic::panic_any(format!("executable policy migrates: {err}")));
assert_eq!(cfg.policy, "cargo-allow");
assert_eq!(cfg.allow.len(), 1);
let entry = cfg
.allow
.first()
.unwrap_or_else(|| std::panic::panic_any("expected executable allow entry"));
assert_eq!(entry.kind, FindingKind::PolicyException);
assert_eq!(entry.family.as_deref(), Some("executable_file"));
assert_eq!(entry.classification, "executable_file");
assert_eq!(
entry.path.as_deref(),
Some(Path::new("scripts/package-proof.sh"))
);
assert_eq!(entry.lifecycle.expires.as_deref(), Some("never"));
assert_eq!(entry.lifecycle.review_after.as_deref(), Some("2026-05-09"));
assert_eq!(
entry.evidence,
vec![
"legacy-policy:exec-package-proof".to_string(),
"interpreter:bash".to_string(),
]
);
assert_eq!(
entry.selector.target_fingerprint.as_deref(),
Some("git-mode:100755")
);
}
#[test]
fn executable_migration_accepts_covered_by_as_legacy_evidence() {
let path = fixture_dir().join("executable-allowlist.toml");
std::fs::write(
&path,
r#"schema_version = 1
policy = "executable-allowlist"
owner = "EffortlessMetrics"
status = "advisory"
[[allow]]
id = "exec-release-helper"
path = "scripts/release.sh"
interpreter = "bash"
owner = "release"
reason = "Release helper fixture."
covered_by = "doc:docs/release/README.md"
created = "2026-05-09"
expires = "permanent"
"#,
)
.unwrap_or_else(|err| std::panic::panic_any(format!("fixture write: {err}")));
let cfg = load_legacy_or_canonical(&path).unwrap_or_else(|err| {
std::panic::panic_any(format!("executable policy with covered_by migrates: {err}"))
});
let entry = cfg
.allow
.first()
.unwrap_or_else(|| std::panic::panic_any("expected executable allow entry"));
assert_eq!(
entry.evidence,
vec![
"doc:docs/release/README.md".to_string(),
"legacy-policy:exec-release-helper".to_string(),
"interpreter:bash".to_string(),
]
);
}
#[test]
fn executable_findings_read_git_stage_executable_paths() {
let stage = "\
100644 abc 0\tREADME.md\n\
100755 def 0\tscripts/package-proof.sh\n\
120000 ghi 0\tscripts/link.sh\n";
let findings = executable_findings_from_git_stage(stage);
assert_eq!(findings.len(), 1);
let finding = findings
.first()
.unwrap_or_else(|| std::panic::panic_any("expected executable finding"));
assert_eq!(finding.kind, FindingKind::PolicyException);
assert_eq!(finding.family.as_deref(), Some("executable_file"));
assert_eq!(finding.path, PathBuf::from("scripts/package-proof.sh"));
assert_eq!(finding.identity.ast_kind, "git_executable_file");
assert_eq!(
finding.identity.target_fingerprint.as_deref(),
Some("git-mode:100755")
);
}
#[test]
fn executable_findings_can_use_source_tree_paths() {
let findings = executable_findings_from_paths(&[PathBuf::from("scripts/package-proof.sh")]);
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].kind, FindingKind::PolicyException);
assert_eq!(findings[0].family.as_deref(), Some("executable_file"));
assert_eq!(findings[0].path, PathBuf::from("scripts/package-proof.sh"));
assert_eq!(findings[0].identity.ast_kind, "git_executable_file");
assert_eq!(
findings[0].identity.target_fingerprint.as_deref(),
Some("git-mode:100755")
);
}
#[test]
fn executable_compat_preserves_missing_and_stale_drift() {
let policy = executable_policy_fixture_path();
let cfg = load_executable_compat_config(&policy).unwrap_or_else(|err| {
std::panic::panic_any(format!("executable compat config loads: {err}"))
});
let matched = allow_match::evaluate(
&cfg,
&[executable_finding(PathBuf::from(
"scripts/package-proof.sh",
))],
allow_match::CheckMode::NoNew,
);
assert!(matched.iter().any(|outcome| {
outcome.finding_index.is_some()
&& outcome.allow_id.as_deref() == Some("exec-package-proof")
&& matches!(
outcome.status,
allow_core::MatchStatus::Matched | allow_core::MatchStatus::ReviewDue
)
}));
let missing_allow = allow_match::evaluate(
&cfg,
&[executable_finding(PathBuf::from("scripts/new-tool.sh"))],
allow_match::CheckMode::NoNew,
);
assert!(
missing_allow
.iter()
.any(|outcome| outcome.status == allow_core::MatchStatus::New)
);
let stale_allow = allow_match::evaluate(&cfg, &[], allow_match::CheckMode::Audit);
assert!(stale_allow.iter().any(|outcome| {
outcome.finding_index.is_none()
&& matches!(
outcome.status,
allow_core::MatchStatus::Stale | allow_core::MatchStatus::ReviewDue
)
}));
}