44// If a copy of the MPL was not distributed with this file, You can obtain one at
55// https://mozilla.org/MPL/2.0/.
66
7- #![ cfg( test) ]
87use std:: net:: { IpAddr , Ipv4Addr } ;
98use std:: sync:: atomic:: Ordering ;
109use std:: time:: Duration ;
@@ -13,9 +12,6 @@ use zond_common::models::host::Host;
1312use zond_common:: models:: ip:: { range:: Ipv4Range , set:: IpSet } ;
1413use zond_core:: scanner:: { self , STOP_SIGNAL } ;
1514
16- #[ cfg( target_os = "linux" ) ]
17- use crate :: utils:: NetnsContext ;
18-
1915#[ tokio:: test]
2016async fn test_discovery_single_loopback ( ) {
2117 let config: ZondConfig = ZondConfig {
@@ -107,71 +103,70 @@ async fn test_stop_signal_aborts() {
107103}
108104
109105#[ tokio:: test]
110- #[ cfg( target_os = "linux" ) ]
111- async fn test_privileged_discovery_netns ( ) {
112- let _ctx: NetnsContext = match NetnsContext :: new ( "test1" ) {
113- Some ( c) => c,
114- None => {
115- eprintln ! ( "Skipping netns test: Requires root privileges or 'ip' command." ) ;
116- return ;
117- }
106+ async fn test_discovery_empty_set ( ) {
107+ let cfg: ZondConfig = ZondConfig {
108+ no_banner : true ,
109+ no_dns : true ,
110+ redact : false ,
111+ quiet : 0 ,
112+ disable_input : true ,
118113 } ;
119114
120- let target_ip: IpAddr = IpAddr :: V4 ( Ipv4Addr :: new ( 10 , 200 , 0 , 2 ) ) ;
115+ let targets = IpSet :: new ( ) ;
116+ let result = scanner:: discover ( targets, & cfg) . await ;
121117
122- let config: ZondConfig = ZondConfig {
118+ assert ! ( result. is_ok( ) ) ;
119+ let hosts = result. unwrap ( ) ;
120+ assert ! (
121+ hosts. is_empty( ) ,
122+ "Scanning empty set should return no hosts"
123+ ) ;
124+ }
125+
126+ #[ tokio:: test]
127+ async fn test_discovery_redundant_ranges ( ) {
128+ let cfg: ZondConfig = ZondConfig {
123129 no_banner : true ,
124130 no_dns : true ,
125131 redact : false ,
126132 quiet : 0 ,
127133 disable_input : true ,
128134 } ;
129135
130- let mut collection = IpSet :: new ( ) ;
131- collection. insert ( target_ip) ;
132-
133- let result = scanner:: discover ( collection, & config) . await ;
134-
135- match result {
136- Ok ( hosts) => {
137- assert ! ( !hosts. is_empty( ) , "Should find the target in the namespace" ) ;
138- let host = hosts
139- . iter ( )
140- . find ( |h| h. primary_ip == target_ip)
141- . expect ( "Target IP not found in results" ) ;
142-
143- assert ! (
144- host. mac. is_some( ) ,
145- "Should resolve MAC address for local neighbor"
146- ) ;
147- println ! ( "Found host: {:?} with MAC {:?}" , host. primary_ip, host. mac) ;
148- }
149- Err ( e) => panic ! ( "Discovery failed: {}" , e) ,
150- }
151- }
136+ let mut targets = IpSet :: new ( ) ;
137+ // Overlapping ranges: 127.0.0.1/31 (1, 2) and 127.0.0.1-5 (1, 2, 3, 4, 5)
138+ targets. insert_range ( "127.0.0.1/31" . parse ( ) . unwrap ( ) ) ;
139+ targets. insert_range ( "127.0.0.1-127.0.0.5" . parse ( ) . unwrap ( ) ) ;
152140
153- #[ test]
154- fn test_lan_network_resolution ( ) {
155- // Assert that the machine running the integration test has at least 1 viable interface
156- // that resolves via our platform agnostics hooks (macOS networksetup, Linux sysfs, Windows GetIfTable2).
157- let result = zond_common:: net:: interface:: get_lan_network ( ) ;
158- assert ! (
159- result. is_ok( ) ,
160- "Expected no OS or Viability errors during interface parsing"
161- ) ;
141+ // IpSet should have merged these into a single range of 6 IPs (0-5)
142+ assert_eq ! ( targets. len( ) , 6 ) ;
162143
163- // Virtualized headless CI runners might return None here since they use virtual bridges,
164- // but the FFI/Syscalls must execute safely regardless!
165- println ! ( "Resolved LAN network: {:?}" , result. unwrap( ) ) ;
144+ let result = scanner:: discover ( targets, & cfg) . await ;
145+ assert ! ( result. is_ok( ) ) ;
166146}
167147
168- #[ test]
169- fn test_prioritized_interfaces_resolution ( ) {
170- let interfaces_res = zond_common:: net:: interface:: get_prioritized_interfaces ( 10 ) ;
171- assert ! ( interfaces_res. is_ok( ) ) ;
172- let interfaces = interfaces_res. unwrap ( ) ;
148+ #[ tokio:: test]
149+ async fn test_discovery_loopback_stress ( ) {
150+ let cfg: ZondConfig = ZondConfig {
151+ no_banner : true ,
152+ no_dns : true ,
153+ redact : false ,
154+ quiet : 0 ,
155+ disable_input : true ,
156+ } ;
157+
158+ let mut targets = IpSet :: new ( ) ;
159+ targets. insert_range ( "127.0.0.0/24" . parse ( ) . unwrap ( ) ) ;
160+
161+ let start = std:: time:: Instant :: now ( ) ;
162+ let result = scanner:: discover ( targets, & cfg) . await ;
163+ let elapsed = start. elapsed ( ) ;
164+
165+ assert ! ( result. is_ok( ) ) ;
166+ println ! ( "Sweep of 256 IPs took {}ms" , elapsed. as_millis( ) ) ;
167+
173168 assert ! (
174- !interfaces . is_empty ( ) ,
175- "Expected at least 1 UP, non-loopback network interface on the host natively "
169+ elapsed < Duration :: from_secs ( 5 ) ,
170+ "Sweep took too long, concurrency might be broken "
176171 ) ;
177172}
0 commit comments