Skip to content

Commit

Permalink
integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
thor314 committed Nov 12, 2024
1 parent 75a337d commit 40b65a4
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 60 deletions.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
PROXY_ADDRESS="http://localhost:3000"
TARGET_URL="https://gist.githubusercontent.com/mattes/23e64faadb5fd4b5112f379903d2572e/raw/ddbf0a56001367467f71bda64347aa881d83533c/example.json"
PROXY_PORT="3000"
RUST_LOG="debug"
29 changes: 29 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
[package]
name = "web-proof-tee"
version = "0.1.0"
edition = "2021"

[dev-dependencies]
tokio = { version = "1.0", features = ["full", "test-util"] }
anyhow = "1.0"
reqwest = { version = "0.11", features = ["json"] }
serde_json = "1.0"
once_cell = "1.19"
rstest = "0.18"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

[workspace]
resolver = "2"
members = [
"client",
"proxy"
]

# Define workspace-wide dependency versions
[workspace.dependencies]
tokio = { version = "1.0", features = ["full"] }
anyhow = "1.0"
reqwest = { version = "0.11", features = ["json"] }
serde_json = "1.0"
once_cell = "1.19"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

[[test]]
name = "integration_tests"
path = "tests/tests.rs"
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
web proofs by TEE
</p>

