diff --git a/Cargo.lock b/Cargo.lock index 6789ea504..d66d20a6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4601,6 +4601,7 @@ dependencies = [ "ed25519-dalek", "fs_extra", "hex", + "httpmock", "predicates", "sep5", "serde_json", diff --git a/cmd/crates/soroban-test/Cargo.toml b/cmd/crates/soroban-test/Cargo.toml index d035652d1..13d313cba 100644 --- a/cmd/crates/soroban-test/Cargo.toml +++ b/cmd/crates/soroban-test/Cargo.toml @@ -32,7 +32,7 @@ assert_fs = "1.0.7" predicates = { workspace = true } fs_extra = "1.3.0" toml = { workspace = true } - +httpmock = "0.7.0" [dev-dependencies] serde_json = "1.0.93" diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index f0aaa5325..c2e3d9ea8 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -118,6 +118,22 @@ impl TestEnv { env } + pub fn with_rpc_provider(rpc_url: &str, rpc_headers: Vec<(String, String)>) -> TestEnv { + let mut env = TestEnv { + network: network::Network { + rpc_url: rpc_url.to_string(), + rpc_headers, + ..Default::default() + }, + ..Default::default() + }; + if let Ok(network_passphrase) = std::env::var("STELLAR_NETWORK_PASSPHRASE") { + env.network.network_passphrase = network_passphrase; + }; + env.generate_account("test", None).assert().success(); + env + } + pub fn new() -> TestEnv { if let Ok(rpc_url) = std::env::var("SOROBAN_RPC_URL") { return Self::with_rpc_url(&rpc_url); @@ -139,6 +155,13 @@ impl TestEnv { cmd.arg(subcommand) .env("SOROBAN_ACCOUNT", TEST_ACCOUNT) .env("SOROBAN_RPC_URL", &self.network.rpc_url) + .env( + "STELLAR_RPC_HEADERS", + format!( + "{}:{}", + &self.network.rpc_headers[0].0, &self.network.rpc_headers[0].1 + ), + ) .env("SOROBAN_NETWORK_PASSPHRASE", LOCAL_NETWORK_PASSPHRASE) .env("XDG_CONFIG_HOME", self.temp_dir.join("config").as_os_str()) .env("XDG_DATA_HOME", self.temp_dir.join("data").as_os_str()) diff --git a/cmd/crates/soroban-test/tests/it/main.rs b/cmd/crates/soroban-test/tests/it/main.rs index e06c1a47d..4dc54a194 100644 --- a/cmd/crates/soroban-test/tests/it/main.rs +++ b/cmd/crates/soroban-test/tests/it/main.rs @@ -6,5 +6,6 @@ mod init; // #[cfg(feature = "it")] mod integration; mod plugin; +mod rpc_provider; mod util; mod version; diff --git a/cmd/crates/soroban-test/tests/it/rpc_provider.rs b/cmd/crates/soroban-test/tests/it/rpc_provider.rs new file mode 100644 index 000000000..f32e07b4f --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/rpc_provider.rs @@ -0,0 +1,98 @@ +use httpmock::{prelude::*, Mock}; +use serde_json::json; +use soroban_cli::commands::{env, tx::new}; +use soroban_rpc::{GetEventsResponse, GetNetworkResponse}; +use soroban_test::{TestEnv, LOCAL_NETWORK_PASSPHRASE}; + +#[tokio::test] +async fn test_use_rpc_provider_with_auth_header() { + // mock out http request to rpc provider + let server = MockServer::start(); + let generate_account_mock = mock_generate_account(&server); + let get_network_mock = mock_get_network(&server); + let get_events_mock = mock_get_events(&server); + + // create a new test environment with the mock server + let rpc_url = server.url(""); + let rpc_headers = vec![("Authorization".to_string(), "Bearer test-token".to_string())]; + let sandbox = &TestEnv::with_rpc_provider(&rpc_url, rpc_headers); + + sandbox + .new_assert_cmd("events") + .arg("--start-ledger") + .arg("1000") + .assert() + .success(); + + // generate account is being called in `with_rpc_provider` + generate_account_mock.assert(); + // get_network and get_events are being called in the `stellar events` command + get_network_mock.assert(); + get_events_mock.assert(); +} + +fn mock_generate_account(server: &MockServer) -> Mock { + server.mock(|when, then| { + when.method(GET) + .path("/friendbot") + .header("accept", "*/*") + .header("user-agent", "soroban-cli/22.0.1"); //update this to be future proof + then.status(200); + }) +} + +fn mock_get_network(server: &MockServer) -> Mock { + server.mock(|when, then| { + when.method(POST) + .path("/") + .header("authorization", "Bearer test-token") + .json_body(json!({ + "jsonrpc": "2.0", + "id": 0, + "method": "getNetwork" + })); + + then.status(200).json_body(json!({ + "jsonrpc": "2.0", + "id": 0, + "result": GetNetworkResponse { + friendbot_url: None, + passphrase: LOCAL_NETWORK_PASSPHRASE.to_string(), + protocol_version: 22} + })); + }) +} + +fn mock_get_events(server: &MockServer) -> Mock { + server.mock(|when, then| { + when.method(POST) + .path("/") + .header("authorization", "Bearer test-token") + .json_body(json!({ + "jsonrpc": "2.0", + "id": 1, + "method": "getEvents", + "params": { + "startLedger": 1000, + "filters": [ + { + "contractIds": [], + "topics": [] + } + ], + "pagination": { + "limit": 10 + } + } + })); + + then.status(200).json_body(json!({ + "jsonrpc": "2.0", + "id": 1, + "result": GetEventsResponse { + events: vec![], + latest_ledger: 1000 + } + })); + }) +}