diff --git a/Cargo.lock b/Cargo.lock index 7a9e185d4dd..6f8761c4a70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10486,15 +10486,19 @@ name = "starknet_http_server" version = "0.0.0" dependencies = [ "axum", + "blockifier", "hyper 0.14.30", "infra_utils", + "mempool_test_utils", "metrics 0.21.1", + "metrics-exporter-prometheus", "papyrus_config", "reqwest 0.11.27", "serde", "serde_json", "starknet_api", "starknet_gateway_types", + "starknet_http_server", "starknet_sequencer_infra", "thiserror", "tokio", diff --git a/crates/starknet_http_server/Cargo.toml b/crates/starknet_http_server/Cargo.toml index 0ea77da0728..c6bfa7598f3 100644 --- a/crates/starknet_http_server/Cargo.toml +++ b/crates/starknet_http_server/Cargo.toml @@ -27,5 +27,11 @@ tracing.workspace = true validator.workspace = true [dev-dependencies] +blockifier = { workspace = true, features = ["testing"] } +mempool_test_utils.workspace = true +metrics-exporter-prometheus.workspace = true serde_json.workspace = true +starknet_api = { workspace = true, features = ["testing"] } +starknet_gateway_types = { workspace = true, features = ["testing"] } +starknet_http_server = { workspace = true, features = ["testing"] } tokio = { workspace = true, features = ["rt"] } diff --git a/crates/starknet_http_server/src/http_server_test.rs b/crates/starknet_http_server/src/http_server_test.rs index 787bed37f10..733d626ca3d 100644 --- a/crates/starknet_http_server/src/http_server_test.rs +++ b/crates/starknet_http_server/src/http_server_test.rs @@ -1,9 +1,26 @@ +use std::net::SocketAddr; +use std::sync::Arc; + use axum::body::{Bytes, HttpBody}; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; +use blockifier::test_utils::CairoVersion; +use infra_utils::metrics::parse_numeric_metric; +use mempool_test_utils::starknet_api_test_utils::invoke_tx; +use metrics_exporter_prometheus::PrometheusBuilder; use starknet_api::transaction::TransactionHash; +use starknet_gateway_types::communication::{GatewayClientError, MockGatewayClient}; +use starknet_sequencer_infra::component_client::ClientError; +use tokio::task; -use crate::http_server::add_tx_result_as_json; +use crate::config::HttpServerConfig; +use crate::http_server::{add_tx_result_as_json, HttpServer}; +use crate::metrics::{ + ADDED_TRANSACTIONS_FAILURE, + ADDED_TRANSACTIONS_SUCCESS, + ADDED_TRANSACTIONS_TOTAL, +}; +use crate::test_utils::HttpTestClient; #[tokio::test] async fn test_tx_hash_json_conversion() { @@ -20,3 +37,65 @@ async fn test_tx_hash_json_conversion() { async fn to_bytes(res: Response) -> Bytes { res.into_body().collect().await.unwrap().to_bytes() } + +#[tokio::test] +async fn get_metrics_test() { + let prometheus_handle = PrometheusBuilder::new() + .install_recorder() + .expect("should be able to build the recorder and install it globally"); + + // TODO(Tsabary): there is a bug in the http server where a failed gateway request crashes it. + // The current setup does not test that. Fix the bug and update this test accordingly. + // Create a mock gateway client that returns arbitrary responses. + const TXS_TO_SEND: usize = 3; + let success_txs_to_send = TXS_TO_SEND; + let failure_txs_to_send = TXS_TO_SEND - success_txs_to_send; + let mut mock_gateway_client = MockGatewayClient::new(); + let mut i = 0; + mock_gateway_client.expect_add_tx().times(TXS_TO_SEND).returning(move |_| { + // TODO(Tsabary): the following discombobulated mechanism is a placeholder for the future + // test, where there will be failed requests as well. + i += 1; + match i { + 0 => Err(GatewayClientError::ClientError(ClientError::UnexpectedResponse( + "mock response".to_string(), + ))), + 1 => Ok(TransactionHash::default()), + _ => Ok(TransactionHash::default()), + } + }); + + // TODO(Tsabary): replace the const port with something that is not hardcoded. + // Create and run the server. + let http_server_config = HttpServerConfig { ip: "127.0.0.1".parse().unwrap(), port: 15123 }; + let mut http_server = + HttpServer::new(http_server_config.clone(), Arc::new(mock_gateway_client)); + tokio::spawn(async move { http_server.run().await }); + + let HttpServerConfig { ip, port } = http_server_config; + let add_tx_http_client = HttpTestClient::new(SocketAddr::from((ip, port))); + + // Ensure the server starts running. + task::yield_now().await; + + // Send transactions to the server. + for _ in std::iter::repeat(()).take(TXS_TO_SEND) { + let rpc_tx = invoke_tx(CairoVersion::default()); + add_tx_http_client.add_tx(rpc_tx).await; + } + + // Obtain metrics. + let metrics = prometheus_handle.render(); + + let added_transactions_total_count = + parse_numeric_metric::(&metrics, ADDED_TRANSACTIONS_TOTAL.0); + let added_transactions_success_count = + parse_numeric_metric::(&metrics, ADDED_TRANSACTIONS_SUCCESS.0); + let added_transactions_failure_count = + parse_numeric_metric::(&metrics, ADDED_TRANSACTIONS_FAILURE.0); + + // Ensure the metrics are as expected. + assert_eq!(added_transactions_total_count.unwrap(), TXS_TO_SEND); + assert_eq!(added_transactions_success_count.unwrap(), success_txs_to_send); + assert_eq!(added_transactions_failure_count.unwrap(), failure_txs_to_send); +} diff --git a/crates/starknet_http_server/src/metrics.rs b/crates/starknet_http_server/src/metrics.rs index 4db77daac92..e84840cf721 100644 --- a/crates/starknet_http_server/src/metrics.rs +++ b/crates/starknet_http_server/src/metrics.rs @@ -1,11 +1,12 @@ use metrics::{absolute_counter, describe_counter, register_counter}; use tracing::info; -const ADDED_TRANSACTIONS_TOTAL: (&str, &str, u64) = +// TODO(Tsabary): add tests for metrics. +pub(crate) const ADDED_TRANSACTIONS_TOTAL: (&str, &str, u64) = ("ADDED_TRANSACTIONS_TOTAL", "Total number of transactions added", 0); -const ADDED_TRANSACTIONS_SUCCESS: (&str, &str, u64) = +pub(crate) const ADDED_TRANSACTIONS_SUCCESS: (&str, &str, u64) = ("ADDED_TRANSACTIONS_SUCCESS", "Number of successfully added transactions", 0); -const ADDED_TRANSACTIONS_FAILURE: (&str, &str, u64) = +pub(crate) const ADDED_TRANSACTIONS_FAILURE: (&str, &str, u64) = ("ADDED_TRANSACTIONS_FAILURE", "Number of faulty added transactions", 0); pub(crate) fn init_metrics() {