From 77d9d8cc412fbdcae0cc1f3fecdbf51dfc49f28e Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 11 Jan 2024 16:46:05 +0100 Subject: [PATCH 01/15] Add assets e2e test Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 61 +++++++++++++++++++++--- tests/testcases/mod.rs | 103 ----------------------------------------- 2 files changed, 55 insertions(+), 109 deletions(-) diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index e5366dc074..efdcbfaf2e 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -160,6 +160,58 @@ fn dynamic_env_test() -> anyhow::Result<()> { Ok(()) } +#[test] +fn assets_routing_test() -> anyhow::Result<()> { + run_test( + "assets-test", + testing_framework::SpinMode::Http, + [], + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); + let mut assert_file = |name: &str, content: &str| { + assert_spin_request( + spin, + testing_framework::Request::new( + reqwest::Method::GET, + &format!("/static/thisshouldbemounted/{name}"), + ), + 200, + &[], + content, + ) + }; + let mut assert_file_content_eq_name = + |name: &str| assert_file(name, &format!("{name}\n")); + + assert_file_content_eq_name("1")?; + assert_file_content_eq_name("2")?; + assert_file_content_eq_name("3")?; + assert_file("empty", "")?; + assert_file("one-byte", "{")?; + + let mut assert_not_found = |path: &str| { + assert_spin_request( + spin, + testing_framework::Request::new( + reqwest::Method::GET, + &format!("/static/{path}"), + ), + 404, + &[], + "Not Found", + ) + }; + + assert_not_found("donotmount/a")?; + assert_not_found("thisshouldbemounted/thisshouldbeexcluded/4")?; + Ok(()) + }, + )?; + + Ok(()) +} + /// Run an e2e test fn run_test( test_name: impl Into, @@ -213,7 +265,9 @@ fn assert_spin_request( ))); } if body != expected_body { - return Err(anyhow::anyhow!("expected {expected_body}, got {body}").into()); + return Err(testing_framework::TestError::Failure( + anyhow::anyhow!("expected body '{expected_body}', got '{body}'").into(), + )); } Ok(()) } @@ -301,11 +355,6 @@ mod spinup_tests { testcases::http_swift_works(CONTROLLER).await } - #[tokio::test] - async fn assets_routing_works() { - testcases::assets_routing_works(CONTROLLER).await - } - #[tokio::test] async fn llm_works() { testcases::llm_works(CONTROLLER).await diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index 674c40a540..42f6e5def9 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -417,109 +417,6 @@ pub async fn http_js_works(controller: &dyn Controller) { tc.run(controller).await.unwrap() } -pub async fn assets_routing_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - get_url(metadata.base.as_str(), "/static/thisshouldbemounted/1").as_str(), - Method::GET, - "", - 200, - &[], - Some("1\n"), - ) - .await?; - - assert_http_response( - get_url(metadata.base.as_str(), "/static/thisshouldbemounted/2").as_str(), - Method::GET, - "", - 200, - &[], - Some("2\n"), - ) - .await?; - - assert_http_response( - get_url(metadata.base.as_str(), "/static/thisshouldbemounted/3").as_str(), - Method::GET, - "", - 200, - &[], - Some("3\n"), - ) - .await?; - - assert_http_response( - get_url(metadata.base.as_str(), "/static/thisshouldbemounted/empty").as_str(), - Method::GET, - "", - 200, - &[], - None, - ) - .await?; - - assert_http_response( - get_url( - metadata.base.as_str(), - "/static/thisshouldbemounted/one-byte", - ) - .as_str(), - Method::GET, - "", - 200, - &[], - Some("{"), - ) - .await?; - - assert_http_response( - get_url(metadata.base.as_str(), "/static/donotmount/a").as_str(), - Method::GET, - "", - 404, - &[], - Some("Not Found"), - ) - .await?; - - assert_http_response( - get_url( - metadata.base.as_str(), - "/static/thisshouldbemounted/thisshouldbeexcluded/4", - ) - .as_str(), - Method::GET, - "", - 404, - &[], - Some("Not Found"), - ) - .await?; - - Ok(()) - } - - let tc = TestCaseBuilder::default() - .name("assets-test".to_string()) - .appname(Some("assets-test".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, From 41982e75980f7de6c01a20fd1289e187f37d3b6e Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 11 Jan 2024 16:59:11 +0100 Subject: [PATCH 02/15] Move legacy test to test framework Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 32 +++++++ .../README.md | 4 +- .../legacy-go.wasm} | Bin .../legacy-javascript.wasm} | Bin .../legacy-rust.wasm} | Bin .../legacy-typescript.wasm} | Bin .../spin.toml | 16 ++-- tests/testcases/mod.rs | 78 ------------------ 8 files changed, 41 insertions(+), 89 deletions(-) rename tests/testcases/{longevity-apps-test => legacy-apps-test}/README.md (81%) rename tests/testcases/{longevity-apps-test/longevity-go.wasm => legacy-apps-test/legacy-go.wasm} (100%) rename tests/testcases/{longevity-apps-test/longevity-javascript.wasm => legacy-apps-test/legacy-javascript.wasm} (100%) rename tests/testcases/{longevity-apps-test/longevity-rust.wasm => legacy-apps-test/legacy-rust.wasm} (100%) rename tests/testcases/{longevity-apps-test/longevity-typescript.wasm => legacy-apps-test/legacy-typescript.wasm} (100%) rename tests/testcases/{longevity-apps-test => legacy-apps-test}/spin.toml (56%) diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index efdcbfaf2e..fcf47e4ce2 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -161,6 +161,7 @@ fn dynamic_env_test() -> anyhow::Result<()> { } #[test] +/// Test that mounting works properly fn assets_routing_test() -> anyhow::Result<()> { run_test( "assets-test", @@ -212,6 +213,37 @@ fn assets_routing_test() -> anyhow::Result<()> { Ok(()) } +#[test] +/// Test that mounting works properly +fn legacy_apps() -> anyhow::Result<()> { + run_test( + "legacy-apps-test", + testing_framework::SpinMode::Http, + [], + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); + let mut test = |lang: &str, body: &str| { + assert_spin_request( + spin, + testing_framework::Request::new(reqwest::Method::GET, &format!("/{lang}")), + 200, + &[], + body, + ) + }; + + test("golang", "Hello Fermyon!\n")?; + test("rust", "Hello, Fermyon")?; + test("javascript", "Hello from JS-SDK")?; + test("typescript", "Hello from TS-SDK")?; + Ok(()) + }, + )?; + + Ok(()) +} + /// Run an e2e test fn run_test( test_name: impl Into, diff --git a/tests/testcases/longevity-apps-test/README.md b/tests/testcases/legacy-apps-test/README.md similarity index 81% rename from tests/testcases/longevity-apps-test/README.md rename to tests/testcases/legacy-apps-test/README.md index e2c9e37341..a84c738274 100644 --- a/tests/testcases/longevity-apps-test/README.md +++ b/tests/testcases/legacy-apps-test/README.md @@ -2,7 +2,7 @@ longevity test is to ensure `wasm` file(s) compiled with a version of `Spin` continues to work with runtime of future version of `Spin`. -The current wasm files are created using following templates with `Spin v0.9.0` +The current wasm files are created using following templates with `Spin v0.9.0 (a99ed51 2023-02-16)` - http-go - http-rust @@ -13,8 +13,6 @@ The `wasm` files are built using `spin build` and copied over here for validatio ## How to re-generate the wasm files -> When updating the wasm files, we also need to update the value of const `SPIN_VERSION_USED_TO_BUILD_APP` in `tests/testcases/mod.rs::longevity_apps_works`. It will enable us to efficiently track the change that triggered the backward incompatibility issue. - ### Install plugin and templates ``` diff --git a/tests/testcases/longevity-apps-test/longevity-go.wasm b/tests/testcases/legacy-apps-test/legacy-go.wasm similarity index 100% rename from tests/testcases/longevity-apps-test/longevity-go.wasm rename to tests/testcases/legacy-apps-test/legacy-go.wasm diff --git a/tests/testcases/longevity-apps-test/longevity-javascript.wasm b/tests/testcases/legacy-apps-test/legacy-javascript.wasm similarity index 100% rename from tests/testcases/longevity-apps-test/longevity-javascript.wasm rename to tests/testcases/legacy-apps-test/legacy-javascript.wasm diff --git a/tests/testcases/longevity-apps-test/longevity-rust.wasm b/tests/testcases/legacy-apps-test/legacy-rust.wasm similarity index 100% rename from tests/testcases/longevity-apps-test/longevity-rust.wasm rename to tests/testcases/legacy-apps-test/legacy-rust.wasm diff --git a/tests/testcases/longevity-apps-test/longevity-typescript.wasm b/tests/testcases/legacy-apps-test/legacy-typescript.wasm similarity index 100% rename from tests/testcases/longevity-apps-test/longevity-typescript.wasm rename to tests/testcases/legacy-apps-test/legacy-typescript.wasm diff --git a/tests/testcases/longevity-apps-test/spin.toml b/tests/testcases/legacy-apps-test/spin.toml similarity index 56% rename from tests/testcases/longevity-apps-test/spin.toml rename to tests/testcases/legacy-apps-test/spin.toml index 7b0f924940..18fe357dde 100644 --- a/tests/testcases/longevity-apps-test/spin.toml +++ b/tests/testcases/legacy-apps-test/spin.toml @@ -1,30 +1,30 @@ spin_version = "1" authors = ["Fermyon Engineering "] -description = "A longevity test for verifying backward compatible runtime" -name = "longevity-test" -trigger = {type = "http"} +description = "A legacy test for verifying backward compatible runtime" +name = "legacy-test" +trigger = { type = "http" } version = "1.0.0" [[component]] id = "go" -source = "longevity-go.wasm" +source = "legacy-go.wasm" [component.trigger] route = "/golang/..." [[component]] id = "rust" -source = "longevity-rust.wasm" +source = "legacy-rust.wasm" [component.trigger] route = "/rust/..." [[component]] id = "typescript" -source = "longevity-typescript.wasm" +source = "legacy-typescript.wasm" [component.trigger] route = "/typescript/..." [[component]] id = "javascript" -source = "longevity-javascript.wasm" +source = "legacy-javascript.wasm" [component.trigger] -route = "/javascript/..." \ No newline at end of file +route = "/javascript/..." diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index 42f6e5def9..559470c1af 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -668,84 +668,6 @@ pub async fn registry_works(controller: &dyn Controller) { tc.run(controller).await.unwrap() } -pub async fn longevity_apps_works(controller: &dyn Controller) { - let current_spin_version = spin::version().unwrap_or_else(|err| { - println!("error getting spin version {}", err); - String::new() - }); - - // version of spin that was used to generate the wasm files used in this testcase - const SPIN_VERSION_USED_TO_BUILD_APP: &str = "spin version 0.9.0 (a99ed51 2023-02-16)"; - - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - get_url(metadata.base.as_str(), "/golang").as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello Fermyon!\n"), - ) - .await?; - - assert_http_response( - get_url(metadata.base.as_str(), "/rust").as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello, Fermyon"), - ) - .await?; - - assert_http_response( - get_url(metadata.base.as_str(), "/javascript").as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello from JS-SDK"), - ) - .await?; - - assert_http_response( - get_url(metadata.base.as_str(), "/typescript").as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello from TS-SDK"), - ) - .await?; - - Ok(()) - } - - let tc = TestCaseBuilder::default() - .name("longevity-apps-test".to_string()) - .appname(Some("longevity-apps-test".to_string())) - .assertions( - |metadata: AppMetadata, - stdout_stream: Option>>, - stderr_stream: Option>>| { - Box::pin(checks(metadata, stdout_stream, stderr_stream)) - }, - ) - .build() - .unwrap(); - - if let Err(e) = tc.run(controller).await { - panic!( - ":\n\napp using a wasm module built by '{}' no longer works with {}\nError: {}", - SPIN_VERSION_USED_TO_BUILD_APP, current_spin_version, e - ) - } -} - pub async fn error_messages(controller: &dyn Controller) { async fn checks( _metadata: AppMetadata, From 2a4f1471d5c1acf05fe4c8d68cf2c2b6b57c75af Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 11 Jan 2024 17:23:59 +0100 Subject: [PATCH 03/15] Test error Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 45 ++++++++++++++++++++--------- tests/testcases/error/error.txt | 4 --- tests/testcases/mod.rs | 35 ---------------------- tests/testing-framework/src/spin.rs | 34 +++++++++++++++++++++- 4 files changed, 65 insertions(+), 53 deletions(-) delete mode 100644 tests/testcases/error/error.txt diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index fcf47e4ce2..a32d5505d0 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -244,6 +244,25 @@ fn legacy_apps() -> anyhow::Result<()> { Ok(()) } +#[test] +fn bad_build_test() -> anyhow::Result<()> { + let mut env = bootstap_env( + "error", + [], + testing_framework::ServicesConfig::none(), + testing_framework::SpinMode::None, + )?; + let expected = r#"Error: Couldn't find trigger executor for local app "spin.toml" + +Caused by: + no triggers in app +"#; + + assert_eq!(env.runtime_mut().stderr(), expected); + + Ok(()) +} + /// Run an e2e test fn run_test( test_name: impl Into, @@ -255,6 +274,18 @@ fn run_test( ) -> testing_framework::TestResult + 'static, ) -> testing_framework::TestResult { + let mut env = bootstap_env(test_name, spin_up_args, services_config, mode)?; + test(&mut env)?; + Ok(()) +} + +/// Bootstrap a test environment +fn bootstap_env( + test_name: impl Into, + spin_up_args: impl IntoIterator, + services_config: testing_framework::ServicesConfig, + mode: testing_framework::SpinMode, +) -> anyhow::Result> { let test_name = test_name.into(); let config = testing_framework::TestEnvironmentConfig::spin( spin_binary(), @@ -263,9 +294,7 @@ fn run_test( services_config, mode, ); - let mut env = testing_framework::TestEnvironment::up(config)?; - test(&mut env)?; - Ok(()) + testing_framework::TestEnvironment::up(config) } /// Assert that a request to the spin server returns the expected status and body @@ -411,14 +440,4 @@ mod spinup_tests { async fn registry_works() { testcases::registry_works(CONTROLLER).await } - - #[tokio::test] - async fn longevity_apps_works() { - testcases::longevity_apps_works(CONTROLLER).await - } - - #[tokio::test] - async fn error_messages() { - testcases::error_messages(CONTROLLER).await - } } diff --git a/tests/testcases/error/error.txt b/tests/testcases/error/error.txt deleted file mode 100644 index 4eaf4ae8db..0000000000 --- a/tests/testcases/error/error.txt +++ /dev/null @@ -1,4 +0,0 @@ -Error: Couldn't find trigger executor for local app "spin.toml" - -Caused by: - no triggers in app \ No newline at end of file diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index 559470c1af..7db163725f 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -668,41 +668,6 @@ pub async fn registry_works(controller: &dyn Controller) { tc.run(controller).await.unwrap() } -pub async fn error_messages(controller: &dyn Controller) { - async fn checks( - _metadata: AppMetadata, - _stdout: Option>>, - stderr: Option>>, - ) -> Result<()> { - let appdir = spin::appdir("error"); - let expected = tokio::fs::read_to_string(appdir.join("error.txt")) - .await - .unwrap() - .replace( - "$APPDIR", - &appdir.canonicalize().unwrap().display().to_string(), - ); - let actual = utils::get_output(stderr).await.unwrap(); - assert_eq!(actual, expected); - Ok(()) - } - - let tc = TestCaseBuilder::default() - .name("error".to_string()) - .appname(Some("error".to_string())) - .assertions( - |metadata: AppMetadata, - stdout_stream: Option>>, - stderr_stream: Option>>| { - Box::pin(checks(metadata, stdout_stream, stderr_stream)) - }, - ) - .build() - .unwrap(); - - tc.try_run(controller).await.unwrap(); -} - async fn get_output_stream( stream: Option>>, ) -> anyhow::Result> { diff --git a/tests/testing-framework/src/spin.rs b/tests/testing-framework/src/spin.rs index 2322637129..9fa61a4693 100644 --- a/tests/testing-framework/src/spin.rs +++ b/tests/testing-framework/src/spin.rs @@ -25,6 +25,7 @@ impl Spin { 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), + SpinMode::None => Self::attempt_start(spin_binary_path, current_dir, spin_up_args), } } @@ -118,6 +119,29 @@ impl Spin { Ok(spin) } + fn attempt_start( + 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()); + child.wait()?; + Ok(Self { + process: child, + stdout, + stderr, + io_mode: IoMode::None, + }) + } + /// Make an HTTP request against Spin /// /// Will fail if Spin has already exited or if the io mode is not HTTP @@ -177,7 +201,7 @@ impl Drop for Spin { impl Runtime for Spin { fn error(&mut self) -> anyhow::Result<()> { - if self.try_wait()?.is_some() { + if !matches!(self.io_mode, IoMode::None) && self.try_wait()?.is_some() { anyhow::bail!("Spin exited early: {}", self.stderr()); } @@ -199,14 +223,22 @@ fn kill_process(process: &mut std::process::Child) { /// How this Spin instance is communicating with the outside world enum IoMode { + /// An http server is running on this port Http(u16), + /// Spin is running in redis mode Redis, + /// Spin may or may not be running + None, } /// The mode start Spin up in pub enum SpinMode { + /// Expect an http listener to start Http, + /// Expect a redis listener to start Redis, + /// Don't expect spin to start + None, } /// Uses a track to ge a random unused port From 3b8e467bb840ebd4f235e760e078a0348a4ec175 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 11 Jan 2024 17:24:36 +0100 Subject: [PATCH 04/15] Remove redundant test Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 5 - .../.cargo/config.toml | 2 - .../headers-env-routes-test/Cargo.lock | 587 ------------------ .../headers-env-routes-test/Cargo.toml | 22 - .../headers-env-routes-test/spin.toml | 15 - .../headers-env-routes-test/src/lib.rs | 25 - tests/testcases/mod.rs | 45 -- 7 files changed, 701 deletions(-) delete mode 100644 tests/testcases/headers-env-routes-test/.cargo/config.toml delete mode 100644 tests/testcases/headers-env-routes-test/Cargo.lock delete mode 100644 tests/testcases/headers-env-routes-test/Cargo.toml delete mode 100644 tests/testcases/headers-env-routes-test/spin.toml delete mode 100644 tests/testcases/headers-env-routes-test/src/lib.rs diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index a32d5505d0..7b5fef049f 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -421,11 +421,6 @@ mod spinup_tests { testcases::llm_works(CONTROLLER).await } - #[tokio::test] - async fn header_env_routes_works() { - testcases::header_env_routes_works(CONTROLLER).await - } - #[tokio::test] async fn redis_go_works() { testcases::redis_go_works(CONTROLLER).await diff --git a/tests/testcases/headers-env-routes-test/.cargo/config.toml b/tests/testcases/headers-env-routes-test/.cargo/config.toml deleted file mode 100644 index 6b77899cb3..0000000000 --- a/tests/testcases/headers-env-routes-test/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-wasi" diff --git a/tests/testcases/headers-env-routes-test/Cargo.lock b/tests/testcases/headers-env-routes-test/Cargo.lock deleted file mode 100644 index 27190675e6..0000000000 --- a/tests/testcases/headers-env-routes-test/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 = "headers-env-routes-test" -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/headers-env-routes-test/Cargo.toml b/tests/testcases/headers-env-routes-test/Cargo.toml deleted file mode 100644 index 47c1fac7dd..0000000000 --- a/tests/testcases/headers-env-routes-test/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "headers-env-routes-test" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = [ "cdylib" ] - -[dependencies] -# Useful crate to handle errors. -anyhow = "1" -# Crate to simplify working with bytes. -bytes = "1" -# General-purpose crate with common HTTP types. -http = "0.2" -spin-sdk = { path = "../../../sdk/rust"} - -[workspace] - -# Metadata about this component. -[package.metadata.component] -name = "spinhelloworld" diff --git a/tests/testcases/headers-env-routes-test/spin.toml b/tests/testcases/headers-env-routes-test/spin.toml deleted file mode 100644 index e44e173aed..0000000000 --- a/tests/testcases/headers-env-routes-test/spin.toml +++ /dev/null @@ -1,15 +0,0 @@ -spin_version = "1" -name = "headers-env-routes-test" -version = "1.0.0" -authors = ["Fermyon Engineering "] -description = "A simple application that returns hello and goodbye." -trigger = {type = "http"} - -[[component]] -id = "env" -source = "target/wasm32-wasi/release/headers_env_routes_test.wasm" -environment = { some_key = "some_value" } -[component.trigger] -route = "/env/..." -[component.build] -command = "cargo build --target wasm32-wasi --release" \ No newline at end of file diff --git a/tests/testcases/headers-env-routes-test/src/lib.rs b/tests/testcases/headers-env-routes-test/src/lib.rs deleted file mode 100644 index 94a46f6074..0000000000 --- a/tests/testcases/headers-env-routes-test/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -use anyhow::Result; -use spin_sdk::{http::IntoResponse, http_component}; - -// This handler does the following: -// - returns all environment variables as headers with an ENV_ prefix -// - returns all request headers as response headers. -#[http_component] -fn handle_http_request(req: http::Request<()>) -> Result { - let resp = append_headers(http::Response::builder(), &req); - Ok(resp.status(200).body(Some("I'm a teapot"))?) -} - -fn append_headers( - mut resp: http::response::Builder, - req: &http::Request<()>, -) -> http::response::Builder { - for (k, v) in std::env::vars() { - resp = resp.header(format!("ENV_{}", k), v); - } - for (k, v) in req.headers().iter() { - resp = resp.header(k, v); - } - - resp -} diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index 7db163725f..733380c9d4 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -456,51 +456,6 @@ pub async fn llm_works(controller: &dyn Controller) { tc.run(controller).await.unwrap() } -pub async fn header_env_routes_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - get_url(metadata.base.as_str(), "/env").as_str(), - Method::GET, - "", - 200, - &[], - Some("I'm a teapot"), - ) - .await?; - - assert_http_response( - get_url(metadata.base.as_str(), "/env/foo").as_str(), - Method::GET, - "", - 200, - &[("env_some_key", "some_value")], - Some("I'm a teapot"), - ) - .await?; - - Ok(()) - } - - let tc = TestCaseBuilder::default() - .name("headers-env-routes-test".to_string()) - .appname(Some("headers-env-routes-test".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 redis_go_works(controller: &dyn Controller) { async fn checks( _: AppMetadata, From fd1b0d5006196480845ea542b4ece21d2c87c670 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 11 Jan 2024 17:46:07 +0100 Subject: [PATCH 05/15] Add outbound http test Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 37 +- tests/test-components/components/Cargo.lock | 18 + .../components/hello-world/Cargo.toml | 12 + .../components/hello-world}/src/lib.rs | 5 +- .../components/outbound-http}/Cargo.toml | 7 - .../components/outbound-http}/src/lib.rs | 1 - tests/testcases/mod.rs | 47 -- .../http-component/.cargo/config.toml | 2 - .../http-component/Cargo.lock | 587 ------------------ .../http-component/Cargo.toml | 19 - .../.cargo/config.toml | 2 - .../outbound-http-component/Cargo.lock | 587 ------------------ .../outbound-http-to-same-app/spin.toml | 15 +- 13 files changed, 67 insertions(+), 1272 deletions(-) create mode 100644 tests/test-components/components/hello-world/Cargo.toml rename tests/{testcases/outbound-http-to-same-app/http-component => test-components/components/hello-world}/src/lib.rs (61%) rename tests/{testcases/outbound-http-to-same-app/outbound-http-component => test-components/components/outbound-http}/Cargo.toml (54%) rename tests/{testcases/outbound-http-to-same-app/outbound-http-component => test-components/components/outbound-http}/src/lib.rs (95%) delete mode 100644 tests/testcases/outbound-http-to-same-app/http-component/.cargo/config.toml delete mode 100644 tests/testcases/outbound-http-to-same-app/http-component/Cargo.lock delete mode 100644 tests/testcases/outbound-http-to-same-app/http-component/Cargo.toml delete mode 100644 tests/testcases/outbound-http-to-same-app/outbound-http-component/.cargo/config.toml delete mode 100644 tests/testcases/outbound-http-to-same-app/outbound-http-component/Cargo.lock diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index 7b5fef049f..3c51832424 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -263,6 +263,38 @@ Caused by: Ok(()) } +#[test] +fn outbound_http_works() -> anyhow::Result<()> { + run_test( + "outbound-http-to-same-app", + testing_framework::SpinMode::Http, + [], + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); + assert_spin_request( + spin, + testing_framework::Request::new(reqwest::Method::GET, "/test/outbound-allowed"), + 200, + &[], + "Hello, Fermyon!\n", + )?; + + assert_spin_request( + spin, + testing_framework::Request::new(reqwest::Method::GET, "/test/outbound-not-allowed"), + 500, + &[], + "", + )?; + + Ok(()) + }, + )?; + + Ok(()) +} + /// Run an e2e test fn run_test( test_name: impl Into, @@ -360,11 +392,6 @@ mod spinup_tests { use {e2e_testing::controller::Controller, e2e_testing::spin_controller::SpinUp}; const CONTROLLER: &dyn Controller = &SpinUp {}; - #[tokio::test] - async fn component_outbound_http_works() { - testcases::component_outbound_http_works(CONTROLLER).await - } - #[tokio::test] async fn http_go_works() { testcases::http_go_works(CONTROLLER).await diff --git a/tests/test-components/components/Cargo.lock b/tests/test-components/components/Cargo.lock index 0685a4ce03..17c1f8675e 100644 --- a/tests/test-components/components/Cargo.lock +++ b/tests/test-components/components/Cargo.lock @@ -180,6 +180,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hello-world" +version = "0.1.0" +dependencies = [ + "anyhow", + "http", + "spin-sdk", +] + [[package]] name = "helper" version = "0.1.0" @@ -281,6 +290,15 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "outbound-http-component" +version = "0.1.0" +dependencies = [ + "anyhow", + "http", + "spin-sdk", +] + [[package]] name = "outbound-mysql-test-component" version = "0.1.0" diff --git a/tests/test-components/components/hello-world/Cargo.toml b/tests/test-components/components/hello-world/Cargo.toml new file mode 100644 index 0000000000..4d439678a3 --- /dev/null +++ b/tests/test-components/components/hello-world/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "hello-world" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1" +http = "0.2" +spin-sdk = { path = "../../../../sdk/rust" } diff --git a/tests/testcases/outbound-http-to-same-app/http-component/src/lib.rs b/tests/test-components/components/hello-world/src/lib.rs similarity index 61% rename from tests/testcases/outbound-http-to-same-app/http-component/src/lib.rs rename to tests/test-components/components/hello-world/src/lib.rs index 6deca439de..b6d839ac0f 100644 --- a/tests/testcases/outbound-http-to-same-app/http-component/src/lib.rs +++ b/tests/test-components/components/hello-world/src/lib.rs @@ -1,12 +1,11 @@ -use anyhow::Result; use spin_sdk::http_component; /// A simple Spin HTTP component. #[http_component] -fn hello_world(req: http::Request<()>) -> Result> { +fn hello_world(req: http::Request<()>) -> anyhow::Result> { println!("{:?}", req.headers()); Ok(http::Response::builder() .status(200) - .header("foo", "bar") + .header("Content-Type", "text/plain") .body("Hello, Fermyon!\n")?) } diff --git a/tests/testcases/outbound-http-to-same-app/outbound-http-component/Cargo.toml b/tests/test-components/components/outbound-http/Cargo.toml similarity index 54% rename from tests/testcases/outbound-http-to-same-app/outbound-http-component/Cargo.toml rename to tests/test-components/components/outbound-http/Cargo.toml index 5e13e10410..8b2a49e5ff 100644 --- a/tests/testcases/outbound-http-to-same-app/outbound-http-component/Cargo.toml +++ b/tests/test-components/components/outbound-http/Cargo.toml @@ -7,13 +7,6 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -# Useful crate to handle errors. anyhow = "1" -# Crate to simplify working with bytes. -bytes = "1" -# General-purpose crate with common HTTP types. http = "0.2" -# The Spin SDK. spin-sdk = { path = "../../../../sdk/rust" } - -[workspace] diff --git a/tests/testcases/outbound-http-to-same-app/outbound-http-component/src/lib.rs b/tests/test-components/components/outbound-http/src/lib.rs similarity index 95% rename from tests/testcases/outbound-http-to-same-app/outbound-http-component/src/lib.rs rename to tests/test-components/components/outbound-http/src/lib.rs index 788e34fa23..5799ac6674 100644 --- a/tests/testcases/outbound-http-to-same-app/outbound-http-component/src/lib.rs +++ b/tests/test-components/components/outbound-http/src/lib.rs @@ -16,6 +16,5 @@ async fn send_outbound(_req: Request) -> Result { .await?; res.headers_mut() .insert("spin-component", "outbound-http-component".try_into()?); - println!("{:?}", res); Ok(res) } diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index 733380c9d4..26fb2b0420 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -14,53 +14,6 @@ fn get_url(base: &str, path: &str) -> String { format!("{}{}", base, path) } -pub async fn component_outbound_http_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - get_url(metadata.base.as_str(), "/test/outbound-allowed").as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello, Fermyon!\n"), - ) - .await?; - - assert_http_response( - get_url(metadata.base.as_str(), "/test/outbound-not-allowed").as_str(), - Method::GET, - "", - 500, - &[], - None, - ) - .await?; - - Ok(()) - } - - let tc = TestCaseBuilder::default() - .name("outbound-http-to-same-app".to_string()) - //the appname should be same as dir where this app exists - .appname(Some("outbound-http-to-same-app".to_string())) - .template(None) - .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 http_python_works(controller: &dyn Controller) { async fn checks( metadata: AppMetadata, diff --git a/tests/testcases/outbound-http-to-same-app/http-component/.cargo/config.toml b/tests/testcases/outbound-http-to-same-app/http-component/.cargo/config.toml deleted file mode 100644 index 6b77899cb3..0000000000 --- a/tests/testcases/outbound-http-to-same-app/http-component/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-wasi" diff --git a/tests/testcases/outbound-http-to-same-app/http-component/Cargo.lock b/tests/testcases/outbound-http-to-same-app/http-component/Cargo.lock deleted file mode 100644 index 103faaed4d..0000000000 --- a/tests/testcases/outbound-http-to-same-app/http-component/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 = "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 = "http-component" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytes", - "http", - "spin-sdk", -] - -[[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/outbound-http-to-same-app/http-component/Cargo.toml b/tests/testcases/outbound-http-to-same-app/http-component/Cargo.toml deleted file mode 100644 index 2d778a64c0..0000000000 --- a/tests/testcases/outbound-http-to-same-app/http-component/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "http-component" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -# Useful crate to handle errors. -anyhow = "1" -# Crate to simplify working with bytes. -bytes = "1" -# General-purpose crate with common HTTP types. -http = "0.2" -# The Spin SDK. -spin-sdk = { path = "../../../../sdk/rust" } - -[workspace] diff --git a/tests/testcases/outbound-http-to-same-app/outbound-http-component/.cargo/config.toml b/tests/testcases/outbound-http-to-same-app/outbound-http-component/.cargo/config.toml deleted file mode 100644 index 6b77899cb3..0000000000 --- a/tests/testcases/outbound-http-to-same-app/outbound-http-component/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-wasi" diff --git a/tests/testcases/outbound-http-to-same-app/outbound-http-component/Cargo.lock b/tests/testcases/outbound-http-to-same-app/outbound-http-component/Cargo.lock deleted file mode 100644 index 207888bc09..0000000000 --- a/tests/testcases/outbound-http-to-same-app/outbound-http-component/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 = "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 = "outbound-http-component" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytes", - "http", - "spin-sdk", -] - -[[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/outbound-http-to-same-app/spin.toml b/tests/testcases/outbound-http-to-same-app/spin.toml index 1b9dea8466..90e517f550 100644 --- a/tests/testcases/outbound-http-to-same-app/spin.toml +++ b/tests/testcases/outbound-http-to-same-app/spin.toml @@ -7,28 +7,19 @@ version = "1.0.0" [[component]] id = "hello" -source = "http-component/target/wasm32-wasi/release/http_component.wasm" +source = "%{source=hello-world}" [component.trigger] route = "/hello/..." -[component.build] -workdir = "http-component" -command = "cargo build --target wasm32-wasi --release" [[component]] id = "outbound-http-allowed" -source = "outbound-http-component/target/wasm32-wasi/release/outbound_http_component.wasm" +source = "%{source=outbound-http}" allowed_http_hosts = ["self"] [component.trigger] route = "/outbound-allowed/..." -[component.build] -workdir = "outbound-http-component" -command = "cargo build --target wasm32-wasi --release" [[component]] id = "outbound-http-not-allowed" -source = "outbound-http-component/target/wasm32-wasi/release/outbound_http_component.wasm" +source = "%{source=outbound-http}" [component.trigger] route = "/outbound-not-allowed/..." -[component.build] -workdir = "outbound-http-component" -command = "cargo build --target wasm32-wasi --release" From 944c86357298c53703ba027d36ec0cce4403f523 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Jan 2024 09:46:08 +0100 Subject: [PATCH 06/15] Make llm test a runtime test Signed-off-by: Ryan Levick --- tests/runtime-tests/tests/llm/error.txt | 1 + tests/runtime-tests/tests/llm/spin.toml | 13 + tests/spinup_tests.rs | 5 - tests/test-components/components/Cargo.lock | 8 + .../components}/llm/Cargo.toml | 6 +- .../test-components/components/llm/src/lib.rs | 28 + tests/testcases/llm/Cargo.lock | 582 ------------------ tests/testcases/llm/spin.toml | 17 - tests/testcases/llm/src/lib.rs | 14 - tests/testcases/mod.rs | 39 -- 10 files changed, 52 insertions(+), 661 deletions(-) create mode 100644 tests/runtime-tests/tests/llm/error.txt create mode 100644 tests/runtime-tests/tests/llm/spin.toml rename tests/{testcases => test-components/components}/llm/Cargo.toml (71%) create mode 100644 tests/test-components/components/llm/src/lib.rs delete mode 100644 tests/testcases/llm/Cargo.lock delete mode 100644 tests/testcases/llm/spin.toml delete mode 100644 tests/testcases/llm/src/lib.rs diff --git a/tests/runtime-tests/tests/llm/error.txt b/tests/runtime-tests/tests/llm/error.txt new file mode 100644 index 0000000000..b9e3ad9f4a --- /dev/null +++ b/tests/runtime-tests/tests/llm/error.txt @@ -0,0 +1 @@ +Could not read model registry directory '.spin/ai-models' \ No newline at end of file diff --git a/tests/runtime-tests/tests/llm/spin.toml b/tests/runtime-tests/tests/llm/spin.toml new file mode 100644 index 0000000000..9a1e18e1f9 --- /dev/null +++ b/tests/runtime-tests/tests/llm/spin.toml @@ -0,0 +1,13 @@ +spin_manifest_version = "1" +authors = ["Ryan Levick "] +description = "" +name = "ai" +trigger = { type = "http", base = "/" } +version = "0.1.0" + +[[component]] +id = "ai" +source = "%{source=llm}" +ai_models = ["llama2-chat"] +[component.trigger] +route = "/..." diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index 3c51832424..65a384a97c 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -443,11 +443,6 @@ mod spinup_tests { testcases::http_swift_works(CONTROLLER).await } - #[tokio::test] - async fn llm_works() { - testcases::llm_works(CONTROLLER).await - } - #[tokio::test] async fn redis_go_works() { testcases::redis_go_works(CONTROLLER).await diff --git a/tests/test-components/components/Cargo.lock b/tests/test-components/components/Cargo.lock index 17c1f8675e..166983768a 100644 --- a/tests/test-components/components/Cargo.lock +++ b/tests/test-components/components/Cargo.lock @@ -2,6 +2,14 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ai" +version = "0.1.0" +dependencies = [ + "helper", + "wit-bindgen 0.16.0", +] + [[package]] name = "anyhow" version = "1.0.75" diff --git a/tests/testcases/llm/Cargo.toml b/tests/test-components/components/llm/Cargo.toml similarity index 71% rename from tests/testcases/llm/Cargo.toml rename to tests/test-components/components/llm/Cargo.toml index c7d87f5f05..1e11ef57f9 100644 --- a/tests/testcases/llm/Cargo.toml +++ b/tests/test-components/components/llm/Cargo.toml @@ -9,7 +9,5 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -anyhow = "1" -spin-sdk = { path = "../../../sdk/rust" } - -[workspace] +helper = { path = "../../helper" } +wit-bindgen = "0.16.0" diff --git a/tests/test-components/components/llm/src/lib.rs b/tests/test-components/components/llm/src/lib.rs new file mode 100644 index 0000000000..5f497d6317 --- /dev/null +++ b/tests/test-components/components/llm/src/lib.rs @@ -0,0 +1,28 @@ +use helper::{ensure, ensure_eq, ensure_ok}; + +use bindings::fermyon::spin2_0_0::llm; + +helper::define_component!(Component); + +impl Component { + fn main() -> Result<(), String> { + let param = llm::InferencingParams { + max_tokens: 1, + repeat_penalty: 0.0, + repeat_penalty_last_n_token_count: 0, + temperature: 0.0, + top_k: 1, + top_p: 1.0, + }; + let inference = ensure_ok!(llm::infer( + &"llama2-chat".to_owned(), + "say hello", + Some(param) + )); + + ensure!(inference.text.len() > 0); + ensure_eq!(inference.usage.generated_token_count, 1); + + Ok(()) + } +} diff --git a/tests/testcases/llm/Cargo.lock b/tests/testcases/llm/Cargo.lock deleted file mode 100644 index f4bee584e0..0000000000 --- a/tests/testcases/llm/Cargo.lock +++ /dev/null @@ -1,582 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ai" -version = "0.1.0" -dependencies = [ - "anyhow", - "spin-sdk", -] - -[[package]] -name = "anyhow" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" - -[[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.28", -] - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[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.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" - -[[package]] -name = "futures-executor" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" - -[[package]] -name = "futures-macro" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "futures-sink" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" - -[[package]] -name = "futures-task" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" - -[[package]] -name = "futures-util" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" -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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" - -[[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.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[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.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" -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.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - -[[package]] -name = "serde" -version = "1.0.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" - -[[package]] -name = "serde_derive" -version = "1.0.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "serde_json" -version = "1.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" -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.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[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.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-metadata" -version = "0.10.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8" -dependencies = [ - "anyhow", - "indexmap", - "serde", - "serde_derive", - "serde_json", - "spdx", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.116.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1" -dependencies = [ - "indexmap", - "semver", -] - -[[package]] -name = "wit-bindgen" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38726c54a5d7c03cac28a2a8de1006cfe40397ddf6def3f836189033a413bc08" -dependencies = [ - "bitflags", - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8bf1fddccaff31a1ad57432d8bfb7027a7e552969b6c68d6d8820dcf5c2371f" -dependencies = [ - "anyhow", - "wit-component", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7200e565124801e01b7b5ddafc559e1da1b2e1bed5364d669cd1d96fb88722" -dependencies = [ - "anyhow", - "heck", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae33920ad8119fe72cf59eb00f127c0b256a236b9de029a1a10397b1f38bdbd" -dependencies = [ - "anyhow", - "proc-macro2", - "quote", - "syn 2.0.28", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-component", -] - -[[package]] -name = "wit-component" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", -] diff --git a/tests/testcases/llm/spin.toml b/tests/testcases/llm/spin.toml deleted file mode 100644 index 101d5c10d4..0000000000 --- a/tests/testcases/llm/spin.toml +++ /dev/null @@ -1,17 +0,0 @@ -spin_manifest_version = "1" -authors = ["Ryan Levick "] -description = "" -name = "ai" -trigger = { type = "http", base = "/" } -version = "0.1.0" - -[[component]] -id = "ai" -source = "target/wasm32-wasi/release/ai.wasm" -allowed_http_hosts = [] -ai_models = ["llama2-chat","all-minilm-l6-v2"] -[component.trigger] -route = "/..." -[component.build] -command = "cargo build --target wasm32-wasi --release" -watch = ["src/**/*.rs", "Cargo.toml"] diff --git a/tests/testcases/llm/src/lib.rs b/tests/testcases/llm/src/lib.rs deleted file mode 100644 index ad40acbb4f..0000000000 --- a/tests/testcases/llm/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -use anyhow::Result; -use spin_sdk::{ - http::{Request, Response}, - http_component, llm, -}; - -/// A simple Spin HTTP component. -#[http_component] -fn hello_world(_req: Request) -> Result { - let model = llm::InferencingModel::Llama2Chat; - let inference = llm::infer(model, "say hello")?; - - Ok(Response::builder().status(200).body(inference.text).build()) -} diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index 26fb2b0420..bd2f6f45b7 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -370,45 +370,6 @@ pub async fn http_js_works(controller: &dyn Controller) { tc.run(controller).await.unwrap() } -pub async fn llm_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - let response = e2e_testing::http_asserts::make_request( - Method::GET, - get_url(metadata.base.as_str(), "/").as_str(), - "", - ) - .await?; - // We avoid actually running inferencing because it's slow and instead just - // ensure that the app boots properly. - assert_eq!(response.status(), 500); - let body = std::str::from_utf8(&response.bytes().await?) - .unwrap() - .to_string(); - assert!(body.contains("Could not read model registry directory")); - - Ok(()) - } - - let tc = TestCaseBuilder::default() - .name("llm".to_string()) - .appname(Some("llm".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 redis_go_works(controller: &dyn Controller) { async fn checks( _: AppMetadata, From c595dc7e051269a28e7c954e7ef7f66002c96d57 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Jan 2024 10:46:32 +0100 Subject: [PATCH 07/15] Add template support Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 62 +++++++++++++++++++ tests/testing-framework/src/spin.rs | 30 ++++----- .../testing-framework/src/test_environment.rs | 61 +++++++++++++----- 3 files changed, 122 insertions(+), 31 deletions(-) diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index 65a384a97c..1045dc1328 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -295,6 +295,68 @@ fn outbound_http_works() -> anyhow::Result<()> { Ok(()) } +#[test] +fn http_python_template_smoke_test() -> anyhow::Result<()> { + smoke_test_template( + "http-py", + "https://github.com/fermyon/spin-python-sdk", + &["py2wasm"], + "Hello from the Python SDK", + ) +} + +/// Run a smoke test against a `spin new` template +fn smoke_test_template( + template_name: &str, + template_url: &str, + plugins: &[&str], + expected_body: &str, +) -> anyhow::Result<()> { + let env: testing_framework::TestEnvironment<()> = + testing_framework::TestEnvironment::boot(&testing_framework::ServicesConfig::none())?; + let mut template_install = std::process::Command::new(spin_binary()); + template_install.args(["templates", "install", "--git", template_url, "--update"]); + env.run_in(&mut template_install)?; + let mut plugin_update = std::process::Command::new(spin_binary()); + plugin_update.args(["plugin", "update"]); + env.run_in(&mut plugin_update)?; + for plugin in plugins { + let mut plugin_install = std::process::Command::new(spin_binary()); + plugin_install.args(["plugin", "install", plugin, "--yes"]); + env.run_in(&mut plugin_install)?; + } + let mut new_app = std::process::Command::new(spin_binary()); + new_app.args([ + "new", + "test", + "-t", + template_name, + "-o", + ".", + "--accept-defaults", + ]); + env.run_in(&mut new_app)?; + let mut build = std::process::Command::new(spin_binary()); + build.args(["build"]); + env.run_in(&mut build)?; + let mut spin = testing_framework::Spin::start( + &spin_binary(), + &env, + vec![] as Vec<&str>, + testing_framework::SpinMode::Http, + )?; + + assert_spin_request( + &mut spin, + testing_framework::Request::new(reqwest::Method::GET, "/"), + 200, + &[], + expected_body, + )?; + + Ok(()) +} + /// Run an e2e test fn run_test( test_name: impl Into, diff --git a/tests/testing-framework/src/spin.rs b/tests/testing-framework/src/spin.rs index 9fa61a4693..be4ecf420f 100644 --- a/tests/testing-framework/src/spin.rs +++ b/tests/testing-framework/src/spin.rs @@ -1,6 +1,6 @@ use anyhow::Context; -use crate::{io::OutputStream, Runtime}; +use crate::{io::OutputStream, Runtime, TestEnvironment}; use std::{ path::Path, process::{Command, Stdio}, @@ -16,29 +16,29 @@ pub struct Spin { } impl Spin { - pub fn start( + pub fn start( spin_binary_path: &Path, - current_dir: &Path, + env: &TestEnvironment, 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), - SpinMode::None => Self::attempt_start(spin_binary_path, current_dir, spin_up_args), + SpinMode::Http => Self::start_http(spin_binary_path, env, spin_up_args), + SpinMode::Redis => Self::start_redis(spin_binary_path, env, spin_up_args), + SpinMode::None => Self::attempt_start(spin_binary_path, env, spin_up_args), } } /// Start Spin in `current_dir` using the binary at `spin_binary_path` - pub fn start_http( + pub fn start_http( spin_binary_path: &Path, - current_dir: &Path, + env: &TestEnvironment, spin_up_args: Vec>, ) -> anyhow::Result { let port = get_random_port()?; let mut child = Command::new(spin_binary_path) .arg("up") - .current_dir(current_dir) + .current_dir(env.path()) .args(["--listen", &format!("127.0.0.1:{port}")]) .args(spin_up_args) .stdout(Stdio::piped()) @@ -86,14 +86,14 @@ impl Spin { ) } - pub fn start_redis( + pub fn start_redis( spin_binary_path: &Path, - current_dir: &Path, + env: &TestEnvironment, spin_up_args: Vec>, ) -> anyhow::Result { let mut child = Command::new(spin_binary_path) .arg("up") - .current_dir(current_dir) + .current_dir(env.path()) .args(spin_up_args) .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -119,14 +119,14 @@ impl Spin { Ok(spin) } - fn attempt_start( + fn attempt_start( spin_binary_path: &Path, - current_dir: &Path, + env: &TestEnvironment, spin_up_args: Vec>, ) -> anyhow::Result { let mut child = Command::new(spin_binary_path) .arg("up") - .current_dir(current_dir) + .current_dir(env.path()) .args(spin_up_args) .stdout(Stdio::piped()) .stderr(Stdio::piped()) diff --git a/tests/testing-framework/src/test_environment.rs b/tests/testing-framework/src/test_environment.rs index 9acf91eab2..5ee59c6053 100644 --- a/tests/testing-framework/src/test_environment.rs +++ b/tests/testing-framework/src/test_environment.rs @@ -18,23 +18,11 @@ pub struct TestEnvironment { } impl TestEnvironment { - /// Spin up a test environment + /// Spin up a test environment with a runtime pub fn up(config: TestEnvironmentConfig) -> anyhow::Result { - let temp = temp_dir::TempDir::new() - .context("failed to produce a temporary directory to run the test in")?; - log::trace!("Temporary directory: {}", temp.path().display()); - let mut services = - Services::start(&config.services_config).context("failed to start services")?; - services.healthy().context("services have failed")?; - let mut env = Self { - temp, - services, - runtime: None, - }; + let mut env = Self::boot(&config.services_config)?; let runtime = (config.create_runtime)(&mut env)?; - env.runtime = Some(runtime); - env.error().context("services have failed")?; - Ok(env) + env.start_runtime(runtime) } /// Whether an error has occurred @@ -48,6 +36,31 @@ impl TestEnvironment { } impl TestEnvironment { + /// Spin up a test environment without a runtime + pub fn boot(services: &ServicesConfig) -> anyhow::Result { + let temp = temp_dir::TempDir::new() + .context("failed to produce a temporary directory to run the test in")?; + log::trace!("Temporary directory: {}", temp.path().display()); + let mut services = Services::start(services).context("failed to start services")?; + services.healthy().context("services have failed")?; + Ok(Self { + temp, + services, + runtime: None, + }) + } + + /// Start the runtime + pub fn start_runtime(self, runtime: N) -> anyhow::Result> { + let mut this = TestEnvironment { + temp: self.temp, + services: self.services, + runtime: Some(runtime), + }; + this.error().context("testing environment is not healthy")?; + Ok(this) + } + /// Get the services that are running for the test pub fn services_mut(&mut self) -> &mut Services { &mut self.services @@ -115,6 +128,22 @@ impl TestEnvironment { std::fs::read(self.temp.path().join(path)) } + /// Run a command in the test environment + /// + /// This blocks until the command has finished running and will error if the command fails + pub fn run_in(&self, cmd: &mut std::process::Command) -> anyhow::Result { + let output = cmd.current_dir(self.temp.path()).output()?; + if !output.status.success() { + anyhow::bail!( + "'{cmd:?}' failed with status code {:?}\nstdout:\n{}\nstderr:\n{}\n", + output.status.code(), + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + } + Ok(output) + } + /// Get the path to test environment pub(crate) fn path(&self) -> &Path { self.temp.path() @@ -148,7 +177,7 @@ impl TestEnvironmentConfig { services_config, create_runtime: Box::new(move |env| { preboot(env)?; - Spin::start(&spin_binary, env.path(), spin_up_args, mode) + Spin::start(&spin_binary, env, spin_up_args, mode) }), } } From 701f743f8943bd19eb4e6787d2ad86dfe83506b4 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Jan 2024 11:01:50 +0100 Subject: [PATCH 08/15] Add Rust template test Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 113 ++++++++++-------- .../testing-framework/src/test_environment.rs | 8 +- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index 1045dc1328..6d1685e33e 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -299,62 +299,15 @@ fn outbound_http_works() -> anyhow::Result<()> { fn http_python_template_smoke_test() -> anyhow::Result<()> { smoke_test_template( "http-py", - "https://github.com/fermyon/spin-python-sdk", + Some("https://github.com/fermyon/spin-python-sdk"), &["py2wasm"], "Hello from the Python SDK", ) } -/// Run a smoke test against a `spin new` template -fn smoke_test_template( - template_name: &str, - template_url: &str, - plugins: &[&str], - expected_body: &str, -) -> anyhow::Result<()> { - let env: testing_framework::TestEnvironment<()> = - testing_framework::TestEnvironment::boot(&testing_framework::ServicesConfig::none())?; - let mut template_install = std::process::Command::new(spin_binary()); - template_install.args(["templates", "install", "--git", template_url, "--update"]); - env.run_in(&mut template_install)?; - let mut plugin_update = std::process::Command::new(spin_binary()); - plugin_update.args(["plugin", "update"]); - env.run_in(&mut plugin_update)?; - for plugin in plugins { - let mut plugin_install = std::process::Command::new(spin_binary()); - plugin_install.args(["plugin", "install", plugin, "--yes"]); - env.run_in(&mut plugin_install)?; - } - let mut new_app = std::process::Command::new(spin_binary()); - new_app.args([ - "new", - "test", - "-t", - template_name, - "-o", - ".", - "--accept-defaults", - ]); - env.run_in(&mut new_app)?; - let mut build = std::process::Command::new(spin_binary()); - build.args(["build"]); - env.run_in(&mut build)?; - let mut spin = testing_framework::Spin::start( - &spin_binary(), - &env, - vec![] as Vec<&str>, - testing_framework::SpinMode::Http, - )?; - - assert_spin_request( - &mut spin, - testing_framework::Request::new(reqwest::Method::GET, "/"), - 200, - &[], - expected_body, - )?; - - Ok(()) +#[test] +fn http_rust_template_smoke_test() -> anyhow::Result<()> { + smoke_test_template("http-rust", None, &[], "Hello, Fermyon") } /// Run an e2e test @@ -443,6 +396,64 @@ fn preboot( Ok(()) } +/// Run a smoke test against a `spin new` template +fn smoke_test_template( + template_name: &str, + template_url: Option<&str>, + plugins: &[&str], + expected_body: &str, +) -> anyhow::Result<()> { + let env: testing_framework::TestEnvironment<()> = + testing_framework::TestEnvironment::boot(&testing_framework::ServicesConfig::none())?; + if let Some(template_url) = template_url { + let mut template_install = std::process::Command::new(spin_binary()); + template_install.args(["templates", "install", "--git", template_url, "--update"]); + env.run_in(&mut template_install)?; + } + + if !plugins.is_empty() { + let mut plugin_update = std::process::Command::new(spin_binary()); + plugin_update.args(["plugin", "update"]); + env.run_in(&mut plugin_update)?; + } + + for plugin in plugins { + let mut plugin_install = std::process::Command::new(spin_binary()); + plugin_install.args(["plugin", "install", plugin, "--yes"]); + env.run_in(&mut plugin_install)?; + } + let mut new_app = std::process::Command::new(spin_binary()); + new_app.args([ + "new", + "test", + "-t", + template_name, + "-o", + ".", + "--accept-defaults", + ]); + env.run_in(&mut new_app)?; + let mut build = std::process::Command::new(spin_binary()); + build.args(["build"]); + env.run_in(&mut build)?; + let mut spin = testing_framework::Spin::start( + &spin_binary(), + &env, + vec![] as Vec<&str>, + testing_framework::SpinMode::Http, + )?; + + assert_spin_request( + &mut spin, + testing_framework::Request::new(reqwest::Method::GET, "/"), + 200, + &[], + expected_body, + )?; + + Ok(()) +} + /// Get the spin binary path fn spin_binary() -> PathBuf { env!("CARGO_BIN_EXE_spin").into() diff --git a/tests/testing-framework/src/test_environment.rs b/tests/testing-framework/src/test_environment.rs index 5ee59c6053..77581cac46 100644 --- a/tests/testing-framework/src/test_environment.rs +++ b/tests/testing-framework/src/test_environment.rs @@ -132,7 +132,13 @@ impl TestEnvironment { /// /// This blocks until the command has finished running and will error if the command fails pub fn run_in(&self, cmd: &mut std::process::Command) -> anyhow::Result { - let output = cmd.current_dir(self.temp.path()).output()?; + let output = cmd + .current_dir(self.temp.path()) + // TODO: figure out how not to hardcode this + // We do this so that if `spin build` is run with a Rust app, + // it builds inside the test environment + .env("CARGO_TARGET_DIR", self.path().join("target")) + .output()?; if !output.status.success() { anyhow::bail!( "'{cmd:?}' failed with status code {:?}\nstdout:\n{}\nstderr:\n{}\n", From f4e23a4db83cbbf83916ccb65854fa74c65e4f2c Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Jan 2024 11:33:34 +0100 Subject: [PATCH 09/15] Move all other http smoke tests over Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 158 +++++++++++------- tests/testcases/mod.rs | 360 ----------------------------------------- 2 files changed, 104 insertions(+), 414 deletions(-) diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index 6d1685e33e..eada8125df 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -301,13 +301,92 @@ fn http_python_template_smoke_test() -> anyhow::Result<()> { "http-py", Some("https://github.com/fermyon/spin-python-sdk"), &["py2wasm"], + |_| Ok(()), "Hello from the Python SDK", ) } #[test] fn http_rust_template_smoke_test() -> anyhow::Result<()> { - smoke_test_template("http-rust", None, &[], "Hello, Fermyon") + smoke_test_template("http-rust", None, &[], |_| Ok(()), "Hello, Fermyon") +} + +#[test] +fn http_c_template_smoke_test() -> anyhow::Result<()> { + smoke_test_template("http-c", None, &[], |_| Ok(()), "Hello from WAGI/1\n") +} + +#[test] +fn http_go_template_smoke_test() -> anyhow::Result<()> { + let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { + let mut tidy = std::process::Command::new("go"); + tidy.args(["mod", "tidy"]); + env.run_in(&mut tidy)?; + Ok(()) + }; + smoke_test_template("http-go", None, &[], prebuild, "Hello Fermyon!\n") +} + +#[test] +fn http_js_template_smoke_test() -> anyhow::Result<()> { + let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { + let mut tidy = std::process::Command::new("npm"); + tidy.args(["install"]); + env.run_in(&mut tidy)?; + Ok(()) + }; + smoke_test_template( + "http-js", + Some("https://github.com/fermyon/spin-js-sdk"), + &["js2wasm"], + prebuild, + "Hello from JS-SDK", + ) +} + +#[test] +fn http_ts_template_smoke_test() -> anyhow::Result<()> { + let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { + let mut tidy = std::process::Command::new("npm"); + tidy.args(["install"]); + env.run_in(&mut tidy)?; + Ok(()) + }; + smoke_test_template( + "http-ts", + Some("https://github.com/fermyon/spin-js-sdk"), + &["js2wasm"], + prebuild, + "Hello from TS-SDK", + ) +} + +#[test] +#[cfg(target_arch = "x86_64")] +fn http_grain_template_smoke_test() -> anyhow::Result<()> { + smoke_test_template("http-grain", None, &[], |_| Ok(()), "Hello, World\n") +} + +#[test] +fn http_zig_template_smoke_test() -> anyhow::Result<()> { + smoke_test_template("http-zig", None, &[], |_| Ok(()), "Hello World!\n") +} + +#[test] +fn http_swift_template_smoke_test() -> anyhow::Result<()> { + smoke_test_template("http-swift", None, &[], |_| Ok(()), "Hello from WAGI/1!\n") +} + +#[test] +fn http_php_template_smoke_test() -> anyhow::Result<()> { + smoke_test_template_with_route( + "http-php", + None, + &[], + |_| Ok(()), + "/index.php", + "Hello Fermyon Spin", + ) } /// Run an e2e test @@ -401,9 +480,29 @@ fn smoke_test_template( template_name: &str, template_url: Option<&str>, plugins: &[&str], + prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, expected_body: &str, ) -> anyhow::Result<()> { - let env: testing_framework::TestEnvironment<()> = + smoke_test_template_with_route( + template_name, + template_url, + plugins, + prebuild_hook, + "/", + expected_body, + ) +} + +/// Run a smoke test against a given http route for a `spin new` template +fn smoke_test_template_with_route( + template_name: &str, + template_url: Option<&str>, + plugins: &[&str], + prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, + route: &str, + expected_body: &str, +) -> anyhow::Result<()> { + let mut env: testing_framework::TestEnvironment<()> = testing_framework::TestEnvironment::boot(&testing_framework::ServicesConfig::none())?; if let Some(template_url) = template_url { let mut template_install = std::process::Command::new(spin_binary()); @@ -433,6 +532,8 @@ fn smoke_test_template( "--accept-defaults", ]); env.run_in(&mut new_app)?; + + prebuild_hook(&mut env)?; let mut build = std::process::Command::new(spin_binary()); build.args(["build"]); env.run_in(&mut build)?; @@ -445,7 +546,7 @@ fn smoke_test_template( assert_spin_request( &mut spin, - testing_framework::Request::new(reqwest::Method::GET, "/"), + testing_framework::Request::new(reqwest::Method::GET, route), 200, &[], expected_body, @@ -465,57 +566,6 @@ mod spinup_tests { use {e2e_testing::controller::Controller, e2e_testing::spin_controller::SpinUp}; const CONTROLLER: &dyn Controller = &SpinUp {}; - #[tokio::test] - async fn http_go_works() { - testcases::http_go_works(CONTROLLER).await - } - - #[tokio::test] - async fn http_c_works() { - testcases::http_c_works(CONTROLLER).await - } - - #[tokio::test] - async fn http_rust_works() { - testcases::http_rust_works(CONTROLLER).await - } - - #[tokio::test] - async fn http_zig_works() { - testcases::http_zig_works(CONTROLLER).await - } - - #[tokio::test] - #[cfg(target_arch = "x86_64")] - async fn http_grain_works() { - testcases::http_grain_works(CONTROLLER).await - } - - #[tokio::test] - async fn http_ts_works() { - testcases::http_ts_works(CONTROLLER).await - } - - #[tokio::test] - async fn http_js_works() { - testcases::http_js_works(CONTROLLER).await - } - - #[tokio::test] - async fn http_python_works() { - testcases::http_python_works(CONTROLLER).await - } - - #[tokio::test] - async fn http_php_works() { - testcases::http_php_works(CONTROLLER).await - } - - #[tokio::test] - async fn http_swift_works() { - testcases::http_swift_works(CONTROLLER).await - } - #[tokio::test] async fn redis_go_works() { testcases::redis_go_works(CONTROLLER).await diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index bd2f6f45b7..b62ceeef1c 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -10,366 +10,6 @@ use std::time::Duration; use tokio::io::AsyncBufRead; use tokio::time::sleep; -fn get_url(base: &str, path: &str) -> String { - format!("{}{}", base, path) -} - -pub async fn http_python_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello from the Python SDK"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-py-template".to_string()) - .template(Some("http-py".to_string())) - .template_install_args(Some(vec![ - "--git".to_string(), - "https://github.com/fermyon/spin-python-sdk".to_string(), - "--update".to_string(), - ])) - .plugins(Some(vec!["py2wasm".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 http_php_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - get_url(metadata.base.as_str(), "/index.php").as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello Fermyon Spin"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-php-template".to_string()) - .template(Some("http-php".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 http_swift_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello from WAGI/1!\n"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-swift-template".to_string()) - .template(Some("http-swift".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 http_go_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello Fermyon!\n"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-go-template".to_string()) - .template(Some("http-go".to_string())) - .pre_build_hooks(Some(vec![vec![ - "go".to_string(), - "mod".to_string(), - "tidy".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 http_c_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello from WAGI/1\n"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-c-template".to_string()) - .template(Some("http-c".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 http_rust_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello, Fermyon"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-rust-template".to_string()) - .template(Some("http-rust".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 http_zig_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello World!\n"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-zig-template".to_string()) - .template(Some("http-zig".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() -} - -#[allow(unused)] -pub async fn http_grain_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello, World\n"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-grain-template".to_string()) - .template(Some("http-grain".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 http_ts_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello from TS-SDK"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-ts-template".to_string()) - .template(Some("http-ts".to_string())) - .template_install_args(Some(vec![ - "--git".to_string(), - "https://github.com/fermyon/spin-js-sdk".to_string(), - "--update".to_string(), - ])) - .plugins(Some(vec!["js2wasm".to_string()])) - .pre_build_hooks(Some(vec![vec!["npm".to_string(), "install".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 http_js_works(controller: &dyn Controller) { - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello from JS-SDK"), - ) - .await - } - - let tc = TestCaseBuilder::default() - .name("http-js-template".to_string()) - .template(Some("http-js".to_string())) - .template_install_args(Some(vec![ - "--git".to_string(), - "https://github.com/fermyon/spin-js-sdk".to_string(), - "--update".to_string(), - ])) - .plugins(Some(vec!["js2wasm".to_string()])) - .pre_build_hooks(Some(vec![vec!["npm".to_string(), "install".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 redis_go_works(controller: &dyn Controller) { async fn checks( _: AppMetadata, From 4a6b3ca7534ad01678e9e2da6987b135084f2653 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Jan 2024 12:22:36 +0100 Subject: [PATCH 10/15] Redis templates smoke tests Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 206 +++++++++++++++++++++++++++++------------ tests/testcases/mod.rs | 128 ------------------------- 2 files changed, 148 insertions(+), 186 deletions(-) diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index eada8125df..304f935308 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -1,3 +1,5 @@ +use anyhow::Context; +use redis::Commands; use reqwest::header::HeaderValue; use std::path::PathBuf; @@ -297,7 +299,7 @@ fn outbound_http_works() -> anyhow::Result<()> { #[test] fn http_python_template_smoke_test() -> anyhow::Result<()> { - smoke_test_template( + http_smoke_test_template( "http-py", Some("https://github.com/fermyon/spin-python-sdk"), &["py2wasm"], @@ -308,12 +310,12 @@ fn http_python_template_smoke_test() -> anyhow::Result<()> { #[test] fn http_rust_template_smoke_test() -> anyhow::Result<()> { - smoke_test_template("http-rust", None, &[], |_| Ok(()), "Hello, Fermyon") + http_smoke_test_template("http-rust", None, &[], |_| Ok(()), "Hello, Fermyon") } #[test] fn http_c_template_smoke_test() -> anyhow::Result<()> { - smoke_test_template("http-c", None, &[], |_| Ok(()), "Hello from WAGI/1\n") + http_smoke_test_template("http-c", None, &[], |_| Ok(()), "Hello from WAGI/1\n") } #[test] @@ -324,7 +326,7 @@ fn http_go_template_smoke_test() -> anyhow::Result<()> { env.run_in(&mut tidy)?; Ok(()) }; - smoke_test_template("http-go", None, &[], prebuild, "Hello Fermyon!\n") + http_smoke_test_template("http-go", None, &[], prebuild, "Hello Fermyon!\n") } #[test] @@ -335,7 +337,7 @@ fn http_js_template_smoke_test() -> anyhow::Result<()> { env.run_in(&mut tidy)?; Ok(()) }; - smoke_test_template( + http_smoke_test_template( "http-js", Some("https://github.com/fermyon/spin-js-sdk"), &["js2wasm"], @@ -352,7 +354,7 @@ fn http_ts_template_smoke_test() -> anyhow::Result<()> { env.run_in(&mut tidy)?; Ok(()) }; - smoke_test_template( + http_smoke_test_template( "http-ts", Some("https://github.com/fermyon/spin-js-sdk"), &["js2wasm"], @@ -364,22 +366,22 @@ fn http_ts_template_smoke_test() -> anyhow::Result<()> { #[test] #[cfg(target_arch = "x86_64")] fn http_grain_template_smoke_test() -> anyhow::Result<()> { - smoke_test_template("http-grain", None, &[], |_| Ok(()), "Hello, World\n") + http_smoke_test_template("http-grain", None, &[], |_| Ok(()), "Hello, World\n") } #[test] fn http_zig_template_smoke_test() -> anyhow::Result<()> { - smoke_test_template("http-zig", None, &[], |_| Ok(()), "Hello World!\n") + http_smoke_test_template("http-zig", None, &[], |_| Ok(()), "Hello World!\n") } #[test] fn http_swift_template_smoke_test() -> anyhow::Result<()> { - smoke_test_template("http-swift", None, &[], |_| Ok(()), "Hello from WAGI/1!\n") + http_smoke_test_template("http-swift", None, &[], |_| Ok(()), "Hello from WAGI/1!\n") } #[test] fn http_php_template_smoke_test() -> anyhow::Result<()> { - smoke_test_template_with_route( + http_smoke_test_template_with_route( "http-php", None, &[], @@ -389,6 +391,42 @@ fn http_php_template_smoke_test() -> anyhow::Result<()> { ) } +#[test] +fn redis_go_template_smoke_test() -> anyhow::Result<()> { + redis_smoke_test_template( + "redis-go", + None, + &[], + |port| { + vec![ + "--value".into(), + "redis-channel=redis-channel".into(), + "--value".into(), + format!("redis-address=redis://localhost:{port}"), + ] + }, + |_| Ok(()), + ) +} + +#[test] +fn redis_rust_template_smoke_test() -> anyhow::Result<()> { + redis_smoke_test_template( + "redis-rust", + None, + &[], + |port| { + vec![ + "--value".into(), + "redis-channel=redis-channel".into(), + "--value".into(), + format!("redis-address=redis://localhost:{port}"), + ] + }, + |_| Ok(()), + ) +} + /// Run an e2e test fn run_test( test_name: impl Into, @@ -475,15 +513,15 @@ fn preboot( Ok(()) } -/// Run a smoke test against a `spin new` template -fn smoke_test_template( +/// Run a smoke test against a `spin new` http template +fn http_smoke_test_template( template_name: &str, template_url: Option<&str>, plugins: &[&str], prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, expected_body: &str, ) -> anyhow::Result<()> { - smoke_test_template_with_route( + http_smoke_test_template_with_route( template_name, template_url, plugins, @@ -493,8 +531,8 @@ fn smoke_test_template( ) } -/// Run a smoke test against a given http route for a `spin new` template -fn smoke_test_template_with_route( +/// Run a smoke test against a given http route for a `spin new` http template +fn http_smoke_test_template_with_route( template_name: &str, template_url: Option<&str>, plugins: &[&str], @@ -502,57 +540,120 @@ fn smoke_test_template_with_route( route: &str, expected_body: &str, ) -> anyhow::Result<()> { + let mut env = bootstrap_smoke_test( + &testing_framework::ServicesConfig::none(), + template_url, + plugins, + template_name, + |_| Ok(Vec::new()), + prebuild_hook, + testing_framework::SpinMode::Http, + )?; + + assert_spin_request( + env.runtime_mut(), + testing_framework::Request::new(reqwest::Method::GET, route), + 200, + &[], + expected_body, + )?; + + Ok(()) +} + +/// Run a smoke test for a `spin new` redis template +fn redis_smoke_test_template( + template_name: &str, + template_url: Option<&str>, + plugins: &[&str], + new_app_args: impl FnOnce(u16) -> Vec, + prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, +) -> anyhow::Result<()> { + let mut env = bootstrap_smoke_test( + &testing_framework::ServicesConfig::new(vec!["redis".into()])?, + template_url, + plugins, + template_name, + |env| { + let redis_port = env + .services_mut() + .get_port(6379)? + .context("no redis port was exposed by test services")?; + Ok(new_app_args(redis_port)) + }, + prebuild_hook, + testing_framework::SpinMode::Redis, + )?; + let redis_port = env + .get_port(6379)? + .context("no redis port was exposed by test services")?; + let mut client = redis::Client::open(format!("redis://localhost:{redis_port}"))?; + let mut conn = client.get_connection()?; + let mut pubsub = conn.as_pubsub(); + pubsub.subscribe("redis-channel")?; + client.publish("redis-channel", "hello from redis")?; + + // Wait for the message to be received (as an approximation for when Spin receives the message) + let _ = pubsub.get_message()?; + // Leave some time for the message to be processed + std::thread::sleep(std::time::Duration::from_millis(100)); + + let stderr = env.runtime_mut().stderr(); + assert!(stderr.contains("hello from redis")); + + Ok(()) +} + +/// Bootstrap a test environment for a smoke test +fn bootstrap_smoke_test( + services: &testing_framework::ServicesConfig, + template_url: Option<&str>, + plugins: &[&str], + template_name: &str, + new_app_args: impl FnOnce( + &mut testing_framework::TestEnvironment<()>, + ) -> anyhow::Result>, + prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> Result<(), anyhow::Error>, + spin_mode: testing_framework::SpinMode, +) -> Result, anyhow::Error> { let mut env: testing_framework::TestEnvironment<()> = - testing_framework::TestEnvironment::boot(&testing_framework::ServicesConfig::none())?; + testing_framework::TestEnvironment::boot(services)?; if let Some(template_url) = template_url { let mut template_install = std::process::Command::new(spin_binary()); template_install.args(["templates", "install", "--git", template_url, "--update"]); env.run_in(&mut template_install)?; } - if !plugins.is_empty() { let mut plugin_update = std::process::Command::new(spin_binary()); plugin_update.args(["plugin", "update"]); env.run_in(&mut plugin_update)?; } - for plugin in plugins { let mut plugin_install = std::process::Command::new(spin_binary()); plugin_install.args(["plugin", "install", plugin, "--yes"]); env.run_in(&mut plugin_install)?; } let mut new_app = std::process::Command::new(spin_binary()); - new_app.args([ - "new", - "test", - "-t", - template_name, - "-o", - ".", - "--accept-defaults", - ]); + new_app + .args([ + "new", + "test", + "-t", + template_name, + "-o", + ".", + "--accept-defaults", + ]) + .args(new_app_args(&mut env)?); env.run_in(&mut new_app)?; - prebuild_hook(&mut env)?; let mut build = std::process::Command::new(spin_binary()); build.args(["build"]); env.run_in(&mut build)?; - let mut spin = testing_framework::Spin::start( - &spin_binary(), - &env, - vec![] as Vec<&str>, - testing_framework::SpinMode::Http, - )?; - - assert_spin_request( - &mut spin, - testing_framework::Request::new(reqwest::Method::GET, route), - 200, - &[], - expected_body, - )?; - - Ok(()) + let spin = + testing_framework::Spin::start(&spin_binary(), &env, vec![] as Vec<&str>, spin_mode)?; + let env = env.start_runtime(spin)?; + Ok(env) } /// Get the spin binary path @@ -562,22 +663,11 @@ fn spin_binary() -> PathBuf { #[cfg(feature = "e2e-tests")] mod spinup_tests { - use super::testcases; - use {e2e_testing::controller::Controller, e2e_testing::spin_controller::SpinUp}; - const CONTROLLER: &dyn Controller = &SpinUp {}; - - #[tokio::test] - async fn redis_go_works() { - testcases::redis_go_works(CONTROLLER).await - } - - #[tokio::test] - async fn redis_rust_works() { - testcases::redis_rust_works(CONTROLLER).await - } - #[tokio::test] async fn registry_works() { + use super::testcases; + use {e2e_testing::controller::Controller, e2e_testing::spin_controller::SpinUp}; + const CONTROLLER: &dyn Controller = &SpinUp {}; testcases::registry_works(CONTROLLER).await } } diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index b62ceeef1c..ab42c44da7 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -10,123 +10,6 @@ use std::time::Duration; use tokio::io::AsyncBufRead; use tokio::time::sleep; -pub async fn redis_go_works(controller: &dyn Controller) { - async fn checks( - _: AppMetadata, - _: Option>>, - stderr_stream: Option>>, - ) -> Result<()> { - wait_for_spin().await; - - let output = utils::run( - &[ - "redis-cli", - "-u", - "redis://redis:6379", - "PUBLISH", - "redis-go-works-channel", - "msg-from-go-channel", - ], - None, - None, - )?; - utils::assert_success(&output); - - let stderr = get_output_stream(stderr_stream).await?; - let expected_logs = vec!["Payload::::", "msg-from-go-channel"]; - - assert!(expected_logs - .iter() - .all(|item| stderr.contains(&item.to_string())), - "Expected log lines to contain all of {expected_logs:?} but actual lines were '{stderr:?}'"); - - Ok(()) - } - - let tc = TestCaseBuilder::default() - .name("redis-go".to_string()) - .template(Some("redis-go".to_string())) - .new_app_args(vec![ - "--value".to_string(), - "redis-channel=redis-go-works-channel".to_string(), - "--value".to_string(), - "redis-address=redis://redis:6379".to_string(), - ]) - .trigger_type("redis".to_string()) - .pre_build_hooks(Some(vec![vec![ - "go".to_string(), - "mod".to_string(), - "tidy".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 redis_rust_works(controller: &dyn Controller) { - async fn checks( - _: AppMetadata, - _: Option>>, - stderr_stream: Option>>, - ) -> Result<()> { - wait_for_spin().await; - - utils::run( - &[ - "redis-cli", - "-u", - "redis://redis:6379", - "PUBLISH", - "redis-rust-works-channel", - "msg-from-rust-channel", - ], - None, - None, - )?; - - let stderr = get_output_stream(stderr_stream).await?; - - let expected_logs = vec!["msg-from-rust-channel"]; - - assert!(expected_logs - .iter() - .all(|item| stderr.contains(&item.to_string())), - "Expected log lines to contain all of {expected_logs:?} but actual lines were '{stderr:?}'"); - - Ok(()) - } - - let tc = TestCaseBuilder::default() - .name("redis-rust".to_string()) - .template(Some("redis-rust".to_string())) - .new_app_args(vec![ - "--value".to_string(), - "redis-channel=redis-rust-works-channel".to_string(), - "--value".to_string(), - "redis-address=redis://redis:6379".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 registry_works(controller: &dyn Controller) { async fn checks( metadata: AppMetadata, @@ -176,14 +59,3 @@ pub async fn registry_works(controller: &dyn Controller) { tc.run(controller).await.unwrap() } - -async fn get_output_stream( - stream: Option>>, -) -> anyhow::Result> { - utils::get_output_stream(stream, Duration::from_secs(5)).await -} - -async fn wait_for_spin() { - //TODO: wait for spin up to be ready dynamically - sleep(Duration::from_secs(10)).await; -} From 25d9f1c62b4c0f366b92f8118e9bcec54957c8c8 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Jan 2024 12:43:56 +0100 Subject: [PATCH 11/15] Rearrange Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 261 +++-------------------------------------- tests/testcases/mod.rs | 256 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 263 insertions(+), 254 deletions(-) diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index 304f935308..2d0484698d 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -1,9 +1,8 @@ -use anyhow::Context; -use redis::Commands; -use reqwest::header::HeaderValue; -use std::path::PathBuf; +use testcases::{ + assert_spin_request, bootstap_env, http_smoke_test_template, + http_smoke_test_template_with_route, redis_smoke_test_template, run_test, +}; -#[cfg(feature = "e2e-tests")] mod testcases; #[test] @@ -298,6 +297,12 @@ fn outbound_http_works() -> anyhow::Result<()> { } #[test] +fn http_rust_template_smoke_test() -> anyhow::Result<()> { + http_smoke_test_template("http-rust", None, &[], |_| Ok(()), "Hello, Fermyon") +} + +#[test] +#[cfg(feature = "e2e-tests")] fn http_python_template_smoke_test() -> anyhow::Result<()> { http_smoke_test_template( "http-py", @@ -309,16 +314,13 @@ fn http_python_template_smoke_test() -> anyhow::Result<()> { } #[test] -fn http_rust_template_smoke_test() -> anyhow::Result<()> { - http_smoke_test_template("http-rust", None, &[], |_| Ok(()), "Hello, Fermyon") -} - -#[test] +#[cfg(feature = "e2e-tests")] fn http_c_template_smoke_test() -> anyhow::Result<()> { http_smoke_test_template("http-c", None, &[], |_| Ok(()), "Hello from WAGI/1\n") } #[test] +#[cfg(feature = "e2e-tests")] fn http_go_template_smoke_test() -> anyhow::Result<()> { let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { let mut tidy = std::process::Command::new("go"); @@ -330,6 +332,7 @@ fn http_go_template_smoke_test() -> anyhow::Result<()> { } #[test] +#[cfg(feature = "e2e-tests")] fn http_js_template_smoke_test() -> anyhow::Result<()> { let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { let mut tidy = std::process::Command::new("npm"); @@ -347,6 +350,7 @@ fn http_js_template_smoke_test() -> anyhow::Result<()> { } #[test] +#[cfg(feature = "e2e-tests")] fn http_ts_template_smoke_test() -> anyhow::Result<()> { let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { let mut tidy = std::process::Command::new("npm"); @@ -365,16 +369,19 @@ fn http_ts_template_smoke_test() -> anyhow::Result<()> { #[test] #[cfg(target_arch = "x86_64")] +#[cfg(feature = "e2e-tests")] fn http_grain_template_smoke_test() -> anyhow::Result<()> { http_smoke_test_template("http-grain", None, &[], |_| Ok(()), "Hello, World\n") } #[test] +#[cfg(feature = "e2e-tests")] fn http_zig_template_smoke_test() -> anyhow::Result<()> { http_smoke_test_template("http-zig", None, &[], |_| Ok(()), "Hello World!\n") } #[test] +#[cfg(feature = "e2e-tests")] fn http_swift_template_smoke_test() -> anyhow::Result<()> { http_smoke_test_template("http-swift", None, &[], |_| Ok(()), "Hello from WAGI/1!\n") } @@ -427,240 +434,6 @@ fn redis_rust_template_smoke_test() -> anyhow::Result<()> { ) } -/// Run an e2e test -fn run_test( - test_name: impl Into, - mode: testing_framework::SpinMode, - spin_up_args: impl IntoIterator, - services_config: testing_framework::ServicesConfig, - test: impl FnOnce( - &mut testing_framework::TestEnvironment, - ) -> testing_framework::TestResult - + 'static, -) -> testing_framework::TestResult { - let mut env = bootstap_env(test_name, spin_up_args, services_config, mode)?; - test(&mut env)?; - Ok(()) -} - -/// Bootstrap a test environment -fn bootstap_env( - test_name: impl Into, - spin_up_args: impl IntoIterator, - services_config: testing_framework::ServicesConfig, - mode: testing_framework::SpinMode, -) -> anyhow::Result> { - 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, - ); - testing_framework::TestEnvironment::up(config) -} - -/// Assert that a request to the spin server returns the expected status and body -fn assert_spin_request( - spin: &mut testing_framework::Spin, - request: testing_framework::Request<'_>, - expected_status: u16, - expected_headers: &[(&str, &str)], - expected_body: &str, -) -> testing_framework::TestResult { - let uri = request.uri; - let mut r = spin.make_http_request(request)?; - let status = r.status(); - let headers = std::mem::take(r.headers_mut()); - let body = r.text().unwrap_or_else(|_| String::from("")); - if status != expected_status { - return Err(testing_framework::TestError::Failure(anyhow::anyhow!( - "Expected status {expected_status} for {uri} but got {status}\nBody:\n{body}", - ))); - } - let wrong_headers: std::collections::HashMap<_, _> = expected_headers - .iter() - .copied() - .filter(|(ek, ev)| headers.get(*ek) != Some(&HeaderValue::from_str(ev).unwrap())) - .collect(); - if !wrong_headers.is_empty() { - return Err(testing_framework::TestError::Failure(anyhow::anyhow!( - "Expected headers {headers:?} to contain {wrong_headers:?}\nBody:\n{body}" - ))); - } - if body != expected_body { - return Err(testing_framework::TestError::Failure( - anyhow::anyhow!("expected body '{expected_body}', got '{body}'").into(), - )); - } - Ok(()) -} - -/// Get the test environment ready to run a test -fn preboot( - test: &str, - env: &mut testing_framework::TestEnvironment, -) -> anyhow::Result<()> { - // Copy everything into the test environment - env.copy_into(format!("tests/testcases/{test}"), "")?; - - // Copy the manifest with all templates substituted - let manifest_path = PathBuf::from(format!("tests/testcases/{test}/spin.toml")); - let mut template = testing_framework::ManifestTemplate::from_file(manifest_path)?; - template.substitute(env)?; - env.write_file("spin.toml", template.contents())?; - Ok(()) -} - -/// Run a smoke test against a `spin new` http template -fn http_smoke_test_template( - template_name: &str, - template_url: Option<&str>, - plugins: &[&str], - prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, - expected_body: &str, -) -> anyhow::Result<()> { - http_smoke_test_template_with_route( - template_name, - template_url, - plugins, - prebuild_hook, - "/", - expected_body, - ) -} - -/// Run a smoke test against a given http route for a `spin new` http template -fn http_smoke_test_template_with_route( - template_name: &str, - template_url: Option<&str>, - plugins: &[&str], - prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, - route: &str, - expected_body: &str, -) -> anyhow::Result<()> { - let mut env = bootstrap_smoke_test( - &testing_framework::ServicesConfig::none(), - template_url, - plugins, - template_name, - |_| Ok(Vec::new()), - prebuild_hook, - testing_framework::SpinMode::Http, - )?; - - assert_spin_request( - env.runtime_mut(), - testing_framework::Request::new(reqwest::Method::GET, route), - 200, - &[], - expected_body, - )?; - - Ok(()) -} - -/// Run a smoke test for a `spin new` redis template -fn redis_smoke_test_template( - template_name: &str, - template_url: Option<&str>, - plugins: &[&str], - new_app_args: impl FnOnce(u16) -> Vec, - prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, -) -> anyhow::Result<()> { - let mut env = bootstrap_smoke_test( - &testing_framework::ServicesConfig::new(vec!["redis".into()])?, - template_url, - plugins, - template_name, - |env| { - let redis_port = env - .services_mut() - .get_port(6379)? - .context("no redis port was exposed by test services")?; - Ok(new_app_args(redis_port)) - }, - prebuild_hook, - testing_framework::SpinMode::Redis, - )?; - let redis_port = env - .get_port(6379)? - .context("no redis port was exposed by test services")?; - let mut client = redis::Client::open(format!("redis://localhost:{redis_port}"))?; - let mut conn = client.get_connection()?; - let mut pubsub = conn.as_pubsub(); - pubsub.subscribe("redis-channel")?; - client.publish("redis-channel", "hello from redis")?; - - // Wait for the message to be received (as an approximation for when Spin receives the message) - let _ = pubsub.get_message()?; - // Leave some time for the message to be processed - std::thread::sleep(std::time::Duration::from_millis(100)); - - let stderr = env.runtime_mut().stderr(); - assert!(stderr.contains("hello from redis")); - - Ok(()) -} - -/// Bootstrap a test environment for a smoke test -fn bootstrap_smoke_test( - services: &testing_framework::ServicesConfig, - template_url: Option<&str>, - plugins: &[&str], - template_name: &str, - new_app_args: impl FnOnce( - &mut testing_framework::TestEnvironment<()>, - ) -> anyhow::Result>, - prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> Result<(), anyhow::Error>, - spin_mode: testing_framework::SpinMode, -) -> Result, anyhow::Error> { - let mut env: testing_framework::TestEnvironment<()> = - testing_framework::TestEnvironment::boot(services)?; - if let Some(template_url) = template_url { - let mut template_install = std::process::Command::new(spin_binary()); - template_install.args(["templates", "install", "--git", template_url, "--update"]); - env.run_in(&mut template_install)?; - } - if !plugins.is_empty() { - let mut plugin_update = std::process::Command::new(spin_binary()); - plugin_update.args(["plugin", "update"]); - env.run_in(&mut plugin_update)?; - } - for plugin in plugins { - let mut plugin_install = std::process::Command::new(spin_binary()); - plugin_install.args(["plugin", "install", plugin, "--yes"]); - env.run_in(&mut plugin_install)?; - } - let mut new_app = std::process::Command::new(spin_binary()); - new_app - .args([ - "new", - "test", - "-t", - template_name, - "-o", - ".", - "--accept-defaults", - ]) - .args(new_app_args(&mut env)?); - env.run_in(&mut new_app)?; - prebuild_hook(&mut env)?; - let mut build = std::process::Command::new(spin_binary()); - build.args(["build"]); - env.run_in(&mut build)?; - let spin = - testing_framework::Spin::start(&spin_binary(), &env, vec![] as Vec<&str>, spin_mode)?; - let env = env.start_runtime(spin)?; - Ok(env) -} - -/// Get the spin binary path -fn spin_binary() -> PathBuf { - env!("CARGO_BIN_EXE_spin").into() -} - #[cfg(feature = "e2e-tests")] mod spinup_tests { #[tokio::test] diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index ab42c44da7..31a6afcab6 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -1,16 +1,18 @@ +use anyhow::Context; use anyhow::Result; -use e2e_testing::controller::Controller; -use e2e_testing::http_asserts::assert_http_response; -use e2e_testing::metadata::AppMetadata; -use e2e_testing::testcase::TestCaseBuilder; -use e2e_testing::{spin, utils}; -use hyper::Method; -use std::pin::Pin; -use std::time::Duration; -use tokio::io::AsyncBufRead; -use tokio::time::sleep; +use redis::Commands; +use reqwest::header::HeaderValue; +use std::path::PathBuf; +#[cfg(feature = "e2e-tests")] pub async fn registry_works(controller: &dyn Controller) { + use e2e_testing::controller::Controller; + use e2e_testing::http_asserts::assert_http_response; + use e2e_testing::metadata::AppMetadata; + use e2e_testing::testcase::TestCaseBuilder; + use hyper::Method; + use std::pin::Pin; + use tokio::io::AsyncBufRead; async fn checks( metadata: AppMetadata, _: Option>>, @@ -59,3 +61,237 @@ pub async fn registry_works(controller: &dyn Controller) { tc.run(controller).await.unwrap() } + +/// Run an e2e test +pub fn run_test( + test_name: impl Into, + mode: testing_framework::SpinMode, + spin_up_args: impl IntoIterator, + services_config: testing_framework::ServicesConfig, + test: impl FnOnce( + &mut testing_framework::TestEnvironment, + ) -> testing_framework::TestResult + + 'static, +) -> testing_framework::TestResult { + let mut env = bootstap_env(test_name, spin_up_args, services_config, mode)?; + test(&mut env)?; + Ok(()) +} + +/// Bootstrap a test environment +pub fn bootstap_env( + test_name: impl Into, + spin_up_args: impl IntoIterator, + services_config: testing_framework::ServicesConfig, + mode: testing_framework::SpinMode, +) -> anyhow::Result> { + 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, + ); + testing_framework::TestEnvironment::up(config) +} + +/// Assert that a request to the spin server returns the expected status and body +pub fn assert_spin_request( + spin: &mut testing_framework::Spin, + request: testing_framework::Request<'_>, + expected_status: u16, + expected_headers: &[(&str, &str)], + expected_body: &str, +) -> testing_framework::TestResult { + let uri = request.uri; + let mut r = spin.make_http_request(request)?; + let status = r.status(); + let headers = std::mem::take(r.headers_mut()); + let body = r.text().unwrap_or_else(|_| String::from("")); + if status != expected_status { + return Err(testing_framework::TestError::Failure(anyhow::anyhow!( + "Expected status {expected_status} for {uri} but got {status}\nBody:\n{body}", + ))); + } + let wrong_headers: std::collections::HashMap<_, _> = expected_headers + .iter() + .copied() + .filter(|(ek, ev)| headers.get(*ek) != Some(&HeaderValue::from_str(ev).unwrap())) + .collect(); + if !wrong_headers.is_empty() { + return Err(testing_framework::TestError::Failure(anyhow::anyhow!( + "Expected headers {headers:?} to contain {wrong_headers:?}\nBody:\n{body}" + ))); + } + if body != expected_body { + return Err(testing_framework::TestError::Failure( + anyhow::anyhow!("expected body '{expected_body}', got '{body}'").into(), + )); + } + Ok(()) +} + +/// Get the test environment ready to run a test +fn preboot( + test: &str, + env: &mut testing_framework::TestEnvironment, +) -> anyhow::Result<()> { + // Copy everything into the test environment + env.copy_into(format!("tests/testcases/{test}"), "")?; + + // Copy the manifest with all templates substituted + let manifest_path = PathBuf::from(format!("tests/testcases/{test}/spin.toml")); + let mut template = testing_framework::ManifestTemplate::from_file(manifest_path)?; + template.substitute(env)?; + env.write_file("spin.toml", template.contents())?; + Ok(()) +} + +/// Run a smoke test against a `spin new` http template +pub fn http_smoke_test_template( + template_name: &str, + template_url: Option<&str>, + plugins: &[&str], + prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, + expected_body: &str, +) -> anyhow::Result<()> { + http_smoke_test_template_with_route( + template_name, + template_url, + plugins, + prebuild_hook, + "/", + expected_body, + ) +} + +/// Run a smoke test against a given http route for a `spin new` http template +pub fn http_smoke_test_template_with_route( + template_name: &str, + template_url: Option<&str>, + plugins: &[&str], + prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, + route: &str, + expected_body: &str, +) -> anyhow::Result<()> { + let mut env = bootstrap_smoke_test( + &testing_framework::ServicesConfig::none(), + template_url, + plugins, + template_name, + |_| Ok(Vec::new()), + prebuild_hook, + testing_framework::SpinMode::Http, + )?; + + assert_spin_request( + env.runtime_mut(), + testing_framework::Request::new(reqwest::Method::GET, route), + 200, + &[], + expected_body, + )?; + + Ok(()) +} + +/// Run a smoke test for a `spin new` redis template +pub fn redis_smoke_test_template( + template_name: &str, + template_url: Option<&str>, + plugins: &[&str], + new_app_args: impl FnOnce(u16) -> Vec, + prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, +) -> anyhow::Result<()> { + let mut env = bootstrap_smoke_test( + &testing_framework::ServicesConfig::new(vec!["redis".into()])?, + template_url, + plugins, + template_name, + |env| { + let redis_port = env + .services_mut() + .get_port(6379)? + .context("no redis port was exposed by test services")?; + Ok(new_app_args(redis_port)) + }, + prebuild_hook, + testing_framework::SpinMode::Redis, + )?; + let redis_port = env + .get_port(6379)? + .context("no redis port was exposed by test services")?; + let mut client = redis::Client::open(format!("redis://localhost:{redis_port}"))?; + let mut conn = client.get_connection()?; + let mut pubsub = conn.as_pubsub(); + pubsub.subscribe("redis-channel")?; + client.publish("redis-channel", "hello from redis")?; + + // Wait for the message to be received (as an approximation for when Spin receives the message) + let _ = pubsub.get_message()?; + // Leave some time for the message to be processed + std::thread::sleep(std::time::Duration::from_millis(100)); + + let stderr = env.runtime_mut().stderr(); + assert!(stderr.contains("hello from redis")); + + Ok(()) +} + +/// Bootstrap a test environment for a smoke test +fn bootstrap_smoke_test( + services: &testing_framework::ServicesConfig, + template_url: Option<&str>, + plugins: &[&str], + template_name: &str, + new_app_args: impl FnOnce( + &mut testing_framework::TestEnvironment<()>, + ) -> anyhow::Result>, + prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> Result<(), anyhow::Error>, + spin_mode: testing_framework::SpinMode, +) -> Result, anyhow::Error> { + let mut env: testing_framework::TestEnvironment<()> = + testing_framework::TestEnvironment::boot(services)?; + if let Some(template_url) = template_url { + let mut template_install = std::process::Command::new(spin_binary()); + template_install.args(["templates", "install", "--git", template_url, "--update"]); + env.run_in(&mut template_install)?; + } + if !plugins.is_empty() { + let mut plugin_update = std::process::Command::new(spin_binary()); + plugin_update.args(["plugin", "update"]); + env.run_in(&mut plugin_update)?; + } + for plugin in plugins { + let mut plugin_install = std::process::Command::new(spin_binary()); + plugin_install.args(["plugin", "install", plugin, "--yes"]); + env.run_in(&mut plugin_install)?; + } + let mut new_app = std::process::Command::new(spin_binary()); + new_app + .args([ + "new", + "test", + "-t", + template_name, + "-o", + ".", + "--accept-defaults", + ]) + .args(new_app_args(&mut env)?); + env.run_in(&mut new_app)?; + prebuild_hook(&mut env)?; + let mut build = std::process::Command::new(spin_binary()); + build.args(["build"]); + env.run_in(&mut build)?; + let spin = + testing_framework::Spin::start(&spin_binary(), &env, vec![] as Vec<&str>, spin_mode)?; + let env = env.start_runtime(spin)?; + Ok(env) +} + +/// Get the spin binary path +fn spin_binary() -> PathBuf { + env!("CARGO_BIN_EXE_spin").into() +} From acd72af09c116e5d0439981c5f01491f8d5ec8f6 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Jan 2024 14:30:45 +0100 Subject: [PATCH 12/15] Move final e2e test over to new testing framework Signed-off-by: Ryan Levick --- tests/spinup_tests.rs | 48 +++++++++--- tests/testcases/mod.rs | 76 +++---------------- .../services/registry.Dockerfile | 3 + 3 files changed, 52 insertions(+), 75 deletions(-) create mode 100644 tests/testing-framework/services/registry.Dockerfile diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index 2d0484698d..e809174523 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -1,5 +1,6 @@ +use anyhow::Context; use testcases::{ - assert_spin_request, bootstap_env, http_smoke_test_template, + assert_spin_request, bootstap_env, bootstrap_smoke_test, http_smoke_test_template, http_smoke_test_template_with_route, redis_smoke_test_template, run_test, }; @@ -434,13 +435,40 @@ fn redis_rust_template_smoke_test() -> anyhow::Result<()> { ) } -#[cfg(feature = "e2e-tests")] -mod spinup_tests { - #[tokio::test] - async fn registry_works() { - use super::testcases; - use {e2e_testing::controller::Controller, e2e_testing::spin_controller::SpinUp}; - const CONTROLLER: &dyn Controller = &SpinUp {}; - testcases::registry_works(CONTROLLER).await - } +#[test] +fn registry_works() -> anyhow::Result<()> { + let services = testing_framework::ServicesConfig::new(vec!["registry".into()])?; + let spin_up_args = |env: &mut testing_framework::TestEnvironment<()>| { + let registry_url = format!( + "localhost:{}/spin-e2e-tests/registry-works/v1", + env.get_port(5000)? + .context("no registry port was exposed by test services")? + ); + let mut registry_push = std::process::Command::new(testcases::spin_binary()); + registry_push.args(&["registry", "push", ®istry_url, "--insecure"]); + env.run_in(&mut registry_push)?; + Ok(vec![ + "--from-registry".into(), + registry_url, + "--insecure".into(), + ]) + }; + let mut env = bootstrap_smoke_test( + &services, + None, + &[], + "http-rust", + |_| Ok(Vec::new()), + |_| Ok(()), + spin_up_args, + testing_framework::SpinMode::Http, + )?; + assert_spin_request( + env.runtime_mut(), + testing_framework::Request::new(reqwest::Method::GET, "/"), + 200, + &[], + "Hello, Fermyon", + )?; + Ok(()) } diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index 31a6afcab6..3b9a7e89c8 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -1,67 +1,8 @@ use anyhow::Context; -use anyhow::Result; use redis::Commands; use reqwest::header::HeaderValue; use std::path::PathBuf; -#[cfg(feature = "e2e-tests")] -pub async fn registry_works(controller: &dyn Controller) { - use e2e_testing::controller::Controller; - use e2e_testing::http_asserts::assert_http_response; - use e2e_testing::metadata::AppMetadata; - use e2e_testing::testcase::TestCaseBuilder; - use hyper::Method; - use std::pin::Pin; - use tokio::io::AsyncBufRead; - async fn checks( - metadata: AppMetadata, - _: Option>>, - _: Option>>, - ) -> Result<()> { - assert_http_response( - metadata.base.as_str(), - Method::GET, - "", - 200, - &[], - Some("Hello Fermyon!\n"), - ) - .await - } - - let registry = "registry:5000"; - let registry_app_url = format!( - "{}/{}/{}:{}", - registry, "spin-e2e-tests", "registry_works", "v1" - ); - let tc = TestCaseBuilder::default() - .name("http-go".to_string()) - .template(Some("http-go".to_string())) - .appname(Some("http-go-registry-generated".to_string())) - .pre_build_hooks(Some(vec![vec![ - "go".to_string(), - "mod".to_string(), - "tidy".to_string(), - ]])) - .push_to_registry(Some(registry_app_url.clone())) - .deploy_args(vec![ - "--from-registry".to_string(), - registry_app_url.clone(), - "--insecure".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() -} - /// Run an e2e test pub fn run_test( test_name: impl Into, @@ -182,6 +123,7 @@ pub fn http_smoke_test_template_with_route( template_name, |_| Ok(Vec::new()), prebuild_hook, + |_| Ok(Vec::new()), testing_framework::SpinMode::Http, )?; @@ -217,6 +159,7 @@ pub fn redis_smoke_test_template( Ok(new_app_args(redis_port)) }, prebuild_hook, + |_| Ok(Vec::new()), testing_framework::SpinMode::Redis, )?; let redis_port = env @@ -240,7 +183,7 @@ pub fn redis_smoke_test_template( } /// Bootstrap a test environment for a smoke test -fn bootstrap_smoke_test( +pub fn bootstrap_smoke_test( services: &testing_framework::ServicesConfig, template_url: Option<&str>, plugins: &[&str], @@ -248,9 +191,12 @@ fn bootstrap_smoke_test( new_app_args: impl FnOnce( &mut testing_framework::TestEnvironment<()>, ) -> anyhow::Result>, - prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> Result<(), anyhow::Error>, + prebuild_hook: impl FnOnce(&mut testing_framework::TestEnvironment<()>) -> anyhow::Result<()>, + spin_up_args: impl FnOnce( + &mut testing_framework::TestEnvironment<()>, + ) -> anyhow::Result>, spin_mode: testing_framework::SpinMode, -) -> Result, anyhow::Error> { +) -> anyhow::Result> { let mut env: testing_framework::TestEnvironment<()> = testing_framework::TestEnvironment::boot(services)?; if let Some(template_url) = template_url { @@ -285,13 +231,13 @@ fn bootstrap_smoke_test( let mut build = std::process::Command::new(spin_binary()); build.args(["build"]); env.run_in(&mut build)?; - let spin = - testing_framework::Spin::start(&spin_binary(), &env, vec![] as Vec<&str>, spin_mode)?; + let spin_up_args = spin_up_args(&mut env)?; + let spin = testing_framework::Spin::start(&spin_binary(), &env, spin_up_args, spin_mode)?; let env = env.start_runtime(spin)?; Ok(env) } /// Get the spin binary path -fn spin_binary() -> PathBuf { +pub fn spin_binary() -> PathBuf { env!("CARGO_BIN_EXE_spin").into() } diff --git a/tests/testing-framework/services/registry.Dockerfile b/tests/testing-framework/services/registry.Dockerfile new file mode 100644 index 0000000000..3d520ef28e --- /dev/null +++ b/tests/testing-framework/services/registry.Dockerfile @@ -0,0 +1,3 @@ +FROM registry:2 + +ENV REGISTRY_HTTP_SECRET=secret \ No newline at end of file From de651fe4d7c03518ad0f9b735fa5dbf7a87d99d9 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Jan 2024 14:37:03 +0100 Subject: [PATCH 13/15] Delete e2e testing crate now that it is unused Signed-off-by: Ryan Levick --- .dockerignore | 1 - .gitignore | 1 - Cargo.lock | 39 +--- Cargo.toml | 1 - Makefile | 10 +- crates/e2e-testing/Cargo.toml | 21 -- crates/e2e-testing/README.md | 3 - crates/e2e-testing/src/asserts.rs | 78 -------- crates/e2e-testing/src/controller.rs | 85 -------- crates/e2e-testing/src/http_asserts.rs | 120 ----------- crates/e2e-testing/src/lib.rs | 8 - crates/e2e-testing/src/metadata.rs | 28 --- crates/e2e-testing/src/spin.rs | 134 ------------- crates/e2e-testing/src/spin_controller.rs | 113 ----------- crates/e2e-testing/src/testcase.rs | 233 ---------------------- crates/e2e-testing/src/utils.rs | 162 --------------- 16 files changed, 6 insertions(+), 1031 deletions(-) delete mode 100644 crates/e2e-testing/Cargo.toml delete mode 100644 crates/e2e-testing/README.md delete mode 100644 crates/e2e-testing/src/asserts.rs delete mode 100644 crates/e2e-testing/src/controller.rs delete mode 100644 crates/e2e-testing/src/http_asserts.rs delete mode 100644 crates/e2e-testing/src/lib.rs delete mode 100644 crates/e2e-testing/src/metadata.rs delete mode 100644 crates/e2e-testing/src/spin.rs delete mode 100644 crates/e2e-testing/src/spin_controller.rs delete mode 100644 crates/e2e-testing/src/testcase.rs delete mode 100644 crates/e2e-testing/src/utils.rs diff --git a/.dockerignore b/.dockerignore index 8bfe832118..db89137a34 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,4 @@ **/target **/node_modules -tests/testcases/*-generated **/spin-plugin-update.lock **/package-lock.json \ No newline at end of file diff --git a/.gitignore b/.gitignore index 91c618f9ca..f2c430ec0e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ main.wasm .parcel-cache .vscode/*.log crates/**/Cargo.lock -tests/testcases/*-generated spin-plugin-update.lock package-lock.json .spin diff --git a/Cargo.lock b/Cargo.lock index 4577f4f236..fd3edbe620 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1803,23 +1803,6 @@ dependencies = [ "reborrow", ] -[[package]] -name = "e2e-testing" -version = "2.2.0-pre0" -dependencies = [ - "anyhow", - "async-trait", - "derive_builder 0.12.0", - "http 1.0.0", - "hyper-tls 0.6.0", - "nix 0.26.4", - "regex", - "reqwest", - "tempfile", - "tokio", - "url", -] - [[package]] name = "either" version = "1.9.0" @@ -2859,22 +2842,6 @@ dependencies = [ "tokio-native-tls", ] -[[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 1.1.0", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.2" @@ -2890,8 +2857,6 @@ dependencies = [ "pin-project-lite", "socket2 0.5.5", "tokio", - "tower", - "tower-service", "tracing", ] @@ -5015,7 +4980,7 @@ dependencies = [ "http-body 0.4.5", "hyper 0.14.27", "hyper-rustls", - "hyper-tls 0.5.0", + "hyper-tls", "ipnet", "js-sys", "log", @@ -5754,7 +5719,6 @@ dependencies = [ "dialoguer", "dirs 4.0.0", "dunce", - "e2e-testing", "futures", "glob", "hex", @@ -7062,7 +7026,6 @@ dependencies = [ "futures-util", "pin-project", "pin-project-lite", - "tokio", "tower-layer", "tower-service", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 9373b02035..35a880212b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,6 @@ hex = "0.4.3" hyper = { workspace = true } sha2 = "0.10.1" which = "4.2.5" -e2e-testing = { path = "crates/e2e-testing" } http-body-util = { workspace = true } testing-framework = { path = "tests/testing-framework" } hyper-util = { version = "0.1.2", features = ["tokio"] } diff --git a/Makefile b/Makefile index 31eec5c44a..f47215e12e 100644 --- a/Makefile +++ b/Makefile @@ -62,9 +62,9 @@ lint: cargo clippy --all --all-targets --features all-tests -- -D warnings cargo fmt --all -- --check -.PHONY: lint-rust-examples-and-testcases -lint-rust-examples-and-testcases: - for manifest_path in $$(find examples tests/testcases -name Cargo.toml); do \ +.PHONY: lint-rust-examples +lint-rust-examples: + for manifest_path in $$(find examples -name Cargo.toml); do \ echo "Linting $${manifest_path}" \ && cargo clippy --manifest-path "$${manifest_path}" -- -D warnings \ && cargo fmt --manifest-path "$${manifest_path}" -- --check \ @@ -73,14 +73,14 @@ lint-rust-examples-and-testcases: done .PHONY: lint-all -lint-all: lint lint-rust-examples-and-testcases +lint-all: lint lint-rust-examples ## Bring all of the checked in `Cargo.lock` files up-to-date .PHONY: update-cargo-locks update-cargo-locks: echo "Updating Cargo.toml" cargo update -w --offline; \ - for manifest_path in $$(find examples tests/testcases -name Cargo.toml); do \ + for manifest_path in $$(find examples -name Cargo.toml); do \ echo "Updating $${manifest_path}" && \ cargo update --manifest-path "$${manifest_path}" -w --offline; \ done diff --git a/crates/e2e-testing/Cargo.toml b/crates/e2e-testing/Cargo.toml deleted file mode 100644 index eeabd56b52..0000000000 --- a/crates/e2e-testing/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "e2e-testing" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } - -[lib] -doctest = false - -[dependencies] -anyhow = "1.0" -async-trait = "0.1" -tokio = { version = "1.23", features = ["full"] } -regex = "1.5.5" -reqwest = { version = "0.11", features = ["blocking"] } -nix = "0.26.1" -url = "2.2.2" -derive_builder = "0.12.0" -hyper-tls = "0.6.0" -tempfile = "3.3.0" -http = "1.0.0" diff --git a/crates/e2e-testing/README.md b/crates/e2e-testing/README.md deleted file mode 100644 index 831438e323..0000000000 --- a/crates/e2e-testing/README.md +++ /dev/null @@ -1,3 +0,0 @@ -e2e-testing - -This crate provides functions and utilities to help with running e2e-tests for spin. \ No newline at end of file diff --git a/crates/e2e-testing/src/asserts.rs b/crates/e2e-testing/src/asserts.rs deleted file mode 100644 index f31d249a19..0000000000 --- a/crates/e2e-testing/src/asserts.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::fmt; - -#[macro_export] -macro_rules! ensure { - ($cond:expr $(,)?) => {{ - use anyhow::ensure; - ensure!($cond, None); - }}; - ($cond:expr, $($arg:tt)+) => {{ - use anyhow::ensure; - ensure!($cond, None); - }}; -} - -#[macro_export] -macro_rules! ensure_eq { - ($left:expr, $right:expr $(,)?) => {{ - match (&$left, &$right) { - (left_val, right_val) => { - use anyhow::ensure; - use $crate::asserts::error_msg; - ensure!(*left_val == *right_val, error_msg("==", &*left_val, &*right_val, None)); - } - } - }}; - ($left:expr, $right:expr, $($arg:tt)+) => {{ - match (&$left, &$right) { - (left_val, right_val) => { - use anyhow::ensure; - use $crate::asserts::error_msg; - ensure!(*left_val == *right_val, error_msg("==", &*left_val, &*right_val, Some(format_args!($($arg)+)))) - } - } - }}; -} - -#[macro_export] -macro_rules! ensure_ne { - ($left:expr, $right:expr $(,)?) => {{ - match (&$left, &$right) { - (left_val, right_val) => { - use anyhow::ensure; - use $crate::asserts::error_msg; - ensure!(*left_val != *right_val, error_msg("!=", &*left_val, &*right_val, None)); - } - } - }}; - ($left:expr, $right:expr, $($arg:tt)+) => {{ - match (&$left, &$right) { - (left_val, right_val) => { - use anyhow::ensure; - use $crate::asserts::error_msg; - ensure!(*left_val != *right_val, error_msg("!=", &*left_val, &*right_val, Some(format_args!($($arg)+)))) - } - } - }}; -} - -pub fn error_msg(op: &str, left: &T, right: &U, args: Option>) -> String -where - T: fmt::Debug + ?Sized, - U: fmt::Debug + ?Sized, -{ - match args { - Some(args) => format!( - r#"assertion failed: `(left {} right)` - left: `{:?}`, - right: `{:?}`: {}"#, - op, left, right, args - ), - None => format!( - r#"assertion failed: `(left {} right)` - left: `{:?}`, - right: `{:?}`"#, - op, left, right, - ), - } -} diff --git a/crates/e2e-testing/src/controller.rs b/crates/e2e-testing/src/controller.rs deleted file mode 100644 index 0d136e7364..0000000000 --- a/crates/e2e-testing/src/controller.rs +++ /dev/null @@ -1,85 +0,0 @@ -use crate::metadata::AppMetadata; -use anyhow::Result; -use async_trait::async_trait; -use std::process::Output; -use tokio::io::BufReader; -use tokio::process::{ChildStderr, ChildStdout}; - -/// defines crate::controller::Controller trait -/// this is to enable running same set of tests -/// using `spin up` or `Deploying to Fermyon Cloud` -#[async_trait] -pub trait Controller { - fn name(&self) -> String; - fn login(&self) -> Result<()>; - fn template_install(&self, args: Vec<&str>) -> Result; - fn new_app(&self, template_name: &str, app_name: &str, args: Vec<&str>) -> Result; - fn build_app(&self, app_name: &str) -> Result; - fn install_plugins(&self, plugins: Vec<&str>) -> Result; - /// Run the app and either return a running `AppInstance` or the `Output` from an exited invocation - async fn run_app( - &self, - app_name: &str, - trigger_type: &str, - mut args: Vec<&str>, - state_dir: &str, - ) -> Result>; - async fn stop_app( - &self, - app_name: Option<&str>, - process: Option, - ) -> Result<()>; -} -/// A running spin app. -/// -/// If it is running using `spin up`, it also has `process` field populated -/// with handle to the `spin up` process -pub struct AppInstance { - pub metadata: AppMetadata, - pub process: Option, - pub stdout_stream: Option>, - pub stderr_stream: Option>, -} - -impl AppInstance { - pub fn new(metadata: AppMetadata) -> AppInstance { - AppInstance { - metadata, - process: None, - stdout_stream: None, - stderr_stream: None, - } - } - - pub fn new_with_process( - metadata: AppMetadata, - process: Option, - ) -> AppInstance { - AppInstance { - metadata, - process, - stdout_stream: None, - stderr_stream: None, - } - } - - pub fn new_with_process_and_logs_stream( - metadata: AppMetadata, - process: Option, - stdout_stream: Option>, - stderr_stream: Option>, - ) -> AppInstance { - AppInstance { - metadata, - process, - stdout_stream, - stderr_stream, - } - } -} - -/// An invocation of `spin up` that has exited -pub struct ExitedInstance { - pub metadata: AppMetadata, - pub output: Output, -} diff --git a/crates/e2e-testing/src/http_asserts.rs b/crates/e2e-testing/src/http_asserts.rs deleted file mode 100644 index 707e003e50..0000000000 --- a/crates/e2e-testing/src/http_asserts.rs +++ /dev/null @@ -1,120 +0,0 @@ -use crate::ensure_eq; -use anyhow::Result; -use http::Method; -use reqwest::{Request, Response}; -use std::str; -use std::thread::sleep; -use std::time::Duration; - -pub async fn assert_status(url: &str, expected: u16) -> Result<()> { - for _ in 0..5 { - let result = assert_status_once(url, expected).await; - if result.is_ok() { - return Ok(()); - } - - println!("assert_status error is {:?}", result.err()); - sleep(Duration::from_secs(2)) - } - - panic!("failed assert_status after 5 retries") -} - -pub async fn assert_status_once(url: &str, expected: u16) -> Result<()> { - let resp = make_request(Method::GET, url, "").await?; - let status = resp.status(); - - let body = resp.bytes().await?; - let actual_body = str::from_utf8(&body).unwrap().to_string(); - - ensure_eq!(status, expected, "{}", actual_body); - - Ok(()) -} - -pub async fn assert_http_response( - url: &str, - method: Method, - body: &str, - expected: u16, - expected_headers: &[(&str, &str)], - expected_body: Option<&str>, -) -> Result<()> { - for _ in 0..5 { - let result = assert_http_response_once( - url, - method.clone(), - body, - expected, - expected_headers, - expected_body, - ) - .await; - if result.is_ok() { - return Ok(()); - } - - println!("assert_http_response error is {:?}", result.err()); - sleep(Duration::from_secs(2)) - } - - panic!("failed assert_http_response after 5 retries") -} - -pub async fn assert_http_response_once( - url: &str, - method: Method, - body: &str, - expected: u16, - expected_headers: &[(&str, &str)], - expected_body: Option<&str>, -) -> Result<()> { - let res = make_request(method, url, body).await?; - - let status = res.status(); - let headers = res.headers().clone(); - let body = res.bytes().await?; - let actual_body = str::from_utf8(&body).unwrap().to_string(); - - ensure_eq!( - expected, - status.as_u16(), - "Expected status {expected} but got {status}. Response body: '{actual_body}'" - ); - - for (k, v) in expected_headers { - ensure_eq!( - &headers - .get(k.to_string()) - .unwrap_or_else(|| panic!("cannot find header {}", k)) - .to_str()?, - v - ) - } - - if let Some(expected_body_str) = expected_body { - ensure_eq!(expected_body_str, actual_body); - } - - Ok(()) -} - -pub async fn create_request(method: Method, url: &str, body: &str) -> Result { - let mut req = reqwest::Request::new( - match method { - Method::GET => reqwest::Method::GET, - Method::POST => reqwest::Method::POST, - _ => todo!("handle {method}"), - }, - url.try_into()?, - ); - *req.body_mut() = Some(body.to_owned().into()); - - Ok(req) -} - -pub async fn make_request(method: Method, path: &str, body: &str) -> Result { - let req = create_request(method, path, body).await?; - let client = reqwest::Client::new(); - Ok(client.execute(req).await?) -} diff --git a/crates/e2e-testing/src/lib.rs b/crates/e2e-testing/src/lib.rs deleted file mode 100644 index 040dea8664..0000000000 --- a/crates/e2e-testing/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod asserts; -pub mod controller; -pub mod http_asserts; -pub mod metadata; -pub mod spin; -pub mod spin_controller; -pub mod testcase; -pub mod utils; diff --git a/crates/e2e-testing/src/metadata.rs b/crates/e2e-testing/src/metadata.rs deleted file mode 100644 index d21e92c61d..0000000000 --- a/crates/e2e-testing/src/metadata.rs +++ /dev/null @@ -1,28 +0,0 @@ -use anyhow::Result; - -#[derive(Clone)] -pub struct AppRoute { - pub name: String, - pub route_url: String, - pub wildcard: bool, -} - -#[derive(Clone)] -pub struct AppMetadata { - pub name: String, - pub base: String, - pub app_routes: Vec, - pub version: String, -} - -impl AppMetadata { - pub fn get_route_with_name(&self, name: String) -> Result<&AppRoute> { - for route in &self.app_routes { - if route.name == name { - return Ok(route); - } - } - - Err(anyhow::Error::msg("requested route not found")) - } -} diff --git a/crates/e2e-testing/src/spin.rs b/crates/e2e-testing/src/spin.rs deleted file mode 100644 index e735b16b97..0000000000 --- a/crates/e2e-testing/src/spin.rs +++ /dev/null @@ -1,134 +0,0 @@ -use crate::utils; -use anyhow::Result; -use std::path::PathBuf; -use std::process::Output; -use std::sync::Mutex; - -#[cfg(target_family = "unix")] -use { - nix::sys::signal::{kill, Signal}, - nix::unistd::Pid, -}; - -static INSTALLING_TEMPLATES_MUTEX: Mutex = Mutex::new(0); -static INSTALLING_PLUGINS_MUTEX: Mutex = Mutex::new(0); - -pub fn template_install(mut args: Vec<&str>) -> Result { - let mut cmd = vec!["spin", "templates", "install"]; - cmd.append(&mut args); - - let _lock = INSTALLING_TEMPLATES_MUTEX.lock().unwrap(); - let result = utils::run(&cmd, None, None)?; - utils::assert_success(&result); - - Ok(result) -} - -pub fn new_app<'a>( - template_name: &'a str, - app_name: &'a str, - mut args: Vec<&'a str>, -) -> Result { - let basedir = utils::testcases_base_dir(); - let mut cmd = vec![ - "spin", - "new", - app_name, - "-t", - template_name, - "--accept-defaults", - ]; - if !args.is_empty() { - cmd.append(&mut args); - } - - let output = utils::run(&cmd, Some(&basedir), None)?; - utils::assert_success(&output); - Ok(output) -} - -pub fn install_plugins(plugins: Vec<&str>) -> Result { - // lock mutex to ensure one install_plugins runs at a time - let _lock = INSTALLING_PLUGINS_MUTEX.lock().unwrap(); - - let mut output = utils::run(&["spin", "plugin", "update"], None, None)?; - utils::assert_success(&output); - - for plugin in plugins { - output = utils::run(&["spin", "plugin", "install", plugin, "--yes"], None, None)?; - utils::assert_success(&output); - } - - Ok(output) -} - -pub fn build_app(appname: &str) -> Result { - let appdir = appdir(appname); - utils::run(&["spin", "build"], Some(&appdir), None) -} - -pub fn appdir(appname: &str) -> PathBuf { - utils::testcases_base_dir().join(appname) -} - -#[cfg(target_family = "unix")] -pub async fn stop_app_process(process: &mut tokio::process::Child) -> Result<(), anyhow::Error> { - let pid = process.id().unwrap(); - // println!("stopping app with pid {}", pid); - let pid = Pid::from_raw(pid as i32); - kill(pid, Signal::SIGINT).map_err(anyhow::Error::msg) -} - -#[cfg(target_family = "windows")] -pub async fn stop_app_process(process: &mut tokio::process::Child) -> Result<(), anyhow::Error> { - // stop the app at the end of testcase - let _ = &mut process.kill().await.map_err(anyhow::Error::msg); - - match process.wait().await { - Ok(_) => Ok(()), - Err(e) => Err(anyhow::Error::msg(e)), - } -} - -pub fn registry_push(appname: &str, registry_app_url: &str) -> Result { - let appdir = appdir(appname); - let output = utils::run( - &["spin", "registry", "push", registry_app_url, "--insecure"], - Some(&appdir), - None, - )?; - utils::assert_success(&output); - Ok(output) -} - -// use docker login until https://github.com/fermyon/spin/issues/1211 -pub fn registry_login(registry_url: &str, username: &str, password: &str) -> Result { - let output = utils::run( - &[ - "spin", - "registry", - "login", - "-u", - username, - "-p", - password, - registry_url, - ], - None, - None, - )?; - utils::assert_success(&output); - Ok(output) -} - -pub fn version() -> Result { - let output = utils::run(&["spin", "--version"], None, None)?; - utils::assert_success(&output); - Ok(std::str::from_utf8(&output.stdout)?.trim().to_string()) -} - -pub fn which_spin() -> Result { - let output = utils::run(&["which", "spin"], None, None)?; - utils::assert_success(&output); - Ok(std::str::from_utf8(&output.stdout)?.trim().to_string()) -} diff --git a/crates/e2e-testing/src/spin_controller.rs b/crates/e2e-testing/src/spin_controller.rs deleted file mode 100644 index 919d021ebb..0000000000 --- a/crates/e2e-testing/src/spin_controller.rs +++ /dev/null @@ -1,113 +0,0 @@ -use crate::controller::{AppInstance, Controller, ExitedInstance}; -use crate::metadata::AppMetadata; -use crate::spin; -use crate::utils; -use anyhow::{Context, Result}; -use async_trait::async_trait; -use std::process::Output; -use tokio::io::BufReader; - -pub struct SpinUp {} - -/// implements crate::controller::Controller trait -/// to run apps using `spin up` -#[async_trait] -impl Controller for SpinUp { - fn name(&self) -> String { - "spin-up".to_string() - } - - fn login(&self) -> Result<()> { - Ok(()) - } - - fn template_install(&self, args: Vec<&str>) -> Result { - spin::template_install(args) - } - - fn new_app(&self, template_name: &str, app_name: &str, args: Vec<&str>) -> Result { - spin::new_app(template_name, app_name, args) - } - - fn install_plugins(&self, plugins: Vec<&str>) -> Result { - spin::install_plugins(plugins) - } - - fn build_app(&self, app_name: &str) -> Result { - spin::build_app(app_name) - } - - async fn run_app( - &self, - app_name: &str, - trigger_type: &str, - mut xargs: Vec<&str>, - state_dir: &str, - ) -> Result> { - let appdir = spin::appdir(app_name); - - let mut cmd = vec!["spin", "up"]; - if !xargs.is_empty() { - cmd.append(&mut xargs); - } - - if !xargs.contains(&"--state_dir") { - cmd.push("--state-dir"); - cmd.push(state_dir); - } - - let mut address = String::new(); - if trigger_type == "http" { - let port = utils::get_random_port()?; - address = format!("127.0.0.1:{}", port); - cmd.append(&mut vec!["--listen", address.as_str()]); - } - - let metadata = AppMetadata { - name: app_name.to_string(), - base: format!("http://{}", address), - app_routes: vec![], - version: "".to_string(), - }; - let mut child = utils::run_async(&cmd, Some(&appdir), None); - - // if http ensure the server is accepting requests before continuing. - if trigger_type == "http" && !utils::wait_tcp(&address, &mut child, "spin").await? { - let output = child - .wait_with_output() - .await - .context("could not get output from running `spin up`")?; - return Ok(Err(ExitedInstance { output, metadata })); - } - - let stdout = child - .stdout - .take() - .expect("child did not have a handle to stdout"); - let stdout_stream = BufReader::new(stdout); - - let stderr = child - .stderr - .take() - .expect("child did not have a handle to stderr"); - let stderr_stream = BufReader::new(stderr); - - Ok(Ok(AppInstance::new_with_process_and_logs_stream( - metadata, - Some(child), - Some(stdout_stream), - Some(stderr_stream), - ))) - } - - async fn stop_app( - &self, - _: Option<&str>, - process: Option, - ) -> Result<()> { - match process { - None => Ok(()), - Some(mut process) => spin::stop_app_process(&mut process).await, - } - } -} diff --git a/crates/e2e-testing/src/testcase.rs b/crates/e2e-testing/src/testcase.rs deleted file mode 100644 index e50f1885cc..0000000000 --- a/crates/e2e-testing/src/testcase.rs +++ /dev/null @@ -1,233 +0,0 @@ -use crate::controller::Controller; -use crate::metadata::AppMetadata; -use crate::spin; -use crate::utils; -use anyhow::{Context, Result}; -use core::pin::Pin; -use derive_builder::Builder; -use std::fs; -use std::future::Future; -use std::io::Cursor; -use tempfile::TempDir; -use tokio::io::AsyncBufRead; - -type ChecksFunc = Box< - dyn Fn( - AppMetadata, - Option>>, - Option>>, - ) -> Pin>>>, ->; - -/// Represents a testcase -#[derive(Builder)] -#[builder(pattern = "owned")] -pub struct TestCase { - /// name of the testcase - pub name: String, - - /// name of the app under test - #[builder(default)] - pub appname: Option, - - /// optional - /// template to use to create new app - #[builder(default)] - pub template: Option, - - /// optional - /// if provided, appended to `spin new` command - #[builder(default = "vec![]")] - pub new_app_args: Vec, - - /// trigger type for this spin app - #[builder(default = "\"http\".to_string()")] - pub trigger_type: String, - - /// optional - /// template install args. appended to `spin install templates - /// defaults to `--git https://github.com/fermyon/spin` - #[builder(default)] - pub template_install_args: Option>, - - /// optional - /// plugins required for the testcase. e.g. js2wasm for js/ts tests - #[builder(default)] - pub plugins: Option>, - - /// optional - /// if provided, appended to `spin up/deploy` command - #[builder(default = "vec![]")] - pub deploy_args: Vec, - - /// optional - /// if provided, executed as command line before running `spin build` - /// useful if some external script or process needs to run before `spin build` - /// e.g. `npm install` before running `spin build` for `js/ts` tests - #[builder(default)] - pub pre_build_hooks: Option>>, - - /// assertions to run once the app is running - #[builder(setter(custom))] - pub assertions: ChecksFunc, - - /// registry app url where app is pushed and run from - #[builder(default)] - pub push_to_registry: Option, -} - -impl TestCaseBuilder { - pub fn assertions( - self, - value: impl Fn( - AppMetadata, - Option>>, - Option>>, - ) -> Pin>>> - + 'static, - ) -> Self { - let mut this = self; - this.assertions = Some(Box::new(value)); - this - } -} - -impl TestCase { - /// Run the test case. - /// - /// This panics if `spin build` fails. Use `TestCase::try_run` if failure is expected. - pub async fn run(&self, controller: &dyn Controller) -> Result<()> { - self.do_run(controller, true).await - } - - /// Run the test case and return the output of `spin build`. - pub async fn try_run(&self, controller: &dyn Controller) -> Result<()> { - self.do_run(controller, false).await - } - - /// Utility for running the `TestCase` allows panicking or not on `spin build` failure. - /// - /// The output from running `spin build` is returned. - async fn do_run(&self, controller: &dyn Controller, bail_on_run_failure: bool) -> Result<()> { - print_version_info(&self.name); - - // install spin plugins if requested in testcase config - if let Some(plugins) = &self.plugins { - controller - .install_plugins(plugins.iter().map(|s| s as &str).collect()) - .context("installing plugins")?; - } - - let appname = self - .appname - .clone() - .unwrap_or_else(|| format!("{}-generated", self.template.as_ref().unwrap())); - - let appdir = spin::appdir(&appname); - - // cleanup existing dir for testcase project code. cleaned up only if testcase is a template based test - if let Some(template) = &self.template { - // install spin templates from git repo. if template_install_args is provided uses that - // defaults to spin repo - let template_install_args = match &self.template_install_args { - Some(args) => args.iter().map(|s| s as &str).collect(), - None => vec!["--git", "https://github.com/fermyon/spin"], - }; - - controller - .template_install(template_install_args) - .context("installing templates")?; - - let _ = fs::remove_dir_all(&appdir); - - let new_app_args = self.new_app_args.iter().map(|s| s as &str).collect(); - controller - .new_app(template, &appname, new_app_args) - .context("creating new app")?; - } - - // run pre-build-steps. It is useful for running any steps required before running `spin build`. - // e.g. for js/ts tests, we need to run `npm install` before running `spin build` - if let Some(pre_build_hooks) = &self.pre_build_hooks { - for pre_build_hook in pre_build_hooks { - utils::assert_success(&utils::run(pre_build_hook, Some(&appdir), None)?); - } - } - - // run spin build - let build_output = controller - .build_app(&appname) - .context("failed building app")?; - if bail_on_run_failure { - utils::assert_success(&build_output); - } - - //push to registry if url provided - if let Some(registry_app_url) = &self.push_to_registry { - spin::registry_push(&appname, registry_app_url.as_str())?; - } - - // create temp dir as state dir - let tempdir = TempDir::new()?; - let state_dir: String = tempdir - .path() - .join(".spin") - .into_os_string() - .into_string() - .unwrap(); - - // run `spin up` (or `spin deploy` for cloud). - // `AppInstance` has some basic info about the running app like base url, routes (only for cloud) etc. - let deploy_args = self.deploy_args.iter().map(|s| s as &str).collect(); - let run_result = controller - .run_app(&appname, &self.trigger_type, deploy_args, &state_dir) - .await - .context("running app")?; - match run_result { - Ok(app) => { - // run test specific assertions - let deployed_app_metadata = app.metadata; - let deployed_app_name = deployed_app_metadata.name.clone(); - - let assertions_result = (self.assertions)( - deployed_app_metadata, - app.stdout_stream.map(|s| Box::pin(s) as _), - app.stderr_stream.map(|s| Box::pin(s) as _), - ) - .await; - - if let Err(e) = controller - .stop_app(Some(deployed_app_name.as_str()), app.process) - .await - { - println!("warn: failed to stop app {deployed_app_name} with error {e:?}"); - } - - assertions_result - } - Err(instance) if bail_on_run_failure => { - utils::assert_success(&instance.output); - Ok(()) - } - Err(instance) => { - (self.assertions)( - instance.metadata, - Some(Box::pin(Cursor::new(instance.output.stdout))), - Some(Box::pin(Cursor::new(instance.output.stderr))), - ) - .await - } - } - } -} - -fn print_version_info(testcase_name: &str) { - let version = spin::version(); - let which_spin = spin::which_spin(); - println!( - r###"[testcase::run] Running testcase "{}" using spin ({}) with version {}"###, - testcase_name, - which_spin.as_deref().unwrap_or("unknown"), - version.as_deref().unwrap_or("unknown") - ); -} diff --git a/crates/e2e-testing/src/utils.rs b/crates/e2e-testing/src/utils.rs deleted file mode 100644 index 989865bddc..0000000000 --- a/crates/e2e-testing/src/utils.rs +++ /dev/null @@ -1,162 +0,0 @@ -use anyhow::{anyhow, Result}; -use std::path::{Path, PathBuf}; -use std::pin::Pin; -use std::str; -use std::{ - collections::HashMap, - net::{Ipv4Addr, SocketAddrV4, TcpListener}, - process::{self, Command, Output}, - time::Duration, -}; - -use tokio::io::{AsyncBufRead, AsyncBufReadExt}; -use tokio::process::Command as TokioCommand; -use tokio::time::timeout; -use tokio::{net::TcpStream, time::sleep}; - -/// Run the command and returns the output -pub fn run>( - args: &[S], - dir: Option<&Path>, - envs: Option>, -) -> Result { - let mut cmd = Command::new(get_os_process()); - cmd.stdout(process::Stdio::piped()); - cmd.stderr(process::Stdio::piped()); - - if let Some(dir) = dir { - cmd.current_dir(dir); - }; - - cmd.arg("-c"); - cmd.arg(args.iter().map(AsRef::as_ref).collect::>().join(" ")); - if let Some(envs) = envs { - for (k, v) in envs { - cmd.env(k, v); - } - } - - println!("Running command: {cmd:?}"); - Ok(cmd.output()?) -} - -/// Asserts that the output from a `Command` was a success (i.e., exist status was 0) -pub fn assert_success(output: &Output) { - let code = output - .status - .code() - .expect("process unexpectedly terminated by a signal"); - if code != 0 { - let stdout = std::str::from_utf8(&output.stdout).unwrap_or(""); - let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); - panic!("exited with code {code}.\n\nstdout:\n{stdout}\n\nstderr:\n{stderr}\n",); - } -} - -fn get_os_process() -> String { - if cfg!(target_os = "windows") { - String::from("powershell.exe") - } else { - String::from("/bin/bash") - } -} - -/// gets a free random port -pub fn get_random_port() -> Result { - Ok( - TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))? - .local_addr()? - .port(), - ) -} - -/// wait for tcp to work on a given port -/// -/// Return `Ok(true)` if the tcp port can be connected and -/// `Ok(false)` if the process exited early -pub async fn wait_tcp( - url: &str, - process: &mut tokio::process::Child, - target: &str, -) -> Result { - let mut wait_count = 0; - while wait_count < 240 { - if let Ok(Some(_)) = process.try_wait() { - return Ok(false); - } - if TcpStream::connect(&url).await.is_ok() { - return Ok(true); - } - - wait_count += 1; - sleep(Duration::from_secs(1)).await; - } - - Err(anyhow!( - "Ran out of retries waiting for {} to start on URL {}", - target, - url - )) -} - -/// run the process in background and returns a tokio::process::Child -/// the caller can then call utils::get_output(&mut child).await -/// to get logs from the stdout -pub fn run_async>( - args: &[S], - dir: Option<&Path>, - envs: Option>, -) -> tokio::process::Child { - let mut cmd = TokioCommand::new(get_os_process()); - cmd.stdout(process::Stdio::piped()); - cmd.stderr(process::Stdio::piped()); - - if let Some(dir) = dir { - cmd.current_dir(dir); - }; - - cmd.arg("-c"); - cmd.arg(args.iter().map(AsRef::as_ref).collect::>().join(" ")); - if let Some(envs) = envs { - for (k, v) in envs { - cmd.env(k, v); - } - } - - cmd.spawn().expect("failed to spawn command") -} - -pub fn testcases_base_dir() -> PathBuf { - Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/testcases") -} - -pub async fn get_output_stream( - reader: Option>>, - max_wait: Duration, -) -> Result> { - let Some(mut reader) = reader else { - let output: Result, anyhow::Error> = Ok(vec![]); - return output; - }; - - let mut output: Vec = vec![]; - - loop { - let mut line = String::new(); - let nextline = reader.read_line(&mut line); - match timeout(max_wait, nextline).await { - Ok(Ok(n)) if n > 0 => { - line.pop(); // pop the newline off - output.push(line) - } - _ => break, - } - } - - Ok(output) -} - -pub async fn get_output(reader: Option>>) -> Result { - let stream = get_output_stream(reader, Duration::from_secs(1)).await?; - Ok(stream.join("\n")) -} From 0b5f3ff38c752edad364f2090f93845dcdcc8bb0 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 12 Jan 2024 15:37:10 +0100 Subject: [PATCH 14/15] Put e2e tests back into module Signed-off-by: Ryan Levick --- .github/workflows/build.yml | 2 +- tests/spinup_tests.rs | 857 +++++++++--------- .../test-components/components/llm/src/lib.rs | 2 +- tests/testcases/mod.rs | 31 +- .../testing-framework/src/services/docker.rs | 14 +- 5 files changed, 471 insertions(+), 435 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd9b7337b6..d3c719f80b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: run: BUILD_SPIN_EXAMPLES=0 make lint - name: Run lints on examples - run: BUILD_SPIN_EXAMPLES=0 make lint-rust-examples-and-testcases + run: BUILD_SPIN_EXAMPLES=0 make lint-rust-examples - name: Cancel everything if linting fails if: failure() diff --git a/tests/spinup_tests.rs b/tests/spinup_tests.rs index e809174523..9444ad6f5c 100644 --- a/tests/spinup_tests.rs +++ b/tests/spinup_tests.rs @@ -1,474 +1,489 @@ -use anyhow::Context; -use testcases::{ - assert_spin_request, bootstap_env, bootstrap_smoke_test, http_smoke_test_template, - http_smoke_test_template_with_route, redis_smoke_test_template, run_test, -}; - mod testcases; -#[test] -/// Test that the --key-value flag works as expected -fn key_value_cli_flag() -> anyhow::Result<()> { - let test_key = uuid::Uuid::new_v4().to_string(); - 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}")], - testing_framework::ServicesConfig::none(), - move |env| { - let spin = env.runtime_mut(); - assert_spin_request( - spin, - testing_framework::Request::new( - reqwest::Method::GET, - &format!("/test?key={test_key}"), - ), - 200, - &[], - &test_value, - ) - }, - )?; - Ok(()) -} - -#[test] -/// Test that basic http trigger support works -fn http_smoke_test() -> anyhow::Result<()> { - run_test( - "http-smoke-test", - testing_framework::SpinMode::Http, - [], - testing_framework::ServicesConfig::none(), - move |env| { - let spin = env.runtime_mut(); - assert_spin_request( - spin, - testing_framework::Request::new(reqwest::Method::GET, "/test/hello"), - 200, - &[], - "I'm a teapot", - )?; - assert_spin_request( - spin, - testing_framework::Request::new( - reqwest::Method::GET, - "/test/hello/wildcards/should/be/handled", - ), - 200, - &[], - "I'm a teapot", - )?; - assert_spin_request( - spin, - testing_framework::Request::new(reqwest::Method::GET, "/thishsouldfail"), - 404, - &[], - "", - )?; - assert_spin_request( - spin, - testing_framework::Request::new(reqwest::Method::GET, "/test/hello/test-placement"), - 200, - &[], - "text for test", - ) - }, - )?; - - Ok(()) -} - -#[test] -// TODO: it seems that running this test on macOS CI is not possible because the docker services doesn't run. -// Investigate if there is a possible fix for this. -#[cfg(feature = "e2e-tests")] -/// Test that basic redis trigger support works -fn redis_smoke_test() -> anyhow::Result<()> { - /// 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; - } - }; - } - +mod spinup_tests { + use super::testcases::{ + assert_spin_request, bootstap_env, bootstrap_smoke_test, http_smoke_test_template, + http_smoke_test_template_with_route, redis_smoke_test_template, run_test, spin_binary, + }; use anyhow::Context; - use redis::Commands; - 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(()) -} -#[test] -/// Test dynamic environment variables -fn dynamic_env_test() -> anyhow::Result<()> { - run_test( - "dynamic-env-test", - testing_framework::SpinMode::Http, - vec!["--env".to_owned(), "foo=bar".to_owned()], - testing_framework::ServicesConfig::none(), - move |env| { - let spin = env.runtime_mut(); - assert_spin_request( - spin, - testing_framework::Request::new(reqwest::Method::GET, "/env"), - 200, - &[("env_some_key", "some_value"), ("ENV_foo", "bar")], - "I'm a teapot", - )?; - Ok(()) - }, - )?; - - Ok(()) -} - -#[test] -/// Test that mounting works properly -fn assets_routing_test() -> anyhow::Result<()> { - run_test( - "assets-test", - testing_framework::SpinMode::Http, - [], - testing_framework::ServicesConfig::none(), - move |env| { - let spin = env.runtime_mut(); - let mut assert_file = |name: &str, content: &str| { + #[test] + /// Test that the --key-value flag works as expected + fn key_value_cli_flag() -> anyhow::Result<()> { + let test_key = uuid::Uuid::new_v4().to_string(); + 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}")], + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); assert_spin_request( spin, testing_framework::Request::new( reqwest::Method::GET, - &format!("/static/thisshouldbemounted/{name}"), + &format!("/test?key={test_key}"), ), 200, &[], - content, + &test_value, ) - }; - let mut assert_file_content_eq_name = - |name: &str| assert_file(name, &format!("{name}\n")); - - assert_file_content_eq_name("1")?; - assert_file_content_eq_name("2")?; - assert_file_content_eq_name("3")?; - assert_file("empty", "")?; - assert_file("one-byte", "{")?; + }, + )?; + Ok(()) + } - let mut assert_not_found = |path: &str| { + #[test] + /// Test that basic http trigger support works + fn http_smoke_test() -> anyhow::Result<()> { + run_test( + "http-smoke-test", + testing_framework::SpinMode::Http, + [], + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); + assert_spin_request( + spin, + testing_framework::Request::new(reqwest::Method::GET, "/test/hello"), + 200, + &[], + "I'm a teapot", + )?; assert_spin_request( spin, testing_framework::Request::new( reqwest::Method::GET, - &format!("/static/{path}"), + "/test/hello/wildcards/should/be/handled", ), + 200, + &[], + "I'm a teapot", + )?; + assert_spin_request( + spin, + testing_framework::Request::new(reqwest::Method::GET, "/thishsouldfail"), 404, &[], - "Not Found", + "", + )?; + assert_spin_request( + spin, + testing_framework::Request::new( + reqwest::Method::GET, + "/test/hello/test-placement", + ), + 200, + &[], + "text for test", ) - }; + }, + )?; - assert_not_found("donotmount/a")?; - assert_not_found("thisshouldbemounted/thisshouldbeexcluded/4")?; - Ok(()) - }, - )?; + Ok(()) + } - Ok(()) -} + #[test] + // TODO: it seems that running this test on macOS CI is not possible because the docker services doesn't run. + // Investigate if there is a possible fix for this. + #[cfg(any(not(target_os = "macos"), feature = "e2e-tests"))] + /// Test that basic redis trigger support works + fn redis_smoke_test() -> anyhow::Result<()> { + /// 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; + } + }; + } + + use anyhow::Context; + use redis::Commands; + 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(()) + } -#[test] -/// Test that mounting works properly -fn legacy_apps() -> anyhow::Result<()> { - run_test( - "legacy-apps-test", - testing_framework::SpinMode::Http, - [], - testing_framework::ServicesConfig::none(), - move |env| { - let spin = env.runtime_mut(); - let mut test = |lang: &str, body: &str| { + #[test] + /// Test dynamic environment variables + fn dynamic_env_test() -> anyhow::Result<()> { + run_test( + "dynamic-env-test", + testing_framework::SpinMode::Http, + vec!["--env".to_owned(), "foo=bar".to_owned()], + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); assert_spin_request( spin, - testing_framework::Request::new(reqwest::Method::GET, &format!("/{lang}")), + testing_framework::Request::new(reqwest::Method::GET, "/env"), 200, - &[], - body, - ) - }; + &[("env_some_key", "some_value"), ("ENV_foo", "bar")], + "I'm a teapot", + )?; + Ok(()) + }, + )?; - test("golang", "Hello Fermyon!\n")?; - test("rust", "Hello, Fermyon")?; - test("javascript", "Hello from JS-SDK")?; - test("typescript", "Hello from TS-SDK")?; - Ok(()) - }, - )?; + Ok(()) + } - Ok(()) -} + #[test] + /// Test that mounting works properly + fn assets_routing_test() -> anyhow::Result<()> { + run_test( + "assets-test", + testing_framework::SpinMode::Http, + [], + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); + let mut assert_file = |name: &str, content: &str| { + assert_spin_request( + spin, + testing_framework::Request::new( + reqwest::Method::GET, + &format!("/static/thisshouldbemounted/{name}"), + ), + 200, + &[], + content, + ) + }; + let mut assert_file_content_eq_name = + |name: &str| assert_file(name, &format!("{name}\n")); + + assert_file_content_eq_name("1")?; + assert_file_content_eq_name("2")?; + assert_file_content_eq_name("3")?; + assert_file("empty", "")?; + assert_file("one-byte", "{")?; + + let mut assert_not_found = |path: &str| { + assert_spin_request( + spin, + testing_framework::Request::new( + reqwest::Method::GET, + &format!("/static/{path}"), + ), + 404, + &[], + "Not Found", + ) + }; + + assert_not_found("donotmount/a")?; + assert_not_found("thisshouldbemounted/thisshouldbeexcluded/4")?; + Ok(()) + }, + )?; + + Ok(()) + } + + #[test] + /// Test that mounting works properly + fn legacy_apps() -> anyhow::Result<()> { + run_test( + "legacy-apps-test", + testing_framework::SpinMode::Http, + [], + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); + let mut test = |lang: &str, body: &str| { + assert_spin_request( + spin, + testing_framework::Request::new(reqwest::Method::GET, &format!("/{lang}")), + 200, + &[], + body, + ) + }; + + test("golang", "Hello Fermyon!\n")?; + test("rust", "Hello, Fermyon")?; + test("javascript", "Hello from JS-SDK")?; + test("typescript", "Hello from TS-SDK")?; + Ok(()) + }, + )?; -#[test] -fn bad_build_test() -> anyhow::Result<()> { - let mut env = bootstap_env( - "error", - [], - testing_framework::ServicesConfig::none(), - testing_framework::SpinMode::None, - )?; - let expected = r#"Error: Couldn't find trigger executor for local app "spin.toml" + Ok(()) + } + + #[test] + fn bad_build_test() -> anyhow::Result<()> { + let mut env = bootstap_env( + "error", + [], + testing_framework::ServicesConfig::none(), + testing_framework::SpinMode::None, + )?; + let expected = r#"Error: Couldn't find trigger executor for local app "spin.toml" Caused by: no triggers in app "#; - assert_eq!(env.runtime_mut().stderr(), expected); + assert_eq!(env.runtime_mut().stderr(), expected); - Ok(()) -} + Ok(()) + } -#[test] -fn outbound_http_works() -> anyhow::Result<()> { - run_test( - "outbound-http-to-same-app", - testing_framework::SpinMode::Http, - [], - testing_framework::ServicesConfig::none(), - move |env| { - let spin = env.runtime_mut(); - assert_spin_request( - spin, - testing_framework::Request::new(reqwest::Method::GET, "/test/outbound-allowed"), - 200, - &[], - "Hello, Fermyon!\n", - )?; - - assert_spin_request( - spin, - testing_framework::Request::new(reqwest::Method::GET, "/test/outbound-not-allowed"), - 500, - &[], - "", - )?; + #[test] + fn outbound_http_works() -> anyhow::Result<()> { + run_test( + "outbound-http-to-same-app", + testing_framework::SpinMode::Http, + [], + testing_framework::ServicesConfig::none(), + move |env| { + let spin = env.runtime_mut(); + assert_spin_request( + spin, + testing_framework::Request::new(reqwest::Method::GET, "/test/outbound-allowed"), + 200, + &[], + "Hello, Fermyon!\n", + )?; - Ok(()) - }, - )?; + assert_spin_request( + spin, + testing_framework::Request::new( + reqwest::Method::GET, + "/test/outbound-not-allowed", + ), + 500, + &[], + "", + )?; - Ok(()) -} + Ok(()) + }, + )?; -#[test] -fn http_rust_template_smoke_test() -> anyhow::Result<()> { - http_smoke_test_template("http-rust", None, &[], |_| Ok(()), "Hello, Fermyon") -} + Ok(()) + } -#[test] -#[cfg(feature = "e2e-tests")] -fn http_python_template_smoke_test() -> anyhow::Result<()> { - http_smoke_test_template( - "http-py", - Some("https://github.com/fermyon/spin-python-sdk"), - &["py2wasm"], - |_| Ok(()), - "Hello from the Python SDK", - ) -} + #[test] + fn http_rust_template_smoke_test() -> anyhow::Result<()> { + http_smoke_test_template("http-rust", None, &[], |_| Ok(()), "Hello, Fermyon") + } -#[test] -#[cfg(feature = "e2e-tests")] -fn http_c_template_smoke_test() -> anyhow::Result<()> { - http_smoke_test_template("http-c", None, &[], |_| Ok(()), "Hello from WAGI/1\n") -} + #[test] + #[cfg(feature = "e2e-tests")] + fn http_python_template_smoke_test() -> anyhow::Result<()> { + http_smoke_test_template( + "http-py", + Some("https://github.com/fermyon/spin-python-sdk"), + &["py2wasm"], + |_| Ok(()), + "Hello from the Python SDK", + ) + } -#[test] -#[cfg(feature = "e2e-tests")] -fn http_go_template_smoke_test() -> anyhow::Result<()> { - let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { - let mut tidy = std::process::Command::new("go"); - tidy.args(["mod", "tidy"]); - env.run_in(&mut tidy)?; - Ok(()) - }; - http_smoke_test_template("http-go", None, &[], prebuild, "Hello Fermyon!\n") -} + #[test] + #[cfg(feature = "e2e-tests")] + fn http_c_template_smoke_test() -> anyhow::Result<()> { + http_smoke_test_template("http-c", None, &[], |_| Ok(()), "Hello from WAGI/1\n") + } -#[test] -#[cfg(feature = "e2e-tests")] -fn http_js_template_smoke_test() -> anyhow::Result<()> { - let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { - let mut tidy = std::process::Command::new("npm"); - tidy.args(["install"]); - env.run_in(&mut tidy)?; - Ok(()) - }; - http_smoke_test_template( - "http-js", - Some("https://github.com/fermyon/spin-js-sdk"), - &["js2wasm"], - prebuild, - "Hello from JS-SDK", - ) -} + #[test] + #[cfg(feature = "e2e-tests")] + fn http_go_template_smoke_test() -> anyhow::Result<()> { + let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { + let mut tidy = std::process::Command::new("go"); + tidy.args(["mod", "tidy"]); + env.run_in(&mut tidy)?; + Ok(()) + }; + http_smoke_test_template("http-go", None, &[], prebuild, "Hello Fermyon!\n") + } -#[test] -#[cfg(feature = "e2e-tests")] -fn http_ts_template_smoke_test() -> anyhow::Result<()> { - let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { - let mut tidy = std::process::Command::new("npm"); - tidy.args(["install"]); - env.run_in(&mut tidy)?; - Ok(()) - }; - http_smoke_test_template( - "http-ts", - Some("https://github.com/fermyon/spin-js-sdk"), - &["js2wasm"], - prebuild, - "Hello from TS-SDK", - ) -} + #[test] + #[cfg(feature = "e2e-tests")] + fn http_js_template_smoke_test() -> anyhow::Result<()> { + let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { + let mut tidy = std::process::Command::new("npm"); + tidy.args(["install"]); + env.run_in(&mut tidy)?; + Ok(()) + }; + http_smoke_test_template( + "http-js", + Some("https://github.com/fermyon/spin-js-sdk"), + &["js2wasm"], + prebuild, + "Hello from JS-SDK", + ) + } -#[test] -#[cfg(target_arch = "x86_64")] -#[cfg(feature = "e2e-tests")] -fn http_grain_template_smoke_test() -> anyhow::Result<()> { - http_smoke_test_template("http-grain", None, &[], |_| Ok(()), "Hello, World\n") -} + #[test] + #[cfg(feature = "e2e-tests")] + fn http_ts_template_smoke_test() -> anyhow::Result<()> { + let prebuild = |env: &mut testing_framework::TestEnvironment<_>| { + let mut tidy = std::process::Command::new("npm"); + tidy.args(["install"]); + env.run_in(&mut tidy)?; + Ok(()) + }; + http_smoke_test_template( + "http-ts", + Some("https://github.com/fermyon/spin-js-sdk"), + &["js2wasm"], + prebuild, + "Hello from TS-SDK", + ) + } -#[test] -#[cfg(feature = "e2e-tests")] -fn http_zig_template_smoke_test() -> anyhow::Result<()> { - http_smoke_test_template("http-zig", None, &[], |_| Ok(()), "Hello World!\n") -} + #[test] + #[cfg(target_arch = "x86_64")] + #[cfg(feature = "e2e-tests")] + fn http_grain_template_smoke_test() -> anyhow::Result<()> { + http_smoke_test_template("http-grain", None, &[], |_| Ok(()), "Hello, World\n") + } -#[test] -#[cfg(feature = "e2e-tests")] -fn http_swift_template_smoke_test() -> anyhow::Result<()> { - http_smoke_test_template("http-swift", None, &[], |_| Ok(()), "Hello from WAGI/1!\n") -} + #[test] + #[cfg(feature = "e2e-tests")] + fn http_zig_template_smoke_test() -> anyhow::Result<()> { + http_smoke_test_template("http-zig", None, &[], |_| Ok(()), "Hello World!\n") + } -#[test] -fn http_php_template_smoke_test() -> anyhow::Result<()> { - http_smoke_test_template_with_route( - "http-php", - None, - &[], - |_| Ok(()), - "/index.php", - "Hello Fermyon Spin", - ) -} + #[test] + #[cfg(feature = "e2e-tests")] + fn http_swift_template_smoke_test() -> anyhow::Result<()> { + http_smoke_test_template("http-swift", None, &[], |_| Ok(()), "Hello from WAGI/1!\n") + } -#[test] -fn redis_go_template_smoke_test() -> anyhow::Result<()> { - redis_smoke_test_template( - "redis-go", - None, - &[], - |port| { - vec![ - "--value".into(), - "redis-channel=redis-channel".into(), - "--value".into(), - format!("redis-address=redis://localhost:{port}"), - ] - }, - |_| Ok(()), - ) -} + #[test] + fn http_php_template_smoke_test() -> anyhow::Result<()> { + http_smoke_test_template_with_route( + "http-php", + None, + &[], + |_| Ok(()), + "/index.php", + "Hello Fermyon Spin", + ) + } -#[test] -fn redis_rust_template_smoke_test() -> anyhow::Result<()> { - redis_smoke_test_template( - "redis-rust", - None, - &[], - |port| { - vec![ - "--value".into(), - "redis-channel=redis-channel".into(), - "--value".into(), - format!("redis-address=redis://localhost:{port}"), - ] - }, - |_| Ok(()), - ) -} + #[test] + fn redis_go_template_smoke_test() -> anyhow::Result<()> { + redis_smoke_test_template( + "redis-go", + None, + &[], + |port| { + vec![ + "--value".into(), + "redis-channel=redis-channel".into(), + "--value".into(), + format!("redis-address=redis://localhost:{port}"), + ] + }, + |env| { + let mut tidy = std::process::Command::new("go"); + tidy.args(["mod", "tidy"]); + env.run_in(&mut tidy)?; + Ok(()) + }, + ) + } -#[test] -fn registry_works() -> anyhow::Result<()> { - let services = testing_framework::ServicesConfig::new(vec!["registry".into()])?; - let spin_up_args = |env: &mut testing_framework::TestEnvironment<()>| { - let registry_url = format!( - "localhost:{}/spin-e2e-tests/registry-works/v1", - env.get_port(5000)? - .context("no registry port was exposed by test services")? - ); - let mut registry_push = std::process::Command::new(testcases::spin_binary()); - registry_push.args(&["registry", "push", ®istry_url, "--insecure"]); - env.run_in(&mut registry_push)?; - Ok(vec![ - "--from-registry".into(), - registry_url, - "--insecure".into(), - ]) - }; - let mut env = bootstrap_smoke_test( - &services, - None, - &[], - "http-rust", - |_| Ok(Vec::new()), - |_| Ok(()), - spin_up_args, - testing_framework::SpinMode::Http, - )?; - assert_spin_request( - env.runtime_mut(), - testing_framework::Request::new(reqwest::Method::GET, "/"), - 200, - &[], - "Hello, Fermyon", - )?; - Ok(()) + #[test] + fn redis_rust_template_smoke_test() -> anyhow::Result<()> { + redis_smoke_test_template( + "redis-rust", + None, + &[], + |port| { + vec![ + "--value".into(), + "redis-channel=redis-channel".into(), + "--value".into(), + format!("redis-address=redis://localhost:{port}"), + ] + }, + |_| Ok(()), + ) + } + + #[test] + fn registry_works() -> anyhow::Result<()> { + let services = testing_framework::ServicesConfig::new(vec!["registry".into()])?; + let spin_up_args = |env: &mut testing_framework::TestEnvironment<()>| { + let registry_url = format!( + "localhost:{}/spin-e2e-tests/registry-works/v1", + env.get_port(5000)? + .context("no registry port was exposed by test services")? + ); + let mut registry_push = std::process::Command::new(spin_binary()); + registry_push.args(["registry", "push", ®istry_url, "--insecure"]); + env.run_in(&mut registry_push)?; + Ok(vec![ + "--from-registry".into(), + registry_url, + "--insecure".into(), + ]) + }; + let mut env = bootstrap_smoke_test( + &services, + None, + &[], + "http-rust", + |_| Ok(Vec::new()), + |_| Ok(()), + spin_up_args, + testing_framework::SpinMode::Http, + )?; + assert_spin_request( + env.runtime_mut(), + testing_framework::Request::new(reqwest::Method::GET, "/"), + 200, + &[], + "Hello, Fermyon", + )?; + Ok(()) + } } diff --git a/tests/test-components/components/llm/src/lib.rs b/tests/test-components/components/llm/src/lib.rs index 5f497d6317..6cc1287949 100644 --- a/tests/test-components/components/llm/src/lib.rs +++ b/tests/test-components/components/llm/src/lib.rs @@ -20,7 +20,7 @@ impl Component { Some(param) )); - ensure!(inference.text.len() > 0); + ensure!(!inference.text.is_empty()); ensure_eq!(inference.usage.generated_token_count, 1); Ok(()) diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index 3b9a7e89c8..f98a5d4c45 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -66,9 +66,9 @@ pub fn assert_spin_request( ))); } if body != expected_body { - return Err(testing_framework::TestError::Failure( - anyhow::anyhow!("expected body '{expected_body}', got '{body}'").into(), - )); + return Err(testing_framework::TestError::Failure(anyhow::anyhow!( + "expected body '{expected_body}', got '{body}'" + ))); } Ok(()) } @@ -182,7 +182,11 @@ pub fn redis_smoke_test_template( Ok(()) } +static TEMPLATE_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(()); + /// Bootstrap a test environment for a smoke test +// TODO: refactor this function to not take so many arguments +#[allow(clippy::too_many_arguments)] pub fn bootstrap_smoke_test( services: &testing_framework::ServicesConfig, template_url: Option<&str>, @@ -199,15 +203,28 @@ pub fn bootstrap_smoke_test( ) -> anyhow::Result> { let mut env: testing_framework::TestEnvironment<()> = testing_framework::TestEnvironment::boot(services)?; - if let Some(template_url) = template_url { - let mut template_install = std::process::Command::new(spin_binary()); - template_install.args(["templates", "install", "--git", template_url, "--update"]); + + let template_url = template_url.unwrap_or("https://github.com/fermyon/spin"); + let mut template_install = std::process::Command::new(spin_binary()); + template_install.args(["templates", "install", "--git", template_url, "--update"]); + // We need to serialize template installs since they can't be run in parallel + { + let _guard = TEMPLATE_MUTEX.lock().unwrap(); env.run_in(&mut template_install)?; } + if !plugins.is_empty() { let mut plugin_update = std::process::Command::new(spin_binary()); plugin_update.args(["plugin", "update"]); - env.run_in(&mut plugin_update)?; + if let Err(e) = env.run_in(&mut plugin_update) { + // We treat plugin updates as best efforts since it only needs to be run once + if !e + .to_string() + .contains("update operation is already in progress") + { + return Err(e); + } + } } for plugin in plugins { let mut plugin_install = std::process::Command::new(spin_binary()); diff --git a/tests/testing-framework/src/services/docker.rs b/tests/testing-framework/src/services/docker.rs index fac10043a2..31ac36002c 100644 --- a/tests/testing-framework/src/services/docker.rs +++ b/tests/testing-framework/src/services/docker.rs @@ -129,7 +129,7 @@ impl Service for DockerService { fn build_image(docker_file_path: &Path, image_name: &String) -> anyhow::Result<()> { let temp_dir = temp_dir::TempDir::new() .context("failed to produce a temporary directory to run docker in")?; - let status = Command::new("docker") + let output = Command::new("docker") .arg("build") .arg("-f") .arg(docker_file_path) @@ -137,12 +137,16 @@ fn build_image(docker_file_path: &Path, image_name: &String) -> anyhow::Result<( .arg(image_name) .arg(temp_dir.path()) .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() + .stderr(Stdio::piped()) + .output() .context("service failed to spawn")?; - if !status.success() { - bail!("failed to build docker image"); + if !output.status.success() { + let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); + bail!( + "failed to build docker '{image_name}' image: status={} stderr={stderr}", + output.status + ); } Ok(()) } From 3bab43a7fc387e6bb7c20055d39da88d497b23f3 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Mon, 15 Jan 2024 11:49:59 +0100 Subject: [PATCH 15/15] No longer run e2e tests in Docker Signed-off-by: Ryan Levick --- .github/workflows/build.yml | 35 +++++++++----- Makefile | 13 +----- e2e-tests-aarch64.Dockerfile | 82 --------------------------------- e2e-tests-docker-compose.yml | 61 ------------------------- e2e-tests.Dockerfile | 88 ------------------------------------ tests/testcases/mod.rs | 9 +++- 6 files changed, 33 insertions(+), 255 deletions(-) delete mode 100644 e2e-tests-aarch64.Dockerfile delete mode 100644 e2e-tests-docker-compose.yml delete mode 100644 e2e-tests.Dockerfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d3c719f80b..92481fba47 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -197,23 +197,34 @@ jobs: # run on a larger runner for more SSD/resource access runs-on: ubuntu-22.04-4core-spin if: ${{ github.repository_owner == 'fermyon' }} - needs: build-rust-ubuntu steps: - uses: actions/checkout@v3 - - name: Retrieve saved Spin Binary - uses: actions/download-artifact@v3 + # Install all the toolchain dependencies + - name: Install Rust wasm target + run: rustup target add wasm32-wasi && rustup target add wasm32-unknown-unknown + - uses: goto-bus-stop/setup-zig@v2 + - uses: actions/setup-go@v4 with: - name: spin-ubuntu-latest - path: target/release/ - - - name: Build e2e tests image + go-version: '1.20.1' + - uses: acifani/setup-tinygo@v2 + with: + tinygo-version: '0.27.0' + - uses: actions/setup-node@v3 + with: + node-version: '16.x' + - name: Install Swift + run: | + wget https://github.com/swiftwasm/swift/releases/download/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu22.04_x86_64.tar.gz + tar -xf swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu22.04_x86_64.tar.gz + echo "$PWD/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/usr/bin" >> $GITHUB_PATH + - name: Install Grain run: | - export E2E_FETCH_SPIN=false - make build-test-spin-up + wget https://github.com/grain-lang/grain/releases/download/grain-v0.5.4/grain-linux-x64 + chmod +x grain-linux-x64 + mv grain-linux-x64 grain + echo "$PWD" >> $GITHUB_PATH - name: Run e2e tests run: | - chmod +x `pwd`/target/release/spin - export E2E_VOLUME_MOUNT="-v `pwd`/target/release/spin:/usr/local/bin/spin" - make run-test-spin-up + make test-spin-up diff --git a/Makefile b/Makefile index f47215e12e..e8f28f3f96 100644 --- a/Makefile +++ b/Makefile @@ -99,17 +99,8 @@ test-integration: $(LOG_LEVEL_VAR) cargo test --test integration --no-fail-fast -- --skip spinup_tests --skip cloud_tests --nocapture .PHONY: test-spin-up -test-spin-up: build-test-spin-up run-test-spin-up - -.PHONY: build-test-spin-up -build-test-spin-up: - docker build -t spin-e2e-tests --build-arg FETCH_SPIN=$(E2E_FETCH_SPIN) --build-arg BUILD_SPIN=$(E2E_BUILD_SPIN) -f $(E2E_TESTS_DOCKERFILE) . - -.PHONY: run-test-spin-up -run-test-spin-up: - REDIS_IMAGE=$(REDIS_IMAGE) MYSQL_IMAGE=$(MYSQL_IMAGE) POSTGRES_IMAGE=$(POSTGRES_IMAGE) \ - BUILD_SPIN=$(E2E_BUILD_SPIN) \ - docker compose -f e2e-tests-docker-compose.yml run $(E2E_SPIN_RELEASE_VOLUME_MOUNT) $(E2E_SPIN_DEBUG_VOLUME_MOUNT) e2e-tests +test-spin-up: + cargo test --release spinup_tests --no-default-features --features e2e-tests --no-fail-fast -- --nocapture .PHONY: test-sdk-go test-sdk-go: diff --git a/e2e-tests-aarch64.Dockerfile b/e2e-tests-aarch64.Dockerfile deleted file mode 100644 index 4c2fc71340..0000000000 --- a/e2e-tests-aarch64.Dockerfile +++ /dev/null @@ -1,82 +0,0 @@ -FROM ubuntu:22.04 - -ARG BUILD_SPIN=false -ARG SPIN_VERSION=canary - -WORKDIR /root -RUN apt-get update && apt-get install -y wget sudo xz-utils gcc git pkg-config redis clang libicu-dev docker.io - -# nodejs -RUN curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - -RUN apt-get install -y nodejs npm - -# golang -RUN wget https://go.dev/dl/go1.20.1.linux-arm64.tar.gz && \ - rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.1.linux-arm64.tar.gz -ENV PATH="$PATH:/usr/local/go/bin" - -# tinygo -RUN wget https://github.com/tinygo-org/tinygo/releases/download/v0.27.0/tinygo_0.27.0_arm64.deb && \ - sudo dpkg -i tinygo_0.27.0_arm64.deb && \ - tinygo env - -RUN wget https://ziglang.org/download/0.10.0/zig-linux-aarch64-0.10.0.tar.xz && \ - tar -xf zig-linux-aarch64-0.10.0.tar.xz -ENV PATH="$PATH:/root/zig-linux-aarch64-0.10.0" - -# # grain - disabled as no arm build -# RUN wget https://github.com/grain-lang/grain/releases/download/grain-v0.5.4/grain-linux-x64 && \ -# mv grain-linux-x64 /usr/local/bin/grain && chmod +x /usr/local/bin/grain - -# # rust -ENV RUSTUP_HOME=/usr/local/rustup \ - CARGO_HOME=/usr/local/cargo \ - PATH=/usr/local/cargo/bin:$PATH - -RUN url="https://static.rust-lang.org/rustup/dist/aarch64-unknown-linux-gnu/rustup-init"; \ - wget "$url"; \ - chmod +x rustup-init; \ - ./rustup-init -y --no-modify-path --default-toolchain 1.73 --default-host aarch64-unknown-linux-gnu; \ - rm rustup-init; \ - chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \ - rustup --version; \ - cargo --version; \ - rustc --version; \ - rustup target add wasm32-wasi; \ - rustup target add wasm32-unknown-unknown; - -# swift -RUN wget https://github.com/swiftwasm/swift/releases/download/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu20.04_aarch64.tar.gz && \ - tar -xf swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu20.04_aarch64.tar.gz -ENV PATH="$PATH:/root/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/usr/bin" - -## check versions -RUN tinygo version; \ - go version; \ - zig version; \ - rustc --version; \ - node --version; \ - swift --version; - -## spin -RUN wget https://github.com/fermyon/spin/releases/download/${SPIN_VERSION}/spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \ - tar -xvf spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \ - ls -ltr && \ - mv spin /usr/local/bin/spin; - -WORKDIR /e2e-tests -COPY . . - -RUN printf '#!/bin/bash \n \ - if [[ "$BUILD_SPIN" == "true" ]]; then \n \ - echo "BUILD_SPIN is true. compiling spin" \n \ - cargo build --release \n \ - fi \n\n \ - cargo test spinup_tests --features e2e-tests --no-fail-fast -- --nocapture \n \ - ' > /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh - -RUN spin --version -ENV PATH=/from-host/target/debug:/from-host/target/release:$HOME/.cargo/bin:$PATH -CMD /usr/local/bin/entrypoint.sh diff --git a/e2e-tests-docker-compose.yml b/e2e-tests-docker-compose.yml deleted file mode 100644 index b553891a34..0000000000 --- a/e2e-tests-docker-compose.yml +++ /dev/null @@ -1,61 +0,0 @@ -version: "3.9" - -services: - mysql: - image: ${MYSQL_IMAGE:-mysql:8.0.22} - ports: - - "3306:3306" - volumes: - - db_data:/var/lib/mysql - restart: always - environment: - MYSQL_ROOT_PASSWORD: spin - MYSQL_DATABASE: spin_dev - MYSQL_USER: spin - MYSQL_PASSWORD: spin - - postgres: - image: ${POSTGRES_IMAGE:-postgres:14.7-alpine} - restart: always - environment: - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=postgres - - POSTGRES_DB=spin_dev - ports: - - '5432:5432' - volumes: - - postgres_data:/var/lib/postgresql/data - - redis: - image: ${REDIS_IMAGE:-redis:7.0.8-alpine3.17} - ports: - - "6379:6379" - restart: always - - registry: - image: ${REGISTRY_IMAGE:-registry:2} - ports: - - "5001:5000" - restart: always - environment: - - REGISTRY_HTTP_SECRET=secret - - e2e-tests: - depends_on: - - mysql - - redis - - postgres - - registry - image: spin-e2e-tests - entrypoint: /usr/local/bin/entrypoint.sh - volumes: - - target_cache:/e2e-tests/target - - cargo_registry_cache:/usr/local/cargo/registry - - cargo_git_cache:/usr/local/cargo/git - -volumes: - db_data: {} - postgres_data: {} - cargo_registry_cache: {} - cargo_git_cache: {} - target_cache: {} diff --git a/e2e-tests.Dockerfile b/e2e-tests.Dockerfile deleted file mode 100644 index ba9d863504..0000000000 --- a/e2e-tests.Dockerfile +++ /dev/null @@ -1,88 +0,0 @@ -FROM ubuntu:22.04 - -ARG BUILD_SPIN=false -ARG FETCH_SPIN=true -ARG SPIN_VERSION=canary - -WORKDIR /root -RUN apt-get update && apt-get install -y wget sudo xz-utils gcc git pkg-config redis clang libicu-dev docker.io netcat - -# nodejs -RUN curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - -RUN apt-get install -y nodejs npm - -# golang -RUN wget https://go.dev/dl/go1.20.1.linux-amd64.tar.gz && \ - rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.1.linux-amd64.tar.gz -ENV PATH="$PATH:/usr/local/go/bin" - -# tinygo -RUN wget https://github.com/tinygo-org/tinygo/releases/download/v0.27.0/tinygo_0.27.0_amd64.deb && \ - sudo dpkg -i tinygo_0.27.0_amd64.deb && \ - tinygo env - -# zig -RUN wget https://ziglang.org/download/0.10.0/zig-linux-x86_64-0.10.0.tar.xz && \ - tar -xf zig-linux-x86_64-0.10.0.tar.xz -ENV PATH="$PATH:/root/zig-linux-x86_64-0.10.0" - -# grain -RUN wget https://github.com/grain-lang/grain/releases/download/grain-v0.5.4/grain-linux-x64 && \ - mv grain-linux-x64 /usr/local/bin/grain && chmod +x /usr/local/bin/grain - -# # rust -ENV RUSTUP_HOME=/usr/local/rustup \ - CARGO_HOME=/usr/local/cargo \ - PATH=/usr/local/cargo/bin:$PATH - -RUN url="https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init"; \ - wget "$url"; \ - chmod +x rustup-init; \ - ./rustup-init -y --no-modify-path --default-toolchain 1.73 --default-host x86_64-unknown-linux-gnu; \ - rm rustup-init; \ - chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \ - rustup --version; \ - cargo --version; \ - rustc --version; \ - rustup target add wasm32-wasi; \ - rustup target add wasm32-unknown-unknown; - -# swift -RUN wget https://github.com/swiftwasm/swift/releases/download/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu22.04_x86_64.tar.gz && \ - tar -xf swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu22.04_x86_64.tar.gz -ENV PATH="$PATH:/root/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/usr/bin" - -## check versions -RUN tinygo version; \ - go version; \ - zig version; \ - rustc --version; \ - node --version; \ - swift --version; - -RUN if [ "${FETCH_SPIN}" = "true" ]; then \ - wget https://github.com/fermyon/spin/releases/download/${SPIN_VERSION}/spin-${SPIN_VERSION}-linux-amd64.tar.gz && \ - tar -xvf spin-${SPIN_VERSION}-linux-amd64.tar.gz && \ - ls -ltr && \ - mv spin /usr/local/bin/spin; \ - fi - -WORKDIR /e2e-tests -COPY . . - -RUN printf '#!/bin/bash \n \ - if [[ "$BUILD_SPIN" == "true" ]]; then \n \ - echo "BUILD_SPIN is true. compiling spin" \n \ - cargo build --release \n \ - fi \n\n \ - cargo test spinup_tests --features e2e-tests --no-fail-fast -- --nocapture \n \ - ' > /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh - -RUN if [ "${FETCH_SPIN}" = "true" ] || [ "${BUILD_SPIN}" = "true" ]; then \ - spin --version; \ - fi - -ENV PATH=/from-host/target/debug:/from-host/target/release:$HOME/.cargo/bin:$PATH -CMD /usr/local/bin/entrypoint.sh diff --git a/tests/testcases/mod.rs b/tests/testcases/mod.rs index f98a5d4c45..4ef208a600 100644 --- a/tests/testcases/mod.rs +++ b/tests/testcases/mod.rs @@ -245,8 +245,15 @@ pub fn bootstrap_smoke_test( .args(new_app_args(&mut env)?); env.run_in(&mut new_app)?; prebuild_hook(&mut env)?; + let path = std::env::var("PATH").unwrap_or_default(); + let path = if path.is_empty() { + spin_binary().parent().unwrap().display().to_string() + } else { + format!("{path}:{}", spin_binary().parent().unwrap().display()) + }; let mut build = std::process::Command::new(spin_binary()); - build.args(["build"]); + // Ensure `spin` is on the path + build.env("PATH", path).args(["build"]); env.run_in(&mut build)?; let spin_up_args = spin_up_args(&mut env)?; let spin = testing_framework::Spin::start(&spin_binary(), &env, spin_up_args, spin_mode)?;