@@ -1866,7 +1866,7 @@ mod tests {
18661866 }
18671867
18681868 #[ tokio:: test]
1869- async fn test_manual_shutdown ( ) {
1869+ async fn test_shutdown_manual ( ) {
18701870 // Set up test nodes
18711871 let ( ( node_1, node_2) , clients) = setup_test_nodes ( ) ;
18721872
@@ -2009,4 +2009,168 @@ mod tests {
20092009 total_payments
20102010 ) ;
20112011 }
2012+
2013+ #[ tokio:: test]
2014+ async fn test_shutdown_multiple_activities ( ) {
2015+ let ( ( node_1, node_2) , clients) = setup_test_nodes ( ) ;
2016+
2017+ // Define activity 1: From node 0 to node 1 with a count limit of 3 payments
2018+ let activity_1 = crate :: ActivityDefinition {
2019+ source : node_1. clone ( ) ,
2020+ destination : node_2. clone ( ) ,
2021+ start_secs : None , // Start immediately
2022+ count : Some ( 3 ) , // Limited to 3 payments
2023+ interval_secs : crate :: ValueOrRange :: Value ( 1 ) , // 1 second interval
2024+ amount_msat : crate :: ValueOrRange :: Value ( 2000 ) , // 2000 msats
2025+ } ;
2026+
2027+ // Define activity 2: From node 2 to node 1 with no count limit
2028+ let activity_2 = crate :: ActivityDefinition {
2029+ source : node_2. clone ( ) ,
2030+ destination : node_1. clone ( ) ,
2031+ start_secs : None , // Start immediately
2032+ count : None , // No limit (will run forever)
2033+ interval_secs : crate :: ValueOrRange :: Value ( 1 ) , // 1 second interval
2034+ amount_msat : crate :: ValueOrRange :: Value ( 3000 ) , // 3000 msats
2035+ } ;
2036+
2037+ // Create simulation with a timeout of 6 seconds
2038+ // This gives enough time for first activity to complete (3 payments at 1 second each)
2039+ // and for the second activity to continue making payments after that
2040+ let timeout_secs = 6 ;
2041+ let simulation = Simulation :: new (
2042+ SimulationCfg :: new (
2043+ Some ( timeout_secs) , // Run for 6 seconds
2044+ 1000 , // Expected payment size
2045+ 0.1 , // Activity multiplier
2046+ None , // No result writing
2047+ Some ( 42 ) , // Seed for determinism
2048+ ) ,
2049+ clients,
2050+ vec ! [ activity_1, activity_2] , // Use both activities
2051+ TaskTracker :: new ( ) ,
2052+ ) ;
2053+
2054+ // Run the simulation for the full timeout duration
2055+ let start = std:: time:: Instant :: now ( ) ;
2056+ let result = simulation. run ( ) . await ;
2057+ let elapsed = start. elapsed ( ) ;
2058+
2059+ // Verify the simulation ran correctly
2060+ assert ! ( result. is_ok( ) , "Simulation should end without error" ) ;
2061+
2062+ // Verify it ran for approximately the timeout duration
2063+ let margin = Duration :: from_secs ( 1 ) ;
2064+ assert ! (
2065+ elapsed >= Duration :: from_secs( 6 ) && elapsed <= Duration :: from_secs( 6 ) + margin,
2066+ "Simulation should have run for approximately {} seconds, but took {:?}" ,
2067+ timeout_secs,
2068+ elapsed
2069+ ) ;
2070+
2071+ // We expect at least 8 payments in total:
2072+ // - 3 from activity 1 (which completes its count)
2073+ // - At least 5 from activity 2 (1 per second for 6 seconds)
2074+ // The exact number may vary slightly due to timing, but should be at least 8
2075+ let total_payments = simulation. get_total_payments ( ) . await ;
2076+ assert ! (
2077+ total_payments >= 8 ,
2078+ "Expected at least 8 payments to be attempted, got {}" ,
2079+ total_payments
2080+ ) ;
2081+ }
2082+
2083+ #[ tokio:: test]
2084+ async fn test_shutdown_multiple_activities_with_error ( ) {
2085+ // Set up test nodes with node_1 that will produce a permanent error
2086+ let ( ( node_1, node_2) , mut clients) = setup_test_nodes ( ) ;
2087+
2088+ // Create mock for node_1 that will return a permanent error on send_payment
2089+ let mut mock_node_1 = MockLightningNode :: new ( ) ;
2090+ let node_1_clone = node_1. clone ( ) ;
2091+
2092+ // Set up basic expectations for node_1
2093+ mock_node_1. expect_get_info ( ) . return_const ( node_1. clone ( ) ) ;
2094+ mock_node_1
2095+ . expect_get_network ( )
2096+ . returning ( || Ok ( Network :: Regtest ) ) ;
2097+ mock_node_1
2098+ . expect_list_channels ( )
2099+ . returning ( || Ok ( vec ! [ 100_000 ] ) ) ;
2100+ mock_node_1
2101+ . expect_get_node_info ( )
2102+ . returning ( move |_| Ok ( node_1_clone. clone ( ) ) ) ;
2103+ mock_node_1. expect_track_payment ( ) . returning ( |_, _| {
2104+ Ok ( crate :: PaymentResult {
2105+ htlc_count : 1 ,
2106+ payment_outcome : crate :: PaymentOutcome :: Success ,
2107+ } )
2108+ } ) ;
2109+
2110+ // Set up node_1 to return a permanent error on send_payment
2111+ mock_node_1. expect_send_payment ( ) . returning ( |_, _| {
2112+ Err ( LightningError :: PermanentError (
2113+ "Simulated permanent error" . to_string ( ) ,
2114+ ) )
2115+ } ) ;
2116+
2117+ // Replace node_1 with our new mock
2118+ clients. insert ( node_1. pubkey , Arc :: new ( Mutex :: new ( mock_node_1) ) ) ;
2119+
2120+ // Define two activities
2121+ // Activity 1: From node_1 to node_2 - This will encounter the permanent error
2122+ let activity_1 = crate :: ActivityDefinition {
2123+ source : node_1. clone ( ) ,
2124+ destination : node_2. clone ( ) ,
2125+ start_secs : None ,
2126+ count : None , // No limit
2127+ interval_secs : crate :: ValueOrRange :: Value ( 1 ) , // 1 second interval
2128+ amount_msat : crate :: ValueOrRange :: Value ( 2000 ) , // 2000 msats
2129+ } ;
2130+
2131+ // Activity 2: From node_2 to node_1 - This would normally succeed
2132+ let activity_2 = crate :: ActivityDefinition {
2133+ source : node_2. clone ( ) ,
2134+ destination : node_1. clone ( ) ,
2135+ start_secs : Some ( 2 ) , // Start 2 seconds after the first activity
2136+ count : Some ( 10 ) , // 10 payments if no error
2137+ interval_secs : crate :: ValueOrRange :: Value ( 1 ) , // 1 second interval
2138+ amount_msat : crate :: ValueOrRange :: Value ( 3000 ) , // 3000 msats
2139+ } ;
2140+
2141+ // Create simulation with a long timeout that we don't expect to be reached
2142+ let simulation = Simulation :: new (
2143+ SimulationCfg :: new (
2144+ Some ( 30 ) , // 30 second timeout (shouldn't matter)
2145+ 1000 , // Expected payment size
2146+ 0.1 , // Activity multiplier
2147+ None , // No result writing
2148+ Some ( 42 ) , // Seed for determinism
2149+ ) ,
2150+ clients,
2151+ vec ! [ activity_1, activity_2] , // Use both activities
2152+ TaskTracker :: new ( ) ,
2153+ ) ;
2154+
2155+ // Run the simulation (should be interrupted by the error)
2156+ let start = std:: time:: Instant :: now ( ) ;
2157+ let _ = simulation. run ( ) . await ;
2158+ let elapsed = start. elapsed ( ) ;
2159+
2160+ // Check that simulation ran for a short time (less than 3 seconds)
2161+ assert ! (
2162+ elapsed < Duration :: from_secs( 3 ) ,
2163+ "Simulation should have shut down quickly after encountering the error, took {:?}" ,
2164+ elapsed
2165+ ) ;
2166+
2167+ // We expect no successful payments to be recorded since the first activity errors immediately
2168+ // and it should shut down the entire simulation
2169+ let total_payments = simulation. get_total_payments ( ) . await ;
2170+ assert_eq ! (
2171+ total_payments, 0 ,
2172+ "Expected no payments to be recorded, got {}" ,
2173+ total_payments
2174+ ) ;
2175+ }
20122176}
0 commit comments