@@ -1939,7 +1939,7 @@ mod tests {
1939
1939
}
1940
1940
1941
1941
#[ tokio:: test]
1942
- async fn test_manual_shutdown ( ) {
1942
+ async fn test_shutdown_manual ( ) {
1943
1943
// Set up test nodes
1944
1944
let ( ( node_1, node_2) , clients) = setup_test_nodes ( ) ;
1945
1945
@@ -2082,4 +2082,168 @@ mod tests {
2082
2082
total_payments
2083
2083
) ;
2084
2084
}
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
+ }
2085
2249
}
0 commit comments