cargo-allow 0.1.3

Source-tree exception ledger and policy scanner for Rust repositories.
mod diff_support;
mod json_assertions;
mod support;

use diff_support::{
    assert_saved_json_diff_failure, assert_saved_json_diff_success, policy_with_evidence,
    write_diff_fixture,
};
use json_assertions::{assert_json_str, assert_json_u64};
use support::{remove_temp_root, temp_root};

#[test]
fn diff_json_reports_broken_evidence_as_current_failure() {
    let root = temp_root("diff-broken-evidence");
    let policy = policy_with_evidence(Some("doc:docs/missing-evidence.md"));
    write_diff_fixture(&root, policy.clone(), policy);
    let output = root.join("diff.json");

    let value = assert_saved_json_diff_failure(&root, &output);
    assert_json_u64(
        &value,
        "/summary/broken_evidence_links",
        1,
        "diff summary broken_evidence_links",
    );
    assert_json_u64(
        &value,
        "/trend/broken_evidence_links",
        1,
        "diff trend broken_evidence_links",
    );
    assert_json_u64(
        &value,
        "/diff/summary/current_failures",
        1,
        "diff posture current_failures",
    );
    assert_json_str(
        &value,
        "/diff/net_posture",
        "worse",
        "diff broken evidence net posture",
    );

    remove_temp_root(root);
}

#[test]
fn diff_json_reports_policy_missing_evidence_counts() {
    let root = temp_root("diff-policy-missing-evidence");
    let policy = policy_with_evidence(None);
    write_diff_fixture(&root, policy.clone(), policy);
    let output = root.join("diff.json");

    let value = assert_saved_json_diff_success(&root, &output);
    assert_json_u64(
        &value,
        "/summary/policy_missing_evidence",
        1,
        "diff summary policy_missing_evidence",
    );
    assert_json_u64(
        &value,
        "/trend/policy_missing_evidence",
        1,
        "diff trend policy_missing_evidence",
    );
    assert_json_str(
        &value,
        "/diff/net_posture",
        "unchanged",
        "diff policy missing evidence net posture",
    );

    remove_temp_root(root);
}

#[test]
fn diff_json_reports_policy_baseline_debt_counts() {
    let root = temp_root("diff-policy-baseline-debt");
    let policy = policy_with_baseline_debt();
    write_diff_fixture(&root, policy.clone(), policy);
    let output = root.join("diff.json");

    let value = assert_saved_json_diff_success(&root, &output);
    assert_json_u64(
        &value,
        "/summary/baseline_debt",
        0,
        "diff summary baseline_debt",
    );
    assert_json_u64(
        &value,
        "/summary/policy_baseline_debt",
        1,
        "diff summary policy_baseline_debt",
    );
    assert_json_u64(
        &value,
        "/trend/baseline_debt",
        1,
        "diff trend baseline_debt",
    );
    assert_json_str(
        &value,
        "/diff/net_posture",
        "unchanged",
        "diff policy baseline debt net posture",
    );

    remove_temp_root(root);
}

fn policy_with_baseline_debt() -> String {
    r#"policy = "cargo-allow"

[workspace]
ignored = ["policy/**"]

[[allow]]
id = "allow-unwrap"
kind = "panic"
family = "unwrap"
path = "src/lib.rs"
owner = "unowned"
classification = "baseline_debt"
reason = "Generated by cargo-allow propose; requires human review."
created = "2026-05-29"
expires = "2026-08-29"

[allow.selector]
ast_kind = "method_call"
container = "load"
callee = "unwrap"
"#
    .to_string()
}