From 08a1c85126c5d39293f9aed0e04940fd4ef0bae0 Mon Sep 17 00:00:00 2001 From: dylan Date: Fri, 18 Apr 2025 20:25:39 -0600 Subject: [PATCH] refactor test utils --- src/lib.rs | 5 +- src/tasks/oauth.rs | 45 ++--------- src/test_utils.rs | 72 +++++++++++++++++ tests/block_builder_test.rs | 154 ++++++++++++++++++------------------ tests/bundle_poller_test.rs | 40 +--------- tests/tx_poller_test.rs | 63 ++------------- 6 files changed, 172 insertions(+), 207 deletions(-) create mode 100644 src/test_utils.rs diff --git a/src/lib.rs b/src/lib.rs index d185db6..bb39edf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,9 @@ pub mod tasks; /// Utilities. pub mod utils; +/// Test utilitites +pub mod test_utils; + // Anonymous import suppresses warnings about unused imports. use openssl as _; -use tracing_subscriber as _; \ No newline at end of file +use tracing_subscriber as _; diff --git a/src/tasks/oauth.rs b/src/tasks/oauth.rs index e5c4067..93949a8 100644 --- a/src/tasks/oauth.rs +++ b/src/tasks/oauth.rs @@ -122,54 +122,21 @@ impl Authenticator { } mod tests { - use crate::config::BuilderConfig; - use alloy::primitives::Address; - use eyre::Result; - #[ignore = "integration test"] #[tokio::test] - async fn test_authenticator() -> Result<()> { + async fn test_authenticator() -> eyre::Result<()> { use super::*; + use crate::test_utils::setup_test_config; use oauth2::TokenResponse; let config = setup_test_config()?; let auth = Authenticator::new(&config); - let token = auth.fetch_oauth_token().await?; - dbg!(&token); + + let _ = auth.fetch_oauth_token().await?; + let token = auth.token().await.unwrap(); - println!("{:?}", token); + assert!(!token.access_token().secret().is_empty()); Ok(()) } - - #[allow(dead_code)] - fn setup_test_config() -> Result { - let config = BuilderConfig { - host_chain_id: 17000, - ru_chain_id: 17001, - host_rpc_url: "host-rpc.example.com".into(), - ru_rpc_url: "ru-rpc.example.com".into(), - zenith_address: Address::default(), - quincey_url: "http://localhost:8080".into(), - builder_port: 8080, - sequencer_key: None, - builder_key: "0000000000000000000000000000000000000000000000000000000000000000".into(), - block_confirmation_buffer: 1, - chain_offset: 0, - target_slot_time: 1, - builder_rewards_address: Address::default(), - rollup_block_gas_limit: 100_000, - tx_pool_url: "http://localhost:9000/".into(), - tx_pool_cache_duration: 5, - oauth_client_id: "some_client_id".into(), - oauth_client_secret: "some_client_secret".into(), - oauth_authenticate_url: "http://localhost:9000".into(), - oauth_token_url: "http://localhost:9000".into(), - tx_broadcast_urls: vec!["http://localhost:9000".into()], - oauth_token_refresh_interval: 300, // 5 minutes - builder_helper_address: Address::default(), - concurrency_limit: 1000, - }; - Ok(config) - } } diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 0000000..20c5fa9 --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,72 @@ +//! Test utilities for testing builder tasks +use crate::{config::BuilderConfig, tasks::block::PECORINO_CHAIN_ID}; +use alloy::{ + consensus::{SignableTransaction, TxEip1559, TxEnvelope}, + primitives::{Address, TxKind, U256}, + signers::{SignerSync, local::PrivateKeySigner}, +}; +use eyre::Result; +use std::str::FromStr; +use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::SubscriberInitExt}; + +/// Sets up a block builder with test values +pub fn setup_test_config() -> Result { + let config = BuilderConfig { + host_chain_id: 17000, + ru_chain_id: 17001, + host_rpc_url: "host-rpc.example.com".into(), + ru_rpc_url: "ru-rpc.example.com".into(), + tx_broadcast_urls: vec!["http://localhost:9000".into()], + zenith_address: Address::default(), + quincey_url: "http://localhost:8080".into(), + builder_port: 8080, + sequencer_key: None, + builder_key: "0000000000000000000000000000000000000000000000000000000000000000".into(), + block_confirmation_buffer: 1, + chain_offset: 0, + target_slot_time: 1, + builder_rewards_address: Address::default(), + rollup_block_gas_limit: 100_000, + tx_pool_url: "http://localhost:9000/".into(), + tx_pool_cache_duration: 5, + oauth_client_id: "some_client_id".into(), + oauth_client_secret: "some_client_secret".into(), + oauth_authenticate_url: "http://localhost:8080".into(), + oauth_token_url: "http://localhost:8080".into(), + oauth_token_refresh_interval: 300, // 5 minutes + builder_helper_address: Address::default(), + concurrency_limit: 1000, + start_timestamp: 1740681556, // pecorino start timestamp as sane default + }; + Ok(config) +} + +/// Returns a new signed test transaction with the provided nonce, value, and mpfpg. +pub fn new_signed_tx( + wallet: &PrivateKeySigner, + nonce: u64, + value: U256, + mpfpg: u128, +) -> Result { + let tx = TxEip1559 { + chain_id: PECORINO_CHAIN_ID, + nonce, + max_fee_per_gas: 50_000, + max_priority_fee_per_gas: mpfpg, + to: TxKind::Call(Address::from_str("0x0000000000000000000000000000000000000000").unwrap()), + value, + gas_limit: 50_000, + ..Default::default() + }; + let signature = wallet.sign_hash_sync(&tx.signature_hash())?; + Ok(TxEnvelope::Eip1559(tx.into_signed(signature))) +} + +/// Initializes a logger that prints during testing +pub fn setup_logging() { + // Initialize logging + let filter = EnvFilter::from_default_env(); + let fmt = tracing_subscriber::fmt::layer().with_filter(filter); + let registry = tracing_subscriber::registry().with(fmt); + let _ = registry.try_init(); +} diff --git a/tests/block_builder_test.rs b/tests/block_builder_test.rs index b1934e3..6f8693a 100644 --- a/tests/block_builder_test.rs +++ b/tests/block_builder_test.rs @@ -1,38 +1,30 @@ #[cfg(test)] mod tests { use alloy::{ - consensus::{SignableTransaction, TxEip1559, TxEnvelope}, - node_bindings::Anvil, - primitives::{Address, TxKind, U256}, - providers::ProviderBuilder, - signers::{SignerSync, local::PrivateKeySigner}, + node_bindings::Anvil, primitives::U256, providers::ProviderBuilder, + signers::local::PrivateKeySigner, }; use builder::{ - config::BuilderConfig, - tasks::{ - block::{BlockBuilder, PECORINO_CHAIN_ID}, - oauth::Authenticator, - }, + tasks::block::{BlockBuilder, PECORINO_CHAIN_ID}, + test_utils::{new_signed_tx, setup_logging, setup_test_config}, }; - use eyre::Result; + use signet_sim::{SimCache, SimItem}; - use std::str::FromStr; - use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::SubscriberInitExt}; + use signet_types::SlotCalculator; + use std::{ + sync::Arc, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, + }; + use tokio::{sync::mpsc::unbounded_channel, time::timeout}; - #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn test_spawn() { - let filter = EnvFilter::from_default_env(); - let fmt = tracing_subscriber::fmt::layer().with_filter(filter); - let registry = tracing_subscriber::registry().with(fmt); - registry.try_init().unwrap(); + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_handle_build() { + setup_logging(); // Make a test config - let config = setup_test_config(); + let config = setup_test_config().unwrap(); let constants = config.load_pecorino_constants(); - // Create an authenticator for bundle integration testing - let authenticator = Authenticator::new(&config); - // Create an anvil instance for testing let anvil_instance = Anvil::new().chain_id(PECORINO_CHAIN_ID).spawn(); @@ -44,8 +36,14 @@ mod tests { // Create a rollup provider let ru_provider = ProviderBuilder::new().on_http(anvil_instance.endpoint_url()); - // Create a block builder - let block_builder = BlockBuilder::new(&config, authenticator, ru_provider.clone()); + // Create a block builder with a slot calculator for testing + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Clock may have gone backwards") + .as_secs(); + dbg!(now); + let slot_calculator = SlotCalculator::new(now, 0, 12); + let block_builder = BlockBuilder::new(&config, ru_provider.clone(), slot_calculator); // Setup a sim cache let sim_items = SimCache::new(); @@ -57,64 +55,68 @@ mod tests { let tx_2 = new_signed_tx(&test_key_1, 0, U256::from(2_f64), 10_000).unwrap(); sim_items.add_item(SimItem::Tx(tx_2)); + let finish_by = Instant::now() + Duration::from_secs(2); + // Spawn the block builder task - let got = block_builder.handle_build(constants, ru_provider, sim_items).await; + let got = block_builder.handle_build(constants, sim_items, finish_by).await; // Assert on the built block assert!(got.is_ok()); assert!(got.unwrap().tx_count() == 2); } - fn setup_test_config() -> BuilderConfig { - let config = BuilderConfig { - host_chain_id: 1, - ru_chain_id: PECORINO_CHAIN_ID, - host_rpc_url: "https://host-rpc.example.com".into(), - ru_rpc_url: "https://rpc.pecorino.signet.sh".into(), - zenith_address: Address::default(), - quincey_url: "http://localhost:8080".into(), - builder_port: 8080, - sequencer_key: None, - builder_key: "0000000000000000000000000000000000000000000000000000000000000000".into(), - block_confirmation_buffer: 1, - chain_offset: 0, - target_slot_time: 1, - builder_rewards_address: Address::default(), - rollup_block_gas_limit: 100_000_000, - tx_pool_url: "http://localhost:9000/".into(), - tx_pool_cache_duration: 5, - oauth_client_id: "some_client_id".into(), - oauth_client_secret: "some_client_secret".into(), - oauth_authenticate_url: "http://localhost:9000".into(), - oauth_token_url: "http://localhost:9000".into(), - tx_broadcast_urls: vec!["http://localhost:9000".into()], - oauth_token_refresh_interval: 300, // 5 minutes - builder_helper_address: Address::default(), - concurrency_limit: 1000, - }; - config - } + /// Tests the full block builder loop + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_spawn() { + setup_logging(); + + // Make a test config + let config = setup_test_config().unwrap(); + let constants = config.load_pecorino_constants(); + + // Create an anvil instance for testing + let anvil_instance = Anvil::new().chain_id(PECORINO_CHAIN_ID).spawn(); + + // Create a wallet + let keys = anvil_instance.keys(); + let test_key_0 = PrivateKeySigner::from_signing_key(keys[0].clone().into()); + let test_key_1 = PrivateKeySigner::from_signing_key(keys[1].clone().into()); + + // Plumb inputs for the test setup + let (tx_sender, tx_receiver) = unbounded_channel(); + let (_, bundle_receiver) = unbounded_channel(); + let (block_sender, mut block_receiver) = unbounded_channel(); + + // Create a rollup provider + let ru_provider = ProviderBuilder::new().on_http(anvil_instance.endpoint_url()); + + // Create a builder with a test slot calculator + let slot_calculator = SlotCalculator::new( + config.start_timestamp, + config.chain_offset, + config.target_slot_time, + ); + let sim_cache = SimCache::new(); + let builder = Arc::new(BlockBuilder::new(&config, ru_provider.clone(), slot_calculator)); + + // Create a sim cache and start filling it with items + let _ = + builder.clone().spawn_cache_handler(tx_receiver, bundle_receiver, sim_cache.clone()); + + // Finally, Kick off the block builder task. + let _ = builder.clone().spawn_builder_task(constants, sim_cache.clone(), block_sender); + + let tx_1 = new_signed_tx(&test_key_0, 0, U256::from(1_f64), 11_000).unwrap(); + let tx_2 = new_signed_tx(&test_key_1, 0, U256::from(2_f64), 10_000).unwrap(); + tx_sender.send(tx_1).unwrap(); + tx_sender.send(tx_2).unwrap(); - // Returns a new signed test transaction with default values - fn new_signed_tx( - wallet: &PrivateKeySigner, - nonce: u64, - value: U256, - mpfpg: u128, - ) -> Result { - let tx = TxEip1559 { - chain_id: PECORINO_CHAIN_ID, - nonce, - max_fee_per_gas: 50_000, - max_priority_fee_per_gas: mpfpg, - to: TxKind::Call( - Address::from_str("0x0000000000000000000000000000000000000000").unwrap(), - ), - value, - gas_limit: 50_000, - ..Default::default() - }; - let signature = wallet.sign_hash_sync(&tx.signature_hash())?; - Ok(TxEnvelope::Eip1559(tx.into_signed(signature))) + // Wait for a block with timeout + let result = timeout(Duration::from_secs(5), block_receiver.recv()).await; + assert!(result.is_ok(), "Did not receive block within 5 seconds"); + let block = result.unwrap(); + dbg!(&block); + assert!(block.is_some(), "Block channel closed without receiving a block"); + assert!(block.unwrap().tx_count() == 2); // TODO: Why is this failing? I'm seeing EVM errors but haven't tracked them down yet. } } diff --git a/tests/bundle_poller_test.rs b/tests/bundle_poller_test.rs index ee143fe..995419d 100644 --- a/tests/bundle_poller_test.rs +++ b/tests/bundle_poller_test.rs @@ -1,49 +1,17 @@ mod tests { - use alloy::primitives::Address; - use builder::{config::BuilderConfig, tasks::oauth::Authenticator}; + use builder::{tasks::oauth::Authenticator, test_utils}; use eyre::Result; #[ignore = "integration test"] #[tokio::test] async fn test_bundle_poller_roundtrip() -> Result<()> { - let config = setup_test_config().await.unwrap(); + let config = test_utils::setup_test_config().unwrap(); let auth = Authenticator::new(&config); + let mut bundle_poller = builder::tasks::bundler::BundlePoller::new(&config, auth); - let got = bundle_poller.check_bundle_cache().await?; - dbg!(got); + let _ = bundle_poller.check_bundle_cache().await?; Ok(()) } - - async fn setup_test_config() -> Result { - let config = BuilderConfig { - host_chain_id: 17000, - ru_chain_id: 17001, - host_rpc_url: "host-rpc.example.com".into(), - ru_rpc_url: "ru-rpc.example.com".into(), - zenith_address: Address::default(), - quincey_url: "http://localhost:8080".into(), - builder_port: 8080, - sequencer_key: None, - builder_key: "0000000000000000000000000000000000000000000000000000000000000000".into(), - block_confirmation_buffer: 1, - chain_offset: 0, - target_slot_time: 1, - builder_rewards_address: Address::default(), - rollup_block_gas_limit: 100_000, - tx_pool_url: "http://localhost:9000/".into(), - // tx_pool_url: "https://transactions.holesky.signet.sh".into(), - tx_pool_cache_duration: 5, - oauth_client_id: "some_client_id".into(), - oauth_client_secret: "some_client_secret".into(), - oauth_authenticate_url: "http://localhost:8080".into(), - oauth_token_url: "http://localhost:8080".into(), - tx_broadcast_urls: vec!["http://localhost:9000".into()], - oauth_token_refresh_interval: 300, // 5 minutes - builder_helper_address: Address::default(), - concurrency_limit: 1000, - }; - Ok(config) - } } diff --git a/tests/tx_poller_test.rs b/tests/tx_poller_test.rs index cd28e68..3d8e5c6 100644 --- a/tests/tx_poller_test.rs +++ b/tests/tx_poller_test.rs @@ -1,18 +1,18 @@ mod tests { - use std::str::FromStr; - - use alloy::consensus::{SignableTransaction, TxEip1559, TxEnvelope}; - use alloy::primitives::{Address, TxKind, U256, bytes}; - use alloy::signers::{SignerSync, local::PrivateKeySigner}; + use alloy::primitives::U256; + use alloy::signers::local::PrivateKeySigner; use builder::config::BuilderConfig; use builder::tasks::tx_poller; + use builder::test_utils::{new_signed_tx, setup_logging, setup_test_config}; // Import the refactored function use eyre::{Ok, Result}; #[ignore = "integration test"] #[tokio::test] async fn test_tx_roundtrip() -> Result<()> { + setup_logging(); + // Create a new test environment - let config = setup_test_config().await?; + let config = setup_test_config()?; // Post a transaction to the cache post_tx(&config).await?; @@ -31,8 +31,9 @@ mod tests { async fn post_tx(config: &BuilderConfig) -> Result<()> { let client = reqwest::Client::new(); + let wallet = PrivateKeySigner::random(); - let tx_envelope = new_test_tx(&wallet)?; + let tx_envelope = new_signed_tx(&wallet, 1, U256::from(1), 10_000)?; let url = format!("{}/transactions", config.tx_pool_url); let response = client.post(&url).json(&tx_envelope).send().await?; @@ -44,52 +45,4 @@ mod tests { Ok(()) } - - // Returns a new signed test transaction with default values - fn new_test_tx(wallet: &PrivateKeySigner) -> Result { - let tx = TxEip1559 { - chain_id: 17001, - nonce: 1, - gas_limit: 50000, - to: TxKind::Call( - Address::from_str("0x0000000000000000000000000000000000000000").unwrap(), - ), - value: U256::from(1_f64), - input: bytes!(""), - ..Default::default() - }; - let signature = wallet.sign_hash_sync(&tx.signature_hash())?; - Ok(TxEnvelope::Eip1559(tx.into_signed(signature))) - } - - // Sets up a block builder with test values - pub async fn setup_test_config() -> Result { - let config = BuilderConfig { - host_chain_id: 17000, - ru_chain_id: 17001, - host_rpc_url: "host-rpc.example.com".into(), - ru_rpc_url: "ru-rpc.example.com".into(), - tx_broadcast_urls: vec!["http://localhost:9000".into()], - zenith_address: Address::default(), - quincey_url: "http://localhost:8080".into(), - builder_port: 8080, - sequencer_key: None, - builder_key: "0000000000000000000000000000000000000000000000000000000000000000".into(), - block_confirmation_buffer: 1, - chain_offset: 0, - target_slot_time: 1, - builder_rewards_address: Address::default(), - rollup_block_gas_limit: 100_000, - tx_pool_url: "http://localhost:9000/".into(), - tx_pool_cache_duration: 5, - oauth_client_id: "some_client_id".into(), - oauth_client_secret: "some_client_secret".into(), - oauth_authenticate_url: "http://localhost:8080".into(), - oauth_token_url: "http://localhost:8080".into(), - oauth_token_refresh_interval: 300, // 5 minutes - builder_helper_address: Address::default(), - concurrency_limit: 1000, - }; - Ok(config) - } }