Skip to content

Commit

Permalink
add e2e tests for php, python, swift, oci, longevity
Browse files Browse the repository at this point in the history
  • Loading branch information
rajatjindal committed Mar 2, 2023
1 parent 0d48649 commit a0d365f
Show file tree
Hide file tree
Showing 20 changed files with 580 additions and 107 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/code-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
use-tool-cache: true

- name: Cargo Tarpaulin
run: cargo tarpaulin --follow-exec --skip-clean -t 6000 --out xml --features openssl/vendored,default,e2e-tests
run: cargo tarpaulin --follow-exec --skip-clean -t 6000 --out xml --features openssl/vendored,default,fermyon-platform
env:
RUST_LOG: spin=trace

Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ E2E_TESTS_DOCKERFILE ?= e2e-tests.Dockerfile
MYSQL_IMAGE ?= mysql:8.0.22
REDIS_IMAGE ?= redis:7.0.8-alpine3.17
POSTGRES_IMAGE ?= postgres:14.7-alpine

REGISTRY_IMAGE ?= registry:2
## overrides for aarch64
ifneq ($(ARCH),x86_64)
MYSQL_IMAGE = arm64v8/mysql:8.0.32
REDIS_IMAGE = arm64v8/redis:6.0-alpine3.17
POSTGRES_IMAGE = arm64v8/postgres:14.7
REGISTRY_IMAGE = arm64v8/registry:2
E2E_TESTS_DOCKERFILE = e2e-tests-aarch64.Dockerfile
endif

Expand Down
2 changes: 1 addition & 1 deletion crates/e2e-testing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ reqwest = { version = "0.11", features = ["blocking"] }
nix = "0.26.1"
url = "2.2.2"
derive_builder = "0.12.0"
hyper-tls = "0.5.0"
hyper-tls = "0.5.0"
133 changes: 70 additions & 63 deletions crates/e2e-testing/src/asserts.rs
Original file line number Diff line number Diff line change
@@ -1,71 +1,78 @@
use anyhow::Result;
use hyper::client::HttpConnector;
use hyper::{body, Body, Client, Request, Response};
use hyper_tls::HttpsConnector;
use std::str;
use std::fmt;

pub async fn assert_status(url: &str, expected: u16) -> Result<()> {
let resp = make_request("GET", url, "").await?;
let status = resp.status();

let response = body::to_bytes(resp.into_body()).await.unwrap().to_vec();
let actual_body = str::from_utf8(&response).unwrap().to_string();

assert_eq!(status, expected, "{}", actual_body);

Ok(())
}

