allow-report 0.1.9

Report and receipt rendering for cargo-allow source exception scans.
Documentation
use super::*;

#[test]
fn worklist_json_renderer_records_filters_summary_and_items() {
    let suggested_actions = vec!["review stale allow".to_string()];
    let proof_commands = vec!["cargo-allow check --mode no-new".to_string()];
    let items = vec![WorklistItem {
        id: "work-0001",
        kind: "stale_allow",
        exception_kind: Some("panic"),
        family: Some("unwrap"),
        owner: Some("parser"),
        classification: Some("baseline_debt"),
        reason: Some("generated baseline"),
        created: Some("2026-05-27"),
        review_after: Some("2026-07-01"),
        expires: Some("2026-08-02"),
        evidence_count: Some(1),
        selector_precision: Some(42),
        risk: "high",
        difficulty: "small",
        status: "stale",
        allow_id: Some("allow-0001"),
        finding_index: None,
        path: Some("crates/parser/src/lib.rs"),
        evidence_reference: None,
        source_package: Some("parser"),
        message: "stale allow",
        suggested_actions: &suggested_actions,
        proof_commands: &proof_commands,
    }];

    let json = render_worklist_json(
        &items,
        WorklistFilters {
            kind: Some("panic"),
            item_kind: Some("stale_allow"),
            risk: Some("high"),
            baseline_debt: true,
            missing_evidence: true,
            ..WorklistFilters::default()
        },
        InventoryContext::source_syntax("git_tracked", Some("H:/Code/Rust/cargo-allow"), Some(47)),
    );

    assert!(json.contains("\"schema_id\": \"cargo-allow.worklist.v1\""));
    assert!(json.contains("\"command\": \"worklist\""));
    assert!(json.contains("\"source\": \"git_tracked\""));
    assert!(json.contains("\"files_scanned\": 47"));
    assert!(json.contains("\"kind\": \"panic\""));
    assert!(json.contains("\"item_kind\": \"stale_allow\""));
    assert!(json.contains("\"baseline_debt\": true"));
    assert!(json.contains("\"missing_evidence\": true"));
    assert!(json.contains("\"broken_evidence\": false"));
    assert!(json.contains("\"weak_evidence\": false"));
    assert!(json.contains("\"work_items\": 1"));
    assert!(json.contains("\"high\": 1"));
    assert!(json.contains("\"small_difficulty\": 1"));
    assert!(json.contains("\"item_kinds\": {"));
    assert!(json.contains("\"stale_allow\": 1"));
    assert!(json.contains("\"id\": \"work-0001\""));
    assert!(json.contains("\"exception_kind\": \"panic\""));
    assert!(json.contains("\"evidence_count\": 1"));
    assert!(json.contains("\"selector_precision\": 42"));
    assert!(json.contains("\"finding_index\": null"));
    assert!(json.contains("\"source_package\": \"parser\""));
    assert!(json.contains("\"suggested_actions\": [\"review stale allow\"]"));
    assert!(json.contains("\"proof_commands\": [\"cargo-allow check --mode no-new\"]"));
    let expected = format!(
        r#"{{
  "schema_version": 1,
  "schema_id": "cargo-allow.worklist.v1",
  "tool": "cargo-allow",
  "command": "worklist",
  "claim_boundary": {},
  "scanner_limitations": {},
  "inventory": {{
    "scope": "source_tree",
    "scanner": "source_syntax",
    "source": "git_tracked",
    "root": "H:/Code/Rust/cargo-allow",
    "files_scanned": 47
  }},
  "filters": {{
    "kind": "panic",
    "family": null,
    "item_kind": "stale_allow",
    "status": null,
    "allow_id": null,
    "path": null,
    "source_package": null,
    "owner": null,
    "classification": null,
    "baseline_debt": true,
    "broad_scope": false,
    "risk": "high",
    "difficulty": null,
    "missing_evidence": true,
    "broken_evidence": false,
    "weak_evidence": false
  }},
  "summary": {{
    "work_items": 1,
    "high": 1,
    "medium": 0,
    "low": 0,
    "small_difficulty": 1,
    "medium_difficulty": 0,
    "item_kinds": {{
      "stale_allow": 1
    }}
  }},
  "work_items": [
    {{
      "id": "work-0001",
      "kind": "stale_allow",
      "exception_kind": "panic",
      "family": "unwrap",
      "owner": "parser",
      "classification": "baseline_debt",
      "reason": "generated baseline",
      "created": "2026-05-27",
      "review_after": "2026-07-01",
      "expires": "2026-08-02",
      "evidence_count": 1,
      "selector_precision": 42,
      "risk": "high",
      "difficulty": "small",
      "status": "stale",
      "allow_id": "allow-0001",
      "finding_index": null,
      "path": "crates/parser/src/lib.rs",
      "source_package": "parser",
      "message": "stale allow",
      "suggested_actions": ["review stale allow"],
      "proof_commands": ["cargo-allow check --mode no-new"]
    }}
  ]
}}
"#,
        render_claim_boundary_json(),
        render_scanner_limitations_json()
    );
    assert_eq!(json, expected);

    let text = render_worklist_human(
        &items,
        WorklistFilters {
            kind: Some("panic"),
            item_kind: Some("stale_allow"),
            risk: Some("high"),
            baseline_debt: true,
            missing_evidence: true,
            ..WorklistFilters::default()
        },
        InventoryContext::source_syntax("git_tracked", Some("H:/Code/Rust/cargo-allow"), Some(47)),
    );

    assert!(text.contains("cargo-allow worklist"));
    assert!(
        text.contains("Inventory: source_tree/source_syntax via git_tracked; files scanned: 47")
    );
    assert!(text.contains("Source tree root: H:/Code/Rust/cargo-allow"));
    assert!(text.contains(
            "Filters: kind=panic, item_kind=stale_allow, baseline_debt=true, risk=high, missing_evidence=true"
        ));
    assert!(text.contains("Work items: 1"));
    assert!(text.contains("Queue kinds:"));
    assert!(text.contains("  stale_allow"));
    assert!(text.contains("work-0001 (high, small) stale_allow"));
    assert!(text.contains("  path: crates/parser/src/lib.rs"));
    assert!(text.contains("  source package: parser"));
    assert!(text.contains("  exception: panic.unwrap"));
    assert!(text.contains("  selector_precision: 42"));
    assert!(text.contains("  action: review stale allow"));
    assert!(text.contains("  proof: cargo-allow check --mode no-new"));
    assert!(text.contains("external evidence tools"));
}