## demo of client-proxy tee flow
Runs a proxied GET to an [arbitrary](https://gist.githubusercontent.com/mattes/23e64faadb5fd4b5112f379903d2572e/raw/ddbf0a56001367467f71bda64347aa881d83533c/example.json) gist json file.

See `.justfile` for commands, e.g. `just test`.

## License
Licensed under your option of either:
Expand Down
2 changes: 1 addition & 1 deletion client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ authors = ["Pluto developers <pluto.xyz>"]
description = """TEE client"""
edition = "2021"
license = "Apache2.0 OR MIT"
name = "web-proof-tee-client"
name = "client"
repository = "https://github.com/pluto/web-proof-tee"
version = "0.1.0"

Expand Down
9 changes: 2 additions & 7 deletions client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,9 @@ use std::env;

use anyhow::{Context, Result};
use dotenv::dotenv;
use serde::Deserialize;
use serde_json::Value;
use tracing::{debug, error, info, warn};

#[derive(Clone, Debug, Deserialize)]
struct Response {
_hello: String,
}

#[tokio::main]
async fn main() -> Result<()> {
dotenv().ok();
Expand Down Expand Up @@ -64,7 +59,7 @@ async fn main() -> Result<()> {
anyhow::bail!("Request failed with status {}: {}", status, text);
}

let data: Response = response.json().await.context("Failed to parse response as JSON")?;
let data: Value = response.json().await.context("Failed to parse response as JSON")?;
info!(
response = ?data,
"Successfully received and parsed response"
Expand Down
3 changes: 1 addition & 2 deletions proxy/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ async fn forward_request(target_url: &str) -> Result<impl IntoResponse> {

let status = StatusCode::from_u16(response.status().as_u16())?;
let body: Value = response.json().await?;

debug!(status = ?status, "Received response from target");
debug!("Parsed response body: {:?}", body);

Ok((status, Json(body)))
}
Empty file removed proxy/src/xclip
Empty file.
153 changes: 103 additions & 50 deletions tests/tests.rs
Original file line number Diff line number Diff line change
@@ -1,66 +1,119 @@
//! Integration tests
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![allow(unreachable_code)]
#![allow(non_snake_case)]
#![allow(clippy::clone_on_copy)]
//! Integration tests
use tracing::Level;
use tracing_subscriber::FmtSubscriber;

static INIT: std::sync::Once = std::sync::Once::new();
fn setup_test_tracing() {
INIT.call_once(|| {
let subscriber =
FmtSubscriber::builder().with_max_level(Level::INFO).with_test_writer().finish();
tracing::subscriber::set_global_default(subscriber)
.expect("setting default subscriber failed");
});
use std::time::Duration;

use anyhow::Result;
use once_cell::sync::Lazy;
use rstest::*;
use tokio::time::sleep;
use tracing::{debug, error, info, instrument, warn};
use tracing_subscriber::fmt::format::FmtSpan;

static TEST_PORT: Lazy<String> = Lazy::new(|| "3333".to_string());
static TEST_SERVER: Lazy<String> = Lazy::new(|| format!("http://localhost:{}", *TEST_PORT));

/// Helper struct to manage server lifecycle
#[derive(Debug)]
struct TestServer {
handle: tokio::process::Child,
}
use arbitrary::Arbitrary;
use rstest::{fixture, rstest};
// rstest provides features to take common context into tests, and set up small cases testing
#[derive(Clone, Debug, Eq, PartialEq, Arbitrary)]
struct Wb {
b: bool,
count: usize,

impl Drop for TestServer {
fn drop(&mut self) { let _ = self.handle.start_kill(); }
}
// context setup function to be implicitly called by `wb`
#[fixture]
fn count() -> usize { return 0usize; }
// context setup function to be implicitly called by `test_wb`
#[fixture]
fn wb(#[default(false)] b: bool, count: usize) -> Wb {
setup_test_tracing();
Wb { b, count }

impl TestServer {
async fn start() -> Result<Self> {
info!("Starting test server on port {}", *TEST_PORT);
let handle = tokio::process::Command::new("cargo")
.args(["run", "-p", "proxy", "--"])
.env("PROXY_PORT", &*TEST_PORT) // Changed from PORT to PROXY_PORT
.env("RUST_LOG", "debug")
.kill_on_drop(true)
.spawn()?;

// Wait longer for server startup
info!("Waiting for server to start up");
sleep(Duration::from_secs(2)).await; // Increased from 5ms to 2s

// Verify server is running by attempting to connect
let client = reqwest::Client::new();
let max_retries = 5;
for i in 0..max_retries {
match client.get(&*TEST_SERVER).send().await {
Ok(_) => {
info!("Server is ready!");
break;
},
Err(e) => {
if i == max_retries - 1 {
error!("Server failed to start after {} retries: {}", max_retries, e);
return Err(anyhow::anyhow!("Server failed to start"));
}
warn!("Server not ready, retrying in 1s...");
sleep(Duration::from_secs(1)).await;
},
}
}

Ok(Self { handle })
}

#[instrument]
async fn stop(mut self) -> Result<()> {
info!("Stopping test server");
self.handle.kill().await?;
Ok(())
}
}

// small-cases fuzzing
// argument wb will inherit the above function if names match; will generate 3x3 case-tests
#[rstest]
#[case(0, true, true)]
#[case(1, true, false)]
fn test_wb(wb: Wb, #[case] n: usize, #[case] b: bool, #[case] expected: bool) {
tracing::info!("wb: {wb:?}");
let wb_ = Wb { count: n, b };
assert_eq!(wb == wb_, expected); // this will fail for case_1 cases
/// Test fixture for managing server and client setup
#[derive(Debug)]
struct TestContext {
server: TestServer,
client: reqwest::Client,
}

// ex 2 - baby fuzz; will generate 2x2 test cases
#[rstest]
fn test_enumerative(#[values(0, 4)] n: usize, #[values(7, 8)] m: usize) {
assert!(n < m);
/// Initialize tracing for tests
fn init_tracing() {
let _ = tracing_subscriber::fmt()
.with_env_filter("debug")
.with_span_events(FmtSpan::CLOSE)
.with_test_writer()
.try_init();
}

// fuzz test
fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
let mut rev = vec![];
for x in xs.iter() {
rev.insert(0, x.clone())
}
rev
/// Fixture setup function
#[fixture]
async fn test_context() -> TestContext {
init_tracing();
info!("Setting up test context");
let server = TestServer::start().await.expect("Failed to start test server");
let client = reqwest::Client::new();
TestContext { server, client }
}

// fuzz, declare quickcheck on any argument implementing Arbitrary
#[quickcheck_macros::quickcheck]
fn prop(xs: Vec<u32>) -> bool { xs == reverse(&reverse(&xs)) }
#[rstest]
#[tokio::test]
async fn test_proxy_forwarding(#[future] test_context: TestContext) -> Result<()> {
let ctx = test_context.await;
let test_url = "https://gist.githubusercontent.com/mattes/23e64faadb5fd4b5112f379903d2572e/raw/ddbf0a56001367467f71bda64347aa881d83533c/example.json";

info!("Sending request through proxy to {}", test_url);
let response = ctx.client.get(&*TEST_SERVER).header("X-Target-URL", test_url).send().await?;

debug!(status = ?response.status(), "Received response");
assert!(response.status().is_success());

let body: serde_json::Value = response.json().await?;
debug!(body = ?body, "Parsed response body");
assert_eq!(body["hello"], "world");

ctx.server.stop().await?;
Ok(())
}

0 comments on commit 40b65a4

Please sign in to comment.