pub async fn assert_http_response(
url: &str,
expected: u16,
expected_headers: &[(&str, &str)],
expected_body: Option<&str>,
) -> Result<()> {
let res = make_request("GET", url, "").await?;

let status = res.status();
assert_eq!(expected, status.as_u16());

let headers = res.headers();
for (k, v) in expected_headers {
assert_eq!(
&headers
.get(k.to_string())
.unwrap_or_else(|| panic!("cannot find header {}", k))
.to_str()?,
v
)
}

if let Some(expected_body_str) = expected_body {
let response = body::to_bytes(res.into_body()).await.unwrap().to_vec();
let actual_body = str::from_utf8(&response).unwrap().to_string();
assert_eq!(expected_body_str, actual_body);
}

Ok(())
#[macro_export]
macro_rules! ensure {
($cond:expr $(,)?) => {{
use anyhow::ensure;
ensure!($cond, None);
}};
($cond:expr, $($arg:tt)+) => {{
use anyhow::ensure;
ensure!($cond, None);
}};
}

pub async fn create_request(method: &str, url: &str, body: &str) -> Result<Request<Body>> {
let req = Request::builder()
.method(method)
.uri(url)
.body(Body::from(body.to_string()))
.expect("request builder");

Ok(req)
#[macro_export]
macro_rules! ensure_eq {
($left:expr, $right:expr $(,)?) => {{
match (&$left, &$right) {
(left_val, right_val) => {
use anyhow::ensure;
use $crate::asserts::error_msg;
ensure!(*left_val == *right_val, error_msg("==", &*left_val, &*right_val, None));
}
}
}};
($left:expr, $right:expr, $($arg:tt)+) => {{
match (&$left, &$right) {
(left_val, right_val) => {
use anyhow::ensure;
use $crate::asserts::error_msg;
ensure!(*left_val == *right_val, error_msg("==", &*left_val, &*right_val, Some(format_args!($($arg)+))))
}
}
}};
}

pub fn create_client() -> Client<HttpsConnector<HttpConnector>> {
let connector = HttpsConnector::new();
Client::builder().build::<_, hyper::Body>(connector)
#[macro_export]
macro_rules! ensure_ne {
($left:expr, $right:expr $(,)?) => {{
match (&$left, &$right) {
(left_val, right_val) => {
use anyhow::ensure;
use $crate::asserts::error_msg;
ensure!(*left_val != *right_val, error_msg("!=", &*left_val, &*right_val, None));
}
}
}};
($left:expr, $right:expr, $($arg:tt)+) => {{
match (&$left, &$right) {
(left_val, right_val) => {
use anyhow::ensure;
use $crate::asserts::error_msg;
ensure!(*left_val != *right_val, error_msg("!=", &*left_val, &*right_val, Some(format_args!($($arg)+))))
}
}
}};
}

pub async fn make_request(method: &str, path: &str, body: &str) -> Result<Response<Body>> {
let c = create_client();
let req = create_request(method, path, body);

let resp = c.request(req.await?).await.unwrap();
Ok(resp)
pub fn error_msg<T, U>(op: &str, left: &T, right: &U, args: Option<fmt::Arguments<'_>>) -> String
where
T: fmt::Debug + ?Sized,
U: fmt::Debug + ?Sized,
{
match args {
Some(args) => format!(
r#"assertion failed: `(left {} right)`
left: `{:?}`,
right: `{:?}`: {}"#,
op, left, right, args
),
None => format!(
r#"assertion failed: `(left {} right)`
left: `{:?}`,
right: `{:?}`"#,
op, left, right,
),
}
}
78 changes: 78 additions & 0 deletions crates/e2e-testing/src/http_asserts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use crate::ensure_eq;
use anyhow::Result;
use hyper::client::HttpConnector;
use hyper::{body, Body, Client, Request, Response};
use hyper_tls::HttpsConnector;
use std::str;

pub async fn assert_status(url: &str, expected: u16) -> Result<()> {
let resp = make_request("GET", url, "").await?;
let status = resp.status();

let response = body::to_bytes(resp.into_body()).await.unwrap().to_vec();
let actual_body = str::from_utf8(&response).unwrap().to_string();

ensure_eq!(status, expected, "{}", actual_body);

Ok(())
}

pub async fn assert_http_response(
url: &str,
expected: u16,
expected_headers: &[(&str, &str)],
expected_body: Option<&str>,
) -> Result<()> {
let res = make_request("GET", url, "").await?;
let status = res.status();
ensure_eq!(expected, status, "{}", "unexpected http status code");

let headers = res.headers();
for (k, v) in expected_headers {
ensure_eq!(
&headers
.get(k.to_string())
.unwrap_or_else(|| panic!("cannot find header {}", k))
.to_str()?,
v,
"{}",
"expected header not found",
);
}

if let Some(expected_body_str) = expected_body {
let response = body::to_bytes(res.into_body()).await.unwrap().to_vec();
let actual_body = str::from_utf8(&response).unwrap().to_string();
ensure_eq!(
expected_body_str,
actual_body,
"{}",
"body assertion failed"
);
}

Ok(())
}

pub async fn create_request(method: &str, url: &str, body: &str) -> Result<Request<Body>> {
let req = Request::builder()
.method(method)
.uri(url)
.body(Body::from(body.to_string()))
.expect("request builder");

Ok(req)
}

pub fn create_client() -> Client<HttpsConnector<HttpConnector>> {
let connector = HttpsConnector::new();
Client::builder().build::<_, hyper::Body>(connector)
}

pub async fn make_request(method: &str, path: &str, body: &str) -> Result<Response<Body>> {
let c = create_client();
let req = create_request(method, path, body);

let resp = c.request(req.await?).await.unwrap();
Ok(resp)
}
1 change: 1 addition & 0 deletions crates/e2e-testing/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod asserts;
pub mod controller;
pub mod http_asserts;
pub mod metadata_extractor;
pub mod spin;
pub mod spin_controller;
Expand Down
34 changes: 34 additions & 0 deletions crates/e2e-testing/src/spin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,33 @@ pub fn install_plugins(plugins: Vec<&str>) -> Result<Output> {
Ok(output)
}

pub fn registry_push(appname: &str, registry_app_url: &str) -> Result<Output> {
let appdir = appdir(appname);
utils::run(
vec!["spin", "registry", "push", registry_app_url, "--insecure"],
Some(&appdir),
None,
)
}

// use docker login until https:/fermyon/spin/issues/1211
pub fn registry_login(registry_url: &str, username: &str, password: &str) -> Result<Output> {
utils::run(
vec![
"spin",
"registry",
"login",
"-u",
username,
"-p",
password,
registry_url,
],
None,
None,
)
}

pub fn build_app(appname: &str) -> Result<Output> {
let appdir = appdir(appname);
utils::run(vec!["spin", "build"], Some(&appdir), None)
Expand Down Expand Up @@ -88,3 +115,10 @@ pub async fn stop_app_process(process: &mut tokio::process::Child) -> Result<(),
Err(e) => Err(anyhow::Error::msg(e)),
}
}

pub fn version() -> Result<String> {
match utils::run(vec!["spin", "--version"], None, None) {
Ok(output) => Ok(format!("{:#?}", std::str::from_utf8(&output.stdout)?)),
Err(err) => Err(err),
}
}
10 changes: 9 additions & 1 deletion crates/e2e-testing/src/testcase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ pub struct TestCase {

/// assertions to run once the app is running
pub assertions: ChecksFunc,

/// registry app url where app is pushed and run from
#[builder(default)]
pub push_to_registry: Option<String>,
}

impl TestCase {
Expand Down Expand Up @@ -118,6 +122,11 @@ impl TestCase {
// run spin build
controller.build_app(&appname).context("building app")?;

//push to registry if url provided
if let Some(registry_app_url) = &self.push_to_registry {
spin::registry_push(&appname, registry_app_url.as_str())?;
}

// run `spin up` (or `spin deploy` for cloud).
// `AppInstance` has some basic info about the running app like base url, routes (only for cloud) etc.
let deploy_args = self.deploy_args.iter().map(|s| s as &str).collect();
Expand All @@ -144,7 +153,6 @@ impl TestCase {
e
),
}

assertions_result
}
}
3 changes: 1 addition & 2 deletions crates/e2e-testing/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ pub async fn wait_tcp(url: &str, process: &mut tokio::process::Child, target: &s

match TcpStream::connect(&url).await {
Ok(_) => break,
Err(e) => {
println!("connect {} error {}, retry {}", &url, e, wait_count);
Err(_) => {
wait_count += 1;
sleep(Duration::from_secs(1)).await;
}
Expand Down
30 changes: 18 additions & 12 deletions e2e-tests-aarch64.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ARG BUILD_SPIN=false
ARG SPIN_VERSION=canary

WORKDIR /root
RUN apt-get update && apt-get install -y wget sudo xz-utils gcc git pkg-config redis
RUN apt-get update && apt-get install -y wget sudo xz-utils gcc git pkg-config redis clang libicu-dev docker.io

# nodejs
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
Expand Down Expand Up @@ -44,27 +44,33 @@ RUN url="https://static.rust-lang.org/rustup/dist/aarch64-unknown-linux-gnu/rust
rustc --version; \
rustup target add wasm32-wasi;

# swift
RUN wget https:/swiftwasm/swift/releases/download/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu20.04_aarch64.tar.gz && \
tar -xf swift-wasm-5.8-SNAPSHOT-2023-02-24-a-ubuntu20.04_aarch64.tar.gz
ENV PATH="$PATH:/root/swift-wasm-5.8-SNAPSHOT-2023-02-24-a/usr/bin"

## check versions
RUN tinygo version; \
go version; \
zig version; \
rustc --version; \
node --version;
node --version; \
swift --version;

## spin
RUN wget https:/fermyon/spin/releases/download/${SPIN_VERSION}/spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \
tar -xvf spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \
ls -ltr && \
mv spin /usr/local/bin/spin;

WORKDIR /e2e-tests
COPY . .

# spin
RUN if [ "${BUILD_SPIN}" != "true" ]; then \
wget https:/fermyon/spin/releases/download/${SPIN_VERSION}/spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \
tar -xvf spin-${SPIN_VERSION}-linux-aarch64.tar.gz && \
ls -ltr && \
mv spin /usr/local/bin/spin; \
else \
cargo build --release && \
cp target/release/spin /usr/local/bin/spin; \
RUN if [ "${BUILD_SPIN}" == "true" ]; then \
cargo build --release && \
cp target/release/spin /usr/local/bin/spin; \
fi

RUN spin --version

CMD cargo test spinup_tests --features new-e2e-tests --no-fail-fast -- --nocapture
CMD cargo test spinup_tests --features e2e-tests --no-fail-fast -- --nocapture
Loading

0 comments on commit a0d365f

Please sign in to comment.