11mod common;
22mod system_integration;
33
4+ #[ macro_use]
5+ mod platform_test_macro;
6+
47#[ cfg( target_os = "linux" ) ]
58mod tests {
69 use super :: * ;
710 use crate :: system_integration:: JailTestPlatform ;
8- use serial_test:: serial;
911
1012 /// Linux-specific platform implementation
1113 struct LinuxPlatform ;
@@ -31,73 +33,13 @@ mod tests {
3133 }
3234 }
3335
34- #[ test]
35- #[ serial] // Network namespaces are global state, must run sequentially
36- fn test_jail_allows_matching_requests ( ) {
37- system_integration:: test_jail_allows_matching_requests :: < LinuxPlatform > ( ) ;
38- }
39-
40- #[ test]
41- #[ serial] // Network namespaces are global state, must run sequentially
42- fn test_jail_denies_non_matching_requests ( ) {
43- system_integration:: test_jail_denies_non_matching_requests :: < LinuxPlatform > ( ) ;
44- }
45-
46- #[ test]
47- #[ serial] // Network namespaces are global state, must run sequentially
48- fn test_jail_method_specific_rules ( ) {
49- system_integration:: test_jail_method_specific_rules :: < LinuxPlatform > ( ) ;
50- }
51-
52- #[ test]
53- #[ serial] // Network namespaces are global state, must run sequentially
54- fn test_jail_log_only_mode ( ) {
55- system_integration:: test_jail_log_only_mode :: < LinuxPlatform > ( ) ;
56- }
57-
58- #[ test]
59- #[ serial] // Network namespaces are global state, must run sequentially
60- fn test_jail_dry_run_mode ( ) {
61- system_integration:: test_jail_dry_run_mode :: < LinuxPlatform > ( ) ;
62- }
63-
64- #[ test]
65- fn test_jail_requires_command ( ) {
66- system_integration:: test_jail_requires_command :: < LinuxPlatform > ( ) ;
67- }
68-
69- #[ test]
70- #[ serial] // Network namespaces are global state, must run sequentially
71- fn test_jail_exit_code_propagation ( ) {
72- system_integration:: test_jail_exit_code_propagation :: < LinuxPlatform > ( ) ;
73- }
74-
75- #[ test]
76- #[ serial] // Network namespaces are global state, must run sequentially
77- fn test_native_jail_blocks_https ( ) {
78- system_integration:: test_native_jail_blocks_https :: < LinuxPlatform > ( ) ;
79- }
80-
81- #[ test]
82- #[ serial] // Network namespaces are global state, must run sequentially
83- fn test_native_jail_allows_https ( ) {
84- system_integration:: test_native_jail_allows_https :: < LinuxPlatform > ( ) ;
85- }
86-
87- #[ test]
88- #[ serial] // Network namespaces are global state, must run sequentially
89- fn test_jail_https_connect_denied ( ) {
90- system_integration:: test_jail_https_connect_denied :: < LinuxPlatform > ( ) ;
91- }
36+ // Generate all the shared platform tests
37+ platform_tests ! ( LinuxPlatform ) ;
9238
93- // Linux with network namespaces supports HTTPS CONNECT
94- #[ test]
95- #[ serial] // Network namespaces are global state, must run sequentially
96- fn test_jail_https_connect_allowed ( ) {
97- system_integration:: test_jail_https_connect_allowed :: < LinuxPlatform > ( ) ;
98- }
39+ // Linux-specific tests below
40+ use serial_test:: serial;
9941
100- // Linux-specific test: verify namespace cleanup
42+ /// Linux-specific test: verify namespace cleanup
10143 #[ test]
10244 #[ serial]
10345 fn test_namespace_cleanup ( ) {
@@ -112,11 +54,11 @@ mod tests {
11254 let initial_namespaces = String :: from_utf8_lossy ( & output. stdout ) ;
11355 let initial_count = initial_namespaces
11456 . lines ( )
115- . filter ( |line| line. starts_with ( "httpjail_" ) )
57+ . filter ( |line| line. contains ( "httpjail_" ) )
11658 . count ( ) ;
11759
11860 // Run httpjail
119- let mut cmd = system_integration :: httpjail_cmd ( ) ;
61+ let mut cmd = common :: httpjail_cmd ( ) ;
12062 cmd. arg ( "-r" )
12163 . arg ( "allow: .*" )
12264 . arg ( "--" )
@@ -125,7 +67,7 @@ mod tests {
12567
12668 let _output = cmd. output ( ) . expect ( "Failed to execute httpjail" ) ;
12769
128- // Check namespaces are cleaned up
70+ // Check namespace was cleaned up
12971 let output = std:: process:: Command :: new ( "ip" )
13072 . args ( & [ "netns" , "list" ] )
13173 . output ( )
@@ -134,87 +76,69 @@ mod tests {
13476 let final_namespaces = String :: from_utf8_lossy ( & output. stdout ) ;
13577 let final_count = final_namespaces
13678 . lines ( )
137- . filter ( |line| line. starts_with ( "httpjail_" ) )
79+ . filter ( |line| line. contains ( "httpjail_" ) )
13880 . count ( ) ;
13981
14082 assert_eq ! (
14183 initial_count, final_count,
142- "Namespace was not cleaned up properly. Initial: {}, Final: {}" ,
84+ "Namespace not cleaned up properly. Initial: {}, Final: {}" ,
14385 initial_count, final_count
14486 ) ;
14587 }
14688
147- // Linux-specific test: verify concurrent execution
89+ /// Linux-specific test: verify concurrent namespace isolation
14890 #[ test]
14991 #[ serial]
15092 fn test_concurrent_namespace_isolation ( ) {
15193 LinuxPlatform :: require_privileges ( ) ;
152-
153- use std:: sync:: Arc ;
154- use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
94+ use std:: process:: Command ;
15595 use std:: thread;
96+ use std:: time:: Duration ;
15697
157- let success_count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
158- let mut handles = vec ! [ ] ;
159-
160- // Spawn multiple httpjail instances concurrently
161- for i in 0 ..3 {
162- let success_count = Arc :: clone ( & success_count) ;
163- let handle = thread:: spawn ( move || {
164- let mut cmd = system_integration:: httpjail_cmd ( ) ;
165- cmd. arg ( "-r" )
166- . arg ( "allow: .*" )
167- . arg ( "--" )
168- . arg ( "sh" )
169- . arg ( "-c" )
170- . arg ( & format ! ( "echo 'Instance {}'" , i) ) ;
171-
172- match cmd. output ( ) {
173- Ok ( output) if output. status . success ( ) => {
174- success_count. fetch_add ( 1 , Ordering :: SeqCst ) ;
175- }
176- _ => { }
177- }
178- } ) ;
179- handles. push ( handle) ;
180- }
181-
182- // Wait for all threads
183- for handle in handles {
184- handle. join ( ) . unwrap ( ) ;
185- }
186-
187- // All instances should succeed
188- assert_eq ! (
189- success_count. load( Ordering :: SeqCst ) ,
190- 3 ,
191- "Not all concurrent instances succeeded"
192- ) ;
193-
194- // Verify all namespaces are cleaned up
195- let output = std:: process:: Command :: new ( "ip" )
196- . args ( & [ "netns" , "list" ] )
98+ // Start first httpjail instance that sleeps
99+ let mut child1 = common:: httpjail_cmd ( )
100+ . arg ( "-r" )
101+ . arg ( "allow: .*" )
102+ . arg ( "--" )
103+ . arg ( "sh" )
104+ . arg ( "-c" )
105+ . arg ( "echo 'Instance 1'; sleep 2; echo 'Instance 1 done'" )
106+ . spawn ( )
107+ . expect ( "Failed to start first httpjail" ) ;
108+
109+ // Give it time to set up
110+ thread:: sleep ( Duration :: from_millis ( 500 ) ) ;
111+
112+ // Start second httpjail instance
113+ let output2 = common:: httpjail_cmd ( )
114+ . arg ( "-r" )
115+ . arg ( "allow: .*" )
116+ . arg ( "--" )
117+ . arg ( "echo" )
118+ . arg ( "Instance 2" )
197119 . output ( )
198- . expect ( "Failed to list namespaces " ) ;
120+ . expect ( "Failed to execute second httpjail " ) ;
199121
200- let namespaces = String :: from_utf8_lossy ( & output. stdout ) ;
201- let httpjail_count = namespaces
202- . lines ( )
203- . filter ( |line| line. starts_with ( "httpjail_" ) )
204- . count ( ) ;
122+ // Both should succeed without interference
123+ let output1 = child1
124+ . wait_with_output ( )
125+ . expect ( "Failed to wait for first httpjail" ) ;
205126
206- assert_eq ! (
207- httpjail_count, 0 ,
208- "Found {} httpjail namespaces still present after concurrent test" ,
209- httpjail_count
127+ assert ! (
128+ output1. status. success( ) ,
129+ "First instance failed: {:?}" ,
130+ String :: from_utf8_lossy( & output1. stderr)
131+ ) ;
132+ assert ! (
133+ output2. status. success( ) ,
134+ "Second instance failed: {:?}" ,
135+ String :: from_utf8_lossy( & output2. stderr)
210136 ) ;
211- }
212- }
213137
214- # [ cfg ( not ( target_os = "linux" ) ) ]
215- mod tests {
216- # [ test ]
217- fn test_platform_not_linux ( ) {
218- println ! ( "Linux integration tests only run on Linux ") ;
138+ // Verify both ran
139+ let stdout1 = String :: from_utf8_lossy ( & output1 . stdout ) ;
140+ let stdout2 = String :: from_utf8_lossy ( & output2 . stdout ) ;
141+ assert ! ( stdout1 . contains ( "Instance 1" ) , "First instance didn't run" ) ;
142+ assert ! ( stdout2 . contains ( "Instance 2" ) , "Second instance didn't run ") ;
219143 }
220144}
0 commit comments