Skip to content

Commit 9b3b8fe

Browse files
authored
feat(utils): mock provider builder macro (#158)
1 parent 77a9f77 commit 9b3b8fe

File tree

11 files changed

+1264
-1
lines changed

11 files changed

+1264
-1
lines changed

Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/oracle/gas/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
[package]
22
edition.workspace = true
33
license.workspace = true
4-
license-file.workspace = true
54
name = "katana-gas-oracle"
65
repository.workspace = true
76
version.workspace = true

crates/utils/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ katana-node = { workspace = true, features = [ "explorer" ] }
1313
katana-primitives.workspace = true
1414
katana-provider.workspace = true
1515
katana-rpc = { workspace = true, features = [ "client" ] }
16+
katana-utils-macro = { path = "macro" }
1617

1718
anyhow.workspace = true
1819
arbitrary.workspace = true
1920
assert_matches.workspace = true
21+
async-trait.workspace = true
2022
futures.workspace = true
2123
rand.workspace = true
2224
starknet.workspace = true
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
//! Example demonstrating the mock_provider macro for creating mock Provider implementations.
2+
//!
3+
//! This example shows how to use the `mock_provider!` macro to create mock implementations
4+
//! of the Starknet Provider trait for testing purposes.
5+
//!
6+
//! Run this example with:
7+
//! ```bash
8+
//! cargo run --example mock_provider_example
9+
//! ```
10+
11+
use katana_utils::mock_provider;
12+
use starknet::core::types::{
13+
BlockId, BlockTag, Felt, L1DataAvailabilityMode, MaybePendingBlockWithTxs, PendingBlockWithTxs,
14+
ResourcePrice,
15+
};
16+
use starknet::providers::Provider;
17+
18+
// Create a mock provider that only implements a few specific methods
19+
mock_provider! {
20+
MyMockProvider,
21+
22+
// Mock the get_block_with_txs method
23+
fn get_block_with_txs: (block_id) => {
24+
println!("Mock get_block_with_txs called");
25+
Ok(MaybePendingBlockWithTxs::PendingBlock(PendingBlockWithTxs {
26+
transactions: vec![],
27+
timestamp: 1234567890,
28+
l1_gas_price: ResourcePrice { price_in_fri: Felt::from(100u32), price_in_wei: Felt::from(200u32) },
29+
l1_data_gas_price: ResourcePrice { price_in_fri: Felt::from(50u32), price_in_wei: Felt::from(75u32) },
30+
l2_gas_price: ResourcePrice { price_in_fri: Felt::from(25u32), price_in_wei: Felt::from(30u32) },
31+
parent_hash: Felt::from(42u32),
32+
sequencer_address: Felt::from(123u32),
33+
starknet_version: "0.13.0".to_string(),
34+
l1_da_mode: L1DataAvailabilityMode::Calldata,
35+
}))
36+
},
37+
38+
// Mock the get_storage_at method using custom parameter names
39+
fn get_storage_at: (addr, storage_key, block) => {
40+
println!("Mock get_storage_at called with custom parameter names:");
41+
println!(" addr: {}", addr.as_ref());
42+
println!(" storage_key: {}", storage_key.as_ref());
43+
println!(" block called");
44+
45+
// Return a mock storage value using custom parameter names
46+
Ok(Felt::from(999u32))
47+
},
48+
49+
// Mock the chain_id method
50+
fn chain_id: () => {
51+
println!("Mock chain_id called");
52+
Ok(Felt::from(1u32)) // Return mock chain ID
53+
},
54+
55+
// Mock the block_number method
56+
fn block_number: () => {
57+
println!("Mock block_number called");
58+
Ok(12345u64) // Return mock block number
59+
}
60+
}
61+
62+
// Create another mock provider with different implementations and custom parameter names
63+
mock_provider! {
64+
DifferentMockProvider,
65+
66+
// This provider only implements chain_id differently
67+
fn chain_id: () => {
68+
println!("Different mock chain_id called");
69+
Ok(Felt::from(999u32)) // Different chain ID
70+
},
71+
72+
// And a different block_number
73+
fn block_number: () => {
74+
println!("Different mock block_number called");
75+
Ok(54321u64) // Different block number
76+
},
77+
78+
// Example with very descriptive custom parameter names
79+
fn get_nonce: (at_block_identifier, for_account_address) => {
80+
println!("Getting nonce for account: {}", for_account_address.as_ref());
81+
Ok(Felt::from(42u32))
82+
}
83+
}
84+
85+
#[tokio::main]
86+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
87+
println!("🚀 Mock Provider Example");
88+
println!("========================\n");
89+
90+
// Create instances of our mock providers
91+
let mock_provider_1 = MyMockProvider::new();
92+
let mock_provider_2 = DifferentMockProvider::new();
93+
94+
println!("1. Testing MyMockProvider:");
95+
println!("--------------------------");
96+
97+
// Test implemented methods
98+
println!("📋 Testing implemented methods:");
99+
100+
let chain_id = mock_provider_1.chain_id().await?;
101+
println!("✅ Chain ID: {}", chain_id);
102+
103+
let block_number = mock_provider_1.block_number().await?;
104+
println!("✅ Block number: {}", block_number);
105+
106+
let storage_value = mock_provider_1
107+
.get_storage_at(Felt::from(0x123u32), Felt::from(0x456u32), BlockId::Tag(BlockTag::Latest))
108+
.await?;
109+
println!("✅ Storage value: {}", storage_value);
110+
111+
let block = mock_provider_1.get_block_with_txs(BlockId::Tag(BlockTag::Latest)).await?;
112+
println!("✅ Block retrieved: {:?}", block);
113+
114+
println!("\n❌ Testing unimplemented method:");
115+
116+
// Test unimplemented method (this will panic with unimplemented!)
117+
println!("Attempting to call spec_version() - this should panic...");
118+
119+
// Catch the panic to demonstrate the behavior
120+
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
121+
tokio::runtime::Runtime::new()
122+
.unwrap()
123+
.block_on(async { mock_provider_1.spec_version().await })
124+
}));
125+
126+
if result.is_err() {
127+
println!("✅ Unimplemented method correctly panicked!");
128+
}
129+
130+
println!("\n2. Testing DifferentMockProvider:");
131+
println!("---------------------------------");
132+
133+
let chain_id_2 = mock_provider_2.chain_id().await?;
134+
println!("✅ Chain ID: {}", chain_id_2);
135+
136+
let block_number_2 = mock_provider_2.block_number().await?;
137+
println!("✅ Block number: {}", block_number_2);
138+
139+
println!("\n3. Demonstrating different behaviors:");
140+
println!("------------------------------------");
141+
println!("MyMockProvider chain_id: {}", mock_provider_1.chain_id().await?);
142+
println!("DifferentMockProvider chain_id: {}", mock_provider_2.chain_id().await?);
143+
println!("MyMockProvider block_number: {}", mock_provider_1.block_number().await?);
144+
println!("DifferentMockProvider block_number: {}", mock_provider_2.block_number().await?);
145+
146+
println!("\n4. Testing custom parameter names:");
147+
println!("----------------------------------");
148+
let nonce =
149+
mock_provider_2.get_nonce(BlockId::Tag(BlockTag::Latest), Felt::from(0x1234u32)).await?;
150+
println!("✅ Nonce from custom params: {}", nonce);
151+
152+
println!("\n✨ Example completed successfully!");
153+
println!("\nKey takeaways:");
154+
println!("- The mock_provider! macro generates a full Provider implementation");
155+
println!("- You only need to implement the methods you care about for testing");
156+
println!("- You can use custom parameter names instead of the exact trait names");
157+
println!("- Unimplemented methods will panic with a clear error message");
158+
println!("- Each mock provider can have different implementations");
159+
println!("- The generated structs implement Default and Debug traits");
160+
161+
Ok(())
162+
}

crates/utils/macro/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
edition.workspace = true
3+
license.workspace = true
4+
name = "katana-utils-macro"
5+
repository.workspace = true
6+
version.workspace = true
7+
8+
[lib]
9+
proc-macro = true
10+
11+
[dependencies]
12+
proc-macro2.workspace = true
13+
quote.workspace = true
14+
syn = { workspace = true, features = [ "extra-traits", "full", "parsing", "proc-macro" ] }
15+
16+
[dev-dependencies]
17+
async-trait.workspace = true
18+
starknet.workspace = true
19+
tokio = { workspace = true, features = [ "test-util" ] }

0 commit comments

Comments
 (0)