From 3d6e7c2287f1b0ce9bdd2fd45616a399299073ba Mon Sep 17 00:00:00 2001 From: Ivan Cavlek <97914489+ICavlek@users.noreply.github.com> Date: Tue, 10 Sep 2024 07:06:28 +0200 Subject: [PATCH] test: account declaration with mock server (#759) --- tests/account.rs | 254 +++++++++++++++++++++++++++ tests/account_declaration.rs | 72 -------- tests/common/constants.rs | 57 +++++++ tests/common/matchers.rs | 321 +++++++++++++++++++++++++++++++++++ tests/common/mod.rs | 2 + tests/common/node.rs | 144 ++++++++++++++++ 6 files changed, 778 insertions(+), 72 deletions(-) create mode 100644 tests/account.rs delete mode 100644 tests/account_declaration.rs create mode 100644 tests/common/matchers.rs create mode 100644 tests/common/node.rs diff --git a/tests/account.rs b/tests/account.rs new file mode 100644 index 00000000..973ca310 --- /dev/null +++ b/tests/account.rs @@ -0,0 +1,254 @@ +use std::{thread, time}; + +use beerus::gen::{ + client::Client, Address, BlockId, BlockTag, BroadcastedDeclareTxn, + BroadcastedTxn, Felt, Rpc, SimulationFlagForEstimateFee, +}; +use common::{ + constants::{ + dummy_transaction_v3, COMPILED_ACCOUNT_CONTRACT, DECLARE_ACCOUNT, + }, + katana::Katana, + matchers::StarknetMatcher::{ + AddDeclareTransaction, AddDeclareTransactionMalicious, ChainId, + ChainIdMalicious, ClassError, ClassMalicious, ClassSuccess, + EstimateFee, EstimateFeeMalicious, Nonce, NonceMalicious, SpecVersion, + SpecVersionMalicious, + }, + node::setup_client_with_mock_starknet_node, +}; + +mod common; + +#[tokio::test] +async fn declare_account_katana() { + let url = "http://127.0.0.1:5050"; + let katana = Katana::init(url).await.unwrap(); + let client = Client::new(url); + + let res_chain_id = client.chainId().await; + assert!(res_chain_id.is_ok()); + assert_eq!(res_chain_id.unwrap().as_ref(), "0x4b4154414e41"); + + let block_id = BlockId::BlockTag(BlockTag::Pending); + let class_hash = Felt::try_new( + "0x6b46f84b1bbb779e588a9c5f577907c3dfb66e6b13cf4c4f480d4fb1677c2ba", + ) + .unwrap(); + let res_class = client.getClass(block_id.clone(), class_hash).await; + assert!(res_class.is_err()); + assert!(res_class.unwrap_err().message.contains("Class hash not found")); + + let contract_address = Address( + Felt::try_new( + "0x6162896d1d7ab204c7ccac6dd5f8e9e7c25ecd5ae4fcb4ad32e57786bb46e03", + ) + .unwrap(), + ); + let res_nonce = client.getNonce(block_id, contract_address).await; + assert!(res_nonce.is_ok()); + assert_eq!(res_nonce.unwrap().as_ref(), "0x0"); + + let res_spec_version = client.specVersion().await; + assert!(res_spec_version.is_ok()); + assert_eq!(res_spec_version.unwrap().as_str(), "0.7.1"); + + let contract: Vec = + serde_json::from_str(COMPILED_ACCOUNT_CONTRACT).unwrap(); + let simulation_flags: Vec = vec![]; + let block_id = BlockId::BlockTag(BlockTag::Pending); + let res_estimate_fee = + client.estimateFee(contract, simulation_flags, block_id).await; + assert!(res_estimate_fee.is_ok()); + + let declare_account: BroadcastedDeclareTxn = + serde_json::from_str(DECLARE_ACCOUNT).unwrap(); + let res_declare_account = + client.addDeclareTransaction(declare_account).await; + assert!(res_declare_account.is_ok()); + + let block_mining_time = time::Duration::from_millis(1000); + thread::sleep(block_mining_time); + + let block_id = BlockId::BlockTag(BlockTag::Pending); + let class_hash = Felt::try_new( + "0x6b46f84b1bbb779e588a9c5f577907c3dfb66e6b13cf4c4f480d4fb1677c2ba", + ) + .unwrap(); + let res_class = client.getClass(block_id.clone(), class_hash).await; + assert!(res_class.is_ok()); + + katana.stop().unwrap(); +} + +#[tokio::test] +async fn chain_id_test() { + let (client, _starknet_node) = + setup_client_with_mock_starknet_node(vec![ChainId]).await; + let result = client.chainId().await; + assert!(result.is_ok()); +} + +#[tokio::test] +async fn chain_id_nonce() { + let (client, _starknet_node) = + setup_client_with_mock_starknet_node(vec![ChainId, Nonce]).await; + assert!(client.chainId().await.is_ok()); + assert!(client + .getNonce( + BlockId::BlockTag(BlockTag::Latest), + Address(Felt::try_new("0x0").unwrap()) + ) + .await + .is_ok()) +} + +#[tokio::test] +async fn chain_id_called_twice() { + let (client, _starknet_node) = + setup_client_with_mock_starknet_node(vec![ChainId, ChainId]).await; + assert!(client.chainId().await.is_ok()); + assert!(client.chainId().await.is_ok()); +} + +#[tokio::test] +async fn get_class_error() { + let (client, _starknet_node) = + setup_client_with_mock_starknet_node(vec![ClassError]).await; + assert!(client + .getClass( + BlockId::BlockTag(BlockTag::Latest), + Felt::try_new("0x0").unwrap() + ) + .await + .is_err()); +} + +#[tokio::test] +async fn get_class_success() { + let (client, _starknet_node) = + setup_client_with_mock_starknet_node(vec![ClassSuccess]).await; + assert!(client + .getClass( + BlockId::BlockTag(BlockTag::Latest), + Felt::try_new("0x0").unwrap() + ) + .await + .is_ok()); +} + +#[tokio::test] +async fn spec_version_estimate_fee() { + let declare_transaction = dummy_transaction_v3(); + let (client, _starknet_node) = + setup_client_with_mock_starknet_node(vec![SpecVersion, EstimateFee]) + .await; + assert!(client.specVersion().await.is_ok()); + let res = client + .estimateFee( + vec![BroadcastedTxn::BroadcastedDeclareTxn( + BroadcastedDeclareTxn::BroadcastedDeclareTxnV3( + declare_transaction, + ), + )], + vec![], + BlockId::BlockTag(BlockTag::Latest), + ) + .await; + assert!(res.is_ok()); +} + +#[tokio::test] +async fn add_declare_transaction() { + let declare_transaction = dummy_transaction_v3(); + let (client, _starknet_node) = + setup_client_with_mock_starknet_node(vec![AddDeclareTransaction]).await; + assert!(client + .addDeclareTransaction(BroadcastedDeclareTxn::BroadcastedDeclareTxnV3( + declare_transaction + )) + .await + .is_ok()); +} + +#[tokio::test] +async fn declare_account_mock() { + let (client, _starknet_node) = setup_client_with_mock_starknet_node(vec![ + ChainId, + ClassError, + ChainId, + Nonce, + SpecVersion, + EstimateFee, + AddDeclareTransaction, + ]) + .await; + let block_id = BlockId::BlockTag(BlockTag::Latest); + let class_hash = Felt::try_new("0x0").unwrap(); + let contract_address = Address(class_hash.clone()); + let declare_transaction = dummy_transaction_v3(); + + assert!(client.chainId().await.is_ok()); + assert!(client.getClass(block_id.clone(), class_hash).await.is_err()); + assert!(client.chainId().await.is_ok()); + assert!(client.getNonce(block_id.clone(), contract_address).await.is_ok()); + assert!(client.specVersion().await.is_ok()); + assert!(client + .estimateFee( + vec![BroadcastedTxn::BroadcastedDeclareTxn( + BroadcastedDeclareTxn::BroadcastedDeclareTxnV3( + declare_transaction.clone(), + ), + )], + vec![], + block_id + ) + .await + .is_ok()); + assert!(client + .addDeclareTransaction(BroadcastedDeclareTxn::BroadcastedDeclareTxnV3( + declare_transaction + )) + .await + .is_ok()); +} + +#[tokio::test] +async fn malicious_data_results_in_err() { + let (client, _starknet_node) = setup_client_with_mock_starknet_node(vec![ + AddDeclareTransactionMalicious, + ChainIdMalicious, + ClassMalicious, + EstimateFeeMalicious, + NonceMalicious, + SpecVersionMalicious, + ]) + .await; + let block_id = BlockId::BlockTag(BlockTag::Latest); + let class_hash = Felt::try_new("0x0").unwrap(); + let contract_address = Address(class_hash.clone()); + let declare_transaction = dummy_transaction_v3(); + + assert!(client + .addDeclareTransaction(BroadcastedDeclareTxn::BroadcastedDeclareTxnV3( + declare_transaction.clone() + )) + .await + .is_err()); + assert!(client.chainId().await.is_err()); + assert!(client + .estimateFee( + vec![BroadcastedTxn::BroadcastedDeclareTxn( + BroadcastedDeclareTxn::BroadcastedDeclareTxnV3( + declare_transaction.clone(), + ), + )], + vec![], + block_id.clone() + ) + .await + .is_err()); + assert!(client.getClass(block_id.clone(), class_hash).await.is_err()); + assert!(client.getNonce(block_id, contract_address).await.is_err()); + assert!(client.specVersion().await.is_err()); +} diff --git a/tests/account_declaration.rs b/tests/account_declaration.rs deleted file mode 100644 index 29e87043..00000000 --- a/tests/account_declaration.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::{thread, time}; - -use beerus::gen::BroadcastedDeclareTxn; -use beerus::gen::{ - client::Client, Address, BlockId, BlockTag, BroadcastedTxn, Felt, Rpc, - SimulationFlagForEstimateFee, -}; -use common::constants::{COMPILED_ACCOUNT_CONTRACT, DECLARE_ACCOUNT}; -use common::katana::Katana; - -mod common; - -#[tokio::test] -async fn declare_account() { - let url = "http://127.0.0.1:5050"; - let katana = Katana::init(url).await.unwrap(); - let client = Client::new(url); - - let res_chain_id = client.chainId().await; - assert!(res_chain_id.is_ok()); - assert_eq!(res_chain_id.unwrap().as_ref(), "0x4b4154414e41"); - - let block_id = BlockId::BlockTag(BlockTag::Pending); - let class_hash = Felt::try_new( - "0x6b46f84b1bbb779e588a9c5f577907c3dfb66e6b13cf4c4f480d4fb1677c2ba", - ) - .unwrap(); - let res_class = client.getClass(block_id.clone(), class_hash).await; - assert!(res_class.is_err()); - assert!(res_class.unwrap_err().message.contains("Class hash not found")); - - let contract_address = Address( - Felt::try_new( - "0x6162896d1d7ab204c7ccac6dd5f8e9e7c25ecd5ae4fcb4ad32e57786bb46e03", - ) - .unwrap(), - ); - let res_nonce = client.getNonce(block_id, contract_address).await; - assert!(res_nonce.is_ok()); - assert_eq!(res_nonce.unwrap().as_ref(), "0x0"); - - let res_spec_version = client.specVersion().await; - assert!(res_spec_version.is_ok()); - assert_eq!(res_spec_version.unwrap().as_str(), "0.7.1"); - - let contract: Vec = - serde_json::from_str(COMPILED_ACCOUNT_CONTRACT).unwrap(); - let simulation_flags: Vec = vec![]; - let block_id = BlockId::BlockTag(BlockTag::Pending); - let res_estimate_fee = - client.estimateFee(contract, simulation_flags, block_id).await; - assert!(res_estimate_fee.is_ok()); - - let declare_account: BroadcastedDeclareTxn = - serde_json::from_str(DECLARE_ACCOUNT).unwrap(); - let res_declare_account = - client.addDeclareTransaction(declare_account).await; - assert!(res_declare_account.is_ok()); - - let block_mining_time = time::Duration::from_millis(1000); - thread::sleep(block_mining_time); - - let block_id = BlockId::BlockTag(BlockTag::Pending); - let class_hash = Felt::try_new( - "0x6b46f84b1bbb779e588a9c5f577907c3dfb66e6b13cf4c4f480d4fb1677c2ba", - ) - .unwrap(); - let res_class = client.getClass(block_id.clone(), class_hash).await; - assert!(res_class.is_ok()); - - katana.stop().unwrap(); -} diff --git a/tests/common/constants.rs b/tests/common/constants.rs index ee2bc2b0..f8bb3930 100644 --- a/tests/common/constants.rs +++ b/tests/common/constants.rs @@ -1,5 +1,62 @@ +use beerus::gen::{ + Address, BroadcastedDeclareTxnV3, BroadcastedDeclareTxnV3Type, + BroadcastedDeclareTxnV3Version, ContractClass, + ContractClassEntryPointsByType, DaMode, Felt, ResourceBounds, + ResourceBoundsMapping, SierraEntryPoint, U128, U64, +}; + #[allow(dead_code)] pub const COMPILED_ACCOUNT_CONTRACT: &str = include_str!("../clob/compiled_account_contract.txt"); #[allow(dead_code)] pub const DECLARE_ACCOUNT: &str = include_str!("../clob/declare_account.txt"); + +#[allow(dead_code)] +pub fn dummy_transaction_v3() -> BroadcastedDeclareTxnV3 { + BroadcastedDeclareTxnV3 { + account_deployment_data: vec![Felt::try_new("0x0").unwrap()], + compiled_class_hash: Felt::try_new("0x0").unwrap(), + contract_class: ContractClass { + sierra_program: vec![Felt::try_new("0x1").unwrap()], + contract_class_version: "0.1.0".to_string(), + entry_points_by_type: ContractClassEntryPointsByType { + constructor: vec![SierraEntryPoint { + selector: Felt::try_new("0x2").unwrap(), + function_idx: 2, + }], + external: vec![ + SierraEntryPoint { + selector: Felt::try_new("0x3").unwrap(), + function_idx: 3, + }, + SierraEntryPoint { + selector: Felt::try_new("0x4").unwrap(), + function_idx: 4, + }, + ], + l1_handler: vec![], + }, + abi: Some("some_abi".to_string()), + }, + fee_data_availability_mode: DaMode::L1, + nonce: Felt::try_new("0x0").unwrap(), + r#type: BroadcastedDeclareTxnV3Type::Declare, + signature: vec![Felt::try_new("0x5").unwrap()], + sender_address: Address(Felt::try_new("0x6").unwrap()), + version: + BroadcastedDeclareTxnV3Version::V0x100000000000000000000000000000003, + nonce_data_availability_mode: DaMode::L1, + paymaster_data: vec![Felt::try_new("0x7").unwrap()], + resource_bounds: ResourceBoundsMapping { + l1_gas: ResourceBounds { + max_amount: U64::try_new("0x0").unwrap(), + max_price_per_unit: U128::try_new("0x0").unwrap(), + }, + l2_gas: ResourceBounds { + max_amount: U64::try_new("0x0").unwrap(), + max_price_per_unit: U128::try_new("0x0").unwrap(), + }, + }, + tip: U64::try_new("0x0").unwrap(), + } +} diff --git a/tests/common/matchers.rs b/tests/common/matchers.rs new file mode 100644 index 00000000..c737eaf0 --- /dev/null +++ b/tests/common/matchers.rs @@ -0,0 +1,321 @@ +use iamgroot::jsonrpc; +use serde_json::Value; +use wiremock::{Match, Request}; + +#[allow(dead_code)] +#[derive(Eq, Hash, PartialEq)] +pub enum StarknetMatcher { + AddDeclareTransaction, + AddDeclareTransactionMalicious, + ChainId, + ChainIdMalicious, + ClassError, + ClassSuccess, + ClassMalicious, + EstimateFee, + EstimateFeeMalicious, + Nonce, + NonceMalicious, + SpecVersion, + SpecVersionMalicious, +} + +pub trait Response { + fn response(&self) -> Value; +} + +pub struct ChainIdMatcher { + pub response: Value, +} + +impl ChainIdMatcher { + pub fn malicious() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": "malicious_result" + }), + } + } +} + +impl Default for ChainIdMatcher { + fn default() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": "0x4b4154414e41" + }), + } + } +} + +impl Response for ChainIdMatcher { + fn response(&self) -> Value { + self.response.clone() + } +} + +impl Match for ChainIdMatcher { + fn matches(&self, request: &Request) -> bool { + let request = request.body_json::().unwrap(); + matches!(request.method.as_str(), "starknet_chainId") + } +} + +pub struct NonceMatcher { + pub response: Value, +} + +impl NonceMatcher { + pub fn malicious() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": "malicious_result" + }), + } + } +} + +impl Default for NonceMatcher { + fn default() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": "0x0" + }), + } + } +} + +impl Match for NonceMatcher { + fn matches(&self, request: &Request) -> bool { + let request = request.body_json::().unwrap(); + matches!(request.method.as_str(), "starknet_getNonce") + } +} + +impl Response for NonceMatcher { + fn response(&self) -> Value { + self.response.clone() + } +} + +pub struct ClassMatcher { + pub response: Value, +} + +impl ClassMatcher { + pub fn error() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": 28, + "message": "Class hash not found" + } + }), + } + } + + pub fn success() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": { + "sierra_program": ["0x1"], + "contract_class_version": "0.1.0", + "entry_points_by_type": { + "CONSTRUCTOR": [{ + "selector": "0x2", + "function_idx": 2, + }], + "EXTERNAL": [{ + "selector": "0x3", + "function_idx": 3, + }, { + "selector": "0x4", + "function_idx": 4, + }], + "L1_HANDLER": [], + }, + "abi": "some_abi" + } + }), + } + } + + pub fn malicious() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": "malicious" + }), + } + } +} + +impl Match for ClassMatcher { + fn matches(&self, request: &Request) -> bool { + let request = request.body_json::().unwrap(); + matches!(request.method.as_str(), "starknet_getClass") + } +} + +impl Response for ClassMatcher { + fn response(&self) -> Value { + self.response.clone() + } +} + +pub struct SpecVersionMatcher { + pub response: Value, +} + +impl SpecVersionMatcher { + pub fn malicious() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": 42 + }), + } + } +} + +impl Default for SpecVersionMatcher { + fn default() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": "0.6.0" + }), + } + } +} + +impl Match for SpecVersionMatcher { + fn matches(&self, request: &Request) -> bool { + let request = request.body_json::().unwrap(); + matches!(request.method.as_str(), "starknet_specVersion") + } +} + +impl Response for SpecVersionMatcher { + fn response(&self) -> Value { + self.response.clone() + } +} + +pub struct EstimateFeeMatcher { + pub response: Value, +} + +impl EstimateFeeMatcher { + pub fn malicious() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": "malicious" + }), + } + } +} + +impl Default for EstimateFeeMatcher { + fn default() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": [{ + "gas_consumed": "0x18bf", + "gas_price": "0x174876e800", + "overall_fee": "0x2402a36771800", + "unit": "WEI" + }] + }), + } + } +} + +impl Match for EstimateFeeMatcher { + fn matches(&self, request: &Request) -> bool { + let request = request.body_json::().unwrap(); + matches!(request.method.as_str(), "starknet_estimateFee") + } +} + +impl Response for EstimateFeeMatcher { + fn response(&self) -> Value { + self.response.clone() + } +} + +pub struct AddDeclareTransactionMatcher { + pub response: Value, +} + +impl AddDeclareTransactionMatcher { + pub fn malicious() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": 42 + }), + } + } +} + +impl Default for AddDeclareTransactionMatcher { + fn default() -> Self { + Self { + response: serde_json::json!( + { + "jsonrpc": "2.0", + "id": 1, + "result": { + "transaction_hash": "0x0", + "class_hash": "0x1", + } + }), + } + } +} + +impl Match for AddDeclareTransactionMatcher { + fn matches(&self, request: &Request) -> bool { + let request = request.body_json::().unwrap(); + matches!(request.method.as_str(), "starknet_addDeclareTransaction") + } +} + +impl Response for AddDeclareTransactionMatcher { + fn response(&self) -> Value { + self.response.clone() + } +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 6bebba0d..82ffc7d4 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -2,3 +2,5 @@ pub mod constants; pub mod context; pub mod error; pub mod katana; +pub mod matchers; +pub mod node; diff --git a/tests/common/node.rs b/tests/common/node.rs new file mode 100644 index 00000000..39d1b09d --- /dev/null +++ b/tests/common/node.rs @@ -0,0 +1,144 @@ +use std::collections::HashMap; + +use beerus::gen::client::Client; +use wiremock::{Match, Mock, MockGuard, MockServer, ResponseTemplate}; + +use super::matchers::{ + AddDeclareTransactionMatcher, ChainIdMatcher, ClassMatcher, + EstimateFeeMatcher, NonceMatcher, Response, SpecVersionMatcher, + StarknetMatcher, +}; + +#[allow(dead_code)] +pub async fn setup_client_with_mock_starknet_node( + methods: Vec, +) -> (Client, StarknetNode) { + let mut starknet_node = StarknetNode::new().await; + let mut map_methods = HashMap::new(); + for method in methods { + *map_methods.entry(method).or_insert(0) += 1; + } + starknet_node.add_methods(map_methods).await; + let client = Client::new(&starknet_node.server.uri()); + (client, starknet_node) +} + +pub struct StarknetNode { + pub server: MockServer, + pub mock_guard: Vec, +} + +impl StarknetNode { + pub async fn new() -> Self { + let server = MockServer::start().await; + Self { server, mock_guard: vec![] } + } + + pub async fn add_methods( + &mut self, + requests: HashMap, + ) { + let mut vec_mock_guards = Vec::with_capacity(requests.len()); + for (request, num_request) in requests.into_iter() { + let mock_guard = match request { + StarknetMatcher::AddDeclareTransaction => { + self.create_mock_guard( + AddDeclareTransactionMatcher::default(), + num_request, + ) + .await + } + StarknetMatcher::AddDeclareTransactionMalicious => { + self.create_mock_guard( + AddDeclareTransactionMatcher::malicious(), + num_request, + ) + .await + } + StarknetMatcher::ClassError => { + self.create_mock_guard(ClassMatcher::error(), num_request) + .await + } + StarknetMatcher::ClassSuccess => { + self.create_mock_guard(ClassMatcher::success(), num_request) + .await + } + StarknetMatcher::ClassMalicious => { + self.create_mock_guard( + ClassMatcher::malicious(), + num_request, + ) + .await + } + StarknetMatcher::ChainId => { + self.create_mock_guard( + ChainIdMatcher::default(), + num_request, + ) + .await + } + StarknetMatcher::ChainIdMalicious => { + self.create_mock_guard( + ChainIdMatcher::malicious(), + num_request, + ) + .await + } + StarknetMatcher::EstimateFee => { + self.create_mock_guard( + EstimateFeeMatcher::default(), + num_request, + ) + .await + } + StarknetMatcher::EstimateFeeMalicious => { + self.create_mock_guard( + EstimateFeeMatcher::malicious(), + num_request, + ) + .await + } + StarknetMatcher::Nonce => { + self.create_mock_guard(NonceMatcher::default(), num_request) + .await + } + StarknetMatcher::NonceMalicious => { + self.create_mock_guard( + NonceMatcher::malicious(), + num_request, + ) + .await + } + StarknetMatcher::SpecVersion => { + self.create_mock_guard( + SpecVersionMatcher::default(), + num_request, + ) + .await + } + StarknetMatcher::SpecVersionMalicious => { + self.create_mock_guard( + SpecVersionMatcher::malicious(), + num_request, + ) + .await + } + }; + vec_mock_guards.push(mock_guard); + } + self.mock_guard = vec_mock_guards; + } + + async fn create_mock_guard( + &self, + matcher: impl Match + Response + 'static, + num_request: u64, + ) -> MockGuard { + let response = matcher.response(); + Mock::given(matcher) + .respond_with(ResponseTemplate::new(200).set_body_json(response)) + .expect(num_request) + .mount_as_scoped(&self.server) + .await + } +}