@@ -1939,7 +1939,7 @@ mod tests {
19391939 }
19401940
19411941 #[ tokio:: test]
1942- async fn test_manual_shutdown ( ) {
1942+ async fn test_shutdown_manual ( ) {
19431943 // Set up test nodes
19441944 let ( ( node_1, node_2) , clients) = setup_test_nodes ( ) ;
19451945
@@ -2082,4 +2082,168 @@ mod tests {
20822082 total_payments
20832083 ) ;
20842084 }
2085+
2086+ #[ tokio:: test]
2087+ async fn test_shutdown_multiple_activities ( ) {
2088+ let ( ( node_1, node_2) , clients) = setup_test_nodes ( ) ;
2089+
2090+ // Define activity 1: From node 0 to node 1 with a count limit of 3 payments
2091+ let activity_1 = crate :: ActivityDefinition {
2092+ source : node_1. clone ( ) ,
2093+ destination : node_2. clone ( ) ,
2094+ start_secs : None , // Start immediately
2095+ count : Some ( 3 ) , // Limited to 3 payments
2096+ interval_secs : crate :: ValueOrRange :: Value ( 1 ) , // 1 second interval
2097+ amount_msat : crate :: ValueOrRange :: Value ( 2000 ) , // 2000 msats
2098+ } ;
2099+
2100+ // Define activity 2: From node 2 to node 1 with no count limit
2101+ let activity_2 = crate :: ActivityDefinition {
2102+ source : node_2. clone ( ) ,
2103+ destination : node_1. clone ( ) ,
2104+ start_secs : None , // Start immediately
2105+ count : None , // No limit (will run forever)
2106+ interval_secs : crate :: ValueOrRange :: Value ( 1 ) , // 1 second interval
2107+ amount_msat : crate :: ValueOrRange :: Value ( 3000 ) , // 3000 msats
2108+ } ;
2109+
2110+ // Create simulation with a timeout of 6 seconds
2111+ // This gives enough time for first activity to complete (3 payments at 1 second each)
2112+ // and for the second activity to continue making payments after that
2113+ let timeout_secs = 6 ;
2114+ let simulation = Simulation :: new (
2115+ SimulationCfg :: new (
2116+ Some ( timeout_secs) , // Run for 6 seconds
2117+ 1000 , // Expected payment size
2118+ 0.1 , // Activity multiplier
2119+ None , // No result writing
2120+ Some ( 42 ) , // Seed for determinism
2121+ ) ,
2122+ clients,
2123+ vec ! [ activity_1, activity_2] , // Use both activities
2124+ TaskTracker :: new ( ) ,
2125+ ) ;
2126+
2127+ // Run the simulation for the full timeout duration
2128+ let start = std:: time:: Instant :: now ( ) ;
2129+ let result = simulation. run ( ) . await ;
2130+ let elapsed = start. elapsed ( ) ;
2131+
2132+ // Verify the simulation ran correctly
2133+ assert ! ( result. is_ok( ) , "Simulation should end without error" ) ;
2134+
2135+ // Verify it ran for approximately the timeout duration
2136+ let margin = Duration :: from_secs ( 1 ) ;
2137+ assert ! (
2138+ elapsed >= Duration :: from_secs( 6 ) && elapsed <= Duration :: from_secs( 6 ) + margin,
2139+ "Simulation should have run for approximately {} seconds, but took {:?}" ,
2140+ timeout_secs,
2141+ elapsed
2142+ ) ;
2143+
2144+ // We expect at least 8 payments in total:
2145+ // - 3 from activity 1 (which completes its count)
2146+ // - At least 5 from activity 2 (1 per second for 6 seconds)
2147+ // The exact number may vary slightly due to timing, but should be at least 8
2148+ let total_payments = simulation. get_total_payments ( ) . await ;
2149+ assert ! (
2150+ total_payments >= 8 ,
2151+ "Expected at least 8 payments to be attempted, got {}" ,
2152+ total_payments
2153+ ) ;
2154+ }
2155+
2156+ #[ tokio:: test]
2157+ async fn test_shutdown_multiple_activities_with_error ( ) {
2158+ // Set up test nodes with node_1 that will produce a permanent error
2159+ let ( ( node_1, node_2) , mut clients) = setup_test_nodes ( ) ;
2160+
2161+ // Create mock for node_1 that will return a permanent error on send_payment
2162+ let mut mock_node_1 = MockLightningNode :: new ( ) ;
2163+ let node_1_clone = node_1. clone ( ) ;
2164+
2165+ // Set up basic expectations for node_1
2166+ mock_node_1. expect_get_info ( ) . return_const ( node_1. clone ( ) ) ;
2167+ mock_node_1
2168+ . expect_get_network ( )
2169+ . returning ( || Ok ( Network :: Regtest ) ) ;
2170+ mock_node_1
2171+ . expect_list_channels ( )
2172+ . returning ( || Ok ( vec ! [ 100_000 ] ) ) ;
2173+ mock_node_1
2174+ . expect_get_node_info ( )
2175+ . returning ( move |_| Ok ( node_1_clone. clone ( ) ) ) ;
2176+ mock_node_1. expect_track_payment ( ) . returning ( |_, _| {
2177+ Ok ( crate :: PaymentResult {
2178+ htlc_count : 1 ,
2179+ payment_outcome : crate :: PaymentOutcome :: Success ,
2180+ } )
2181+ } ) ;
2182+
2183+ // Set up node_1 to return a permanent error on send_payment
2184+ mock_node_1. expect_send_payment ( ) . returning ( |_, _| {
2185+ Err ( LightningError :: PermanentError (
2186+ "Simulated permanent error" . to_string ( ) ,
2187+ ) )
2188+ } ) ;
2189+
2190+ // Replace node_1 with our new mock
2191+ clients. insert ( node_1. pubkey , Arc :: new ( Mutex :: new ( mock_node_1) ) ) ;
2192+
2193+ // Define two activities
2194+ // Activity 1: From node_1 to node_2 - This will encounter the permanent error
2195+ let activity_1 = crate :: ActivityDefinition {
2196+ source : node_1. clone ( ) ,
2197+ destination : node_2. clone ( ) ,
2198+ start_secs : None ,
2199+ count : None , // No limit
2200+ interval_secs : crate :: ValueOrRange :: Value ( 1 ) , // 1 second interval
2201+ amount_msat : crate :: ValueOrRange :: Value ( 2000 ) , // 2000 msats
2202+ } ;
2203+
2204+ // Activity 2: From node_2 to node_1 - This would normally succeed
2205+ let activity_2 = crate :: ActivityDefinition {
2206+ source : node_2. clone ( ) ,
2207+ destination : node_1. clone ( ) ,
2208+ start_secs : Some ( 2 ) , // Start 2 seconds after the first activity
2209+ count : Some ( 10 ) , // 10 payments if no error
2210+ interval_secs : crate :: ValueOrRange :: Value ( 1 ) , // 1 second interval
2211+ amount_msat : crate :: ValueOrRange :: Value ( 3000 ) , // 3000 msats
2212+ } ;
2213+
2214+ // Create simulation with a long timeout that we don't expect to be reached
2215+ let simulation = Simulation :: new (
2216+ SimulationCfg :: new (
2217+ Some ( 30 ) , // 30 second timeout (shouldn't matter)
2218+ 1000 , // Expected payment size
2219+ 0.1 , // Activity multiplier
2220+ None , // No result writing
2221+ Some ( 42 ) , // Seed for determinism
2222+ ) ,
2223+ clients,
2224+ vec ! [ activity_1, activity_2] , // Use both activities
2225+ TaskTracker :: new ( ) ,
2226+ ) ;
2227+
2228+ // Run the simulation (should be interrupted by the error)
2229+ let start = std:: time:: Instant :: now ( ) ;
2230+ let _ = simulation. run ( ) . await ;
2231+ let elapsed = start. elapsed ( ) ;
2232+
2233+ // Check that simulation ran for a short time (less than 3 seconds)
2234+ assert ! (
2235+ elapsed < Duration :: from_secs( 3 ) ,
2236+ "Simulation should have shut down quickly after encountering the error, took {:?}" ,
2237+ elapsed
2238+ ) ;
2239+
2240+ // We expect no successful payments to be recorded since the first activity errors immediately
2241+ // and it should shut down the entire simulation
2242+ let total_payments = simulation. get_total_payments ( ) . await ;
2243+ assert_eq ! (
2244+ total_payments, 0 ,
2245+ "Expected no payments to be recorded, got {}" ,
2246+ total_payments
2247+ ) ;
2248+ }
20852249}
0 commit comments