优化rtr
This commit is contained in:
@@ -59,6 +59,18 @@ fn snapshot_hashes_and_sorted_view_to_string(snapshot: &Snapshot) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn serials_all(serial: u32) -> [u32; 3] {
|
||||
[serial; 3]
|
||||
}
|
||||
|
||||
fn snapshots_all(snapshot: Snapshot) -> [Snapshot; 3] {
|
||||
[snapshot.clone(), snapshot.clone(), snapshot]
|
||||
}
|
||||
|
||||
fn deltas_all(deltas: VecDeque<Arc<Delta>>) -> [VecDeque<Arc<Delta>>; 3] {
|
||||
[deltas.clone(), deltas.clone(), deltas]
|
||||
}
|
||||
|
||||
/// Snapshot ?hash ?
|
||||
/// payload snapshot_hash / origins_hash ?
|
||||
#[test]
|
||||
@@ -127,8 +139,8 @@ async fn init_keeps_cache_running_when_file_loader_returns_no_data() {
|
||||
.unwrap();
|
||||
|
||||
assert!(!cache.is_data_available());
|
||||
assert_eq!(cache.serial(), 0);
|
||||
assert!(cache.snapshot().payloads_for_rtr().is_empty());
|
||||
assert_eq!(cache.serial_for_version(2), 0);
|
||||
assert!(cache.snapshot_for_version(2).payloads_for_rtr().is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -162,37 +174,40 @@ async fn init_restores_wraparound_delta_window_from_store() {
|
||||
vec![],
|
||||
);
|
||||
|
||||
let snapshots = snapshots_all(snapshot.clone());
|
||||
let session_ids = session_ids.as_array();
|
||||
|
||||
store
|
||||
.save_cache_state(
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
u32::MAX,
|
||||
Some(&d_max),
|
||||
Some((u32::MAX, u32::MAX)),
|
||||
false,
|
||||
&serials_all(u32::MAX),
|
||||
&[None, None, Some(&d_max)],
|
||||
&[None, None, Some((u32::MAX, u32::MAX))],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state(
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
0,
|
||||
Some(&d_zero),
|
||||
Some((u32::MAX, 0)),
|
||||
false,
|
||||
&serials_all(0),
|
||||
&[None, None, Some(&d_zero)],
|
||||
&[None, None, Some((u32::MAX, 0))],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state(
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
1,
|
||||
Some(&d_one),
|
||||
Some((u32::MAX, 1)),
|
||||
false,
|
||||
&serials_all(1),
|
||||
&[None, None, Some(&d_one)],
|
||||
&[None, None, Some((u32::MAX, 1))],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -202,7 +217,7 @@ async fn init_restores_wraparound_delta_window_from_store() {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
match cache.get_deltas_since(u32::MAX.wrapping_sub(1)) {
|
||||
match cache.get_deltas_since_for_version(2, u32::MAX.wrapping_sub(1)) {
|
||||
SerialResult::Delta(delta) => {
|
||||
assert_eq!(delta.serial(), 1);
|
||||
assert_eq!(delta.announced().len(), 3);
|
||||
@@ -211,6 +226,60 @@ async fn init_restores_wraparound_delta_window_from_store() {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn init_restore_respects_max_delta_limit() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
let session_ids = SessionIds::from_array([42, 43, 44]).as_array();
|
||||
|
||||
let a = Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496));
|
||||
let b = Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497));
|
||||
let snapshot = Snapshot::from_payloads(vec![a.clone(), b.clone()]);
|
||||
let snapshots = snapshots_all(snapshot);
|
||||
|
||||
let d17 = Delta::new(17, vec![a], vec![]);
|
||||
let d18 = Delta::new(18, vec![b], vec![]);
|
||||
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials_all(17),
|
||||
&[None, None, Some(&d17)],
|
||||
&[None, None, Some((17, 17))],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials_all(18),
|
||||
&[None, None, Some(&d18)],
|
||||
&[None, None, Some((17, 18))],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let cache = rpki::rtr::cache::RtrCache::default()
|
||||
.init(&store, 1, false, Timing::new(600, 600, 7200), || {
|
||||
Ok(Vec::new())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
match cache.get_deltas_since_for_version(2, 16) {
|
||||
SerialResult::ResetRequired => {}
|
||||
_ => panic!("expected ResetRequired for serial older than retained max_delta window"),
|
||||
}
|
||||
|
||||
match cache.get_deltas_since_for_version(2, 17) {
|
||||
SerialResult::Delta(delta) => assert_eq!(delta.serial(), 18),
|
||||
_ => panic!("expected one retained delta from 17 -> 18"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_prunes_delta_window_when_cumulative_delta_size_reaches_snapshot_size() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
@@ -229,8 +298,8 @@ async fn update_prunes_delta_window_when_cumulative_delta_size_reaches_snapshot_
|
||||
let mut cache = RtrCacheBuilder::new()
|
||||
.availability(CacheAvailability::Ready)
|
||||
.session_ids(SessionIds::from_array([42, 43, 44]))
|
||||
.serial(1)
|
||||
.snapshot(initial_snapshot)
|
||||
.serials(serials_all(1))
|
||||
.snapshots(snapshots_all(initial_snapshot))
|
||||
.max_delta(16)
|
||||
.prune_delta_by_snapshot_size(true)
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
@@ -247,7 +316,7 @@ async fn update_prunes_delta_window_when_cumulative_delta_size_reaches_snapshot_
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
match cache.get_deltas_since(1) {
|
||||
match cache.get_deltas_since_for_version(2, 1) {
|
||||
SerialResult::ResetRequired => {}
|
||||
_ => panic!(
|
||||
"expected delta window to be pruned when cumulative delta size exceeds snapshot size"
|
||||
@@ -444,14 +513,14 @@ fn delta_new_sorts_announced_descending_and_withdrawn_ascending() {
|
||||
fn get_deltas_since_returns_up_to_date_when_client_serial_matches_current() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::default())
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(100);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
let input =
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100);
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial_for_version(2), 100);
|
||||
let output = serial_result_detail_to_string(&result);
|
||||
|
||||
test_report(
|
||||
@@ -488,16 +557,16 @@ fn get_deltas_since_returns_reset_required_when_client_serial_is_too_old() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(102)
|
||||
.serials(serials_all(102))
|
||||
.timing(Timing::default())
|
||||
.deltas(deltas.clone())
|
||||
.deltas_by_version(deltas_all(deltas.clone()))
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(99);
|
||||
let result = cache.get_deltas_since_for_version(2, 99);
|
||||
|
||||
let input = format!(
|
||||
"{}delta_window:\n{}",
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 99),
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial_for_version(2), 99),
|
||||
indent_block(&deltas_window_to_string(&deltas), 2),
|
||||
);
|
||||
let output = serial_result_detail_to_string(&result);
|
||||
@@ -552,17 +621,17 @@ fn get_deltas_since_returns_minimal_merged_delta() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(103)
|
||||
.serials(serials_all(103))
|
||||
.timing(Timing::default())
|
||||
.snapshot(final_snapshot)
|
||||
.deltas(deltas.clone())
|
||||
.snapshots(snapshots_all(final_snapshot))
|
||||
.deltas_by_version(deltas_all(deltas.clone()))
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(101);
|
||||
let result = cache.get_deltas_since_for_version(2, 101);
|
||||
|
||||
let input = format!(
|
||||
"{}delta_window:\n{}",
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 101),
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial_for_version(2), 101),
|
||||
indent_block(&deltas_window_to_string(&deltas), 2),
|
||||
);
|
||||
let output = serial_result_detail_to_string(&result);
|
||||
@@ -602,14 +671,14 @@ fn get_deltas_since_returns_minimal_merged_delta() {
|
||||
fn get_deltas_since_returns_reset_required_when_client_serial_is_in_future() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::default())
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(101);
|
||||
let result = cache.get_deltas_since_for_version(2, 101);
|
||||
|
||||
let input =
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 101);
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial_for_version(2), 101);
|
||||
let output = serial_result_detail_to_string(&result);
|
||||
|
||||
test_report(
|
||||
@@ -648,19 +717,19 @@ fn get_deltas_since_supports_incremental_updates_across_serial_wraparound() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(0)
|
||||
.serials(serials_all(0))
|
||||
.timing(Timing::default())
|
||||
.snapshot(final_snapshot)
|
||||
.deltas(deltas.clone())
|
||||
.snapshots(snapshots_all(final_snapshot))
|
||||
.deltas_by_version(deltas_all(deltas.clone()))
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(u32::MAX.wrapping_sub(1));
|
||||
let result = cache.get_deltas_since_for_version(2, u32::MAX.wrapping_sub(1));
|
||||
|
||||
let input = format!(
|
||||
"{}delta_window:\n{}",
|
||||
get_deltas_since_input_to_string(
|
||||
cache.session_id_for_version(1),
|
||||
cache.serial(),
|
||||
cache.serial_for_version(2),
|
||||
u32::MAX.wrapping_sub(1)
|
||||
),
|
||||
indent_block(&deltas_window_to_string(&deltas), 2),
|
||||
@@ -723,24 +792,24 @@ fn get_deltas_since_returns_reset_required_when_client_serial_is_too_old_across_
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(1)
|
||||
.serials(serials_all(1))
|
||||
.timing(Timing::default())
|
||||
.snapshot(Snapshot::from_payloads(vec![
|
||||
.snapshots(snapshots_all(Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497)),
|
||||
Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498)),
|
||||
]))
|
||||
.deltas(deltas.clone())
|
||||
])))
|
||||
.deltas_by_version(deltas_all(deltas.clone()))
|
||||
.build();
|
||||
|
||||
let client_serial = u32::MAX.wrapping_sub(2);
|
||||
let result = cache.get_deltas_since(client_serial);
|
||||
let result = cache.get_deltas_since_for_version(2, client_serial);
|
||||
|
||||
let input = format!(
|
||||
"{}delta_window:\n{}",
|
||||
get_deltas_since_input_to_string(
|
||||
cache.session_id_for_version(1),
|
||||
cache.serial(),
|
||||
cache.serial_for_version(2),
|
||||
client_serial
|
||||
),
|
||||
indent_block(&deltas_window_to_string(&deltas), 2),
|
||||
@@ -764,14 +833,14 @@ fn get_deltas_since_returns_reset_required_when_client_serial_is_too_old_across_
|
||||
fn get_deltas_since_returns_reset_required_when_client_serial_is_in_future_across_wraparound() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(u32::MAX)
|
||||
.serials(serials_all(u32::MAX))
|
||||
.timing(Timing::default())
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(0);
|
||||
let result = cache.get_deltas_since_for_version(2, 0);
|
||||
|
||||
let input =
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 0);
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial_for_version(2), 0);
|
||||
let output = serial_result_detail_to_string(&result);
|
||||
|
||||
test_report(
|
||||
@@ -802,9 +871,9 @@ async fn update_no_change_keeps_serial_and_produces_no_delta() {
|
||||
|
||||
let mut cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::default())
|
||||
.snapshot(snapshot.clone())
|
||||
.snapshots(snapshots_all(snapshot.clone()))
|
||||
.build();
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
@@ -814,8 +883,8 @@ async fn update_no_change_keeps_serial_and_produces_no_delta() {
|
||||
|
||||
cache.update(new_payloads.clone(), &store).unwrap();
|
||||
|
||||
let current_snapshot = cache.snapshot();
|
||||
let result = cache.get_deltas_since(100);
|
||||
let current_snapshot = cache.snapshot_for_version(2);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
let input = format!(
|
||||
"old_snapshot :\n{}new_payloads :\n{}",
|
||||
@@ -825,7 +894,7 @@ async fn update_no_change_keeps_serial_and_produces_no_delta() {
|
||||
|
||||
let output = format!(
|
||||
"cache.serial_after_update: {}\ncurrent_snapshot:\n{}get_deltas_since(100):\n{}",
|
||||
cache.serial(),
|
||||
cache.serial_for_version(2),
|
||||
indent_block(
|
||||
&snapshot_hashes_and_sorted_view_to_string(¤t_snapshot),
|
||||
2
|
||||
@@ -840,8 +909,8 @@ async fn update_no_change_keeps_serial_and_produces_no_delta() {
|
||||
&output,
|
||||
);
|
||||
|
||||
assert_eq!(cache.serial(), 100);
|
||||
assert!(cache.snapshot().same_content(&snapshot));
|
||||
assert_eq!(cache.serial_for_version(2), 100);
|
||||
assert!(cache.snapshot_for_version(2).same_content(&snapshot));
|
||||
|
||||
match result {
|
||||
SerialResult::UpToDate => {}
|
||||
@@ -861,9 +930,9 @@ async fn update_add_only_increments_serial_and_generates_announced_delta() {
|
||||
|
||||
let mut cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::default())
|
||||
.snapshot(old_snapshot.clone())
|
||||
.snapshots(snapshots_all(old_snapshot.clone()))
|
||||
.build();
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
@@ -876,8 +945,8 @@ async fn update_add_only_increments_serial_and_generates_announced_delta() {
|
||||
|
||||
cache.update(new_payloads.clone(), &store).unwrap();
|
||||
|
||||
let current_snapshot = cache.snapshot();
|
||||
let result = cache.get_deltas_since(100);
|
||||
let current_snapshot = cache.snapshot_for_version(2);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
let input = format!(
|
||||
"old_snapshot :\n{}new_payloads :\n{}",
|
||||
@@ -887,7 +956,7 @@ async fn update_add_only_increments_serial_and_generates_announced_delta() {
|
||||
|
||||
let output = format!(
|
||||
"cache.serial_after_update: {}\ncurrent_snapshot:\n{}get_deltas_since(100):\n{}",
|
||||
cache.serial(),
|
||||
cache.serial_for_version(2),
|
||||
indent_block(
|
||||
&snapshot_hashes_and_sorted_view_to_string(¤t_snapshot),
|
||||
2
|
||||
@@ -902,7 +971,7 @@ async fn update_add_only_increments_serial_and_generates_announced_delta() {
|
||||
&output,
|
||||
);
|
||||
|
||||
assert_eq!(cache.serial(), 101);
|
||||
assert_eq!(cache.serial_for_version(2), 101);
|
||||
|
||||
match result {
|
||||
SerialResult::Delta(delta) => {
|
||||
@@ -934,9 +1003,9 @@ async fn update_remove_only_increments_serial_and_generates_withdrawn_delta() {
|
||||
|
||||
let mut cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::default())
|
||||
.snapshot(old_snapshot.clone())
|
||||
.snapshots(snapshots_all(old_snapshot.clone()))
|
||||
.build();
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
@@ -946,8 +1015,8 @@ async fn update_remove_only_increments_serial_and_generates_withdrawn_delta() {
|
||||
|
||||
cache.update(new_payloads.clone(), &store).unwrap();
|
||||
|
||||
let current_snapshot = cache.snapshot();
|
||||
let result = cache.get_deltas_since(100);
|
||||
let current_snapshot = cache.snapshot_for_version(2);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
let input = format!(
|
||||
"old_snapshot :\n{}new_payloads :\n{}",
|
||||
@@ -957,7 +1026,7 @@ async fn update_remove_only_increments_serial_and_generates_withdrawn_delta() {
|
||||
|
||||
let output = format!(
|
||||
"cache.serial_after_update: {}\ncurrent_snapshot:\n{}get_deltas_since(100):\n{}",
|
||||
cache.serial(),
|
||||
cache.serial_for_version(2),
|
||||
indent_block(
|
||||
&snapshot_hashes_and_sorted_view_to_string(¤t_snapshot),
|
||||
2
|
||||
@@ -972,7 +1041,7 @@ async fn update_remove_only_increments_serial_and_generates_withdrawn_delta() {
|
||||
&output,
|
||||
);
|
||||
|
||||
assert_eq!(cache.serial(), 101);
|
||||
assert_eq!(cache.serial_for_version(2), 101);
|
||||
|
||||
match result {
|
||||
SerialResult::Delta(delta) => {
|
||||
@@ -1010,9 +1079,9 @@ async fn update_add_and_remove_increments_serial_and_generates_both_sides() {
|
||||
|
||||
let mut cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::default())
|
||||
.snapshot(old_snapshot.clone())
|
||||
.snapshots(snapshots_all(old_snapshot.clone()))
|
||||
.build();
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
@@ -1025,8 +1094,8 @@ async fn update_add_and_remove_increments_serial_and_generates_both_sides() {
|
||||
|
||||
cache.update(new_payloads.clone(), &store).unwrap();
|
||||
|
||||
let current_snapshot = cache.snapshot();
|
||||
let result = cache.get_deltas_since(100);
|
||||
let current_snapshot = cache.snapshot_for_version(2);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
let input = format!(
|
||||
"old_snapshot :\n{}new_payloads :\n{}",
|
||||
@@ -1036,7 +1105,7 @@ async fn update_add_and_remove_increments_serial_and_generates_both_sides() {
|
||||
|
||||
let output = format!(
|
||||
"cache.serial_after_update: {}\ncurrent_snapshot:\n{}get_deltas_since(100):\n{}",
|
||||
cache.serial(),
|
||||
cache.serial_for_version(2),
|
||||
indent_block(
|
||||
&snapshot_hashes_and_sorted_view_to_string(¤t_snapshot),
|
||||
2
|
||||
@@ -1051,7 +1120,7 @@ async fn update_add_and_remove_increments_serial_and_generates_both_sides() {
|
||||
&output,
|
||||
);
|
||||
|
||||
assert_eq!(cache.serial(), 101);
|
||||
assert_eq!(cache.serial_for_version(2), 101);
|
||||
|
||||
match result {
|
||||
SerialResult::Delta(delta) => {
|
||||
@@ -1099,17 +1168,17 @@ fn get_deltas_since_cancels_announce_then_withdraw_for_same_prefix() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(102)
|
||||
.serials(serials_all(102))
|
||||
.timing(Timing::default())
|
||||
.snapshot(final_snapshot)
|
||||
.deltas(deltas.clone())
|
||||
.snapshots(snapshots_all(final_snapshot))
|
||||
.deltas_by_version(deltas_all(deltas.clone()))
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(100);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
let input = format!(
|
||||
"{}delta_window:\n{}",
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100),
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial_for_version(2), 100),
|
||||
indent_block(&deltas_window_to_string(&deltas), 2),
|
||||
);
|
||||
let output = serial_result_detail_to_string(&result);
|
||||
@@ -1153,17 +1222,17 @@ fn get_deltas_since_cancels_withdraw_then_announce_for_same_prefix() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(102)
|
||||
.serials(serials_all(102))
|
||||
.timing(Timing::default())
|
||||
.snapshot(final_snapshot)
|
||||
.deltas(deltas.clone())
|
||||
.snapshots(snapshots_all(final_snapshot))
|
||||
.deltas_by_version(deltas_all(deltas.clone()))
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(100);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
let input = format!(
|
||||
"{}delta_window:\n{}",
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100),
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial_for_version(2), 100),
|
||||
indent_block(&deltas_window_to_string(&deltas), 2),
|
||||
);
|
||||
let output = serial_result_detail_to_string(&result);
|
||||
@@ -1207,17 +1276,17 @@ fn get_deltas_since_merges_replacement_into_withdraw_and_announce() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(102)
|
||||
.serials(serials_all(102))
|
||||
.timing(Timing::default())
|
||||
.snapshot(final_snapshot)
|
||||
.deltas(deltas.clone())
|
||||
.snapshots(snapshots_all(final_snapshot))
|
||||
.deltas_by_version(deltas_all(deltas.clone()))
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(100);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
let input = format!(
|
||||
"{}delta_window:\n{}",
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100),
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial_for_version(2), 100),
|
||||
indent_block(&deltas_window_to_string(&deltas), 2),
|
||||
);
|
||||
let output = serial_result_detail_to_string(&result);
|
||||
@@ -1285,18 +1354,18 @@ fn get_deltas_since_merges_multiple_deltas_to_final_minimal_view() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(103)
|
||||
.serials(serials_all(103))
|
||||
.timing(Timing::default())
|
||||
.snapshot(final_snapshot)
|
||||
.deltas(deltas.clone())
|
||||
.snapshots(snapshots_all(final_snapshot))
|
||||
.deltas_by_version(deltas_all(deltas.clone()))
|
||||
.build();
|
||||
|
||||
// ?serial=100 A/B ?+C
|
||||
let result = cache.get_deltas_since(100);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
let input = format!(
|
||||
"{}delta_window:\n{}",
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100),
|
||||
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial_for_version(2), 100),
|
||||
indent_block(&deltas_window_to_string(&deltas), 2),
|
||||
);
|
||||
let output = serial_result_detail_to_string(&result);
|
||||
@@ -1381,13 +1450,13 @@ fn get_deltas_since_merges_aspa_replacement_into_single_announcement() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(102)
|
||||
.serials(serials_all(102))
|
||||
.timing(Timing::default())
|
||||
.snapshot(Snapshot::from_payloads(vec![Payload::Aspa(new.clone())]))
|
||||
.deltas(deltas)
|
||||
.snapshots(snapshots_all(Snapshot::from_payloads(vec![Payload::Aspa(new.clone())])))
|
||||
.deltas_by_version(deltas_all(deltas))
|
||||
.build();
|
||||
|
||||
let result = cache.get_deltas_since(100);
|
||||
let result = cache.get_deltas_since_for_version(2, 100);
|
||||
|
||||
match result {
|
||||
SerialResult::Delta(delta) => {
|
||||
|
||||
@@ -13,7 +13,10 @@ fn fixture_path(name: &str) -> PathBuf {
|
||||
#[test]
|
||||
#[ignore = "manual CCR loader smoke test for local samples"]
|
||||
fn ccr_loader_smoke() {
|
||||
let snapshot = load_ccr_snapshot_from_file(fixture_path("20260324T000037Z-sng1.ccr"))
|
||||
// let path = "./mini_data/20260403T000001Z-mini-a.ccr";
|
||||
// let path = "20260403T000101Z-mini-b.ccr";
|
||||
let path = "20260403T000201Z-mini-c.ccr";
|
||||
let snapshot = load_ccr_snapshot_from_file(fixture_path(path))
|
||||
.expect("load CCR snapshot");
|
||||
|
||||
println!("content_type_oid: {}", snapshot.content_type_oid);
|
||||
@@ -22,6 +25,8 @@ fn ccr_loader_smoke() {
|
||||
println!("vap_count : {}", snapshot.vaps.len());
|
||||
println!("first_vrp : {:?}", snapshot.vrps.first());
|
||||
println!("first_vap : {:?}", snapshot.vaps.first());
|
||||
println!("vrps : {:?}", snapshot.vrps);
|
||||
println!("vaps : {:?}", snapshot.vaps);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -65,3 +70,19 @@ fn snapshot_to_payloads_with_options_skips_invalid_aspa_when_not_strict() {
|
||||
assert_eq!(conversion.invalid_vaps.len(), 1);
|
||||
assert!(conversion.invalid_vaps[0].contains("provider list must not contain AS0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_mini_ccr_files_are_parseable() {
|
||||
let cases = [
|
||||
("20260403T000001Z-mini-a.ccr", 2usize, 1usize),
|
||||
("20260403T000101Z-mini-b.ccr", 3usize, 1usize),
|
||||
("20260403T000201Z-mini-c.ccr", 2usize, 2usize),
|
||||
];
|
||||
|
||||
for (name, expect_vrps, expect_vaps) in cases {
|
||||
let snapshot = load_ccr_snapshot_from_file(fixture_path(name))
|
||||
.unwrap_or_else(|e| panic!("failed to parse {}: {:?}", name, e));
|
||||
assert_eq!(snapshot.vrps.len(), expect_vrps, "vrp count mismatch for {name}");
|
||||
assert_eq!(snapshot.vaps.len(), expect_vaps, "vap count mismatch for {name}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mod common;
|
||||
mod common;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
@@ -31,6 +31,7 @@ use rpki::rtr::pdu::{
|
||||
Aspa as AspaPdu, CacheReset, CacheResponse, EndOfDataV1, ErrorReport, Header, IPv4Prefix,
|
||||
IPv6Prefix, ResetQuery, RouterKey as RouterKeyPdu, SerialNotify, SerialQuery,
|
||||
};
|
||||
use rpki::rtr::store::RtrStore;
|
||||
use rpki::rtr::server::connection::handle_tls_connection;
|
||||
use rpki::rtr::server::tls::load_rustls_server_config_with_options;
|
||||
use rpki::rtr::session::RtrSession;
|
||||
@@ -273,6 +274,42 @@ fn assert_error_report_matches(
|
||||
assert_eq!(report.erroneous_pdu(), offending_pdu);
|
||||
}
|
||||
|
||||
fn serials_all(serial: u32) -> [u32; 3] {
|
||||
[serial; 3]
|
||||
}
|
||||
|
||||
fn snapshots_all(snapshot: Snapshot) -> [Snapshot; 3] {
|
||||
[snapshot.clone(), snapshot.clone(), snapshot]
|
||||
}
|
||||
|
||||
fn deltas_all(deltas: VecDeque<Arc<Delta>>) -> [VecDeque<Arc<Delta>>; 3] {
|
||||
[deltas.clone(), deltas.clone(), deltas]
|
||||
}
|
||||
|
||||
async fn wait_for_store_serials(store: &RtrStore, expected: [u32; 3]) {
|
||||
for _ in 0..100 {
|
||||
let current = [
|
||||
store.get_serial_for_version(0).unwrap(),
|
||||
store.get_serial_for_version(1).unwrap(),
|
||||
store.get_serial_for_version(2).unwrap(),
|
||||
];
|
||||
if current == [Some(expected[0]), Some(expected[1]), Some(expected[2])] {
|
||||
return;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
}
|
||||
|
||||
panic!(
|
||||
"timed out waiting store serials {:?}, current={:?}",
|
||||
expected,
|
||||
[
|
||||
store.get_serial_for_version(0).unwrap(),
|
||||
store.get_serial_for_version(1).unwrap(),
|
||||
store.get_serial_for_version(2).unwrap(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// 测试:Reset Query 会返回完整 snapshot,并以 End of Data 结束响应。
|
||||
#[tokio::test]
|
||||
async fn reset_query_returns_snapshot_and_end_of_data() {
|
||||
@@ -285,9 +322,9 @@ async fn reset_query_returns_snapshot_and_end_of_data() {
|
||||
let snapshot = Snapshot::from_payloads(vec![Payload::RouteOrigin(origin)]);
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.snapshot(snapshot)
|
||||
.snapshots(snapshots_all(snapshot))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
@@ -333,7 +370,7 @@ async fn reset_query_returns_snapshot_and_end_of_data() {
|
||||
async fn reset_query_uses_version_specific_session_id() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([40, 41, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -360,12 +397,98 @@ async fn reset_query_uses_version_specific_session_id() {
|
||||
shutdown_server(client, shutdown_tx, server_handle).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn restart_restores_versioned_state_and_serves_queries() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
|
||||
let prefix = IPAddressPrefix {
|
||||
address: IPAddress::from_ipv4(Ipv4Addr::new(192, 0, 2, 0)),
|
||||
prefix_length: 24,
|
||||
};
|
||||
let origin = Payload::RouteOrigin(RouteOrigin::new(prefix, 24, 64496u32.into()));
|
||||
let valid_spki = vec![
|
||||
0x30, 0x13, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
|
||||
0x01, 0x05, 0x00, 0x03, 0x02, 0x00, 0x00,
|
||||
];
|
||||
let router_key = Payload::RouterKey(RouterKey::new(
|
||||
Ski::default(),
|
||||
Asn::from(64496u32),
|
||||
valid_spki.clone(),
|
||||
));
|
||||
let aspa_old = Payload::Aspa(Aspa::new(Asn::from(64496u32), vec![Asn::from(64497u32)]));
|
||||
let aspa_new = Payload::Aspa(Aspa::new(
|
||||
Asn::from(64496u32),
|
||||
vec![Asn::from(64497u32), Asn::from(64498u32)],
|
||||
));
|
||||
|
||||
let initial_payloads = vec![origin.clone(), router_key.clone(), aspa_old];
|
||||
let mut cache = rpki::rtr::cache::RtrCache::default()
|
||||
.init(&store, 16, false, Timing::new(600, 600, 7200), || {
|
||||
Ok(initial_payloads.clone())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
cache
|
||||
.update(vec![origin.clone(), router_key.clone(), aspa_new], &store)
|
||||
.unwrap();
|
||||
wait_for_store_serials(&store, [1, 1, 2]).await;
|
||||
|
||||
let restored = rpki::rtr::cache::RtrCache::default()
|
||||
.init(&store, 16, false, Timing::new(600, 600, 7200), || {
|
||||
panic!("file loader should not be used when restoring from store")
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(restored.serial_for_version(0), 1);
|
||||
assert_eq!(restored.serial_for_version(1), 1);
|
||||
assert_eq!(restored.serial_for_version(2), 2);
|
||||
|
||||
let shared = shared_cache(restored);
|
||||
|
||||
let (addr, _notify_tx, shutdown_tx, server_handle) = start_session_server(shared.clone()).await;
|
||||
let mut client = TcpStream::connect(addr).await.unwrap();
|
||||
ResetQuery::new(0).write(&mut client).await.unwrap();
|
||||
let response = CacheResponse::read(&mut client).await.unwrap();
|
||||
assert_eq!(response.version(), 0);
|
||||
let expected_sid_v0 = shared.read().unwrap().session_id_for_version(0);
|
||||
assert_eq!(response.session_id(), expected_sid_v0);
|
||||
let _v4 = IPv4Prefix::read(&mut client).await.unwrap();
|
||||
let eod_v0 = rpki::rtr::pdu::EndOfDataV0::read(&mut client).await.unwrap();
|
||||
assert_eq!(eod_v0.serial_number(), 1);
|
||||
shutdown_server(client, shutdown_tx, server_handle).await;
|
||||
|
||||
let (addr, _notify_tx, shutdown_tx, server_handle) = start_session_server(shared.clone()).await;
|
||||
let mut client = TcpStream::connect(addr).await.unwrap();
|
||||
ResetQuery::new(1).write(&mut client).await.unwrap();
|
||||
let response = CacheResponse::read(&mut client).await.unwrap();
|
||||
assert_eq!(response.version(), 1);
|
||||
let expected_sid_v1 = shared.read().unwrap().session_id_for_version(1);
|
||||
assert_eq!(response.session_id(), expected_sid_v1);
|
||||
let _v4 = IPv4Prefix::read(&mut client).await.unwrap();
|
||||
let _rk = RouterKeyPdu::read(&mut client).await.unwrap();
|
||||
let eod_v1 = EndOfDataV1::read(&mut client).await.unwrap();
|
||||
assert_eq!(eod_v1.serial_number(), 1);
|
||||
shutdown_server(client, shutdown_tx, server_handle).await;
|
||||
|
||||
let (addr, _notify_tx, shutdown_tx, server_handle) = start_session_server(shared.clone()).await;
|
||||
let mut client = TcpStream::connect(addr).await.unwrap();
|
||||
let sid_v2 = shared.read().unwrap().session_id_for_version(2);
|
||||
SerialQuery::new(2, sid_v2, 1).write(&mut client).await.unwrap();
|
||||
let response = CacheResponse::read(&mut client).await.unwrap();
|
||||
assert_eq!(response.version(), 2);
|
||||
assert_eq!(response.session_id(), sid_v2);
|
||||
let _aspa = AspaPdu::read(&mut client).await.unwrap();
|
||||
let eod_v2 = EndOfDataV1::read(&mut client).await.unwrap();
|
||||
assert_eq!(eod_v2.serial_number(), 2);
|
||||
shutdown_server(client, shutdown_tx, server_handle).await;
|
||||
}
|
||||
|
||||
/// 测试:当 Serial Query 的 session_id 和 serial 都与当前 cache 一致时,仅返回 End of Data。
|
||||
#[tokio::test]
|
||||
async fn serial_query_returns_end_of_data_when_up_to_date() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing {
|
||||
refresh: 600,
|
||||
retry: 600,
|
||||
@@ -402,7 +525,7 @@ async fn serial_query_returns_end_of_data_when_up_to_date() {
|
||||
async fn serial_query_returns_corrupt_data_when_session_id_mismatch() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing {
|
||||
refresh: 600,
|
||||
retry: 600,
|
||||
@@ -457,13 +580,13 @@ async fn serial_query_returns_deltas_when_incremental_update_available() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(101)
|
||||
.serials(serials_all(101))
|
||||
.timing(Timing {
|
||||
refresh: 600,
|
||||
retry: 600,
|
||||
expire: 7200,
|
||||
})
|
||||
.deltas(deltas)
|
||||
.deltas_by_version(deltas_all(deltas))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
@@ -542,10 +665,10 @@ async fn serial_query_returns_deltas_across_serial_wraparound() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(0)
|
||||
.serials(serials_all(0))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.snapshot(snapshot)
|
||||
.deltas(deltas)
|
||||
.snapshots(snapshots_all(snapshot))
|
||||
.deltas_by_version(deltas_all(deltas))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
@@ -587,7 +710,7 @@ async fn serial_query_returns_deltas_across_serial_wraparound() {
|
||||
async fn serial_query_returns_cache_reset_for_future_serial_across_wraparound() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(u32::MAX)
|
||||
.serials(serials_all(u32::MAX))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -637,9 +760,9 @@ async fn reset_query_returns_payloads_in_rtr_order() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.snapshot(snapshot)
|
||||
.snapshots(snapshots_all(snapshot))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
@@ -748,13 +871,13 @@ async fn serial_query_returns_announcements_before_withdrawals() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(101)
|
||||
.serials(serials_all(101))
|
||||
.timing(Timing {
|
||||
refresh: 600,
|
||||
retry: 600,
|
||||
expire: 7200,
|
||||
})
|
||||
.deltas(deltas)
|
||||
.deltas_by_version(deltas_all(deltas))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
@@ -840,7 +963,7 @@ async fn serial_query_returns_announcements_before_withdrawals() {
|
||||
async fn established_session_sends_serial_notify() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -882,7 +1005,7 @@ async fn established_session_sends_serial_notify() {
|
||||
async fn first_pdu_with_too_high_version_returns_unsupported_version_error() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -922,7 +1045,7 @@ async fn first_pdu_with_too_high_version_returns_unsupported_version_error() {
|
||||
async fn session_rejects_version_change_after_negotiation() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -975,7 +1098,7 @@ async fn session_rejects_version_change_after_negotiation() {
|
||||
async fn notify_is_not_sent_before_version_negotiation() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1012,7 +1135,7 @@ async fn notify_is_not_sent_before_version_negotiation() {
|
||||
async fn serial_notify_is_rate_limited_to_once_per_minute() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1073,7 +1196,7 @@ async fn reset_query_returns_no_data_available_when_cache_is_unavailable() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.availability(rpki::rtr::cache::CacheAvailability::NoDataAvailable)
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1108,7 +1231,7 @@ async fn serial_query_returns_no_data_available_when_cache_is_unavailable() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.availability(rpki::rtr::cache::CacheAvailability::NoDataAvailable)
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1142,7 +1265,7 @@ async fn serial_query_returns_no_data_available_when_cache_is_unavailable() {
|
||||
async fn first_pdu_with_invalid_length_returns_corrupt_data() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1211,7 +1334,7 @@ async fn first_pdu_with_invalid_length_returns_corrupt_data() {
|
||||
async fn established_session_closes_after_receiving_error_report() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1260,7 +1383,7 @@ async fn established_session_closes_after_receiving_error_report() {
|
||||
async fn established_session_invalid_header_returns_corrupt_data() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1324,7 +1447,7 @@ async fn established_session_invalid_header_returns_corrupt_data() {
|
||||
async fn established_session_unknown_pdu_returns_unsupported_pdu_type() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1393,9 +1516,9 @@ async fn version_zero_does_not_send_router_key_or_aspa() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.snapshot(snapshot)
|
||||
.snapshots(snapshots_all(snapshot))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
@@ -1436,9 +1559,9 @@ async fn version_two_aspa_withdraw_has_empty_provider_list() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(101)
|
||||
.serials(serials_all(101))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.deltas(deltas)
|
||||
.deltas_by_version(deltas_all(deltas))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
@@ -1477,7 +1600,11 @@ async fn version_two_aspa_withdraw_has_empty_provider_list() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn version_one_sends_router_key_but_not_aspa() {
|
||||
let router_key = RouterKey::new(Ski::default(), Asn::from(64496u32), vec![1u8; 32]);
|
||||
let valid_spki = vec![
|
||||
0x30, 0x13, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
|
||||
0x01, 0x05, 0x00, 0x03, 0x02, 0x00, 0x00,
|
||||
];
|
||||
let router_key = RouterKey::new(Ski::default(), Asn::from(64496u32), valid_spki);
|
||||
let aspa = Aspa::new(Asn::from(64496u32), vec![Asn::from(64497u32)]);
|
||||
let snapshot = Snapshot::from_payloads(vec![
|
||||
Payload::RouterKey(router_key),
|
||||
@@ -1486,9 +1613,9 @@ async fn version_one_sends_router_key_but_not_aspa() {
|
||||
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.snapshot(snapshot)
|
||||
.snapshots(snapshots_all(snapshot))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
@@ -1530,7 +1657,7 @@ async fn version_one_sends_router_key_but_not_aspa() {
|
||||
async fn established_session_idle_timeout_returns_transport_failed() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1588,7 +1715,7 @@ async fn established_session_idle_timeout_returns_transport_failed() {
|
||||
async fn tls_client_with_matching_san_ip_is_accepted() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1618,7 +1745,7 @@ async fn tls_client_with_matching_san_ip_is_accepted() {
|
||||
async fn tls_client_accepts_server_certificate_with_dns_san() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1655,7 +1782,7 @@ async fn tls_client_accepts_server_certificate_with_dns_san() {
|
||||
async fn tls_server_dns_name_san_strict_mode_rejects_ip_only_certificate() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1678,7 +1805,7 @@ async fn tls_server_dns_name_san_strict_mode_rejects_ip_only_certificate() {
|
||||
async fn tls_client_with_mismatched_san_ip_is_rejected() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1716,7 +1843,7 @@ async fn tls_client_with_mismatched_san_ip_is_rejected() {
|
||||
async fn invalid_timing_prevents_end_of_data_response() {
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 8000, 7200))
|
||||
.build();
|
||||
|
||||
@@ -1752,9 +1879,9 @@ async fn invalid_aspa_prevents_snapshot_response() {
|
||||
))]);
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.snapshot(snapshot)
|
||||
.snapshots(snapshots_all(snapshot))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
@@ -1790,9 +1917,9 @@ async fn invalid_router_key_prevents_snapshot_response() {
|
||||
))]);
|
||||
let cache = RtrCacheBuilder::new()
|
||||
.session_ids(SessionIds::from_array([42, 42, 42]))
|
||||
.serial(100)
|
||||
.serials(serials_all(100))
|
||||
.timing(Timing::new(600, 600, 7200))
|
||||
.snapshot(snapshot)
|
||||
.snapshots(snapshots_all(snapshot))
|
||||
.build();
|
||||
|
||||
let server_cache = shared_cache(cache);
|
||||
|
||||
45
tests/test_store_boundary.rs
Normal file
45
tests/test_store_boundary.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn collect_rs_files(dir: &Path, out: &mut Vec<PathBuf>) {
|
||||
let entries = fs::read_dir(dir).unwrap();
|
||||
for entry in entries {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
collect_rs_files(&path, out);
|
||||
} else if path.extension().and_then(|s| s.to_str()) == Some("rs") {
|
||||
out.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_cache_state_versioned_has_limited_call_sites() {
|
||||
let repo_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let mut files = Vec::new();
|
||||
collect_rs_files(&repo_root.join("src"), &mut files);
|
||||
collect_rs_files(&repo_root.join("tests"), &mut files);
|
||||
|
||||
let allowed = [
|
||||
repo_root.join("src/rtr/store.rs"),
|
||||
repo_root.join("src/rtr/cache/store.rs"),
|
||||
repo_root.join("tests/test_store_db.rs"),
|
||||
repo_root.join("tests/test_cache.rs"),
|
||||
repo_root.join("tests/test_store_boundary.rs"),
|
||||
];
|
||||
|
||||
let mut violations = Vec::new();
|
||||
for file in files {
|
||||
let content = fs::read_to_string(&file).unwrap();
|
||||
if content.contains(".save_cache_state_versioned(") && !allowed.contains(&file) {
|
||||
violations.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
violations.is_empty(),
|
||||
"unexpected direct save_cache_state_versioned() call sites: {:?}",
|
||||
violations
|
||||
);
|
||||
}
|
||||
@@ -2,371 +2,110 @@ mod common;
|
||||
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
use common::test_helper::{
|
||||
indent_block, payloads_to_string, test_report, v4_origin, v6_origin,
|
||||
};
|
||||
use common::test_helper::{v4_origin, v6_origin};
|
||||
|
||||
use rpki::rtr::cache::{CacheAvailability, Delta, SessionIds, Snapshot};
|
||||
use rpki::rtr::cache::{CacheAvailability, Delta, Snapshot};
|
||||
use rpki::rtr::payload::Payload;
|
||||
use rpki::rtr::store::RtrStore;
|
||||
|
||||
fn snapshot_to_string(snapshot: &Snapshot) -> String {
|
||||
let payloads = snapshot.payloads_for_rtr();
|
||||
payloads_to_string(&payloads)
|
||||
}
|
||||
|
||||
fn delta_to_string(delta: &Delta) -> String {
|
||||
format!(
|
||||
"serial: {}\nannounced:\n{}withdrawn:\n{}",
|
||||
delta.serial(),
|
||||
indent_block(&payloads_to_string(delta.announced()), 2),
|
||||
indent_block(&payloads_to_string(delta.withdrawn()), 2),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_save_and_get_snapshot() {
|
||||
fn store_db_versioned_state_persists_and_restores_all_versions() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
|
||||
let input_payloads = vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
Payload::RouteOrigin(v6_origin(
|
||||
let snapshots = [
|
||||
Snapshot::from_payloads(vec![Payload::RouteOrigin(v4_origin(
|
||||
192, 0, 2, 0, 24, 24, 64496,
|
||||
))]),
|
||||
Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497)),
|
||||
]),
|
||||
Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497)),
|
||||
Payload::RouteOrigin(v6_origin(
|
||||
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
|
||||
32,
|
||||
48,
|
||||
64498,
|
||||
)),
|
||||
]),
|
||||
];
|
||||
let session_ids = [410u16, 411u16, 412u16];
|
||||
let serials = [100u32, 200u32, 300u32];
|
||||
|
||||
let d0 = Delta::new(
|
||||
100,
|
||||
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
||||
vec![],
|
||||
);
|
||||
let d2 = Delta::new(
|
||||
300,
|
||||
vec![Payload::RouteOrigin(v6_origin(
|
||||
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
|
||||
32,
|
||||
48,
|
||||
64497,
|
||||
)),
|
||||
];
|
||||
let snapshot = Snapshot::from_payloads(input_payloads.clone());
|
||||
|
||||
store.save_snapshot(&snapshot).unwrap();
|
||||
let loaded = store.get_snapshot().unwrap().expect("snapshot should exist");
|
||||
|
||||
let input = format!(
|
||||
"db_path: {}\nsnapshot:\n{}",
|
||||
dir.path().display(),
|
||||
indent_block(&payloads_to_string(&input_payloads), 2),
|
||||
);
|
||||
|
||||
let output = format!(
|
||||
"loaded snapshot:\n{}same_content: {}\n",
|
||||
indent_block(&snapshot_to_string(&loaded), 2),
|
||||
snapshot.same_content(&loaded),
|
||||
);
|
||||
|
||||
test_report(
|
||||
"store_db_save_and_get_snapshot",
|
||||
"验证 save_snapshot() 后可以通过 get_snapshot() 正确读回 Snapshot。",
|
||||
&input,
|
||||
&output,
|
||||
);
|
||||
|
||||
assert!(snapshot.same_content(&loaded));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_set_and_get_meta_fields() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
let session_ids = SessionIds::from_array([40, 41, 42]);
|
||||
|
||||
store.set_session_ids(&session_ids).unwrap();
|
||||
store.set_serial(100).unwrap();
|
||||
store.set_delta_window(101, 110).unwrap();
|
||||
|
||||
let loaded_session_ids = store.get_session_ids().unwrap();
|
||||
let serial = store.get_serial().unwrap();
|
||||
let window = store.get_delta_window().unwrap();
|
||||
|
||||
let input = format!(
|
||||
"db_path: {}\nset_session_ids={:?}\nset_serial=100\nset_delta_window=(101, 110)\n",
|
||||
dir.path().display(),
|
||||
session_ids,
|
||||
);
|
||||
|
||||
let output = format!(
|
||||
"get_session_ids: {:?}\nget_serial: {:?}\nget_delta_window: {:?}\n",
|
||||
loaded_session_ids, serial, window,
|
||||
);
|
||||
|
||||
test_report(
|
||||
"store_db_set_and_get_meta_fields",
|
||||
"验证 session_ids / serial / delta_window 能正确写入并读回。",
|
||||
&input,
|
||||
&output,
|
||||
);
|
||||
|
||||
assert_eq!(loaded_session_ids, Some(session_ids));
|
||||
assert_eq!(serial, Some(100));
|
||||
assert_eq!(window, Some((101, 110)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_clear_delta_window_removes_both_bounds() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
|
||||
store.set_delta_window(101, 110).unwrap();
|
||||
assert_eq!(store.get_delta_window().unwrap(), Some((101, 110)));
|
||||
|
||||
store.clear_delta_window().unwrap();
|
||||
|
||||
assert_eq!(store.get_delta_window().unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_save_and_get_delta() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
|
||||
let delta = Delta::new(
|
||||
101,
|
||||
vec![Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497))],
|
||||
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
||||
);
|
||||
|
||||
store.save_delta(&delta).unwrap();
|
||||
let loaded = store.get_delta(101).unwrap().expect("delta should exist");
|
||||
|
||||
let input = format!(
|
||||
"db_path: {}\ndelta:\n{}",
|
||||
dir.path().display(),
|
||||
indent_block(&delta_to_string(&delta), 2),
|
||||
);
|
||||
|
||||
let output = format!(
|
||||
"loaded delta:\n{}",
|
||||
indent_block(&delta_to_string(&loaded), 2),
|
||||
);
|
||||
|
||||
test_report(
|
||||
"store_db_save_and_get_delta",
|
||||
"验证 save_delta() 后可以通过 get_delta(serial) 正确读回 Delta。",
|
||||
&input,
|
||||
&output,
|
||||
);
|
||||
|
||||
assert_eq!(loaded.serial(), 101);
|
||||
assert_eq!(loaded.announced().len(), 1);
|
||||
assert_eq!(loaded.withdrawn().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_load_deltas_since_returns_only_newer_deltas_in_order() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
|
||||
let d101 = Delta::new(
|
||||
101,
|
||||
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
||||
64498,
|
||||
))],
|
||||
vec![],
|
||||
);
|
||||
let d102 = Delta::new(
|
||||
102,
|
||||
vec![Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497))],
|
||||
vec![],
|
||||
);
|
||||
let d103 = Delta::new(
|
||||
103,
|
||||
vec![Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498))],
|
||||
vec![],
|
||||
);
|
||||
|
||||
store.save_delta(&d101).unwrap();
|
||||
store.save_delta(&d102).unwrap();
|
||||
store.save_delta(&d103).unwrap();
|
||||
|
||||
let loaded = store.load_deltas_since(101).unwrap();
|
||||
|
||||
let input = format!(
|
||||
"db_path: {}\nsaved delta serials: [101, 102, 103]\nload_deltas_since(101)\n",
|
||||
dir.path().display(),
|
||||
);
|
||||
|
||||
let output = {
|
||||
let mut s = String::new();
|
||||
for (idx, d) in loaded.iter().enumerate() {
|
||||
s.push_str(&format!("loaded[{}]:\n", idx));
|
||||
s.push_str(&indent_block(&delta_to_string(d), 2));
|
||||
}
|
||||
s
|
||||
};
|
||||
|
||||
test_report(
|
||||
"store_db_load_deltas_since_returns_only_newer_deltas_in_order",
|
||||
"验证 load_deltas_since(x) 只返回 serial > x 的 Delta,且顺序正确。",
|
||||
&input,
|
||||
&output,
|
||||
);
|
||||
|
||||
assert_eq!(loaded.len(), 2);
|
||||
assert_eq!(loaded[0].serial(), 102);
|
||||
assert_eq!(loaded[1].serial(), 103);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_save_snapshot_and_meta_writes_all_fields() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
let session_ids = SessionIds::from_array([40, 41, 42]);
|
||||
|
||||
let snapshot = Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497)),
|
||||
]);
|
||||
|
||||
store
|
||||
.save_snapshot_and_meta(&snapshot, &session_ids, 100)
|
||||
.unwrap();
|
||||
|
||||
let loaded_snapshot = store.get_snapshot().unwrap().expect("snapshot should exist");
|
||||
let loaded_session_ids = store.get_session_ids().unwrap();
|
||||
let loaded_serial = store.get_serial().unwrap();
|
||||
|
||||
let input = format!(
|
||||
"db_path: {}\nsnapshot:\n{}session_ids={:?}\nserial=100\n",
|
||||
dir.path().display(),
|
||||
indent_block(&snapshot_to_string(&snapshot), 2),
|
||||
session_ids,
|
||||
);
|
||||
|
||||
let output = format!(
|
||||
"loaded_snapshot:\n{}loaded_session_ids: {:?}\nloaded_serial: {:?}\n",
|
||||
indent_block(&snapshot_to_string(&loaded_snapshot), 2),
|
||||
loaded_session_ids,
|
||||
loaded_serial,
|
||||
);
|
||||
|
||||
test_report(
|
||||
"store_db_save_snapshot_and_meta_writes_all_fields",
|
||||
"验证 save_snapshot_and_meta() 会同时写入 snapshot、session_ids 和 serial。",
|
||||
&input,
|
||||
&output,
|
||||
);
|
||||
|
||||
assert!(snapshot.same_content(&loaded_snapshot));
|
||||
assert_eq!(loaded_session_ids, Some(session_ids));
|
||||
assert_eq!(loaded_serial, Some(100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_save_cache_state_writes_delta_snapshot_meta_and_window_together() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
let session_ids = SessionIds::from_array([40, 41, 42]);
|
||||
|
||||
let snapshot = Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497)),
|
||||
]);
|
||||
let delta = Delta::new(
|
||||
101,
|
||||
vec![Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497))],
|
||||
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
||||
);
|
||||
|
||||
store
|
||||
.save_cache_state(
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
101,
|
||||
Some(&delta),
|
||||
Some((101, 101)),
|
||||
false,
|
||||
&serials,
|
||||
&[Some(&d0), None, Some(&d2)],
|
||||
&[Some((100, 100)), None, Some((300, 300))],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let loaded_snapshot = store.get_snapshot().unwrap().expect("snapshot should exist");
|
||||
let loaded_session_ids = store.get_session_ids().unwrap();
|
||||
let loaded_serial = store.get_serial().unwrap();
|
||||
let loaded_availability = store.get_availability().unwrap();
|
||||
let loaded_delta = store.get_delta(101).unwrap().expect("delta should exist");
|
||||
let loaded_window = store.get_delta_window().unwrap();
|
||||
assert_eq!(store.get_availability().unwrap(), Some(CacheAvailability::Ready));
|
||||
|
||||
assert!(snapshot.same_content(&loaded_snapshot));
|
||||
assert_eq!(loaded_session_ids, Some(session_ids));
|
||||
assert_eq!(loaded_serial, Some(101));
|
||||
assert_eq!(loaded_availability, Some(CacheAvailability::Ready));
|
||||
assert_eq!(loaded_delta.serial(), 101);
|
||||
assert_eq!(loaded_window, Some((101, 101)));
|
||||
for version in 0u8..=2 {
|
||||
let idx = version as usize;
|
||||
let loaded_snapshot = store
|
||||
.get_snapshot_for_version(version)
|
||||
.unwrap()
|
||||
.expect("snapshot should exist");
|
||||
let loaded_session_id = store
|
||||
.get_session_id_for_version(version)
|
||||
.unwrap()
|
||||
.expect("session_id should exist");
|
||||
let loaded_serial = store
|
||||
.get_serial_for_version(version)
|
||||
.unwrap()
|
||||
.expect("serial should exist");
|
||||
|
||||
assert!(snapshots[idx].same_content(&loaded_snapshot));
|
||||
assert_eq!(loaded_session_id, session_ids[idx]);
|
||||
assert_eq!(loaded_serial, serials[idx]);
|
||||
}
|
||||
|
||||
assert_eq!(store.get_delta_window_for_version(0).unwrap(), Some((100, 100)));
|
||||
assert_eq!(store.get_delta_window_for_version(1).unwrap(), None);
|
||||
assert_eq!(store.get_delta_window_for_version(2).unwrap(), Some((300, 300)));
|
||||
assert_eq!(
|
||||
store.get_delta_for_version(0, 100).unwrap().map(|d| d.serial()),
|
||||
Some(100)
|
||||
);
|
||||
assert!(store.get_delta_for_version(1, 200).unwrap().is_none());
|
||||
assert_eq!(
|
||||
store.get_delta_for_version(2, 300).unwrap().map(|d| d.serial()),
|
||||
Some(300)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_save_cache_state_prunes_deltas_older_than_window_min() {
|
||||
fn store_db_versioned_delta_window_wraparound_is_isolated_by_version() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
let session_ids = SessionIds::from_array([40, 41, 42]);
|
||||
|
||||
let snapshot = Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
]);
|
||||
|
||||
let d101 = Delta::new(
|
||||
101,
|
||||
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
||||
vec![],
|
||||
);
|
||||
let d102 = Delta::new(
|
||||
102,
|
||||
vec![Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497))],
|
||||
vec![],
|
||||
);
|
||||
let d103 = Delta::new(
|
||||
103,
|
||||
vec![Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498))],
|
||||
vec![],
|
||||
);
|
||||
|
||||
store
|
||||
.save_cache_state(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&session_ids,
|
||||
101,
|
||||
Some(&d101),
|
||||
Some((101, 101)),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&session_ids,
|
||||
102,
|
||||
Some(&d102),
|
||||
Some((101, 102)),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&session_ids,
|
||||
103,
|
||||
Some(&d103),
|
||||
Some((103, 103)),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(store.get_delta(101).unwrap().is_none());
|
||||
assert!(store.get_delta(102).unwrap().is_none());
|
||||
assert_eq!(store.get_delta(103).unwrap().map(|d| d.serial()), Some(103));
|
||||
assert_eq!(store.get_delta_window().unwrap(), Some((103, 103)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_load_delta_window_restores_wraparound_window_in_serial_order() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
let session_ids = SessionIds::from_array([40, 41, 42]);
|
||||
let snapshot = Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
]);
|
||||
let snapshots = std::array::from_fn(|_| Snapshot::empty());
|
||||
let session_ids = [600u16, 601u16, 602u16];
|
||||
let serials = [0u32, 0u32, 0u32];
|
||||
|
||||
let d_max = Delta::new(
|
||||
u32::MAX,
|
||||
@@ -383,162 +122,228 @@ fn store_db_load_delta_window_restores_wraparound_window_in_serial_order() {
|
||||
vec![Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498))],
|
||||
vec![],
|
||||
);
|
||||
|
||||
store
|
||||
.save_cache_state(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&session_ids,
|
||||
u32::MAX,
|
||||
Some(&d_max),
|
||||
Some((u32::MAX, u32::MAX)),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&session_ids,
|
||||
0,
|
||||
Some(&d_zero),
|
||||
Some((u32::MAX, 0)),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state(
|
||||
CacheAvailability::Ready,
|
||||
&snapshot,
|
||||
&session_ids,
|
||||
1,
|
||||
Some(&d_one),
|
||||
Some((u32::MAX, 1)),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let loaded = store.load_delta_window(u32::MAX, 1).unwrap();
|
||||
|
||||
assert_eq!(loaded.iter().map(Delta::serial).collect::<Vec<_>>(), vec![u32::MAX, 0, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_load_snapshot_and_serial_returns_consistent_pair() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
|
||||
let snapshot = Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498)),
|
||||
]);
|
||||
|
||||
store.save_snapshot_and_serial(&snapshot, 200).unwrap();
|
||||
|
||||
let loaded = store
|
||||
.load_snapshot_and_serial()
|
||||
.unwrap()
|
||||
.expect("snapshot+serial should exist");
|
||||
|
||||
let input = format!(
|
||||
"db_path: {}\nsnapshot:\n{}serial=200\n",
|
||||
dir.path().display(),
|
||||
indent_block(&snapshot_to_string(&snapshot), 2),
|
||||
);
|
||||
|
||||
let output = format!(
|
||||
"loaded_snapshot:\n{}loaded_serial: {}\n",
|
||||
indent_block(&snapshot_to_string(&loaded.0), 2),
|
||||
loaded.1,
|
||||
);
|
||||
|
||||
test_report(
|
||||
"store_db_load_snapshot_and_serial_returns_consistent_pair",
|
||||
"验证 load_snapshot_and_serial() 能正确返回一致的 snapshot 与 serial。",
|
||||
&input,
|
||||
&output,
|
||||
);
|
||||
|
||||
assert!(snapshot.same_content(&loaded.0));
|
||||
assert_eq!(loaded.1, 200);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_delete_snapshot_delta_and_serial_removes_data() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
|
||||
let snapshot = Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
]);
|
||||
let delta = Delta::new(
|
||||
101,
|
||||
vec![Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497))],
|
||||
let d_v1_only = Delta::new(
|
||||
0,
|
||||
vec![Payload::RouteOrigin(v4_origin(10, 0, 0, 0, 24, 24, 64500))],
|
||||
vec![],
|
||||
);
|
||||
|
||||
store.save_snapshot(&snapshot).unwrap();
|
||||
store.save_delta(&delta).unwrap();
|
||||
store.set_serial(100).unwrap();
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[None, None, Some(&d_max)],
|
||||
&[None, None, None],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[None, None, Some(&d_zero)],
|
||||
&[None, None, None],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[None, None, Some(&d_one)],
|
||||
&[None, None, None],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[None, Some(&d_v1_only), None],
|
||||
&[None, None, None],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
store.delete_snapshot().unwrap();
|
||||
store.delete_delta(101).unwrap();
|
||||
store.delete_serial().unwrap();
|
||||
|
||||
let loaded_snapshot = store.get_snapshot().unwrap();
|
||||
let loaded_delta = store.get_delta(101).unwrap();
|
||||
let loaded_serial = store.get_serial().unwrap();
|
||||
|
||||
let input = format!(
|
||||
"db_path: {}\nsave snapshot + delta(101) + serial(100), then delete all three.\n",
|
||||
dir.path().display(),
|
||||
let v2_loaded = store.load_delta_window_for_version(2, u32::MAX, 1).unwrap();
|
||||
assert_eq!(
|
||||
v2_loaded.iter().map(Delta::serial).collect::<Vec<_>>(),
|
||||
vec![u32::MAX, 0, 1]
|
||||
);
|
||||
|
||||
let output = format!(
|
||||
"get_snapshot: {:?}\nget_delta(101): {:?}\nget_serial: {:?}\n",
|
||||
loaded_snapshot.as_ref().map(|_| "Some(snapshot)"),
|
||||
loaded_delta.as_ref().map(|_| "Some(delta)"),
|
||||
loaded_serial,
|
||||
);
|
||||
|
||||
test_report(
|
||||
"store_db_delete_snapshot_delta_and_serial_removes_data",
|
||||
"验证 delete_snapshot()/delete_delta()/delete_serial() 后,对应数据不再可读。",
|
||||
&input,
|
||||
&output,
|
||||
);
|
||||
|
||||
assert!(loaded_snapshot.is_none());
|
||||
assert!(loaded_delta.is_none());
|
||||
assert!(loaded_serial.is_none());
|
||||
let v1_loaded = store.load_delta_window_for_version(1, 0, 0).unwrap();
|
||||
assert_eq!(v1_loaded.iter().map(Delta::serial).collect::<Vec<_>>(), vec![0]);
|
||||
assert_eq!(v1_loaded[0].announced().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_load_snapshot_and_serial_errors_on_inconsistent_state() {
|
||||
fn store_db_versioned_clear_window_affects_only_target_version() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
|
||||
let snapshot = Snapshot::from_payloads(vec![
|
||||
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
||||
]);
|
||||
|
||||
store.save_snapshot(&snapshot).unwrap();
|
||||
// 故意不写 serial,制造不一致状态
|
||||
|
||||
let result = store.load_snapshot_and_serial();
|
||||
|
||||
let input = format!(
|
||||
"db_path: {}\n仅保存 snapshot,不保存 serial。\n",
|
||||
dir.path().display(),
|
||||
let snapshots = [
|
||||
Snapshot::from_payloads(vec![Payload::RouteOrigin(v4_origin(
|
||||
192, 0, 2, 0, 24, 24, 64496,
|
||||
))]),
|
||||
Snapshot::from_payloads(vec![Payload::RouteOrigin(v4_origin(
|
||||
198, 51, 100, 0, 24, 24, 64497,
|
||||
))]),
|
||||
Snapshot::from_payloads(vec![Payload::RouteOrigin(v4_origin(
|
||||
203, 0, 113, 0, 24, 24, 64498,
|
||||
))]),
|
||||
];
|
||||
let session_ids = [420u16, 421u16, 422u16];
|
||||
let serials = [10u32, 20u32, 30u32];
|
||||
let d0 = Delta::new(
|
||||
10,
|
||||
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
||||
vec![],
|
||||
);
|
||||
let d2 = Delta::new(
|
||||
30,
|
||||
vec![Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498))],
|
||||
vec![],
|
||||
);
|
||||
|
||||
let output = format!("load_snapshot_and_serial result: {:?}\n", result);
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[Some(&d0), None, Some(&d2)],
|
||||
&[Some((10, 10)), None, Some((30, 30))],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
test_report(
|
||||
"store_db_load_snapshot_and_serial_errors_on_inconsistent_state",
|
||||
"验证当 snapshot 和 serial 状态不一致时,load_snapshot_and_serial() 返回错误。",
|
||||
&input,
|
||||
&output,
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[None, None, None],
|
||||
&[None, None, None],
|
||||
&[true, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(store.get_delta_window_for_version(0).unwrap(), None);
|
||||
assert!(store.get_delta_for_version(0, 10).unwrap().is_none());
|
||||
|
||||
assert_eq!(store.get_delta_window_for_version(2).unwrap(), Some((30, 30)));
|
||||
assert_eq!(
|
||||
store.get_delta_for_version(2, 30).unwrap().map(|d| d.serial()),
|
||||
Some(30)
|
||||
);
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_versioned_prunes_outside_window() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
|
||||
let snapshots = std::array::from_fn(|_| {
|
||||
Snapshot::from_payloads(vec![Payload::RouteOrigin(v4_origin(
|
||||
192, 0, 2, 0, 24, 24, 64496,
|
||||
))])
|
||||
});
|
||||
let session_ids = [500u16, 501u16, 502u16];
|
||||
let serials = [102u32, 0u32, 0u32];
|
||||
let d100 = Delta::new(
|
||||
100,
|
||||
vec![Payload::RouteOrigin(v4_origin(10, 0, 0, 0, 24, 24, 65001))],
|
||||
vec![],
|
||||
);
|
||||
let d101 = Delta::new(
|
||||
101,
|
||||
vec![Payload::RouteOrigin(v4_origin(10, 0, 1, 0, 24, 24, 65002))],
|
||||
vec![],
|
||||
);
|
||||
let d102 = Delta::new(
|
||||
102,
|
||||
vec![Payload::RouteOrigin(v4_origin(10, 0, 2, 0, 24, 24, 65003))],
|
||||
vec![],
|
||||
);
|
||||
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[Some(&d100), None, None],
|
||||
&[Some((100, 100)), None, None],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[Some(&d101), None, None],
|
||||
&[Some((100, 101)), None, None],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[Some(&d102), None, None],
|
||||
&[Some((102, 102)), None, None],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(store.get_delta_for_version(0, 100).unwrap().is_none());
|
||||
assert!(store.get_delta_for_version(0, 101).unwrap().is_none());
|
||||
assert_eq!(
|
||||
store.get_delta_for_version(0, 102).unwrap().map(|d| d.serial()),
|
||||
Some(102)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_db_versioned_load_delta_window_requires_complete_range() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = RtrStore::open(dir.path()).unwrap();
|
||||
let snapshots = std::array::from_fn(|_| Snapshot::empty());
|
||||
let session_ids = [700u16, 701u16, 702u16];
|
||||
let serials = [0u32, 0u32, 0u32];
|
||||
|
||||
let d11 = Delta::new(
|
||||
11,
|
||||
vec![Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497))],
|
||||
vec![],
|
||||
);
|
||||
store
|
||||
.save_cache_state_versioned(
|
||||
CacheAvailability::Ready,
|
||||
&snapshots,
|
||||
&session_ids,
|
||||
&serials,
|
||||
&[None, Some(&d11), None],
|
||||
&[None, None, None],
|
||||
&[false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let err = store.load_delta_window_for_version(1, 10, 11).unwrap_err();
|
||||
assert!(err
|
||||
.to_string()
|
||||
.contains("delta window starts at 10, but first persisted delta is Some(11)"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user