diff --git a/cli/tests/integration/async_io.rs b/cli/tests/integration/async_io.rs index fda48333..d7b88418 100644 --- a/cli/tests/integration/async_io.rs +++ b/cli/tests/integration/async_io.rs @@ -1,4 +1,7 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use hyper::{body::HttpBody, Body, Request, Response, StatusCode}; use std::sync::{ atomic::{AtomicUsize, Ordering}, @@ -13,8 +16,7 @@ use tokio::sync::Barrier; // // https://github.com/fastly/Viceroy/issues/207 tracks the broader issue. #[cfg(target_family = "unix")] -#[tokio::test(flavor = "multi_thread")] -async fn async_io_methods() -> TestResult { +viceroy_test!(async_io_methods, |is_component| { let request_count = Arc::new(AtomicUsize::new(0)); let req_count_1 = request_count.clone(); let req_count_2 = request_count.clone(); @@ -34,6 +36,7 @@ async fn async_io_methods() -> TestResult { // total and will behave differently depending on which request # it is // processing. let test = Test::using_fixture("async_io.wasm") + .adapt_component(is_component) .async_backend("Simple", "/", None, move |req: Request| { assert_eq!(req.headers()["Host"], "simple.org"); let req_count_1 = req_count_1.clone(); @@ -206,4 +209,4 @@ async fn async_io_methods() -> TestResult { assert_eq!(resp.headers()["Ready-Index"], "timeout"); Ok(()) -} +}); diff --git a/cli/tests/integration/body.rs b/cli/tests/integration/body.rs index b86f483d..8a205de0 100644 --- a/cli/tests/integration/body.rs +++ b/cli/tests/integration/body.rs @@ -1,13 +1,16 @@ //! Tests related to HTTP request and response bodies. use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::{body, StatusCode}, }; -#[tokio::test(flavor = "multi_thread")] -async fn bodies_can_be_written_and_appended() -> TestResult { +viceroy_test!(bodies_can_be_written_and_appended, |is_component| { let resp = Test::using_fixture("write-body.wasm") + .adapt_component(is_component) .against_empty() .await?; @@ -19,13 +22,13 @@ async fn bodies_can_be_written_and_appended() -> TestResult { assert_eq!(&body, "Hello, Viceroy!"); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn bodies_can_be_written_and_read() -> TestResult { +viceroy_test!(bodies_can_be_written_and_read, |is_component| { let resp = Test::using_fixture("write-and-read-body.wasm") + .adapt_component(is_component) .against_empty() .await?; assert_eq!(resp.status(), StatusCode::OK); Ok(()) -} +}); diff --git a/cli/tests/integration/client_certs.rs b/cli/tests/integration/client_certs.rs index a13d0f1a..03ad733f 100644 --- a/cli/tests/integration/client_certs.rs +++ b/cli/tests/integration/client_certs.rs @@ -1,4 +1,7 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use base64::engine::{general_purpose, Engine}; use hyper::http::response; use hyper::server::conn::AddrIncoming; @@ -127,10 +130,8 @@ fn build_server_tls_config() -> ServerConfig { .expect("valid server cert") } -#[tokio::test(flavor = "multi_thread")] -#[serial_test::serial] -async fn custom_ca_works() -> TestResult { - let test = Test::using_fixture("mutual-tls.wasm"); +viceroy_test!(custom_ca_works, |is_component| { + let test = Test::using_fixture("mutual-tls.wasm").adapt_component(is_component); let server_addr: SocketAddr = "127.0.0.1:0".parse().expect("localhost parses"); let incoming = AddrIncoming::bind(&server_addr).expect("bind"); let bound_port = incoming.local_addr().port(); @@ -197,17 +198,16 @@ async fn custom_ca_works() -> TestResult { StatusCode::SERVICE_UNAVAILABLE ); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -#[serial_test::serial] -async fn client_certs_work() -> TestResult { +viceroy_test!(client_certs_work, |is_component| { // Set up the test harness std::env::set_var( "SSL_CERT_FILE", concat!(env!("CARGO_MANIFEST_DIR"), "/../test-fixtures/data/ca.pem"), ); - let test = Test::using_fixture("mutual-tls.wasm"); + let test = Test::using_fixture("mutual-tls.wasm").adapt_component(is_component); + let server_addr: SocketAddr = "127.0.0.1:0".parse().expect("localhost parses"); let incoming = AddrIncoming::bind(&server_addr).expect("bind"); let bound_port = incoming.local_addr().port(); @@ -261,4 +261,4 @@ async fn client_certs_work() -> TestResult { std::env::remove_var("SSL_CERT_FILE"); Ok(()) -} +}); diff --git a/cli/tests/integration/config_store_lookup.rs b/cli/tests/integration/config_store_lookup.rs index b6725af4..d0680896 100644 --- a/cli/tests/integration/config_store_lookup.rs +++ b/cli/tests/integration/config_store_lookup.rs @@ -1,8 +1,10 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use hyper::{body::to_bytes, StatusCode}; -#[tokio::test(flavor = "multi_thread")] -async fn json_config_store_lookup_works() -> TestResult { +viceroy_test!(json_config_store_lookup_works, |is_component| { const FASTLY_TOML: &str = r#" name = "json-config_store-lookup" description = "json config_store lookup test" @@ -13,8 +15,8 @@ async fn json_config_store_lookup_works() -> TestResult { format = "json" "#; - // let resp = Test::using_fixture("config_store-lookup.wasm") let resp = Test::using_fixture("config-store-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -27,10 +29,9 @@ async fn json_config_store_lookup_works() -> TestResult { .is_empty()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn inline_toml_config_store_lookup_works() -> TestResult { +viceroy_test!(inline_toml_config_store_lookup_works, |is_component| { const FASTLY_TOML: &str = r#" name = "inline-toml-config_store-lookup" description = "inline toml config_store lookup test" @@ -44,6 +45,7 @@ async fn inline_toml_config_store_lookup_works() -> TestResult { "#; let resp = Test::using_fixture("config-store-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -56,10 +58,9 @@ async fn inline_toml_config_store_lookup_works() -> TestResult { .is_empty()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn missing_config_store_works() -> TestResult { +viceroy_test!(missing_config_store_works, |is_component| { const FASTLY_TOML: &str = r#" name = "missing-config_store-config" description = "missing config_store test" @@ -67,6 +68,7 @@ async fn missing_config_store_works() -> TestResult { "#; let resp = Test::using_fixture("config-store-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -74,4 +76,4 @@ async fn missing_config_store_works() -> TestResult { assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); Ok(()) -} +}); diff --git a/cli/tests/integration/device_detection_lookup.rs b/cli/tests/integration/device_detection_lookup.rs index e06895ba..08938e95 100644 --- a/cli/tests/integration/device_detection_lookup.rs +++ b/cli/tests/integration/device_detection_lookup.rs @@ -1,8 +1,10 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use hyper::{body::to_bytes, StatusCode}; -#[tokio::test(flavor = "multi_thread")] -async fn json_device_detection_lookup_works() -> TestResult { +viceroy_test!(json_device_detection_lookup_works, |is_component| { const FASTLY_TOML: &str = r#" name = "json-device-detection-lookup" description = "json device detection lookup test" @@ -15,6 +17,7 @@ async fn json_device_detection_lookup_works() -> TestResult { "#; let resp = Test::using_fixture("device-detection-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -27,10 +30,9 @@ async fn json_device_detection_lookup_works() -> TestResult { .is_empty()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn inline_toml_device_detection_lookup_works() -> TestResult { +viceroy_test!(inline_toml_device_detection_lookup_works, |is_component| { const FASTLY_TOML: &str = r#" name = "inline-toml-device-detection-lookup" description = "inline toml device detection lookup test" @@ -51,6 +53,7 @@ async fn inline_toml_device_detection_lookup_works() -> TestResult { "#; let resp = Test::using_fixture("device-detection-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -63,4 +66,4 @@ async fn inline_toml_device_detection_lookup_works() -> TestResult { .is_empty()); Ok(()) -} +}); diff --git a/cli/tests/integration/dictionary_lookup.rs b/cli/tests/integration/dictionary_lookup.rs index 35e8f2ba..90044dbb 100644 --- a/cli/tests/integration/dictionary_lookup.rs +++ b/cli/tests/integration/dictionary_lookup.rs @@ -1,8 +1,10 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use hyper::{body::to_bytes, StatusCode}; -#[tokio::test(flavor = "multi_thread")] -async fn json_dictionary_lookup_works() -> TestResult { +viceroy_test!(json_dictionary_lookup_works, |is_component| { const FASTLY_TOML: &str = r#" name = "json-dictionary-lookup" description = "json dictionary lookup test" @@ -16,6 +18,7 @@ async fn json_dictionary_lookup_works() -> TestResult { "#; let resp = Test::using_fixture("dictionary-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -28,10 +31,9 @@ async fn json_dictionary_lookup_works() -> TestResult { .is_empty()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn inline_toml_dictionary_lookup_works() -> TestResult { +viceroy_test!(inline_toml_dictionary_lookup_works, |is_component| { const FASTLY_TOML: &str = r#" name = "inline-toml-dictionary-lookup" description = "inline toml dictionary lookup test" @@ -47,6 +49,7 @@ async fn inline_toml_dictionary_lookup_works() -> TestResult { "#; let resp = Test::using_fixture("dictionary-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -59,10 +62,9 @@ async fn inline_toml_dictionary_lookup_works() -> TestResult { .is_empty()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn missing_dictionary_works() -> TestResult { +viceroy_test!(missing_dictionary_works, |is_component| { const FASTLY_TOML: &str = r#" name = "missing-dictionary-config" description = "missing dictionary test" @@ -70,6 +72,7 @@ async fn missing_dictionary_works() -> TestResult { "#; let resp = Test::using_fixture("dictionary-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -77,4 +80,4 @@ async fn missing_dictionary_works() -> TestResult { assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); Ok(()) -} +}); diff --git a/cli/tests/integration/downstream_req.rs b/cli/tests/integration/downstream_req.rs index d6ce355d..1c2e2425 100644 --- a/cli/tests/integration/downstream_req.rs +++ b/cli/tests/integration/downstream_req.rs @@ -1,18 +1,21 @@ use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::{Request, StatusCode}, }; -#[tokio::test(flavor = "multi_thread")] -async fn downstream_request_works() -> TestResult { +viceroy_test!(downstream_request_works, |is_component| { let req = Request::get("/") .header("Accept", "text/html") .header("X-Custom-Test", "abcdef") .body("Hello, world!")?; let resp = Test::using_fixture("downstream-req.wasm") + .adapt_component(is_component) .against(req) .await?; assert_eq!(resp.status(), StatusCode::OK); Ok(()) -} +}); diff --git a/cli/tests/integration/edge_rate_limiting.rs b/cli/tests/integration/edge_rate_limiting.rs index 4a9e40de..544998c3 100644 --- a/cli/tests/integration/edge_rate_limiting.rs +++ b/cli/tests/integration/edge_rate_limiting.rs @@ -1,15 +1,18 @@ //! Tests related to HTTP request and response bodies. use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::StatusCode, }; -#[tokio::test(flavor = "multi_thread")] -async fn check_hostcalls_implemented() -> TestResult { +viceroy_test!(check_hostcalls_implemented, |is_component| { let resp = Test::using_fixture("edge-rate-limiting.wasm") + .adapt_component(is_component) .against_empty() .await?; assert_eq!(resp.status(), StatusCode::OK); Ok(()) -} +}); diff --git a/cli/tests/integration/geolocation_lookup.rs b/cli/tests/integration/geolocation_lookup.rs index 0d5597f6..cf4d73cb 100644 --- a/cli/tests/integration/geolocation_lookup.rs +++ b/cli/tests/integration/geolocation_lookup.rs @@ -1,8 +1,10 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use hyper::{body::to_bytes, StatusCode}; -#[tokio::test(flavor = "multi_thread")] -async fn json_geolocation_lookup_works() -> TestResult { +viceroy_test!(json_geolocation_lookup_works, |is_component| { const FASTLY_TOML: &str = r#" name = "json-geolocation-lookup" description = "json geolocation lookup test" @@ -16,6 +18,7 @@ async fn json_geolocation_lookup_works() -> TestResult { "#; let resp = Test::using_fixture("geolocation-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -28,10 +31,9 @@ async fn json_geolocation_lookup_works() -> TestResult { .is_empty()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn inline_toml_geolocation_lookup_works() -> TestResult { +viceroy_test!(inline_toml_geolocation_lookup_works, |is_component| { const FASTLY_TOML: &str = r#" name = "inline-toml-geolocation-lookup" description = "inline toml geolocation lookup test" @@ -83,6 +85,7 @@ async fn inline_toml_geolocation_lookup_works() -> TestResult { "#; let resp = Test::using_fixture("geolocation-lookup.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -95,28 +98,31 @@ async fn inline_toml_geolocation_lookup_works() -> TestResult { .is_empty()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn default_configuration_geolocation_lookup_works() -> TestResult { - const FASTLY_TOML: &str = r#" +viceroy_test!( + default_configuration_geolocation_lookup_works, + |is_component| { + const FASTLY_TOML: &str = r#" name = "default-config-geolocation-lookup" description = "default config geolocation lookup test" authors = ["Test User "] language = "rust" "#; - let resp = Test::using_fixture("geolocation-lookup-default.wasm") - .using_fastly_toml(FASTLY_TOML)? - .against_empty() - .await?; + let resp = Test::using_fixture("geolocation-lookup-default.wasm") + .adapt_component(is_component) + .using_fastly_toml(FASTLY_TOML)? + .against_empty() + .await?; - assert_eq!(resp.status(), StatusCode::OK); - assert!(to_bytes(resp.into_body()) - .await - .expect("can read body") - .to_vec() - .is_empty()); + assert_eq!(resp.status(), StatusCode::OK); + assert!(to_bytes(resp.into_body()) + .await + .expect("can read body") + .to_vec() + .is_empty()); - Ok(()) -} + Ok(()) + } +); diff --git a/cli/tests/integration/grpc.rs b/cli/tests/integration/grpc.rs index 5ca6e7ea..da3dc39f 100644 --- a/cli/tests/integration/grpc.rs +++ b/cli/tests/integration/grpc.rs @@ -1,13 +1,15 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use hyper::http::response; use hyper::server::conn::AddrIncoming; use hyper::service::{make_service_fn, service_fn}; use hyper::{Request, Server, StatusCode}; use std::net::SocketAddr; -#[tokio::test(flavor = "multi_thread")] -async fn grpc_creates_h2_connection() -> TestResult { - let test = Test::using_fixture("grpc.wasm"); +viceroy_test!(grpc_creates_h2_connection, |is_component| { + let test = Test::using_fixture("grpc.wasm").adapt_component(is_component); let server_addr: SocketAddr = "127.0.0.1:0".parse().expect("localhost parses"); let incoming = AddrIncoming::bind(&server_addr).expect("bind"); let bound_port = incoming.local_addr().port(); @@ -39,4 +41,4 @@ async fn grpc_creates_h2_connection() -> TestResult { // assert_eq!(resp.into_body().read_into_string().await?, "Hello!"); Ok(()) -} +}); diff --git a/cli/tests/integration/http_semantics.rs b/cli/tests/integration/http_semantics.rs index ead4cd87..6fcc4afb 100644 --- a/cli/tests/integration/http_semantics.rs +++ b/cli/tests/integration/http_semantics.rs @@ -1,14 +1,17 @@ //! Tests related to HTTP semantics (e.g. framing headers, status codes). use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::{header, Request, Response, StatusCode}, }; -#[tokio::test(flavor = "multi_thread")] -async fn framing_headers_are_overridden() -> TestResult { +viceroy_test!(framing_headers_are_overridden, |is_component| { // Set up the test harness let test = Test::using_fixture("bad-framing-headers.wasm") + .adapt_component(is_component) // The "TheOrigin" backend checks framing headers on the request and then echos its body. .backend("TheOrigin", "/", None, |req| { assert!(!req.headers().contains_key(header::TRANSFER_ENCODING)); @@ -34,12 +37,12 @@ async fn framing_headers_are_overridden() -> TestResult { ); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn content_length_is_computed_correctly() -> TestResult { +viceroy_test!(content_length_is_computed_correctly, |is_component| { // Set up the test harness let test = Test::using_fixture("content-length.wasm") + .adapt_component(is_component) // The "TheOrigin" backend supplies a fixed-size body. .backend("TheOrigin", "/", None, |_| { Response::new(Vec::from(&b"ABCDEFGHIJKLMNOPQRST"[..])) @@ -59,4 +62,4 @@ async fn content_length_is_computed_correctly() -> TestResult { assert_eq!(resp_body, "ABCD12345xyzEFGHIJKLMNOPQRST"); Ok(()) -} +}); diff --git a/cli/tests/integration/kv_store.rs b/cli/tests/integration/kv_store.rs index f535b98e..85dc2d2a 100644 --- a/cli/tests/integration/kv_store.rs +++ b/cli/tests/integration/kv_store.rs @@ -1,8 +1,10 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use hyper::{body::to_bytes, StatusCode}; -#[tokio::test(flavor = "multi_thread")] -async fn kv_store() -> TestResult { +viceroy_test!(kv_store, |is_component| { const FASTLY_TOML: &str = r#" name = "kv-store-test" description = "kv store test" @@ -14,6 +16,7 @@ async fn kv_store() -> TestResult { "#; let resp = Test::using_fixture("kv_store.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -26,10 +29,9 @@ async fn kv_store() -> TestResult { .is_empty()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn object_stores_backward_compat() -> TestResult { +viceroy_test!(object_stores_backward_compat, |is_component| { // Previously the "kv_stores" key was named "object_stores" and // the "file" key was named "path". This test ensures that both // still work. @@ -44,6 +46,7 @@ async fn object_stores_backward_compat() -> TestResult { "#; let resp = Test::using_fixture("kv_store.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -56,9 +59,9 @@ async fn object_stores_backward_compat() -> TestResult { .is_empty()); Ok(()) -} -#[tokio::test(flavor = "multi_thread")] -async fn object_store_backward_compat() -> TestResult { +}); + +viceroy_test!(object_store_backward_compat, |is_component| { // Previously the "object_stores" key was named "object_store" and // the "file" key was named "path". This test ensures that both // still work. @@ -73,6 +76,7 @@ async fn object_store_backward_compat() -> TestResult { "#; let resp = Test::using_fixture("kv_store.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -85,10 +89,9 @@ async fn object_store_backward_compat() -> TestResult { .is_empty()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn kv_store_bad_configs() -> TestResult { +viceroy_test!(kv_store_bad_configs, |is_component| { const BAD_1_FASTLY_TOML: &str = r#" name = "kv-store-test" description = "kv store test" @@ -97,7 +100,10 @@ async fn kv_store_bad_configs() -> TestResult { [local_server] kv_stores.store_one = [{key = 3, data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_1_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm") + .adapt_component(is_component) + .using_fastly_toml(BAD_1_FASTLY_TOML) + { Err(e) => assert_eq!( "invalid configuration for 'store_one': The `key` value for an object is not a string.", &e.to_string() @@ -113,7 +119,9 @@ async fn kv_store_bad_configs() -> TestResult { [local_server] kv_stores.store_one = [{key = "first", data = 3}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_2_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm") + .adapt_component(is_component) + .using_fastly_toml(BAD_2_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': The `data` value for the object `first` is not a string.", &e.to_string()), _ => panic!(), } @@ -126,7 +134,7 @@ async fn kv_store_bad_configs() -> TestResult { [local_server] kv_stores.store_one = [{key = "first", data = "This is some data", file = "../test-fixtures/data/kv-store.txt"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_3_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_3_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': The `file` and `data` keys for the object `first` are set. Only one can be used.", &e.to_string()), _ => panic!(), } @@ -139,7 +147,7 @@ async fn kv_store_bad_configs() -> TestResult { [local_server] kv_stores.store_one = [{key = "first", file = 3}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_4_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_4_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': The `file` value for the object `first` is not a string.", &e.to_string()), _ => panic!(), } @@ -164,7 +172,10 @@ async fn kv_store_bad_configs() -> TestResult { #[cfg(target_os = "windows")] let invalid_path_message = "invalid configuration for 'store_one': The system cannot find the path specified. (os error 3)"; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_5_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm") + .adapt_component(is_component) + .using_fastly_toml(BAD_5_FASTLY_TOML) + { Err(e) => assert_eq!(invalid_path_message, &e.to_string()), _ => panic!(), } @@ -177,7 +188,7 @@ async fn kv_store_bad_configs() -> TestResult { [local_server] kv_stores.store_one = [{key = "first"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_6_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_6_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': The `file` or `data` key for the object `first` is not set. One must be used.", &e.to_string()), _ => panic!(), } @@ -190,7 +201,7 @@ async fn kv_store_bad_configs() -> TestResult { [local_server] kv_stores.store_one = [{data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_7_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_7_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': The `key` key for an object is not set. It must be used.", &e.to_string()), _ => panic!(), } @@ -203,7 +214,7 @@ async fn kv_store_bad_configs() -> TestResult { [local_server] kv_stores.store_one = "lol lmao" "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_8_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_8_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': There is no array of objects for the given store.", &e.to_string()), _ => panic!(), } @@ -216,16 +227,15 @@ async fn kv_store_bad_configs() -> TestResult { [local_server] kv_stores.store_one = ["This is some data"] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_9_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_9_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': There is an object in the given store that is not a table of keys.", &e.to_string()), _ => panic!(), } Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn kv_store_bad_key_values() -> TestResult { +viceroy_test!(kv_store_bad_key_values, |is_component| { const BAD_1_FASTLY_TOML: &str = r#" name = "kv-store-test" description = "kv store test" @@ -234,7 +244,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_1_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_1_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot be empty.", &e.to_string()), _ => panic!(), } @@ -247,7 +257,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,keeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeEEEEEEEEEEEEEEEEEEEEEEEEEEEEEeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeey", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_2_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_2_FASTLY_TOML) { Err(e) => assert_eq!( "invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot be over 1024 bytes in size.", &e.to_string() @@ -263,7 +273,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = ".well-known/acme-challenge/wheeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_3_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_3_FASTLY_TOML) { Err(e) => assert_eq!( "invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot start with `.well-known/acme-challenge`.", &e.to_string() @@ -279,7 +289,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = ".", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_4_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_4_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot be named `.`.", &e.to_string()), _ => panic!(), } @@ -292,7 +302,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "..", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_5_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_5_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot be named `..`.", &e.to_string()), _ => panic!(), } @@ -305,7 +315,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "carriage\rreturn", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_6_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_6_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot contain a `\r`.", &e.to_string()), _ => panic!(), } @@ -318,7 +328,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "newlines\nin\nthis\neconomy?", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_7_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_7_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot contain a `\n`.", &e.to_string()), _ => panic!(), } @@ -331,7 +341,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "howdy[", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_8_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_8_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot contain a `[`.", &e.to_string()), _ => panic!(), } @@ -344,7 +354,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "hello]", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_9_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_9_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot contain a `]`.", &e.to_string()), _ => panic!(), } @@ -357,7 +367,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "yoohoo*", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_10_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_10_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot contain a `*`.", &e.to_string()), _ => panic!(), } @@ -370,7 +380,7 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "hey?", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_11_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_11_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot contain a `?`.", &e.to_string()), _ => panic!(), } @@ -383,10 +393,10 @@ async fn kv_store_bad_key_values() -> TestResult { [local_server] kv_stores.store_one = [{key = "ello ello#", data = "This is some data"}] "#; - match Test::using_fixture("kv_store.wasm").using_fastly_toml(BAD_12_FASTLY_TOML) { + match Test::using_fixture("kv_store.wasm").adapt_component(is_component).using_fastly_toml(BAD_12_FASTLY_TOML) { Err(e) => assert_eq!("invalid configuration for 'store_one': Invalid `key` value used: Keys for objects cannot contain a `#`.", &e.to_string()), _ => panic!(), } Ok(()) -} +}); diff --git a/cli/tests/integration/logging.rs b/cli/tests/integration/logging.rs index 192ae3af..3af888dd 100644 --- a/cli/tests/integration/logging.rs +++ b/cli/tests/integration/logging.rs @@ -1,5 +1,8 @@ use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::StatusCode, std::{ io::{self, Write}, @@ -20,10 +23,10 @@ impl Write for LogWriter { } } -#[tokio::test(flavor = "multi_thread")] -async fn logging_works() -> TestResult { +viceroy_test!(logging_works, |is_component| { let log_writer = Arc::new(Mutex::new(LogWriter(Vec::new()))); let resp = Test::using_fixture("logging.wasm") + .adapt_component(is_component) .capture_logs(log_writer.clone()) .log_stderr() .log_stdout() @@ -61,4 +64,4 @@ async fn logging_works() -> TestResult { assert_eq!(read_log_line(), "stderr :: on each write\n"); Ok(()) -} +}); diff --git a/cli/tests/integration/secret_store.rs b/cli/tests/integration/secret_store.rs index b6d8900f..25ef674a 100644 --- a/cli/tests/integration/secret_store.rs +++ b/cli/tests/integration/secret_store.rs @@ -1,10 +1,12 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use hyper::{body::to_bytes, StatusCode}; use viceroy_lib::config::FastlyConfig; use viceroy_lib::error::{FastlyConfigError, SecretStoreConfigError}; -#[tokio::test(flavor = "multi_thread")] -async fn secret_store_works() -> TestResult { +viceroy_test!(secret_store_works, |is_component| { const FASTLY_TOML: &str = r#" name = "secret-store" description = "secret store test" @@ -15,6 +17,7 @@ async fn secret_store_works() -> TestResult { "#; let resp = Test::using_fixture("secret-store.wasm") + .adapt_component(is_component) .using_fastly_toml(FASTLY_TOML)? .against_empty() .await?; @@ -27,7 +30,7 @@ async fn secret_store_works() -> TestResult { .is_empty()); Ok(()) -} +}); fn bad_config_test(toml_fragment: &str) -> Result { let toml = format!( diff --git a/cli/tests/integration/sending_response.rs b/cli/tests/integration/sending_response.rs index 35d775fd..ca147d17 100644 --- a/cli/tests/integration/sending_response.rs +++ b/cli/tests/integration/sending_response.rs @@ -1,38 +1,43 @@ //! Tests related to sending HTTP responses downstream. use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::{ body::{to_bytes, HttpBody}, StatusCode, }, }; -/// Use the `teapot-status` fixture to check that responses can be sent downstream by the guest. -/// -/// `teapot-status.wasm` will create a [`418 I'm a teapot`][tea] response, per [RFC2324][rfc]. This -/// status code is used to clearly indicate that a response came from the guest program. -/// -/// [rfc]: https://tools.ietf.org/html/rfc2324 -/// [tea]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 -#[tokio::test(flavor = "multi_thread")] -async fn responses_can_be_sent_downstream() -> TestResult { +// Use the `teapot-status` fixture to check that responses can be sent downstream by the guest. +// +// `teapot-status.wasm` will create a [`418 I'm a teapot`][tea] response, per [RFC2324][rfc]. This +// status code is used to clearly indicate that a response came from the guest program. +// +// [rfc]: https://tools.ietf.org/html/rfc2324 +// [tea]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 +viceroy_test!(responses_can_be_sent_downstream, |is_component| { let resp = Test::using_fixture("teapot-status.wasm") + .adapt_component(is_component) .against_empty() .await?; assert_eq!(resp.status(), StatusCode::IM_A_TEAPOT); Ok(()) -} +}); -/// Run a program that does nothing, to check that an empty response is sent downstream by default. -/// -/// `noop.wasm` is an empty guest program. This exists to show that if no response is sent -/// downstream by the guest, a [`200 OK`][ok] response will be sent. -/// -/// [ok]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200 -#[tokio::test(flavor = "multi_thread")] -async fn empty_ok_response_by_default() -> TestResult { - let resp = Test::using_fixture("noop.wasm").against_empty().await?; +// Run a program that does nothing, to check that an empty response is sent downstream by default. +// +// `noop.wasm` is an empty guest program. This exists to show that if no response is sent +// downstream by the guest, a [`200 OK`][ok] response will be sent. +// +// [ok]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200 +viceroy_test!(empty_ok_response_by_default, |is_component| { + let resp = Test::using_fixture("noop.wasm") + .adapt_component(is_component) + .against_empty() + .await?; assert_eq!(resp.status(), StatusCode::OK); assert!(to_bytes(resp.into_body()) @@ -42,23 +47,25 @@ async fn empty_ok_response_by_default() -> TestResult { .is_empty()); Ok(()) -} +}); -/// Run a program that panics, to check that a [`500 Internal Server Error`][err] response is sent -/// downstream. -/// -/// [err]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500 -#[tokio::test(flavor = "multi_thread")] -async fn five_hundred_when_guest_panics() -> TestResult { - let resp = Test::using_fixture("panic.wasm").against_empty().await?; +// Run a program that panics, to check that a [`500 Internal Server Error`][err] response is sent +// downstream. +// +// [err]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500 +viceroy_test!(five_hundred_when_guest_panics, |is_component| { + let resp = Test::using_fixture("panic.wasm") + .adapt_component(is_component) + .against_empty() + .await?; assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); Ok(()) -} +}); -/// Test that gradually writing to a streaming body works. -#[tokio::test(flavor = "multi_thread")] -async fn responses_can_be_streamed_downstream() -> TestResult { +// Test that gradually writing to a streaming body works. +viceroy_test!(responses_can_be_streamed_downstream, |is_component| { let mut resp = Test::using_fixture("streaming-response.wasm") + .adapt_component(is_component) .via_hyper() .against_empty() .await?; @@ -85,4 +92,4 @@ async fn responses_can_be_streamed_downstream() -> TestResult { assert_eq!(i, 1000); Ok(()) -} +}); diff --git a/cli/tests/integration/sleep.rs b/cli/tests/integration/sleep.rs index 4c49c591..cf58819d 100644 --- a/cli/tests/integration/sleep.rs +++ b/cli/tests/integration/sleep.rs @@ -1,12 +1,18 @@ -use crate::common::{Test, TestResult}; +use crate::{ + common::{Test, TestResult}, + viceroy_test, +}; use hyper::{body::to_bytes, StatusCode}; -/// Run a program that only sleeps. This exercises async functionality in wasi. -/// Check that an empty response is sent downstream by default. -/// -/// `sleep.wasm` is a guest program which sleeps for 100 milliseconds,then returns. -#[tokio::test(flavor = "multi_thread")] -async fn empty_ok_response_by_default_after_sleep() -> TestResult { - let resp = Test::using_fixture("sleep.wasm").against_empty().await?; + +// Run a program that only sleeps. This exercises async functionality in wasi. +// Check that an empty response is sent downstream by default. +// +// `sleep.wasm` is a guest program which sleeps for 100 milliseconds,then returns. +viceroy_test!(empty_ok_response_by_default_after_sleep, |is_component| { + let resp = Test::using_fixture("sleep.wasm") + .adapt_component(is_component) + .against_empty() + .await?; assert_eq!(resp.status(), StatusCode::OK); assert!(to_bytes(resp.into_body()) @@ -16,4 +22,4 @@ async fn empty_ok_response_by_default_after_sleep() -> TestResult { .is_empty()); Ok(()) -} +}); diff --git a/cli/tests/integration/upstream.rs b/cli/tests/integration/upstream.rs index bb638d59..1cb56723 100644 --- a/cli/tests/integration/upstream.rs +++ b/cli/tests/integration/upstream.rs @@ -1,19 +1,22 @@ use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::{ header::{self, HeaderValue}, Request, Response, StatusCode, }, }; -#[tokio::test(flavor = "multi_thread")] -async fn upstream_sync() -> TestResult { +viceroy_test!(upstream_sync, |is_component| { //////////////////////////////////////////////////////////////////////////////////// // Setup //////////////////////////////////////////////////////////////////////////////////// // Set up the test harness let test = Test::using_fixture("upstream.wasm") + .adapt_component(is_component) // The "origin" backend simply echos the request body .backend("origin", "/", None, |req| { let body = req.into_body(); @@ -120,12 +123,12 @@ async fn upstream_sync() -> TestResult { assert!(resp.status().is_server_error()); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn override_host_works() -> TestResult { +viceroy_test!(override_host_works, |is_component| { // Set up the test harness let test = Test::using_fixture("upstream.wasm") + .adapt_component(is_component) .backend("override-host", "/", Some("otherhost.com"), |req| { assert_eq!( req.headers().get(header::HOST), @@ -148,12 +151,12 @@ async fn override_host_works() -> TestResult { assert_eq!(resp.status(), StatusCode::OK); Ok(()) -} +}); -/// Test that we can transparently gunzip responses when required. -#[tokio::test(flavor = "multi_thread")] -async fn transparent_gunzip() -> TestResult { +// Test that we can transparently gunzip responses when required. +viceroy_test!(transparent_gunzip, |is_component| { let resp = Test::using_fixture("gzipped-response.wasm") + .adapt_component(is_component) .backend("echo", "/", None, |mut req| { let mut response_builder = Response::builder(); @@ -179,4 +182,4 @@ async fn transparent_gunzip() -> TestResult { ); Ok(()) -} +}); diff --git a/cli/tests/integration/upstream_async.rs b/cli/tests/integration/upstream_async.rs index bb21442a..2acd3f82 100644 --- a/cli/tests/integration/upstream_async.rs +++ b/cli/tests/integration/upstream_async.rs @@ -3,12 +3,14 @@ use std::sync::Arc; use tokio::sync::Semaphore; use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::{Response, StatusCode}, }; -#[tokio::test(flavor = "multi_thread")] -async fn upstream_async_methods() -> TestResult { +viceroy_test!(upstream_async_methods, |is_component| { // Set up two backends that share a semaphore that starts with zero permits. `backend1` must // take a semaphore permit and then "forget" it before returning its response. `backend2` adds a // permit to the semaphore and promptly returns. This relationship allows the test fixtures to @@ -17,6 +19,7 @@ async fn upstream_async_methods() -> TestResult { let sema_backend1 = Arc::new(Semaphore::new(0)); let sema_backend2 = sema_backend1.clone(); let test = Test::using_fixture("upstream-async.wasm") + .adapt_component(is_component) .async_backend("backend1", "/", None, move |_| { let sema_backend1 = sema_backend1.clone(); Box::new(async move { @@ -46,4 +49,4 @@ async fn upstream_async_methods() -> TestResult { let resp = test.against_empty().await?; assert_eq!(resp.status(), StatusCode::OK); Ok(()) -} +}); diff --git a/cli/tests/integration/upstream_dynamic.rs b/cli/tests/integration/upstream_dynamic.rs index 757f840b..eb8b4715 100644 --- a/cli/tests/integration/upstream_dynamic.rs +++ b/cli/tests/integration/upstream_dynamic.rs @@ -1,19 +1,22 @@ use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::{ header::{self, HeaderValue}, Request, Response, StatusCode, }, }; -#[tokio::test(flavor = "multi_thread")] -async fn upstream_sync() -> TestResult { +viceroy_test!(upstream_sync, |is_component| { //////////////////////////////////////////////////////////////////////////////////// // Setup //////////////////////////////////////////////////////////////////////////////////// // Set up the test harness let test = Test::using_fixture("upstream-dynamic.wasm") + .adapt_component(is_component) // The "origin" backend simply echos the request body .backend("origin", "/", None, |req| { let body = req.into_body(); @@ -62,12 +65,12 @@ async fn upstream_sync() -> TestResult { ); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn override_host_works() -> TestResult { +viceroy_test!(override_host_works, |is_component| { // Set up the test harness let test = Test::using_fixture("upstream-dynamic.wasm") + .adapt_component(is_component) .backend( "override-host", "/", @@ -99,12 +102,12 @@ async fn override_host_works() -> TestResult { assert_eq!(resp.status(), StatusCode::OK); Ok(()) -} +}); -#[tokio::test(flavor = "multi_thread")] -async fn duplication_errors_right() -> TestResult { +viceroy_test!(duplication_errors_right, |is_component| { // Set up the test harness let test = Test::using_fixture("upstream-dynamic.wasm") + .adapt_component(is_component) .backend("static", "/", None, |_| Response::new(vec![])) .await; // Make sure the backends are started so we can know where to direct the request @@ -137,4 +140,4 @@ async fn duplication_errors_right() -> TestResult { assert_eq!(resp.status(), StatusCode::CONFLICT); Ok(()) -} +}); diff --git a/cli/tests/integration/upstream_streaming.rs b/cli/tests/integration/upstream_streaming.rs index f274950a..11cbb64e 100644 --- a/cli/tests/integration/upstream_streaming.rs +++ b/cli/tests/integration/upstream_streaming.rs @@ -1,13 +1,16 @@ use { - crate::common::{Test, TestResult}, + crate::{ + common::{Test, TestResult}, + viceroy_test, + }, hyper::{body::HttpBody, Response, StatusCode}, }; -/// Test that guests can stream a body into an upstream request. -#[tokio::test(flavor = "multi_thread")] -async fn upstream_streaming() -> TestResult { +// Test that guests can stream a body into an upstream request. +viceroy_test!(upstream_streaming, |is_component| { // Set up the test harness let test = Test::using_fixture("upstream-streaming.wasm") + .adapt_component(is_component) // The "origin" backend simply echos the request body .backend("origin", "/", None, |req| Response::new(req.into_body())) .await; @@ -32,4 +35,4 @@ async fn upstream_streaming() -> TestResult { assert_eq!(i, 1000); Ok(()) -} +}); diff --git a/crates/adapter/src/fastly/core.rs b/crates/adapter/src/fastly/core.rs index 28124247..9982e4bc 100644 --- a/crates/adapter/src/fastly/core.rs +++ b/crates/adapter/src/fastly/core.rs @@ -1232,13 +1232,26 @@ pub mod fastly_http_req { nwritten: *mut usize, ) -> FastlyStatus { let name = unsafe { slice::from_raw_parts(name, name_len) }; - alloc_result_opt!(value, value_max_len, nwritten, { - fastly::api::http_req::header_value_get( - req_handle, - name, - u64::try_from(value_max_len).trapping_unwrap(), - ) - }) + with_buffer!( + value, + value_max_len, + { + fastly::api::http_req::header_value_get( + req_handle, + name, + u64::try_from(value_max_len).trapping_unwrap(), + ) + }, + |res| { + let res = + handle_buffer_len!(res, nwritten).ok_or(FastlyStatus::INVALID_ARGUMENT)?; + unsafe { + *nwritten = res.len(); + } + + std::mem::forget(res); + } + ) } #[export_name = "fastly_http_req#header_remove"] @@ -1336,7 +1349,7 @@ pub mod fastly_http_req { Err((detail, e)) => { unsafe { *error_detail = detail - .unwrap_or_else(|| http_req::SendErrorDetailTag::Ok.into()) + .unwrap_or_else(|| http_req::SendErrorDetailTag::Uninitialized.into()) .into(); *resp_handle_out = INVALID_HANDLE; *resp_body_handle_out = INVALID_HANDLE; @@ -1546,7 +1559,7 @@ pub mod fastly_http_req { Err((detail, e)) => { unsafe { *error_detail = detail - .unwrap_or_else(|| http_req::SendErrorDetailTag::Ok.into()) + .unwrap_or_else(|| http_req::SendErrorDetailTag::Uninitialized.into()) .into(); *is_done_out = 0; *resp_handle_out = INVALID_HANDLE; @@ -1604,7 +1617,7 @@ pub mod fastly_http_req { Err((detail, e)) => { unsafe { *error_detail = detail - .unwrap_or_else(|| http_req::SendErrorDetailTag::Ok.into()) + .unwrap_or_else(|| http_req::SendErrorDetailTag::Uninitialized.into()) .into(); *resp_handle_out = INVALID_HANDLE; *resp_body_handle_out = INVALID_HANDLE; @@ -1634,7 +1647,7 @@ pub mod fastly_http_req { Err((detail, e)) => { unsafe { *error_detail = detail - .unwrap_or_else(|| http_req::SendErrorDetailTag::Ok.into()) + .unwrap_or_else(|| http_req::SendErrorDetailTag::Uninitialized.into()) .into(); *resp_handle_out = INVALID_HANDLE; *resp_body_handle_out = INVALID_HANDLE; @@ -1759,13 +1772,26 @@ pub mod fastly_http_resp { nwritten: *mut usize, ) -> FastlyStatus { let name = unsafe { slice::from_raw_parts(name, name_len) }; - alloc_result_opt!(value, value_max_len, nwritten, { - http_resp::header_value_get( - resp_handle, - name, - u64::try_from(value_max_len).trapping_unwrap(), - ) - }) + with_buffer!( + value, + value_max_len, + { + http_resp::header_value_get( + resp_handle, + name, + u64::try_from(value_max_len).trapping_unwrap(), + ) + }, + |res| { + let res = + handle_buffer_len!(res, nwritten).ok_or(FastlyStatus::INVALID_ARGUMENT)?; + unsafe { + *nwritten = res.len(); + } + + std::mem::forget(res); + } + ) } #[export_name = "fastly_http_resp#header_values_get"] @@ -2028,7 +2054,7 @@ pub mod fastly_device_detection { nwritten_out: *mut usize, ) -> FastlyStatus { let user_agent = unsafe { slice::from_raw_parts(user_agent, user_agent_max_len) }; - alloc_result!(buf, buf_len, nwritten_out, { + alloc_result_opt!(buf, buf_len, nwritten_out, { device_detection::lookup(user_agent, u64::try_from(buf_len).trapping_unwrap()) }) } @@ -2177,7 +2203,7 @@ pub mod fastly_kv_store { *kv_store_handle_out = INVALID_HANDLE; } - FastlyStatus::NONE + FastlyStatus::INVALID_ARGUMENT } Ok(Some(res)) => { unsafe { @@ -2659,7 +2685,7 @@ pub mod fastly_async_io { unsafe { *done_index_out = u32::MAX; } - FastlyStatus::NONE + FastlyStatus::OK } Err(e) => e.into(), } diff --git a/lib/data/viceroy-component-adapter.wasm b/lib/data/viceroy-component-adapter.wasm index d642012d..65194a2a 100755 Binary files a/lib/data/viceroy-component-adapter.wasm and b/lib/data/viceroy-component-adapter.wasm differ diff --git a/lib/src/component/config_store.rs b/lib/src/component/config_store.rs index 439079a1..d807c901 100644 --- a/lib/src/component/config_store.rs +++ b/lib/src/component/config_store.rs @@ -1,6 +1,6 @@ use { super::fastly::api::{config_store, types}, - crate::{error, session::Session}, + crate::session::Session, }; #[async_trait::async_trait] @@ -25,11 +25,7 @@ impl config_store::Host for Session { }; if item.len() > usize::try_from(max_len).unwrap() { - return Err(error::Error::BufferLengthError { - buf: "item_out", - len: "item_max_len", - } - .into()); + return Err(types::Error::BufferLen(u64::try_from(item.len()).unwrap())); } Ok(Some(item.clone())) diff --git a/lib/src/component/device_detection.rs b/lib/src/component/device_detection.rs new file mode 100644 index 00000000..f7fa7efc --- /dev/null +++ b/lib/src/component/device_detection.rs @@ -0,0 +1,25 @@ +use { + super::fastly::api::{device_detection, types}, + crate::session::Session, +}; + +#[async_trait::async_trait] +impl device_detection::Host for Session { + async fn lookup( + &mut self, + user_agent: String, + max_len: u64, + ) -> Result, types::Error> { + if let Some(result) = self.device_detection_lookup(&user_agent) { + if result.len() > max_len as usize { + return Err(types::Error::BufferLen( + u64::try_from(result.len()).unwrap_or(0), + )); + } + + Ok(Some(result)) + } else { + Ok(None) + } + } +} diff --git a/lib/src/component/dictionary.rs b/lib/src/component/dictionary.rs index 41205303..9dfbc5ca 100644 --- a/lib/src/component/dictionary.rs +++ b/lib/src/component/dictionary.rs @@ -1,6 +1,6 @@ use { super::fastly::api::{dictionary, types}, - crate::{error, session::Session}, + crate::session::Session, }; #[async_trait::async_trait] @@ -25,11 +25,7 @@ impl dictionary::Host for Session { }; if item.len() > usize::try_from(max_len).unwrap() { - return Err(error::Error::BufferLengthError { - buf: "item_out", - len: "item_max_len", - } - .into()); + return Err(types::Error::BufferLen(u64::try_from(item.len()).unwrap())); } Ok(Some(item.clone())) diff --git a/lib/src/component/erl.rs b/lib/src/component/erl.rs new file mode 100644 index 00000000..9667197e --- /dev/null +++ b/lib/src/component/erl.rs @@ -0,0 +1,60 @@ +use { + super::fastly::api::{erl, types}, + crate::session::Session, +}; + +#[async_trait::async_trait] +impl erl::Host for Session { + async fn check_rate( + &mut self, + _rc: String, + _entry: String, + _delta: u32, + _window: u32, + _limit: u32, + _pb: String, + _ttl: u32, + ) -> Result { + Ok(0) + } + + async fn ratecounter_increment( + &mut self, + _rc: String, + _entry: String, + _delta: u32, + ) -> Result<(), types::Error> { + Ok(()) + } + + async fn ratecounter_lookup_rate( + &mut self, + _rc: String, + _entry: String, + _window: u32, + ) -> Result { + Ok(0) + } + + async fn ratecounter_lookup_count( + &mut self, + _rc: String, + _entry: String, + _duration: u32, + ) -> Result { + Ok(0) + } + + async fn penaltybox_add( + &mut self, + _pb: String, + _entry: String, + _ttl: u32, + ) -> Result<(), types::Error> { + Ok(()) + } + + async fn penaltybox_has(&mut self, _pb: String, _entry: String) -> Result { + Ok(0) + } +} diff --git a/lib/src/component/http_req.rs b/lib/src/component/http_req.rs index 0589c5fc..a477428b 100644 --- a/lib/src/component/http_req.rs +++ b/lib/src/component/http_req.rs @@ -384,7 +384,7 @@ impl http_req::Host for Session { let req = Request::from_parts(req_parts, req_body); let backend = self .backend(&backend_name) - .ok_or(types::Error::from(types::Error::UnknownError))?; + .ok_or_else(|| Error::UnknownBackend(backend_name))?; // synchronously send the request let resp = upstream::send_request(req, backend, self.tls_config()).await?; @@ -637,16 +637,20 @@ impl http_req::Host for Session { options: http_types::BackendConfigOptions, config: http_types::DynamicBackendConfig, ) -> Result<(), types::Error> { + if options.contains(http_types::BackendConfigOptions::RESERVED) { + return Err(types::Error::InvalidArgument); + } + let name = prefix.as_str(); let origin_name = target.as_str(); let override_host = if options.contains(http_types::BackendConfigOptions::HOST_OVERRIDE) { if config.host_override.is_empty() { - return Err(types::Error::InvalidArgument.into()); + return Err(types::Error::InvalidArgument); } if config.host_override.len() > 1024 { - return Err(types::Error::InvalidArgument.into()); + return Err(types::Error::InvalidArgument); } Some(HeaderValue::from_bytes(config.host_override.as_bytes())?) @@ -654,10 +658,25 @@ impl http_req::Host for Session { None }; - let scheme = if options.contains(http_types::BackendConfigOptions::USE_SSL) { - "https" + let use_ssl = options.contains(http_types::BackendConfigOptions::USE_SSL); + let scheme = if use_ssl { "https" } else { "http" }; + + let ca_certs = if use_ssl && options.contains(http_types::BackendConfigOptions::CA_CERT) { + if config.ca_cert.is_empty() { + return Err(types::Error::InvalidArgument); + } + + if config.ca_cert.len() > (64 * 1024) { + return Err(types::Error::InvalidArgument); + } + + let mut byte_cursor = std::io::Cursor::new(config.ca_cert.as_bytes()); + rustls_pemfile::certs(&mut byte_cursor)? + .drain(..) + .map(rustls::Certificate) + .collect() } else { - "http" + vec![] }; let mut cert_host = if options.contains(http_types::BackendConfigOptions::CERT_HOSTNAME) { @@ -675,12 +694,10 @@ impl http_req::Host for Session { }; let use_sni = if options.contains(http_types::BackendConfigOptions::SNI_HOSTNAME) { - if config.sni_hostname.len() > 1024 { - return Err(types::Error::InvalidArgument.into()); - } - if config.sni_hostname.is_empty() { false + } else if config.sni_hostname.len() > 1024 { + return Err(types::Error::InvalidArgument.into()); } else { if let Some(cert_host) = &cert_host { if cert_host != &config.sni_hostname { @@ -727,7 +744,7 @@ impl http_req::Host for Session { None }; - let grpc = false; + let grpc = options.contains(http_types::BackendConfigOptions::GRPC); let new_backend = Backend { uri: Uri::builder() @@ -740,7 +757,7 @@ impl http_req::Host for Session { use_sni, grpc, client_cert, - ca_certs: Vec::new(), + ca_certs, }; if !self.add_backend(name, new_backend) { @@ -789,10 +806,26 @@ impl http_req::Host for Session { async fn original_header_names_get( &mut self, - _max_len: u64, - _cursor: u32, + max_len: u64, + cursor: u32, ) -> Result, Option)>, types::Error> { - Err(Error::NotAvailable("Client Compliance Region").into()) + let headers = self.downstream_original_headers(); + let (buf, next) = write_values( + headers.keys(), + b'\0', + usize::try_from(max_len).unwrap(), + cursor, + ) + .map_err(|needed| types::Error::BufferLen(u64::try_from(needed).unwrap_or(0)))?; + + // At this point we know that the buffer being empty will also mean that there are no + // remaining entries to read. + if buf.is_empty() { + debug_assert!(next.is_none()); + Ok(None) + } else { + Ok(Some((buf, next))) + } } async fn original_header_count(&mut self) -> Result { diff --git a/lib/src/component/mod.rs b/lib/src/component/mod.rs index 833a0295..56c5a6e1 100644 --- a/lib/src/component/mod.rs +++ b/lib/src/component/mod.rs @@ -44,7 +44,9 @@ pub fn link_host_functions(linker: &mut component::Linker) -> anyh fastly::api::async_io::add_to_linker(linker, |x| x.session())?; fastly::api::backend::add_to_linker(linker, |x| x.session())?; fastly::api::cache::add_to_linker(linker, |x| x.session())?; + fastly::api::device_detection::add_to_linker(linker, |x| x.session())?; fastly::api::dictionary::add_to_linker(linker, |x| x.session())?; + fastly::api::erl::add_to_linker(linker, |x| x.session())?; fastly::api::geo::add_to_linker(linker, |x| x.session())?; fastly::api::http_body::add_to_linker(linker, |x| x.session())?; fastly::api::http_req::add_to_linker(linker, |x| x.session())?; @@ -65,7 +67,9 @@ pub mod async_io; pub mod backend; pub mod cache; pub mod config_store; +pub mod device_detection; pub mod dictionary; +pub mod erl; pub mod error; pub mod geo; pub mod headers; diff --git a/lib/wit/deps/fastly/compute.wit b/lib/wit/deps/fastly/compute.wit index 1f865a33..3a47e8f5 100644 --- a/lib/wit/deps/fastly/compute.wit +++ b/lib/wit/deps/fastly/compute.wit @@ -580,7 +580,7 @@ interface geo { interface device-detection { use types.{error}; - lookup: func(user-agent: string, max-len: u64) -> result; + lookup: func(user-agent: string, max-len: u64) -> result, error>; } /*