use std::process::Command; #[test] fn cli_run_offline_mode_executes_and_writes_json_and_ccr() { let db_dir = tempfile::tempdir().expect("db tempdir"); let repo_dir = tempfile::tempdir().expect("repo tempdir"); let out_dir = tempfile::tempdir().expect("out tempdir"); let report_path = out_dir.path().join("report.json"); let ccr_path = out_dir.path().join("result.ccr"); let policy_path = out_dir.path().join("policy.toml"); std::fs::write(&policy_path, "sync_preference = \"rsync_only\"\n").expect("write policy"); let tal_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/tal/apnic-rfc7730-https.tal"); let ta_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer"); let argv = vec![ "rpki".to_string(), "--db".to_string(), db_dir.path().to_string_lossy().to_string(), "--policy".to_string(), policy_path.to_string_lossy().to_string(), "--tal-path".to_string(), tal_path.to_string_lossy().to_string(), "--ta-path".to_string(), ta_path.to_string_lossy().to_string(), "--rsync-local-dir".to_string(), repo_dir.path().to_string_lossy().to_string(), "--max-depth".to_string(), "0".to_string(), "--max-instances".to_string(), "1".to_string(), "--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 bytes = std::fs::read(&report_path).expect("read report json"); let v: serde_json::Value = serde_json::from_slice(&bytes).expect("parse report json"); assert_eq!(v["format_version"], 2); } #[test] fn cli_run_offline_mode_writes_decodable_ccr() { let db_dir = tempfile::tempdir().expect("db tempdir"); let repo_dir = tempfile::tempdir().expect("repo tempdir"); let out_dir = tempfile::tempdir().expect("out tempdir"); let ccr_path = out_dir.path().join("result.ccr"); let tal_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/tal/apnic-rfc7730-https.tal"); let ta_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer"); let argv = vec![ "rpki".to_string(), "--db".to_string(), db_dir.path().to_string_lossy().to_string(), "--tal-path".to_string(), tal_path.to_string_lossy().to_string(), "--ta-path".to_string(), ta_path.to_string_lossy().to_string(), "--rsync-local-dir".to_string(), repo_dir.path().to_string_lossy().to_string(), "--max-depth".to_string(), "0".to_string(), "--max-instances".to_string(), "1".to_string(), "--ccr-out".to_string(), ccr_path.to_string_lossy().to_string(), ]; rpki::cli::run(&argv).expect("cli run"); let bytes = std::fs::read(&ccr_path).expect("read ccr"); let ccr = rpki::ccr::decode_content_info(&bytes).expect("decode ccr"); assert!(ccr.content.tas.is_some()); } #[test] fn cli_run_offline_mode_writes_cir_and_static_pool() { let db_dir = tempfile::tempdir().expect("db tempdir"); let repo_dir = tempfile::tempdir().expect("repo tempdir"); let out_dir = tempfile::tempdir().expect("out tempdir"); let cir_path = out_dir.path().join("result.cir"); let static_root = out_dir.path().join("static"); let policy_path = out_dir.path().join("policy.toml"); std::fs::write(&policy_path, "sync_preference = \"rsync_only\"\n").expect("write policy"); let tal_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/tal/apnic-rfc7730-https.tal"); let ta_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer"); let argv = vec![ "rpki".to_string(), "--db".to_string(), db_dir.path().to_string_lossy().to_string(), "--policy".to_string(), policy_path.to_string_lossy().to_string(), "--tal-path".to_string(), tal_path.to_string_lossy().to_string(), "--ta-path".to_string(), ta_path.to_string_lossy().to_string(), "--rsync-local-dir".to_string(), repo_dir.path().to_string_lossy().to_string(), "--max-depth".to_string(), "0".to_string(), "--max-instances".to_string(), "1".to_string(), "--cir-enable".to_string(), "--cir-out".to_string(), cir_path.to_string_lossy().to_string(), "--cir-static-root".to_string(), static_root.to_string_lossy().to_string(), "--cir-tal-uri".to_string(), "https://example.test/root.tal".to_string(), ]; rpki::cli::run(&argv).expect("cli run"); let bytes = std::fs::read(&cir_path).expect("read cir"); let cir = rpki::cir::decode_cir(&bytes).expect("decode cir"); assert_eq!(cir.tals.len(), 1); assert_eq!(cir.tals[0].tal_uri, "https://example.test/root.tal"); assert!(cir .objects .iter() .any(|item| item.rsync_uri.contains("apnic-rpki-root-iana-origin.cer"))); let mut file_count = 0usize; let mut stack = vec![static_root.clone()]; while let Some(path) = stack.pop() { for entry in std::fs::read_dir(path).expect("read_dir") { let entry = entry.expect("entry"); let path = entry.path(); if path.is_dir() { stack.push(path); } else { file_count += 1; } } } assert!(file_count >= 1); } #[test] fn cli_run_blackbox_rsync_wrapper_mode_matches_reference_ccr_without_ta_path() { let real_rsync = std::path::Path::new("/usr/bin/rsync"); if !real_rsync.exists() { return; } let db_dir = tempfile::tempdir().expect("db tempdir"); let out_dir = tempfile::tempdir().expect("out tempdir"); let mirror_root = out_dir.path().join("mirror"); let ref_ccr_path = out_dir.path().join("reference.ccr"); let actual_ccr_path = out_dir.path().join("actual.ccr"); let tal_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/tal/apnic-rfc7730-https.tal"); let ta_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer"); let ta_bytes = std::fs::read(&ta_path).expect("read ta"); std::fs::create_dir_all(mirror_root.join("rpki.apnic.net").join("repository")) .expect("mkdir mirror"); std::fs::write( mirror_root .join("rpki.apnic.net") .join("repository") .join("apnic-rpki-root-iana-origin.cer"), ta_bytes, ) .expect("write ta into mirror"); let bin = env!("CARGO_BIN_EXE_rpki"); let wrapper = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("scripts/cir/cir-rsync-wrapper"); let reference = Command::new(bin) .env("REAL_RSYNC_BIN", real_rsync) .env("CIR_MIRROR_ROOT", &mirror_root) .args([ "--db", db_dir.path().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", "2026-04-07T00:00:00Z", "--max-depth", "0", "--max-instances", "1", "--ccr-out", ref_ccr_path.to_string_lossy().as_ref(), ]) .output() .expect("run reference wrapper mode"); assert!(reference.status.success(), "stderr={}", String::from_utf8_lossy(&reference.stderr)); let out = Command::new(bin) .env("REAL_RSYNC_BIN", real_rsync) .env("CIR_MIRROR_ROOT", &mirror_root) .args([ "--db", db_dir.path().join("actual-db").to_string_lossy().as_ref(), "--tal-path", tal_path.to_string_lossy().as_ref(), "--disable-rrdp", "--rsync-command", wrapper.to_string_lossy().as_ref(), "--validation-time", "2026-04-07T00:00:00Z", "--max-depth", "0", "--max-instances", "1", "--ccr-out", actual_ccr_path.to_string_lossy().as_ref(), ]) .output() .expect("run blackbox wrapper mode"); assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr)); let reference = rpki::ccr::decode_content_info(&std::fs::read(&ref_ccr_path).unwrap()) .expect("decode reference ccr"); let actual = rpki::ccr::decode_content_info(&std::fs::read(&actual_ccr_path).unwrap()) .expect("decode actual ccr"); assert_eq!(actual.content.version, reference.content.version); assert_eq!(actual.content.hash_alg, reference.content.hash_alg); assert_eq!(actual.content.mfts, reference.content.mfts); assert_eq!(actual.content.vrps, reference.content.vrps); assert_eq!(actual.content.vaps, reference.content.vaps); assert_eq!(actual.content.tas, reference.content.tas); assert_eq!(actual.content.rks, reference.content.rks); }