use super::*;
use std::path::Path;
#[test]
fn render_prune_stale_preview_is_dry_run_first() {
let candidates = vec![PruneCandidate {
id: "allow-stale".to_string(),
kind: FindingKind::Panic,
family: Some("unwrap".to_string()),
owner: "parser".to_string(),
classification: "reviewed_exception".to_string(),
scope: "src/lib.rs".to_string(),
reason: "The old exception is gone.".to_string(),
}];
let removed_toml_blocks =
vec!["[[allow]]\nid = \"allow-stale\"\nkind = \"panic\"\nfamily = \"unwrap\"".to_string()];
let text = render_prune_stale_result(
&candidates,
&removed_toml_blocks,
true,
false,
None,
test_prune_context(),
);
assert!(
text.contains("Inventory: source_tree/source_syntax via git_tracked; files scanned: 49")
);
assert!(text.contains("Source tree root: H:/Code/Rust/cargo-allow"));
assert!(text.contains("mode: dry-run"));
assert!(text.contains("requested: --dry-run"));
assert!(text.contains("stale entries: 1"));
assert!(text.contains("allow-stale"));
assert!(text.contains("No files were changed"));
assert!(text.contains("TOML removal preview:"));
assert!(text.contains("```diff"));
assert!(text.contains("- [[allow]]"));
assert!(text.contains("- id = \"allow-stale\""));
}
#[test]
fn render_prune_stale_json_records_context_and_candidates() {
let candidates = vec![PruneCandidate {
id: "allow-stale".to_string(),
kind: FindingKind::Panic,
family: Some("unwrap".to_string()),
owner: "parser".to_string(),
classification: "baseline_debt".to_string(),
scope: "crates/parser/src/lib.rs".to_string(),
reason: "old baseline entry".to_string(),
}];
let json = render_prune_stale_json(
&candidates,
true,
false,
None,
PruneContext {
inventory: allow_report::InventoryContext::source_syntax(
"git_tracked",
Some("H:/Code/Rust/cargo-allow"),
Some(49),
),
mutation_receipt: allow_report::MutationReceipt {
operation: "prune",
tool_version: "0.1.10",
repo_root: Some("H:/Code/Rust/cargo-allow"),
config_source: Some("policy/allow.toml"),
ledger_ids: Vec::new(),
changed_allow_ids: vec!["allow-stale"],
before_fingerprints: vec![Some(
"sha256:v1:0000000000000000000000000000000000000000000000000000000000000000"
.to_string(),
)],
after_fingerprints: vec![None],
result: "stdout",
next_commands: vec!["cargo-allow check --mode no-new".to_string()],
},
},
);
assert!(json.contains("\"schema_version\": 1"));
assert!(json.contains(&format!(
"\"schema_id\": \"{}\"",
allow_report::PRUNE_SCHEMA_ID
)));
assert!(json.contains("\"command\": \"prune\""));
assert!(json.contains("\"claim_boundary\""));
assert!(json.contains("\"scanner_limitations\""));
assert!(json.contains("\"source\": \"git_tracked\""));
assert!(json.contains("\"root\": \"H:/Code/Rust/cargo-allow\""));
assert!(json.contains("\"files_scanned\": 49"));
assert!(json.contains("\"dry_run\": true"));
assert!(json.contains("\"write_requested\": false"));
assert!(json.contains("\"explicit_dry_run\": true"));
assert!(json.contains("\"written_path\": null"));
assert!(json.contains("\"stale_entries\": 1"));
assert!(json.contains("\"id\": \"allow-stale\""));
assert!(json.contains("\"kind\": \"panic\""));
assert!(json.contains("\"family\": \"unwrap\""));
let parsed = serde_json::from_str::<serde_json::Value>(&json);
assert!(parsed.is_ok(), "prune output must remain valid JSON");
let Some(parsed) = parsed.ok() else {
return;
};
assert_eq!(
parsed
.pointer("/mutation_receipt/operation")
.and_then(serde_json::Value::as_str),
Some("prune")
);
assert_eq!(
parsed
.pointer("/mutation_receipt/changed_allow_ids/0")
.and_then(serde_json::Value::as_str),
Some("allow-stale")
);
assert_eq!(
parsed.pointer("/mutation_receipt/after_fingerprints/0"),
Some(&serde_json::Value::Null)
);
}
#[test]
fn render_prune_stale_result_reports_written_policy() {
let candidates = vec![PruneCandidate {
id: "allow-stale".to_string(),
kind: FindingKind::Panic,
family: Some("unwrap".to_string()),
owner: "parser".to_string(),
classification: "reviewed_exception".to_string(),
scope: "src/lib.rs".to_string(),
reason: "The old exception is gone.".to_string(),
}];
let text = render_prune_stale_result(
&candidates,
&[],
false,
true,
Some(Path::new("policy/allow.toml")),
test_prune_context(),
);
assert!(text.contains("mode: write"));
assert!(text.contains("Removed stale entries from `policy/allow.toml`"));
assert!(!text.contains("No files were changed"));
}
#[test]
fn render_prune_stale_result_reports_write_mode_with_no_candidates() {
let text = render_prune_stale_result(&[], &[], false, true, None, test_prune_context());
assert!(text.contains("mode: write"));
assert!(text.contains("No stale allow entries found."));
assert!(!text.contains("TOML removal preview:"));
}
#[test]
fn test_prune_context_return_value_observer() {
let context = test_prune_context();
assert_eq!(context.inventory.scope, "source_tree");
assert_eq!(context.inventory.scanner, "source_syntax");
assert_eq!(context.inventory.source, "git_tracked");
assert_eq!(context.inventory.root, Some("H:/Code/Rust/cargo-allow"));
assert_eq!(context.inventory.files_scanned, Some(49));
}
fn test_prune_context() -> PruneContext<'static> {
PruneContext {
inventory: allow_report::InventoryContext::source_syntax(
"git_tracked",
Some("H:/Code/Rust/cargo-allow"),
Some(49),
),
mutation_receipt: allow_report::MutationReceipt {
operation: "prune",
tool_version: "0.1.10",
repo_root: Some("H:/Code/Rust/cargo-allow"),
config_source: Some("policy/allow.toml"),
ledger_ids: Vec::new(),
changed_allow_ids: vec!["allow-stale"],
before_fingerprints: vec![Some(
"sha256:v1:0000000000000000000000000000000000000000000000000000000000000000"
.to_string(),
)],
after_fingerprints: vec![None],
result: "stdout",
next_commands: vec!["cargo-allow check --mode no-new".to_string()],
},
}
}