#[test]
fn worklist_json_renderer_includes_optional_evidence_reference() {
    let suggested_actions = vec!["replace weak evidence reference".to_string()];
    let proof_commands = vec!["cargo-allow explain allow-weak".to_string()];
    let items = vec![WorklistItem {
        id: "work-weak-evidence-reference-0001",
        kind: "weak_evidence_reference",
        exception_kind: Some("unsafe"),
        family: Some("unsafe_block"),
        owner: Some("runtime"),
        classification: Some("reviewed_unsafe_boundary"),
        reason: Some("fixture"),
        created: None,
        review_after: None,
        expires: None,
        evidence_count: Some(1),
        selector_precision: Some(17),
        risk: "high",
        difficulty: "small",
        status: "evidence_missing",
        allow_id: Some("allow-weak"),
        finding_index: None,
        path: None,
        evidence_reference: Some(EvidenceReference {
            raw: "spreadsheet:manual-review",
            prefix: Some("spreadsheet"),
            target: Some("manual-review"),
            status: "unstructured",
            category: "unknown_prefix",
            message: "unrecognized evidence prefix; not locally validated",
        }),
        source_package: None,
        message: "allow-weak evidence `spreadsheet:manual-review`: unrecognized evidence prefix; not locally validated",
        suggested_actions: &suggested_actions,
        proof_commands: &proof_commands,
    }];

    let json = render_worklist_json(
        &items,
        WorklistFilters::default(),
        InventoryContext::source_syntax("git_tracked", Some("H:/Code/Rust/cargo-allow"), Some(47)),
    );

    assert!(json.contains("\"path\": null"));
    assert!(json.contains("\"evidence_reference\": {"));
    assert!(json.contains("\"raw\": \"spreadsheet:manual-review\""));
    assert!(json.contains("\"prefix\": \"spreadsheet\""));
    assert!(json.contains("\"target\": \"manual-review\""));
    assert!(json.contains("\"status\": \"unstructured\""));
    assert!(json.contains("\"category\": \"unknown_prefix\""));
    assert!(json.contains("\"selector_precision\": 17"));
    assert!(json.contains("\"source_package\": null"));

    let text = render_worklist_human(
        &items,
        WorklistFilters::default(),
        InventoryContext::source_syntax("git_tracked", Some("H:/Code/Rust/cargo-allow"), Some(47)),
    );

    assert!(
        text.contains("work-weak-evidence-reference-0001 (high, small) weak_evidence_reference")
    );
    assert!(text.contains(
        "  evidence reference: weak: spreadsheet:manual-review (status=unstructured, prefix=spreadsheet, target=manual-review)"
    ));
    assert!(
        text.contains("  evidence message: unrecognized evidence prefix; not locally validated")
    );
    assert!(text.contains("  selector_precision: 17"));
}

#[test]
fn worklist_human_renderer_shows_ledger_inspection_proof_commands() {
    let suggested_actions = vec!["review broad scope".to_string()];
    let proof_commands = vec![
        "cargo-allow explain allow-broad".to_string(),
        "cargo-allow list --allow-id allow-broad --format json".to_string(),
        "cargo-allow worklist --allow-id allow-broad --format json".to_string(),
        "cargo-allow worklist --broad-scope --format json".to_string(),
        "cargo-allow check --kind non-rust --mode no-new".to_string(),
        "cargo-allow worklist --item-kind broad_scope --format json".to_string(),
        "cargo-allow list --broad-scope --format json".to_string(),
        "cargo-allow worklist --kind non-rust --format json".to_string(),
        "cargo-allow worklist --format json".to_string(),
    ];
    let items = vec![WorklistItem {
        id: "work-broad-scope-0001",
        kind: "broad_scope",
        exception_kind: Some("non_rust_file"),
        family: Some("documentation"),
        owner: Some("docs"),
        classification: Some("reviewed_exception"),
        reason: Some("fixture"),
        created: None,
        review_after: None,
        expires: None,
        evidence_count: Some(1),
        selector_precision: Some(12),
        risk: "medium",
        difficulty: "small",
        status: "matched",
        allow_id: Some("allow-broad"),
        finding_index: None,
        path: Some("docs/**"),
        evidence_reference: None,
        source_package: None,
        message: "broad scope",
        suggested_actions: &suggested_actions,
        proof_commands: &proof_commands,
    }];

    let text = render_worklist_human(
        &items,
        WorklistFilters::default(),
        InventoryContext::source_syntax("git_tracked", Some("H:/Code/Rust/cargo-allow"), Some(47)),
    );

    assert!(text.contains("  proof: cargo-allow list --broad-scope --format json"));
    assert!(text.contains("  proof: cargo-allow worklist --kind non-rust --format json"));
    assert!(
        !text.contains("  proof: cargo-allow worklist --format json"),
        "human output should still avoid dumping every long-tail proof command"
    );
}