From faef531d91e4c3571a1fb4d770ed96b178a787f3 Mon Sep 17 00:00:00 2001 From: clowzed Date: Fri, 10 Nov 2023 23:00:04 +0300 Subject: [PATCH 1/3] Added testing for standart endpoints --- Cargo.lock | 313 +++++++++++++++++- Cargo.toml | 7 + assets/a.zip | Bin 0 -> 572 bytes src/handlers/auth.rs | 2 +- src/main.rs | 742 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 1050 insertions(+), 14 deletions(-) create mode 100644 assets/a.zip diff --git a/Cargo.lock b/Cargo.lock index efc031a..454e87f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -425,6 +425,24 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "axum-test-helper" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298f62fa902c2515c169ab0bfb56c593229f33faa01131215d58e3d4898e3aa9" +dependencies = [ + "axum", + "bytes", + "http", + "http-body", + "hyper", + "reqwest", + "serde", + "tokio", + "tower", + "tower-service", +] + [[package]] name = "axum_typed_multipart" version = "0.10.0" @@ -762,6 +780,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -1052,6 +1080,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -1226,6 +1269,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 1.9.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1379,6 +1441,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", + "h2", "http", "http-body", "httparse", @@ -1392,6 +1455,19 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -1431,6 +1507,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.0.0" @@ -1472,6 +1558,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itertools" version = "0.11.0" @@ -1647,6 +1739,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1691,6 +1793,24 @@ dependencies = [ "version_check", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -1795,6 +1915,50 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "openssl" +version = "0.10.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-float" version = "3.9.1" @@ -2201,6 +2365,47 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64 0.21.4", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.16.20" @@ -2367,6 +2572,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2548,6 +2762,29 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.188" @@ -2570,9 +2807,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -2609,6 +2846,7 @@ dependencies = [ "async_zip", "axum", "axum-extra", + "axum-test-helper", "axum_typed_multipart", "bytes", "chrono", @@ -2616,15 +2854,21 @@ dependencies = [ "entity", "envy", "futures-util", + "hyper", "jsonwebtoken", "migration", + "mime", + "reqwest", "sea-orm", "serde", + "serde_json", + "serde_urlencoded", "sha256", "thiserror", "tokio", "tokio-postgres", "tokio-util", + "tower", "tracing", "tracing-subscriber", "uuid", @@ -2825,7 +3069,7 @@ dependencies = [ "futures-util", "hashlink", "hex", - "indexmap", + "indexmap 2.0.0", "log", "memchr", "once_cell", @@ -3068,6 +3312,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -3190,6 +3455,16 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-postgres" version = "0.7.10" @@ -3360,6 +3635,15 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -3537,6 +3821,19 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.64" @@ -3663,6 +3960,16 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index bc6c532..6bc395b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,13 @@ bytes = "1.5.0" async-trait = "0.1.73" tokio-util = { version = "0.7.9", features = ["io"] } dotenv = "0.15.0" +tower = { version = "0.4.13", features = ["util"] } +serde_urlencoded = "0.7.1" +serde_json = "1.0.108" +mime = "0.3.17" +axum-test-helper = "0.3.0" +hyper = "0.14.27" +reqwest = { version = "0.11.22", features = ["json", "multipart"] } [workspace] members = [".", "entity", "migration"] diff --git a/assets/a.zip b/assets/a.zip new file mode 100644 index 0000000000000000000000000000000000000000..fa405454021a8e5629fefdf42956d1d17f3573a5 GIT binary patch literal 572 zcmWIWW@h1H00GIqv~Vy3N-#0VFeK`ShHx@4U;N{dy6v@3YH0;G10%}|W(Ec@5dhcw zy%$w44_I$zUP@|(UPei74%n1QKvSl?!)l5M&`u79x80fHTl+W7ECKS`fS4O>N^xpY zzFtW_)DXM3KB+E1jOG;4vyNPc6$Dxz{+*+AW)pXBEQ|CDPZjRf&e{^Z#>*P#?c4SD zr`&ngWgbga=chIYcXZ#+tn!m6+FK_g$fsWv+NZ44@cNne9mD4n?bgb!zV@szJZ Router { let config = config::Config::default(); let mut opt = ConnectOptions::new(&config.database_url); @@ -33,12 +31,6 @@ async fn main() { Migrator::up(&connection, None).await.unwrap(); - let addr = SocketAddr::from(([0, 0, 0, 0], config.port)); - - tracing_subscriber::fmt() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .init(); - let api_router = Router::new() .route("/health", get(|| async { StatusCode::OK })) .route("/login", post(handlers::auth::login)) @@ -64,8 +56,738 @@ async fn main() { app = app.layer(DefaultBodyLimit::max(config.max_body_limit_size.unwrap())); } + app +} + +#[tokio::main] +async fn main() { + dotenv::dotenv().ok(); + let config = config::Config::default(); + let addr = SocketAddr::from(([0, 0, 0, 0], config.port)); + + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + axum::Server::bind(&addr) - .serve(app.into_make_service()) + .serve(app().await.into_make_service()) .await .unwrap(); } + +#[cfg(test)] +mod tests { + use crate::{handlers::auth::AuthToken, services::auth::AuthCredentials}; + + use super::*; + use axum::{ + body::Body, + http::{header, HeaderName, HeaderValue, Method, Request, StatusCode}, + }; + use axum_test_helper::TestClient; + use std::io::Read; + use tower::ServiceExt; // for `oneshot` and `ready` + + async fn prepare() -> Router { + dotenv::dotenv().ok(); + //tracing_subscriber::fmt() + // .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + // .try_init() + // .ok(); + app().await + } + + #[tokio::test] + async fn health_check() { + let app = prepare().await; + + let response = app + .oneshot( + Request::builder() + .uri("/api/health") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK) + } + + #[tokio::test] + async fn registration_check() { + let app = prepare().await; + let random_username = uuid::Uuid::new_v4(); + let random_password = uuid::Uuid::new_v4(); + let body = serde_urlencoded::to_string(AuthCredentials { + username: random_username.to_string(), + password: random_password.to_string(), + }) + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/registration") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/registration") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::CONFLICT); + + let body = serde_urlencoded::to_string(AuthCredentials { + username: random_username.to_string(), + password: String::default(), + }) + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/registration") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + } + + #[tokio::test] + async fn login_check() { + let app = prepare().await; + let random_username = uuid::Uuid::new_v4(); + let random_password = uuid::Uuid::new_v4(); + let body = serde_urlencoded::to_string(AuthCredentials { + username: random_username.to_string(), + password: random_password.to_string(), + }) + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/registration") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/login") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/login") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + + assert!(serde_json::from_slice::(&body).is_ok()); + + let body = serde_urlencoded::to_string(AuthCredentials { + username: random_username.to_string(), + password: String::from("wrong"), + }) + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/login") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + } + + #[tokio::test] + async fn upload_check() { + let app = prepare().await; + let random_username = uuid::Uuid::new_v4(); + let random_password = uuid::Uuid::new_v4(); + let first_user = serde_urlencoded::to_string(AuthCredentials { + username: random_username.to_string(), + password: random_password.to_string(), + }) + .unwrap(); + + let second_user = serde_urlencoded::to_string(AuthCredentials { + username: uuid::Uuid::new_v4().to_string(), + password: uuid::Uuid::new_v4().to_string(), + }) + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/registration") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(second_user.clone())) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/registration") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(first_user.clone())) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/login") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(first_user.clone())) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + + let token = serde_json::from_slice::(&body).unwrap().token; + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/login") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(second_user.clone())) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + + let second_token = serde_json::from_slice::(&body).unwrap().token; + + let client = TestClient::new(app); + + let test_zip = std::path::PathBuf::from("./assets/a.zip"); + assert!(test_zip.exists()); + + let mut f = std::fs::File::open(&test_zip).expect("no file found"); + let metadata = std::fs::metadata(&test_zip).expect("unable to read metadata"); + let mut buffer = vec![0; metadata.len() as usize]; + f.read_exact(&mut buffer).expect("buffer overflow"); + + let form = reqwest::multipart::Form::new() + .part("archive", reqwest::multipart::Part::bytes(buffer.clone())); + + let random_subdoamain = uuid::Uuid::new_v4().to_string(); + let bearer = format!("Bearer {token}"); + let response = client + .post("/api/upload") + .multipart(form) + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&bearer).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + + let bearer = format!("Bearer {second_token}"); + let form = reqwest::multipart::Form::new() + .part("archive", reqwest::multipart::Part::bytes(buffer)); + let response = client + .post("/api/upload") + .multipart(form) + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&bearer).unwrap(), + ) + .send() + .await; + assert_eq!(response.status(), StatusCode::FORBIDDEN); + } + + #[tokio::test] + async fn teardown_check() { + let app = prepare().await; + let random_username = uuid::Uuid::new_v4(); + let random_password = uuid::Uuid::new_v4(); + let body = serde_urlencoded::to_string(AuthCredentials { + username: random_username.to_string(), + password: random_password.to_string(), + }) + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/registration") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/login") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + + let token = serde_json::from_slice::(&body).unwrap().token; + + let client = TestClient::new(app); + + let test_zip = std::path::PathBuf::from("./assets/a.zip"); + assert!(test_zip.exists()); + + let mut f = std::fs::File::open(&test_zip).expect("no file found"); + let metadata = std::fs::metadata(&test_zip).expect("unable to read metadata"); + let mut buffer = vec![0; metadata.len() as usize]; + f.read_exact(&mut buffer).expect("buffer overflow"); + + let form = reqwest::multipart::Form::new() + .part("archive", reqwest::multipart::Part::bytes(buffer)); + + let random_subdoamain = uuid::Uuid::new_v4().to_string(); + let bearer = format!("Bearer {token}"); + let response = client + .post("/api/upload") + .multipart(form) + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&bearer).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .get("/a/index.html") + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .post("/api/teardown") + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&bearer).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .get("/a/index") + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::NOT_FOUND); + } + + #[tokio::test] + async fn disable_enable_check() { + let app = prepare().await; + let random_username = uuid::Uuid::new_v4(); + let random_password = uuid::Uuid::new_v4(); + let body = serde_urlencoded::to_string(AuthCredentials { + username: random_username.to_string(), + password: random_password.to_string(), + }) + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/registration") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/login") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + + let token = serde_json::from_slice::(&body).unwrap().token; + + let client = TestClient::new(app); + + let test_zip = std::path::PathBuf::from("./assets/a.zip"); + assert!(test_zip.exists()); + + let mut f = std::fs::File::open(&test_zip).expect("no file found"); + let metadata = std::fs::metadata(&test_zip).expect("unable to read metadata"); + let mut buffer = vec![0; metadata.len() as usize]; + f.read_exact(&mut buffer).expect("buffer overflow"); + + let form = reqwest::multipart::Form::new() + .part("archive", reqwest::multipart::Part::bytes(buffer)); + + let random_subdoamain = uuid::Uuid::new_v4().to_string(); + let bearer = format!("Bearer {token}"); + let response = client + .post("/api/upload") + .multipart(form) + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&bearer).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .get("/a/index.html") + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .post("/api/disable") + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&bearer).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .get("/a/index") + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE); + + let response = client + .post("/api/enable") + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&bearer).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + } + + #[tokio::test] + async fn download_check() { + let app = prepare().await; + let random_username = uuid::Uuid::new_v4(); + let random_password = uuid::Uuid::new_v4(); + let user = serde_urlencoded::to_string(AuthCredentials { + username: random_username.to_string(), + password: random_password.to_string(), + }) + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/registration") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(user.clone())) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let response = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/login") + .header( + header::CONTENT_TYPE, + mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(), + ) + .body(Body::from(user.clone())) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + + let token = serde_json::from_slice::(&body).unwrap().token; + + let client = TestClient::new(app.clone()); + + let test_zip = std::path::PathBuf::from("./assets/a.zip"); + assert!(test_zip.exists()); + + let mut f = std::fs::File::open(&test_zip).expect("no file found"); + let metadata = std::fs::metadata(&test_zip).expect("unable to read metadata"); + let mut buffer = vec![0; metadata.len() as usize]; + f.read_exact(&mut buffer).expect("buffer overflow"); + + let form = reqwest::multipart::Form::new() + .part("archive", reqwest::multipart::Part::bytes(buffer.clone())); + + let random_subdoamain = uuid::Uuid::new_v4().to_string(); + let bearer = format!("Bearer {token}"); + let response = client + .post("/api/upload") + .multipart(form) + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&bearer).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .get("/a/index.html") + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .send() + .await; + + assert_eq!(response.status(), StatusCode::OK); + + let bearer = format!("Bearer {token}"); + + let response = app + .clone() + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri("/api/download") + .header( + HeaderName::from_static("x-subdomain"), + HeaderValue::from_str(&random_subdoamain).unwrap(), + ) + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&bearer).unwrap(), + ) + .body(Body::from(body.clone())) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + assert_eq!(body, buffer) + } +} From 2b718fbc21523239aa117136b77a3b254f0afe43 Mon Sep 17 00:00:00 2001 From: clowzed Date: Fri, 10 Nov 2023 23:08:02 +0300 Subject: [PATCH 2/3] Added test.yml for testing all pushes --- .github/workflows/tests.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..dad59d0 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,36 @@ + +name: Running Tests + +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres + + env: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + POSTGRES_DB: postgres + + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose + env: + DATABASE_URL: "postgres://postgres:postgres@localhost:5432/postgres" + PORT: 8080 + From 18cc37a535cde8a99161db8ed970de1fa92752d0 Mon Sep 17 00:00:00 2001 From: clowzed Date: Sat, 11 Nov 2023 00:15:17 +0300 Subject: [PATCH 3/3] run tests consecutively to avoid constraint violation --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dad59d0..f23814d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,7 +29,7 @@ jobs: - name: Build run: cargo build --verbose - name: Run tests - run: cargo test --verbose + run: cargo test --verbose -- --test-threads=1 env: DATABASE_URL: "postgres://postgres:postgres@localhost:5432/postgres" PORT: 8080