use std::path::Path; use rpki::data_model::crl::RpkixCrl; use rpki::data_model::manifest::ManifestObject; use rpki::data_model::rc::ResourceCertificate; use rpki::fetch::rsync::LocalDirRsyncFetcher; use rpki::policy::{Policy, SyncPreference}; use rpki::storage::RocksStore; use rpki::sync::rrdp::Fetcher; use rpki::validation::run::{run_publication_point_once, verified_pack_exists}; fn fixture_to_rsync_uri(path: &Path) -> String { let rel = path .strip_prefix("tests/fixtures/repository") .expect("path under tests/fixtures/repository"); let mut it = rel.components(); let host = it .next() .expect("host component") .as_os_str() .to_string_lossy(); let rest = it.as_path().to_string_lossy(); format!("rsync://{host}/{rest}") } fn fixture_dir_to_rsync_uri(dir: &Path) -> String { let mut s = fixture_to_rsync_uri(dir); if !s.ends_with('/') { s.push('/'); } s } struct NeverHttpFetcher; impl Fetcher for NeverHttpFetcher { fn fetch(&self, _uri: &str) -> Result, String> { Err("http fetch disabled in offline test".to_string()) } } #[test] fn e2e_offline_uses_rsync_then_writes_verified_pack_then_outputs_vrps() { let fixture_dir = Path::new("tests/fixtures/repository/rpki.cernet.net/repo/cernet/0"); let rsync_base_uri = "rsync://rpki.cernet.net/repo/cernet/0/"; let manifest_path = fixture_dir.join("05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft"); let manifest_rsync_uri = fixture_to_rsync_uri(&manifest_path); let publication_point_rsync_uri = fixture_dir_to_rsync_uri(fixture_dir); let issuer_ca_der = std::fs::read("tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer") .expect("read issuer ca"); let issuer_ca = ResourceCertificate::decode_der(&issuer_ca_der).expect("decode issuer ca"); // Choose a validation_time that is safely inside: // - manifest thisUpdate..nextUpdate // - issuer CA validity // - CRL thisUpdate..nextUpdate let manifest_der = std::fs::read(&manifest_path).expect("read manifest"); let manifest = ManifestObject::decode_der(&manifest_der).expect("decode manifest"); let crl_der = std::fs::read(fixture_dir.join("05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.crl")) .expect("read crl"); let crl = RpkixCrl::decode_der(&crl_der).expect("decode crl"); let mut t = manifest.manifest.this_update; if issuer_ca.tbs.validity_not_before > t { t = issuer_ca.tbs.validity_not_before; } if crl.this_update.utc > t { t = crl.this_update.utc; } t += time::Duration::seconds(1); let mut policy = Policy::default(); policy.sync_preference = SyncPreference::RsyncOnly; let rsync_fetcher = LocalDirRsyncFetcher::new(fixture_dir); let http_fetcher = NeverHttpFetcher; let temp = tempfile::tempdir().expect("tempdir"); let store = RocksStore::open(temp.path()).expect("open rocksdb"); let expected_files = std::fs::read_dir(fixture_dir) .expect("read fixture dir") .filter_map(|e| e.ok()) .filter_map(|e| e.metadata().ok().map(|m| (e, m))) .filter(|(_e, m)| m.is_file()) .count(); assert!(expected_files >= 3, "fixture dir seems incomplete"); let out = run_publication_point_once( &store, &policy, None, rsync_base_uri, &manifest_rsync_uri, &publication_point_rsync_uri, &http_fetcher, &rsync_fetcher, &issuer_ca_der, None, issuer_ca.tbs.extensions.ip_resources.as_ref(), issuer_ca.tbs.extensions.as_resources.as_ref(), t, ) .expect("run publication point once"); assert!(verified_pack_exists(&store, &manifest_rsync_uri).expect("exists check")); assert_eq!(out.repo_sync.objects_written, expected_files); assert!( out.objects.vrps.iter().any(|v| v.asn == 4538), "expected VRPs for AS4538" ); }