From f32ddedfe22530e1f51ff8ada3b2793fe3b285a9 Mon Sep 17 00:00:00 2001 From: Johannes Lund Date: Thu, 7 Nov 2024 16:42:24 +0100 Subject: [PATCH] Tweak integration tests - Use random TCP ports - Use random output files - Use goldenfile (has the practical UPDATE_GOLDENFILES=1 flag) --- Cargo.lock | 115 +++++++++++-------------- Cargo.toml | 4 +- src/sources/hydra.rs | 2 +- tests/hydra.rs | 100 +++++++++++++-------- tests/hydra/{golden_1.txt => golden_1} | 0 tests/hydra/{golden_2.txt => golden_2} | 0 6 files changed, 116 insertions(+), 105 deletions(-) rename tests/hydra/{golden_1.txt => golden_1} (100%) rename tests/hydra/{golden_2.txt => golden_2} (100%) diff --git a/Cargo.lock b/Cargo.lock index 9681184e..91f73cf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,22 +174,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -[[package]] -name = "assert_cmd" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" -dependencies = [ - "anstyle", - "bstr", - "doc-comment", - "libc", - "predicates", - "predicates-core", - "predicates-tree", - "wait-timeout", -] - [[package]] name = "async-channel" version = "1.9.0" @@ -1884,12 +1868,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - [[package]] name = "digest" version = "0.9.0" @@ -2677,6 +2655,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "goldenfile" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "672ff1c2f0537cf3f92065ce8aa77e2fc3f2abae2c805eb67f40ceecfbdee428" +dependencies = [ + "scopeguard", + "similar-asserts", + "tempfile", + "yansi", +] + [[package]] name = "google-cloud-auth" version = "0.11.0" @@ -4139,7 +4129,6 @@ name = "oura" version = "2.0.0-alpha.4" dependencies = [ "anyhow", - "assert_cmd", "async-trait", "aws-config", "aws-sdk-lambda", @@ -4159,6 +4148,7 @@ dependencies = [ "futures-util", "gasket", "gasket-prometheus", + "goldenfile", "google-cloud-default", "google-cloud-googleapis", "google-cloud-pubsub", @@ -4176,6 +4166,7 @@ dependencies = [ "net2", "openssl", "pallas", + "port-selector", "r2d2_redis", "regex", "reqwest 0.11.24", @@ -4184,6 +4175,7 @@ dependencies = [ "sqlx", "strum 0.24.1", "strum_macros 0.25.3", + "tempfile", "thiserror", "tokio", "tokio-tungstenite", @@ -4831,6 +4823,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "port-selector" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd119ef551a50cd8939f0ff93bd062891f7b0dbb771b4a05df8a9c13aebaff68" +dependencies = [ + "rand", +] + [[package]] name = "portable-atomic" version = "1.6.0" @@ -4849,33 +4850,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "predicates" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" -dependencies = [ - "anstyle", - "difflib", - "predicates-core", -] - -[[package]] -name = "predicates-core" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" - -[[package]] -name = "predicates-tree" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" -dependencies = [ - "predicates-core", - "termtree", -] - [[package]] name = "prettyplease" version = "0.2.17" @@ -6052,6 +6026,26 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +dependencies = [ + "bstr", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe85670573cd6f0fa97940f26e7e6601213c3b0555246c24234131f88c5709e" +dependencies = [ + "console", + "similar", +] + [[package]] name = "simple_asn1" version = "0.6.2" @@ -6611,12 +6605,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - [[package]] name = "textwrap" version = "0.16.1" @@ -7330,15 +7318,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - [[package]] name = "waker-fn" version = "1.1.1" @@ -8303,6 +8282,12 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yasna" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index 10ea44f3..2eff47b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,4 +85,6 @@ futures-util = "0.3" bytes = "1.7.2" [dev-dependencies] -assert_cmd = "2" +goldenfile = "1.7.3" +tempfile = "3.4" +port-selector = "0.1.6" diff --git a/src/sources/hydra.rs b/src/sources/hydra.rs index 41e750dd..2bc5cda7 100644 --- a/src/sources/hydra.rs +++ b/src/sources/hydra.rs @@ -254,7 +254,7 @@ impl gasket::framework::Worker for Worker { #[derive(Deserialize)] pub struct Config { - hydra_socket_url: String, + pub hydra_socket_url: String, } impl Config { diff --git a/tests/hydra.rs b/tests/hydra.rs index 69588a66..99c72e1f 100644 --- a/tests/hydra.rs +++ b/tests/hydra.rs @@ -6,13 +6,19 @@ use std::time::Duration; use anyhow::Result; use futures_util::SinkExt; use oura::sources::hydra::{HydraMessage, HydraMessagePayload}; +use oura::sinks::Config::FileRotate; use serde_json::json; use tokio::net::TcpListener; +use oura::sources::Config::Hydra; use oura::daemon::{run_daemon, ConfigRoot}; use gasket::daemon::Daemon; +use goldenfile::Mint; +use std::io::Write; +use tempfile::NamedTempFile; use tokio::runtime::Runtime; use tokio::time; use tokio_tungstenite::accept_async; +use port_selector::random_free_port; use tokio_tungstenite::tungstenite::protocol::Message; type TestResult = Result<(), Box>; @@ -455,74 +461,92 @@ fn scenario_2() -> TestResult { #[test] fn hydra_oura_stdout_scenario_1() -> TestResult { - let golden_jsons = fs::read_to_string("tests/hydra/golden_1.txt")?; - hydra_oura_stdout_test("tests/hydra/scenario_1.txt".to_string(), golden_jsons) + hydra_oura_stdout_test("tests/hydra/scenario_1.txt".to_string(), "golden_1".to_string()) } #[test] fn hydra_oura_stdout_scenario_2() -> TestResult { - let golden_jsons = fs::read_to_string("tests/hydra/golden_2.txt")?; - hydra_oura_stdout_test("tests/hydra/scenario_2.txt".to_string(), golden_jsons) + hydra_oura_stdout_test("tests/hydra/scenario_2.txt".to_string(), "golden_2".to_string()) } // Run: // cargo test hydra_oura -- --nocapture // in order to see println -fn hydra_oura_stdout_test(file: String, expected: String) -> TestResult { +fn hydra_oura_stdout_test(scenario_file: String, golden_name: String) -> TestResult { + + let mut mint = Mint::new("tests/hydra"); + let mut golden = mint.new_goldenfile(golden_name.clone()).unwrap(); + let rt = Runtime::new().unwrap(); let _ = rt.block_on(async move { - let addr = "127.0.0.1:4001".to_string(); + let port: u16 = random_free_port().unwrap(); + let addr= format!("127.0.0.1:{}", port); let server = TcpListener::bind(&addr).await?; - println!("WebSocket server started on ws://{}", addr); + let output_file = NamedTempFile::new()?; + let config = test_config(&output_file, &addr); - let _ = tokio::spawn(async move { oura_pipeline().await }); + println!("WebSocket server starting on ws://{}", addr); - mock_hydra_node(server, file).await; - - let emitted_jsons = fs::read_to_string("tests/hydra/logs.txt")?; - assert_eq!(emitted_jsons, expected); + let _ = tokio::spawn(async move { run_oura(config) }); + let _ = mock_hydra_node(server, scenario_file).await; + // After the connection is established, give oura time to process + // the chain data and write the output to disk. + time::sleep(Duration::from_secs(3)).await; + let emitted_jsons= fs::read_to_string(output_file)?; + golden.write_all(emitted_jsons.as_bytes()).unwrap(); + println!("test done, will exit"); Ok::<(), std::io::Error>(()) }); Ok(()) } +/// Will await the first connection, and then return while handling it in the +/// background. async fn mock_hydra_node(server: TcpListener, file: String) { - let (tx, _rx) = mpsc::channel(); - while let Ok((stream, _)) = server.accept().await { - tokio::spawn(handle_connection(stream, file, tx)); - time::sleep(Duration::from_secs(3)).await; - break; + async fn handle_connection( + stream: tokio::net::TcpStream, + file: String, + tx: mpsc::Sender, + ) -> Result<()> { + let mut ws_stream = accept_async(stream).await?; + println!("WebSocket server oura connection established"); + + let to_send = fs::read_to_string(file)?; + + let mut lines = 0; + for line in to_send.lines() { + ws_stream.send(Message::Text(line.to_string())).await?; + lines += 1; + } + tx.send(lines).unwrap(); + Ok(()) } + + let (tx, _rx) = mpsc::channel(); + let (stream, _) = server.accept().await.unwrap(); + let _ = tokio::spawn(handle_connection(stream, file, tx)); } -async fn handle_connection( - stream: tokio::net::TcpStream, - file: String, - tx: mpsc::Sender, -) -> Result<()> { - let mut ws_stream = accept_async(stream).await?; - println!("WebSocket server oura connection established"); - let to_send = fs::read_to_string(file)?; +fn test_config(tmp_output_file: &NamedTempFile, socket_path: &String) -> ConfigRoot { + let mut config = ConfigRoot::new(&Some(PathBuf::from("tests/daemon.toml"))).unwrap(); - let mut lines = 0; - for line in to_send.lines() { - ws_stream.send(Message::Text(line.to_string())).await?; - lines += 1; + if let FileRotate(ref mut file_rotate) = config.sink { + file_rotate.output_path = Some(tmp_output_file.path().to_string_lossy().to_string()); + } else { + panic!("assumed config template to use file_rotate sink"); } - tx.send(lines).unwrap(); - Ok(()) -} + if let Hydra(ref mut hydra_config) = config.source { + hydra_config.hydra_socket_url = "ws://".to_string() + socket_path; + } else { + panic!("assumed config template to use hydra source"); + } -async fn oura_pipeline() -> Result<()> { - tokio::spawn(invoke_pipeline()); - time::sleep(Duration::from_secs(1)).await; - Ok(()) + config } -async fn invoke_pipeline() -> Result { - let config = ConfigRoot::new(&Some(PathBuf::from("tests/daemon.toml")))?; +fn run_oura(config: ConfigRoot) -> Result { run_daemon(config).map_err(|e| anyhow::anyhow!(e)) } diff --git a/tests/hydra/golden_1.txt b/tests/hydra/golden_1 similarity index 100% rename from tests/hydra/golden_1.txt rename to tests/hydra/golden_1 diff --git a/tests/hydra/golden_2.txt b/tests/hydra/golden_2 similarity index 100% rename from tests/hydra/golden_2.txt rename to tests/hydra/golden_2