diff --git a/contracts/room-contract/tests/common/mod.rs b/contracts/room-contract/tests/common/mod.rs index 5cf2374d..714af9cf 100644 --- a/contracts/room-contract/tests/common/mod.rs +++ b/contracts/room-contract/tests/common/mod.rs @@ -172,39 +172,66 @@ pub async fn deploy_room_contract( } pub async fn subscribe_to_contract(client: &mut WebApi, key: ContractKey) -> Result<()> { - println!("Starting subscribe_to_contract for key: {}", key); - - println!("Sending Subscribe request to WebSocket..."); - let send_result = client - .send(ClientRequest::ContractOp(ContractRequest::Subscribe { - key: *key.id(), // Subscribe uses ContractInstanceId - summary: None, - })) - .await; + subscribe_to_contract_with_retries(client, key, 1).await +} - match send_result { - Ok(()) => { - println!("Subscribe request sent successfully to WebSocket"); - } - Err(e) => { - println!("Failed to send Subscribe request: {}", e); - return Err(e.into()); +pub async fn subscribe_to_contract_with_retries( + client: &mut WebApi, + key: ContractKey, + max_attempts: u32, +) -> Result<()> { + let mut last_error = None; + + for attempt in 1..=max_attempts { + if attempt > 1 { + println!( + "[RETRY] Subscribe attempt {}/{} for key: {}", + attempt, max_attempts, key + ); + // Wait before retry to allow network to stabilize + tokio::time::sleep(Duration::from_secs(5)).await; + } else { + println!("Starting subscribe_to_contract for key: {}", key); } - } - println!("Now waiting for SubscribeResponse via WebSocket..."); - let wait_result = wait_for_subscribe_response(client, &key).await; + println!("Sending Subscribe request to WebSocket..."); + let send_result = client + .send(ClientRequest::ContractOp(ContractRequest::Subscribe { + key: *key.id(), // Subscribe uses ContractInstanceId + summary: None, + })) + .await; - match &wait_result { - Ok(()) => { - println!("wait_for_subscribe_response completed successfully"); + match send_result { + Ok(()) => { + println!("Subscribe request sent successfully to WebSocket"); + } + Err(e) => { + println!("Failed to send Subscribe request: {}", e); + last_error = Some(anyhow::anyhow!("Failed to send Subscribe request: {}", e)); + continue; + } } - Err(e) => { - println!("wait_for_subscribe_response failed: {}", e); + + println!("Now waiting for SubscribeResponse via WebSocket..."); + let wait_result = wait_for_subscribe_response(client, &key).await; + + match wait_result { + Ok(()) => { + println!("wait_for_subscribe_response completed successfully"); + return Ok(()); + } + Err(e) => { + println!( + "wait_for_subscribe_response failed (attempt {}/{}): {}", + attempt, max_attempts, e + ); + last_error = Some(e); + } } } - wait_result + Err(last_error.unwrap_or_else(|| anyhow::anyhow!("Subscribe failed after {} attempts", max_attempts))) } // Contract compilation constants diff --git a/contracts/room-contract/tests/integration_tests.rs b/contracts/room-contract/tests/integration_tests.rs index 1f841bb4..16f2aa26 100644 --- a/contracts/room-contract/tests/integration_tests.rs +++ b/contracts/room-contract/tests/integration_tests.rs @@ -5,7 +5,8 @@ mod common; use common::{ collect_river_node_diagnostics, connect_ws_with_retries, deploy_room_contract, get_all_room_states, river_states_equal, send_test_message, subscribe_to_contract, - update_room_state_delta, wait_for_update_response, RoomTestState, + subscribe_to_contract_with_retries, update_room_state_delta, wait_for_update_response, + RoomTestState, }; use freenet_scaffold::ComposableState; use freenet_stdlib::prelude::*; @@ -202,7 +203,9 @@ async fn test_invitation_message_propagation() -> TestResult { println!("\n Step 4: Bob accepts invitation and joins room"); - subscribe_to_contract(&mut _bob_client, contract_key).await + // Use retries for Bob's subscribe since it needs network routing + // which can be slower in CI environments + subscribe_to_contract_with_retries(&mut _bob_client, contract_key, 3).await .map_err(|e| format!("Bob subscribe failed: {}", e))?; let mut bob_clients = vec![&mut _bob_client];