use std::fs; use std::process::Command; fn multi_rir_bundle_root() -> std::path::PathBuf { std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../rpki/target/live/20260316-112341-multi-final3") } fn helper_script() -> std::path::PathBuf { std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("scripts/payload_replay/multi_rir_case_info.py") } fn wrapper_script() -> std::path::PathBuf { std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("scripts/payload_replay/run_multi_rir_replay_case.sh") } #[test] fn multi_rir_case_info_resolves_all_five_rirs_and_timings() { let bundle_root = multi_rir_bundle_root(); assert!(bundle_root.is_dir(), "bundle root missing: {}", bundle_root.display()); let expected = [ ("afrinic", "afrinic", "afrinic.tal", "afrinic-ta.cer"), ("apnic", "apnic", "apnic-rfc7730-https.tal", "apnic-ta.cer"), ("arin", "arin", "arin.tal", "arin-ta.cer"), ("lacnic", "lacnic", "lacnic.tal", "lacnic-ta.cer"), ("ripe", "ripe", "ripe-ncc.tal", "ripe-ncc-ta.cer"), ]; for (rir, trust_anchor, tal_suffix, ta_suffix) in expected { let out = Command::new("python3") .arg(helper_script()) .args([ "--bundle-root", bundle_root.to_string_lossy().as_ref(), "--rir", rir, ]) .output() .expect("run helper script"); assert!( out.status.success(), "helper failed for {rir}: status={}\nstdout={}\nstderr={}", out.status, String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); let json: serde_json::Value = serde_json::from_slice(&out.stdout).expect("parse helper json"); assert_eq!(json["rir"].as_str(), Some(rir)); assert_eq!(json["trust_anchor"].as_str(), Some(trust_anchor)); assert!(json["base_archive"].as_str().unwrap_or("").ends_with("base-payload-archive")); assert!(json["delta_archive"].as_str().unwrap_or("").ends_with("payload-delta-archive")); assert!(json["base_locks"].as_str().unwrap_or("").ends_with("base-locks.json")); assert!(json["delta_locks"].as_str().unwrap_or("").ends_with("locks-delta.json")); assert!(json["tal_path"].as_str().unwrap_or("").ends_with(tal_suffix)); assert!(json["ta_path"].as_str().unwrap_or("").ends_with(ta_suffix)); assert!(json["validation_times"]["snapshot"].as_str().unwrap_or("").contains("T")); assert!(json["validation_times"]["delta"].as_str().unwrap_or("").contains("T")); assert!(json["routinator_timings"]["base_replay_seconds"] .as_f64() .unwrap_or(0.0) > 0.0); assert!(json["routinator_timings"]["delta_replay_seconds"] .as_f64() .unwrap_or(0.0) > 0.0); } } #[test] fn multi_rir_case_info_prefers_lock_validation_time_over_replay_started_at() { let td = tempfile::tempdir().expect("tempdir"); let bundle_root = td.path(); let rir_root = bundle_root.join("apnic"); fs::create_dir_all(rir_root.join("base-payload-archive")).expect("base archive dir"); fs::create_dir_all(rir_root.join("payload-delta-archive")).expect("delta archive dir"); fs::create_dir_all(rir_root.join("timings")).expect("timings dir"); fs::write( bundle_root.join("timing-summary.json"), r#"{"apnic":{"durations":{"base-replay":1.5,"delta-replay":2.5}}}"#, ) .expect("write timing summary"); fs::write( rir_root.join("base-locks.json"), r#"{"version":1,"capture":"base-cap","validationTime":"2026-03-16T11:49:15+08:00","rrdp":{},"rsync":{}}"#, ) .expect("write base locks"); fs::write( rir_root.join("locks-delta.json"), r#"{"version":1,"capture":"delta-cap","baseCapture":"base-cap","baseLocksSha256":"deadbeef","validationTime":"2026-03-16T12:14:10+08:00","rrdp":{},"rsync":{}}"#, ) .expect("write delta locks"); fs::write( rir_root.join("timings/base-replay.json"), r#"{"startedAt":"2099-01-01T00:00:00Z","durationSeconds":1.5}"#, ) .expect("write base timing"); fs::write( rir_root.join("timings/delta-replay.json"), r#"{"startedAt":"2099-01-02T00:00:00Z","durationSeconds":2.5}"#, ) .expect("write delta timing"); for rel in [ "base-vrps.csv", "record-delta.csv", "replay-delta.csv", "verification.json", "README.md", ] { fs::write(rir_root.join(rel), "placeholder ").expect("write required file"); } let repo_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let out = Command::new("python3") .arg(helper_script()) .args([ "--bundle-root", bundle_root.to_string_lossy().as_ref(), "--repo-root", repo_root.to_string_lossy().as_ref(), "--rir", "apnic", ]) .output() .expect("run helper script"); assert!( out.status.success(), "helper failed: status={} stdout={} stderr={}", out.status, String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); let json: serde_json::Value = serde_json::from_slice(&out.stdout).expect("parse helper json"); assert_eq!( json["validation_times"]["snapshot"].as_str(), Some("2026-03-16T11:49:15+08:00") ); assert_eq!( json["validation_times"]["delta"].as_str(), Some("2026-03-16T12:14:10+08:00") ); assert_eq!( json["timing_started_at"]["snapshot_replay"].as_str(), Some("2099-01-01T00:00:00Z") ); assert_eq!( json["timing_started_at"]["delta_replay"].as_str(), Some("2099-01-02T00:00:00Z") ); } #[test] fn multi_rir_wrapper_describe_mode_works_for_ripe() { let bundle_root = multi_rir_bundle_root(); assert!(bundle_root.is_dir(), "bundle root missing: {}", bundle_root.display()); let out = Command::new(wrapper_script()) .env("BUNDLE_ROOT", &bundle_root) .args(["ripe", "describe"]) .output() .expect("run wrapper script"); assert!( out.status.success(), "wrapper failed: status={}\nstdout={}\nstderr={}", out.status, String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); let json: serde_json::Value = serde_json::from_slice(&out.stdout).expect("parse wrapper describe json"); assert_eq!(json["rir"].as_str(), Some("ripe")); assert_eq!(json["trust_anchor"].as_str(), Some("ripe")); assert!(json["verification_json"] .as_str() .unwrap_or("") .ends_with("verification.json")); }