Skip to content

Commit c6a1335

Browse files
committed
test: cleanup and assert new fallback logic
1 parent db6cfaa commit c6a1335

File tree

1 file changed

+46
-46
lines changed

1 file changed

+46
-46
lines changed

src/robust_provider/provider.rs

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,6 @@ mod tests {
350350
Ok((anvil, provider.root().to_owned()))
351351
}
352352

353-
async fn mine_after_delay(provider: RootProvider, blocks: u64, delay: Duration) {
354-
sleep(delay).await;
355-
provider.anvil_mine(Some(blocks), None).await.unwrap();
356-
}
357-
358353
fn assert_backend_gone_or_timeout(err: Error) {
359354
match err {
360355
Error::Timeout => {}
@@ -418,20 +413,30 @@ mod tests {
418413
};
419414
}
420415

421-
async fn trigger_fb_and_assert_next_block(
416+
/// Waits for current provider to timeout, then mines on `next_provider` to trigger failover.
417+
async fn trigger_failover_with_delay(
422418
stream: &mut RobustSubscriptionStream<alloy::network::Ethereum>,
423-
fallback_provider: RootProvider,
419+
next_provider: RootProvider,
424420
expected_block: u64,
421+
extra_delay: Duration,
425422
) -> anyhow::Result<()> {
426423
let task = tokio::spawn(async move {
427-
sleep(SHORT_TIMEOUT + BUFFER_TIME).await;
428-
fallback_provider.anvil_mine(Some(1), None).await.unwrap();
424+
sleep(SHORT_TIMEOUT + extra_delay + BUFFER_TIME).await;
425+
next_provider.anvil_mine(Some(1), None).await.unwrap();
429426
});
430427
assert_next_block!(*stream, expected_block);
431428
task.await?;
432429
Ok(())
433430
}
434431

432+
async fn trigger_failover(
433+
stream: &mut RobustSubscriptionStream<alloy::network::Ethereum>,
434+
next_provider: RootProvider,
435+
expected_block: u64,
436+
) -> anyhow::Result<()> {
437+
trigger_failover_with_delay(stream, next_provider, expected_block, Duration::ZERO).await
438+
}
439+
435440
#[tokio::test]
436441
async fn test_retry_with_timeout_succeeds_on_first_attempt() {
437442
let provider = test_provider(100, 3, 10);
@@ -626,7 +631,7 @@ mod tests {
626631
assert_next_block!(stream, 2);
627632

628633
// After timeout, should failover to fallback provider
629-
trigger_fb_and_assert_next_block(&mut stream, fallback.clone(), 1).await?;
634+
trigger_failover(&mut stream, fallback.clone(), 1).await?;
630635

631636
fallback.anvil_mine(Some(1), None).await?;
632637
assert_next_block!(stream, 2);
@@ -649,65 +654,66 @@ mod tests {
649654
let subscription = robust.subscribe_blocks().await?;
650655
let mut stream = subscription.into_stream();
651656

652-
// Test: Start on primary
657+
// Start on primary
653658
primary.anvil_mine(Some(1), None).await?;
654659
assert_next_block!(stream, 1);
655660

656-
// Trigger failover to fallback
657-
trigger_fb_and_assert_next_block(&mut stream, fallback.clone(), 1).await?;
661+
// PP times out -> FP1
662+
trigger_failover(&mut stream, fallback.clone(), 1).await?;
658663

659664
fallback.anvil_mine(Some(1), None).await?;
660665
assert_next_block!(stream, 2);
661666

662-
// Wait for reconnect interval to trigger primary reconnection
663-
sleep(RECONNECT_INTERVAL + BUFFER_TIME).await;
667+
// FP1 times out -> PP (reconnect succeeds)
668+
trigger_failover(&mut stream, primary.clone(), 2).await?;
664669

665-
// Mine on primary after reconnection window
666-
let reconnect_task =
667-
tokio::spawn(mine_after_delay(primary.clone(), 1, Duration::from_millis(50)));
670+
// PP times out -> FP1 (fallback index was reset)
671+
trigger_failover(&mut stream, fallback.clone(), 3).await?;
668672

669-
// Verify: Successfully reconnected to primary (block 2 on primary chain)
670-
assert_next_block!(stream, 2);
671-
reconnect_task.await?;
672-
673-
assert_stream_finished!(stream);
673+
fallback.anvil_mine(Some(1), None).await?;
674+
assert_next_block!(stream, 4);
674675

675676
Ok(())
676677
}
677678

678679
#[tokio::test]
679680
async fn subscription_cycles_through_multiple_fallbacks() -> anyhow::Result<()> {
680-
// Setup: Three providers, will cycle through all
681-
let (_anvil_1, provider_1) = spawn_ws_anvil().await?;
682-
let (_anvil_2, provider_2) = spawn_ws_anvil().await?;
683-
let (_anvil_3, provider_3) = spawn_ws_anvil().await?;
684-
685-
let robust = RobustProviderBuilder::fragile(provider_1.clone())
686-
.fallback(provider_2.clone())
687-
.fallback(provider_3.clone())
681+
let (anvil_pp, primary) = spawn_ws_anvil().await?;
682+
let (_anvil_1, fb_1) = spawn_ws_anvil().await?;
683+
let (_anvil_2, fb_2) = spawn_ws_anvil().await?;
684+
685+
let robust = RobustProviderBuilder::fragile(primary.clone())
686+
.fallback(fb_1.clone())
687+
.fallback(fb_2.clone())
688688
.subscription_timeout(SHORT_TIMEOUT)
689+
.call_timeout(SHORT_TIMEOUT)
689690
.build()
690691
.await?;
691692

692693
let subscription = robust.subscribe_blocks().await?;
693694
let mut stream = subscription.into_stream();
694695

695-
// Test: Start on primary
696-
provider_1.anvil_mine(Some(1), None).await?;
696+
// Start on primary
697+
primary.anvil_mine(Some(1), None).await?;
697698
assert_next_block!(stream, 1);
698699

699-
// Failover to second provider
700-
trigger_fb_and_assert_next_block(&mut stream, provider_2.clone(), 1).await?;
700+
// Kill primary - all future PP reconnection attempts will fail
701+
drop(anvil_pp);
701702

702-
// Failover to third provider
703-
trigger_fb_and_assert_next_block(&mut stream, provider_3.clone(), 1).await?;
703+
// PP times out -> FP1
704+
trigger_failover(&mut stream, fb_1.clone(), 1).await?;
705+
706+
// FP1 times out -> tries PP (fails, takes call_timeout) -> FP2
707+
trigger_failover_with_delay(&mut stream, fb_2.clone(), 1, SHORT_TIMEOUT).await?;
708+
709+
fb_2.anvil_mine(Some(1), None).await?;
710+
assert_next_block!(stream, 2);
704711

705712
Ok(())
706713
}
707714

708715
#[tokio::test]
709716
async fn subscription_fails_with_no_fallbacks() -> anyhow::Result<()> {
710-
// Setup: Single provider with no fallbacks
711717
let (_anvil, provider) = spawn_ws_anvil().await?;
712718

713719
let robust = RobustProviderBuilder::fragile(provider.clone())
@@ -718,18 +724,12 @@ mod tests {
718724
let subscription = robust.subscribe_blocks().await?;
719725
let mut stream = subscription.into_stream();
720726

721-
// Test: Provider works initially
722727
provider.anvil_mine(Some(1), None).await?;
723728
assert_next_block!(stream, 1);
724729

725-
// Verify: No fallback available, should error
726-
let task = tokio::spawn(async move {
727-
sleep(SHORT_TIMEOUT + BUFFER_TIME).await;
728-
provider.anvil_mine(Some(1), None).await.unwrap();
729-
});
730+
// No fallback available - should error after timeout
731+
sleep(SHORT_TIMEOUT + BUFFER_TIME).await;
730732
let err = stream.next().await.unwrap().unwrap_err();
731-
task.await?;
732-
733733
assert_backend_gone_or_timeout(err);
734734

735735
Ok(())

0 commit comments

Comments
 (0)