From a3373cc73940007464716814dfab212987e2eb0c Mon Sep 17 00:00:00 2001 From: Nasr Date: Wed, 20 Aug 2025 15:32:55 +0100 Subject: [PATCH 1/9] test(fetcher): retry logic for chunked batched requests --- crates/indexer/fetcher/src/test.rs | 177 ++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 1 deletion(-) diff --git a/crates/indexer/fetcher/src/test.rs b/crates/indexer/fetcher/src/test.rs index 9a231467..40ed3b6b 100644 --- a/crates/indexer/fetcher/src/test.rs +++ b/crates/indexer/fetcher/src/test.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; use std::sync::Arc; - use cainome::cairo_serde::ContractAddress; use dojo_test_utils::migration::copy_spawn_and_move_db; use dojo_test_utils::setup::TestSetup; @@ -1828,3 +1827,179 @@ async fn test_fetch_comprehensive_multi_contract_spam_with_selective_indexing_an ); println!(" • Total events found: {}", total_events_found); } + +/// Integration test that verifies retry logic works when fetching blocks with real transactions. +/// This test uses Katana to create a realistic scenario where block fetching might fail initially +/// but eventually succeeds, ensuring all transactions are properly indexed. +#[tokio::test(flavor = "multi_thread")] +#[katana_runner::test(accounts = 3, db_dir = copy_spawn_and_move_db().as_str(), block_time = 1000)] +async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { + let setup = TestSetup::from_examples("/tmp", "../../../examples/"); + let metadata = setup.load_metadata("spawn-and-move", Profile::DEV); + + let account = sequencer.account(0); + let provider = Arc::new(JsonRpcClient::new(HttpTransport::new(sequencer.url()))); + + let world_local = metadata.load_dojo_world_local().unwrap(); + let world_address = world_local.deterministic_world_address().unwrap(); + let actions_address = world_local + .get_contract_address_local(compute_selector_from_names("ns", "actions")) + .unwrap(); + + let world = WorldContract::new(world_address, &account); + + // Grant writer permissions + let grant_writer_res = world + .grant_writer( + &compute_bytearray_hash("ns"), + &ContractAddress(actions_address), + ) + .send_with_cfg(&TxnConfig::init_wait()) + .await + .unwrap(); + + TransactionWaiter::new(grant_writer_res.transaction_hash, &provider) + .await + .unwrap(); + + // Mine a block to establish initial state + sequencer.dev_client().generate_block().await.unwrap(); + + let initial_block = provider.block_hash_and_number().await.unwrap(); + let initial_block_number = initial_block.block_number; + + // Create several transactions that will generate events + let mut transaction_hashes = Vec::new(); + + // Submit multiple spawn transactions to create events that need to be indexed + for i in 0..3 { + let spawn_tx = account + .execute_v3(vec![Call { + to: actions_address, + selector: get_selector_from_name("spawn").unwrap(), + calldata: vec![], + }]) + .send() + .await + .unwrap(); + transaction_hashes.push(spawn_tx.transaction_hash); + + let move_tx = account + .execute_v3(vec![Call { + to: actions_address, + selector: get_selector_from_name("move").unwrap(), + calldata: vec![Felt::from(i + 1)], // Different directions + }]) + .send() + .await + .unwrap(); + transaction_hashes.push(move_tx.transaction_hash); + } + + // Wait for transactions to be included and mine the block + for tx_hash in &transaction_hashes { + TransactionWaiter::new(*tx_hash, &provider).await.unwrap(); + } + sequencer.dev_client().generate_block().await.unwrap(); + + let target_block_number = initial_block_number + 1; + + // Create fetcher with small batch size to increase chances of retry scenarios + let fetcher = Fetcher::new( + provider.clone(), + FetcherConfig { + blocks_chunk_size: 1, + batch_chunk_size: 1, // Very small batch size to test individual requests + flags: FetchingFlags::TRANSACTIONS, + ..Default::default() + }, + ); + + // Set up cursor to fetch the block with our transactions + let cursors = HashMap::from([( + world_address, + ContractCursor { + contract_address: world_address, + last_pending_block_tx: None, + head: Some(initial_block_number), // Start from before our transactions + last_block_timestamp: None, + tps: None, + }, + )]); + + // Fetch the range - this should succeed even if there are temporary failures + // The retry logic in chunked_batch_requests should handle any transient errors + let fetch_result = fetcher.fetch_range(&cursors, target_block_number + 1).await; + + // Verify the fetch succeeded + assert!(fetch_result.is_ok(), "Fetch should succeed despite potential retry scenarios"); + let (range_result, updated_cursors) = fetch_result.unwrap(); + + // Verify the target block was fetched + assert!( + range_result.blocks.contains_key(&target_block_number), + "Target block {} should be present in results", + target_block_number + ); + + let target_block = &range_result.blocks[&target_block_number]; + + // Verify all our transactions are present in the block + for tx_hash in &transaction_hashes { + assert!( + target_block.transactions.contains_key(tx_hash), + "Transaction {:?} should be present in block {}", + tx_hash, + target_block_number + ); + } + + // Verify transactions have events (proving they were fully processed) + let mut total_events = 0; + for tx_hash in &transaction_hashes { + let transaction = &target_block.transactions[tx_hash]; + assert!( + !transaction.events.is_empty(), + "Transaction {:?} should have events", + tx_hash + ); + assert!( + transaction.transaction.is_some(), + "Transaction {:?} should have transaction data", + tx_hash + ); + total_events += transaction.events.len(); + } + + // Verify cursor tracking + assert!( + updated_cursors.cursor_transactions.contains_key(&world_address), + "World address should be tracked in cursor transactions" + ); + + let world_cursor_txs = &updated_cursors.cursor_transactions[&world_address]; + for tx_hash in &transaction_hashes { + assert!( + world_cursor_txs.contains(tx_hash), + "Transaction {:?} should be tracked in cursor transactions", + tx_hash + ); + } + + // Verify cursor state is updated correctly + let world_cursor = &updated_cursors.cursors[&world_address]; + assert_eq!( + world_cursor.head, + Some(target_block_number), + "World cursor head should be updated to target block" + ); + assert!( + world_cursor.last_block_timestamp.is_some(), + "World cursor should have timestamp" + ); + + println!("✅ Integration test passed!"); + println!("📊 Successfully indexed block {} with {} transactions and {} events", + target_block_number, transaction_hashes.len(), total_events); + println!("🔄 Retry logic handled any transient failures during block fetching"); +} From c6e7096b6bd7f9cf49722b2b52eef181145709ff Mon Sep 17 00:00:00 2001 From: Nasr Date: Wed, 20 Aug 2025 15:34:06 +0100 Subject: [PATCH 2/9] fmt --- crates/indexer/fetcher/src/test.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/crates/indexer/fetcher/src/test.rs b/crates/indexer/fetcher/src/test.rs index 40ed3b6b..200ceadc 100644 --- a/crates/indexer/fetcher/src/test.rs +++ b/crates/indexer/fetcher/src/test.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; -use std::sync::Arc; use cainome::cairo_serde::ContractAddress; use dojo_test_utils::migration::copy_spawn_and_move_db; use dojo_test_utils::setup::TestSetup; @@ -16,6 +14,8 @@ use starknet::macros::felt; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::{JsonRpcClient, Provider}; use starknet_crypto::Felt; +use std::collections::HashMap; +use std::sync::Arc; use torii_storage::proto::ContractCursor; use url::Url; @@ -1870,7 +1870,7 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { // Create several transactions that will generate events let mut transaction_hashes = Vec::new(); - + // Submit multiple spawn transactions to create events that need to be indexed for i in 0..3 { let spawn_tx = account @@ -1930,9 +1930,12 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { // Fetch the range - this should succeed even if there are temporary failures // The retry logic in chunked_batch_requests should handle any transient errors let fetch_result = fetcher.fetch_range(&cursors, target_block_number + 1).await; - + // Verify the fetch succeeded - assert!(fetch_result.is_ok(), "Fetch should succeed despite potential retry scenarios"); + assert!( + fetch_result.is_ok(), + "Fetch should succeed despite potential retry scenarios" + ); let (range_result, updated_cursors) = fetch_result.unwrap(); // Verify the target block was fetched @@ -1973,7 +1976,9 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { // Verify cursor tracking assert!( - updated_cursors.cursor_transactions.contains_key(&world_address), + updated_cursors + .cursor_transactions + .contains_key(&world_address), "World address should be tracked in cursor transactions" ); @@ -1999,7 +2004,11 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { ); println!("✅ Integration test passed!"); - println!("📊 Successfully indexed block {} with {} transactions and {} events", - target_block_number, transaction_hashes.len(), total_events); + println!( + "📊 Successfully indexed block {} with {} transactions and {} events", + target_block_number, + transaction_hashes.len(), + total_events + ); println!("🔄 Retry logic handled any transient failures during block fetching"); } From fbe3cc4124eb90166991e18fe7f4f251ef026e9f Mon Sep 17 00:00:00 2001 From: Nasr Date: Wed, 20 Aug 2025 16:11:44 +0100 Subject: [PATCH 3/9] t fix --- crates/indexer/fetcher/src/test.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/indexer/fetcher/src/test.rs b/crates/indexer/fetcher/src/test.rs index 200ceadc..4eb92f05 100644 --- a/crates/indexer/fetcher/src/test.rs +++ b/crates/indexer/fetcher/src/test.rs @@ -1832,7 +1832,7 @@ async fn test_fetch_comprehensive_multi_contract_spam_with_selective_indexing_an /// This test uses Katana to create a realistic scenario where block fetching might fail initially /// but eventually succeeds, ensuring all transactions are properly indexed. #[tokio::test(flavor = "multi_thread")] -#[katana_runner::test(accounts = 3, db_dir = copy_spawn_and_move_db().as_str(), block_time = 1000)] +#[katana_runner::test(accounts = 10, db_dir = copy_spawn_and_move_db().as_str(), block_time = 3600000)] async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { let setup = TestSetup::from_examples("/tmp", "../../../examples/"); let metadata = setup.load_metadata("spawn-and-move", Profile::DEV); @@ -1862,16 +1862,16 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { .await .unwrap(); - // Mine a block to establish initial state + // Mine a block sequencer.dev_client().generate_block().await.unwrap(); - let initial_block = provider.block_hash_and_number().await.unwrap(); - let initial_block_number = initial_block.block_number; + let latest_block = provider.block_hash_and_number().await.unwrap(); + let current_block_number = latest_block.block_number; // Create several transactions that will generate events let mut transaction_hashes = Vec::new(); - // Submit multiple spawn transactions to create events that need to be indexed + // Submit multiple spawn and move transactions to create events that need to be indexed for i in 0..3 { let spawn_tx = account .execute_v3(vec![Call { @@ -1902,9 +1902,9 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { } sequencer.dev_client().generate_block().await.unwrap(); - let target_block_number = initial_block_number + 1; + let target_block_number = current_block_number + 1; - // Create fetcher with small batch size to increase chances of retry scenarios + // Create fetcher with small batch size to test retry scenarios more effectively let fetcher = Fetcher::new( provider.clone(), FetcherConfig { @@ -1921,7 +1921,7 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { ContractCursor { contract_address: world_address, last_pending_block_tx: None, - head: Some(initial_block_number), // Start from before our transactions + head: Some(current_block_number), // Start from before our transactions last_block_timestamp: None, tps: None, }, From 73a4e165608b35c2c9fc46bc32589534af7f4073 Mon Sep 17 00:00:00 2001 From: Nasr Date: Wed, 20 Aug 2025 16:11:57 +0100 Subject: [PATCH 4/9] fmt --- crates/indexer/fetcher/src/test.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/indexer/fetcher/src/test.rs b/crates/indexer/fetcher/src/test.rs index 4eb92f05..7075a8e1 100644 --- a/crates/indexer/fetcher/src/test.rs +++ b/crates/indexer/fetcher/src/test.rs @@ -1896,10 +1896,7 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { transaction_hashes.push(move_tx.transaction_hash); } - // Wait for transactions to be included and mine the block - for tx_hash in &transaction_hashes { - TransactionWaiter::new(*tx_hash, &provider).await.unwrap(); - } + // Mine the block to include our transactions sequencer.dev_client().generate_block().await.unwrap(); let target_block_number = current_block_number + 1; From 95e4a5ac7384a3b6c03cba623d598b1306419447 Mon Sep 17 00:00:00 2001 From: Nasr Date: Wed, 20 Aug 2025 17:11:06 +0100 Subject: [PATCH 5/9] update test with mock --- crates/indexer/fetcher/src/test.rs | 393 +++++++++++++++++++++++++---- 1 file changed, 338 insertions(+), 55 deletions(-) diff --git a/crates/indexer/fetcher/src/test.rs b/crates/indexer/fetcher/src/test.rs index 7075a8e1..4d8f4606 100644 --- a/crates/indexer/fetcher/src/test.rs +++ b/crates/indexer/fetcher/src/test.rs @@ -12,15 +12,296 @@ use starknet::core::types::{BlockId, BlockWithReceipts, Call, MaybePreConfirmedB use starknet::core::utils::get_selector_from_name; use starknet::macros::felt; use starknet::providers::jsonrpc::HttpTransport; -use starknet::providers::{JsonRpcClient, Provider}; +use starknet::providers::{JsonRpcClient, Provider, ProviderError, ProviderRequestData, ProviderResponseData}; use starknet_crypto::Felt; use std::collections::HashMap; use std::sync::Arc; +use std::sync::atomic::{AtomicU32, Ordering}; +use async_trait::async_trait; use torii_storage::proto::ContractCursor; use url::Url; use crate::{Fetcher, FetcherConfig, FetchingFlags}; +/// Mock provider that fails batch requests for the first N attempts, then succeeds +#[derive(Debug, Clone)] +pub struct MockRetryProvider { + inner: Arc>, + failure_count: Arc, + max_failures: u32, + stats: Arc, +} + +#[derive(Debug, Default)] +pub struct RetryStats { + pub total_calls: AtomicU32, + pub failed_attempts: AtomicU32, + pub successful_attempts: AtomicU32, +} + +#[derive(Debug)] +pub struct RetryStatsSnapshot { + pub total_calls: u32, + pub failed_attempts: u32, + pub successful_attempts: u32, +} + +impl MockRetryProvider { + pub fn new(inner: Arc>, max_failures: u32) -> Self { + Self { + inner, + failure_count: Arc::new(AtomicU32::new(0)), + max_failures, + stats: Arc::new(RetryStats::default()), + } + } + + pub fn get_retry_stats(&self) -> RetryStatsSnapshot { + RetryStatsSnapshot { + total_calls: self.stats.total_calls.load(Ordering::SeqCst), + failed_attempts: self.stats.failed_attempts.load(Ordering::SeqCst), + successful_attempts: self.stats.successful_attempts.load(Ordering::SeqCst), + } + } +} + +#[async_trait] +impl Provider for MockRetryProvider { + async fn batch_requests(&self, requests: R) -> Result, ProviderError> + where + R: AsRef<[ProviderRequestData]> + Send + Sync, + { + self.stats.total_calls.fetch_add(1, Ordering::SeqCst); + + let current_failures = self.failure_count.load(Ordering::SeqCst); + if current_failures < self.max_failures { + // Simulate failure for first N attempts + self.failure_count.fetch_add(1, Ordering::SeqCst); + self.stats.failed_attempts.fetch_add(1, Ordering::SeqCst); + + println!(" 🔴 Mock provider simulating failure #{} (max: {})", current_failures + 1, self.max_failures); + return Err(ProviderError::RateLimited); + } + + // After max failures, delegate to real provider + self.stats.successful_attempts.fetch_add(1, Ordering::SeqCst); + println!(" 🟢 Mock provider delegating to real provider (attempt #{})", current_failures + 1); + self.inner.batch_requests(requests).await + } + + // Delegate all other methods to inner provider with correct generic signatures + async fn spec_version(&self) -> Result { + self.inner.spec_version().await + } + + async fn get_block_with_tx_hashes(&self, block_id: B) -> Result + where + B: AsRef + Send + Sync, + { + self.inner.get_block_with_tx_hashes(block_id).await + } + + async fn get_block_with_txs(&self, block_id: B) -> Result + where + B: AsRef + Send + Sync, + { + self.inner.get_block_with_txs(block_id).await + } + + async fn get_block_with_receipts(&self, block_id: B) -> Result + where + B: AsRef + Send + Sync, + { + self.inner.get_block_with_receipts(block_id).await + } + + async fn get_state_update(&self, block_id: B) -> Result + where + B: AsRef + Send + Sync, + { + self.inner.get_state_update(block_id).await + } + + async fn get_storage_at(&self, contract_address: A, key: K, block_id: B) -> Result + where + A: AsRef + Send + Sync, + K: AsRef + Send + Sync, + B: AsRef + Send + Sync, + { + self.inner.get_storage_at(contract_address, key, block_id).await + } + + async fn get_messages_status(&self, transaction_hash: starknet::core::types::Hash256) -> Result, ProviderError> { + self.inner.get_messages_status(transaction_hash).await + } + + async fn get_transaction_status(&self, transaction_hash: H) -> Result + where + H: AsRef + Send + Sync, + { + self.inner.get_transaction_status(transaction_hash).await + } + + async fn get_transaction_by_hash(&self, transaction_hash: H) -> Result + where + H: AsRef + Send + Sync, + { + self.inner.get_transaction_by_hash(transaction_hash).await + } + + async fn get_transaction_by_block_id_and_index(&self, block_id: B, index: u64) -> Result + where + B: AsRef + Send + Sync, + { + self.inner.get_transaction_by_block_id_and_index(block_id, index).await + } + + async fn get_transaction_receipt(&self, transaction_hash: H) -> Result + where + H: AsRef + Send + Sync, + { + self.inner.get_transaction_receipt(transaction_hash).await + } + + async fn get_class(&self, block_id: B, class_hash: H) -> Result + where + B: AsRef + Send + Sync, + H: AsRef + Send + Sync, + { + self.inner.get_class(block_id, class_hash).await + } + + async fn get_class_hash_at(&self, block_id: B, contract_address: A) -> Result + where + B: AsRef + Send + Sync, + A: AsRef + Send + Sync, + { + self.inner.get_class_hash_at(block_id, contract_address).await + } + + async fn get_class_at(&self, block_id: B, contract_address: A) -> Result + where + B: AsRef + Send + Sync, + A: AsRef + Send + Sync, + { + self.inner.get_class_at(block_id, contract_address).await + } + + async fn get_block_transaction_count(&self, block_id: B) -> Result + where + B: AsRef + Send + Sync, + { + self.inner.get_block_transaction_count(block_id).await + } + + async fn call(&self, request: R, block_id: B) -> Result, ProviderError> + where + R: AsRef + Send + Sync, + B: AsRef + Send + Sync, + { + self.inner.call(request, block_id).await + } + + async fn estimate_fee(&self, request: R, simulation_flags: S, block_id: B) -> Result, ProviderError> + where + R: AsRef<[starknet::core::types::BroadcastedTransaction]> + Send + Sync, + S: AsRef<[starknet::core::types::SimulationFlagForEstimateFee]> + Send + Sync, + B: AsRef + Send + Sync, + { + self.inner.estimate_fee(request, simulation_flags, block_id).await + } + + async fn estimate_message_fee(&self, message: M, block_id: B) -> Result + where + M: AsRef + Send + Sync, + B: AsRef + Send + Sync, + { + self.inner.estimate_message_fee(message, block_id).await + } + + async fn block_number(&self) -> Result { + self.inner.block_number().await + } + + async fn block_hash_and_number(&self) -> Result { + self.inner.block_hash_and_number().await + } + + async fn chain_id(&self) -> Result { + self.inner.chain_id().await + } + + async fn syncing(&self) -> Result { + self.inner.syncing().await + } + + async fn get_events(&self, filter: starknet::core::types::EventFilter, continuation_token: Option, chunk_size: u64) -> Result { + self.inner.get_events(filter, continuation_token, chunk_size).await + } + + async fn get_nonce(&self, block_id: B, contract_address: A) -> Result + where + B: AsRef + Send + Sync, + A: AsRef + Send + Sync, + { + self.inner.get_nonce(block_id, contract_address).await + } + + async fn get_storage_proof(&self, block_id: B, class_hashes: H, contract_addresses: A, contracts_storage_keys: K) -> Result + where + B: AsRef + Send + Sync, + H: AsRef<[starknet_crypto::Felt]> + Send + Sync, + A: AsRef<[starknet_crypto::Felt]> + Send + Sync, + K: AsRef<[starknet::core::types::ContractStorageKeys]> + Send + Sync, + { + self.inner.get_storage_proof(block_id, class_hashes, contract_addresses, contracts_storage_keys).await + } + + async fn add_invoke_transaction(&self, invoke_transaction: I) -> Result + where + I: AsRef + Send + Sync, + { + self.inner.add_invoke_transaction(invoke_transaction).await + } + + async fn add_declare_transaction(&self, declare_transaction: D) -> Result + where + D: AsRef + Send + Sync, + { + self.inner.add_declare_transaction(declare_transaction).await + } + + async fn add_deploy_account_transaction(&self, deploy_account_transaction: D) -> Result + where + D: AsRef + Send + Sync, + { + self.inner.add_deploy_account_transaction(deploy_account_transaction).await + } + + async fn trace_transaction(&self, transaction_hash: H) -> Result + where + H: AsRef + Send + Sync, + { + self.inner.trace_transaction(transaction_hash).await + } + + async fn simulate_transactions(&self, block_id: B, transactions: T, simulation_flags: S) -> Result, ProviderError> + where + B: AsRef + Send + Sync, + T: AsRef<[starknet::core::types::BroadcastedTransaction]> + Send + Sync, + S: AsRef<[starknet::core::types::SimulationFlag]> + Send + Sync, + { + self.inner.simulate_transactions(block_id, transactions, simulation_flags).await + } + + async fn trace_block_transactions(&self, block_id: B) -> Result, ProviderError> + where + B: AsRef + Send + Sync, + { + self.inner.trace_block_transactions(block_id).await + } +} + const CARTRIDGE_NODE_MAINNET: &str = "https://api.cartridge.gg/x/starknet/mainnet/rpc/v0_8"; const ETERNUM_ADDRESS: Felt = felt!("0x5c6d0020a9927edca9ddc984b97305439c0b32a1ec8d3f0eaf6291074cc9799"); @@ -1828,17 +2109,21 @@ async fn test_fetch_comprehensive_multi_contract_spam_with_selective_indexing_an println!(" • Total events found: {}", total_events_found); } + /// Integration test that verifies retry logic works when fetching blocks with real transactions. -/// This test uses Katana to create a realistic scenario where block fetching might fail initially -/// but eventually succeeds, ensuring all transactions are properly indexed. +/// This test: +/// 1. Mines a block with transactions using Katana +/// 2. Uses a mock provider that fails batch requests for the first 2 attempts +/// 3. Runs fetch_range and verifies the output is correct despite retries +/// 4. Checks all transactions are present in results #[tokio::test(flavor = "multi_thread")] #[katana_runner::test(accounts = 10, db_dir = copy_spawn_and_move_db().as_str(), block_time = 3600000)] -async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { +async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { let setup = TestSetup::from_examples("/tmp", "../../../examples/"); let metadata = setup.load_metadata("spawn-and-move", Profile::DEV); let account = sequencer.account(0); - let provider = Arc::new(JsonRpcClient::new(HttpTransport::new(sequencer.url()))); + let real_provider = Arc::new(JsonRpcClient::new(HttpTransport::new(sequencer.url()))); let world_local = metadata.load_dojo_world_local().unwrap(); let world_address = world_local.deterministic_world_address().unwrap(); @@ -1846,32 +2131,23 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { .get_contract_address_local(compute_selector_from_names("ns", "actions")) .unwrap(); + // Grant writer permissions to the actions contract let world = WorldContract::new(world_address, &account); - - // Grant writer permissions - let grant_writer_res = world - .grant_writer( - &compute_bytearray_hash("ns"), - &ContractAddress(actions_address), - ) - .send_with_cfg(&TxnConfig::init_wait()) - .await - .unwrap(); - - TransactionWaiter::new(grant_writer_res.transaction_hash, &provider) + let tx_hash = world + .grant_writer(&compute_bytearray_hash("dojo"), &ContractAddress(actions_address)) + .send() .await - .unwrap(); - - // Mine a block - sequencer.dev_client().generate_block().await.unwrap(); + .unwrap() + .transaction_hash; + TransactionWaiter::new(tx_hash, &real_provider).await.unwrap(); - let latest_block = provider.block_hash_and_number().await.unwrap(); - let current_block_number = latest_block.block_number; + let current_block_number = real_provider.block_number().await.unwrap(); + println!("Current block: {}", current_block_number); - // Create several transactions that will generate events + // Step 1: Mine a block with a few transactions from Katana let mut transaction_hashes = Vec::new(); - // Submit multiple spawn and move transactions to create events that need to be indexed + // Execute a few transactions to create events for i in 0..3 { let spawn_tx = account .execute_v3(vec![Call { @@ -1898,15 +2174,20 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { // Mine the block to include our transactions sequencer.dev_client().generate_block().await.unwrap(); - let target_block_number = current_block_number + 1; + + println!("✅ Step 1: Mined block {} with {} transactions", target_block_number, transaction_hashes.len()); + + // Step 2: Create mock provider that fails batch requests for the first 2 attempts + let mock_provider = MockRetryProvider::new(real_provider.clone(), 2); + println!("✅ Step 2: Created mock provider that will fail first 2 batch requests"); - // Create fetcher with small batch size to test retry scenarios more effectively + // Step 3: Create fetcher with mock provider and run fetch_range let fetcher = Fetcher::new( - provider.clone(), + mock_provider.clone(), FetcherConfig { blocks_chunk_size: 1, - batch_chunk_size: 1, // Very small batch size to test individual requests + batch_chunk_size: 1, // Small batch size to trigger batch_requests flags: FetchingFlags::TRANSACTIONS, ..Default::default() }, @@ -1924,18 +2205,32 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { }, )]); - // Fetch the range - this should succeed even if there are temporary failures - // The retry logic in chunked_batch_requests should handle any transient errors + println!("🔄 Step 3: Running fetch_range (expecting 2 failures then success)..."); let fetch_result = fetcher.fetch_range(&cursors, target_block_number + 1).await; - // Verify the fetch succeeded + // Verify the fetch succeeded despite the initial failures assert!( fetch_result.is_ok(), - "Fetch should succeed despite potential retry scenarios" + "Fetch should succeed after retry attempts: {:?}", + fetch_result.err() ); + let (range_result, updated_cursors) = fetch_result.unwrap(); - - // Verify the target block was fetched + + // Verify retry attempts were made + let retry_stats = mock_provider.get_retry_stats(); + println!("📊 Retry Statistics:"); + println!(" • Total batch_requests calls: {}", retry_stats.total_calls); + println!(" • Failed attempts: {}", retry_stats.failed_attempts); + println!(" • Successful attempts: {}", retry_stats.successful_attempts); + + assert!(retry_stats.failed_attempts >= 2, "Should have failed at least 2 times"); + assert!(retry_stats.successful_attempts > 0, "Should have succeeded eventually"); + assert_eq!(retry_stats.total_calls, retry_stats.failed_attempts + retry_stats.successful_attempts); + + println!("✅ Step 3: fetch_range succeeded after {} failed attempts", retry_stats.failed_attempts); + + // Step 4: Verify the output is correct and all transactions are present assert!( range_result.blocks.contains_key(&target_block_number), "Target block {} should be present in results", @@ -1943,7 +2238,7 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { ); let target_block = &range_result.blocks[&target_block_number]; - + // Verify all our transactions are present in the block for tx_hash in &transaction_hashes { assert!( @@ -1972,13 +2267,6 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { } // Verify cursor tracking - assert!( - updated_cursors - .cursor_transactions - .contains_key(&world_address), - "World address should be tracked in cursor transactions" - ); - let world_cursor_txs = &updated_cursors.cursor_transactions[&world_address]; for tx_hash in &transaction_hashes { assert!( @@ -1995,17 +2283,12 @@ async fn test_fetch_range_with_retry_logic_integration(sequencer: &RunnerCtx) { Some(target_block_number), "World cursor head should be updated to target block" ); - assert!( - world_cursor.last_block_timestamp.is_some(), - "World cursor should have timestamp" - ); - println!("✅ Integration test passed!"); - println!( - "📊 Successfully indexed block {} with {} transactions and {} events", - target_block_number, - transaction_hashes.len(), - total_events - ); - println!("🔄 Retry logic handled any transient failures during block fetching"); + println!("✅ Step 4: All {} transactions and {} events verified in results", + transaction_hashes.len(), total_events); + + println!("🎉 Retry logic test completed successfully!"); + println!(" • Mock provider failed {} times as expected", retry_stats.failed_attempts); + println!(" • fetch_range eventually succeeded and returned correct data"); + println!(" • All {} transactions properly indexed", transaction_hashes.len()); } From 927166dcb58f99c79534f9bea9680dd46d3c3c22 Mon Sep 17 00:00:00 2001 From: Nasr Date: Wed, 20 Aug 2025 17:12:01 +0100 Subject: [PATCH 6/9] fmt --- crates/indexer/fetcher/src/test.rs | 299 ++++++++++++++++++++++------- 1 file changed, 233 insertions(+), 66 deletions(-) diff --git a/crates/indexer/fetcher/src/test.rs b/crates/indexer/fetcher/src/test.rs index 4d8f4606..4c77937c 100644 --- a/crates/indexer/fetcher/src/test.rs +++ b/crates/indexer/fetcher/src/test.rs @@ -1,3 +1,4 @@ +use async_trait::async_trait; use cainome::cairo_serde::ContractAddress; use dojo_test_utils::migration::copy_spawn_and_move_db; use dojo_test_utils::setup::TestSetup; @@ -12,12 +13,13 @@ use starknet::core::types::{BlockId, BlockWithReceipts, Call, MaybePreConfirmedB use starknet::core::utils::get_selector_from_name; use starknet::macros::felt; use starknet::providers::jsonrpc::HttpTransport; -use starknet::providers::{JsonRpcClient, Provider, ProviderError, ProviderRequestData, ProviderResponseData}; +use starknet::providers::{ + JsonRpcClient, Provider, ProviderError, ProviderRequestData, ProviderResponseData, +}; use starknet_crypto::Felt; use std::collections::HashMap; -use std::sync::Arc; use std::sync::atomic::{AtomicU32, Ordering}; -use async_trait::async_trait; +use std::sync::Arc; use torii_storage::proto::ContractCursor; use url::Url; @@ -67,25 +69,37 @@ impl MockRetryProvider { #[async_trait] impl Provider for MockRetryProvider { - async fn batch_requests(&self, requests: R) -> Result, ProviderError> + async fn batch_requests( + &self, + requests: R, + ) -> Result, ProviderError> where R: AsRef<[ProviderRequestData]> + Send + Sync, { self.stats.total_calls.fetch_add(1, Ordering::SeqCst); - + let current_failures = self.failure_count.load(Ordering::SeqCst); if current_failures < self.max_failures { // Simulate failure for first N attempts self.failure_count.fetch_add(1, Ordering::SeqCst); self.stats.failed_attempts.fetch_add(1, Ordering::SeqCst); - - println!(" 🔴 Mock provider simulating failure #{} (max: {})", current_failures + 1, self.max_failures); + + println!( + " 🔴 Mock provider simulating failure #{} (max: {})", + current_failures + 1, + self.max_failures + ); return Err(ProviderError::RateLimited); } - + // After max failures, delegate to real provider - self.stats.successful_attempts.fetch_add(1, Ordering::SeqCst); - println!(" 🟢 Mock provider delegating to real provider (attempt #{})", current_failures + 1); + self.stats + .successful_attempts + .fetch_add(1, Ordering::SeqCst); + println!( + " 🟢 Mock provider delegating to real provider (attempt #{})", + current_failures + 1 + ); self.inner.batch_requests(requests).await } @@ -94,76 +108,117 @@ impl Provider for MockRetryProvider { self.inner.spec_version().await } - async fn get_block_with_tx_hashes(&self, block_id: B) -> Result + async fn get_block_with_tx_hashes( + &self, + block_id: B, + ) -> Result where B: AsRef + Send + Sync, { self.inner.get_block_with_tx_hashes(block_id).await } - async fn get_block_with_txs(&self, block_id: B) -> Result + async fn get_block_with_txs( + &self, + block_id: B, + ) -> Result where B: AsRef + Send + Sync, { self.inner.get_block_with_txs(block_id).await } - async fn get_block_with_receipts(&self, block_id: B) -> Result + async fn get_block_with_receipts( + &self, + block_id: B, + ) -> Result where B: AsRef + Send + Sync, { self.inner.get_block_with_receipts(block_id).await } - async fn get_state_update(&self, block_id: B) -> Result + async fn get_state_update( + &self, + block_id: B, + ) -> Result where B: AsRef + Send + Sync, { self.inner.get_state_update(block_id).await } - async fn get_storage_at(&self, contract_address: A, key: K, block_id: B) -> Result + async fn get_storage_at( + &self, + contract_address: A, + key: K, + block_id: B, + ) -> Result where A: AsRef + Send + Sync, K: AsRef + Send + Sync, B: AsRef + Send + Sync, { - self.inner.get_storage_at(contract_address, key, block_id).await + self.inner + .get_storage_at(contract_address, key, block_id) + .await } - async fn get_messages_status(&self, transaction_hash: starknet::core::types::Hash256) -> Result, ProviderError> { + async fn get_messages_status( + &self, + transaction_hash: starknet::core::types::Hash256, + ) -> Result, ProviderError> { self.inner.get_messages_status(transaction_hash).await } - async fn get_transaction_status(&self, transaction_hash: H) -> Result + async fn get_transaction_status( + &self, + transaction_hash: H, + ) -> Result where H: AsRef + Send + Sync, { self.inner.get_transaction_status(transaction_hash).await } - async fn get_transaction_by_hash(&self, transaction_hash: H) -> Result + async fn get_transaction_by_hash( + &self, + transaction_hash: H, + ) -> Result where H: AsRef + Send + Sync, { self.inner.get_transaction_by_hash(transaction_hash).await } - async fn get_transaction_by_block_id_and_index(&self, block_id: B, index: u64) -> Result + async fn get_transaction_by_block_id_and_index( + &self, + block_id: B, + index: u64, + ) -> Result where B: AsRef + Send + Sync, { - self.inner.get_transaction_by_block_id_and_index(block_id, index).await + self.inner + .get_transaction_by_block_id_and_index(block_id, index) + .await } - async fn get_transaction_receipt(&self, transaction_hash: H) -> Result + async fn get_transaction_receipt( + &self, + transaction_hash: H, + ) -> Result where H: AsRef + Send + Sync, { self.inner.get_transaction_receipt(transaction_hash).await } - async fn get_class(&self, block_id: B, class_hash: H) -> Result + async fn get_class( + &self, + block_id: B, + class_hash: H, + ) -> Result where B: AsRef + Send + Sync, H: AsRef + Send + Sync, @@ -171,15 +226,25 @@ impl Provider for MockRetryProvider { self.inner.get_class(block_id, class_hash).await } - async fn get_class_hash_at(&self, block_id: B, contract_address: A) -> Result + async fn get_class_hash_at( + &self, + block_id: B, + contract_address: A, + ) -> Result where B: AsRef + Send + Sync, A: AsRef + Send + Sync, { - self.inner.get_class_hash_at(block_id, contract_address).await + self.inner + .get_class_hash_at(block_id, contract_address) + .await } - async fn get_class_at(&self, block_id: B, contract_address: A) -> Result + async fn get_class_at( + &self, + block_id: B, + contract_address: A, + ) -> Result where B: AsRef + Send + Sync, A: AsRef + Send + Sync, @@ -194,7 +259,11 @@ impl Provider for MockRetryProvider { self.inner.get_block_transaction_count(block_id).await } - async fn call(&self, request: R, block_id: B) -> Result, ProviderError> + async fn call( + &self, + request: R, + block_id: B, + ) -> Result, ProviderError> where R: AsRef + Send + Sync, B: AsRef + Send + Sync, @@ -202,16 +271,27 @@ impl Provider for MockRetryProvider { self.inner.call(request, block_id).await } - async fn estimate_fee(&self, request: R, simulation_flags: S, block_id: B) -> Result, ProviderError> + async fn estimate_fee( + &self, + request: R, + simulation_flags: S, + block_id: B, + ) -> Result, ProviderError> where R: AsRef<[starknet::core::types::BroadcastedTransaction]> + Send + Sync, S: AsRef<[starknet::core::types::SimulationFlagForEstimateFee]> + Send + Sync, B: AsRef + Send + Sync, { - self.inner.estimate_fee(request, simulation_flags, block_id).await + self.inner + .estimate_fee(request, simulation_flags, block_id) + .await } - async fn estimate_message_fee(&self, message: M, block_id: B) -> Result + async fn estimate_message_fee( + &self, + message: M, + block_id: B, + ) -> Result where M: AsRef + Send + Sync, B: AsRef + Send + Sync, @@ -223,7 +303,9 @@ impl Provider for MockRetryProvider { self.inner.block_number().await } - async fn block_hash_and_number(&self) -> Result { + async fn block_hash_and_number( + &self, + ) -> Result { self.inner.block_hash_and_number().await } @@ -235,11 +317,22 @@ impl Provider for MockRetryProvider { self.inner.syncing().await } - async fn get_events(&self, filter: starknet::core::types::EventFilter, continuation_token: Option, chunk_size: u64) -> Result { - self.inner.get_events(filter, continuation_token, chunk_size).await + async fn get_events( + &self, + filter: starknet::core::types::EventFilter, + continuation_token: Option, + chunk_size: u64, + ) -> Result { + self.inner + .get_events(filter, continuation_token, chunk_size) + .await } - async fn get_nonce(&self, block_id: B, contract_address: A) -> Result + async fn get_nonce( + &self, + block_id: B, + contract_address: A, + ) -> Result where B: AsRef + Send + Sync, A: AsRef + Send + Sync, @@ -247,54 +340,93 @@ impl Provider for MockRetryProvider { self.inner.get_nonce(block_id, contract_address).await } - async fn get_storage_proof(&self, block_id: B, class_hashes: H, contract_addresses: A, contracts_storage_keys: K) -> Result + async fn get_storage_proof( + &self, + block_id: B, + class_hashes: H, + contract_addresses: A, + contracts_storage_keys: K, + ) -> Result where B: AsRef + Send + Sync, H: AsRef<[starknet_crypto::Felt]> + Send + Sync, A: AsRef<[starknet_crypto::Felt]> + Send + Sync, K: AsRef<[starknet::core::types::ContractStorageKeys]> + Send + Sync, { - self.inner.get_storage_proof(block_id, class_hashes, contract_addresses, contracts_storage_keys).await + self.inner + .get_storage_proof( + block_id, + class_hashes, + contract_addresses, + contracts_storage_keys, + ) + .await } - async fn add_invoke_transaction(&self, invoke_transaction: I) -> Result + async fn add_invoke_transaction( + &self, + invoke_transaction: I, + ) -> Result where I: AsRef + Send + Sync, { self.inner.add_invoke_transaction(invoke_transaction).await } - async fn add_declare_transaction(&self, declare_transaction: D) -> Result + async fn add_declare_transaction( + &self, + declare_transaction: D, + ) -> Result where D: AsRef + Send + Sync, { - self.inner.add_declare_transaction(declare_transaction).await + self.inner + .add_declare_transaction(declare_transaction) + .await } - async fn add_deploy_account_transaction(&self, deploy_account_transaction: D) -> Result + async fn add_deploy_account_transaction( + &self, + deploy_account_transaction: D, + ) -> Result where D: AsRef + Send + Sync, { - self.inner.add_deploy_account_transaction(deploy_account_transaction).await + self.inner + .add_deploy_account_transaction(deploy_account_transaction) + .await } - async fn trace_transaction(&self, transaction_hash: H) -> Result + async fn trace_transaction( + &self, + transaction_hash: H, + ) -> Result where H: AsRef + Send + Sync, { self.inner.trace_transaction(transaction_hash).await } - async fn simulate_transactions(&self, block_id: B, transactions: T, simulation_flags: S) -> Result, ProviderError> + async fn simulate_transactions( + &self, + block_id: B, + transactions: T, + simulation_flags: S, + ) -> Result, ProviderError> where B: AsRef + Send + Sync, T: AsRef<[starknet::core::types::BroadcastedTransaction]> + Send + Sync, S: AsRef<[starknet::core::types::SimulationFlag]> + Send + Sync, { - self.inner.simulate_transactions(block_id, transactions, simulation_flags).await + self.inner + .simulate_transactions(block_id, transactions, simulation_flags) + .await } - async fn trace_block_transactions(&self, block_id: B) -> Result, ProviderError> + async fn trace_block_transactions( + &self, + block_id: B, + ) -> Result, ProviderError> where B: AsRef + Send + Sync, { @@ -2109,7 +2241,6 @@ async fn test_fetch_comprehensive_multi_contract_spam_with_selective_indexing_an println!(" • Total events found: {}", total_events_found); } - /// Integration test that verifies retry logic works when fetching blocks with real transactions. /// This test: /// 1. Mines a block with transactions using Katana @@ -2134,12 +2265,17 @@ async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { // Grant writer permissions to the actions contract let world = WorldContract::new(world_address, &account); let tx_hash = world - .grant_writer(&compute_bytearray_hash("dojo"), &ContractAddress(actions_address)) + .grant_writer( + &compute_bytearray_hash("dojo"), + &ContractAddress(actions_address), + ) .send() .await .unwrap() .transaction_hash; - TransactionWaiter::new(tx_hash, &real_provider).await.unwrap(); + TransactionWaiter::new(tx_hash, &real_provider) + .await + .unwrap(); let current_block_number = real_provider.block_number().await.unwrap(); println!("Current block: {}", current_block_number); @@ -2175,8 +2311,12 @@ async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { // Mine the block to include our transactions sequencer.dev_client().generate_block().await.unwrap(); let target_block_number = current_block_number + 1; - - println!("✅ Step 1: Mined block {} with {} transactions", target_block_number, transaction_hashes.len()); + + println!( + "✅ Step 1: Mined block {} with {} transactions", + target_block_number, + transaction_hashes.len() + ); // Step 2: Create mock provider that fails batch requests for the first 2 attempts let mock_provider = MockRetryProvider::new(real_provider.clone(), 2); @@ -2214,21 +2354,39 @@ async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { "Fetch should succeed after retry attempts: {:?}", fetch_result.err() ); - + let (range_result, updated_cursors) = fetch_result.unwrap(); - + // Verify retry attempts were made let retry_stats = mock_provider.get_retry_stats(); println!("📊 Retry Statistics:"); - println!(" • Total batch_requests calls: {}", retry_stats.total_calls); + println!( + " • Total batch_requests calls: {}", + retry_stats.total_calls + ); println!(" • Failed attempts: {}", retry_stats.failed_attempts); - println!(" • Successful attempts: {}", retry_stats.successful_attempts); - - assert!(retry_stats.failed_attempts >= 2, "Should have failed at least 2 times"); - assert!(retry_stats.successful_attempts > 0, "Should have succeeded eventually"); - assert_eq!(retry_stats.total_calls, retry_stats.failed_attempts + retry_stats.successful_attempts); - - println!("✅ Step 3: fetch_range succeeded after {} failed attempts", retry_stats.failed_attempts); + println!( + " • Successful attempts: {}", + retry_stats.successful_attempts + ); + + assert!( + retry_stats.failed_attempts >= 2, + "Should have failed at least 2 times" + ); + assert!( + retry_stats.successful_attempts > 0, + "Should have succeeded eventually" + ); + assert_eq!( + retry_stats.total_calls, + retry_stats.failed_attempts + retry_stats.successful_attempts + ); + + println!( + "✅ Step 3: fetch_range succeeded after {} failed attempts", + retry_stats.failed_attempts + ); // Step 4: Verify the output is correct and all transactions are present assert!( @@ -2238,7 +2396,7 @@ async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { ); let target_block = &range_result.blocks[&target_block_number]; - + // Verify all our transactions are present in the block for tx_hash in &transaction_hashes { assert!( @@ -2284,11 +2442,20 @@ async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { "World cursor head should be updated to target block" ); - println!("✅ Step 4: All {} transactions and {} events verified in results", - transaction_hashes.len(), total_events); - + println!( + "✅ Step 4: All {} transactions and {} events verified in results", + transaction_hashes.len(), + total_events + ); + println!("🎉 Retry logic test completed successfully!"); - println!(" • Mock provider failed {} times as expected", retry_stats.failed_attempts); + println!( + " • Mock provider failed {} times as expected", + retry_stats.failed_attempts + ); println!(" • fetch_range eventually succeeded and returned correct data"); - println!(" • All {} transactions properly indexed", transaction_hashes.len()); + println!( + " • All {} transactions properly indexed", + transaction_hashes.len() + ); } From 400cec31812d97f63e53686c2e3da6b4bb58fa82 Mon Sep 17 00:00:00 2001 From: Nasr Date: Wed, 20 Aug 2025 18:37:55 +0100 Subject: [PATCH 7/9] fix namespace --- crates/indexer/fetcher/src/test.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/indexer/fetcher/src/test.rs b/crates/indexer/fetcher/src/test.rs index 4c77937c..58291566 100644 --- a/crates/indexer/fetcher/src/test.rs +++ b/crates/indexer/fetcher/src/test.rs @@ -2264,16 +2264,16 @@ async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { // Grant writer permissions to the actions contract let world = WorldContract::new(world_address, &account); - let tx_hash = world + let grant_writer_res = world .grant_writer( - &compute_bytearray_hash("dojo"), + &compute_bytearray_hash("ns"), &ContractAddress(actions_address), ) - .send() + .send_with_cfg(&TxnConfig::init_wait()) .await - .unwrap() - .transaction_hash; - TransactionWaiter::new(tx_hash, &real_provider) + .unwrap(); + + TransactionWaiter::new(grant_writer_res.transaction_hash, &real_provider) .await .unwrap(); From 593891ee5f2d184b02afccee38f7183beb09f3fb Mon Sep 17 00:00:00 2001 From: Nasr Date: Wed, 20 Aug 2025 22:29:48 +0100 Subject: [PATCH 8/9] fix block head --- crates/indexer/fetcher/src/test.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/indexer/fetcher/src/test.rs b/crates/indexer/fetcher/src/test.rs index 58291566..49b7ab52 100644 --- a/crates/indexer/fetcher/src/test.rs +++ b/crates/indexer/fetcher/src/test.rs @@ -2310,7 +2310,10 @@ async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { // Mine the block to include our transactions sequencer.dev_client().generate_block().await.unwrap(); - let target_block_number = current_block_number + 1; + + // Get the actual latest block number after mining + let latest_block = real_provider.block_hash_and_number().await.unwrap(); + let target_block_number = latest_block.block_number; println!( "✅ Step 1: Mined block {} with {} transactions", @@ -2339,13 +2342,18 @@ async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { ContractCursor { contract_address: world_address, last_pending_block_tx: None, - head: Some(current_block_number), // Start from before our transactions + head: Some(target_block_number - 1), // Start from before our target transactions last_block_timestamp: None, tps: None, }, )]); println!("🔄 Step 3: Running fetch_range (expecting 2 failures then success)..."); + println!( + " Fetching from block {} to {}", + target_block_number - 1, + target_block_number + 1 + ); let fetch_result = fetcher.fetch_range(&cursors, target_block_number + 1).await; // Verify the fetch succeeded despite the initial failures From 6cf462a4bb599121b27f0236c0b3cc31c15bd3aa Mon Sep 17 00:00:00 2001 From: Nasr Date: Wed, 20 Aug 2025 23:17:39 +0100 Subject: [PATCH 9/9] f --- crates/indexer/fetcher/src/test.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/indexer/fetcher/src/test.rs b/crates/indexer/fetcher/src/test.rs index 49b7ab52..22a84ed6 100644 --- a/crates/indexer/fetcher/src/test.rs +++ b/crates/indexer/fetcher/src/test.rs @@ -2349,12 +2349,8 @@ async fn test_fetch_range_with_retry_logic(sequencer: &RunnerCtx) { )]); println!("🔄 Step 3: Running fetch_range (expecting 2 failures then success)..."); - println!( - " Fetching from block {} to {}", - target_block_number - 1, - target_block_number + 1 - ); - let fetch_result = fetcher.fetch_range(&cursors, target_block_number + 1).await; + println!(" Fetching up to block {}", target_block_number); + let fetch_result = fetcher.fetch_range(&cursors, target_block_number).await; // Verify the fetch succeeded despite the initial failures assert!(