-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
142 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) | ||
} |