From 0ab06cfd31dce418b7c20e5bd4f156f239ccbc10 Mon Sep 17 00:00:00 2001 From: Itay Tsabary Date: Wed, 27 Nov 2024 19:54:44 +0200 Subject: [PATCH 1/2] chore(tests_integration): move http client test util commit-id:2d145538 --- Cargo.lock | 4 +- crates/starknet_http_server/Cargo.toml | 5 ++ crates/starknet_http_server/src/lib.rs | 2 + crates/starknet_http_server/src/test_utils.rs | 55 +++++++++++++++++++ crates/starknet_integration_tests/Cargo.toml | 4 +- .../src/flow_test_setup.rs | 3 +- .../src/integration_test_setup.rs | 3 +- .../starknet_integration_tests/src/utils.rs | 47 +--------------- .../tests/mempool_p2p_flow_test.rs | 2 +- 9 files changed, 71 insertions(+), 54 deletions(-) create mode 100644 crates/starknet_http_server/src/test_utils.rs diff --git a/Cargo.lock b/Cargo.lock index 3fddafbc6b..763688914b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10415,7 +10415,9 @@ version = "0.0.0" dependencies = [ "axum", "hyper 0.14.30", + "mempool_test_utils", "papyrus_config", + "reqwest 0.11.27", "serde", "serde_json", "starknet_api", @@ -10433,7 +10435,6 @@ version = "0.0.0" dependencies = [ "anyhow", "assert_matches", - "axum", "blockifier", "cairo-lang-starknet-classes", "futures", @@ -10448,7 +10449,6 @@ dependencies = [ "papyrus_rpc", "papyrus_storage", "pretty_assertions", - "reqwest 0.11.27", "rstest", "serde_json", "starknet-types-core", diff --git a/crates/starknet_http_server/Cargo.toml b/crates/starknet_http_server/Cargo.toml index 0503a48283..1e62232cb5 100644 --- a/crates/starknet_http_server/Cargo.toml +++ b/crates/starknet_http_server/Cargo.toml @@ -5,13 +5,18 @@ edition.workspace = true license.workspace = true repository.workspace = true +[features] +testing = ["mempool_test_utils", "reqwest"] + [lints] workspace = true [dependencies] axum.workspace = true hyper.workspace = true +mempool_test_utils = { workspace = true, optional = true } papyrus_config.workspace = true +reqwest = { workspace = true, optional = true } serde.workspace = true starknet_api.workspace = true starknet_gateway_types.workspace = true diff --git a/crates/starknet_http_server/src/lib.rs b/crates/starknet_http_server/src/lib.rs index 762c8af043..350df0d4c2 100644 --- a/crates/starknet_http_server/src/lib.rs +++ b/crates/starknet_http_server/src/lib.rs @@ -2,3 +2,5 @@ pub mod communication; pub mod config; pub mod errors; pub mod http_server; +#[cfg(feature = "testing")] +pub mod test_utils; diff --git a/crates/starknet_http_server/src/test_utils.rs b/crates/starknet_http_server/src/test_utils.rs new file mode 100644 index 0000000000..8a319a8121 --- /dev/null +++ b/crates/starknet_http_server/src/test_utils.rs @@ -0,0 +1,55 @@ +use std::net::SocketAddr; + +use axum::body::Body; +use mempool_test_utils::starknet_api_test_utils::rpc_tx_to_json; +use reqwest::{Client, Response}; +use starknet_api::rpc_transaction::RpcTransaction; +use starknet_api::transaction::TransactionHash; +use starknet_gateway_types::errors::GatewaySpecError; +use starknet_sequencer_infra::test_utils::get_available_socket; + +use crate::config::HttpServerConfig; + +/// A test utility client for interacting with an http server. +pub struct HttpTestClient { + socket: SocketAddr, + client: Client, +} + +impl HttpTestClient { + pub fn new(socket: SocketAddr) -> Self { + let client = Client::new(); + Self { socket, client } + } + + pub async fn assert_add_tx_success(&self, rpc_tx: RpcTransaction) -> TransactionHash { + let response = self.add_tx(rpc_tx).await; + assert!(response.status().is_success()); + + response.json().await.unwrap() + } + + // TODO: implement when usage eventually arises. + pub async fn assert_add_tx_error(&self, _tx: RpcTransaction) -> GatewaySpecError { + todo!() + } + + // Prefer using assert_add_tx_success or other higher level methods of this client, to ensure + // tests are boilerplate and implementation-detail free. + pub async fn add_tx(&self, rpc_tx: RpcTransaction) -> Response { + let tx_json = rpc_tx_to_json(&rpc_tx); + self.client + .post(format!("http://{}/add_tx", self.socket)) + .header("content-type", "application/json") + .body(Body::from(tx_json)) + .send() + .await + .unwrap() + } +} + +pub async fn create_http_server_config() -> HttpServerConfig { + // TODO(Tsabary): use ser_generated_param. + let socket = get_available_socket().await; + HttpServerConfig { ip: socket.ip(), port: socket.port() } +} diff --git a/crates/starknet_integration_tests/Cargo.toml b/crates/starknet_integration_tests/Cargo.toml index 8b3b43ef64..88427a77e5 100644 --- a/crates/starknet_integration_tests/Cargo.toml +++ b/crates/starknet_integration_tests/Cargo.toml @@ -11,7 +11,6 @@ workspace = true [dependencies] anyhow.workspace = true assert_matches.workspace = true -axum.workspace = true blockifier.workspace = true cairo-lang-starknet-classes.workspace = true futures.workspace = true @@ -24,7 +23,6 @@ papyrus_network = { workspace = true, features = ["testing"] } papyrus_protobuf.workspace = true papyrus_rpc.workspace = true papyrus_storage = { workspace = true, features = ["testing"] } -reqwest.workspace = true serde_json.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true @@ -33,7 +31,7 @@ starknet_client.workspace = true starknet_consensus_manager.workspace = true starknet_gateway = { workspace = true, features = ["testing"] } starknet_gateway_types.workspace = true -starknet_http_server.workspace = true +starknet_http_server = { workspace = true, features = ["testing"] } starknet_mempool_p2p.workspace = true starknet_monitoring_endpoint = { workspace = true, features = ["testing"] } starknet_sequencer_infra = { workspace = true, features = ["testing"] } diff --git a/crates/starknet_integration_tests/src/flow_test_setup.rs b/crates/starknet_integration_tests/src/flow_test_setup.rs index cecefd60cd..500746aa7f 100644 --- a/crates/starknet_integration_tests/src/flow_test_setup.rs +++ b/crates/starknet_integration_tests/src/flow_test_setup.rs @@ -7,6 +7,7 @@ use starknet_api::rpc_transaction::RpcTransaction; use starknet_api::transaction::TransactionHash; use starknet_gateway_types::errors::GatewaySpecError; use starknet_http_server::config::HttpServerConfig; +use starknet_http_server::test_utils::HttpTestClient; use starknet_sequencer_infra::trace_util::configure_tracing; use starknet_sequencer_node::servers::run_component_servers; use starknet_sequencer_node::utils::create_node_modules; @@ -16,7 +17,7 @@ use tokio::runtime::Handle; use tokio::task::JoinHandle; use crate::state_reader::{spawn_test_rpc_state_reader, StorageTestSetup}; -use crate::utils::{create_chain_info, create_config, HttpTestClient}; +use crate::utils::{create_chain_info, create_config}; pub struct FlowTestSetup { pub task_executor: TokioExecutor, diff --git a/crates/starknet_integration_tests/src/integration_test_setup.rs b/crates/starknet_integration_tests/src/integration_test_setup.rs index 3beaf9cf17..770c5be416 100644 --- a/crates/starknet_integration_tests/src/integration_test_setup.rs +++ b/crates/starknet_integration_tests/src/integration_test_setup.rs @@ -4,13 +4,14 @@ use std::path::PathBuf; use mempool_test_utils::starknet_api_test_utils::MultiAccountTransactionGenerator; use papyrus_storage::StorageConfig; use starknet_http_server::config::HttpServerConfig; +use starknet_http_server::test_utils::HttpTestClient; use starknet_monitoring_endpoint::config::MonitoringEndpointConfig; use starknet_monitoring_endpoint::test_utils::IsAliveClient; use tempfile::{tempdir, TempDir}; use crate::config_utils::dump_config_file_changes; use crate::state_reader::{spawn_test_rpc_state_reader, StorageTestSetup}; -use crate::utils::{create_chain_info, create_config, HttpTestClient}; +use crate::utils::{create_chain_info, create_config}; pub struct IntegrationTestSetup { // Client for adding transactions to the sequencer node. diff --git a/crates/starknet_integration_tests/src/utils.rs b/crates/starknet_integration_tests/src/utils.rs index fb89e98051..f8cf3f1e06 100644 --- a/crates/starknet_integration_tests/src/utils.rs +++ b/crates/starknet_integration_tests/src/utils.rs @@ -2,21 +2,15 @@ use std::future::Future; use std::net::SocketAddr; use std::time::Duration; -use axum::body::Body; use blockifier::context::ChainInfo; use blockifier::test_utils::contracts::FeatureContract; use blockifier::test_utils::CairoVersion; -use mempool_test_utils::starknet_api_test_utils::{ - rpc_tx_to_json, - AccountId, - MultiAccountTransactionGenerator, -}; +use mempool_test_utils::starknet_api_test_utils::{AccountId, MultiAccountTransactionGenerator}; use papyrus_consensus::config::ConsensusConfig; use papyrus_network::network_manager::test_utils::create_network_config_connected_to_broadcast_channels; use papyrus_network::network_manager::BroadcastTopicChannels; use papyrus_protobuf::consensus::ProposalPart; use papyrus_storage::StorageConfig; -use reqwest::{Client, Response}; use starknet_api::block::BlockNumber; use starknet_api::contract_address; use starknet_api::core::ContractAddress; @@ -31,7 +25,6 @@ use starknet_gateway::config::{ StatefulTransactionValidatorConfig, StatelessTransactionValidatorConfig, }; -use starknet_gateway_types::errors::GatewaySpecError; use starknet_http_server::config::HttpServerConfig; use starknet_sequencer_infra::test_utils::get_available_socket; use starknet_sequencer_node::config::node_config::SequencerNodeConfig; @@ -105,44 +98,6 @@ pub fn test_rpc_state_reader_config(rpc_server_addr: SocketAddr) -> RpcStateRead } } -/// A test utility client for interacting with an http server. -pub struct HttpTestClient { - socket: SocketAddr, - client: Client, -} - -impl HttpTestClient { - pub fn new(socket: SocketAddr) -> Self { - let client = Client::new(); - Self { socket, client } - } - - pub async fn assert_add_tx_success(&self, rpc_tx: RpcTransaction) -> TransactionHash { - let response = self.add_tx(rpc_tx).await; - assert!(response.status().is_success()); - - response.json().await.unwrap() - } - - // TODO: implement when usage eventually arises. - pub async fn assert_add_tx_error(&self, _tx: RpcTransaction) -> GatewaySpecError { - todo!() - } - - // Prefer using assert_add_tx_success or other higher level methods of this client, to ensure - // tests are boilerplate and implementation-detail free. - pub async fn add_tx(&self, rpc_tx: RpcTransaction) -> Response { - let tx_json = rpc_tx_to_json(&rpc_tx); - self.client - .post(format!("http://{}/add_tx", self.socket)) - .header("content-type", "application/json") - .body(Body::from(tx_json)) - .send() - .await - .unwrap() - } -} - /// Creates a multi-account transaction generator for integration tests. pub fn create_integration_test_tx_generator() -> MultiAccountTransactionGenerator { let mut tx_generator: MultiAccountTransactionGenerator = diff --git a/crates/starknet_integration_tests/tests/mempool_p2p_flow_test.rs b/crates/starknet_integration_tests/tests/mempool_p2p_flow_test.rs index 2c1f5d42ae..e67fddad95 100644 --- a/crates/starknet_integration_tests/tests/mempool_p2p_flow_test.rs +++ b/crates/starknet_integration_tests/tests/mempool_p2p_flow_test.rs @@ -9,6 +9,7 @@ use papyrus_protobuf::mempool::RpcTransactionWrapper; use rstest::{fixture, rstest}; use starknet_api::rpc_transaction::RpcTransaction; use starknet_http_server::config::HttpServerConfig; +use starknet_http_server::test_utils::HttpTestClient; use starknet_integration_tests::state_reader::{spawn_test_rpc_state_reader, StorageTestSetup}; use starknet_integration_tests::utils::{ create_batcher_config, @@ -18,7 +19,6 @@ use starknet_integration_tests::utils::{ create_integration_test_tx_generator, run_integration_test_scenario, test_rpc_state_reader_config, - HttpTestClient, }; use starknet_mempool_p2p::config::MempoolP2pConfig; use starknet_mempool_p2p::MEMPOOL_TOPIC; From 9dc416ae3fa421557c2698fc077367e2aa381550 Mon Sep 17 00:00:00 2001 From: Itay Tsabary Date: Wed, 27 Nov 2024 20:18:04 +0200 Subject: [PATCH 2/2] chore(tests_integration): move node runner test util commit-id:2959cc90 --- .../tests/end_to_end_integration_test.rs | 40 +------------------ .../src/test_utils/compilation.rs | 37 ++++++++++++++++- 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/crates/starknet_integration_tests/tests/end_to_end_integration_test.rs b/crates/starknet_integration_tests/tests/end_to_end_integration_test.rs index d2af0e5e30..ab0ba4d67b 100644 --- a/crates/starknet_integration_tests/tests/end_to_end_integration_test.rs +++ b/crates/starknet_integration_tests/tests/end_to_end_integration_test.rs @@ -1,9 +1,5 @@ -use std::path::PathBuf; -use std::process::Stdio; use std::time::Duration; -use infra_utils::command::create_shell_command; -use infra_utils::path::resolve_project_relative_path; use mempool_test_utils::starknet_api_test_utils::{AccountId, MultiAccountTransactionGenerator}; use papyrus_execution::execution_utils::get_nonce_at; use papyrus_storage::state::StateStorageReader; @@ -15,10 +11,8 @@ use starknet_api::state::StateNumber; use starknet_integration_tests::integration_test_setup::IntegrationTestSetup; use starknet_integration_tests::utils::{create_integration_test_tx_generator, send_account_txs}; use starknet_sequencer_infra::trace_util::configure_tracing; -use starknet_sequencer_node::test_utils::compilation::{compile_node_result, NODE_EXECUTABLE_PATH}; +use starknet_sequencer_node::test_utils::compilation::spawn_run_node; use starknet_types_core::felt::Felt; -use tokio::process::Child; -use tokio::task::{self, JoinHandle}; use tokio::time::interval; use tracing::{error, info}; @@ -27,38 +21,6 @@ fn tx_generator() -> MultiAccountTransactionGenerator { create_integration_test_tx_generator() } -// TODO(Tsabary): Move to a suitable util location. -async fn spawn_node_child_task(node_config_path: PathBuf) -> Child { - // TODO(Tsabary): Capture output to a log file, and present it in case of a failure. - info!("Compiling the node."); - compile_node_result().await.expect("Failed to compile the sequencer node."); - let node_executable = resolve_project_relative_path(NODE_EXECUTABLE_PATH) - .expect("Node executable should be available") - .to_string_lossy() - .to_string(); - - info!("Running the node from: {}", node_executable); - create_shell_command(node_executable.as_str()) - .arg("--config_file") - .arg(node_config_path.to_str().unwrap()) - .stderr(Stdio::inherit()) - .stdout(Stdio::null()) - .kill_on_drop(true) // Required for stopping the node when the handle is dropped. - .spawn() - .expect("Failed to spawn the sequencer node.") -} - -async fn spawn_run_node(node_config_path: PathBuf) -> JoinHandle<()> { - task::spawn(async move { - info!("Running the node from its spawned task."); - let _node_run_result = spawn_node_child_task(node_config_path). - await. // awaits the completion of spawn_node_child_task. - wait(). // runs the node until completion -- should be running indefinitely. - await; // awaits the completion of the node. - panic!("Node stopped unexpectedly."); - }) -} - /// Reads the latest block number from the storage. fn get_latest_block_number(storage_reader: &StorageReader) -> BlockNumber { let txn = storage_reader.begin_ro_txn().unwrap(); diff --git a/crates/starknet_sequencer_node/src/test_utils/compilation.rs b/crates/starknet_sequencer_node/src/test_utils/compilation.rs index 5975f44073..b1fa967fb8 100644 --- a/crates/starknet_sequencer_node/src/test_utils/compilation.rs +++ b/crates/starknet_sequencer_node/src/test_utils/compilation.rs @@ -1,8 +1,12 @@ use std::io; +use std::path::PathBuf; use std::process::{ExitStatus, Stdio}; use infra_utils::command::create_shell_command; -use tracing::info; +use infra_utils::path::resolve_project_relative_path; +use tokio::process::Child; +use tokio::task::{self, JoinHandle}; +use tracing::{error, info}; pub const NODE_EXECUTABLE_PATH: &str = "target/debug/starknet_sequencer_node"; @@ -47,3 +51,34 @@ pub async fn compile_node_result() -> Result<(), NodeCompilationError> { Err(e) => Err(NodeCompilationError::IO(e)), } } + +pub async fn spawn_run_node(node_config_path: PathBuf) -> JoinHandle<()> { + task::spawn(async move { + info!("Running the node from its spawned task."); + let _node_run_result = spawn_node_child_task(node_config_path). + await. // awaits the completion of spawn_node_child_task. + wait(). // runs the node until completion -- should be running indefinitely. + await; // awaits the completion of the node. + panic!("Node stopped unexpectedly."); + }) +} + +async fn spawn_node_child_task(node_config_path: PathBuf) -> Child { + // TODO(Tsabary): Capture output to a log file, and present it in case of a failure. + info!("Compiling the node."); + compile_node_result().await.expect("Failed to compile the sequencer node."); + let node_executable = resolve_project_relative_path(NODE_EXECUTABLE_PATH) + .expect("Node executable should be available") + .to_string_lossy() + .to_string(); + + info!("Running the node from: {}", node_executable); + create_shell_command(node_executable.as_str()) + .arg("--config_file") + .arg(node_config_path.to_str().unwrap()) + .stderr(Stdio::inherit()) + .stdout(Stdio::null()) + .kill_on_drop(true) // Required for stopping the node when the handle is dropped. + .spawn() + .expect("Failed to spawn the sequencer node.") +}