diff --git a/server/src/main.rs b/server/src/main.rs index 362625cdeb..0bb52f9593 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -49,6 +49,13 @@ async fn main() -> anyhow::Result<()> { let postgres = postgres::pool().await?; let s3 = s3::client().await; + match s3.head_bucket().bucket(&CONFIG.s3_bucket).send().await { + Ok(_) => info!("s3 bucket {} OK", &CONFIG.s3_bucket), + Err(e) => { + warn!("s3 bucket {} not available: {}", &CONFIG.s3_bucket, e); + } + } + let bind_to = SocketAddr::new(CONFIG.bind_host.as_str().parse()?, CONFIG.bind_port); async fn auth( diff --git a/server/src/s3.rs b/server/src/s3.rs index e9ce9c3ce7..d51b605545 100644 --- a/server/src/s3.rs +++ b/server/src/s3.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use aws_config::BehaviorVersion; +use aws_config::{BehaviorVersion, Region, default_provider::region}; use aws_sdk_s3::{ Config, types::{CompletedMultipartUpload, CompletedPart}, @@ -19,6 +19,10 @@ pub async fn client() -> S3Client { .into_builder() .build(); + let endpoint = sdk_config.endpoint_url().unwrap(); + let region = sdk_config.region().unwrap(); + tracing::debug!(endpoint, ?region, "s3 connection"); + let s3_config = Config::from(sdk_config) .to_builder() .force_path_style(true) diff --git a/tests/src/auth.rs b/tests/src/auth.rs index c05ae22a3d..17fe163cf1 100644 --- a/tests/src/auth.rs +++ b/tests/src/auth.rs @@ -1,29 +1,41 @@ +use secrecy::ExposeSecret; use tanu::{ - check, check_eq, eyre, + check, check_eq, check_ne, eyre, http::{Client, Method, StatusCode}, }; use crate::config::CONFIG; use crate::util::*; -#[tanu::test] -async fn auth_without_token_unauthorized() -> eyre::Result<()> { +// #[tanu::test("HEAD", Method::HEAD)] +#[tanu::test("GET", Method::GET)] +#[tanu::test("PUT", Method::PUT)] +#[tanu::test("POST", Method::POST)] +#[tanu::test("DELETE", Method::DELETE)] +async fn auth_without_token_unauthorized(_: &str, method: Method) -> eyre::Result<()> { let http = Client::new(); - for method in [ - //Method::HEAD, - Method::GET, - Method::PUT, - Method::POST, - Method::DELETE, - ] { - let res = http - .request(&method, &format!("{}/api/huly/example", CONFIG.base_url)) - .send() - .await?; - check!(!res.status().is_success(), "method: {method}"); - check_eq!(StatusCode::UNAUTHORIZED, res.status(), "method: {method}"); - } + let res = http.request(&method, &path(&random_key())).send().await?; + check!(!res.status().is_success(), "method: {method}"); + check_eq!(StatusCode::UNAUTHORIZED, res.status(), "method: {method}"); + + Ok(()) +} + +// #[tanu::test("HEAD", Method::HEAD)] +#[tanu::test("GET", Method::GET)] +#[tanu::test("PUT", Method::PUT)] +#[tanu::test("POST", Method::POST)] +// #[tanu::test("DELETE", Method::DELETE)] +async fn auth_with_token(_: &str, method: Method) -> eyre::Result<()> { + let http = Client::new(); + + let res = http + .request(&method, &path(&random_key())) + .bearer_auth(CONFIG.token_valid.expose_secret()) + .send() + .await?; + check_ne!(StatusCode::UNAUTHORIZED, res.status(), "method: {method}"); Ok(()) } diff --git a/tests/src/main.rs b/tests/src/main.rs index 28931b9d7e..3796280f80 100644 --- a/tests/src/main.rs +++ b/tests/src/main.rs @@ -1,6 +1,7 @@ mod auth; mod config; mod post; +mod put; mod sanity; mod util; diff --git a/tests/src/post.rs b/tests/src/post.rs index 37dc154836..cb9d4c264c 100644 --- a/tests/src/post.rs +++ b/tests/src/post.rs @@ -1,43 +1,62 @@ -use tanu::{check, eyre, http::Client}; +use tanu::{check, check_eq, eyre, http::Client}; use crate::util::*; #[tanu::test] pub async fn post_small() -> eyre::Result<()> { + let key = random_key(); + let text = random_text(1024 * 10); + let http = Client::new(); - let res = http - .key_post(&random_key()) - .body(random_body(1024 * 1024 * 10)) - .send() - .await?; + + // create new blob + let res = http.key_post(&key).body(text.clone()).send().await?; check!(res.status().is_success()); + + // check content + let res = http.key_get(&key).send().await?; + check!(res.status().is_success()); + check_eq!(text, res.text().await?); + Ok(()) } #[tanu::test] pub async fn post_large() -> eyre::Result<()> { + let key = random_key(); + let body = random_body(1024 * 1024 * 10); // above multipart threshold + let http = Client::new(); - let res = http - .key_post(&random_key()) - .body(random_body(1024 * 1024 * 10)) // above multipart threshold - .send() - .await?; + + let res = http.key_post(&key).body(body).send().await?; check!(res.status().is_success()); + + let res = http.key_get(&key).send().await?; + check!(res.status().is_success()); + Ok(()) } #[tanu::test] pub async fn post_new_and_append() -> eyre::Result<()> { - let http = Client::new(); let key = random_key(); + let part1 = random_text(1024 * 10); + let part2 = random_text(1024 * 10); - let res = http.key_post(&key).body(random_body(1024)).send().await?; + let http = Client::new(); - check!(res.status().is_success(), "{:#?}", res); + // create new blob + let res = http.key_post(&key).body(part1.clone()).send().await?; + check!(res.status().is_success()); - let res = http.key_post(&key).body(random_body(1024)).send().await?; + // append to the existing blob + let res = http.key_post(&key).body(part2.clone()).send().await?; + check!(res.status().is_success()); - check!(res.status().is_success(), "{:#?}", res); + // check content + let res = http.key_get(&key).send().await?; + check!(res.status().is_success()); + check_eq!(format!("{}{}", part1, part2), res.text().await?); Ok(()) } diff --git a/tests/src/put.rs b/tests/src/put.rs new file mode 100644 index 0000000000..00954abe99 --- /dev/null +++ b/tests/src/put.rs @@ -0,0 +1,46 @@ +use tanu::{check, check_eq, eyre, http::Client}; + +use crate::util::*; + +#[tanu::test] +pub async fn put_new() -> eyre::Result<()> { + let key = random_key(); + let text = random_text(1024 * 10); + + let http = Client::new(); + + // create new blob + let res = http.key_put(&key).body(text.clone()).send().await?; + check!(res.status().is_success()); + + // check content + let res = http.key_get(&key).send().await?; + check!(res.status().is_success()); + check_eq!(text, res.text().await?); + + Ok(()) +} + +#[tanu::test] +pub async fn put_existing() -> eyre::Result<()> { + let key = random_key(); + let text1 = random_text(1024); + let text2 = random_text(1024 * 10); + + let http = Client::new(); + + // create new blob + let res = http.key_put(&key).body(text1.clone()).send().await?; + check!(res.status().is_success()); + + // update content + let res = http.key_put(&key).body(text2.clone()).send().await?; + check!(res.status().is_success()); + + // check content + let res = http.key_get(&key).send().await?; + check!(res.status().is_success()); + check_eq!(text2, res.text().await?); + + Ok(()) +} diff --git a/tests/src/util.rs b/tests/src/util.rs index 4abceb734e..436e417312 100644 --- a/tests/src/util.rs +++ b/tests/src/util.rs @@ -1,5 +1,5 @@ use crate::config::CONFIG; -use rand::RngCore; +use rand::{Rng, RngCore}; use reqwest::Body; use secrecy::ExposeSecret; use tanu::http::{Client, Method, RequestBuilder}; @@ -13,13 +13,14 @@ pub trait ClientExt { fn request(&self, method: &Method, path: &str) -> RequestBuilder; } -fn path(key: &str) -> String { +pub fn path(key: &str) -> String { format!("{}/api/{}/{key}", CONFIG.base_url, CONFIG.workspace) } impl ClientExt for Client { fn request(&self, method: &Method, path: &str) -> RequestBuilder { match *method { + Method::HEAD => self.head(path), Method::GET => self.get(path), Method::PUT => self.put(path), Method::POST => self.post(path), @@ -54,6 +55,20 @@ impl ClientExt for Client { } } +pub fn random_text(length: usize) -> String { + let mut rng = rand::rng(); + let charset: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 "; + + let text: String = (0..length) + .map(|_| { + let idx = rng.random_range(0..charset.len()); + charset[idx] as char + }) + .collect(); + + text +} + pub fn random_body(size: usize) -> Body { let mut rng = rand::rng(); let mut bytes = vec![0u8; size];