From 38b5a3812e7163abf78803294f15484320931cff Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:12:09 +0100 Subject: [PATCH] add integration tests --- crates/store_sqlite/src/tests/mod.rs | 9 +- src/integration_tests/caldav/mod.rs | 111 ++++++++++++++++++ ...ion_tests__caldav__caldav_principal-2.snap | 5 + ...ion_tests__caldav__caldav_principal-3.snap | 53 +++++++++ ...ion_tests__caldav__caldav_principal-4.snap | 53 +++++++++ ...ation_tests__caldav__caldav_principal.snap | 5 + ...egration_tests__caldav__caldav_root-2.snap | 5 + ...egration_tests__caldav__caldav_root-3.snap | 27 +++++ ...ntegration_tests__caldav__caldav_root.snap | 5 + src/integration_tests/carddav/mod.rs | 53 +++++++++ ...ration_tests__carddav__carddav_root-2.snap | 5 + ...ration_tests__carddav__carddav_root-3.snap | 27 +++++ ...egration_tests__carddav__carddav_root.snap | 5 + src/{tests.rs => integration_tests/mod.rs} | 47 +++++++- src/main.rs | 4 +- 15 files changed, 405 insertions(+), 9 deletions(-) create mode 100644 src/integration_tests/caldav/mod.rs create mode 100644 src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-2.snap create mode 100644 src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-3.snap create mode 100644 src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-4.snap create mode 100644 src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal.snap create mode 100644 src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root-2.snap create mode 100644 src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root-3.snap create mode 100644 src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root.snap create mode 100644 src/integration_tests/carddav/mod.rs create mode 100644 src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root-2.snap create mode 100644 src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root-3.snap create mode 100644 src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root.snap rename src/{tests.rs => integration_tests/mod.rs} (60%) diff --git a/crates/store_sqlite/src/tests/mod.rs b/crates/store_sqlite/src/tests/mod.rs index c175657..e597b64 100644 --- a/crates/store_sqlite/src/tests/mod.rs +++ b/crates/store_sqlite/src/tests/mod.rs @@ -2,7 +2,10 @@ use crate::{ SqliteStore, addressbook_store::SqliteAddressbookStore, calendar_store::SqliteCalendarStore, principal_store::SqlitePrincipalStore, }; -use rustical_store::auth::{AuthenticationProvider, Principal, PrincipalType}; +use rustical_store::{ + Secret, + auth::{AuthenticationProvider, Principal, PrincipalType}, +}; use sqlx::SqlitePool; use tokio::sync::OnceCell; @@ -31,6 +34,10 @@ async fn get_test_db() -> SqlitePool { ) .await .unwrap(); + principal_store + .add_app_token("user", "test".to_string(), "pass".to_string()) + .await + .unwrap(); db }) diff --git a/src/integration_tests/caldav/mod.rs b/src/integration_tests/caldav/mod.rs new file mode 100644 index 0000000..72cd78d --- /dev/null +++ b/src/integration_tests/caldav/mod.rs @@ -0,0 +1,111 @@ +use crate::integration_tests::{ResponseExtractString, get_app}; +use axum::body::Body; +use axum::extract::Request; +use headers::{Authorization, HeaderMapExt}; +use http::{HeaderValue, StatusCode}; +use rstest::rstest; +use tower::ServiceExt; + +#[rstest] +#[tokio::test] +async fn test_caldav_root( + #[from(get_app)] + #[future] + app: axum::Router, +) { + let app = app.await; + + let request_template = || { + Request::builder() + .method("PROPFIND") + .uri("/caldav") + .body(Body::empty()) + .unwrap() + }; + + // Try without authentication + let request = request_template(); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + let body = response.extract_string().await; + insta::assert_snapshot!(body); + + // Try with wrong password + let mut request = request_template(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "wrongpass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + let body = response.extract_string().await; + insta::assert_snapshot!(body); + + // Try with correct credentials + let mut request = request_template(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + + let response = app.oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::MULTI_STATUS); + let body = response.extract_string().await; + insta::assert_snapshot!(body); +} + +#[rstest] +#[tokio::test] +async fn test_caldav_principal( + #[from(get_app)] + #[future] + app: axum::Router, +) { + let app = app.await; + + let request_template = || { + Request::builder() + .method("PROPFIND") + .uri("/caldav/principal/user") + .body(Body::empty()) + .unwrap() + }; + + // Try without authentication + let request = request_template(); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + let body = response.extract_string().await; + insta::assert_snapshot!(body); + + // Try with wrong password + let mut request = request_template(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "wrongpass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + let body = response.extract_string().await; + insta::assert_snapshot!(body); + + // Try with correct credentials + let mut request = request_template(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::MULTI_STATUS); + let body = response.extract_string().await; + insta::assert_snapshot!(body); + + // Try with Depth: 1 + let mut request = request_template(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + request + .headers_mut() + .insert("Depth", HeaderValue::from_static("1")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::MULTI_STATUS); + let body = response.extract_string().await; + insta::assert_snapshot!(body); +} diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-2.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-2.snap new file mode 100644 index 0000000..64c638c --- /dev/null +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-2.snap @@ -0,0 +1,5 @@ +--- +source: src/integration_tests/caldav/mod.rs +expression: body +--- + diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-3.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-3.snap new file mode 100644 index 0000000..dfabdac --- /dev/null +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-3.snap @@ -0,0 +1,53 @@ +--- +source: src/integration_tests/caldav/mod.rs +expression: body +--- + + + + /caldav/principal/user/ + + + INDIVIDUAL + + /caldav/principal/user/ + + + /caldav/principal/user/ + + + + + + + + + + + + + + + /caldav/principal/user/ + + + + + + user + + /caldav/principal/user/ + + + + + + + + /caldav/principal/user/ + + + HTTP/1.1 200 OK + + + diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-4.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-4.snap new file mode 100644 index 0000000..dfabdac --- /dev/null +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal-4.snap @@ -0,0 +1,53 @@ +--- +source: src/integration_tests/caldav/mod.rs +expression: body +--- + + + + /caldav/principal/user/ + + + INDIVIDUAL + + /caldav/principal/user/ + + + /caldav/principal/user/ + + + + + + + + + + + + + + + /caldav/principal/user/ + + + + + + user + + /caldav/principal/user/ + + + + + + + + /caldav/principal/user/ + + + HTTP/1.1 200 OK + + + diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal.snap new file mode 100644 index 0000000..64c638c --- /dev/null +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_principal.snap @@ -0,0 +1,5 @@ +--- +source: src/integration_tests/caldav/mod.rs +expression: body +--- + diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root-2.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root-2.snap new file mode 100644 index 0000000..64c638c --- /dev/null +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root-2.snap @@ -0,0 +1,5 @@ +--- +source: src/integration_tests/caldav/mod.rs +expression: body +--- + diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root-3.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root-3.snap new file mode 100644 index 0000000..c0c14ff --- /dev/null +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root-3.snap @@ -0,0 +1,27 @@ +--- +source: src/integration_tests/caldav/mod.rs +expression: body +--- + + + + /caldav/ + + + + + + RustiCal DAV root + + /caldav/principal/user/ + + + + + + + + HTTP/1.1 200 OK + + + diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root.snap new file mode 100644 index 0000000..64c638c --- /dev/null +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__caldav_root.snap @@ -0,0 +1,5 @@ +--- +source: src/integration_tests/caldav/mod.rs +expression: body +--- + diff --git a/src/integration_tests/carddav/mod.rs b/src/integration_tests/carddav/mod.rs new file mode 100644 index 0000000..3e3af8a --- /dev/null +++ b/src/integration_tests/carddav/mod.rs @@ -0,0 +1,53 @@ +use crate::integration_tests::{ResponseExtractString, get_app}; +use axum::body::Body; +use axum::extract::Request; +use headers::{Authorization, HeaderMapExt}; +use http::StatusCode; +use rstest::rstest; +use tower::ServiceExt; + +#[rstest] +#[tokio::test] +async fn test_carddav_root( + #[from(get_app)] + #[future] + app: axum::Router, +) { + let app = app.await; + + let request_template = || { + Request::builder() + .method("PROPFIND") + .uri("/carddav") + .body(Body::empty()) + .unwrap() + }; + + // Try without authentication + let request = request_template(); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + let body = response.extract_string().await; + insta::assert_snapshot!(body); + + // Try with wrong password + let mut request = request_template(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "wrongpass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + let body = response.extract_string().await; + insta::assert_snapshot!(body); + + // Try with correct credentials + let mut request = request_template(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + + let response = app.oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::MULTI_STATUS); + let body = response.extract_string().await; + insta::assert_snapshot!(body); +} diff --git a/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root-2.snap b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root-2.snap new file mode 100644 index 0000000..946c71e --- /dev/null +++ b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root-2.snap @@ -0,0 +1,5 @@ +--- +source: src/integration_tests/carddav/mod.rs +expression: body +--- + diff --git a/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root-3.snap b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root-3.snap new file mode 100644 index 0000000..1e52bec --- /dev/null +++ b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root-3.snap @@ -0,0 +1,27 @@ +--- +source: src/integration_tests/carddav/mod.rs +expression: body +--- + + + + /carddav/ + + + + + + RustiCal DAV root + + /carddav/principal/user/ + + + + + + + + HTTP/1.1 200 OK + + + diff --git a/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root.snap b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root.snap new file mode 100644 index 0000000..946c71e --- /dev/null +++ b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__carddav_root.snap @@ -0,0 +1,5 @@ +--- +source: src/integration_tests/carddav/mod.rs +expression: body +--- + diff --git a/src/tests.rs b/src/integration_tests/mod.rs similarity index 60% rename from src/tests.rs rename to src/integration_tests/mod.rs index 0ac889e..fb0c628 100644 --- a/src/tests.rs +++ b/src/integration_tests/mod.rs @@ -1,4 +1,6 @@ use crate::{app::make_app, config::NextcloudLoginConfig}; +use axum::extract::Request; +use axum::{body::Body, response::Response}; use rstest::rstest; use rustical_frontend::FrontendConfig; use rustical_store_sqlite::{ @@ -12,10 +14,10 @@ use rustical_store_sqlite::{ }, }; use std::sync::Arc; +use tower::ServiceExt; -#[rstest] -#[tokio::test] -async fn test_app( +#[rstest::fixture] +pub async fn get_app( #[from(get_test_calendar_store)] #[future] cal_store: SqliteCalendarStore, @@ -28,13 +30,13 @@ async fn test_app( #[from(get_test_subscription_store)] #[future] sub_store: SqliteStore, -) { +) -> axum::Router { let addr_store = Arc::new(addr_store.await); let cal_store = Arc::new(cal_store.await); let sub_store = Arc::new(sub_store.await); let principal_store = Arc::new(principal_store.await); - let _app = make_app( + make_app( addr_store, cal_store, sub_store, @@ -48,5 +50,38 @@ async fn test_app( false, true, 20, - ); + ) } + +pub trait ResponseExtractString { + #[allow(async_fn_in_trait)] + async fn extract_string(self) -> String; +} + +impl ResponseExtractString for Response { + async fn extract_string(self) -> String { + let bytes = axum::body::to_bytes(self.into_body(), usize::MAX) + .await + .unwrap(); + String::from_utf8(bytes.to_vec()).unwrap() + } +} + +#[rstest] +#[tokio::test] +async fn test_ping( + #[from(get_app)] + #[future] + app: axum::Router, +) { + let app = app.await; + + let response = app + .oneshot(Request::builder().uri("/ping").body(Body::empty()).unwrap()) + .await + .unwrap(); + assert!(response.status().is_success()); +} + +mod caldav; +mod carddav; diff --git a/src/main.rs b/src/main.rs index 8a9d0db..53e4e00 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,9 +30,9 @@ use tracing::info; mod app; mod commands; mod config; -mod setup_tracing; #[cfg(test)] -mod tests; +pub mod integration_tests; +mod setup_tracing; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)]