初始化 Panda RPKI v0.1.0 开源候选版本
This commit is contained in:
214
src/repository/sync/rrdp/tests_parts/edge_cases.rs
Normal file
214
src/repository/sync/rrdp/tests_parts/edge_cases.rs
Normal file
@@ -0,0 +1,214 @@
|
||||
// RRDP test group: edge cases.
|
||||
|
||||
#[test]
|
||||
fn load_rrdp_local_state_uses_source_record_only() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(tmp.path()).expect("open rocksdb");
|
||||
let notif_uri = "https://example.net/notification.xml";
|
||||
assert_eq!(
|
||||
load_rrdp_local_state(&store, notif_uri).expect("load empty"),
|
||||
None
|
||||
);
|
||||
|
||||
update_rrdp_source_record_on_success(
|
||||
&store,
|
||||
notif_uri,
|
||||
"source-session",
|
||||
9,
|
||||
crate::repository::storage::RrdpSourceSyncState::DeltaReady,
|
||||
Some("https://example.net/snapshot.xml"),
|
||||
Some(&hex::encode([0x11; 32])),
|
||||
)
|
||||
.expect("write source record");
|
||||
|
||||
let got = load_rrdp_local_state(&store, notif_uri)
|
||||
.expect("load source preferred")
|
||||
.expect("source present");
|
||||
assert_eq!(
|
||||
got,
|
||||
RrdpState {
|
||||
session_id: "source-session".to_string(),
|
||||
serial: 9,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_from_notification_falls_back_to_snapshot_if_missing_required_deltas() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(tmp.path()).expect("open rocksdb");
|
||||
|
||||
let sid = "550e8400-e29b-41d4-a716-446655440000";
|
||||
let notif_uri = "https://example.net/notification.xml";
|
||||
|
||||
// Seed state serial=1 with a only.
|
||||
let snapshot_uri_1 = "https://example.net/snapshot-1.xml";
|
||||
let snapshot_1 = snapshot_xml(sid, 1, &[("rsync://example.net/repo/a.mft", b"a1")]);
|
||||
let snapshot_hash_1 = hex::encode(sha2::Sha256::digest(&snapshot_1));
|
||||
let notif_1 = notification_xml(sid, 1, snapshot_uri_1, &snapshot_hash_1);
|
||||
let fetcher_1 = MapFetcher {
|
||||
map: HashMap::from([(snapshot_uri_1.to_string(), snapshot_1)]),
|
||||
};
|
||||
sync_from_notification_snapshot(&store, notif_uri, ¬if_1, &fetcher_1).expect("seed");
|
||||
|
||||
// Notification serial=3 only includes delta serial=3 (contiguous per RFC, but missing
|
||||
// serial=2 relative to our local state, so we must use snapshot).
|
||||
let snapshot_uri_3 = "https://example.net/snapshot-3.xml";
|
||||
let snapshot_3 = snapshot_xml(sid, 3, &[("rsync://example.net/repo/z.roa", b"z3")]);
|
||||
let snapshot_hash_3 = hex::encode(sha2::Sha256::digest(&snapshot_3));
|
||||
let delta_3_hash_hex = "11".repeat(32);
|
||||
let notif_3 = notification_xml_with_deltas(
|
||||
sid,
|
||||
3,
|
||||
snapshot_uri_3,
|
||||
&snapshot_hash_3,
|
||||
&[(
|
||||
"d3",
|
||||
3,
|
||||
"https://example.net/delta-3.xml",
|
||||
&delta_3_hash_hex,
|
||||
)],
|
||||
);
|
||||
|
||||
let fetcher = MapFetcher {
|
||||
map: HashMap::from([(snapshot_uri_3.to_string(), snapshot_3)]),
|
||||
};
|
||||
|
||||
let published = sync_from_notification(&store, notif_uri, ¬if_3, &fetcher).expect("sync");
|
||||
assert_eq!(published, 1);
|
||||
assert!(
|
||||
store
|
||||
.load_current_object_bytes_by_uri("rsync://example.net/repo/z.roa")
|
||||
.expect("get current")
|
||||
.is_some()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_from_notification_snapshot_rejects_snapshot_hash_mismatch() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(tmp.path()).expect("open rocksdb");
|
||||
|
||||
let sid = "550e8400-e29b-41d4-a716-446655440000";
|
||||
let serial = 1u64;
|
||||
let notif_uri = "https://example.net/notification.xml";
|
||||
let snapshot_uri = "https://example.net/snapshot.xml";
|
||||
|
||||
let snapshot = snapshot_xml(sid, serial, &[("rsync://example.net/repo/a.mft", b"x")]);
|
||||
let notif = notification_xml(sid, serial, snapshot_uri, &"00".repeat(32));
|
||||
|
||||
let fetcher = MapFetcher {
|
||||
map: HashMap::from([(snapshot_uri.to_string(), snapshot)]),
|
||||
};
|
||||
let err = sync_from_notification_snapshot(&store, notif_uri, ¬if, &fetcher).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
RrdpSyncError::Rrdp(RrdpError::SnapshotHashMismatch)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_snapshot_rejects_session_id_and_serial_mismatch() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(tmp.path()).expect("open rocksdb");
|
||||
let notif_uri = "https://example.net/notification.xml";
|
||||
|
||||
let expected_sid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
|
||||
let got_sid = "550e8400-e29b-41d4-a716-446655440001";
|
||||
|
||||
let snapshot = snapshot_xml(got_sid, 2, &[("rsync://example.net/repo/a.mft", b"x")]);
|
||||
let err = apply_snapshot(&store, notif_uri, None, &snapshot, expected_sid, 2).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
RrdpSyncError::Rrdp(RrdpError::SnapshotSessionIdMismatch { .. })
|
||||
));
|
||||
|
||||
let snapshot = snapshot_xml(
|
||||
expected_sid.to_string().as_str(),
|
||||
3,
|
||||
&[("rsync://example.net/repo/a.mft", b"x")],
|
||||
);
|
||||
let err = apply_snapshot(&store, notif_uri, None, &snapshot, expected_sid, 2).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
RrdpSyncError::Rrdp(RrdpError::SnapshotSerialMismatch { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_all_ascii_whitespace_removes_newlines_and_spaces() {
|
||||
assert_eq!(strip_all_ascii_whitespace(" a \n b\tc "), "abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_snapshot_reports_publish_errors() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(tmp.path()).expect("open rocksdb");
|
||||
let notif_uri = "https://example.net/notification.xml";
|
||||
let sid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
|
||||
|
||||
// Missing publish/@uri
|
||||
let xml = format!(
|
||||
r#"<snapshot xmlns="{RRDP_XMLNS}" version="1" session_id="{sid}" serial="1"><publish>AA==</publish></snapshot>"#
|
||||
)
|
||||
.into_bytes();
|
||||
let err = apply_snapshot(&store, notif_uri, None, &xml, sid, 1).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
RrdpSyncError::Rrdp(RrdpError::PublishUriMissing)
|
||||
));
|
||||
|
||||
// Missing base64 content (no text nodes).
|
||||
let xml = format!(
|
||||
r#"<snapshot xmlns="{RRDP_XMLNS}" version="1" session_id="{sid}" serial="1"><publish uri="rsync://example.net/repo/a.cer"></publish></snapshot>"#
|
||||
)
|
||||
.into_bytes();
|
||||
let err = apply_snapshot(&store, notif_uri, None, &xml, sid, 1).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
RrdpSyncError::Rrdp(RrdpError::PublishContentMissing)
|
||||
));
|
||||
|
||||
// Invalid base64 content.
|
||||
let xml = format!(
|
||||
r#"<snapshot xmlns="{RRDP_XMLNS}" version="1" session_id="{sid}" serial="1"><publish uri="rsync://example.net/repo/a.cer">!!!</publish></snapshot>"#
|
||||
)
|
||||
.into_bytes();
|
||||
let err = apply_snapshot(&store, notif_uri, None, &xml, sid, 1).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
RrdpSyncError::Rrdp(RrdpError::PublishBase64(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_snapshot_handles_multiple_publish_batches() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(tmp.path()).expect("open rocksdb");
|
||||
let notif_uri = "https://example.net/notification.xml";
|
||||
let sid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
|
||||
|
||||
let total = RRDP_SNAPSHOT_APPLY_BATCH_SIZE + 7;
|
||||
let mut xml =
|
||||
format!(r#"<snapshot xmlns="{RRDP_XMLNS}" version="1" session_id="{sid}" serial="1">"#);
|
||||
for i in 0..total {
|
||||
let uri = format!("rsync://example.net/repo/{i:04}.roa");
|
||||
let bytes = format!("payload-{i}").into_bytes();
|
||||
let b64 = base64::engine::general_purpose::STANDARD.encode(bytes);
|
||||
xml.push_str(&format!(r#"<publish uri="{uri}">{b64}</publish>"#));
|
||||
}
|
||||
xml.push_str("</snapshot>");
|
||||
|
||||
let published =
|
||||
apply_snapshot(&store, notif_uri, None, xml.as_bytes(), sid, 1).expect("apply snapshot");
|
||||
assert_eq!(published, total);
|
||||
|
||||
for idx in [0usize, RRDP_SNAPSHOT_APPLY_BATCH_SIZE - 1, total - 1] {
|
||||
let uri = format!("rsync://example.net/repo/{idx:04}.roa");
|
||||
let got = store
|
||||
.load_current_object_bytes_by_uri(&uri)
|
||||
.expect("load object")
|
||||
.expect("object exists");
|
||||
assert_eq!(got, format!("payload-{idx}").into_bytes());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user