Skip to content

Commit 21cba9f

Browse files
authored
Provider tests (#210)
1 parent a520889 commit 21cba9f

File tree

1 file changed

+241
-4
lines changed

1 file changed

+241
-4
lines changed

src/robust_provider/provider.rs

Lines changed: 241 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{fmt::Debug, time::Duration};
33
use alloy::{
44
eips::{BlockId, BlockNumberOrTag},
55
network::{Ethereum, Network},
6-
primitives::BlockHash,
6+
primitives::{BlockHash, BlockNumber},
77
providers::{Provider, RootProvider},
88
pubsub::Subscription,
99
rpc::types::{Filter, Log},
@@ -90,7 +90,7 @@ impl<N: Network> RobustProvider<N> {
9090
/// # Errors
9191
///
9292
/// See [retry errors](#retry-errors).
93-
pub async fn get_block_number(&self) -> Result<u64, Error> {
93+
pub async fn get_block_number(&self) -> Result<BlockNumber, Error> {
9494
info!("eth_getBlockNumber called");
9595
let result = self
9696
.retry_with_total_timeout(
@@ -115,7 +115,7 @@ impl<N: Network> RobustProvider<N> {
115115
/// # Errors
116116
///
117117
/// See [retry errors](#retry-errors).
118-
pub async fn get_block_number_by_id(&self, block_id: BlockId) -> Result<u64, Error> {
118+
pub async fn get_block_number_by_id(&self, block_id: BlockId) -> Result<BlockNumber, Error> {
119119
info!("get_block_number_by_id called");
120120
let result = self
121121
.retry_with_total_timeout(
@@ -326,10 +326,30 @@ mod tests {
326326
consensus::BlockHeader,
327327
providers::{ProviderBuilder, WsConnect, ext::AnvilApi},
328328
};
329-
use alloy_node_bindings::Anvil;
329+
use alloy_node_bindings::{Anvil, AnvilInstance};
330330
use std::sync::atomic::{AtomicUsize, Ordering};
331331
use tokio::time::sleep;
332332

333+
async fn setup_anvil() -> anyhow::Result<(AnvilInstance, RobustProvider, impl Provider)> {
334+
let anvil = Anvil::new().try_spawn()?;
335+
let alloy_provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
336+
337+
let robust = RobustProviderBuilder::new(alloy_provider.clone())
338+
.max_timeout(Duration::from_secs(5))
339+
.build()
340+
.await?;
341+
342+
Ok((anvil, robust, alloy_provider))
343+
}
344+
345+
async fn setup_anvil_with_blocks(
346+
num_blocks: u64,
347+
) -> anyhow::Result<(AnvilInstance, RobustProvider, impl Provider)> {
348+
let (anvil, robust, alloy_provider) = setup_anvil().await?;
349+
alloy_provider.anvil_mine(Some(num_blocks), None).await?;
350+
Ok((anvil, robust, alloy_provider))
351+
}
352+
333353
fn test_provider(timeout: u64, max_retries: usize, min_delay: u64) -> RobustProvider {
334354
RobustProvider {
335355
providers: vec![RootProvider::new_http("http://localhost:8545".parse().unwrap())],
@@ -535,4 +555,221 @@ mod tests {
535555

536556
Ok(())
537557
}
558+
559+
#[tokio::test]
560+
async fn test_get_block_by_number_succeeds() -> anyhow::Result<()> {
561+
let (_anvil, robust, alloy_provider) = setup_anvil_with_blocks(100).await?;
562+
563+
let tags = [
564+
BlockNumberOrTag::Number(50),
565+
BlockNumberOrTag::Latest,
566+
BlockNumberOrTag::Earliest,
567+
BlockNumberOrTag::Safe,
568+
BlockNumberOrTag::Finalized,
569+
];
570+
571+
for tag in tags {
572+
let robust_block = robust.get_block_by_number(tag).await?;
573+
let alloy_block =
574+
alloy_provider.get_block_by_number(tag).await?.expect("block should exist");
575+
576+
assert_eq!(robust_block.header.number, alloy_block.header.number);
577+
assert_eq!(robust_block.header.hash, alloy_block.header.hash);
578+
}
579+
580+
Ok(())
581+
}
582+
583+
#[tokio::test]
584+
async fn test_get_block_by_number_future_block_fails() -> anyhow::Result<()> {
585+
let (_anvil, robust, _alloy_provider) = setup_anvil().await?;
586+
587+
let future_block = 999_999;
588+
let result = robust.get_block_by_number(BlockNumberOrTag::Number(future_block)).await;
589+
590+
assert!(matches!(result, Err(Error::BlockNotFound(_))));
591+
592+
Ok(())
593+
}
594+
595+
#[tokio::test]
596+
async fn test_get_block_succeeds() -> anyhow::Result<()> {
597+
let (_anvil, robust, alloy_provider) = setup_anvil_with_blocks(100).await?;
598+
599+
let block_ids = [
600+
BlockId::number(50),
601+
BlockId::latest(),
602+
BlockId::earliest(),
603+
BlockId::safe(),
604+
BlockId::finalized(),
605+
];
606+
607+
for block_id in block_ids {
608+
let robust_block = robust.get_block(block_id).await?;
609+
let alloy_block =
610+
alloy_provider.get_block(block_id).await?.expect("block should exist");
611+
612+
assert_eq!(robust_block.header.number, alloy_block.header.number);
613+
assert_eq!(robust_block.header.hash, alloy_block.header.hash);
614+
}
615+
616+
// test block hash
617+
let block = alloy_provider
618+
.get_block_by_number(BlockNumberOrTag::Number(50))
619+
.await?
620+
.expect("block should exist");
621+
let block_hash = block.header.hash;
622+
let block_id = BlockId::hash(block_hash);
623+
let robust_block = robust.get_block(block_id).await?;
624+
assert_eq!(robust_block.header.hash, block_hash);
625+
assert_eq!(robust_block.header.number, 50);
626+
627+
Ok(())
628+
}
629+
630+
#[tokio::test]
631+
async fn test_get_block_fails() -> anyhow::Result<()> {
632+
let (_anvil, robust, _alloy_provider) = setup_anvil().await?;
633+
634+
// Future block number
635+
let result = robust.get_block(BlockId::number(999_999)).await;
636+
assert!(matches!(result, Err(Error::BlockNotFound(_))));
637+
638+
// Non-existent hash
639+
let result = robust.get_block(BlockId::hash(BlockHash::ZERO)).await;
640+
assert!(matches!(result, Err(Error::BlockNotFound(_))));
641+
642+
Ok(())
643+
}
644+
645+
#[tokio::test]
646+
async fn test_get_block_number_succeeds() -> anyhow::Result<()> {
647+
let (_anvil, robust, alloy_provider) = setup_anvil_with_blocks(100).await?;
648+
649+
let robust_block_num = robust.get_block_number().await?;
650+
let alloy_block_num = alloy_provider.get_block_number().await?;
651+
assert_eq!(robust_block_num, alloy_block_num);
652+
assert_eq!(robust_block_num, 100);
653+
654+
alloy_provider.anvil_mine(Some(10), None).await?;
655+
let new_block = robust.get_block_number().await?;
656+
assert_eq!(new_block, 110);
657+
658+
Ok(())
659+
}
660+
661+
#[tokio::test]
662+
async fn test_get_block_number_by_id_succeeds() -> anyhow::Result<()> {
663+
let (_anvil, robust, alloy_provider) = setup_anvil_with_blocks(100).await?;
664+
665+
let block_num = robust.get_block_number_by_id(BlockId::number(50)).await?;
666+
assert_eq!(block_num, 50);
667+
668+
let block = alloy_provider
669+
.get_block_by_number(BlockNumberOrTag::Number(50))
670+
.await?
671+
.expect("block should exist");
672+
let block_num = robust.get_block_number_by_id(BlockId::hash(block.header.hash)).await?;
673+
assert_eq!(block_num, 50);
674+
675+
let block_num = robust.get_block_number_by_id(BlockId::latest()).await?;
676+
assert_eq!(block_num, 100);
677+
678+
let block_num = robust.get_block_number_by_id(BlockId::earliest()).await?;
679+
assert_eq!(block_num, 0);
680+
681+
// Returns block number even if it doesnt 'exist' on chain
682+
let block_num = robust.get_block_number_by_id(BlockId::number(999_999)).await?;
683+
let alloy_block_num = alloy_provider
684+
.get_block_number_by_id(BlockId::number(999_999))
685+
.await?
686+
.expect("Should return block num");
687+
assert_eq!(alloy_block_num, block_num);
688+
assert_eq!(block_num, 999_999);
689+
690+
Ok(())
691+
}
692+
693+
#[tokio::test]
694+
async fn test_get_block_number_by_id_fails() -> anyhow::Result<()> {
695+
let (_anvil, robust, _alloy_provider) = setup_anvil().await?;
696+
697+
let result = robust.get_block_number_by_id(BlockId::hash(BlockHash::ZERO)).await;
698+
assert!(matches!(result, Err(Error::BlockNotFound(_))));
699+
700+
Ok(())
701+
}
702+
703+
#[tokio::test]
704+
async fn test_get_latest_confirmed_succeeds() -> anyhow::Result<()> {
705+
let (_anvil, robust, _alloy_provider) = setup_anvil_with_blocks(100).await?;
706+
707+
// With confirmations
708+
let confirmed_block = robust.get_latest_confirmed(10).await?;
709+
assert_eq!(confirmed_block, 90);
710+
711+
// Zero confirmations returns latest
712+
let confirmed_block = robust.get_latest_confirmed(0).await?;
713+
assert_eq!(confirmed_block, 100);
714+
715+
// Single confirmation
716+
let confirmed_block = robust.get_latest_confirmed(1).await?;
717+
assert_eq!(confirmed_block, 99);
718+
719+
// confirmations = latest - 1
720+
let confirmed_block = robust.get_latest_confirmed(99).await?;
721+
assert_eq!(confirmed_block, 1);
722+
723+
// confirmations = latest (should return 0)
724+
let confirmed_block = robust.get_latest_confirmed(100).await?;
725+
assert_eq!(confirmed_block, 0);
726+
727+
// confirmations = latest + 1 (saturates at zero)
728+
let confirmed_block = robust.get_latest_confirmed(101).await?;
729+
assert_eq!(confirmed_block, 0);
730+
731+
// Saturates at zero when confirmations > latest
732+
let confirmed_block = robust.get_latest_confirmed(200).await?;
733+
assert_eq!(confirmed_block, 0);
734+
735+
Ok(())
736+
}
737+
738+
#[tokio::test]
739+
async fn test_get_block_by_hash_succeeds() -> anyhow::Result<()> {
740+
let (_anvil, robust, alloy_provider) = setup_anvil_with_blocks(100).await?;
741+
742+
let block = alloy_provider
743+
.get_block_by_number(BlockNumberOrTag::Number(50))
744+
.await?
745+
.expect("block should exist");
746+
let block_hash = block.header.hash;
747+
748+
let robust_block = robust.get_block_by_hash(block_hash).await?;
749+
let alloy_block =
750+
alloy_provider.get_block_by_hash(block_hash).await?.expect("block should exist");
751+
assert_eq!(robust_block.header.hash, alloy_block.header.hash);
752+
assert_eq!(robust_block.header.number, alloy_block.header.number);
753+
754+
let genesis = alloy_provider
755+
.get_block_by_number(BlockNumberOrTag::Earliest)
756+
.await?
757+
.expect("genesis should exist");
758+
let genesis_hash = genesis.header.hash;
759+
let robust_block = robust.get_block_by_hash(genesis_hash).await?;
760+
assert_eq!(robust_block.header.number, 0);
761+
assert_eq!(robust_block.header.hash, genesis_hash);
762+
763+
Ok(())
764+
}
765+
766+
#[tokio::test]
767+
async fn test_get_block_by_hash_fails() -> anyhow::Result<()> {
768+
let (_anvil, robust, _alloy_provider) = setup_anvil().await?;
769+
770+
let result = robust.get_block_by_hash(BlockHash::ZERO).await;
771+
assert!(matches!(result, Err(Error::BlockNotFound(_))));
772+
773+
Ok(())
774+
}
538775
}

0 commit comments

Comments
 (0)