mod common; use std::net::Ipv6Addr; use common::test_helper::{ indent_block, payloads_to_string, test_report, v4_origin, v6_origin, }; use rpki::rtr::cache::{Delta, Snapshot}; use rpki::rtr::payload::Payload; use rpki::rtr::store_db::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() { 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( 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(); store.set_session_id(42).unwrap(); store.set_serial(100).unwrap(); store.set_delta_window(101, 110).unwrap(); let session_id = store.get_session_id().unwrap(); let serial = store.get_serial().unwrap(); let window = store.get_delta_window().unwrap(); let input = format!( "db_path: {}\nset_session_id=42\nset_serial=100\nset_delta_window=(101, 110)\n", dir.path().display(), ); let output = format!( "get_session_id: {:?}\nget_serial: {:?}\nget_delta_window: {:?}\n", session_id, serial, window, ); test_report( "store_db_set_and_get_meta_fields", "验证 session_id / serial / delta_window 能正确写入并读回。", &input, &output, ); assert_eq!(session_id, Some(42)); assert_eq!(serial, Some(100)); assert_eq!(window, Some((101, 110))); } #[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))], 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 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, 42, 100).unwrap(); let loaded_snapshot = store.get_snapshot().unwrap().expect("snapshot should exist"); let loaded_session = store.get_session_id().unwrap(); let loaded_serial = store.get_serial().unwrap(); let input = format!( "db_path: {}\nsnapshot:\n{}session_id=42\nserial=100\n", dir.path().display(), indent_block(&snapshot_to_string(&snapshot), 2), ); let output = format!( "loaded_snapshot:\n{}loaded_session_id: {:?}\nloaded_serial: {:?}\n", indent_block(&snapshot_to_string(&loaded_snapshot), 2), loaded_session, loaded_serial, ); test_report( "store_db_save_snapshot_and_meta_writes_all_fields", "验证 save_snapshot_and_meta() 会同时写入 snapshot、session_id 和 serial。", &input, &output, ); assert!(snapshot.same_content(&loaded_snapshot)); assert_eq!(loaded_session, Some(42)); assert_eq!(loaded_serial, Some(100)); } #[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))], vec![], ); store.save_snapshot(&snapshot).unwrap(); store.save_delta(&delta).unwrap(); store.set_serial(100).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 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()); } #[test] fn store_db_load_snapshot_and_serial_errors_on_inconsistent_state() { 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 output = format!("load_snapshot_and_serial result: {:?}\n", result); test_report( "store_db_load_snapshot_and_serial_errors_on_inconsistent_state", "验证当 snapshot 和 serial 状态不一致时,load_snapshot_and_serial() 返回错误。", &input, &output, ); assert!(result.is_err()); }