From 801aa228b732e69703e980fef30762a41704c1f8 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Thu, 20 Feb 2025 14:57:45 -0300 Subject: [PATCH 01/11] feat: add faucet website test --- Cargo.lock | 387 ++++++++++++++++++++++++++-- bin/faucet/Cargo.toml | 7 + bin/faucet/src/main.rs | 171 +++++++++--- bin/faucet/src/stub_rpc_api.rs | 166 ++++++++++++ bin/faucet/src/test_data/faucet.mac | Bin 0 -> 1697 bytes 5 files changed, 674 insertions(+), 57 deletions(-) create mode 100644 bin/faucet/src/stub_rpc_api.rs create mode 100644 bin/faucet/src/test_data/faucet.mac diff --git a/Cargo.lock b/Cargo.lock index a7eb2e6ce..03f0f72ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -200,7 +200,7 @@ dependencies = [ "axum-core 0.4.5", "bytes", "futures-util", - "http", + "http 1.2.0", "http-body", "http-body-util", "itoa", @@ -227,7 +227,7 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http", + "http 1.2.0", "http-body", "http-body-util", "hyper", @@ -260,7 +260,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", + "http 1.2.0", "http-body", "http-body-util", "mime", @@ -279,7 +279,7 @@ checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" dependencies = [ "bytes", "futures-util", - "http", + "http 1.2.0", "http-body", "http-body-util", "mime", @@ -315,6 +315,12 @@ dependencies = [ "backtrace", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -574,6 +580,37 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation" version = "0.10.0" @@ -824,6 +861,15 @@ dependencies = [ "log", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -852,6 +898,31 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fantoccini" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7722aeee9c2be6fa131166990295089d73d973012b758a2208b9ba51af5dd024" +dependencies = [ + "base64 0.22.1", + "cookie 0.18.1", + "futures-core", + "futures-util", + "http 1.2.0", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "mime", + "openssl", + "serde", + "serde_json", + "time", + "tokio", + "url", + "webdriver", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -886,6 +957,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1055,7 +1141,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.2.0", "indexmap 2.7.1", "slab", "tokio", @@ -1111,6 +1197,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.2.0" @@ -1129,7 +1226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.2.0", ] [[package]] @@ -1140,7 +1237,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", + "http 1.2.0", "http-body", "pin-project-lite", ] @@ -1167,7 +1264,7 @@ dependencies = [ "futures-channel", "futures-util", "h2", - "http", + "http 1.2.0", "http-body", "httparse", "httpdate", @@ -1178,6 +1275,23 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-timeout" version = "0.5.2" @@ -1191,6 +1305,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.10" @@ -1200,7 +1330,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", + "http 1.2.0", "http-body", "hyper", "pin-project-lite", @@ -1410,6 +1540,12 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "is_ci" version = "1.2.0" @@ -1753,7 +1889,8 @@ dependencies = [ "anyhow", "axum 0.8.1", "clap", - "http", + "fantoccini", + "http 1.2.0", "http-body-util", "miden-lib", "miden-node-proto", @@ -1763,12 +1900,16 @@ dependencies = [ "mime", "rand", "rand_chacha", + "reqwest", "serde", + "serde_json", "static-files", "thiserror 2.0.11", "tokio", + "tokio-stream", "toml", "tonic", + "tonic-web", "tower 0.5.2", "tower-http 0.6.2", "tracing", @@ -1962,7 +2103,7 @@ version = "0.8.0" dependencies = [ "anyhow", "figment", - "http", + "http 1.2.0", "itertools 0.14.0", "miden-objects", "opentelemetry", @@ -2153,6 +2294,23 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +[[package]] +name = "native-tls" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -2304,12 +2462,50 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "openssl" +version = "0.10.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-sys" +version = "0.9.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "opentelemetry" version = "0.28.0" @@ -2332,7 +2528,7 @@ checksum = "5bef114c6d41bea83d6dc60eb41720eedd0261a67af57b66dd2b84ac46c01d91" dependencies = [ "async-trait", "futures-core", - "http", + "http 1.2.0", "opentelemetry", "opentelemetry-proto", "opentelemetry_sdk", @@ -2833,6 +3029,50 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "reqwest" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 1.2.0", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower 0.5.2", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + [[package]] name = "ring" version = "0.17.8" @@ -2939,7 +3179,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.2.0", ] [[package]] @@ -3022,6 +3262,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework" version = "3.2.0" @@ -3029,7 +3282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -3287,6 +3540,9 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" @@ -3299,6 +3555,27 @@ dependencies = [ "syn", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "target-triple" version = "0.1.3" @@ -3489,6 +3766,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.1" @@ -3566,10 +3853,10 @@ dependencies = [ "async-stream", "async-trait", "axum 0.7.9", - "base64", + "base64 0.22.1", "bytes", "h2", - "http", + "http 1.2.0", "http-body", "http-body-util", "hyper", @@ -3610,9 +3897,9 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5299dd20801ad736dccb4a5ea0da7376e59cd98f213bf1c3d478cf53f4834b58" dependencies = [ - "base64", + "base64 0.22.1", "bytes", - "http", + "http 1.2.0", "http-body", "http-body-util", "pin-project", @@ -3668,7 +3955,7 @@ checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "bitflags", "bytes", - "http", + "http 1.2.0", "http-body", "http-body-util", "pin-project-lite", @@ -3684,7 +3971,7 @@ checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "bitflags", "bytes", - "http", + "http 1.2.0", "http-body", "pin-project-lite", "tower-layer", @@ -3871,6 +4158,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.14" @@ -4061,6 +4354,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -4093,6 +4399,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "web-time" version = "1.1.0" @@ -4103,6 +4419,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webdriver" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144ab979b12d36d65065635e646549925de229954de2eb3b47459b432a42db71" +dependencies = [ + "base64 0.21.7", + "bytes", + "cookie 0.16.2", + "http 0.2.12", + "log", + "serde", + "serde_derive", + "serde_json", + "thiserror 1.0.69", + "time", + "unicode-segmentation", + "url", +] + [[package]] name = "winapi" version = "0.3.9" @@ -4188,6 +4524,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.2.0" diff --git a/bin/faucet/Cargo.toml b/bin/faucet/Cargo.toml index 83194cc0f..181d8de51 100644 --- a/bin/faucet/Cargo.toml +++ b/bin/faucet/Cargo.toml @@ -43,3 +43,10 @@ url = { workspace = true } # Required to inject build metadata. miden-node-utils = { workspace = true, features = ["vergen"] } static-files = "0.2" + +[dev-dependencies] +fantoccini = { version = "0.21" } +reqwest = { version = "0.12" } +serde_json = { version = "1.0" } +tokio-stream = { workspace = true, features = ["net"] } +tonic-web = { version = "0.12" } diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index 64e223d0b..a8d395c2b 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -5,6 +5,9 @@ mod handlers; mod state; mod store; +#[cfg(test)] +mod stub_rpc_api; + use std::path::PathBuf; use anyhow::Context; @@ -100,43 +103,7 @@ async fn main() -> anyhow::Result<()> { Command::Start { config } => { let config: FaucetConfig = load_config(config).context("failed to load configuration file")?; - - let faucet_state = FaucetState::new(config.clone()).await?; - - info!(target: COMPONENT, %config, "Initializing server"); - - let app = Router::new() - .route("/", get(get_index_html)) - .route("/index.js", get(get_index_js)) - .route("/index.css", get(get_index_css)) - .route("/background.png", get(get_background)) - .route("/favicon.ico", get(get_favicon)) - .route("/get_metadata", get(get_metadata)) - .route("/get_tokens", post(get_tokens)) - .layer( - ServiceBuilder::new() - .layer(TraceLayer::new_for_http()) - .layer(SetResponseHeaderLayer::if_not_present( - http::header::CACHE_CONTROL, - HeaderValue::from_static("no-cache"), - )) - .layer( - CorsLayer::new() - .allow_origin(tower_http::cors::Any) - .allow_methods(tower_http::cors::Any), - ), - ) - .with_state(faucet_state); - - let socket_addr = config.endpoint.socket_addrs(|| None)?.into_iter().next().ok_or( - anyhow::anyhow!("Couldn't get any socket addrs for endpoint: {}", config.endpoint), - )?; - let listener = - TcpListener::bind(socket_addr).await.context("failed to bind TCP listener")?; - - info!(target: COMPONENT, endpoint = %config.endpoint, "Server started"); - - axum::serve(listener, app).await.unwrap(); + serve_faucet(config).await?; }, Command::CreateFaucetAccount { @@ -229,3 +196,133 @@ fn long_version() -> LongVersion { debug: option_env!("VERGEN_CARGO_DEBUG").unwrap_or_default(), } } + +fn create_new_router(faucet_state: FaucetState) -> Router { + Router::new() + .route("/", get(get_index_html)) + .route("/index.js", get(get_index_js)) + .route("/index.css", get(get_index_css)) + .route("/background.png", get(get_background)) + .route("/favicon.ico", get(get_favicon)) + .route("/get_metadata", get(get_metadata)) + .route("/get_tokens", post(get_tokens)) + .layer( + ServiceBuilder::new() + .layer(TraceLayer::new_for_http()) + .layer(SetResponseHeaderLayer::if_not_present( + http::header::CACHE_CONTROL, + HeaderValue::from_static("no-cache"), + )) + .layer( + CorsLayer::new() + .allow_origin(tower_http::cors::Any) + .allow_methods(tower_http::cors::Any), + ), + ) + .with_state(faucet_state) +} + +async fn serve_faucet(config: FaucetConfig) -> anyhow::Result<()> { + let faucet_state = FaucetState::new(config.clone()).await?; + + info!(target: COMPONENT, %config, "Initializing server"); + + let app = create_new_router(faucet_state); + + let socket_addr = + config + .endpoint + .socket_addrs(|| None)? + .into_iter() + .next() + .ok_or(anyhow::anyhow!( + "Couldn't get any socket addrs for endpoint: {}", + config.endpoint + ))?; + let listener = TcpListener::bind(socket_addr).await.context("failed to bind TCP listener")?; + + info!(target: COMPONENT, endpoint = %config.endpoint, "Server started"); + + axum::serve(listener, app).await.unwrap(); + Ok(()) +} + +#[cfg(test)] +mod test { + use std::{process::Command, str::FromStr, time::Duration}; + + use fantoccini::ClientBuilder; + use serde_json::{json, Map}; + use tokio::time::sleep; + use url::Url; + + use crate::{config::FaucetConfig, serve_faucet, stub_rpc_api::serve_stub}; + + #[tokio::test] + async fn test_website() { + let stub_node_url = Url::from_str("http://localhost:50051").unwrap(); + + // Start the stub node + let stub_url_clone = stub_node_url.clone(); + tokio::spawn(async move { serve_stub(&stub_url_clone).await.unwrap() }); + + // Start the faucet connected to the stub + let config = FaucetConfig { + node_url: stub_node_url, + faucet_account_path: "src/test_data/faucet.mac".into(), + ..FaucetConfig::default() + }; + let website_url = config.endpoint.clone(); + tokio::spawn(async move { serve_faucet(config).await.unwrap() }); + + // Start chromedriver. This requires having chromedriver and chrome installed + let chromedriver_port = "57709"; + #[allow(clippy::zombie_processes)] + let mut chromedriver = Command::new("chromedriver") + .arg(format!("--port={chromedriver_port}")) + .spawn() + .expect("failed to start chromedriver"); + + // Wait for the server to be running + sleep(Duration::from_secs(1)).await; + + // Start fantoccini client + let mut caps = Map::new(); + caps.insert( + "goog:chromeOptions".to_string(), + json!({"args": ["--headless", "--disable-gpu", "--no-sandbox"]}), + ); + let client = ClientBuilder::native() + .capabilities(caps) + .connect(&format!("http://localhost:{chromedriver_port}")) + .await + .expect("failed to connect to WebDriver"); + + // Open the website + client.goto(website_url.as_str()).await.unwrap(); + + let title = client.title().await.unwrap(); + assert_eq!(title, "Miden Faucet"); + + // Collect all the requests made by the website and test that they all return 200 + let script = r" + let requests = []; + window.performance.getEntriesByType('resource').forEach(entry => { + requests.push(entry.name); + }); + return requests; + "; + let requests = client.execute(script, vec![]).await.unwrap(); + assert!(!requests.as_array().unwrap().is_empty()); + + for request in requests.as_array().unwrap() { + let uri = request.as_str().unwrap(); + let status = reqwest::get(uri).await.unwrap().status(); + assert_eq!(status, 200); + } + + // Close the client and kill chromedriver + client.close().await.unwrap(); + chromedriver.kill().unwrap(); + } +} diff --git a/bin/faucet/src/stub_rpc_api.rs b/bin/faucet/src/stub_rpc_api.rs new file mode 100644 index 000000000..81860ad6d --- /dev/null +++ b/bin/faucet/src/stub_rpc_api.rs @@ -0,0 +1,166 @@ +use miden_node_proto::generated::{ + block::BlockHeader, + digest::Digest, + requests::{ + CheckNullifiersByPrefixRequest, CheckNullifiersRequest, GetAccountDetailsRequest, + GetAccountProofsRequest, GetAccountStateDeltaRequest, GetBlockByNumberRequest, + GetBlockHeaderByNumberRequest, GetNotesByIdRequest, SubmitProvenTransactionRequest, + SyncNoteRequest, SyncStateRequest, + }, + responses::{ + CheckNullifiersByPrefixResponse, CheckNullifiersResponse, GetAccountDetailsResponse, + GetAccountProofsResponse, GetAccountStateDeltaResponse, GetBlockByNumberResponse, + GetBlockHeaderByNumberResponse, GetNotesByIdResponse, SubmitProvenTransactionResponse, + SyncNoteResponse, SyncStateResponse, + }, + rpc::api_server, +}; +use miden_node_utils::errors::ApiError; +use tokio::net::TcpListener; +use tokio_stream::wrappers::TcpListenerStream; +use tonic::{Request, Response, Status}; +use url::Url; + +#[derive(Clone)] +pub struct StubRpcApi; + +#[tonic::async_trait] +impl api_server::Api for StubRpcApi { + async fn check_nullifiers( + &self, + _request: Request, + ) -> Result, Status> { + unimplemented!(); + } + + async fn check_nullifiers_by_prefix( + &self, + _request: Request, + ) -> Result, Status> { + unimplemented!(); + } + + #[allow(clippy::unreadable_literal)] + async fn get_block_header_by_number( + &self, + _request: Request, + ) -> Result, Status> { + Ok(Response::new(GetBlockHeaderByNumberResponse { + block_header: Some(BlockHeader { + version: 1, + prev_hash: Some(Digest { d0: 0, d1: 0, d2: 0, d3: 0 }), + block_num: 0, + chain_root: Some(Digest { + d0: 10892410042676993129, + d1: 465072181589837593, + d2: 8905599737602832342, + d3: 16439138630577134987, + }), + account_root: Some(Digest { + d0: 10837452312629690394, + d1: 13240547218519223665, + d2: 18205663827662873122, + d3: 10163700835301150362, + }), + nullifier_root: Some(Digest { + d0: 15321474589252129342, + d1: 17373224439259377994, + d2: 15071539326562317628, + d3: 3312677166725950353, + }), + note_root: Some(Digest { + d0: 10650694022550988030, + d1: 5634734408638476525, + d2: 9233115969432897632, + d3: 1437907447409278328, + }), + tx_hash: Some(Digest { d0: 0, d1: 0, d2: 0, d3: 0 }), + kernel_root: Some(Digest { + d0: 8894402440595556547, + d1: 11075240337243789177, + d2: 12654662110212372673, + d3: 12816653122390928829, + }), + proof_hash: Some(Digest { d0: 0, d1: 0, d2: 0, d3: 0 }), + timestamp: 1672531200, + }), + mmr_path: None, + chain_length: None, + })) + } + + async fn sync_state( + &self, + _request: Request, + ) -> Result, Status> { + unimplemented!(); + } + + async fn sync_notes( + &self, + _request: Request, + ) -> Result, Status> { + unimplemented!(); + } + + async fn get_notes_by_id( + &self, + _request: Request, + ) -> Result, Status> { + unimplemented!(); + } + + async fn submit_proven_transaction( + &self, + _request: Request, + ) -> Result, Status> { + Ok(Response::new(SubmitProvenTransactionResponse { block_height: 0 })) + } + + async fn get_account_details( + &self, + _request: Request, + ) -> Result, Status> { + Err(Status::not_found("account not found")) + } + + async fn get_block_by_number( + &self, + _request: Request, + ) -> Result, Status> { + unimplemented!() + } + + async fn get_account_state_delta( + &self, + _request: Request, + ) -> Result, Status> { + unimplemented!() + } + + async fn get_account_proofs( + &self, + _request: Request, + ) -> Result, Status> { + unimplemented!() + } +} + +pub async fn serve_stub(endpoint: &Url) -> Result<(), ApiError> { + let addr = endpoint + .socket_addrs(|| None) + .map_err(ApiError::EndpointToSocketFailed)? + .into_iter() + .next() + .unwrap(); + + let listener = TcpListener::bind(addr).await?; + let api_service = api_server::ApiServer::new(StubRpcApi); + + tonic::transport::Server::builder() + .accept_http1(true) + .add_service(tonic_web::enable(api_service)) + .serve_with_incoming(TcpListenerStream::new(listener)) + .await + .map_err(ApiError::ApiServeFailed) +} diff --git a/bin/faucet/src/test_data/faucet.mac b/bin/faucet/src/test_data/faucet.mac new file mode 100644 index 0000000000000000000000000000000000000000..be8615c4b432713e6067c1253e1e0f575c49c1f3 GIT binary patch literal 1697 zcmai!eQXnT7{`D2(%#W_y<6L@9qqUdhYUeigeYw5E8|6t10tHJiJO@ChcUcq)PQtd z3(l7)@}G%DFeaM#5qPZ#tL|KgiG`6f@k z`8?0_&-3C-FTHLSuTqV3(^dd#KTBOmLkPI17B;Y>KflbAsuS1Oxfh;$ddOXO>&=U#MrVT0*fg(Q-sb#i&xY^TiSNz6 zu=^puA?RKt z7`oR^@PAhVDEZ3c8(*;`6Gg-R{paBIH@?Y~swT&N-2MHS$Ky?6tH85-4k1qaBxj$d z+RI1%nCw#Tbz||cvA-Sc9R$Px>ja{V(`!8vXptE|5_L45C=;g?k~V&qaRyj>c zViY!pyjMQJOVkmmb4VXX zXtFGIx`;218mATcxHr)}WT+3RW9_I_Au<7V)MeQDJJRo_$&EACgV?l_2>Dp2r>V;> z?ASE(`JiBPsO5k}$%baXK*9V;47;h(_rF7AB>@5^Yiqj0FD3>N_8=f-D^b?7ep$xA zfp)S3MY}u(XBnr$;7O6(4q>5w4)iljzoAn^fgNoQ8K)mLpL$Pf9@6YP!~rkCgf;sH z^r0YDJ}a{>lWQZ`>E|GXwRyQ0y?95M+;bju0Vxs|4g~Y;F9O+$H-|>M{J`pC5OZNSUkY)Jv-Cg2O0)$$>;f~B!@SfhmV+tKO~zK7~>Io?0CZ= zDpBGW_qw=ZhoJ1qa(oQl#*irb;?4gKTRpIg}21`AeMCzr#F)}4}KY@$yPDu z#a;gEJAuj31BiCbjy`Vf5$u-a1*hX5dT1Q8>>#xq;) zJKkraHfac#9CNb`j``>|eXf;fMbx@AZSxUl+S!K9rf$2%4>9|JoDem(fPDO{I#ZFu zZ=)S9G33#UMJudx z5Pakl9t@+MusIf_A`Z>oa}wprvoNrt`dpi4$6+ic@(ILJ-ADRl$>jh*Ia#p$sm|4~ zM4f$|X|lEoj6?jmId@|TF@cWF=j&iGtyK^UO0zI|#hM>kt<*!CC5b}?DN|d)X#4?M z&k5soSHN-ueifcYR@BTWRJX5PGa($>pf;x(mNfmekxvz)g-m^`QM}oLk4-3Y+7*0W zWz?7Ge;(AOiKOXHR`rG`?GODm}|A?oPa z7^I?^3%J}SA@vi6$f{(`tx`}^nVo= Date: Mon, 24 Feb 2025 18:10:24 -0300 Subject: [PATCH 02/11] review: refactor test setup and comment --- bin/faucet/src/main.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index a8d395c2b..a08678a67 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -197,7 +197,7 @@ fn long_version() -> LongVersion { } } -fn create_new_router(faucet_state: FaucetState) -> Router { +fn create_router(faucet_state: FaucetState) -> Router { Router::new() .route("/", get(get_index_html)) .route("/index.js", get(get_index_js)) @@ -227,7 +227,7 @@ async fn serve_faucet(config: FaucetConfig) -> anyhow::Result<()> { info!(target: COMPONENT, %config, "Initializing server"); - let app = create_new_router(faucet_state); + let app = create_router(faucet_state); let socket_addr = config @@ -260,12 +260,17 @@ mod test { #[tokio::test] async fn test_website() { + // This test starts a stub node, a faucet connected to the stub node, and a chromedriver + // to test the faucet website. It then loads the website and checks that all the requests + // made return status 200. + let stub_node_url = Url::from_str("http://localhost:50051").unwrap(); // Start the stub node - let stub_url_clone = stub_node_url.clone(); - tokio::spawn(async move { serve_stub(&stub_url_clone).await.unwrap() }); - + tokio::spawn({ + let stub_node_url = stub_node_url.clone(); + async move { serve_stub(&stub_node_url).await.unwrap() } + }); // Start the faucet connected to the stub let config = FaucetConfig { node_url: stub_node_url, @@ -277,7 +282,7 @@ mod test { // Start chromedriver. This requires having chromedriver and chrome installed let chromedriver_port = "57709"; - #[allow(clippy::zombie_processes)] + #[expect(clippy::zombie_processes)] let mut chromedriver = Command::new("chromedriver") .arg(format!("--port={chromedriver_port}")) .spawn() @@ -287,13 +292,14 @@ mod test { sleep(Duration::from_secs(1)).await; // Start fantoccini client - let mut caps = Map::new(); - caps.insert( - "goog:chromeOptions".to_string(), - json!({"args": ["--headless", "--disable-gpu", "--no-sandbox"]}), - ); let client = ClientBuilder::native() - .capabilities(caps) + .capabilities(Map::from_iter( + [( + "goog:chromeOptions".to_string(), + json!({"args": ["--headless", "--disable-gpu", "--no-sandbox"]}), + )] + .into_iter(), + )) .connect(&format!("http://localhost:{chromedriver_port}")) .await .expect("failed to connect to WebDriver"); From e39e1f9f59a5d5f1f6e01eab615f4456cf3f86cd Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Mon, 24 Feb 2025 18:55:59 -0300 Subject: [PATCH 03/11] review: update js script to check failed requests --- Cargo.lock | 135 ----------------------------------------- bin/faucet/Cargo.toml | 1 - bin/faucet/src/main.rs | 29 ++++----- 3 files changed, 13 insertions(+), 152 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03f0f72ee..3cbcc5f87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -861,15 +861,6 @@ dependencies = [ "log", ] -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -1275,23 +1266,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.27.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" -dependencies = [ - "futures-util", - "http 1.2.0", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - [[package]] name = "hyper-timeout" version = "0.5.2" @@ -1540,12 +1514,6 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - [[package]] name = "is_ci" version = "1.2.0" @@ -1900,7 +1868,6 @@ dependencies = [ "mime", "rand", "rand_chacha", - "reqwest", "serde", "serde_json", "static-files", @@ -3029,50 +2996,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "reqwest" -version = "0.12.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 1.2.0", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower 0.5.2", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-registry", -] - [[package]] name = "ring" version = "0.17.8" @@ -3540,9 +3463,6 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] [[package]] name = "synstructure" @@ -3555,27 +3475,6 @@ dependencies = [ "syn", ] -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "target-triple" version = "0.1.3" @@ -4354,19 +4253,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -4399,16 +4285,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "web-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "web-time" version = "1.1.0" @@ -4524,17 +4400,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.52.6", -] - [[package]] name = "windows-result" version = "0.2.0" diff --git a/bin/faucet/Cargo.toml b/bin/faucet/Cargo.toml index 181d8de51..a1b66b565 100644 --- a/bin/faucet/Cargo.toml +++ b/bin/faucet/Cargo.toml @@ -46,7 +46,6 @@ static-files = "0.2" [dev-dependencies] fantoccini = { version = "0.21" } -reqwest = { version = "0.12" } serde_json = { version = "1.0" } tokio-stream = { workspace = true, features = ["net"] } tonic-web = { version = "0.12" } diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index a08678a67..58b3d93fb 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -293,13 +293,14 @@ mod test { // Start fantoccini client let client = ClientBuilder::native() - .capabilities(Map::from_iter( + .capabilities( [( "goog:chromeOptions".to_string(), json!({"args": ["--headless", "--disable-gpu", "--no-sandbox"]}), )] - .into_iter(), - )) + .into_iter() + .collect::>(), + ) .connect(&format!("http://localhost:{chromedriver_port}")) .await .expect("failed to connect to WebDriver"); @@ -310,22 +311,18 @@ mod test { let title = client.title().await.unwrap(); assert_eq!(title, "Miden Faucet"); - // Collect all the requests made by the website and test that they all return 200 + // Execute a script to get all the failed requests let script = r" - let requests = []; - window.performance.getEntriesByType('resource').forEach(entry => { - requests.push(entry.name); + let errors = []; + performance.getEntriesByType('resource').forEach(entry => { + if (entry.responseStatus && entry.responseStatus >= 400) { + errors.push({url: entry.name, status: entry.responseStatus}); + } }); - return requests; + return errors; "; - let requests = client.execute(script, vec![]).await.unwrap(); - assert!(!requests.as_array().unwrap().is_empty()); - - for request in requests.as_array().unwrap() { - let uri = request.as_str().unwrap(); - let status = reqwest::get(uri).await.unwrap().status(); - assert_eq!(status, 200); - } + let failed_requests = client.execute(script, vec![]).await.unwrap(); + assert!(failed_requests.as_array().unwrap().is_empty()); // Close the client and kill chromedriver client.close().await.unwrap(); From f6acdb043f2bbee44c9e12ac9248795f1661827b Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Tue, 25 Feb 2025 12:04:33 -0300 Subject: [PATCH 04/11] review: remove unreadable literals and comment genesis block --- bin/faucet/src/stub_rpc_api.rs | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/bin/faucet/src/stub_rpc_api.rs b/bin/faucet/src/stub_rpc_api.rs index 81860ad6d..0a89336ff 100644 --- a/bin/faucet/src/stub_rpc_api.rs +++ b/bin/faucet/src/stub_rpc_api.rs @@ -40,49 +40,49 @@ impl api_server::Api for StubRpcApi { unimplemented!(); } - #[allow(clippy::unreadable_literal)] async fn get_block_header_by_number( &self, _request: Request, ) -> Result, Status> { + // Values are taken from the default genesis block as at v0.7 Ok(Response::new(GetBlockHeaderByNumberResponse { block_header: Some(BlockHeader { version: 1, prev_hash: Some(Digest { d0: 0, d1: 0, d2: 0, d3: 0 }), block_num: 0, chain_root: Some(Digest { - d0: 10892410042676993129, - d1: 465072181589837593, - d2: 8905599737602832342, - d3: 16439138630577134987, + d0: 0x9729_9D39_2DA8_DC69, + d1: 0x674_44AF_6294_0719, + d2: 0x7B97_0BC7_07A0_F7D6, + d3: 0xE423_8D7C_78F3_9D8B, }), account_root: Some(Digest { - d0: 10837452312629690394, - d1: 13240547218519223665, - d2: 18205663827662873122, - d3: 10163700835301150362, + d0: 0x9666_5D75_8487_401A, + d1: 0xB7BF_DF8B_379F_ED71, + d2: 0xFCA7_82CB_2406_2222, + d3: 0x8D0C_B80F_6377_4E9A, }), nullifier_root: Some(Digest { - d0: 15321474589252129342, - d1: 17373224439259377994, - d2: 15071539326562317628, - d3: 3312677166725950353, + d0: 0xD4A0_CFF6_578C_123E, + d1: 0xF11A_1794_8930_B14A, + d2: 0xD128_DD2A_4213_B53C, + d3: 0x2DF8_FE54_F23F_6B91, }), note_root: Some(Digest { - d0: 10650694022550988030, - d1: 5634734408638476525, - d2: 9233115969432897632, - d3: 1437907447409278328, + d0: 0x93CE_DDC8_A187_24FE, + d1: 0x4E32_9917_2E91_30ED, + d2: 0x8022_9E0E_1808_C860, + d3: 0x13F4_7934_7EB7_FD78, }), tx_hash: Some(Digest { d0: 0, d1: 0, d2: 0, d3: 0 }), kernel_root: Some(Digest { - d0: 8894402440595556547, - d1: 11075240337243789177, - d2: 12654662110212372673, - d3: 12816653122390928829, + d0: 0x7B6F_43E5_2910_C8C3, + d1: 0x99B3_2868_577E_5779, + d2: 0xAF9E_6424_57CD_B8C1, + d3: 0xB1DD_E61B_F983_2DBD, }), proof_hash: Some(Digest { d0: 0, d1: 0, d2: 0, d3: 0 }), - timestamp: 1672531200, + timestamp: 0x63B0_CD00, }), mmr_path: None, chain_length: None, From 1f74f8546cc7b6f5ded68184cb52b65f6fce8f20 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Tue, 25 Feb 2025 12:08:43 -0300 Subject: [PATCH 05/11] review: improve socket addr error handling --- bin/faucet/src/main.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index 58b3d93fb..0771cbca9 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -229,16 +229,12 @@ async fn serve_faucet(config: FaucetConfig) -> anyhow::Result<()> { let app = create_router(faucet_state); - let socket_addr = - config - .endpoint - .socket_addrs(|| None)? - .into_iter() - .next() - .ok_or(anyhow::anyhow!( - "Couldn't get any socket addrs for endpoint: {}", - config.endpoint - ))?; + let socket_addr = config + .endpoint + .socket_addrs(|| None)? + .into_iter() + .next() + .with_context(|| format!("no sockets available on {}", config.endpoint))?; let listener = TcpListener::bind(socket_addr).await.context("failed to bind TCP listener")?; info!(target: COMPONENT, endpoint = %config.endpoint, "Server started"); From b669344a77fd28b4a3fba236bc46a21a706b90fe Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Tue, 25 Feb 2025 12:09:55 -0300 Subject: [PATCH 06/11] review: document test --- bin/faucet/src/main.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index 0771cbca9..4a9eff6be 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -254,12 +254,11 @@ mod test { use crate::{config::FaucetConfig, serve_faucet, stub_rpc_api::serve_stub}; + /// This test starts a stub node, a faucet connected to the stub node, and a chromedriver + /// to test the faucet website. It then loads the website and checks that all the requests + /// made return status 200. #[tokio::test] async fn test_website() { - // This test starts a stub node, a faucet connected to the stub node, and a chromedriver - // to test the faucet website. It then loads the website and checks that all the requests - // made return status 200. - let stub_node_url = Url::from_str("http://localhost:50051").unwrap(); // Start the stub node From aaa3bf9ab770b2a07dcd0edf4a63fefae7f94de2 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Thu, 27 Feb 2025 17:31:16 -0300 Subject: [PATCH 07/11] review: remove sleep and move code --- bin/faucet/src/main.rs | 50 ++++++++++------------ bin/faucet/{src => }/test_data/faucet.mac | Bin 2 files changed, 23 insertions(+), 27 deletions(-) rename bin/faucet/{src => }/test_data/faucet.mac (100%) diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index 4a9eff6be..c11b3c202 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -197,6 +197,27 @@ fn long_version() -> LongVersion { } } +async fn serve_faucet(config: FaucetConfig) -> anyhow::Result<()> { + let faucet_state = FaucetState::new(config.clone()).await?; + + info!(target: COMPONENT, %config, "Initializing server"); + + let app = create_router(faucet_state); + + let socket_addr = config + .endpoint + .socket_addrs(|| None)? + .into_iter() + .next() + .with_context(|| format!("no sockets available on {}", config.endpoint))?; + let listener = TcpListener::bind(socket_addr).await.context("failed to bind TCP listener")?; + + info!(target: COMPONENT, endpoint = %config.endpoint, "Server started"); + + axum::serve(listener, app).await.unwrap(); + Ok(()) +} + fn create_router(faucet_state: FaucetState) -> Router { Router::new() .route("/", get(get_index_html)) @@ -222,34 +243,12 @@ fn create_router(faucet_state: FaucetState) -> Router { .with_state(faucet_state) } -async fn serve_faucet(config: FaucetConfig) -> anyhow::Result<()> { - let faucet_state = FaucetState::new(config.clone()).await?; - - info!(target: COMPONENT, %config, "Initializing server"); - - let app = create_router(faucet_state); - - let socket_addr = config - .endpoint - .socket_addrs(|| None)? - .into_iter() - .next() - .with_context(|| format!("no sockets available on {}", config.endpoint))?; - let listener = TcpListener::bind(socket_addr).await.context("failed to bind TCP listener")?; - - info!(target: COMPONENT, endpoint = %config.endpoint, "Server started"); - - axum::serve(listener, app).await.unwrap(); - Ok(()) -} - #[cfg(test)] mod test { - use std::{process::Command, str::FromStr, time::Duration}; + use std::{process::Command, str::FromStr}; use fantoccini::ClientBuilder; use serde_json::{json, Map}; - use tokio::time::sleep; use url::Url; use crate::{config::FaucetConfig, serve_faucet, stub_rpc_api::serve_stub}; @@ -269,7 +268,7 @@ mod test { // Start the faucet connected to the stub let config = FaucetConfig { node_url: stub_node_url, - faucet_account_path: "src/test_data/faucet.mac".into(), + faucet_account_path: "test_data/faucet.mac".into(), ..FaucetConfig::default() }; let website_url = config.endpoint.clone(); @@ -283,9 +282,6 @@ mod test { .spawn() .expect("failed to start chromedriver"); - // Wait for the server to be running - sleep(Duration::from_secs(1)).await; - // Start fantoccini client let client = ClientBuilder::native() .capabilities( diff --git a/bin/faucet/src/test_data/faucet.mac b/bin/faucet/test_data/faucet.mac similarity index 100% rename from bin/faucet/src/test_data/faucet.mac rename to bin/faucet/test_data/faucet.mac From 102b761cb176b39fa549d71185b918f45575b499 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Thu, 27 Feb 2025 17:51:21 -0300 Subject: [PATCH 08/11] fix: add stdout to wait for chromedriver --- bin/faucet/src/main.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index c11b3c202..96993a3d8 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -245,7 +245,7 @@ fn create_router(faucet_state: FaucetState) -> Router { #[cfg(test)] mod test { - use std::{process::Command, str::FromStr}; + use std::{io::{BufRead, BufReader}, process::{Command, Stdio}, str::FromStr}; use fantoccini::ClientBuilder; use serde_json::{json, Map}; @@ -279,9 +279,17 @@ mod test { #[expect(clippy::zombie_processes)] let mut chromedriver = Command::new("chromedriver") .arg(format!("--port={chromedriver_port}")) + .stdout(Stdio::piped()) .spawn() .expect("failed to start chromedriver"); - + // Wait for chromedriver to be running + let stdout = chromedriver.stdout.take().unwrap(); + for line in BufReader::new(stdout).lines() { + if line.unwrap().contains("ChromeDriver was started successfully") { + break; + } + } + // Start fantoccini client let client = ClientBuilder::native() .capabilities( From 9e047f66dbe8b0d73fd592125293b035325d51ac Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Thu, 27 Feb 2025 17:52:35 -0300 Subject: [PATCH 09/11] chore: format --- bin/faucet/src/main.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index 96993a3d8..11386db44 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -245,7 +245,11 @@ fn create_router(faucet_state: FaucetState) -> Router { #[cfg(test)] mod test { - use std::{io::{BufRead, BufReader}, process::{Command, Stdio}, str::FromStr}; + use std::{ + io::{BufRead, BufReader}, + process::{Command, Stdio}, + str::FromStr, + }; use fantoccini::ClientBuilder; use serde_json::{json, Map}; @@ -289,7 +293,7 @@ mod test { break; } } - + // Start fantoccini client let client = ClientBuilder::native() .capabilities( From b9a462456affc39947d402af6841194fbeca74d4 Mon Sep 17 00:00:00 2001 From: tomasarrachea Date: Fri, 28 Feb 2025 13:24:09 -0300 Subject: [PATCH 10/11] review: generate faucet account in the test --- bin/faucet/src/main.rs | 127 +++++++++++++++++++------------- bin/faucet/test_data/faucet.mac | Bin 1697 -> 0 bytes 2 files changed, 76 insertions(+), 51 deletions(-) delete mode 100644 bin/faucet/test_data/faucet.mac diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index 11386db44..dfd87c3c0 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -99,11 +99,53 @@ async fn main() -> anyhow::Result<()> { let cli = Cli::parse(); + run_faucet_command(cli).await +} + +async fn run_faucet_command(cli: Cli) -> anyhow::Result<()> { match &cli.command { Command::Start { config } => { let config: FaucetConfig = load_config(config).context("failed to load configuration file")?; - serve_faucet(config).await?; + let faucet_state = FaucetState::new(config.clone()).await?; + + info!(target: COMPONENT, %config, "Initializing server"); + + let app = Router::new() + .route("/", get(get_index_html)) + .route("/index.js", get(get_index_js)) + .route("/index.css", get(get_index_css)) + .route("/background.png", get(get_background)) + .route("/favicon.ico", get(get_favicon)) + .route("/get_metadata", get(get_metadata)) + .route("/get_tokens", post(get_tokens)) + .layer( + ServiceBuilder::new() + .layer(TraceLayer::new_for_http()) + .layer(SetResponseHeaderLayer::if_not_present( + http::header::CACHE_CONTROL, + HeaderValue::from_static("no-cache"), + )) + .layer( + CorsLayer::new() + .allow_origin(tower_http::cors::Any) + .allow_methods(tower_http::cors::Any), + ), + ) + .with_state(faucet_state); + + let socket_addr = config + .endpoint + .socket_addrs(|| None)? + .into_iter() + .next() + .with_context(|| format!("no sockets available on {}", config.endpoint))?; + let listener = + TcpListener::bind(socket_addr).await.context("failed to bind TCP listener")?; + + info!(target: COMPONENT, endpoint = %config.endpoint, "Server started"); + + axum::serve(listener, app).await.unwrap(); }, Command::CreateFaucetAccount { @@ -197,56 +239,12 @@ fn long_version() -> LongVersion { } } -async fn serve_faucet(config: FaucetConfig) -> anyhow::Result<()> { - let faucet_state = FaucetState::new(config.clone()).await?; - - info!(target: COMPONENT, %config, "Initializing server"); - - let app = create_router(faucet_state); - - let socket_addr = config - .endpoint - .socket_addrs(|| None)? - .into_iter() - .next() - .with_context(|| format!("no sockets available on {}", config.endpoint))?; - let listener = TcpListener::bind(socket_addr).await.context("failed to bind TCP listener")?; - - info!(target: COMPONENT, endpoint = %config.endpoint, "Server started"); - - axum::serve(listener, app).await.unwrap(); - Ok(()) -} - -fn create_router(faucet_state: FaucetState) -> Router { - Router::new() - .route("/", get(get_index_html)) - .route("/index.js", get(get_index_js)) - .route("/index.css", get(get_index_css)) - .route("/background.png", get(get_background)) - .route("/favicon.ico", get(get_favicon)) - .route("/get_metadata", get(get_metadata)) - .route("/get_tokens", post(get_tokens)) - .layer( - ServiceBuilder::new() - .layer(TraceLayer::new_for_http()) - .layer(SetResponseHeaderLayer::if_not_present( - http::header::CACHE_CONTROL, - HeaderValue::from_static("no-cache"), - )) - .layer( - CorsLayer::new() - .allow_origin(tower_http::cors::Any) - .allow_methods(tower_http::cors::Any), - ), - ) - .with_state(faucet_state) -} - #[cfg(test)] mod test { use std::{ + env::temp_dir, io::{BufRead, BufReader}, + path::PathBuf, process::{Command, Stdio}, str::FromStr, }; @@ -255,7 +253,7 @@ mod test { use serde_json::{json, Map}; use url::Url; - use crate::{config::FaucetConfig, serve_faucet, stub_rpc_api::serve_stub}; + use crate::{config::FaucetConfig, run_faucet_command, stub_rpc_api::serve_stub, Cli}; /// This test starts a stub node, a faucet connected to the stub node, and a chromedriver /// to test the faucet website. It then loads the website and checks that all the requests @@ -269,14 +267,41 @@ mod test { let stub_node_url = stub_node_url.clone(); async move { serve_stub(&stub_node_url).await.unwrap() } }); - // Start the faucet connected to the stub + + let config_path = temp_dir().join(PathBuf::from("faucet.toml")); + let faucet_account_path = temp_dir().join(PathBuf::from("account.mac")); + + // Create config let config = FaucetConfig { node_url: stub_node_url, - faucet_account_path: "test_data/faucet.mac".into(), + faucet_account_path: faucet_account_path.clone(), ..FaucetConfig::default() }; + let config_as_toml_string = toml::to_string(&config).unwrap(); + std::fs::write(&config_path, config_as_toml_string).unwrap(); + + // Create faucet account + run_faucet_command(Cli { + command: crate::Command::CreateFaucetAccount { + config_path: config_path.clone(), + output_path: faucet_account_path.clone(), + token_symbol: "TEST".to_string(), + decimals: 2, + max_supply: 1000, + }, + }) + .await + .unwrap(); + + // Start the faucet connected to the stub let website_url = config.endpoint.clone(); - tokio::spawn(async move { serve_faucet(config).await.unwrap() }); + tokio::spawn(async move { + run_faucet_command(Cli { + command: crate::Command::Start { config: config_path }, + }) + .await + .unwrap(); + }); // Start chromedriver. This requires having chromedriver and chrome installed let chromedriver_port = "57709"; diff --git a/bin/faucet/test_data/faucet.mac b/bin/faucet/test_data/faucet.mac deleted file mode 100644 index be8615c4b432713e6067c1253e1e0f575c49c1f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1697 zcmai!eQXnT7{`D2(%#W_y<6L@9qqUdhYUeigeYw5E8|6t10tHJiJO@ChcUcq)PQtd z3(l7)@}G%DFeaM#5qPZ#tL|KgiG`6f@k z`8?0_&-3C-FTHLSuTqV3(^dd#KTBOmLkPI17B;Y>KflbAsuS1Oxfh;$ddOXO>&=U#MrVT0*fg(Q-sb#i&xY^TiSNz6 zu=^puA?RKt z7`oR^@PAhVDEZ3c8(*;`6Gg-R{paBIH@?Y~swT&N-2MHS$Ky?6tH85-4k1qaBxj$d z+RI1%nCw#Tbz||cvA-Sc9R$Px>ja{V(`!8vXptE|5_L45C=;g?k~V&qaRyj>c zViY!pyjMQJOVkmmb4VXX zXtFGIx`;218mATcxHr)}WT+3RW9_I_Au<7V)MeQDJJRo_$&EACgV?l_2>Dp2r>V;> z?ASE(`JiBPsO5k}$%baXK*9V;47;h(_rF7AB>@5^Yiqj0FD3>N_8=f-D^b?7ep$xA zfp)S3MY}u(XBnr$;7O6(4q>5w4)iljzoAn^fgNoQ8K)mLpL$Pf9@6YP!~rkCgf;sH z^r0YDJ}a{>lWQZ`>E|GXwRyQ0y?95M+;bju0Vxs|4g~Y;F9O+$H-|>M{J`pC5OZNSUkY)Jv-Cg2O0)$$>;f~B!@SfhmV+tKO~zK7~>Io?0CZ= zDpBGW_qw=ZhoJ1qa(oQl#*irb;?4gKTRpIg}21`AeMCzr#F)}4}KY@$yPDu z#a;gEJAuj31BiCbjy`Vf5$u-a1*hX5dT1Q8>>#xq;) zJKkraHfac#9CNb`j``>|eXf;fMbx@AZSxUl+S!K9rf$2%4>9|JoDem(fPDO{I#ZFu zZ=)S9G33#UMJudx z5Pakl9t@+MusIf_A`Z>oa}wprvoNrt`dpi4$6+ic@(ILJ-ADRl$>jh*Ia#p$sm|4~ zM4f$|X|lEoj6?jmId@|TF@cWF=j&iGtyK^UO0zI|#hM>kt<*!CC5b}?DN|d)X#4?M z&k5soSHN-ueifcYR@BTWRJX5PGa($>pf;x(mNfmekxvz)g-m^`QM}oLk4-3Y+7*0W zWz?7Ge;(AOiKOXHR`rG`?GODm}|A?oPa z7^I?^3%J}SA@vi6$f{(`tx`}^nVo= Date: Fri, 28 Feb 2025 15:02:37 -0300 Subject: [PATCH 11/11] review: remove unnecessary PathBuf --- bin/faucet/src/main.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index dfd87c3c0..c6dce5eed 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -244,7 +244,6 @@ mod test { use std::{ env::temp_dir, io::{BufRead, BufReader}, - path::PathBuf, process::{Command, Stdio}, str::FromStr, }; @@ -268,8 +267,8 @@ mod test { async move { serve_stub(&stub_node_url).await.unwrap() } }); - let config_path = temp_dir().join(PathBuf::from("faucet.toml")); - let faucet_account_path = temp_dir().join(PathBuf::from("account.mac")); + let config_path = temp_dir().join("faucet.toml"); + let faucet_account_path = temp_dir().join("account.mac"); // Create config let config = FaucetConfig {