mod common; use std::net::Ipv6Addr; use common::test_helper::{v4_origin, v6_origin}; use rpki::rtr::cache::{CacheAvailability, Delta, Snapshot}; use rpki::rtr::payload::Payload; use rpki::rtr::store::RtrStore; #[test] fn store_db_versioned_state_persists_and_restores_all_versions() { let dir = tempfile::tempdir().unwrap(); let store = RtrStore::open(dir.path()).unwrap(); 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, 64498, ))], vec![], ); store .save_cache_state_versioned( CacheAvailability::Ready, &snapshots, &session_ids, &serials, &[Some(&d0), None, Some(&d2)], &[Some((100, 100)), None, Some((300, 300))], &[false, false, false], ) .unwrap(); assert_eq!(store.get_availability().unwrap(), Some(CacheAvailability::Ready)); 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_versioned_delta_window_wraparound_is_isolated_by_version() { let dir = tempfile::tempdir().unwrap(); let store = RtrStore::open(dir.path()).unwrap(); 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, vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))], vec![], ); let d_zero = Delta::new( 0, vec![Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497))], vec![], ); let d_one = Delta::new( 1, vec![Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498))], vec![], ); let d_v1_only = Delta::new( 0, vec![Payload::RouteOrigin(v4_origin(10, 0, 0, 0, 24, 24, 64500))], vec![], ); 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(); let v2_loaded = store.load_delta_window_for_version(2, u32::MAX, 1).unwrap(); assert_eq!( v2_loaded.iter().map(Delta::serial).collect::>(), vec![u32::MAX, 0, 1] ); let v1_loaded = store.load_delta_window_for_version(1, 0, 0).unwrap(); assert_eq!(v1_loaded.iter().map(Delta::serial).collect::>(), vec![0]); assert_eq!(v1_loaded[0].announced().len(), 1); } #[test] fn store_db_versioned_clear_window_affects_only_target_version() { let dir = tempfile::tempdir().unwrap(); let store = RtrStore::open(dir.path()).unwrap(); 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![], ); 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(); 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) ); } #[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)")); }