diff --git a/crates/services/producer/src/block_producer.rs b/crates/services/producer/src/block_producer.rs index 8f61a69e5d9..96903b6457a 100644 --- a/crates/services/producer/src/block_producer.rs +++ b/crates/services/producer/src/block_producer.rs @@ -159,7 +159,7 @@ where height, previous_block: latest_height, } - .into()) + .into()); } let maybe_mint_tx = transactions_source.pop(); diff --git a/crates/services/producer/src/block_producer/tests.rs b/crates/services/producer/src/block_producer/tests.rs index c70c66722ea..b2c9a95d0cd 100644 --- a/crates/services/producer/src/block_producer/tests.rs +++ b/crates/services/producer/src/block_producer/tests.rs @@ -12,7 +12,6 @@ use crate::{ }, }, mocks::{ - FailingMockExecutor, MockDb, MockExecutor, MockExecutorWithCapture, @@ -518,9 +517,12 @@ mod produce_and_execute_block_txpool { #[tokio::test] async fn production_fails_on_execution_error() { - let ctx = TestContext::default_from_executor(FailingMockExecutor(Mutex::new( - Some(ExecutorError::TransactionIdCollision(Default::default())), - ))); + let db = TestContext::::default_db(); + let executor = crate::mocks::create_failing_mock_executor( + ExecutorError::TransactionIdCollision(Default::default()), + db.clone(), + ); + let ctx = TestContext::default_from_db_and_executor(db, executor); let producer = ctx.producer(); @@ -1015,7 +1017,7 @@ impl TestContext { } pub fn default_from_db(db: MockDb) -> Self { - let executor = MockExecutor(db.clone()); + let executor = crate::mocks::create_mock_executor(db.clone()); Self::default_from_db_and_executor(db, executor) } } @@ -1039,8 +1041,9 @@ impl TestContext { } pub fn default_from_db_and_executor(db: MockDb, executor: Executor) -> Self { - let txpool = MockTxPool::default(); - let relayer = MockRelayer::default(); + let txpool = crate::mocks::create_mock_txpool(vec![]); + let relayer = + crate::mocks::create_mock_relayer(DaBlockHeight::default(), HashMap::new()); let config = Config::default(); let gas_price = Some(0); Self { @@ -1229,13 +1232,10 @@ impl TestContextBuilder { fn build(self) -> TestContext { let block_gas_limit = self.block_gas_limit.unwrap_or_default(); - let mock_relayer = MockRelayer { - latest_block_height: self.latest_block_height, - latest_da_blocks_with_costs_and_transactions_number: self - .blocks_with_gas_costs_and_transactions_number - .clone(), - ..Default::default() - }; + let mock_relayer = crate::mocks::create_mock_relayer( + self.latest_block_height, + self.blocks_with_gas_costs_and_transactions_number.clone(), + ); let db = MockDb { blocks: self.pre_existing_blocks(), @@ -1253,13 +1253,10 @@ impl TestContextBuilder { fn build_with_executor(self, executor: Ex) -> TestContext { let block_gas_limit = self.block_gas_limit.unwrap_or_default(); - let mock_relayer = MockRelayer { - latest_block_height: self.latest_block_height, - latest_da_blocks_with_costs_and_transactions_number: self - .blocks_with_gas_costs_and_transactions_number - .clone(), - ..Default::default() - }; + let mock_relayer = crate::mocks::create_mock_relayer( + self.latest_block_height, + self.blocks_with_gas_costs_and_transactions_number.clone(), + ); let db = MockDb { blocks: self.pre_existing_blocks(), diff --git a/crates/services/producer/src/mocks.rs b/crates/services/producer/src/mocks.rs index 299a491ae62..c2d38247c29 100644 --- a/crates/services/producer/src/mocks.rs +++ b/crates/services/producer/src/mocks.rs @@ -28,7 +28,6 @@ use fuel_core_types::{ }, fuel_tx::Transaction, fuel_types::{ - Address, BlockHeight, Bytes32, ChainId, @@ -53,65 +52,161 @@ use std::{ Mutex, }, }; -// TODO: Replace mocks with `mockall`. - -#[derive(Default, Clone)] -pub struct MockRelayer { - pub block_production_key: Address, - pub latest_block_height: DaBlockHeight, - pub latest_da_blocks_with_costs_and_transactions_number: - HashMap, -} +// Mockall-generated mocks for testing +#[cfg(feature = "test-helpers")] +use mockall::mock; -#[async_trait::async_trait] -impl Relayer for MockRelayer { - async fn wait_for_at_least_height( - &self, - _height: &DaBlockHeight, - ) -> anyhow::Result { - let highest = self.latest_block_height; - Ok(highest) - } +#[cfg(feature = "test-helpers")] +mock! { + pub Relayer {} - async fn get_cost_and_transactions_number_for_block( - &self, - height: &DaBlockHeight, - ) -> anyhow::Result { - let (gas_cost, tx_count) = self - .latest_da_blocks_with_costs_and_transactions_number - .get(height) - .cloned() - .unwrap_or_default(); - Ok(RelayerBlockInfo { gas_cost, tx_count }) + #[async_trait::async_trait] + impl Relayer for Relayer { + async fn wait_for_at_least_height( + &self, + height: &DaBlockHeight, + ) -> anyhow::Result; + + async fn get_cost_and_transactions_number_for_block( + &self, + height: &DaBlockHeight, + ) -> anyhow::Result; } } -#[derive(Default)] -pub struct MockTxPool(pub Vec); +// Re-export for backward compatibility during migration +#[cfg(feature = "test-helpers")] +pub use MockRelayer as MockRelayerLegacy; -impl TxPool for MockTxPool { - type TxSource = Vec; +// Helper function to create a MockRelayer with default behavior +#[cfg(feature = "test-helpers")] +pub fn create_mock_relayer( + latest_height: DaBlockHeight, + blocks_with_costs: HashMap, +) -> MockRelayer { + let mut mock = MockRelayer::new(); - async fn get_source(&self, _: u64, _: BlockHeight) -> anyhow::Result { - Ok(self.0.clone()) - } + // Default behavior: return the latest height + mock.expect_wait_for_at_least_height() + .returning(move |_| Ok(latest_height)); + + // Default behavior: return gas cost and tx count from the map + mock.expect_get_cost_and_transactions_number_for_block() + .returning(move |height| { + let (gas_cost, tx_count) = + blocks_with_costs.get(height).cloned().unwrap_or_default(); + Ok(RelayerBlockInfo { gas_cost, tx_count }) + }); + + mock } -#[derive(Default)] -pub struct MockExecutor(pub MockDb); +// MockTxPool with associated type +#[cfg(feature = "test-helpers")] +mock! { + pub TxPool {} -impl AsMut for MockDb { - fn as_mut(&mut self) -> &mut MockDb { - self + impl TxPool for TxPool { + type TxSource = Vec; + + async fn get_source(&self, gas_price: u64, block_height: BlockHeight) -> anyhow::Result>; } } -impl AsRef for MockDb { - fn as_ref(&self) -> &MockDb { - self +// Helper function to create a MockTxPool with default behavior +#[cfg(feature = "test-helpers")] +pub fn create_mock_txpool(transactions: Vec) -> MockTxPool { + let mut mock = MockTxPool::new(); + + // Default behavior: return the transactions + mock.expect_get_source() + .returning(move |_, _| Ok(transactions.clone())); + + mock +} + +// MockExecutor - BlockProducer implementation +#[cfg(feature = "test-helpers")] +mock! { + pub Executor {} + + impl BlockProducer> for Executor { + type Deadline = (); + + async fn produce_without_commit( + &self, + component: Components>, + deadline: (), + ) -> ExecutorResult>; } } +// Helper function to create a MockExecutor with default successful behavior +#[cfg(feature = "test-helpers")] +pub fn create_mock_executor(db: MockDb) -> MockExecutor { + let mut mock = MockExecutor::new(); + + // Default behavior: create a block and insert it into the database + mock.expect_produce_without_commit() + .returning(move |component, _| { + let block = arc_pool_tx_comp_to_block(&component); + // simulate executor inserting a block + let mut block_db = db.blocks.lock().unwrap(); + block_db.insert( + *block.header().height(), + block.compress(&ChainId::default()), + ); + Ok(UncommittedResult::new( + ExecutionResult { + block, + skipped_transactions: vec![], + tx_status: vec![], + events: vec![], + }, + Default::default(), + )) + }); + + mock +} + +// Helper function to create a failing MockExecutor +// This replaces the old FailingMockExecutor struct +// Fails once with the provided error, then succeeds on subsequent calls +#[cfg(feature = "test-helpers")] +pub fn create_failing_mock_executor(error: ExecutorError, db: MockDb) -> MockExecutor { + let mut mock = MockExecutor::new(); + + // First call: fail with the provided error + mock.expect_produce_without_commit() + .times(1) + .return_once(move |_, _| Err(error)); + + // Subsequent calls: succeed with standard executor logic + mock.expect_produce_without_commit() + .returning(move |component, _| { + let block = arc_pool_tx_comp_to_block(&component); + let mut block_db = db.blocks.lock().unwrap(); + block_db.insert( + *block.header().height(), + block.compress(&ChainId::default()), + ); + Ok(UncommittedResult::new( + ExecutionResult { + block, + skipped_transactions: vec![], + tx_status: vec![], + events: vec![], + }, + Default::default(), + )) + }); + + mock +} + +// Helper functions for block creation (used by mocks) +#[cfg(feature = "test-helpers")] fn arc_pool_tx_comp_to_block(component: &Components>) -> Block { let transactions = component.transactions_source.clone(); Block::new( @@ -125,59 +220,15 @@ fn arc_pool_tx_comp_to_block(component: &Components>) -> Block .unwrap() } -impl BlockProducer> for MockExecutor { - type Deadline = (); - - async fn produce_without_commit( - &self, - component: Components>, - _: (), - ) -> ExecutorResult> { - let block = arc_pool_tx_comp_to_block(&component); - // simulate executor inserting a block - let mut block_db = self.0.blocks.lock().unwrap(); - block_db.insert( - *block.header().height(), - block.compress(&ChainId::default()), - ); - Ok(UncommittedResult::new( - ExecutionResult { - block, - skipped_transactions: vec![], - tx_status: vec![], - events: vec![], - }, - Default::default(), - )) +impl AsMut for MockDb { + fn as_mut(&mut self) -> &mut MockDb { + self } } -pub struct FailingMockExecutor(pub Mutex>); - -impl BlockProducer> for FailingMockExecutor { - type Deadline = (); - async fn produce_without_commit( - &self, - component: Components>, - _: (), - ) -> ExecutorResult> { - // simulate an execution failure - let mut err = self.0.lock().unwrap(); - match err.take() { - Some(err) => Err(err), - _ => { - let block = arc_pool_tx_comp_to_block(&component); - Ok(UncommittedResult::new( - ExecutionResult { - block, - skipped_transactions: vec![], - tx_status: vec![], - events: vec![], - }, - Default::default(), - )) - } - } +impl AsRef for MockDb { + fn as_ref(&self) -> &MockDb { + self } }