diff --git a/.env b/.env index b9fe4ea..7c2fa16 100644 --- a/.env +++ b/.env @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 1f917e5..3cfc38f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/README.md b/README.md index 8761250..a49367a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ web proofs by TEE

+## 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: diff --git a/client/Cargo.toml b/client/Cargo.toml index 8276576..dbdfd07 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Pluto developers "] 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" diff --git a/client/src/main.rs b/client/src/main.rs index 1c0e444..75fbf13 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -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(); @@ -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" diff --git a/proxy/src/main.rs b/proxy/src/main.rs index 73a6c43..ec6b3e2 100644 --- a/proxy/src/main.rs +++ b/proxy/src/main.rs @@ -109,8 +109,7 @@ async fn forward_request(target_url: &str) -> Result { 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))) } diff --git a/proxy/src/xclip b/proxy/src/xclip deleted file mode 100644 index e69de29..0000000 diff --git a/tests/tests.rs b/tests/tests.rs index a1accc0..be8067d 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -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 = Lazy::new(|| "3333".to_string()); +static TEST_SERVER: Lazy = 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 { + 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(xs: &[T]) -> Vec { - 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) -> 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(()) +}