20260410 完成五个rir 基于cir的三方回放,raw by hash 独立db,发现内存占用大,连续大rir 录制发生oom

This commit is contained in:
2026-04-11 11:24:32 +08:00
parent e083fe4daa
commit 77fc2f1a41
96 changed files with 5159 additions and 1720 deletions

View File

@@ -311,7 +311,9 @@ fn landing_packfile_cbor_put(store: &RocksStore, obj_type: ObjType, sample: &str
entry.origin_uris.push(key);
entry.object_type = Some("cbor".to_string());
entry.encoding = Some("cbor".to_string());
store.put_raw_by_hash_entry(&entry).expect("store raw_by_hash");
store
.put_raw_by_hash_entry(&entry)
.expect("store raw_by_hash");
}
#[derive(Clone, Debug, serde::Serialize)]
@@ -624,7 +626,9 @@ fn stage2_decode_validate_and_landing_benchmark_selected_der_v2() {
println!();
}
if mode.do_landing() {
println!("## landing (PackFile::from_bytes_compute_sha256 + CBOR + RocksDB current-state landing)");
println!(
"## landing (PackFile::from_bytes_compute_sha256 + CBOR + RocksDB current-state landing)"
);
println!();
println!("| type | sample | size_bytes | complexity | avg ns/op | ops/s |");
println!("|---|---|---:|---:|---:|---:|");

View File

@@ -30,6 +30,7 @@ fn live_http_fetcher() -> BlockingHttpFetcher {
timeout: Duration::from_secs(timeout_secs),
large_body_timeout: Duration::from_secs(timeout_secs),
user_agent: "rpki-dev/0.1 (stage2 live rrdp delta test)".to_string(),
..HttpFetcherConfig::default()
})
.expect("http fetcher")
}

View File

