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)]