From 26652395b75b960535f051bf099a0f20f37e1041 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Tue, 2 Jul 2024 10:12:50 -0700 Subject: [PATCH] Run tests as components (#396) Update tests to run as both core-wasm modules and components. This also includes a number of fixes to both the component adapter and component host side implementation. --- cli/tests/integration/async_io.rs | 11 ++- cli/tests/integration/body.rs | 17 ++-- cli/tests/integration/client_certs.rs | 22 ++--- cli/tests/integration/config_store_lookup.rs | 24 ++--- .../integration/device_detection_lookup.rs | 17 ++-- cli/tests/integration/dictionary_lookup.rs | 23 ++--- cli/tests/integration/downstream_req.rs | 11 ++- cli/tests/integration/edge_rate_limiting.rs | 11 ++- cli/tests/integration/geolocation_lookup.rs | 50 ++++++----- cli/tests/integration/grpc.rs | 12 +-- cli/tests/integration/http_semantics.rs | 17 ++-- cli/tests/integration/kv_store.rs | 84 ++++++++++-------- cli/tests/integration/logging.rs | 11 ++- cli/tests/integration/secret_store.rs | 11 ++- cli/tests/integration/sending_response.rs | 73 ++++++++------- cli/tests/integration/sleep.rs | 24 +++-- cli/tests/integration/upstream.rs | 25 +++--- cli/tests/integration/upstream_async.rs | 11 ++- cli/tests/integration/upstream_dynamic.rs | 23 ++--- cli/tests/integration/upstream_streaming.rs | 13 +-- crates/adapter/src/fastly/core.rs | 68 +++++++++----- lib/data/viceroy-component-adapter.wasm | Bin 172824 -> 168845 bytes lib/src/component/config_store.rs | 8 +- lib/src/component/device_detection.rs | 25 ++++++ lib/src/component/dictionary.rs | 8 +- lib/src/component/erl.rs | 60 +++++++++++++ lib/src/component/http_req.rs | 63 +++++++++---- lib/src/component/mod.rs | 4 + lib/wit/deps/fastly/compute.wit | 2 +- 29 files changed, 470 insertions(+), 258 deletions(-) create mode 100644 lib/src/component/device_detection.rs create mode 100644 lib/src/component/erl.rs 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 d642012d2d6bb13300fd47d22e98efae90304cc3..65194a2a1c7c338eabbc351e4ba657725f4d6a43 100755 GIT binary patch delta 19468 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 OH;`Nm5sq#k&;Ku!0>(xF 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>; } /*