@@ -1,8 +1,8 @@
use rpki::ccr::{
AspaPayloadSet, AspaPayloadState, CcrContentInfo, CcrDigestAlgorithm, ManifestInstance,
ManifestState, RoaPayloadSet, RoaPayloadState, RouterKey, RouterKeySet, RouterKeyState,
RpkiCanonicalCacheRepresentation, TrustAnchorState, compute_state_hash,
decode_content_info, encode::{
RpkiCanonicalCacheRepresentation, TrustAnchorState, compute_state_hash, decode_content_info,
encode::{
encode_manifest_state_payload_der, encode_router_key_state_payload_der,
encode_trust_anchor_state_payload_der,
},
@@ -22,7 +22,8 @@ fn sample_time() -> time::OffsetDateTime {
#[test]
fn minimal_trust_anchor_ccr_roundtrips() {
let skis = vec![vec![0x11; 20], vec![0x22; 20]];
let skis_der = encode_trust_anchor_state_payload_der(&skis).expect("encode trust anchor payload");
let skis_der =
encode_trust_anchor_state_payload_der(&skis).expect("encode trust anchor payload");
let state = TrustAnchorState {
skis,
hash: compute_state_hash(&skis_der),
@@ -47,7 +48,8 @@ fn minimal_trust_anchor_ccr_roundtrips() {
#[test]
fn decode_rejects_wrong_content_type_oid() {
let skis = vec![vec![0x11; 20]];
let skis_der = encode_trust_anchor_state_payload_der(&skis).expect("encode trust anchor payload");
let skis_der =
encode_trust_anchor_state_payload_der(&skis).expect("encode trust anchor payload");
let content_info = CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
version: 0,
hash_alg: CcrDigestAlgorithm::Sha256,
@@ -69,7 +71,10 @@ fn decode_rejects_wrong_content_type_oid() {
.expect("oid present");
der[pos + needle.len() - 1] ^= 0x01;
let err = decode_content_info(&der).expect_err("wrong content type must fail");
assert!(err.to_string().contains("unexpected contentType OID"), "{err}");
assert!(
err.to_string().contains("unexpected contentType OID"),
"{err}"
);
}
#[test]
@@ -85,13 +90,17 @@ fn ccr_requires_at_least_one_state_aspect() {
rks: None,
});
let err = encode_content_info(&ccr).expect_err("empty state aspects must fail");
assert!(err.to_string().contains("at least one of mfts/vrps/vaps/tas/rks"));
assert!(
err.to_string()
.contains("at least one of mfts/vrps/vaps/tas/rks")
);
}
#[test]
fn state_hash_helpers_accept_matching_and_reject_tampered_payload() {
let skis = vec![vec![0x11; 20]];
let payload_der = encode_trust_anchor_state_payload_der(&skis).expect("encode trust anchor payload");
let payload_der =
encode_trust_anchor_state_payload_der(&skis).expect("encode trust anchor payload");
let hash = compute_state_hash(&payload_der);
assert!(verify_state_hash(&hash, &payload_der));
let mut tampered = payload_der.clone();
@@ -105,12 +114,15 @@ fn manifest_and_router_key_skeletons_encode_payloads_and_validate_sorting() {
hash: vec![0x33; 32],
size: 2048,
aki: vec![0x44; 20],
manifest_number: BigUnsigned { bytes_be: vec![0x01] },
manifest_number: BigUnsigned {
bytes_be: vec![0x01],
},
this_update: sample_time(),
locations: vec![vec![0x30, 0x00]],
subordinates: vec![vec![0x55; 20]],
}];
let mis_der = encode_manifest_state_payload_der(&manifest_instances).expect("encode manifest state payload");
let mis_der = encode_manifest_state_payload_der(&manifest_instances)
.expect("encode manifest state payload");
let manifest_state = ManifestState {
mis: manifest_instances,
most_recent_update: sample_time(),
@@ -239,10 +251,16 @@ fn decode_rejects_wrong_digest_algorithm_oid() {
});
let mut der = encode_content_info(&ccr).expect("encode ccr");
let oid = rpki::data_model::oid::OID_SHA256_RAW;
let pos = der.windows(oid.len()).position(|w| w == oid).expect("sha256 oid present");
let pos = der
.windows(oid.len())
.position(|w| w == oid)
.expect("sha256 oid present");
der[pos + oid.len() - 1] ^= 0x01;
let err = decode_content_info(&der).expect_err("decode must reject wrong digest oid");
assert!(err.to_string().contains("unexpected digest algorithm OID"), "{err}");
assert!(
err.to_string().contains("unexpected digest algorithm OID"),
"{err}"
);
}
#[test]
@@ -258,7 +276,10 @@ fn decode_rejects_bad_generalized_time() {
rks: None,
});
let mut der = encode_content_info(&ccr).expect("encode ccr");
let pos = der.windows(15).position(|w| w == b"20260324000000Z").expect("time present");
let pos = der
.windows(15)
.position(|w| w == b"20260324000000Z")
.expect("time present");
der[pos + 14] = b'X';
let err = decode_content_info(&der).expect_err("bad time must fail");
assert!(err.to_string().contains("GeneralizedTime"), "{err}");
@@ -268,7 +289,9 @@ fn decode_rejects_bad_generalized_time() {
fn manifest_state_validate_rejects_unsorted_subordinates() {
let mut state = sample_manifest_state();
state.mis[0].subordinates = vec![vec![0x40; 20], vec![0x30; 20]];
let err = state.validate().expect_err("unsorted subordinates must fail");
let err = state
.validate()
.expect_err("unsorted subordinates must fail");
assert!(err.to_string().contains("subordinates"), "{err}");
}
@@ -276,8 +299,14 @@ fn manifest_state_validate_rejects_unsorted_subordinates() {
fn roa_payload_state_validate_rejects_duplicate_asn_sets() {
let state = RoaPayloadState {
rps: vec![
RoaPayloadSet { as_id: 64496, ip_addr_blocks: vec![vec![0x30, 0x00]] },
RoaPayloadSet { as_id: 64496, ip_addr_blocks: vec![vec![0x30, 0x00]] },
RoaPayloadSet {
as_id: 64496,
ip_addr_blocks: vec![vec![0x30, 0x00]],
},
RoaPayloadSet {
as_id: 64496,
ip_addr_blocks: vec![vec![0x30, 0x00]],
},
],
hash: vec![0u8; 32],
};
@@ -288,7 +317,10 @@ fn roa_payload_state_validate_rejects_duplicate_asn_sets() {
#[test]
fn aspa_payload_state_validate_rejects_unsorted_providers() {
let state = AspaPayloadState {
aps: vec![AspaPayloadSet { customer_as_id: 64496, providers: vec![64498, 64497] }],
aps: vec![AspaPayloadSet {
customer_as_id: 64496,
providers: vec![64498, 64497],
}],
hash: vec![0u8; 32],
};
let err = state.validate().expect_err("unsorted providers must fail");
@@ -311,13 +343,21 @@ fn router_key_state_validate_rejects_unsorted_router_keys() {
rksets: vec![RouterKeySet {
as_id: 64496,
router_keys: vec![
RouterKey { ski: vec![0x42; 20], spki_der: vec![0x30, 0x00] },
RouterKey { ski: vec![0x41; 20], spki_der: vec![0x30, 0x00] },
RouterKey {
ski: vec![0x42; 20],
spki_der: vec![0x30, 0x00],
},
RouterKey {
ski: vec![0x41; 20],
spki_der: vec![0x30, 0x00],
},
],
}],
hash: vec![0u8; 32],
};
let err = state.validate().expect_err("unsorted router keys must fail");
let err = state
.validate()
.expect_err("unsorted router keys must fail");
assert!(err.to_string().contains("router_keys"), "{err}");
}
@@ -332,6 +372,8 @@ fn manifest_instance_validate_rejects_bad_location_tag() {
locations: vec![vec![0x04, 0x00]],
subordinates: vec![],
};
let err = instance.validate().expect_err("bad AccessDescription tag must fail");
let err = instance
.validate()
.expect_err("bad AccessDescription tag must fail");
assert!(err.to_string().contains("unexpected tag"), "{err}");
}

View File

@@ -1,19 +1,21 @@
use rpki::ccr::{
CcrContentInfo, CcrDigestAlgorithm, ManifestInstance, ManifestState, RoaPayloadSet,
RoaPayloadState, RouterKey, RouterKeySet, RouterKeyState, TrustAnchorState,
compute_state_hash, decode_content_info, dump_content_info_json_value,
RoaPayloadState, RouterKey, RouterKeySet, RouterKeyState, TrustAnchorState, compute_state_hash,
decode_content_info, dump_content_info_json_value,
encode::{
encode_aspa_payload_state_payload_der, encode_content_info,
encode_manifest_state_payload_der, encode_roa_payload_state_payload_der,
encode_router_key_state_payload_der, encode_trust_anchor_state_payload_der,
},
verify::{verify_against_report_json_path, verify_against_vcir_store, verify_content_info_bytes},
verify::{
verify_against_report_json_path, verify_against_vcir_store, verify_content_info_bytes,
},
};
use rpki::data_model::common::BigUnsigned;
use rpki::storage::{
PackTime, RocksStore, ValidatedCaInstanceResult, ValidatedManifestMeta,
VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary,
VcirChildEntry, VcirInstanceGate, VcirRelatedArtifact, VcirSummary,
PackTime, RocksStore, ValidatedCaInstanceResult, ValidatedManifestMeta, VcirArtifactKind,
VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary, VcirChildEntry,
VcirInstanceGate, VcirRelatedArtifact, VcirSummary,
};
fn sample_time() -> time::OffsetDateTime {
@@ -45,28 +47,51 @@ fn sample_manifest_state() -> ManifestState {
fn sample_roa_state() -> RoaPayloadState {
let rps = vec![RoaPayloadSet {
as_id: 64496,
ip_addr_blocks: vec![vec![0x30, 0x08, 0x04, 0x02, 0x00, 0x01, 0x30, 0x02, 0x03, 0x00]],
ip_addr_blocks: vec![vec![
0x30, 0x08, 0x04, 0x02, 0x00, 0x01, 0x30, 0x02, 0x03, 0x00,
]],
}];
let der = encode_roa_payload_state_payload_der(&rps).expect("encode rps");
RoaPayloadState { rps, hash: compute_state_hash(&der) }
RoaPayloadState {
rps,
hash: compute_state_hash(&der),
}
}
fn sample_aspa_state() -> rpki::ccr::AspaPayloadState {
let aps = vec![rpki::ccr::AspaPayloadSet { customer_as_id: 64496, providers: vec![64497] }];
let aps = vec![rpki::ccr::AspaPayloadSet {
customer_as_id: 64496,
providers: vec![64497],
}];
let der = encode_aspa_payload_state_payload_der(&aps).expect("encode aps");
rpki::ccr::AspaPayloadState { aps, hash: compute_state_hash(&der) }
rpki::ccr::AspaPayloadState {
aps,
hash: compute_state_hash(&der),
}
}
fn sample_ta_state() -> TrustAnchorState {
let skis = vec![vec![0x11; 20]];
let der = encode_trust_anchor_state_payload_der(&skis).expect("encode skis");
TrustAnchorState { skis, hash: compute_state_hash(&der) }
TrustAnchorState {
skis,
hash: compute_state_hash(&der),
}
}
fn sample_rks() -> RouterKeyState {
let rksets = vec![RouterKeySet { as_id: 64496, router_keys: vec![RouterKey { ski: vec![0x22; 20], spki_der: vec![0x30, 0x00] }] }];
let rksets = vec![RouterKeySet {
as_id: 64496,
router_keys: vec![RouterKey {
ski: vec![0x22; 20],
spki_der: vec![0x30, 0x00],
}],
}];
let der = encode_router_key_state_payload_der(&rksets).expect("encode rk");
RouterKeyState { rksets, hash: compute_state_hash(&der) }
RouterKeyState {
rksets,
hash: compute_state_hash(&der),
}
}
fn sample_ccr() -> Vec<u8> {
@@ -102,7 +127,10 @@ fn verify_content_info_bytes_rejects_tampered_manifest_hash() {
content_info.content.mfts.as_mut().unwrap().hash[0] ^= 0x01;
let der = encode_content_info(&content_info).expect("encode tampered ccr");
let err = verify_content_info_bytes(&der).expect_err("tampered hash must fail");
assert!(err.to_string().contains("ManifestState hash mismatch"), "{err}");
assert!(
err.to_string().contains("ManifestState hash mismatch"),
"{err}"
);
}
#[test]
@@ -128,8 +156,21 @@ fn verify_against_report_json_path_rejects_mismatching_report() {
let mut ci = decode_content_info(&sample_ccr()).expect("decode ccr");
ci.content.vrps = Some(RoaPayloadState {
rps: vec![RoaPayloadSet { as_id: 64496, ip_addr_blocks: vec![vec![0x30, 0x08, 0x04, 0x02, 0x00, 0x01, 0x30, 0x02, 0x03, 0x00]] }],
hash: compute_state_hash(&encode_roa_payload_state_payload_der(&[RoaPayloadSet { as_id: 64496, ip_addr_blocks: vec![vec![0x30, 0x08, 0x04, 0x02, 0x00, 0x01, 0x30, 0x02, 0x03, 0x00]] }]).unwrap()),
rps: vec![RoaPayloadSet {
as_id: 64496,
ip_addr_blocks: vec![vec![
0x30, 0x08, 0x04, 0x02, 0x00, 0x01, 0x30, 0x02, 0x03, 0x00,
]],
}],
hash: compute_state_hash(
&encode_roa_payload_state_payload_der(&[RoaPayloadSet {
as_id: 64496,
ip_addr_blocks: vec![vec![
0x30, 0x08, 0x04, 0x02, 0x00, 0x01, 0x30, 0x02, 0x03, 0x00,
]],
}])
.unwrap(),
),
});
verify_against_report_json_path(&ci, &report_path).expect_err("report mismatch expected");
}
@@ -184,7 +225,7 @@ fn verify_against_vcir_store_matches_manifest_hashes() {
summary: VcirSummary {
local_vrp_count: 0,
local_aspa_count: 0,
local_router_key_count: 0,
local_router_key_count: 0,
child_count: 1,
accepted_object_count: 1,
rejected_object_count: 0,

View File

@@ -1,4 +1,3 @@
use rpki::ccr::{
CcrContentInfo, CcrDigestAlgorithm, TrustAnchorState, compute_state_hash,
encode::{encode_content_info, encode_trust_anchor_state_payload_der},
@@ -20,7 +19,10 @@ fn sample_ccr_file() -> (tempfile::TempDir, std::path::PathBuf) {
mfts: None,
vrps: None,
vaps: None,
tas: Some(TrustAnchorState { skis, hash: compute_state_hash(&skis_der) }),
tas: Some(TrustAnchorState {
skis,
hash: compute_state_hash(&skis_der),
}),
rks: None,
});
let path = dir.path().join("sample.ccr");
@@ -36,7 +38,11 @@ fn ccr_dump_binary_prints_json_summary() {
.args(["--ccr", ccr_path.to_string_lossy().as_ref()])
.output()
.expect("run ccr_dump");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let json: serde_json::Value = serde_json::from_slice(&out.stdout).expect("parse json");
assert_eq!(json["version"], 0);
assert_eq!(json["state_aspects"]["tas"]["ski_count"], 1);
@@ -50,22 +56,25 @@ fn ccr_verify_binary_prints_summary() {
.args(["--ccr", ccr_path.to_string_lossy().as_ref()])
.output()
.expect("run ccr_verify");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let json: serde_json::Value = serde_json::from_slice(&out.stdout).expect("parse json");
assert_eq!(json["version"], 0);
assert_eq!(json["trust_anchor_ski_count"], 1);
assert_eq!(json["state_hashes_ok"], true);
}
#[test]
fn ccr_to_routinator_csv_binary_writes_vrp_csv() {
use rpki::ccr::{
CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation,
build_roa_payload_state, encode::encode_content_info,
};
use rpki::validation::objects::Vrp;
use rpki::data_model::roa::{IpPrefix, RoaAfi};
use rpki::validation::objects::Vrp;
let dir = tempfile::tempdir().expect("tempdir");
let ccr_path = dir.path().join("vrp.ccr");
let csv_path = dir.path().join("out.csv");
@@ -107,7 +116,11 @@ fn ccr_to_routinator_csv_binary_writes_vrp_csv() {
])
.output()
.expect("run ccr_to_routinator_csv");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let csv = std::fs::read_to_string(csv_path).expect("read csv");
assert!(csv.contains("ASN,IP Prefix,Max Length,Trust Anchor"));
assert!(csv.contains("AS64496,203.0.113.0/24,24,apnic"));
@@ -171,7 +184,11 @@ fn ccr_to_compare_views_binary_writes_vrp_and_vap_csvs() {
])
.output()
.expect("run ccr_to_compare_views");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let vrps_csv = std::fs::read_to_string(vrps_path).expect("read vrps csv");
let vaps_csv = std::fs::read_to_string(vaps_path).expect("read vaps csv");
@@ -234,7 +251,11 @@ fn ccr_to_compare_views_binary_writes_header_only_vap_csv_when_absent() {
])
.output()
.expect("run ccr_to_compare_views");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let vaps_csv = std::fs::read_to_string(vaps_path).expect("read vaps csv");
assert_eq!(vaps_csv, "Customer ASN,Providers,Trust Anchor\n");

View File

@@ -2,12 +2,18 @@ use std::collections::BTreeSet;
use std::path::PathBuf;
use std::process::Command;
use rpki::ccr::{encode_content_info, CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation, TrustAnchorState};
use rpki::cir::{encode_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
use rpki::ccr::{
CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation, TrustAnchorState,
encode_content_info,
};
use rpki::cir::{
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
};
#[test]
fn cir_full_and_delta_pair_reuses_shared_static_pool() {
let script = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_record_full_delta.sh");
let script =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_record_full_delta.sh");
let out_dir = tempfile::tempdir().expect("tempdir");
let out = out_dir.path().join("cir-pair");
let fixture_root = out_dir.path().join("fixture");
@@ -16,11 +22,7 @@ fn cir_full_and_delta_pair_reuses_shared_static_pool() {
std::fs::create_dir_all(&static_payload_root).unwrap();
let base_locks = fixture_root.join("base-locks.json");
let delta_locks = fixture_root.join("locks-delta.json");
std::fs::write(
&base_locks,
br#"{"validationTime":"2026-03-16T11:49:15Z"}"#,
)
.unwrap();
std::fs::write(&base_locks, br#"{"validationTime":"2026-03-16T11:49:15Z"}"#).unwrap();
std::fs::write(
&delta_locks,
br#"{"validationTime":"2026-03-16T11:50:15Z"}"#,
@@ -84,7 +86,10 @@ fn cir_full_and_delta_pair_reuses_shared_static_pool() {
mfts: None,
vrps: None,
vaps: None,
tas: Some(TrustAnchorState { skis: vec![vec![0x11; 20]], hash: vec![0x22; 32] }),
tas: Some(TrustAnchorState {
skis: vec![vec![0x11; 20]],
hash: vec![0x22; 32],
}),
rks: None,
});
let full_cir_path = fixture_root.join("full.cir");
@@ -97,8 +102,16 @@ fn cir_full_and_delta_pair_reuses_shared_static_pool() {
std::fs::write(&delta_cir_path, encode_cir(&delta_cir).unwrap()).unwrap();
std::fs::write(&full_ccr_path, encode_content_info(&empty_ccr).unwrap()).unwrap();
std::fs::write(&delta_ccr_path, encode_content_info(&empty_ccr).unwrap()).unwrap();
std::fs::write(&full_report_path, br#"{"format_version":2,"publication_points":[]}"#).unwrap();
std::fs::write(&delta_report_path, br#"{"format_version":2,"publication_points":[]}"#).unwrap();
std::fs::write(
&full_report_path,
br#"{"format_version":2,"publication_points":[]}"#,
)
.unwrap();
std::fs::write(
&delta_report_path,
br#"{"format_version":2,"publication_points":[]}"#,
)
.unwrap();
let stub = out_dir.path().join("stub-rpki.sh");
std::fs::write(
@@ -195,8 +208,9 @@ fi
String::from_utf8_lossy(&proc.stderr)
);
let full_cir = rpki::cir::decode_cir(&std::fs::read(out.join("full").join("input.cir")).unwrap())
.expect("decode full cir");
let full_cir =
rpki::cir::decode_cir(&std::fs::read(out.join("full").join("input.cir")).unwrap())
.expect("decode full cir");
let delta_cir =
rpki::cir::decode_cir(&std::fs::read(out.join("delta-001").join("input.cir")).unwrap())
.expect("decode delta cir");

View File

@@ -2,10 +2,12 @@ use std::path::PathBuf;
use std::process::Command;
use rpki::ccr::{
encode_content_info, CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation,
TrustAnchorState,
CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation, TrustAnchorState,
encode_content_info,
};
use rpki::cir::{
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
};
use rpki::cir::{encode_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
#[test]
fn cir_drop_report_counts_dropped_roa_objects_and_vrps() {
@@ -24,7 +26,10 @@ fn cir_drop_report_counts_dropped_roa_objects_and_vrps() {
use sha2::{Digest, Sha256};
hex::encode(Sha256::digest(&roa_bytes))
};
let dir = static_root.join("20260409").join(&hash[0..2]).join(&hash[2..4]);
let dir = static_root
.join("20260409")
.join(&hash[0..2])
.join(&hash[2..4]);
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join(&hash), &roa_bytes).unwrap();
@@ -100,12 +105,20 @@ fn cir_drop_report_counts_dropped_roa_objects_and_vrps() {
])
.output()
.expect("run cir_drop_report");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let output: serde_json::Value =
serde_json::from_slice(&std::fs::read(&json_out).unwrap()).unwrap();
assert_eq!(output["summary"]["droppedObjectCount"], 1);
assert!(output["summary"]["droppedVrpCount"].as_u64().unwrap_or(0) >= 1);
assert_eq!(output["summary"]["droppedByKind"]["roa"], 1);
assert!(std::fs::read_to_string(&md_out).unwrap().contains("Dropped By Reason"));
assert!(
std::fs::read_to_string(&md_out)
.unwrap()
.contains("Dropped By Reason")
);
}

View File

@@ -2,8 +2,8 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use rpki::cir::{
encode_cir, materialize_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
CIR_VERSION_V1,
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
materialize_cir,
};
fn apnic_tal_path() -> PathBuf {
@@ -59,7 +59,11 @@ fn write_static(root: &Path, date: &str, bytes: &[u8]) {
std::fs::write(dir.join(hash), bytes).expect("write static object");
}
fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror_root: &Path) -> PathBuf {
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");
@@ -91,7 +95,11 @@ fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror
])
.output()
.expect("run reference rpki");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
reference_ccr
}
@@ -116,7 +124,8 @@ fn cir_replay_matrix_script_matches_reference_for_all_participants() {
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_matrix.sh");
let script =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_replay_matrix.sh");
let out = Command::new(script)
.args([
"--cir",
@@ -134,13 +143,19 @@ fn cir_replay_matrix_script_matches_reference_for_all_participants() {
])
.output()
.expect("run cir matrix script");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
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("summary.json")).expect("read summary"))
.expect("parse summary");
assert_eq!(summary["allMatch"], true);
let participants = summary["participants"].as_array().expect("participants array");
let participants = summary["participants"]
.as_array()
.expect("participants array");
assert_eq!(participants.len(), 3);
for participant in participants {
assert_eq!(participant["exitCode"], 0);

View File

@@ -59,7 +59,11 @@ fn write_static(root: &Path, date: &str, bytes: &[u8]) {
std::fs::write(dir.join(hash), bytes).expect("write static object");
}
fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror_root: &Path) -> PathBuf {
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");
@@ -91,7 +95,11 @@ fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror
])
.output()
.expect("run reference rpki");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
reference_ccr
}
@@ -114,7 +122,8 @@ fn cir_routinator_script_matches_reference_on_ta_only_cir() {
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 script =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_replay_routinator.sh");
let out = Command::new(script)
.args([
"--cir",
@@ -128,7 +137,11 @@ fn cir_routinator_script_matches_reference_on_ta_only_cir() {
])
.output()
.expect("run routinator cir script");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
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"),
)
@@ -156,7 +169,8 @@ fn cir_rpki_client_script_matches_reference_on_ta_only_cir() {
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 script =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_replay_rpki_client.sh");
let out = Command::new(script)
.args([
"--cir",
@@ -172,7 +186,11 @@ fn cir_rpki_client_script_matches_reference_on_ta_only_cir() {
])
.output()
.expect("run rpki-client cir script");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
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"),
)

View File

@@ -1,27 +1,40 @@
use std::path::PathBuf;
use std::process::Command;
use rpki::ccr::{encode_content_info, CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation, TrustAnchorState};
use rpki::cir::{encode_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
use rpki::ccr::{
CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation, TrustAnchorState,
encode_content_info,
};
use rpki::cir::{
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
};
#[test]
fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
let out_dir = tempfile::tempdir().expect("tempdir");
let out = out_dir.path().join("cir-sequence");
let script =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_record_sequence_offline.sh");
let script = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("scripts/cir/run_cir_record_sequence_offline.sh");
let fixture_root = out_dir.path().join("fixture");
let static_payload_root = fixture_root.join("payloads");
std::fs::create_dir_all(&static_payload_root).unwrap();
let base_locks = fixture_root.join("base-locks.json");
let delta_locks = fixture_root.join("locks-delta.json");
std::fs::write(&base_locks, br#"{"validationTime":"2026-03-16T11:49:15Z"}"#).unwrap();
std::fs::write(&delta_locks, br#"{"validationTime":"2026-03-16T11:50:15Z"}"#).unwrap();
std::fs::write(
&delta_locks,
br#"{"validationTime":"2026-03-16T11:50:15Z"}"#,
)
.unwrap();
let mk_cir = |uri: &str, hash_hex: &str, vt: &str| CanonicalInputRepresentation {
version: CIR_VERSION_V1,
hash_alg: CirHashAlgorithm::Sha256,
validation_time: time::OffsetDateTime::parse(vt, &time::format_description::well_known::Rfc3339).unwrap(),
validation_time: time::OffsetDateTime::parse(
vt,
&time::format_description::well_known::Rfc3339,
)
.unwrap(),
objects: vec![CirObject {
rsync_uri: uri.to_string(),
sha256: hex::decode(hash_hex).unwrap(),
@@ -39,7 +52,11 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
use sha2::{Digest, Sha256};
hex::encode(Sha256::digest(b"delta-object"))
};
let full_cir = mk_cir("rsync://example.net/repo/full.roa", &full_hash, "2026-03-16T11:49:15Z");
let full_cir = mk_cir(
"rsync://example.net/repo/full.roa",
&full_hash,
"2026-03-16T11:49:15Z",
);
let delta_cir = CanonicalInputRepresentation {
version: CIR_VERSION_V1,
hash_alg: CirHashAlgorithm::Sha256,
@@ -68,7 +85,10 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
mfts: None,
vrps: None,
vaps: None,
tas: Some(TrustAnchorState { skis: vec![vec![0x11; 20]], hash: vec![0x22; 32] }),
tas: Some(TrustAnchorState {
skis: vec![vec![0x11; 20]],
hash: vec![0x22; 32],
}),
rks: None,
});
let full_cir_path = fixture_root.join("full.cir");
@@ -82,8 +102,16 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
std::fs::write(&delta_cir_path, encode_cir(&delta_cir).unwrap()).unwrap();
std::fs::write(&full_ccr_path, encode_content_info(&empty_ccr).unwrap()).unwrap();
std::fs::write(&delta_ccr_path, encode_content_info(&empty_ccr).unwrap()).unwrap();
std::fs::write(&full_report_path, br#"{"format_version":2,"publication_points":[]}"#).unwrap();
std::fs::write(&delta_report_path, br#"{"format_version":2,"publication_points":[]}"#).unwrap();
std::fs::write(
&full_report_path,
br#"{"format_version":2,"publication_points":[]}"#,
)
.unwrap();
std::fs::write(
&delta_report_path,
br#"{"format_version":2,"publication_points":[]}"#,
)
.unwrap();
std::fs::write(static_payload_root.join("full-object"), b"full-object").unwrap();
std::fs::write(static_payload_root.join("delta-object"), b"delta-object").unwrap();

View File

@@ -1,7 +1,10 @@
use std::path::{Path, PathBuf};
use std::process::Command;
use rpki::cir::{encode_cir, materialize_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
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")
@@ -56,7 +59,11 @@ fn write_static(root: &Path, date: &str, bytes: &[u8]) {
std::fs::write(dir.join(hash), bytes).expect("write static object");
}
fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror_root: &Path) -> PathBuf {
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");
@@ -77,7 +84,9 @@ fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror
"--rsync-command",
wrapper.to_string_lossy().as_ref(),
"--validation-time",
&cir.validation_time.format(&time::format_description::well_known::Rfc3339).unwrap(),
&cir.validation_time
.format(&time::format_description::well_known::Rfc3339)
.unwrap(),
"--max-depth",
"0",
"--max-instances",
@@ -87,7 +96,11 @@ fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror
])
.output()
.expect("run reference rpki");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
reference_ccr
}
@@ -102,14 +115,30 @@ fn prepare_sequence_root(td: &Path) -> PathBuf {
let (cir, ta_bytes) = build_ta_only_cir();
let cir_bytes = encode_cir(&cir).expect("encode cir");
std::fs::write(sequence_root.join("full").join("input.cir"), &cir_bytes).unwrap();
std::fs::write(sequence_root.join("delta-001").join("input.cir"), &cir_bytes).unwrap();
std::fs::write(sequence_root.join("delta-002").join("input.cir"), &cir_bytes).unwrap();
std::fs::write(
sequence_root.join("delta-001").join("input.cir"),
&cir_bytes,
)
.unwrap();
std::fs::write(
sequence_root.join("delta-002").join("input.cir"),
&cir_bytes,
)
.unwrap();
write_static(&static_root, "20260407", &ta_bytes);
materialize_cir(&cir, &static_root, &mirror_root, true).unwrap();
let reference = prepare_reference_ccr(td, &cir, &mirror_root);
std::fs::copy(&reference, sequence_root.join("full").join("result.ccr")).unwrap();
std::fs::copy(&reference, sequence_root.join("delta-001").join("result.ccr")).unwrap();
std::fs::copy(&reference, sequence_root.join("delta-002").join("result.ccr")).unwrap();
std::fs::copy(
&reference,
sequence_root.join("delta-001").join("result.ccr"),
)
.unwrap();
std::fs::copy(
&reference,
sequence_root.join("delta-002").join("result.ccr"),
)
.unwrap();
std::fs::write(sequence_root.join("full").join("report.json"), b"{}").unwrap();
std::fs::write(sequence_root.join("delta-001").join("report.json"), b"{}").unwrap();
std::fs::write(sequence_root.join("delta-002").join("report.json"), b"{}").unwrap();
@@ -122,7 +151,11 @@ fn prepare_sequence_root(td: &Path) -> PathBuf {
{"stepId":"delta-002","kind":"delta","validationTime":"2026-04-07T00:00:00Z","cirPath":"delta-002/input.cir","ccrPath":"delta-002/result.ccr","reportPath":"delta-002/report.json","previousStepId":"delta-001"}
]
});
std::fs::write(sequence_root.join("sequence.json"), serde_json::to_vec_pretty(&sequence).unwrap()).unwrap();
std::fs::write(
sequence_root.join("sequence.json"),
serde_json::to_vec_pretty(&sequence).unwrap(),
)
.unwrap();
sequence_root
}
@@ -141,13 +174,14 @@ fn peer_sequence_replay_scripts_replay_all_steps() {
let routinator_script = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("scripts/cir/run_cir_replay_sequence_routinator.sh");
let out = Command::new(routinator_script)
.args([
"--sequence-root",
sequence_root.to_string_lossy().as_ref(),
])
.args(["--sequence-root", sequence_root.to_string_lossy().as_ref()])
.output()
.expect("run routinator sequence replay");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let r_summary: serde_json::Value = serde_json::from_slice(
&std::fs::read(sequence_root.join("sequence-summary-routinator.json")).unwrap(),
)
@@ -166,7 +200,11 @@ fn peer_sequence_replay_scripts_replay_all_steps() {
])
.output()
.expect("run rpki-client sequence replay");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let c_summary: serde_json::Value = serde_json::from_slice(
&std::fs::read(sequence_root.join("sequence-summary-rpki-client.json")).unwrap(),
)

View File

@@ -1,7 +1,10 @@
use std::path::{Path, PathBuf};
use std::process::Command;
use rpki::cir::{encode_cir, materialize_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
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")
@@ -56,7 +59,11 @@ fn write_static(root: &Path, date: &str, bytes: &[u8]) {
std::fs::write(dir.join(hash), bytes).expect("write static object");
}
fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror_root: &Path) -> PathBuf {
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");
@@ -77,7 +84,9 @@ fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror
"--rsync-command",
wrapper.to_string_lossy().as_ref(),
"--validation-time",
&cir.validation_time.format(&time::format_description::well_known::Rfc3339).unwrap(),
&cir.validation_time
.format(&time::format_description::well_known::Rfc3339)
.unwrap(),
"--max-depth",
"0",
"--max-instances",
@@ -87,7 +96,11 @@ fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror
])
.output()
.expect("run reference rpki");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
reference_ccr
}
@@ -107,14 +120,30 @@ fn ours_sequence_replay_script_replays_all_steps() {
let (cir, ta_bytes) = build_ta_only_cir();
let cir_bytes = encode_cir(&cir).expect("encode cir");
std::fs::write(sequence_root.join("full").join("input.cir"), &cir_bytes).unwrap();
std::fs::write(sequence_root.join("delta-001").join("input.cir"), &cir_bytes).unwrap();
std::fs::write(sequence_root.join("delta-002").join("input.cir"), &cir_bytes).unwrap();
std::fs::write(
sequence_root.join("delta-001").join("input.cir"),
&cir_bytes,
)
.unwrap();
std::fs::write(
sequence_root.join("delta-002").join("input.cir"),
&cir_bytes,
)
.unwrap();
write_static(&static_root, "20260407", &ta_bytes);
materialize_cir(&cir, &static_root, &mirror_root, true).unwrap();
let reference = prepare_reference_ccr(td.path(), &cir, &mirror_root);
std::fs::copy(&reference, sequence_root.join("full").join("result.ccr")).unwrap();
std::fs::copy(&reference, sequence_root.join("delta-001").join("result.ccr")).unwrap();
std::fs::copy(&reference, sequence_root.join("delta-002").join("result.ccr")).unwrap();
std::fs::copy(
&reference,
sequence_root.join("delta-001").join("result.ccr"),
)
.unwrap();
std::fs::copy(
&reference,
sequence_root.join("delta-002").join("result.ccr"),
)
.unwrap();
std::fs::write(sequence_root.join("full").join("report.json"), b"{}").unwrap();
std::fs::write(sequence_root.join("delta-001").join("report.json"), b"{}").unwrap();
std::fs::write(sequence_root.join("delta-002").join("report.json"), b"{}").unwrap();
@@ -134,8 +163,8 @@ fn ours_sequence_replay_script_replays_all_steps() {
)
.unwrap();
let script =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_replay_sequence_ours.sh");
let script = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("scripts/cir/run_cir_replay_sequence_ours.sh");
let out = Command::new(script)
.args([
"--sequence-root",
@@ -145,11 +174,16 @@ fn ours_sequence_replay_script_replays_all_steps() {
])
.output()
.expect("run sequence replay");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let summary: serde_json::Value =
serde_json::from_slice(&std::fs::read(sequence_root.join("sequence-summary.json")).unwrap())
.unwrap();
let summary: serde_json::Value = serde_json::from_slice(
&std::fs::read(sequence_root.join("sequence-summary.json")).unwrap(),
)
.unwrap();
assert_eq!(summary["stepCount"], 3);
assert_eq!(summary["allMatch"], true);
}

View File

@@ -1,6 +1,6 @@
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use std::process::Command;
use std::os::unix::fs::MetadataExt;
fn wrapper_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/cir-rsync-wrapper")
@@ -24,7 +24,11 @@ fn cir_rsync_wrapper_passes_through_help() {
.arg("-h")
.output()
.expect("run wrapper -h");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stdout.contains("rsync") || stderr.contains("rsync"));
@@ -60,8 +64,15 @@ fn cir_rsync_wrapper_rewrites_rsync_source_to_mirror_tree() {
])
.output()
.expect("run wrapper rewrite");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert_eq!(std::fs::read(dest_root.join("a.roa")).expect("read copied roa"), b"roa");
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
assert_eq!(
std::fs::read(dest_root.join("a.roa")).expect("read copied roa"),
b"roa"
);
assert!(!dest_root.join("nested").join("b.txt").exists());
}
@@ -93,13 +104,23 @@ fn cir_rsync_wrapper_rewrites_module_root_without_trailing_slash_as_contents() {
])
.output()
.expect("run wrapper rewrite");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert_eq!(std::fs::read(dest_root.join("root.cer")).expect("read copied root cer"), b"cer");
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
assert_eq!(
std::fs::read(dest_root.join("root.cer")).expect("read copied root cer"),
b"cer"
);
assert_eq!(
std::fs::read(dest_root.join("sub").join("child.roa")).expect("read copied child roa"),
b"roa"
);
assert!(!dest_root.join("repo").exists(), "module root must not be nested under destination");
assert!(
!dest_root.join("repo").exists(),
"module root must not be nested under destination"
);
}
#[test]
@@ -113,7 +134,11 @@ fn cir_rsync_wrapper_requires_mirror_root_for_rsync_source() {
let out = Command::new(wrapper_path())
.env("REAL_RSYNC_BIN", real)
.args(["-rt", "rsync://example.net/repo/", dest_root.to_string_lossy().as_ref()])
.args([
"-rt",
"rsync://example.net/repo/",
dest_root.to_string_lossy().as_ref(),
])
.output()
.expect("run wrapper missing env");
assert!(!out.status.success());
@@ -141,8 +166,15 @@ fn cir_rsync_wrapper_leaves_local_source_untouched() {
])
.output()
.expect("run wrapper local passthrough");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert_eq!(std::fs::read(dest_root.join("src").join("x.cer")).expect("read copied file"), b"x");
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
assert_eq!(
std::fs::read(dest_root.join("src").join("x.cer")).expect("read copied file"),
b"x"
);
}
#[test]
@@ -177,7 +209,11 @@ fn cir_rsync_wrapper_local_link_mode_uses_hardlinks_for_rewritten_sources() {
])
.output()
.expect("run wrapper local-link mode");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let dst_file = dest_root.join("a.roa");
let dst_nested = dest_root.join("nested").join("b.cer");
@@ -186,5 +222,9 @@ fn cir_rsync_wrapper_local_link_mode_uses_hardlinks_for_rewritten_sources() {
let src_meta = std::fs::metadata(&src_file).expect("src metadata");
let dst_meta = std::fs::metadata(&dst_file).expect("dst metadata");
assert_eq!(src_meta.ino(), dst_meta.ino(), "expected hardlinked destination file");
assert_eq!(
src_meta.ino(),
dst_meta.ino(),
"expected hardlinked destination file"
);
}

View File

@@ -19,21 +19,15 @@ fn cli_payload_delta_replay_rejects_wrong_base_locks() {
let delta_archive = demo_root.join("payload-delta-archive");
let delta_locks = demo_root.join("locks-delta.json");
assert!(
base_archive.is_dir(),
"base archive missing: {}",
base_archive.display()
);
assert!(
delta_archive.is_dir(),
"delta archive missing: {}",
delta_archive.display()
);
assert!(
delta_locks.is_file(),
"delta locks missing: {}",
delta_locks.display()
);
if !base_archive.is_dir() || !delta_archive.is_dir() || !delta_locks.is_file() {
eprintln!(
"skipping cli delta replay smoke; missing fixtures: base_archive={} delta_archive={} delta_locks={}",
base_archive.display(),
delta_archive.display(),
delta_locks.display()
);
return;
}
let out = Command::new(bin)
.args([

View File

@@ -16,16 +16,14 @@ fn cli_payload_replay_root_only_smoke_writes_report_json() {
let locks_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("target/live/payload_replay/locks.json");
assert!(
archive_root.is_dir(),
"payload replay archive missing: {}",
archive_root.display()
);
assert!(
locks_path.is_file(),
"payload replay locks missing: {}",
locks_path.display()
);
if !archive_root.is_dir() || !locks_path.is_file() {
eprintln!(
"skipping cli payload replay smoke; missing fixtures: archive={} locks={}",
archive_root.display(),
locks_path.display()
);
return;
}
let out = Command::new(bin)
.args([

View File

@@ -45,7 +45,6 @@ fn cli_run_offline_mode_executes_and_writes_json_and_ccr() {
assert_eq!(v["format_version"], 2);
}
#[test]
fn cli_run_offline_mode_writes_decodable_ccr() {
let db_dir = tempfile::tempdir().expect("db tempdir");
@@ -130,10 +129,11 @@ fn cli_run_offline_mode_writes_cir_and_static_pool() {
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")));
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()];
@@ -182,14 +182,18 @@ fn cli_run_blackbox_rsync_wrapper_mode_matches_reference_ccr_without_ta_path() {
.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 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(),
db_dir
.path()
.join("reference-db")
.to_string_lossy()
.as_ref(),
"--tal-path",
tal_path.to_string_lossy().as_ref(),
"--ta-path",
@@ -208,7 +212,11 @@ fn cli_run_blackbox_rsync_wrapper_mode_matches_reference_ccr_without_ta_path() {
])
.output()
.expect("run reference wrapper mode");
assert!(reference.status.success(), "stderr={}", String::from_utf8_lossy(&reference.stderr));
assert!(
reference.status.success(),
"stderr={}",
String::from_utf8_lossy(&reference.stderr)
);
let out = Command::new(bin)
.env("REAL_RSYNC_BIN", real_rsync)
@@ -232,7 +240,11 @@ fn cli_run_blackbox_rsync_wrapper_mode_matches_reference_ccr_without_ta_path() {
])
.output()
.expect("run blackbox wrapper mode");
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
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");

View File

@@ -6,9 +6,9 @@ use rpki::data_model::manifest::ManifestObject;
use rpki::policy::{CaFailedFetchPolicy, Policy};
use rpki::storage::{
PackTime, RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore,
ValidatedCaInstanceResult, ValidatedManifestMeta,
VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary,
VcirInstanceGate, VcirRelatedArtifact, VcirSummary,
ValidatedCaInstanceResult, ValidatedManifestMeta, VcirArtifactKind, VcirArtifactRole,
VcirArtifactValidationStatus, VcirAuditSummary, VcirInstanceGate, VcirRelatedArtifact,
VcirSummary,
};
use rpki::validation::manifest::{PublicationPointSource, process_manifest_publication_point};
@@ -99,7 +99,7 @@ fn store_validated_manifest_baseline(
summary: VcirSummary {
local_vrp_count: 0,
local_aspa_count: 0,
local_router_key_count: 0,
local_router_key_count: 0,
child_count: 0,
accepted_object_count: 1,
rejected_object_count: 0,
@@ -165,7 +165,12 @@ fn manifest_success_returns_validated_publication_point_data() {
let bytes = std::fs::read(&file_path)
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
put_current_object(
&store,
&rsync_uri,
bytes,
rsync_uri.rsplit('.').next().unwrap_or("bin"),
);
}
let policy = Policy::default();
@@ -218,7 +223,12 @@ fn manifest_hash_mismatch_reuses_current_instance_vcir_when_enabled() {
let bytes = std::fs::read(&file_path)
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
put_current_object(
&store,
&rsync_uri,
bytes,
rsync_uri.rsplit('.').next().unwrap_or("bin"),
);
}
let policy = Policy::default();
@@ -255,7 +265,12 @@ fn manifest_hash_mismatch_reuses_current_instance_vcir_when_enabled() {
.expect("load victim raw")
.expect("victim raw exists");
tampered[0] ^= 0xFF;
put_current_object(&store, &victim_uri, tampered, victim_uri.rsplit('.').next().unwrap_or("bin"));
put_current_object(
&store,
&victim_uri,
tampered,
victim_uri.rsplit('.').next().unwrap_or("bin"),
);
let second = process_manifest_publication_point(
&store,
@@ -304,7 +319,12 @@ fn manifest_failed_fetch_stop_all_output() {
let bytes = std::fs::read(&file_path)
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
put_current_object(
&store,
&rsync_uri,
bytes,
rsync_uri.rsplit('.').next().unwrap_or("bin"),
);
}
let mut policy = Policy::default();
@@ -332,7 +352,12 @@ fn manifest_failed_fetch_stop_all_output() {
.expect("load victim raw")
.expect("victim raw exists");
tampered[0] ^= 0xFF;
put_current_object(&store, &victim_uri, tampered, victim_uri.rsplit('.').next().unwrap_or("bin"));
put_current_object(
&store,
&victim_uri,
tampered,
victim_uri.rsplit('.').next().unwrap_or("bin"),
);
policy.ca_failed_fetch_policy = CaFailedFetchPolicy::StopAllOutput;
let err = process_manifest_publication_point(
@@ -378,7 +403,12 @@ fn manifest_failed_fetch_rejects_stale_current_instance_vcir() {
let bytes = std::fs::read(&file_path)
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
put_current_object(
&store,
&rsync_uri,
bytes,
rsync_uri.rsplit('.').next().unwrap_or("bin"),
);
}
let policy = Policy::default();
@@ -459,7 +489,12 @@ fn manifest_revalidation_with_unchanged_manifest_is_fresh() {
let bytes = std::fs::read(&file_path)
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
put_current_object(
&store,
&rsync_uri,
bytes,
rsync_uri.rsplit('.').next().unwrap_or("bin"),
);
}
let policy = Policy::default();
@@ -539,7 +574,12 @@ fn manifest_rollback_is_treated_as_failed_fetch_and_reuses_current_instance_vcir
let bytes = std::fs::read(&file_path)
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
put_current_object(
&store,
&rsync_uri,
bytes,
rsync_uri.rsplit('.').next().unwrap_or("bin"),
);
}
let policy = Policy::default();

View File

@@ -6,9 +6,9 @@ use rpki::data_model::manifest::ManifestObject;
use rpki::policy::{CaFailedFetchPolicy, Policy};
use rpki::storage::{
PackTime, RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore,
ValidatedCaInstanceResult, ValidatedManifestMeta,
VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary,
VcirInstanceGate, VcirRelatedArtifact, VcirSummary,
ValidatedCaInstanceResult, ValidatedManifestMeta, VcirArtifactKind, VcirArtifactRole,
VcirArtifactValidationStatus, VcirAuditSummary, VcirInstanceGate, VcirRelatedArtifact,
VcirSummary,
};
use rpki::validation::manifest::{
ManifestProcessError, PublicationPointSource, process_manifest_publication_point,
@@ -102,7 +102,7 @@ fn store_validated_manifest_baseline(
summary: VcirSummary {
local_vrp_count: 0,
local_aspa_count: 0,
local_router_key_count: 0,
local_router_key_count: 0,
child_count: 0,
accepted_object_count: 1,
rejected_object_count: 0,
@@ -161,7 +161,12 @@ fn store_manifest_and_locked_files(
let bytes = std::fs::read(&file_path)
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
put_current_object(
&store,
&rsync_uri,
bytes,
rsync_uri.rsplit('.').next().unwrap_or("bin"),
);
}
}

View File

@@ -19,7 +19,11 @@ fn wrapper_script() -> std::path::PathBuf {
#[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());
assert!(
bundle_root.is_dir(),
"bundle root missing: {}",
bundle_root.display()
);
let expected = [
("afrinic", "afrinic", "afrinic.tal", "afrinic-ta.cer"),
@@ -53,22 +57,61 @@ fn multi_rir_case_info_resolves_all_five_rirs_and_timings() {
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["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);
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
);
}
}
@@ -113,8 +156,12 @@ fn multi_rir_case_info_prefers_lock_validation_time_over_replay_started_at() {
"verification.json",
"README.md",
] {
fs::write(rir_root.join(rel), "placeholder
").expect("write required file");
fs::write(
rir_root.join(rel),
"placeholder
",
)
.expect("write required file");
}
let repo_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
@@ -163,7 +210,11 @@ stderr={}",
#[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());
assert!(
bundle_root.is_dir(),
"bundle root missing: {}",
bundle_root.display()
);
let out = Command::new(wrapper_script())
.env("BUNDLE_ROOT", &bundle_root)
@@ -183,8 +234,10 @@ fn multi_rir_wrapper_describe_mode_works_for_ripe() {
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"));
assert!(
json["verification_json"]
.as_str()
.unwrap_or("")
.ends_with("verification.json")
);
}

View File

@@ -403,7 +403,6 @@ fn process_snapshot_for_issuer_drop_publication_point_on_invalid_aspa_bytes() {
assert!(!out.warnings.is_empty());
}
#[test]
fn process_snapshot_for_issuer_populates_local_outputs_cache_from_real_cernet_fixture() {
let (dir, rsync_base_uri, manifest_file) = cernet_fixture();
@@ -432,16 +431,21 @@ fn process_snapshot_for_issuer_populates_local_outputs_cache_from_real_cernet_fi
None,
);
assert!(!out.local_outputs_cache.is_empty(), "expected cached VCIR local outputs");
assert!(
!out.local_outputs_cache.is_empty(),
"expected cached VCIR local outputs"
);
assert_eq!(out.local_outputs_cache.len(), out.vrps.len());
assert!(out
.local_outputs_cache
.iter()
.all(|entry| entry.output_type == VcirOutputType::Vrp));
assert!(out
.local_outputs_cache
.iter()
.all(|entry| entry.source_object_type == "roa"));
assert!(
out.local_outputs_cache
.iter()
.all(|entry| entry.output_type == VcirOutputType::Vrp)
);
assert!(
out.local_outputs_cache
.iter()
.all(|entry| entry.source_object_type == "roa")
);
}
// NOTE: DN-based issuer resolution and pack-local CA indexing have been removed for determinism.

View File

@@ -206,8 +206,14 @@ fn write_multi_rir_case_report_combines_compare_and_timing() {
let md = std::fs::read_to_string(&out_md).expect("read markdown");
assert!(md.contains("AFRINIC Replay Report"), "{md}");
assert!(md.contains("| snapshot | true | 12.000 | 6.000 | 2.000 | 0 | 0 |"), "{md}");
assert!(md.contains("| delta | true | 8.000 | 4.000 | 2.000 | 0 | 0 |"), "{md}");
assert!(
md.contains("| snapshot | true | 12.000 | 6.000 | 2.000 | 0 | 0 |"),
"{md}"
);
assert!(
md.contains("| delta | true | 8.000 | 4.000 | 2.000 | 0 | 0 |"),
"{md}"
);
}
#[test]
@@ -282,7 +288,10 @@ fn write_multi_rir_summary_aggregates_case_reports() {
let md = std::fs::read_to_string(&out_md).expect("read summary md");
assert!(md.contains("Multi-RIR Replay Summary"), "{md}");
assert!(md.contains("| afrinic | true | 10.000 | 5.000 | 2.000 | true | 12.000 | 6.000 | 3.000 |"), "{md}");
assert!(
md.contains("| afrinic | true | 10.000 | 5.000 | 2.000 | true | 12.000 | 6.000 | 3.000 |"),
"{md}"
);
}
#[test]

View File

@@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
use rpki::fetch::rsync::{LocalDirRsyncFetcher, RsyncFetchError, RsyncFetcher};
use rpki::policy::{Policy, SyncPreference};
use rpki::storage::RocksStore;
use rpki::sync::repo::{RepoSyncSource, sync_publication_point};
use rpki::sync::repo::{RepoSyncPhase, RepoSyncSource, sync_publication_point};
use rpki::sync::rrdp::Fetcher;
struct MapFetcher {
@@ -98,6 +98,7 @@ fn repo_sync_uses_rrdp_when_available() {
.expect("sync");
assert_eq!(out.source, RepoSyncSource::Rrdp);
assert_eq!(out.phase, RepoSyncPhase::RrdpOk);
assert_eq!(out.objects_written, 2);
assert_eq!(*calls.lock().unwrap(), 0);
@@ -145,6 +146,7 @@ fn repo_sync_skips_snapshot_when_state_unchanged() {
)
.expect("sync 1");
assert_eq!(out1.source, RepoSyncSource::Rrdp);
assert_eq!(out1.phase, RepoSyncPhase::RrdpOk);
assert_eq!(out1.objects_written, 2);
let out2 = sync_publication_point(
@@ -159,6 +161,7 @@ fn repo_sync_skips_snapshot_when_state_unchanged() {
)
.expect("sync 2");
assert_eq!(out2.source, RepoSyncSource::Rrdp);
assert_eq!(out2.phase, RepoSyncPhase::RrdpOk);
assert_eq!(
out2.objects_written, 0,
"expected to skip snapshot apply when state unchanged"
@@ -208,6 +211,7 @@ fn repo_sync_falls_back_to_rsync_on_rrdp_failure() {
.expect("fallback sync");
assert_eq!(out.source, RepoSyncSource::Rsync);
assert_eq!(out.phase, RepoSyncPhase::RrdpFailedRsyncOk);
assert_eq!(out.objects_written, 1);
assert_eq!(*calls.lock().unwrap(), 1);
assert!(!out.warnings.is_empty());
@@ -252,6 +256,7 @@ fn repo_sync_rsync_populates_current_repository_view() {
.expect("rsync-only sync");
assert_eq!(out.source, RepoSyncSource::Rsync);
assert_eq!(out.phase, RepoSyncPhase::RsyncOnlyOk);
assert_eq!(out.objects_written, 2);
assert_current_object(&store, "rsync://example.net/repo/a/one.cer", b"1");

View File

@@ -1,9 +1,9 @@
use std::process::Command;
use rpki::data_model::rc::{ResourceCertificate, ResourceCertKind};
use rpki::data_model::rc::{ResourceCertKind, ResourceCertificate};
use rpki::data_model::router_cert::{
BgpsecRouterCertificate, BgpsecRouterCertificateDecodeError,
BgpsecRouterCertificatePathError, BgpsecRouterCertificateProfileError,
BgpsecRouterCertificate, BgpsecRouterCertificateDecodeError, BgpsecRouterCertificatePathError,
BgpsecRouterCertificateProfileError,
};
fn openssl_available() -> bool {
@@ -104,7 +104,11 @@ sbgp-autonomousSysNum = critical, AS:64496
authorityKeyIdentifier = keyid:always
"#,
dir = dir.display(),
eku_line = if include_eku { "extendedKeyUsage = 1.3.6.1.5.5.7.3.30" } else { "" },
eku_line = if include_eku {
"extendedKeyUsage = 1.3.6.1.5.5.7.3.30"
} else {
""
},
extra_ext = extra_ext
);
std::fs::write(dir.join("openssl.cnf"), cnf.as_bytes()).expect("write cnf");
@@ -266,7 +270,16 @@ fn decode_bgpsec_router_certificate_fixture_smoke() {
fn router_certificate_profile_rejects_missing_eku() {
let g = generate_router_cert_with_variant("ec-p256", false, "");
let err = BgpsecRouterCertificate::decode_der(&g.router_der).unwrap_err();
assert!(matches!(err, BgpsecRouterCertificateDecodeError::Validate(BgpsecRouterCertificateProfileError::MissingBgpsecRouterEku | BgpsecRouterCertificateProfileError::MissingExtendedKeyUsage)), "{err}");
assert!(
matches!(
err,
BgpsecRouterCertificateDecodeError::Validate(
BgpsecRouterCertificateProfileError::MissingBgpsecRouterEku
| BgpsecRouterCertificateProfileError::MissingExtendedKeyUsage
)
),
"{err}"
);
}
#[test]
@@ -277,7 +290,15 @@ fn router_certificate_profile_rejects_sia_and_ip_resources_and_ranges() {
"subjectInfoAccess = caRepository;URI:rsync://example.test/repo/router/\n",
);
let err = BgpsecRouterCertificate::decode_der(&g.router_der).unwrap_err();
assert!(matches!(err, BgpsecRouterCertificateDecodeError::Validate(BgpsecRouterCertificateProfileError::SubjectInfoAccessPresent)), "{err}");
assert!(
matches!(
err,
BgpsecRouterCertificateDecodeError::Validate(
BgpsecRouterCertificateProfileError::SubjectInfoAccessPresent
)
),
"{err}"
);
let g = generate_router_cert_with_variant(
"ec-p256",
@@ -285,7 +306,15 @@ fn router_certificate_profile_rejects_sia_and_ip_resources_and_ranges() {
"sbgp-ipAddrBlock = critical, IPv4:10.0.0.0/8\n",
);
let err = BgpsecRouterCertificate::decode_der(&g.router_der).unwrap_err();
assert!(matches!(err, BgpsecRouterCertificateDecodeError::Validate(BgpsecRouterCertificateProfileError::IpResourcesPresent)), "{err}");
assert!(
matches!(
err,
BgpsecRouterCertificateDecodeError::Validate(
BgpsecRouterCertificateProfileError::IpResourcesPresent
)
),
"{err}"
);
let g = generate_router_cert_with_variant(
"ec-p256",
@@ -293,18 +322,43 @@ fn router_certificate_profile_rejects_sia_and_ip_resources_and_ranges() {
"sbgp-autonomousSysNum = critical, AS:64496-64500\n",
);
let err = BgpsecRouterCertificate::decode_der(&g.router_der).unwrap_err();
assert!(matches!(err, BgpsecRouterCertificateDecodeError::Validate(BgpsecRouterCertificateProfileError::AsResourcesRangeNotAllowed)), "{err}");
assert!(
matches!(
err,
BgpsecRouterCertificateDecodeError::Validate(
BgpsecRouterCertificateProfileError::AsResourcesRangeNotAllowed
)
),
"{err}"
);
}
#[test]
fn router_certificate_profile_rejects_wrong_spki_algorithm_or_curve() {
let g = generate_router_cert_with_variant("rsa", true, "");
let err = BgpsecRouterCertificate::decode_der(&g.router_der).unwrap_err();
assert!(matches!(err, BgpsecRouterCertificateDecodeError::Validate(BgpsecRouterCertificateProfileError::SpkiAlgorithmNotEcPublicKey)), "{err}");
assert!(
matches!(
err,
BgpsecRouterCertificateDecodeError::Validate(
BgpsecRouterCertificateProfileError::SpkiAlgorithmNotEcPublicKey
)
),
"{err}"
);
let g = generate_router_cert_with_variant("ec-p384", true, "");
let err = BgpsecRouterCertificate::decode_der(&g.router_der).unwrap_err();
assert!(matches!(err, BgpsecRouterCertificateDecodeError::Validate(BgpsecRouterCertificateProfileError::SpkiWrongCurve | BgpsecRouterCertificateProfileError::SpkiEcPointNotUncompressedP256)), "{err}");
assert!(
matches!(
err,
BgpsecRouterCertificateDecodeError::Validate(
BgpsecRouterCertificateProfileError::SpkiWrongCurve
| BgpsecRouterCertificateProfileError::SpkiEcPointNotUncompressedP256
)
),
"{err}"
);
}
#[test]
@@ -317,11 +371,15 @@ fn router_certificate_path_validation_accepts_valid_and_rejects_wrong_issuer() {
let g = generate_router_cert_with_variant("ec-p256", true, "");
let issuer = ResourceCertificate::decode_der(&g.issuer_ca_der).expect("decode issuer");
let wrong_issuer = ResourceCertificate::decode_der(&g.wrong_issuer_der).expect("decode wrong issuer");
let wrong_issuer =
ResourceCertificate::decode_der(&g.wrong_issuer_der).expect("decode wrong issuer");
let issuer_crl = RpkixCrl::decode_der(&g.issuer_crl_der).expect("decode crl");
let (rem, issuer_spki) = SubjectPublicKeyInfo::from_der(&issuer.tbs.subject_public_key_info).expect("issuer spki");
let (rem, issuer_spki) =
SubjectPublicKeyInfo::from_der(&issuer.tbs.subject_public_key_info).expect("issuer spki");
assert!(rem.is_empty());
let (rem, wrong_spki) = SubjectPublicKeyInfo::from_der(&wrong_issuer.tbs.subject_public_key_info).expect("wrong issuer spki");
let (rem, wrong_spki) =
SubjectPublicKeyInfo::from_der(&wrong_issuer.tbs.subject_public_key_info)
.expect("wrong issuer spki");
assert!(rem.is_empty());
let now = time::OffsetDateTime::now_utc();
@@ -334,7 +392,8 @@ fn router_certificate_path_validation_accepts_valid_and_rejects_wrong_issuer() {
Some("rsync://example.test/repo/issuer/issuer.cer"),
Some("rsync://example.test/repo/issuer/issuer.crl"),
now,
).expect("router path valid");
)
.expect("router path valid");
assert_eq!(cert.asns, vec![64496]);
let err = BgpsecRouterCertificate::validate_path_with_prevalidated_issuer(
@@ -346,8 +405,12 @@ fn router_certificate_path_validation_accepts_valid_and_rejects_wrong_issuer() {
None,
None,
now,
).unwrap_err();
assert!(matches!(err, BgpsecRouterCertificatePathError::CertPath(_)), "{err}");
)
.unwrap_err();
assert!(
matches!(err, BgpsecRouterCertificatePathError::CertPath(_)),
"{err}"
);
let rc = ResourceCertificate::decode_der(&g.router_der).expect("decode router rc");
let mut revoked = HashSet::new();
@@ -361,6 +424,10 @@ fn router_certificate_path_validation_accepts_valid_and_rejects_wrong_issuer() {
Some("rsync://example.test/repo/issuer/issuer.cer"),
Some("rsync://example.test/repo/issuer/issuer.crl"),
now,
).unwrap_err();
assert!(matches!(err, BgpsecRouterCertificatePathError::CertPath(_)), "{err}");
)
.unwrap_err();
assert!(
matches!(err, BgpsecRouterCertificatePathError::CertPath(_)),
"{err}"
);
}

View File

@@ -94,12 +94,7 @@ fn rsync_fallback_breakdown_luys_cloud() {
let sha256_hex = hex::encode(sha2::Sha256::digest(bytes));
let mut raw = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes.clone());
raw.origin_uris.push(uri.clone());
raw.object_type = Some(
uri.rsplit('.')
.next()
.unwrap_or("bin")
.to_ascii_lowercase(),
);
raw.object_type = Some(uri.rsplit('.').next().unwrap_or("bin").to_ascii_lowercase());
raw.encoding = Some("der".to_string());
store.put_raw_by_hash_entry(&raw).expect("put raw_by_hash");
store
@@ -107,12 +102,7 @@ fn rsync_fallback_breakdown_luys_cloud() {
rsync_uri: uri.clone(),
current_hash: Some(sha256_hex),
repository_source: Some(rsync_base_uri.clone()),
object_type: Some(
uri.rsplit('.')
.next()
.unwrap_or("bin")
.to_ascii_lowercase(),
),
object_type: Some(uri.rsplit('.').next().unwrap_or("bin").to_ascii_lowercase()),
state: RepositoryViewState::Present,
})
.expect("put repository view");

View File

@@ -17,7 +17,11 @@ fn signed_object_decode_accepts_real_arin_ber_indefinite_roa_from_multi_rir_bund
let xml_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
"../../rpki/target/live/20260316-112341-multi-final3/arin/base-payload-archive/v1/captures/arin-base-arin/rrdp/repos/8bd1405242614aed9f13321d266fe3193db0ca842e0cbffda2b3df05481c331b/4a394319-7460-4141-a416-1addb69284ff/snapshot-160090-880434ae2a6f0e5fff224391c65a22ab037e09ac1d3ebad0ceda18558b49b13e.xml",
);
assert!(xml_path.is_file(), "xml path missing: {}", xml_path.display());
assert!(
xml_path.is_file(),
"xml path missing: {}",
xml_path.display()
);
let xml = std::fs::read_to_string(&xml_path).expect("read snapshot xml");
let uri = "rsync://rpki.arin.net/repository/arin-rpki-ta/5e4a23ea-e80a-403e-b08c-2171da2157d3/2a246947-2d62-4a6c-ba05-87187f0099b2/9ed5ce80-224e-46ab-94f1-1afce8ccf13f/0b13beb5-6bbb-3994-a254-02c5b10175c5.roa";
let der = extract_publish_bytes(&xml, uri);
@@ -25,8 +29,12 @@ fn signed_object_decode_accepts_real_arin_ber_indefinite_roa_from_multi_rir_bund
assert_eq!(der.first().copied(), Some(0x30));
assert_eq!(der.get(1).copied(), Some(0x80));
let signed_object = RpkiSignedObject::decode_der(&der).expect("decode BER-indefinite CMS signed object");
assert_eq!(signed_object.signed_data.encap_content_info.econtent_type, "1.2.840.113549.1.9.16.1.24");
let signed_object =
RpkiSignedObject::decode_der(&der).expect("decode BER-indefinite CMS signed object");
assert_eq!(
signed_object.signed_data.encap_content_info.econtent_type,
"1.2.840.113549.1.9.16.1.24"
);
let roa = RoaObject::decode_der(&der).expect("decode ROA object from BER-indefinite CMS");
assert!(!roa.roa.ip_addr_blocks.is_empty());

View File

@@ -79,7 +79,9 @@ fn rrdp_source_roundtrip_by_notification_uri() {
last_snapshot_hash: None,
last_error: None,
};
store.put_rrdp_source_record(&record).expect("put rrdp_source");
store
.put_rrdp_source_record(&record)
.expect("put rrdp_source");
let got = store
.get_rrdp_source_record(notif)

View File

@@ -395,7 +395,9 @@ fn tree_aggregates_router_keys_from_publication_point_results() {
source_object_uri: "rsync://example.test/repo/router1.cer".to_string(),
source_object_hash: "11".repeat(32),
source_ee_cert_hash: "11".repeat(32),
item_effective_until: PackTime { rfc3339_utc: "2026-12-31T00:00:00Z".to_string() },
item_effective_until: PackTime {
rfc3339_utc: "2026-12-31T00:00:00Z".to_string(),
},
},
RouterKeyPayload {
as_id: 64497,
@@ -404,7 +406,9 @@ fn tree_aggregates_router_keys_from_publication_point_results() {
source_object_uri: "rsync://example.test/repo/router2.cer".to_string(),
source_object_hash: "22".repeat(32),
source_ee_cert_hash: "22".repeat(32),
item_effective_until: PackTime { rfc3339_utc: "2026-12-31T00:00:00Z".to_string() },
item_effective_until: PackTime {
rfc3339_utc: "2026-12-31T00:00:00Z".to_string(),
},
},
],
local_outputs_cache: Vec::new(),