use std::path::{Path, PathBuf}; use std::process::Command; use rpki::cir::{ CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir, materialize_cir, }; fn apnic_tal_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/tal/apnic-rfc7730-https.tal") } fn apnic_ta_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer") } fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec) { let tal_bytes = std::fs::read(apnic_tal_path()).expect("read tal"); let ta_bytes = std::fs::read(apnic_ta_path()).expect("read ta"); let tal = rpki::data_model::tal::Tal::decode_bytes(&tal_bytes).expect("decode tal"); let ta_rsync_uri = tal .ta_uris .iter() .find(|uri| uri.scheme() == "rsync") .expect("tal has rsync uri") .as_str() .to_string(); let ta_hash = { use sha2::{Digest, Sha256}; Sha256::digest(&ta_bytes).to_vec() }; ( CanonicalInputRepresentation { version: CIR_VERSION_V1, hash_alg: CirHashAlgorithm::Sha256, validation_time: time::OffsetDateTime::parse( "2026-04-07T00:00:00Z", &time::format_description::well_known::Rfc3339, ) .unwrap(), objects: vec![CirObject { rsync_uri: ta_rsync_uri, sha256: ta_hash, }], tals: vec![CirTal { tal_uri: "https://example.test/root.tal".to_string(), tal_bytes, }], }, ta_bytes, ) } fn write_static(root: &Path, date: &str, bytes: &[u8]) { use sha2::{Digest, Sha256}; let hash = hex::encode(Sha256::digest(bytes)); let dir = root.join(date).join(&hash[0..2]).join(&hash[2..4]); std::fs::create_dir_all(&dir).expect("mkdir static"); std::fs::write(dir.join(hash), bytes).expect("write static object"); } fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror_root: &Path) -> PathBuf { let reference_ccr = work.join("reference.ccr"); let rpki_bin = env!("CARGO_BIN_EXE_rpki"); let wrapper = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/cir-rsync-wrapper"); let tal_path = apnic_tal_path(); let ta_path = apnic_ta_path(); let out = Command::new(rpki_bin) .env("REAL_RSYNC_BIN", "/usr/bin/rsync") .env("CIR_MIRROR_ROOT", mirror_root) .args([ "--db", work.join("reference-db").to_string_lossy().as_ref(), "--tal-path", tal_path.to_string_lossy().as_ref(), "--ta-path", ta_path.to_string_lossy().as_ref(), "--disable-rrdp", "--rsync-command", wrapper.to_string_lossy().as_ref(), "--validation-time", &cir.validation_time .format(&time::format_description::well_known::Rfc3339) .unwrap(), "--max-depth", "0", "--max-instances", "1", "--ccr-out", reference_ccr.to_string_lossy().as_ref(), ]) .output() .expect("run reference rpki"); assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr)); reference_ccr } #[test] fn cir_routinator_script_matches_reference_on_ta_only_cir() { if !Path::new("/usr/bin/rsync").exists() || !Path::new("/home/yuyr/dev/rust_playground/routinator/target/debug/routinator").exists() { return; } let td = tempfile::tempdir().expect("tempdir"); let static_root = td.path().join("static"); let cir_path = td.path().join("sample.cir"); let mirror_root = td.path().join("mirror"); let out_dir = td.path().join("routinator-out"); let (cir, ta_bytes) = build_ta_only_cir(); std::fs::write(&cir_path, encode_cir(&cir).expect("encode cir")).expect("write cir"); write_static(&static_root, "20260407", &ta_bytes); materialize_cir(&cir, &static_root, &mirror_root, true).expect("materialize"); let reference_ccr = prepare_reference_ccr(td.path(), &cir, &mirror_root); let script = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_replay_routinator.sh"); let out = Command::new(script) .args([ "--cir", cir_path.to_string_lossy().as_ref(), "--static-root", static_root.to_string_lossy().as_ref(), "--out-dir", out_dir.to_string_lossy().as_ref(), "--reference-ccr", reference_ccr.to_string_lossy().as_ref(), ]) .output() .expect("run routinator cir script"); assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr)); let summary: serde_json::Value = serde_json::from_slice( &std::fs::read(out_dir.join("compare-summary.json")).expect("read summary"), ) .expect("parse summary"); assert_eq!(summary["vrps"]["match"], true); assert_eq!(summary["vaps"]["match"], true); } #[test] fn cir_rpki_client_script_matches_reference_on_ta_only_cir() { if !Path::new("/usr/bin/rsync").exists() || !Path::new("/home/yuyr/dev/rpki-client-9.7/build-m5/src/rpki-client").exists() { return; } let td = tempfile::tempdir().expect("tempdir"); let static_root = td.path().join("static"); let cir_path = td.path().join("sample.cir"); let mirror_root = td.path().join("mirror"); let out_dir = td.path().join("rpki-client-out"); let (cir, ta_bytes) = build_ta_only_cir(); std::fs::write(&cir_path, encode_cir(&cir).expect("encode cir")).expect("write cir"); write_static(&static_root, "20260407", &ta_bytes); materialize_cir(&cir, &static_root, &mirror_root, true).expect("materialize"); let reference_ccr = prepare_reference_ccr(td.path(), &cir, &mirror_root); let script = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_replay_rpki_client.sh"); let out = Command::new(script) .args([ "--cir", cir_path.to_string_lossy().as_ref(), "--static-root", static_root.to_string_lossy().as_ref(), "--out-dir", out_dir.to_string_lossy().as_ref(), "--reference-ccr", reference_ccr.to_string_lossy().as_ref(), "--build-dir", "/home/yuyr/dev/rpki-client-9.7/build-m5", ]) .output() .expect("run rpki-client cir script"); assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr)); let summary: serde_json::Value = serde_json::from_slice( &std::fs::read(out_dir.join("compare-summary.json")).expect("read summary"), ) .expect("parse summary"); assert_eq!(summary["vrps"]["match"], true); assert_eq!(summary["vaps"]["match"], true); }