use std::path::PathBuf; use std::process::Command; use rpki::ccr::{ encode_content_info, CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation, TrustAnchorState, }; use rpki::cir::{encode_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1}; #[test] fn cir_drop_report_counts_dropped_roa_objects_and_vrps() { let td = tempfile::tempdir().expect("tempdir"); let cir_path = td.path().join("input.cir"); let ccr_path = td.path().join("output.ccr"); let report_path = td.path().join("report.json"); let static_root = td.path().join("static"); let json_out = td.path().join("drop.json"); let md_out = td.path().join("drop.md"); let roa_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/AS4538.roa"); let roa_bytes = std::fs::read(&roa_path).expect("read roa fixture"); let hash = { use sha2::{Digest, Sha256}; hex::encode(Sha256::digest(&roa_bytes)) }; let dir = static_root.join("20260409").join(&hash[0..2]).join(&hash[2..4]); std::fs::create_dir_all(&dir).unwrap(); std::fs::write(dir.join(&hash), &roa_bytes).unwrap(); let cir = CanonicalInputRepresentation { version: CIR_VERSION_V1, hash_alg: CirHashAlgorithm::Sha256, validation_time: time::OffsetDateTime::parse( "2026-04-09T00:00:00Z", &time::format_description::well_known::Rfc3339, ) .unwrap(), objects: vec![CirObject { rsync_uri: "rsync://example.net/repo/AS4538.roa".to_string(), sha256: hex::decode(&hash).unwrap(), }], tals: vec![CirTal { tal_uri: "https://example.test/root.tal".to_string(), tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(), }], }; std::fs::write(&cir_path, encode_cir(&cir).unwrap()).unwrap(); let ccr = CcrContentInfo::new(RpkiCanonicalCacheRepresentation { version: 0, hash_alg: CcrDigestAlgorithm::Sha256, produced_at: cir.validation_time, mfts: None, vrps: None, vaps: None, tas: Some(TrustAnchorState { skis: vec![vec![0x11; 20]], hash: vec![0x22; 32], }), rks: None, }); std::fs::write(&ccr_path, encode_content_info(&ccr).unwrap()).unwrap(); let report = serde_json::json!({ "format_version": 2, "publication_points": [ { "manifest_rsync_uri": "rsync://example.net/repo/example.mft", "publication_point_rsync_uri": "rsync://example.net/repo/", "objects": [ { "rsync_uri": "rsync://example.net/repo/AS4538.roa", "sha256_hex": hash, "kind": "roa", "result": "error", "detail": "policy rejected" } ] } ] }); std::fs::write(&report_path, serde_json::to_vec_pretty(&report).unwrap()).unwrap(); let bin = env!("CARGO_BIN_EXE_cir_drop_report"); let out = Command::new(bin) .args([ "--cir", cir_path.to_string_lossy().as_ref(), "--ccr", ccr_path.to_string_lossy().as_ref(), "--report-json", report_path.to_string_lossy().as_ref(), "--static-root", static_root.to_string_lossy().as_ref(), "--json-out", json_out.to_string_lossy().as_ref(), "--md-out", md_out.to_string_lossy().as_ref(), ]) .output() .expect("run cir_drop_report"); assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr)); let output: serde_json::Value = serde_json::from_slice(&std::fs::read(&json_out).unwrap()).unwrap(); assert_eq!(output["summary"]["droppedObjectCount"], 1); assert!(output["summary"]["droppedVrpCount"].as_u64().unwrap_or(0) >= 1); assert_eq!(output["summary"]["droppedByKind"]["roa"], 1); assert!(std::fs::read_to_string(&md_out).unwrap().contains("Dropped By Reason")); }