Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add e2e tests for php, swift, oci, longevity #1215

Merged
merged 1 commit into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ 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
134 changes: 70 additions & 64 deletions crates/e2e-testing/src/asserts.rs
Original file line number Diff line number Diff line change
@@ -1,72 +1,78 @@
use anyhow::Result;
use hyper::client::HttpConnector;
use hyper::{body, Body, Client, Method, 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(Method::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(())
#[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 assert_http_response(
url: &str,
method: Method,
body: &str,
expected: u16,
expected_headers: &[(&str, &str)],
expected_body: Option<&str>,
) -> Result<()> {
let res = make_request(method, url, body).await?;

let status = res.status();
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(())
}

pub async fn create_request(method: Method, 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: Method, 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,
),
}
}
73 changes: 73 additions & 0 deletions crates/e2e-testing/src/http_asserts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use crate::ensure_eq;
use anyhow::Result;
use hyper::client::HttpConnector;
use hyper::{body, Body, Client, Method, Request, Response};
use hyper_tls::HttpsConnector;
use std::str;

pub async fn assert_status(url: &str, expected: u16) -> Result<()> {
let resp = make_request(Method::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,
method: Method,
body: &str,
expected: u16,
expected_headers: &[(&str, &str)],
expected_body: Option<&str>,
) -> Result<()> {
let res = make_request(method, url, body).await?;

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

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
)
}

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);
}

Ok(())
}

pub async fn create_request(method: Method, 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: Method, 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 @@ -88,3 +88,37 @@ pub async fn stop_app_process(process: &mut tokio::process::Child) -> Result<(),
Err(e) => Err(anyhow::Error::msg(e)),
}
}

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 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),
}
}
9 changes: 9 additions & 0 deletions 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 Down
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
9 changes: 9 additions & 0 deletions e2e-tests-docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,20 @@ services:
- "6379:6379"
restart: always

registry:
image: ${REGISTRY_IMAGE:-registry:2}
ports:
- "5000:5000"
restart: always
environment:
- REGISTRY_HTTP_SECRET=secret

e2e-tests:
depends_on:
- mysql
- redis
- postgres
- registry
image: spin-e2e-tests
entrypoint: cargo test spinup_tests --features e2e-tests --no-fail-fast -- --nocapture
volumes:
Expand Down
Loading