Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/services/producer/src/block_producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ where
height,
previous_block: latest_height,
}
.into())
.into());
}

let maybe_mint_tx = transactions_source.pop();
Expand Down
40 changes: 19 additions & 21 deletions crates/services/producer/src/block_producer/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use crate::{
},
},
mocks::{
FailingMockExecutor,
MockDb,
MockExecutor,
MockExecutorWithCapture,
Expand Down Expand Up @@ -518,9 +517,13 @@ 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::<MockExecutor>::default_db();
let ctx = TestContext::default_from_executor(
crate::mocks::create_failing_mock_executor(
ExecutorError::TransactionIdCollision(Default::default()),
db,
),
);
Comment thread
raushan728 marked this conversation as resolved.

let producer = ctx.producer();

Expand Down Expand Up @@ -1015,7 +1018,7 @@ impl TestContext<MockExecutor> {
}

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)
}
}
Expand All @@ -1039,8 +1042,9 @@ impl<Executor> TestContext<Executor> {
}

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 {
Expand Down Expand Up @@ -1229,13 +1233,10 @@ impl TestContextBuilder {
fn build(self) -> TestContext<MockExecutor> {
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(),
Expand All @@ -1253,13 +1254,10 @@ impl TestContextBuilder {
fn build_with_executor<Ex>(self, executor: Ex) -> TestContext<Ex> {
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(),
Expand Down
239 changes: 145 additions & 94 deletions crates/services/producer/src/mocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ use fuel_core_types::{
},
fuel_tx::Transaction,
fuel_types::{
Address,
BlockHeight,
Bytes32,
ChainId,
Expand All @@ -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<DaBlockHeight, (u64, u64)>,
}
// 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<DaBlockHeight> {
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<RelayerBlockInfo> {
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<DaBlockHeight>;

async fn get_cost_and_transactions_number_for_block(
&self,
height: &DaBlockHeight,
) -> anyhow::Result<RelayerBlockInfo>;
}
}

#[derive(Default)]
pub struct MockTxPool(pub Vec<Transaction>);
// Re-export for backward compatibility during migration
#[cfg(feature = "test-helpers")]
pub use MockRelayer as MockRelayerLegacy;

impl TxPool for MockTxPool {
type TxSource = Vec<Transaction>;
// 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<DaBlockHeight, (u64, u64)>,
) -> MockRelayer {
let mut mock = MockRelayer::new();

async fn get_source(&self, _: u64, _: BlockHeight) -> anyhow::Result<Self::TxSource> {
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<MockDb> for MockDb {
fn as_mut(&mut self) -> &mut MockDb {
self
impl TxPool for TxPool {
type TxSource = Vec<Transaction>;

async fn get_source(&self, gas_price: u64, block_height: BlockHeight) -> anyhow::Result<Vec<Transaction>>;
}
}

impl AsRef<MockDb> 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<Transaction>) -> 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<Vec<Transaction>> for Executor {
type Deadline = ();

async fn produce_without_commit(
&self,
component: Components<Vec<Transaction>>,
deadline: (),
) -> ExecutorResult<UncommittedResult<Changes>>;
}
}

// 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
}
Comment thread
raushan728 marked this conversation as resolved.

// Helper functions for block creation (used by mocks)
#[cfg(feature = "test-helpers")]
fn arc_pool_tx_comp_to_block(component: &Components<Vec<Transaction>>) -> Block {
let transactions = component.transactions_source.clone();
Block::new(
Expand All @@ -125,59 +220,15 @@ fn arc_pool_tx_comp_to_block(component: &Components<Vec<Transaction>>) -> Block
.unwrap()
}

impl BlockProducer<Vec<Transaction>> for MockExecutor {
type Deadline = ();

async fn produce_without_commit(
&self,
component: Components<Vec<Transaction>>,
_: (),
) -> ExecutorResult<UncommittedResult<Changes>> {
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<MockDb> for MockDb {
fn as_mut(&mut self) -> &mut MockDb {
self
}
}

pub struct FailingMockExecutor(pub Mutex<Option<ExecutorError>>);

impl BlockProducer<Vec<Transaction>> for FailingMockExecutor {
type Deadline = ();
async fn produce_without_commit(
&self,
component: Components<Vec<Transaction>>,
_: (),
) -> ExecutorResult<UncommittedResult<Changes>> {
// 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<MockDb> for MockDb {
fn as_ref(&self) -> &MockDb {
self
}
}

Expand Down