diff --git a/Cargo.lock b/Cargo.lock index 10d81c2a0f..b3ed167f42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4219,7 +4219,7 @@ name = "outbound-redis" version = "2.2.0-pre0" dependencies = [ "anyhow", - "redis", + "redis 0.21.7", "spin-app", "spin-core", "spin-outbound-networking", @@ -4836,6 +4836,21 @@ dependencies = [ "url", ] +[[package]] +name = "redis" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c580d9cbbe1d1b479e8d67cf9daf6a62c957e6846048408b80b43ac3f6af84cd" +dependencies = [ + "combine", + "itoa", + "percent-encoding", + "ryu", + "sha1_smol", + "socket2 0.4.10", + "url", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -5701,6 +5716,7 @@ dependencies = [ "outbound-redis", "path-absolutize", "rand 0.8.5", + "redis 0.24.0", "regex", "reqwest", "rpassword", @@ -5866,7 +5882,7 @@ name = "spin-key-value-redis" version = "0.1.0" dependencies = [ "anyhow", - "redis", + "redis 0.21.7", "spin-core", "spin-key-value", "spin-world", @@ -6092,7 +6108,7 @@ dependencies = [ "anyhow", "async-trait", "futures", - "redis", + "redis 0.21.7", "serde", "spin-app", "spin-core", diff --git a/Cargo.toml b/Cargo.toml index 180ef6856d..8a889770ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,6 +91,7 @@ which = "4.2.5" e2e-testing = { path = "crates/e2e-testing" } http-body-util = { workspace = true } testing-framework = { path = "tests/testing-framework" } +redis = "0.24" runtime-tests = { path = "tests/runtime-tests" } test-components = { path = "tests/test-components" } test-codegen-macro = { path = "crates/test-codegen-macro" } diff --git a/tests/integration.rs b/tests/integration.rs index b2d14abed6..dc434ff272 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -40,7 +40,8 @@ mod integration_tests { "{}/{}", RUST_HTTP_INTEGRATION_TEST, DEFAULT_MANIFEST_LOCATION ), - |spin| { + |env| { + let spin = env.runtime_mut(); assert_spin_status(spin, "/test/hello", 200)?; assert_spin_status(spin, "/test/hello/wildcards/should/be/handled", 200)?; assert_spin_status(spin, "/thisshouldfail", 404)?; @@ -56,7 +57,8 @@ mod integration_tests { fn test_duplicate_rust_local() -> Result<()> { integration_test( format!("{}/{}", RUST_HTTP_INTEGRATION_TEST, "double-trouble.toml"), - |spin| { + |env| { + let spin = env.runtime_mut(); assert_spin_status(spin, "/route1", 200)?; assert_spin_status(spin, "/route2", 200)?; assert_spin_status(spin, "/thisshouldfail", 404)?; @@ -242,7 +244,9 @@ mod integration_tests { fn integration_test( manifest_path: impl Into, - test: impl FnOnce(&mut testing_framework::Spin) -> testing_framework::TestResult + test: impl FnOnce( + &mut testing_framework::TestEnvironment, + ) -> testing_framework::TestResult + 'static, ) -> anyhow::Result<()> { let manifest_path = manifest_path.into(); @@ -261,9 +265,11 @@ mod integration_tests { Ok(()) }, testing_framework::ServicesConfig::none(), + testing_framework::SpinMode::Http, ); let mut env = testing_framework::TestEnvironment::up(spin)?; - Ok(env.test(test)?) + test(&mut env)?; + Ok(()) } fn assert_spin_status( diff --git a/tests/runtime-tests/src/lib.rs b/tests/runtime-tests/src/lib.rs index 6221ebce6b..ac692573a1 100644 --- a/tests/runtime-tests/src/lib.rs +++ b/tests/runtime-tests/src/lib.rs @@ -60,7 +60,13 @@ impl RuntimeTest { Ok(()) }; let services_config = services_config(&config)?; - let env_config = TestEnvironmentConfig::spin(spin_binary, [], preboot, services_config); + let env_config = TestEnvironmentConfig::spin( + spin_binary, + [], + preboot, + services_config, + testing_framework::SpinMode::Http, + ); let env = TestEnvironment::up(env_config)?; Ok(Self { test_path: config.test_path, @@ -84,7 +90,7 @@ impl RuntimeTest { } }; } - let response = self.env.test(test); + let response = test(&mut self.env); let error_file = self.test_path.join("error.txt"); match response { Ok(()) if !error_file.exists() => log::info!("Test passed!"), @@ -173,7 +179,8 @@ fn copy_manifest(test_dir: &Path, env: &mut TestEnvironment) -> anyhow::Re Ok(()) } -fn test(runtime: &mut Spin) -> TestResult { +fn test(env: &mut TestEnvironment) -> TestResult { + let runtime = env.runtime_mut(); let response = runtime.make_http_request(reqwest::Method::GET, "/")?; if response.status() == 200 { return Ok(()); diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index 4499e4aa48..6af9550a86 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -1,10 +1,25 @@ +use anyhow::Context; +use redis::Commands; use std::path::PathBuf; #[cfg(feature = "e2e-tests")] mod testcases; -fn spin_binary() -> PathBuf { - env!("CARGO_BIN_EXE_spin").into() +/// Helper macro to assert that a condition is true eventually +macro_rules! assert_eventually { + ($e:expr) => { + let mut i = 0; + loop { + if $e { + break; + } else if i > 20 { + assert!($e); + break; + } + std::thread::sleep(std::time::Duration::from_millis(100)); + i += 1; + } + }; } #[test] @@ -14,8 +29,11 @@ fn key_value_cli_flag() -> anyhow::Result<()> { let test_value = uuid::Uuid::new_v4().to_string(); run_test( "key-value", + testing_framework::SpinMode::Http, ["--key-value".into(), format!("{test_key}={test_value}")], - move |spin: &mut testing_framework::Spin| { + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); assert_spin_request( spin, reqwest::Method::GET, @@ -29,36 +47,34 @@ fn key_value_cli_flag() -> anyhow::Result<()> { } #[test] +/// Test that basic http trigger support works fn http_smoke_test() -> anyhow::Result<()> { run_test( - "smoke-test", + "http-smoke-test", + testing_framework::SpinMode::Http, [], - move |spin: &mut testing_framework::Spin| { + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); assert_spin_request( spin, reqwest::Method::GET, - &format!("/test/hello"), + "/test/hello", 200, "I'm a teapot", )?; assert_spin_request( spin, reqwest::Method::GET, - &format!("/test/hello/wildcards/should/be/handled"), + "/test/hello/wildcards/should/be/handled", 200, "I'm a teapot", )?; + assert_spin_request(spin, reqwest::Method::GET, "/thishsouldfail", 404, "")?; assert_spin_request( spin, reqwest::Method::GET, - &format!("/thishsouldfail"), - 404, - "", - )?; - assert_spin_request( - spin, - reqwest::Method::GET, - &format!("/test/hello/test-placement"), + "/test/hello/test-placement", 200, "text for test", ) @@ -68,14 +84,63 @@ fn http_smoke_test() -> anyhow::Result<()> { Ok(()) } +#[test] +/// Test that basic redis trigger support works +fn redis_smoke_test() -> anyhow::Result<()> { + run_test( + "redis-smoke-test", + testing_framework::SpinMode::Redis, + [], + testing_framework::ServicesConfig::new(vec!["redis".into()])?, + move |env| { + let redis_port = env + .services_mut() + .get_port(6379)? + .context("no redis port was exposed by test services")?; + + let mut redis = redis::Client::open(format!("redis://localhost:{redis_port}")) + .context("could not connect to redis in test")?; + redis + .publish("my-channel", "msg-from-test") + .context("could not publish test message to redis")?; + assert_eventually!({ + match env.read_file(".spin/logs/hello_stdout.txt") { + Ok(logs) => { + let logs = String::from_utf8_lossy(&logs); + logs.contains("Got message: 'msg-from-test'") + } + Err(e) if e.kind() == std::io::ErrorKind::NotFound => false, + Err(e) => return Err(anyhow::anyhow!("could not read stdout file: {e}").into()), + } + }); + Ok(()) + }, + )?; + + Ok(()) +} + /// Run an e2e test fn run_test( test_name: impl Into, + mode: testing_framework::SpinMode, spin_up_args: impl IntoIterator, - test: impl testing_framework::Test, + services_config: testing_framework::ServicesConfig, + test: impl FnOnce( + &mut testing_framework::TestEnvironment, + ) -> testing_framework::TestResult + + 'static, ) -> testing_framework::TestResult { - let config = environment_config(test_name.into(), spin_up_args); - testing_framework::TestEnvironment::up(config)?.test(test)?; + let test_name = test_name.into(); + let config = testing_framework::TestEnvironmentConfig::spin( + spin_binary(), + spin_up_args, + move |env| preboot(&test_name, env), + services_config, + mode, + ); + let mut env = testing_framework::TestEnvironment::up(config)?; + test(&mut env)?; Ok(()) } @@ -101,19 +166,6 @@ fn assert_spin_request( Ok(()) } -/// Get the configuration for a test environment -fn environment_config( - test_name: String, - spin_up_args: impl IntoIterator, -) -> testing_framework::TestEnvironmentConfig { - testing_framework::TestEnvironmentConfig::spin( - spin_binary(), - spin_up_args, - move |env| preboot(&test_name, env), - testing_framework::ServicesConfig::none(), - ) -} - /// Get the test environment ready to run a test fn preboot( test: &str, @@ -130,6 +182,11 @@ fn preboot( Ok(()) } +/// Get the spin binary path +fn spin_binary() -> PathBuf { + env!("CARGO_BIN_EXE_spin").into() +} + #[cfg(feature = "e2e-tests")] mod spinup_tests { use super::testcases; @@ -197,11 +254,6 @@ mod spinup_tests { testcases::assets_routing_works(CONTROLLER).await } - #[tokio::test] - async fn head_rust_sdk_redis() { - testcases::head_rust_sdk_redis(CONTROLLER).await - } - #[tokio::test] async fn llm_works() { testcases::llm_works(CONTROLLER).await diff --git a/tests/test-components/components/Cargo.lock b/tests/test-components/components/Cargo.lock index e34b8d2bfe..5c2d5b7396 100644 --- a/tests/test-components/components/Cargo.lock +++ b/tests/test-components/components/Cargo.lock @@ -153,6 +153,15 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "head-rust-sdk-redis" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytes", + "spin-sdk", +] + [[package]] name = "heck" version = "0.4.1" diff --git a/tests/test-components/components/redis-smoke-test/Cargo.toml b/tests/test-components/components/redis-smoke-test/Cargo.toml new file mode 100644 index 0000000000..6d16c22b74 --- /dev/null +++ b/tests/test-components/components/redis-smoke-test/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "head-rust-sdk-redis" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1" +bytes = "1" +spin-sdk = { path = "../../../../sdk/rust" } diff --git a/tests/testcases/head-rust-sdk-redis/src/lib.rs b/tests/test-components/components/redis-smoke-test/src/lib.rs similarity index 100% rename from tests/testcases/head-rust-sdk-redis/src/lib.rs rename to tests/test-components/components/redis-smoke-test/src/lib.rs diff --git a/tests/testcases/head-rust-sdk-redis/.cargo/config.toml b/tests/testcases/head-rust-sdk-redis/.cargo/config.toml deleted file mode 100644 index 6b77899cb3..0000000000 --- a/tests/testcases/head-rust-sdk-redis/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-wasi" diff --git a/tests/testcases/head-rust-sdk-redis/Cargo.lock b/tests/testcases/head-rust-sdk-redis/Cargo.lock deleted file mode 100644 index f5c1e6231c..0000000000 --- a/tests/testcases/head-rust-sdk-redis/Cargo.lock +++ /dev/null @@ -1,587 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "async-trait" -version = "0.1.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "hashbrown" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" - -[[package]] -name = "head-rust-sdk-redis" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytes", - "http", - "spin-sdk", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "indexmap" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" -dependencies = [ - "equivalent", - "hashbrown", - "serde", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "proc-macro2" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "routefinder" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f8f99b10dedd317514253dda1fa7c14e344aac96e1f78149a64879ce282aca" -dependencies = [ - "smartcow", - "smartstring", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "semver" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" - -[[package]] -name = "serde" -version = "1.0.189" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.189" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "serde_json" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - -[[package]] -name = "smartcow" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "656fcb1c1fca8c4655372134ce87d8afdf5ec5949ebabe8d314be0141d8b5da2" -dependencies = [ - "smartstring", -] - -[[package]] -name = "smartstring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" -dependencies = [ - "autocfg", - "static_assertions", - "version_check", -] - -[[package]] -name = "spdx" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71" -dependencies = [ - "smallvec", -] - -[[package]] -name = "spin-macro" -version = "2.2.0-pre0" -dependencies = [ - "anyhow", - "bytes", - "http", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "spin-sdk" -version = "2.2.0-pre0" -dependencies = [ - "anyhow", - "async-trait", - "bytes", - "form_urlencoded", - "futures", - "http", - "once_cell", - "routefinder", - "serde", - "serde_json", - "spin-macro", - "thiserror", - "wit-bindgen", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasm-encoder" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca90ba1b5b0a70d3d49473c5579951f3bddc78d47b59256d2f9d4922b150aca" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-metadata" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14abc161bfda5b519aa229758b68f2a52b45a12b993808665c857d1a9a00223c" -dependencies = [ - "anyhow", - "indexmap", - "serde", - "serde_derive", - "serde_json", - "spdx", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.115.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e06c0641a4add879ba71ccb3a1e4278fd546f76f1eafb21d8f7b07733b547cd5" -dependencies = [ - "indexmap", - "semver", -] - -[[package]] -name = "wit-bindgen" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d92ce0ca6b6074059413a9581a637550c3a740581c854f9847ec293c8aed71" -dependencies = [ - "bitflags", - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "565b945ae074886071eccf9cdaf8ccd7b959c2b0d624095bea5fe62003e8b3e0" -dependencies = [ - "anyhow", - "wit-component", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5695ff4e41873ed9ce56d2787e6b5772bdad9e70e2c1d2d160621d1762257f4f" -dependencies = [ - "anyhow", - "heck", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91835ea4231da1fe7971679d505ba14be7826e192b6357f08465866ef482e08" -dependencies = [ - "anyhow", - "proc-macro2", - "quote", - "syn 2.0.38", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-component", -] - -[[package]] -name = "wit-component" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87488b57a08e2cbbd076b325acbe7f8666965af174d69d5929cd373bd54547f" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ace9943d89bbf3dbbc71b966da0e7302057b311f36a4ac3d65ddfef17b52cf" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", -] diff --git a/tests/testcases/head-rust-sdk-redis/Cargo.toml b/tests/testcases/head-rust-sdk-redis/Cargo.toml deleted file mode 100644 index 3de29bf0e3..0000000000 --- a/tests/testcases/head-rust-sdk-redis/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "head-rust-sdk-redis" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = [ "cdylib" ] - -[dependencies] -anyhow = "1" -bytes = "1" -http = "0.2" -spin-sdk = { path = "../../../sdk/rust"} - -[workspace] diff --git a/tests/testcases/smoke-test/assets/test.txt b/tests/testcases/http-smoke-test/assets/test.txt similarity index 100% rename from tests/testcases/smoke-test/assets/test.txt rename to tests/testcases/http-smoke-test/assets/test.txt diff --git a/tests/testcases/smoke-test/spin.toml b/tests/testcases/http-smoke-test/spin.toml similarity index 100% rename from tests/testcases/smoke-test/spin.toml rename to tests/testcases/http-smoke-test/spin.toml diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index c2353288bc..84710deae7 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -520,40 +520,6 @@ pub async fn assets_routing_works(controller: &dyn Controller) { tc.run(controller).await.unwrap() } -/// Test a redis app using the current branch's version of the Rust SDK -pub async fn head_rust_sdk_redis(controller: &dyn Controller) { - async fn checks( - _: AppMetadata, - _: Option>>, - stderr_stream: Option>>, - ) -> Result<()> { - wait_for_spin().await; - let stderr = get_output_stream(stderr_stream).await?; - anyhow::ensure!( - stderr.is_empty(), - "expected stderr to be empty, but it was not: {}", - stderr.join("\n") - ); - Ok(()) - } - - let tc = TestCaseBuilder::default() - .name("head-rust-sdk-redis".to_string()) - .appname(Some("head-rust-sdk-redis".to_string())) - .trigger_type("redis".to_string()) - .assertions( - |metadata: AppMetadata, - stdout_stream: Option>>, - stderr_stream: Option>>| { - Box::pin(checks(metadata, stdout_stream, stderr_stream)) - }, - ) - .build() - .unwrap(); - - tc.run(controller).await.unwrap() -} - pub async fn llm_works(controller: &dyn Controller) { async fn checks( metadata: AppMetadata, diff --git a/tests/testcases/head-rust-sdk-redis/spin.toml b/tests/testcases/redis-smoke-test/spin.toml similarity index 74% rename from tests/testcases/head-rust-sdk-redis/spin.toml rename to tests/testcases/redis-smoke-test/spin.toml index 7a5a8bf1f3..61403e352e 100644 --- a/tests/testcases/head-rust-sdk-redis/spin.toml +++ b/tests/testcases/redis-smoke-test/spin.toml @@ -2,12 +2,12 @@ spin_version = "1" authors = ["Fermyon Engineering "] description = "A simple redis application that exercises the Rust SDK in the current branch" name = "head-rust-sdk-redis" -trigger = {type = "redis", address = "redis://redis:6379"} +trigger = { type = "redis", address = "redis://localhost:%{port=6379}" } version = "1.0.0" [[component]] id = "hello" -source = "target/wasm32-wasi/release/head_rust_sdk_redis.wasm" +source = "%{source=redis-smoke-test}" [component.trigger] channel = "my-channel" [component.build] diff --git a/tests/testing-framework/src/lib.rs b/tests/testing-framework/src/lib.rs index 53bde64847..599560ee67 100644 --- a/tests/testing-framework/src/lib.rs +++ b/tests/testing-framework/src/lib.rs @@ -12,7 +12,7 @@ mod test_environment; pub use manifest_template::ManifestTemplate; pub use services::ServicesConfig; -pub use spin::Spin; +pub use spin::{Spin, SpinMode}; pub use test_environment::{TestEnvironment, TestEnvironmentConfig}; #[derive(Debug, Clone, Copy)] @@ -41,18 +41,18 @@ pub trait Test { type Failure; /// Run the test against the runtime - fn test(self, runtime: &mut Self::Runtime) -> TestResult; + fn test(self, env: &mut TestEnvironment) -> TestResult; } impl Test for F where - F: FnOnce(&mut Spin) -> TestResult + 'static, + F: FnOnce(&mut TestEnvironment) -> TestResult + 'static, { type Runtime = Spin; type Failure = E; - fn test(self, runtime: &mut Self::Runtime) -> TestResult { - self(runtime) + fn test(self, env: &mut TestEnvironment) -> TestResult { + self(env) } } diff --git a/tests/testing-framework/src/services.rs b/tests/testing-framework/src/services.rs index 5a87cc3d02..9baae60f63 100644 --- a/tests/testing-framework/src/services.rs +++ b/tests/testing-framework/src/services.rs @@ -52,8 +52,8 @@ impl Services { Ok(()) } - /// Get the host port that a service exposes a guest port on. - pub(crate) fn get_port(&mut self, guest_port: u16) -> anyhow::Result> { + /// Get the host port that one of the services exposes a guest port on. + pub fn get_port(&mut self, guest_port: u16) -> anyhow::Result> { let mut result = None; for service in &mut self.services { let host_port = service.ports().unwrap().get(&guest_port); diff --git a/tests/testing-framework/src/spin.rs b/tests/testing-framework/src/spin.rs index 6a01e9b80f..cb7d4fc585 100644 --- a/tests/testing-framework/src/spin.rs +++ b/tests/testing-framework/src/spin.rs @@ -10,15 +10,27 @@ pub struct Spin { #[allow(dead_code)] stdout: OutputStream, stderr: OutputStream, - port: u16, + io_mode: IoMode, } impl Spin { - /// Start Spin in `current_dir` using the binary at `spin_binary_path` pub fn start( spin_binary_path: &Path, current_dir: &Path, spin_up_args: Vec>, + mode: SpinMode, + ) -> anyhow::Result { + match mode { + SpinMode::Http => Self::start_http(spin_binary_path, current_dir, spin_up_args), + SpinMode::Redis => Self::start_redis(spin_binary_path, current_dir, spin_up_args), + } + } + + /// Start Spin in `current_dir` using the binary at `spin_binary_path` + pub fn start_http( + spin_binary_path: &Path, + current_dir: &Path, + spin_up_args: Vec>, ) -> anyhow::Result { let port = get_random_port()?; let mut child = Command::new(spin_binary_path) @@ -36,13 +48,13 @@ impl Spin { process: child, stdout, stderr, - port, + io_mode: IoMode::Http(port), }; let start = std::time::Instant::now(); loop { match std::net::TcpStream::connect(format!("127.0.0.1:{port}")) { Ok(_) => { - log::debug!("Spin started on port {}.", spin.port); + log::debug!("Spin started on port {port}."); return Ok(spin); } Err(e) => { @@ -71,18 +83,54 @@ impl Spin { ) } + pub fn start_redis( + spin_binary_path: &Path, + current_dir: &Path, + spin_up_args: Vec>, + ) -> anyhow::Result { + let mut child = Command::new(spin_binary_path) + .arg("up") + .current_dir(current_dir) + .args(spin_up_args) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + let stdout = OutputStream::new(child.stdout.take().unwrap()); + let stderr = OutputStream::new(child.stderr.take().unwrap()); + let mut spin = Self { + process: child, + stdout, + stderr, + io_mode: IoMode::Redis, + }; + // TODO this is a hack to wait for the redis service to start + std::thread::sleep(std::time::Duration::from_millis(10000)); + if let Some(status) = spin.try_wait()? { + anyhow::bail!( + "Spin exited early with status code {:?}\n{}{}", + status.code(), + spin.stdout.output_as_str().unwrap_or(""), + spin.stderr.output_as_str().unwrap_or("") + ); + } + Ok(spin) + } + pub fn make_http_request( &mut self, method: reqwest::Method, path: &str, ) -> anyhow::Result { + let IoMode::Http(port) = self.io_mode else { + anyhow::bail!("Spin is not running in HTTP mode"); + }; if let Some(status) = self.try_wait()? { anyhow::bail!("Spin exited early with status code {:?}", status.code()); } - log::debug!("Connecting to HTTP server on port {}...", self.port); + log::debug!("Connecting to HTTP server on port {port}..."); let request = reqwest::blocking::Request::new( method, - format!("http://localhost:{}{}", self.port, path) + format!("http://localhost:{}{}", port, path) .parse() .unwrap(), ); @@ -94,6 +142,10 @@ impl Spin { Ok(response) } + pub fn stdout(&mut self) -> &str { + self.stdout.output_as_str().unwrap_or("") + } + pub fn stderr(&mut self) -> &str { self.stderr.output_as_str().unwrap_or("") } @@ -131,6 +183,18 @@ fn kill_process(process: &mut std::process::Child) { } } +/// How this Spin instance is communicating with the outside world +enum IoMode { + Http(u16), + Redis, +} + +/// The mode start Spin up in +pub enum SpinMode { + Http, + Redis, +} + /// Uses a track to ge a random unused port fn get_random_port() -> anyhow::Result { Ok(std::net::TcpListener::bind("localhost:0")? diff --git a/tests/testing-framework/src/test_environment.rs b/tests/testing-framework/src/test_environment.rs index 4ba5d88e2e..9acf91eab2 100644 --- a/tests/testing-framework/src/test_environment.rs +++ b/tests/testing-framework/src/test_environment.rs @@ -2,8 +2,8 @@ use std::path::{Path, PathBuf}; use crate::{ services::{Services, ServicesConfig}, - spin::Spin, - Runtime, Test, TestResult, + spin::{Spin, SpinMode}, + Runtime, }; use anyhow::Context as _; @@ -37,15 +37,6 @@ impl TestEnvironment { Ok(env) } - /// Run test against runtime - pub fn test>(&mut self, test: T) -> TestResult { - let runtime = self - .runtime - .as_mut() - .context("runtime was not initialized")?; - test.test(runtime) - } - /// Whether an error has occurred fn error(&mut self) -> anyhow::Result<()> { self.services.healthy()?; @@ -57,6 +48,18 @@ impl TestEnvironment { } impl TestEnvironment { + /// Get the services that are running for the test + pub fn services_mut(&mut self) -> &mut Services { + &mut self.services + } + + /// Get the runtime that is running for the test + pub fn runtime_mut(&mut self) -> &mut R { + self.runtime + .as_mut() + .expect("runtime has not been initialized") + } + /// Copy a file into the test environment at the given relative path pub fn copy_into(&self, from: impl AsRef, into: impl AsRef) -> anyhow::Result<()> { fn copy_dir_all(from: &Path, into: &Path) -> anyhow::Result<()> { @@ -107,6 +110,11 @@ impl TestEnvironment { Ok(()) } + /// Read a file from the test environment at the given relative path + pub fn read_file(&self, path: impl AsRef) -> std::io::Result> { + std::fs::read(self.temp.path().join(path)) + } + /// Get the path to test environment pub(crate) fn path(&self) -> &Path { self.temp.path() @@ -128,18 +136,19 @@ impl TestEnvironmentConfig { /// * `preboot` - a callback that happens after the services have started but before the runtime is /// * `test` - a callback that runs the test against the runtime /// * `services_config` - the services that the test requires - pub fn spin<'a>( + pub fn spin( spin_binary: PathBuf, spin_up_args: impl IntoIterator, preboot: impl FnOnce(&mut TestEnvironment) -> anyhow::Result<()> + 'static, services_config: ServicesConfig, + mode: SpinMode, ) -> Self { let spin_up_args = spin_up_args.into_iter().collect(); Self { services_config, create_runtime: Box::new(move |env| { preboot(env)?; - Spin::start(&spin_binary, env.path(), spin_up_args) + Spin::start(&spin_binary, env.path(), spin_up_args, mode) }), } }