Skip to content

Commit

Permalink
test: add coverage for all edge cases of the TracerBuilder (#1320)
Browse files Browse the repository at this point in the history
* feat: TracerBuilder test

* fix: comment and optional mockall

* fix: mockall on testing

* fix: error message

* feat: added test_tracer_builder_build_error

* fix: remove test_tracer_builder_with_transaction_hash_failure

* fix: lint

* fix: test

* fix: test

* fix: eth_provider import
  • Loading branch information
eugypalu authored Aug 9, 2024
1 parent 43186e9 commit 9e7b4b6
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 1 deletion.
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ dojo-test-utils = { git = 'https://github.com/dojoengine/dojo', tag = "v1.0.0-al
katana-primitives = { git = 'https://github.com/dojoengine/dojo', tag = "v1.0.0-alpha.2", default-features = false, features = [
"serde",
], optional = true }
mockall = { version = "0.13.0", default-features = false, optional = true }

[patch.crates-io]
starknet-core = { git = "https://github.com/kariy/starknet-rs", branch = "dojo-patch" }
Expand Down Expand Up @@ -178,6 +179,7 @@ testing = [
"tokio-stream",
"tokio-util",
"walkdir",
"mockall",
]
hive = []
arbitrary = ["rand", "dep:arbitrary"]
Expand Down
42 changes: 42 additions & 0 deletions src/test_utils/mock_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::providers::eth_provider::provider::{EthProviderResult, EthereumProvider};
use async_trait::async_trait;
use mockall::mock;
use reth_primitives::{Address, BlockId, BlockNumberOrTag, Bytes, B256, U256, U64};
use reth_rpc_types::{
txpool::TxpoolContent, Filter, FilterChanges, Header, SyncStatus, TransactionReceipt, TransactionRequest,
};

mock! {
#[derive(Clone, Debug)]
pub EthereumProviderStruct {}

#[async_trait]
impl EthereumProvider for EthereumProviderStruct {
async fn header(&self, block_id: &BlockId) -> EthProviderResult<Option<Header>>;
async fn block_number(&self) -> EthProviderResult<U64>;
async fn syncing(&self) -> EthProviderResult<SyncStatus>;
async fn chain_id(&self) -> EthProviderResult<Option<U64>>;
async fn block_by_hash(&self, hash: B256, full: bool) -> EthProviderResult<Option<reth_rpc_types::RichBlock>>;
async fn block_by_number(&self, number_or_tag: BlockNumberOrTag, full: bool) -> EthProviderResult<Option<reth_rpc_types::RichBlock>>;
async fn block_transaction_count_by_hash(&self, hash: B256) -> EthProviderResult<Option<U256>>;
async fn block_transaction_count_by_number(&self, number_or_tag: BlockNumberOrTag) -> EthProviderResult<Option<U256>>;
async fn transaction_by_hash(&self, hash: B256) -> EthProviderResult<Option<reth_rpc_types::Transaction>>;
async fn transaction_by_block_hash_and_index(&self, hash: B256, index: reth_rpc_types::Index) -> EthProviderResult<Option<reth_rpc_types::Transaction>>;
async fn transaction_by_block_number_and_index(&self, number_or_tag: BlockNumberOrTag, index: reth_rpc_types::Index) -> EthProviderResult<Option<reth_rpc_types::Transaction>>;
async fn transaction_receipt(&self, hash: B256) -> EthProviderResult<Option<TransactionReceipt>>;
async fn balance(&self, address: Address, block_id: Option<BlockId>) -> EthProviderResult<U256>;
async fn storage_at(&self, address: Address, index: reth_rpc_types::serde_helpers::JsonStorageKey, block_id: Option<BlockId>) -> EthProviderResult<B256>;
async fn transaction_count(&self, address: Address, block_id: Option<BlockId>) -> EthProviderResult<U256>;
async fn get_code(&self, address: Address, block_id: Option<BlockId>) -> EthProviderResult<Bytes>;
async fn get_logs(&self, filter: Filter) -> EthProviderResult<FilterChanges>;
async fn call(&self, request: TransactionRequest, block_id: Option<BlockId>, state_overrides: Option<reth_rpc_types::state::StateOverride>, block_overrides: Option<Box<reth_rpc_types::BlockOverrides>>) -> EthProviderResult<Bytes>;
async fn estimate_gas(&self, call: TransactionRequest, block_id: Option<BlockId>) -> EthProviderResult<U256>;
async fn fee_history(&self, block_count: U64, newest_block: BlockNumberOrTag, reward_percentiles: Option<Vec<f64>>) -> EthProviderResult<reth_rpc_types::FeeHistory>;
async fn send_raw_transaction(&self, transaction: Bytes) -> EthProviderResult<B256>;
async fn gas_price(&self) -> EthProviderResult<U256>;
async fn block_receipts(&self, block_id: Option<BlockId>) -> EthProviderResult<Option<Vec<TransactionReceipt>>>;
async fn block_transactions(&self, block_id: Option<BlockId>) -> EthProviderResult<Option<Vec<reth_rpc_types::Transaction>>>;
async fn txpool_transactions(&self) -> EthProviderResult<Vec<reth_rpc_types::Transaction>>;
async fn txpool_content(&self) -> EthProviderResult<TxpoolContent>;
}
}
1 change: 1 addition & 0 deletions src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod fixtures;
pub mod hive;
pub mod katana;
pub mod macros;
pub mod mock_provider;
pub mod mongo;
pub mod rpc;
pub mod tx_waiter;
111 changes: 110 additions & 1 deletion src/tracing/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl<P: EthereumProvider + Send + Sync + Clone> TracerBuilder<P, Floating> {

// we can't trace a pending transaction
if transaction.block_number.is_none() {
return Err(EthApiError::UnknownBlock(transaction_hash.into()));
return Err(EthApiError::TransactionNotFound(transaction_hash));
}

self.with_block_id(BlockId::Number(transaction.block_number.unwrap().into())).await
Expand Down Expand Up @@ -220,3 +220,112 @@ impl<P: EthereumProvider + Send + Sync + Clone> TracerBuilder<P, Pinned> {
env
}
}

// The following tests validates the behavior of the TracerBuilder when interacting with a mock Ethereum provider.
// Each test focuses on different scenarios where the TracerBuilder is expected to handle various errors correctly,
// such as unknown blocks, not found transactions, and invalid chain IDs.
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::mock_provider::MockEthereumProviderStruct;
use reth_primitives::U64;
use reth_rpc_types::Transaction;
use std::sync::Arc;
#[tokio::test]
async fn test_tracer_builder_block_failure_with_none_block_number() {
// Create a mock Ethereum provider
let mut mock_provider = MockEthereumProviderStruct::new();
// Expect the chain_id call to return 1
mock_provider.expect_chain_id().returning(|| Ok(Some(U64::from(1))));
// Expect the block_by_number call to return an error for an unknown block
mock_provider.expect_block_by_number().returning(|_, _| Ok(None));

// Create a TracerBuilder with the mock provider
let builder = TracerBuilder::new(Arc::new(&mock_provider)).await.unwrap();
// Attempt to use the builder with a specific block ID, expecting an error
let result = builder.block(BlockId::Number(1.into())).await;
// Check that the result is an UnknownBlock error
assert!(matches!(result, Err(EthApiError::UnknownBlock(_))));
}

#[tokio::test]
async fn test_tracer_builder_block_failure_with_none_block_hash() {
// Create a mock Ethereum provider
let mut mock_provider = MockEthereumProviderStruct::new();
// Expect the chain_id call to return 1
mock_provider.expect_chain_id().returning(|| Ok(Some(U64::from(1))));
// Expect the block_by_hash call to return an error for an unknown block
mock_provider.expect_block_by_hash().returning(|_, _| Ok(None));

// Create a TracerBuilder with the mock provider
let builder = TracerBuilder::new(Arc::new(&mock_provider)).await.unwrap();
// Attempt to use the builder with a specific block hash, expecting an error
let result = builder.block(BlockId::Hash(B256::repeat_byte(1).into())).await;
// Check that the result is an UnknownBlock error
assert!(matches!(result, Err(EthApiError::UnknownBlock(_))));
}

#[tokio::test]
async fn test_tracer_builder_with_transaction_not_found() {
// Create a mock Ethereum provider
let mut mock_provider = MockEthereumProviderStruct::new();
// Expect the chain_id call to return 1
mock_provider.expect_chain_id().returning(|| Ok(Some(U64::from(1))));
// Expect the transaction_by_hash call to return Ok(None) for not found transaction
mock_provider.expect_transaction_by_hash().returning(|_| Ok(None));

// Create a TracerBuilder with the mock provider
let builder = TracerBuilder::new(Arc::new(&mock_provider)).await.unwrap();
// Attempt to use the builder with a specific transaction hash, expecting an error
let result = builder.with_transaction_hash(B256::repeat_byte(0)).await;
// Check that the result is a TransactionNotFound error
assert!(matches!(result, Err(EthApiError::TransactionNotFound(_))));
}

#[tokio::test]
async fn test_tracer_builder_with_unknown_block() {
// Create a mock Ethereum provider
let mut mock_provider = MockEthereumProviderStruct::new();
// Expect the chain_id call to return 1
mock_provider.expect_chain_id().returning(|| Ok(Some(U64::from(1))));
// Expect the transaction_by_hash call to return a transaction with no block number
mock_provider
.expect_transaction_by_hash()
.returning(|_| Ok(Some(Transaction { block_number: None, ..Default::default() })));

// Create a TracerBuilder with the mock provider
let builder = TracerBuilder::new(Arc::new(&mock_provider)).await.unwrap();
// Attempt to use the builder with a specific transaction hash, expecting an error
let result = builder.with_transaction_hash(B256::repeat_byte(0)).await;
// Check that the result is an UnknownBlock error
assert!(matches!(result, Err(EthApiError::TransactionNotFound(_))));
}

#[tokio::test]
async fn test_tracer_builder_build_error() {
// Create a mock Ethereum provider
let mut mock_provider = MockEthereumProviderStruct::new();
// Expect the chain_id call to return 1
mock_provider.expect_chain_id().returning(|| Ok(Some(U64::from(1))));
// Expect the block_by_number call to return a block with non-full transactions
mock_provider.expect_block_by_number().returning(|_, _| {
Ok(Some(
Block {
transactions: BlockTransactions::Hashes(vec![]),
header: Header { hash: Some(B256::repeat_byte(1)), ..Default::default() },
..Default::default()
}
.into(),
))
});

// Create a TracerBuilder with the mock provider
let builder = TracerBuilder::new(Arc::new(&mock_provider)).await.unwrap();
// Attempt to use the builder with a specific block ID
let builder = builder.with_block_id(BlockId::Number(1.into())).await.unwrap();
// Attempt to build the tracer, expecting an error
let result = builder.build();
// Check that the result is an ExpectedFullTransactions error
assert!(matches!(result, Err(EthApiError::Transaction(TransactionError::ExpectedFullTransactions))));
}
}

0 comments on commit 9e7b4b6

Please sign in to comment.