diff --git a/Cargo.lock b/Cargo.lock index 504c1db3d3..b01dfc7dcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1327,27 +1327,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-object-pool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ac0219111eb7bb7cb76d4cf2cb50c598e7ae549091d3616f9e95442c18486f" -dependencies = [ - "async-lock", - "event-listener", -] - [[package]] name = "async-recursion" version = "1.1.1" @@ -5767,28 +5746,13 @@ checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ "base64 0.21.7", "bytes", - "headers-core 0.2.0", + "headers-core", "http 0.2.12", "httpdate", "mime", "sha1", ] -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64 0.22.1", - "bytes", - "headers-core 0.3.0", - "http 1.4.0", - "httpdate", - "mime", - "sha1", -] - [[package]] name = "headers-core" version = "0.2.0" @@ -5798,15 +5762,6 @@ dependencies = [ "http 0.2.12", ] -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http 1.4.0", -] - [[package]] name = "heapless" version = "0.7.17" @@ -6013,40 +5968,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "httpmock" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4888a4d02d8e1f92ffb6b4965cf5ff56dda36ef41975f41c6fa0f6bde78c4e" -dependencies = [ - "assert-json-diff", - "async-object-pool", - "async-trait", - "base64 0.22.1", - "bytes", - "crossbeam-utils", - "form_urlencoded", - "futures-timer", - "futures-util", - "headers 0.4.1", - "http 1.4.0", - "http-body-util", - "hyper 1.9.0", - "hyper-util", - "path-tree", - "regex", - "serde", - "serde_json", - "serde_regex", - "similar", - "stringmetrics", - "tabwriter", - "thiserror 2.0.18", - "tokio", - "tracing", - "url", -] - [[package]] name = "humantime" version = "2.3.0" @@ -8411,15 +8332,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" -[[package]] -name = "path-tree" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a97453bc21a968f722df730bfe11bd08745cb50d1300b0df2bda131dece136" -dependencies = [ - "smallvec", -] - [[package]] name = "pathfinder" version = "0.22.2" @@ -10530,16 +10442,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_regex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" -dependencies = [ - "regex", - "serde", -] - [[package]] name = "serde_spanned" version = "1.1.1" @@ -10698,12 +10600,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" -[[package]] -name = "similar" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" - [[package]] name = "siphasher" version = "1.0.2" @@ -10926,8 +10822,6 @@ dependencies = [ "fake", "flate2", "futures", - "gateway-test-utils", - "httpmock", "metrics", "mockall 0.11.4", "pathfinder-common", @@ -11055,12 +10949,6 @@ dependencies = [ "precomputed-hash", ] -[[package]] -name = "stringmetrics" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3c8667cd96245cbb600b8dec5680a7319edd719c5aa2b5d23c6bff94f39765" - [[package]] name = "strsim" version = "0.10.0" @@ -11181,15 +11069,6 @@ dependencies = [ "libc", ] -[[package]] -name = "tabwriter" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce91f2f0ec87dff7e6bcbbeb267439aa1188703003c6055193c821487400432" -dependencies = [ - "unicode-width", -] - [[package]] name = "tagptr" version = "0.2.0" @@ -12001,12 +11880,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -12242,7 +12115,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "headers 0.3.9", + "headers", "http 0.2.12", "hyper 0.14.32", "log", diff --git a/Cargo.toml b/Cargo.toml index bf23b42ecc..5cc05346ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ members = [ "crates/gas-price", "crates/gateway-client", "crates/gateway-test-fixtures", - "crates/gateway-test-utils", "crates/gateway-types", "crates/merkle-tree", "crates/p2p", @@ -82,7 +81,6 @@ governor = "0.10.4" hex = "0.4.3" http = "1.4.0" http-body = "1.0.1" -httpmock = "0.8.3" hyper = "1.8.1" ipnet = "2.12.0" jemallocator = "0.5.4" @@ -152,6 +150,7 @@ url = "2.5.8" vergen = { version = "8", default-features = false } void = "1.0.2" warp = "0.3.7" +wiremock = "0.6.5" zeroize = "1.8.2" zstd = "0.13.3" diff --git a/crates/gateway-client/Cargo.toml b/crates/gateway-client/Cargo.toml index fa32602df7..28d7def586 100644 --- a/crates/gateway-client/Cargo.toml +++ b/crates/gateway-client/Cargo.toml @@ -32,8 +32,6 @@ tracing = { workspace = true } assert_matches = { workspace = true } base64 = { workspace = true } fake = { workspace = true } -gateway-test-utils = { path = "../gateway-test-utils" } -httpmock = { workspace = true } pathfinder-crypto = { path = "../crypto" } pretty_assertions_sorted = { workspace = true } reqwest = { workspace = true, features = ["json"] } @@ -42,7 +40,7 @@ test-log = { workspace = true, features = ["trace"] } tracing-subscriber = { workspace = true } warp = { workspace = true, features = ["compression-gzip"] } # Because httpmock doesn't support byte-matching on response body -wiremock = "0.6.5" +wiremock = { workspace = true } [[test]] name = "integration-metrics" diff --git a/crates/gateway-client/src/builder.rs b/crates/gateway-client/src/builder.rs index 45efbbc082..231fec98af 100644 --- a/crates/gateway-client/src/builder.rs +++ b/crates/gateway-client/src/builder.rs @@ -732,31 +732,28 @@ mod tests { mod api_key_is_set_when_configured { use fake::{Fake, Faker}; - use httpmock::prelude::*; - use httpmock::Mock; use serde_json::json; + use wiremock::{matchers, Mock, MockServer, ResponseTemplate}; use crate::Client; - async fn setup_with_fake_api_key(server: &MockServer) -> (Mock<'_>, Client) { + async fn setup_with_fake_api_key(server: &MockServer) -> Client { let api_key = Faker.fake::(); - let mock = server.mock(|when, then| { - when.any_request().header("X-Throttling-Bypass", &api_key); - then.status(200).json_body(json!({})); - }); + Mock::given(matchers::header("X-Throttling-Bypass", &api_key)) + .respond_with(ResponseTemplate::new(200).set_body_json(json!({}))) + .mount(server) + .await; - let client = Client::for_test(server.base_url().parse().unwrap()) + Client::for_test(server.uri().parse().unwrap()) .unwrap() - .with_api_key(Some(api_key.clone())); - - (mock, client) + .with_api_key(Some(api_key)) } #[tokio::test] async fn get() -> anyhow::Result<()> { - let server = MockServer::start_async().await; - let (mock, client) = setup_with_fake_api_key(&server).await; + let server = MockServer::start().await; + let client = setup_with_fake_api_key(&server).await; let _: serde_json::Value = client .clone() @@ -774,15 +771,16 @@ mod tests { .get() .await?; - mock.assert_calls(2); + let requests = server.received_requests().await.unwrap(); + assert_eq!(requests.len(), 2); Ok(()) } #[tokio::test] async fn get_as_bytes() -> anyhow::Result<()> { - let server = MockServer::start_async().await; - let (mock, client) = setup_with_fake_api_key(&server).await; + let server = MockServer::start().await; + let client = setup_with_fake_api_key(&server).await; let _: bytes::Bytes = client .clone() @@ -800,15 +798,16 @@ mod tests { .get_as_bytes() .await?; - mock.assert_calls(2); + let requests = server.received_requests().await.unwrap(); + assert_eq!(requests.len(), 2); Ok(()) } #[tokio::test] async fn post_with_json() -> anyhow::Result<()> { - let server = MockServer::start_async().await; - let (mock, client) = setup_with_fake_api_key(&server).await; + let server = MockServer::start().await; + let client = setup_with_fake_api_key(&server).await; let _: serde_json::Value = client .clone() @@ -826,7 +825,8 @@ mod tests { .post_with_json(&json!({}), None) .await?; - mock.assert_calls(2); + let requests = server.received_requests().await.unwrap(); + assert_eq!(requests.len(), 2); Ok(()) } diff --git a/crates/gateway-client/src/lib.rs b/crates/gateway-client/src/lib.rs index e334785b1b..6715700fa0 100644 --- a/crates/gateway-client/src/lib.rs +++ b/crates/gateway-client/src/lib.rs @@ -746,16 +746,27 @@ impl GatewayApi for Client { #[cfg(test)] mod tests { use assert_matches::assert_matches; - use gateway_test_utils::*; use pathfinder_common::macro_prelude::*; use pathfinder_common::prelude::*; use pathfinder_crypto::Felt; use starknet_gateway_test_fixtures::testnet::*; use starknet_gateway_types::error::KnownStarknetErrorCode; use starknet_gateway_types::request::add_transaction::ContractDefinition; + use wiremock::{matchers, Mock, MockServer, ResponseTemplate}; use super::*; + fn response_body_from(code: KnownStarknetErrorCode) -> serde_json::Value { + use starknet_gateway_types::error::StarknetError; + + let e = StarknetError { + code: code.into(), + message: "".to_string(), + }; + let s = serde_json::to_string(&e).unwrap(); + serde_json::from_str(&s).unwrap() + } + #[test_log::test(tokio::test)] async fn client_user_agent() { use std::convert::Infallible; @@ -800,17 +811,20 @@ mod tests { #[tokio::test] async fn invalid_hash() { - let (_jh, url) = setup([( - format!( - "/feeder_gateway/get_transaction_status?transactionHash={}", - INVALID_TX_HASH.0.to_hex_str() - ), - ( - r#"{"tx_status": "NOT_RECEIVED", "finality_status": "NOT_RECEIVED", "execution_status": null}"#, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_transaction_status")) + .and(matchers::query_param( + "transactionHash", + INVALID_TX_HASH.0.to_hex_str(), + )) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "tx_status": "NOT_RECEIVED", + "finality_status": "NOT_RECEIVED", + "execution_status": serde_json::Value::Null, + }))) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); assert_eq!( client .transaction_status(INVALID_TX_HASH) @@ -824,22 +838,20 @@ mod tests { #[tokio::test] async fn eth_contract_addresses() { - let (_jh, url) = setup([( - "/feeder_gateway/get_contract_addresses", - ( - r#"{ - "FriStatementContract": "0x55d049b4C82807808E76e61a08C6764bbf2ffB55", - "GpsStatementVerifier": "0x2046B966994Adcb88D83f467a41b75d64C2a619F", - "MemoryPageFactRegistry": "0x5628E75245Cc69eCA0994F0449F4dDA9FbB5Ec6a", - "MerkleStatementContract": "0xd414f8f535D4a96cB00fFC8E85160b353cb7809c", - "Starknet": "0x4737c0c1B4D5b1A687B42610DdabEE781152359c", - "strk_l2_token_address": "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", - "eth_l2_token_address": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" - }"#, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_contract_addresses")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "FriStatementContract": "0x55d049b4C82807808E76e61a08C6764bbf2ffB55", + "GpsStatementVerifier": "0x2046B966994Adcb88D83f467a41b75d64C2a619F", + "MemoryPageFactRegistry": "0x5628E75245Cc69eCA0994F0449F4dDA9FbB5Ec6a", + "MerkleStatementContract": "0xd414f8f535D4a96cB00fFC8E85160b353cb7809c", + "Starknet": "0x4737c0c1B4D5b1A687B42610DdabEE781152359c", + "strk_l2_token_address": "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "eth_l2_token_address": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" + }))) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); client.eth_contract_addresses().await.unwrap(); } @@ -900,11 +912,15 @@ mod tests { async fn v0_is_deprecated() { use request::add_transaction::{InvokeFunction, InvokeFunctionV0V1}; - let (_jh, url) = setup([( - "/gateway/add_transaction", - response_from(KnownStarknetErrorCode::DeprecatedTransaction), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::method("POST")) + .and(matchers::path("/gateway/add_transaction")) + .respond_with(ResponseTemplate::new(500).set_body_json(response_body_from( + KnownStarknetErrorCode::DeprecatedTransaction, + ))) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); let (_, fee, sig, nonce, addr, call) = inputs(); let invoke = InvokeFunction::V0(InvokeFunctionV0V1 { max_fee: fee, @@ -926,14 +942,16 @@ mod tests { async fn successful() { use request::add_transaction::{InvokeFunction, InvokeFunctionV0V1}; - let (_jh, url) = setup([( - "/gateway/add_transaction", - ( - r#"{"code":"TRANSACTION_RECEIVED","transaction_hash":"0x0389DD0629F42176CC8B6C43ACEFC0713D0064ECDFC0470E0FC179F53421A38B"}"#, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::method("POST")) + .and(matchers::path("/gateway/add_transaction")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "code": "TRANSACTION_RECEIVED", + "transaction_hash": "0x0389DD0629F42176CC8B6C43ACEFC0713D0064ECDFC0470E0FC179F53421A38B" + }))) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); // test with values dumped from `starknet invoke` for a test contract let (_, fee, sig, nonce, addr, call) = inputs(); let invoke = InvokeFunction::V1(InvokeFunctionV0V1 { @@ -958,11 +976,15 @@ mod tests { async fn v0_is_deprecated() { use request::add_transaction::{Declare, DeclareV0V1V2}; - let (_jh, url) = setup([( - "/gateway/add_transaction", - response_from(KnownStarknetErrorCode::DeprecatedTransaction), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::method("POST")) + .and(matchers::path("/gateway/add_transaction")) + .respond_with(ResponseTemplate::new(500).set_body_json(response_body_from( + KnownStarknetErrorCode::DeprecatedTransaction, + ))) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); let declare = Declare::V0(DeclareV0V1V2 { version: TransactionVersion::ZERO, @@ -987,16 +1009,17 @@ mod tests { async fn successful_v1() { use request::add_transaction::{Declare, DeclareV0V1V2}; - let (_jh, url) = setup([( - "/gateway/add_transaction", - ( - r#"{"code": "TRANSACTION_RECEIVED", - "transaction_hash": "0x77ccba4df42cf0f74a8eb59a96d7880fae371edca5d000ca5f9985652c8a8ed", - "class_hash": "0x711941b11a8236b8cca42b664e19342ac7300abb1dc44957763cb65877c2708"}"#, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::method("POST")) + .and(matchers::path("/gateway/add_transaction")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "code": "TRANSACTION_RECEIVED", + "transaction_hash": "0x77ccba4df42cf0f74a8eb59a96d7880fae371edca5d000ca5f9985652c8a8ed", + "class_hash": "0x711941b11a8236b8cca42b664e19342ac7300abb1dc44957763cb65877c2708" + }))) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); let declare = Declare::V1(DeclareV0V1V2 { version: TransactionVersion::ONE, @@ -1064,16 +1087,17 @@ mod tests { async fn successful_v2() { use request::add_transaction::{Declare, DeclareV0V1V2}; - let (_jh, url) = setup([( - "/gateway/add_transaction", - ( - r#"{"code": "TRANSACTION_RECEIVED", - "transaction_hash": "0x77ccba4df42cf0f74a8eb59a96d7880fae371edca5d000ca5f9985652c8a8ed", - "class_hash": "0x711941b11a8236b8cca42b664e19342ac7300abb1dc44957763cb65877c2708"}"#, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::method("POST")) + .and(matchers::path("/gateway/add_transaction")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "code": "TRANSACTION_RECEIVED", + "transaction_hash": "0x77ccba4df42cf0f74a8eb59a96d7880fae371edca5d000ca5f9985652c8a8ed", + "class_hash": "0x711941b11a8236b8cca42b664e19342ac7300abb1dc44957763cb65877c2708" + }))) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); let declare = Declare::V2(DeclareV0V1V2 { version: TransactionVersion::TWO, @@ -1233,18 +1257,19 @@ mod tests { mod block_header { use super::*; - const REPLY: &str = r#"{ - "block_hash": "0x6a2755817d86ade81ed0fea2eaf23d94264e2f25aff43ecb2e5000bf3ec28b7", - "block_number": 9703 - }"#; - #[test_log::test(tokio::test)] async fn success_by_number() { - let (_jh, url) = setup([( - "/feeder_gateway/get_block?blockNumber=9703&headerOnly=true", - (REPLY.to_owned(), 200), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_block")) + .and(matchers::query_param("blockNumber", "9703")) + .and(matchers::query_param("headerOnly", "true")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "block_hash": "0x6a2755817d86ade81ed0fea2eaf23d94264e2f25aff43ecb2e5000bf3ec28b7", + "block_number": 9703 + }))) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); client .block_header(BlockId::Number(BlockNumber::new_or_panic(9703))) @@ -1254,13 +1279,17 @@ mod tests { #[test_log::test(tokio::test)] async fn success_by_hash() { - let (_jh, url) = setup([( - "/feeder_gateway/get_block?\ - blockHash=0x6a2755817d86ade81ed0fea2eaf23d94264e2f25aff43ecb2e5000bf3ec28b7&\ - headerOnly=true", - (REPLY.to_owned(), 200), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_block")) + .and(matchers::query_param("blockHash", "0x6a2755817d86ade81ed0fea2eaf23d94264e2f25aff43ecb2e5000bf3ec28b7")) + .and(matchers::query_param("headerOnly", "true")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "block_hash": "0x6a2755817d86ade81ed0fea2eaf23d94264e2f25aff43ecb2e5000bf3ec28b7", + "block_number": 9703 + }))) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); client .block_header( @@ -1276,11 +1305,20 @@ mod tests { #[test_log::test(tokio::test)] async fn block_not_found() { const BLOCK_NUMBER: u64 = 99999999; - let (_jh, url) = setup([( - format!("/feeder_gateway/get_block?blockNumber={BLOCK_NUMBER}&headerOnly=true",), - response_from(KnownStarknetErrorCode::BlockNotFound), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_block")) + .and(matchers::query_param( + "blockNumber", + BLOCK_NUMBER.to_string(), + )) + .and(matchers::query_param("headerOnly", "true")) + .respond_with( + ResponseTemplate::new(500) + .set_body_json(response_body_from(KnownStarknetErrorCode::BlockNotFound)), + ) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); let error = client .block_header(BlockNumber::new_or_panic(BLOCK_NUMBER).into()) .await @@ -1297,25 +1335,32 @@ mod tests { #[test_log::test(tokio::test)] async fn success() { - let (_jh, url) = setup([( - "/feeder_gateway/get_state_update?blockNumber=pending&includeBlock=true", - ( - starknet_gateway_test_fixtures::v0_13_1::state_update_with_block::SEPOLIA_INTEGRATION_PENDING, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let body: serde_json::Value = serde_json::from_str(starknet_gateway_test_fixtures::v0_13_1::state_update_with_block::SEPOLIA_INTEGRATION_PENDING).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_state_update")) + .and(matchers::query_param("blockNumber", "pending")) + .and(matchers::query_param("includeBlock", "true")) + .respond_with(ResponseTemplate::new(200).set_body_json(body)) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); client.pending_block().await.unwrap(); } #[test_log::test(tokio::test)] async fn block_not_found() { - let (_jh, url) = setup([( - "/feeder_gateway/get_state_update?blockNumber=pending&includeBlock=true", - response_from(KnownStarknetErrorCode::BlockNotFound), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_state_update")) + .and(matchers::query_param("blockNumber", "pending")) + .and(matchers::query_param("includeBlock", "true")) + .respond_with( + ResponseTemplate::new(500) + .set_body_json(response_body_from(KnownStarknetErrorCode::BlockNotFound)), + ) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); let error = client.pending_block().await.unwrap_err(); assert_matches!( error, @@ -1329,14 +1374,15 @@ mod tests { #[test_log::test(tokio::test)] async fn success() { - let (_jh, url) = setup([( - "/feeder_gateway/get_state_update?blockNumber=9703&includeBlock=true", - ( - starknet_gateway_test_fixtures::v0_13_1::state_update_with_block::SEPOLIA_INTEGRATION_NUMBER_9703, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let body: serde_json::Value = serde_json::from_str(starknet_gateway_test_fixtures::v0_13_1::state_update_with_block::SEPOLIA_INTEGRATION_NUMBER_9703).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_state_update")) + .and(matchers::query_param("blockNumber", "9703")) + .and(matchers::query_param("includeBlock", "true")) + .respond_with(ResponseTemplate::new(200).set_body_json(body)) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); client .state_update_with_block(BlockNumber::new_or_panic(9703)) @@ -1346,14 +1392,15 @@ mod tests { #[test_log::test(tokio::test)] async fn success_0_14_1_with_migrated_compiled_classes() { - let (_jh, url) = setup([( - "/feeder_gateway/get_state_update?blockNumber=3077642&includeBlock=true", - ( - starknet_gateway_test_fixtures::v0_14_1::state_update_with_block::SEPOLIA_INTEGRATION_3077642, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let body: serde_json::Value = serde_json::from_str(starknet_gateway_test_fixtures::v0_14_1::state_update_with_block::SEPOLIA_INTEGRATION_3077642).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_state_update")) + .and(matchers::query_param("blockNumber", "3077642")) + .and(matchers::query_param("includeBlock", "true")) + .respond_with(ResponseTemplate::new(200).set_body_json(body)) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); client .state_update_with_block(BlockNumber::new_or_panic(3077642)) @@ -1365,14 +1412,15 @@ mod tests { // chain. #[test_log::test(tokio::test)] async fn success_0_14_3_with_invoke_proof_facts() { - let (_jh, url) = setup([( - "/feeder_gateway/get_state_update?blockNumber=3077642&includeBlock=true", - ( - starknet_gateway_test_fixtures::v0_14_3::state_update_with_block::SEPOLIA_INTEGRATION_FAKE, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let body: serde_json::Value = serde_json::from_str(starknet_gateway_test_fixtures::v0_14_3::state_update_with_block::SEPOLIA_INTEGRATION_FAKE).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_state_update")) + .and(matchers::query_param("blockNumber", "3077642")) + .and(matchers::query_param("includeBlock", "true")) + .respond_with(ResponseTemplate::new(200).set_body_json(body)) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); client .state_update_with_block(BlockNumber::new_or_panic(3077642)) @@ -1383,13 +1431,20 @@ mod tests { #[test_log::test(tokio::test)] async fn block_not_found() { const BLOCK_NUMBER: u64 = 99999999; - let (_jh, url) = setup([( - format!( - "/feeder_gateway/get_state_update?blockNumber={BLOCK_NUMBER}&includeBlock=true" - ), - response_from(KnownStarknetErrorCode::BlockNotFound), - )]); - let client = Client::for_test(url).unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_state_update")) + .and(matchers::query_param( + "blockNumber", + BLOCK_NUMBER.to_string(), + )) + .and(matchers::query_param("includeBlock", "true")) + .respond_with( + ResponseTemplate::new(500) + .set_body_json(response_body_from(KnownStarknetErrorCode::BlockNotFound)), + ) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); let error = client .state_update_with_block(BlockNumber::new_or_panic(BLOCK_NUMBER)) .await @@ -1406,14 +1461,17 @@ mod tests { #[tokio::test] async fn success() { - let (_jh, url) = setup([( - "/feeder_gateway/get_signature?blockNumber=350000", - ( - starknet_gateway_test_fixtures::v0_13_2::signature::SEPOLIA_INTEGRATION_35748, - 200, - ), - )]); - let client = Client::for_test(url).unwrap(); + let body: serde_json::Value = serde_json::from_str( + starknet_gateway_test_fixtures::v0_13_2::signature::SEPOLIA_INTEGRATION_35748, + ) + .unwrap(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_signature")) + .and(matchers::query_param("blockNumber", "350000")) + .respond_with(ResponseTemplate::new(200).set_body_json(body)) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()).unwrap(); client .signature(BlockId::Number(BlockNumber::new_or_panic(350000))) diff --git a/crates/gateway-client/tests/metrics.rs b/crates/gateway-client/tests/metrics.rs index 83edbc716a..48fb26b7a0 100644 --- a/crates/gateway-client/tests/metrics.rs +++ b/crates/gateway-client/tests/metrics.rs @@ -4,49 +4,62 @@ //! cause weird test failures without any obvious clue to what might have caused //! those failures in the first place. -use std::future::Future; +use std::sync::{Arc, RwLock}; use futures::stream::StreamExt; -use gateway_test_utils::{response_from, setup_with_varied_responses}; use pathfinder_common::BlockNumber; use pretty_assertions_sorted::assert_eq; use starknet_gateway_client::{BlockId, Client, GatewayApi}; -use starknet_gateway_types::error::KnownStarknetErrorCode; +use starknet_gateway_types::error::{test_response_from, KnownStarknetErrorCode}; +use wiremock::{matchers, Mock, MockServer, Request, Respond, ResponseTemplate}; -#[tokio::test] -async fn all_counter_types_including_tags() { - with_method( - "get_block", - |client, x| async move { - let _ = client.block_header(x).await; - }, - ( - r#"{"block_hash": "0x7d328a71faf48c5c3857e99f20a77b18522480956d1cd5bff1ff2df3c8b427b", "block_number": 0}"# - .to_owned(), - 200, - ), - ) - .await; +struct VariedResponse { + counter: Arc>, + responses: Vec<(String, u16)>, +} + +impl VariedResponse { + pub fn new(responses: Vec<(String, u16)>) -> Self { + Self { + counter: Arc::new(RwLock::new(0)), + responses, + } + } +} + +impl Respond for VariedResponse { + fn respond(&self, _request: &Request) -> ResponseTemplate { + let mut counter = self.counter.write().unwrap(); + if *counter < self.responses.len() { + let rsp_def = &self.responses[*counter]; + *counter += 1; + ResponseTemplate::new(rsp_def.1).set_body_string(rsp_def.0.clone()) + } else { + panic!("{} responses already exhausted", self.responses.len()); + } + } } -async fn with_method(method_name: &'static str, f: F, response: (String, u16)) -where - F: Fn(Client, BlockId) -> Fut, - Fut: Future, -{ +#[tokio::test] +async fn all_counter_types_including_tags() { use pathfinder_common::test_utils::metrics::FakeRecorder; + let method_name = "get_block"; + let method_call = |client: Client, x| async move { + let _ = client.block_header(x).await; + }; + let recorder = FakeRecorder::new_for(&["get_block"]); let handle = recorder.handle(); // Automatically deregister the recorder let _guard = metrics::set_default_local_recorder(&recorder); - let responses = [ + let responses = vec![ // Any valid fixture - response, + (r#"{"block_hash": "0x7d328a71faf48c5c3857e99f20a77b18522480956d1cd5bff1ff2df3c8b427b", "block_number": 0}"#.to_owned(), 200), // 1 Starknet error - response_from(KnownStarknetErrorCode::BlockNotFound), + test_response_from(KnownStarknetErrorCode::BlockNotFound), // 2 decode errors (r#"{"not":"valid"}"#.to_owned(), 200), (r#"{"not":"valid, again"}"#.to_owned(), 200), @@ -56,27 +69,34 @@ where ("".to_owned(), 429), ]; - let (_jh, url) = setup_with_varied_responses([ - ( - format!("/feeder_gateway/{method_name}?blockNumber=123&headerOnly=true"), - responses.clone(), - ), - ( - format!("/feeder_gateway/{method_name}?blockNumber=latest&headerOnly=true"), - responses.clone(), - ), - ( - format!("/feeder_gateway/{method_name}?blockNumber=pending&headerOnly=true"), - responses, - ), - ]); - let client = Client::for_test(url).unwrap().disable_retry_for_tests(); + let server = MockServer::start().await; + Mock::given(matchers::path("/feeder_gateway/get_block")) + .and(matchers::query_param("blockNumber", "123")) + .and(matchers::query_param("headerOnly", "true")) + .respond_with(VariedResponse::new(responses.clone())) + .mount(&server) + .await; + Mock::given(matchers::path("/feeder_gateway/get_block")) + .and(matchers::query_param("blockNumber", "latest")) + .and(matchers::query_param("headerOnly", "true")) + .respond_with(VariedResponse::new(responses.clone())) + .mount(&server) + .await; + Mock::given(matchers::path("/feeder_gateway/get_block")) + .and(matchers::query_param("blockNumber", "pending")) + .and(matchers::query_param("headerOnly", "true")) + .respond_with(VariedResponse::new(responses)) + .mount(&server) + .await; + let client = Client::for_test(server.uri().parse().unwrap()) + .unwrap() + .disable_retry_for_tests(); [BlockId::Number(BlockNumber::new_or_panic(123)); 7] .into_iter() .chain([BlockId::Latest; 7].into_iter()) .chain([BlockId::Pending; 7].into_iter()) - .map(|x| f(client.clone(), x)) + .map(|x| method_call(client.clone(), x)) .collect::>() .collect::>() .await; diff --git a/crates/gateway-types/src/error.rs b/crates/gateway-types/src/error.rs index 384299c12d..11193be8c7 100644 --- a/crates/gateway-types/src/error.rs +++ b/crates/gateway-types/src/error.rs @@ -135,6 +135,24 @@ pub enum KnownStarknetErrorCode { InvalidProof, } +/// Helper function which allows for easy creation of a response tuple +/// that contains a +/// [StarknetError](starknet_gateway_types::error::StarknetError) for a +/// given [KnownStarknetErrorCode]. +/// +/// The response tuple can then be used by the [setup] function. +/// +/// The `message` field is always an empty string. +/// The HTTP status code for this response is always `500` (`Internal Server +/// Error`). +pub fn test_response_from(code: KnownStarknetErrorCode) -> (String, u16) { + let e = StarknetError { + code: code.into(), + message: "".to_string(), + }; + (serde_json::to_string(&e).unwrap(), 500) +} + #[cfg(test)] mod tests { use super::StarknetErrorCode;