use std::collections::BTreeSet; use std::path::{Path, PathBuf}; use rpki::bundle::{VapCompareRow, VrpCompareRow, decode_ccr_compare_views}; fn fixture(rel: &str) -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(rel) } fn run_case( name: &str, tal_ta_pairs: &[(&Path, &Path)], ) -> ( serde_json::Value, BTreeSet, BTreeSet, rpki::ccr::CcrContentInfo, ) { let db_dir = tempfile::tempdir().expect("db tempdir"); let out_dir = tempfile::tempdir().expect("out tempdir"); let report_path = out_dir.path().join(format!("{name}.report.json")); let ccr_path = out_dir.path().join(format!("{name}.ccr")); let mut argv = vec![ "rpki".to_string(), "--db".to_string(), db_dir.path().to_string_lossy().to_string(), "--parallel-phase1".to_string(), "--disable-rrdp".to_string(), "--rsync-local-dir".to_string(), fixture("tests/fixtures/repository").to_string_lossy().to_string(), "--validation-time".to_string(), "2026-04-07T00:00:00Z".to_string(), "--max-depth".to_string(), "0".to_string(), "--max-instances".to_string(), tal_ta_pairs.len().to_string(), ]; for (tal_path, ta_path) in tal_ta_pairs { argv.push("--tal-path".to_string()); argv.push(tal_path.to_string_lossy().to_string()); argv.push("--ta-path".to_string()); argv.push(ta_path.to_string_lossy().to_string()); } argv.extend([ "--report-json".to_string(), report_path.to_string_lossy().to_string(), "--ccr-out".to_string(), ccr_path.to_string_lossy().to_string(), ]); rpki::cli::run(&argv).expect("cli run"); let report: serde_json::Value = serde_json::from_slice(&std::fs::read(&report_path).expect("read report")) .expect("parse report"); let ccr = rpki::ccr::decode_content_info(&std::fs::read(&ccr_path).expect("read ccr")) .expect("decode ccr"); let (vrps, vaps) = decode_ccr_compare_views(&ccr, "multi").expect("compare views"); (report, vrps, vaps, ccr) } #[test] fn multi_tal_parallel_output_matches_union_of_single_tal_outputs() { let apnic_tal = fixture("tests/fixtures/tal/apnic-rfc7730-https.tal"); let apnic_ta = fixture("tests/fixtures/ta/apnic-ta.cer"); let arin_tal = fixture("tests/fixtures/tal/arin.tal"); let arin_ta = fixture("tests/fixtures/ta/arin-ta.cer"); let (apnic_report, apnic_vrps, apnic_vaps, _) = run_case("apnic", &[(&apnic_tal, &apnic_ta)]); let (arin_report, arin_vrps, arin_vaps, _) = run_case("arin", &[(&arin_tal, &arin_ta)]); let (multi_report, multi_vrps, multi_vaps, multi_ccr) = run_case("multi", &[(&apnic_tal, &apnic_ta), (&arin_tal, &arin_ta)]); let expected_vrps = apnic_vrps.union(&arin_vrps).cloned().collect::>(); let expected_vaps = apnic_vaps.union(&arin_vaps).cloned().collect::>(); assert_eq!(multi_vrps, expected_vrps); assert_eq!(multi_vaps, expected_vaps); let single_point_count = apnic_report["publication_points"] .as_array() .expect("apnic points") .len() + arin_report["publication_points"] .as_array() .expect("arin points") .len(); assert_eq!( multi_report["publication_points"] .as_array() .expect("multi points") .len(), single_point_count ); let tas = multi_ccr .content .tas .as_ref() .expect("multi-TAL CCR must include trust anchors"); assert_eq!(tas.skis.len(), 2); }