use super::*;
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn peer_lifecycle_registry_owns_connected_udp_activation_plan() {
let node = make_node();
let transport_id = TransportId::new(1);
let configured_full = Identity::generate();
let configured_identity = PeerIdentity::from_pubkey_full(configured_full.pubkey_full());
let configured_addr = *configured_identity.node_addr();
let discovered_full = Identity::generate();
let discovered_identity = PeerIdentity::from_pubkey_full(discovered_full.pubkey_full());
let discovered_addr = *discovered_identity.node_addr();
let stale_full = Identity::generate();
let stale_identity = PeerIdentity::from_pubkey_full(stale_full.pubkey_full());
let stale_addr = *stale_identity.node_addr();
let installed_full = Identity::generate();
let installed_identity = PeerIdentity::from_pubkey_full(installed_full.pubkey_full());
let installed_addr = *installed_identity.node_addr();
let mut config = Config::new();
config.peers.push(crate::config::PeerConfig::new(
configured_full.npub(),
"udp",
"127.0.0.1:1",
));
let configured_peers = ConfiguredPeerSendWeights::from_config(&config);
let mut registry = PeerLifecycleRegistry::default();
registry.insert_with_current_session_index(
discovered_addr,
make_active_test_peer(
&node,
&discovered_full,
discovered_identity,
transport_id,
LinkId::new(20),
TransportAddr::from_string("connected-udp-discovered"),
SessionIndex::new(20),
SessionIndex::new(30),
),
);
registry.insert_with_current_session_index(
configured_addr,
make_active_test_peer(
&node,
&configured_full,
configured_identity,
transport_id,
LinkId::new(10),
TransportAddr::from_string("connected-udp-configured"),
SessionIndex::new(10),
SessionIndex::new(11),
),
);
let mut stale_peer = make_active_test_peer(
&node,
&stale_full,
stale_identity,
transport_id,
LinkId::new(30),
TransportAddr::from_string("connected-udp-stale"),
SessionIndex::new(40),
SessionIndex::new(41),
);
stale_peer.mark_stale();
registry.insert_with_current_session_index(stale_addr, stale_peer);
let mut installed_peer = make_active_test_peer(
&node,
&installed_full,
installed_identity,
transport_id,
LinkId::new(40),
TransportAddr::from_string("connected-udp-installed"),
SessionIndex::new(50),
SessionIndex::new(51),
);
let (socket, drain) = make_test_connected_udp_pair(transport_id);
installed_peer.set_connected_udp(socket, drain);
registry.insert_with_current_session_index(installed_addr, installed_peer);
let plan = registry.connected_udp_activation_plan(&configured_peers);
assert_eq!(
plan.installed_count, 1,
"lifecycle owner should count already-installed connected UDP peers"
);
assert_eq!(
plan.candidates,
vec![configured_addr, discovered_addr],
"configured peers should be activated before discovered peers, while stale and already-connected peers are skipped"
);
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn connected_udp_decrypt_fast_path_only_prepares_matching_established_packets() {
let transport_id = TransportId::new(7);
let receiver_idx = SessionIndex::new(0x0a0b_0c0d);
let session_key =
crate::node::decrypt_worker::DecryptSessionKey::new(transport_id, receiver_idx.as_u32());
let workers = crate::node::decrypt_worker::DecryptWorkerPool::spawn(1);
let (fallback_tx, _fallback_rx) =
crate::node::decrypt_worker::decrypt_worker_fallback_channels();
let fast_path =
ConnectedUdpDecryptFastPath::new(session_key, make_node_addr(0x77), workers, fallback_tx);
let remote_addr = TransportAddr::from_string("127.0.0.1:2121");
let header = build_established_header(receiver_idx, 99, FLAG_CE | FLAG_SP, 0);
let packet = build_encrypted(&header, &[0u8; 16]);
let job = fast_path
.prepare_job(transport_id, remote_addr.clone(), packet.clone(), 1_234)
.expect("matching established packet should prepare a decrypt job");
assert_eq!(job.packet_data, packet);
assert_eq!(job.session_key, session_key);
assert_eq!(job.fmp_counter, 99);
assert_eq!(job.fmp_flags, FLAG_CE | FLAG_SP);
assert_eq!(job.fmp_header, header);
assert_eq!(job.fmp_ciphertext_offset, ESTABLISHED_HEADER_SIZE);
let wrong_header = build_established_header(SessionIndex::new(0x0102_0304), 100, 0, 0);
let wrong_packet = build_encrypted(&wrong_header, &[0u8; 16]);
match fast_path.prepare_job(
transport_id,
remote_addr.clone(),
wrong_packet.clone(),
1_235,
) {
Ok(_) => panic!("wrong receiver index must not bypass rx_loop"),
Err(returned) => assert_eq!(returned, wrong_packet),
}
let mut non_established = vec![0u8; ESTABLISHED_HEADER_SIZE + 16];
non_established[0] = 0x01;
match fast_path.prepare_job(transport_id, remote_addr, non_established.clone(), 1_236) {
Ok(_) => panic!("non-established packets must stay on the packet channel"),
Err(returned) => assert_eq!(returned, non_established),
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn peer_lifecycle_registry_owns_connected_udp_install_and_clear() {
let node = make_node();
let peer_full = Identity::generate();
let peer_identity = PeerIdentity::from_pubkey_full(peer_full.pubkey_full());
let peer_addr = *peer_identity.node_addr();
let transport_id = TransportId::new(1);
let mut registry = PeerLifecycleRegistry::default();
let active_peer = make_active_test_peer(
&node,
&peer_full,
peer_identity,
transport_id,
LinkId::new(10),
TransportAddr::from_string("connected-udp-install"),
SessionIndex::new(10),
SessionIndex::new(11),
);
registry.insert_with_current_session_index(peer_addr, active_peer);
let (socket, drain) = make_test_connected_udp_pair(transport_id);
let installed = registry.install_connected_udp_if_eligible(&peer_addr, socket, drain);
assert_eq!(
installed,
ConnectedUdpInstallResult::Installed,
"lifecycle owner should install connected UDP only after eligibility recheck"
);
assert!(
registry
.get(&peer_addr)
.expect("active peer")
.connected_udp()
.is_some(),
"connected UDP socket should be visible through the active peer after lifecycle install"
);
let (second_socket, second_drain) = make_test_connected_udp_pair(transport_id);
assert_eq!(
registry.install_connected_udp_if_eligible(&peer_addr, second_socket, second_drain),
ConnectedUdpInstallResult::NotEligible,
"already-installed connected UDP peers must not get a replacement from the activation race path"
);
assert_eq!(
registry.clear_connected_udp_for_peer(&peer_addr),
ConnectedUdpClearResult::Cleared,
"lifecycle owner should clear an installed connected UDP socket/drain pair"
);
assert!(
registry
.get(&peer_addr)
.expect("active peer")
.connected_udp()
.is_none(),
"connected UDP socket should be gone after lifecycle clear"
);
assert_eq!(
registry.clear_connected_udp_for_peer(&peer_addr),
ConnectedUdpClearResult::AlreadyClear,
"clearing an already-clear peer should be idempotent"
);
assert_eq!(
registry.clear_connected_udp_for_peer(&NodeAddr::from_bytes([0x42; 16])),
ConnectedUdpClearResult::MissingPeer,
"clear should report when the peer lifecycle owner has no active peer"
);
}
#[test]
fn peer_lifecycle_registry_owns_link_dead_direct_path_degradation() {
let node = make_node();
let peer_full = Identity::generate();
let peer_identity = PeerIdentity::from_pubkey_full(peer_full.pubkey_full());
let peer_addr = *peer_identity.node_addr();
let transport_id = TransportId::new(1);
let link_id = LinkId::new(10);
let remote_addr = TransportAddr::from_string("link-dead-peer");
let current_our_index = SessionIndex::new(10);
let their_index = SessionIndex::new(20);
let mut registry = PeerLifecycleRegistry::default();
let mut active_peer = make_active_test_peer(
&node,
&peer_full,
peer_identity,
transport_id,
link_id,
remote_addr,
current_our_index,
their_index,
);
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
let (socket, drain) = make_test_connected_udp_pair(transport_id);
active_peer.set_connected_udp(socket, drain);
assert!(
active_peer.connected_udp().is_some(),
"fixture should start with connected UDP installed"
);
}
registry.insert_with_current_session_index(peer_addr, active_peer);
let degraded = registry
.mark_link_dead_direct_path(&peer_addr)
.expect("link-dead degradation should find active peer");
assert_eq!(
degraded.link_id, link_id,
"lifecycle owner should return the degraded link for logging and cleanup"
);
#[cfg(any(target_os = "linux", target_os = "macos"))]
assert!(
degraded.connected_udp_cleared,
"link-dead degradation should clear connected UDP through the lifecycle owner"
);
let peer = registry
.get(&peer_addr)
.expect("link-dead degradation must keep active peer storage");
assert!(
peer.can_send(),
"stale direct paths remain probeable instead of becoming disconnected"
);
assert!(
!peer.is_healthy(),
"link-dead direct paths should no longer be healthy for payload routing"
);
assert_eq!(
peer.link_id(),
link_id,
"link-dead degradation should not swap peer link identity"
);
#[cfg(any(target_os = "linux", target_os = "macos"))]
assert!(
peer.connected_udp().is_none(),
"connected UDP socket/drain pair must not outlive stale direct-path evidence"
);
}
#[test]
fn peer_lifecycle_registry_owns_active_peer_teardown_session_indices() {
let node = make_node();
let peer_full = Identity::generate();
let peer_identity = PeerIdentity::from_pubkey_full(peer_full.pubkey_full());
let peer_addr = *peer_identity.node_addr();
let transport_id = TransportId::new(1);
let link_id = LinkId::new(10);
let remote_addr = TransportAddr::from_string("teardown-peer");
let current_our_index = SessionIndex::new(10);
let their_index = SessionIndex::new(20);
let rekey_our_index = SessionIndex::new(11);
let mut registry = PeerLifecycleRegistry::default();
let mut active_peer = make_active_test_peer(
&node,
&peer_full,
peer_identity,
transport_id,
link_id,
remote_addr,
current_our_index,
their_index,
);
arm_test_fmp_rekey(&mut active_peer, rekey_our_index);
assert!(registry.insert(peer_addr, active_peer).is_none());
assert_eq!(
registry.insert_session_index((transport_id, current_our_index.as_u32()), peer_addr),
None
);
assert_eq!(
registry.insert_session_index((transport_id, rekey_our_index.as_u32()), peer_addr),
None
);
let removed = registry
.remove_with_session_indices(&peer_addr)
.expect("active peer teardown should return the removed peer plus session indices");
assert_eq!(removed.peer.node_addr(), &peer_addr);
assert_eq!(
removed.session_indices,
vec![
PeerSessionIndex {
kind: PeerSessionIndexKind::Current,
key: (transport_id, current_our_index.as_u32()),
index: current_our_index,
},
PeerSessionIndex {
kind: PeerSessionIndexKind::Rekey,
key: (transport_id, rekey_our_index.as_u32()),
index: rekey_our_index,
},
],
"peer lifecycle teardown must own which active-peer session indices need deregister/free"
);
assert!(
registry.get(&peer_addr).is_none(),
"teardown removal must remove active peer storage"
);
}
#[test]
fn peer_lifecycle_registry_owns_connection_and_active_peer_storage() {
let peer_full = Identity::generate();
let peer_identity = PeerIdentity::from_pubkey_full(peer_full.pubkey_full());
let peer_addr = *peer_identity.node_addr();
let transport_id = TransportId::new(1);
let link_id = LinkId::new(77);
let session_key = (transport_id, 10);
let mut registry = PeerLifecycleRegistry::default();
let connection = PeerConnection::outbound(link_id, peer_identity, 1_000);
assert!(registry.insert_connection(link_id, connection).is_none());
assert_eq!(registry.connection_len(), 1);
assert!(registry.contains_connection(&link_id));
assert_eq!(
registry
.get_connection(&link_id)
.and_then(|conn: &PeerConnection| conn.expected_identity())
.map(|identity: &PeerIdentity| identity.node_addr()),
Some(&peer_addr)
);
assert!(
registry
.insert(peer_addr, ActivePeer::new(peer_identity, link_id, 2_000))
.is_none()
);
assert_eq!(registry.len(), 1);
assert!(registry.contains_key(&peer_addr));
assert_eq!(registry.insert_session_index(session_key, peer_addr), None);
assert_eq!(registry.lookup_session_index(session_key), Some(peer_addr));
let removed_connection = registry
.remove_connection(&link_id)
.expect("pending connection storage should live in the lifecycle owner");
assert_eq!(removed_connection.link_id(), link_id);
assert!(
registry.get(&peer_addr).is_some(),
"active peer storage must survive pending-connection teardown"
);
assert_eq!(registry.lookup_session_index(session_key), Some(peer_addr));
let removed_peer = registry
.remove(&peer_addr)
.expect("active peer storage should live in the lifecycle owner");
assert_eq!(removed_peer.node_addr(), &peer_addr);
assert!(registry.connection_is_empty());
assert!(registry.is_empty());
}