Registry testing - part 12
This commit is contained in:
@@ -4,17 +4,14 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::utils::{server::TestServer, webdav::GenerateTestDavResource};
|
||||
use dav_proto::schema::property::{DavProperty, WebDavProperty};
|
||||
use groupware::DavResourceName;
|
||||
use hyper::StatusCode;
|
||||
|
||||
use crate::webdav::GenerateTestDavResource;
|
||||
|
||||
use super::{DavResponse, DummyWebDavClient, WebDavTest};
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
let owner_client = test.client("bill");
|
||||
let sharee_client = test.client("john");
|
||||
pub async fn test(test: &TestServer) {
|
||||
let owner_client = test.account("bill@example.com").webdav_client();
|
||||
let sharee_client = test.account("john@example.com").webdav_client();
|
||||
|
||||
for resource_type in [
|
||||
DavResourceName::File,
|
||||
@@ -23,10 +20,16 @@ pub async fn test(test: &WebDavTest) {
|
||||
] {
|
||||
println!("Running ACL tests ({})...", resource_type.base_path());
|
||||
let is_file = resource_type == DavResourceName::File;
|
||||
let sharee_principal = format!("{}/john/", DavResourceName::Principal.base_path());
|
||||
let sharee_base_path = format!("{}/john/", resource_type.base_path());
|
||||
let owner_principal = format!("{}/bill/", DavResourceName::Principal.base_path());
|
||||
let owner_base_path = format!("{}/bill/", resource_type.base_path());
|
||||
let sharee_principal = format!(
|
||||
"{}/john%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
);
|
||||
let sharee_base_path = format!("{}/john%40example.com/", resource_type.base_path());
|
||||
let owner_principal = format!(
|
||||
"{}/bill%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
);
|
||||
let owner_base_path = format!("{}/bill%40example.com/", resource_type.base_path());
|
||||
|
||||
// Create a resource for the owner
|
||||
let owner_folder = format!("{owner_base_path}test-shared/");
|
||||
|
||||
@@ -4,15 +4,14 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::utils::server::TestServer;
|
||||
use dav_proto::Depth;
|
||||
use hyper::StatusCode;
|
||||
|
||||
use super::WebDavTest;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
pub async fn test(test: &TestServer) {
|
||||
println!("Running basic tests...");
|
||||
let john = test.client("john");
|
||||
let jane = test.client("jane");
|
||||
let john = test.account("john@example.com").webdav_client();
|
||||
let jane = test.account("jane@example.com").webdav_client();
|
||||
|
||||
// Test OPTIONS request
|
||||
john.request("OPTIONS", "/dav/file", "")
|
||||
@@ -37,18 +36,22 @@ pub async fn test(test: &WebDavTest) {
|
||||
.await
|
||||
.with_values(
|
||||
"D:multistatus.D:response.D:href",
|
||||
["/dav/card/", "/dav/card/john/"],
|
||||
["/dav/card/", "/dav/card/john%40example.com/"],
|
||||
);
|
||||
jane.request("PROPFIND", "/.well-known/caldav", "")
|
||||
.await
|
||||
.with_values(
|
||||
"D:multistatus.D:response.D:href",
|
||||
["/dav/cal/", "/dav/cal/jane/", "/dav/cal/support/"],
|
||||
[
|
||||
"/dav/cal/",
|
||||
"/dav/cal/jane%40example.com/",
|
||||
"/dav/cal/support%40example.com/",
|
||||
],
|
||||
);
|
||||
|
||||
// Test 404 responses
|
||||
jane.sync_collection(
|
||||
"/dav/cal/jane/default/",
|
||||
"/dav/cal/jane%40example.com/default/",
|
||||
"",
|
||||
Depth::Infinity,
|
||||
None,
|
||||
@@ -56,19 +59,19 @@ pub async fn test(test: &WebDavTest) {
|
||||
)
|
||||
.await;
|
||||
jane.sync_collection(
|
||||
"/dav/cal/jane/test-404/",
|
||||
"/dav/cal/jane%40example.com/test-404/",
|
||||
"",
|
||||
Depth::Infinity,
|
||||
None,
|
||||
["D:getetag"],
|
||||
)
|
||||
.await;
|
||||
jane.request("PROPFIND", "/dav/cal/jane/default/", "")
|
||||
jane.request("PROPFIND", "/dav/cal/jane%40example.com/default/", "")
|
||||
.await
|
||||
.with_status(StatusCode::MULTI_STATUS);
|
||||
jane.request(
|
||||
"REPORT",
|
||||
"/dav/cal/jane/default/",
|
||||
"/dav/cal/jane%40example.com/default/",
|
||||
concat!(
|
||||
r#"<CAL:calendar-query xmlns="DAV:" "#,
|
||||
r#"xmlns:CAL="urn:ietf:params:xml:ns:caldav"><prop><getetag />"#,
|
||||
@@ -81,7 +84,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
.with_status(StatusCode::MULTI_STATUS);
|
||||
jane.request(
|
||||
"REPORT",
|
||||
"/dav/cal/jane/test-404/",
|
||||
"/dav/cal/jane%40example.com/test-404/",
|
||||
concat!(
|
||||
r#"<CAL:calendar-query xmlns="DAV:" "#,
|
||||
r#"xmlns:CAL="urn:ietf:params:xml:ns:caldav"><prop><getetag />"#,
|
||||
@@ -92,12 +95,13 @@ pub async fn test(test: &WebDavTest) {
|
||||
)
|
||||
.await
|
||||
.with_status(StatusCode::MULTI_STATUS);
|
||||
jane.request("PROPFIND", "/dav/cal/jane/test-404/", "")
|
||||
jane.request("PROPFIND", "/dav/cal/jane%40example.com/test-404/", "")
|
||||
.await
|
||||
.with_status(StatusCode::NOT_FOUND);
|
||||
|
||||
john.delete_default_containers().await;
|
||||
jane.delete_default_containers().await;
|
||||
jane.delete_default_containers_by_account("support").await;
|
||||
jane.delete_default_containers_by_account("support@example.com")
|
||||
.await;
|
||||
test.assert_is_empty().await;
|
||||
}
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::WebDavTest;
|
||||
use crate::jmap::mail::mailbox::destroy_all_mailboxes_for_account;
|
||||
use crate::utils::server::TestServer;
|
||||
use email::cache::MessageCacheFetch;
|
||||
use hyper::StatusCode;
|
||||
use mail_parser::{DateTime, MessageParser};
|
||||
use store::write::now;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
pub async fn test(test: &TestServer) {
|
||||
println!("Running calendar e-mail alarms tests...");
|
||||
let client = test.client("john");
|
||||
let account = test.account("john@example.com");
|
||||
let client = account.webdav_client();
|
||||
client
|
||||
.request_with_headers(
|
||||
"PUT",
|
||||
"/dav/cal/john/default/its-alarming-how-charming-i-feel.ics",
|
||||
"/dav/cal/john%40example.com/default/its-alarming-how-charming-i-feel.ics",
|
||||
[("content-type", "text/calendar; charset=utf-8")],
|
||||
TEST_ALARM_1.replace(
|
||||
"$START",
|
||||
@@ -74,7 +74,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
}
|
||||
assert!(
|
||||
contents.contains(concat!(
|
||||
"/dav/cal/john/default/",
|
||||
"/dav/cal/john%40example.com/default/",
|
||||
"its-alarming-how-charming-i-feel.ics"
|
||||
)),
|
||||
"failed for {contents}"
|
||||
@@ -82,7 +82,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
}
|
||||
|
||||
client.delete_default_containers().await;
|
||||
destroy_all_mailboxes_for_account(client.account_id).await;
|
||||
test.destroy_all_mailboxes(account).await;
|
||||
test.assert_is_empty().await
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::WebDavTest;
|
||||
use crate::utils::server::TestServer;
|
||||
|
||||
use ahash::AHashSet;
|
||||
use calcard::{common::timezone::Tz, icalendar::ICalendar};
|
||||
use groupware::{
|
||||
@@ -15,10 +16,10 @@ use hyper::StatusCode;
|
||||
use store::write::serialize::rkyv_unarchive;
|
||||
use types::TimeRange;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
pub async fn test(test: &TestServer) {
|
||||
println!("Running REPORT calendar-query & free-busy-query tests...");
|
||||
let client = test.client("john");
|
||||
let cal_path = format!("{}/john/default/", DavResourceName::Cal.base_path());
|
||||
let client = test.account("john@example.com").webdav_client();
|
||||
let cal_path = format!("{}/john%40example.com/default/", DavResourceName::Cal.base_path());
|
||||
|
||||
#[allow(clippy::never_loop)]
|
||||
for (num, ics) in [
|
||||
@@ -324,7 +325,7 @@ fn roundtrip_expansion(ics: &str, ignore_errors: bool) {
|
||||
|
||||
fn rfc_file_name(num: usize) -> String {
|
||||
format!(
|
||||
"{}/john/default/abcd{num}.ics",
|
||||
"{}/john%40example.com/default/abcd{num}.ics",
|
||||
DavResourceName::Cal.base_path()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::WebDavTest;
|
||||
use crate::{
|
||||
jmap::mail::mailbox::destroy_all_mailboxes_for_account,
|
||||
webdav::{DummyWebDavClient, prop::ALL_DAV_PROPERTIES},
|
||||
utils::{server::TestServer, webdav::DummyWebDavClient},
|
||||
webdav::prop::ALL_DAV_PROPERTIES,
|
||||
};
|
||||
|
||||
use calcard::{
|
||||
common::timezone::Tz,
|
||||
icalendar::{
|
||||
@@ -16,7 +16,6 @@ use calcard::{
|
||||
ICalendarProperty, ICalendarRecurrenceRule, ICalendarWeekday,
|
||||
},
|
||||
};
|
||||
use common::{Server, auth::AccessToken};
|
||||
use dav_proto::schema::property::{CalDavProperty, DavProperty, WebDavProperty};
|
||||
use email::cache::MessageCacheFetch;
|
||||
use groupware::{
|
||||
@@ -30,23 +29,30 @@ use std::str::FromStr;
|
||||
use store::write::now;
|
||||
use types::collection::SyncCollection;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
pub async fn test(test: &TestServer) {
|
||||
println!("Running calendar scheduling tests...");
|
||||
let bill_client = test.client("bill");
|
||||
let jane_client = test.client("jane");
|
||||
let john_client = test.client("john");
|
||||
let bill = test.account("bill@example.com");
|
||||
let jane = test.account("jane@example.com");
|
||||
let john = test.account("john@example.com");
|
||||
let bill_client = bill.webdav_client();
|
||||
let jane_client = jane.webdav_client();
|
||||
let john_client = john.webdav_client();
|
||||
|
||||
// Validate hierarchy of scheduling resources
|
||||
let response = jane_client
|
||||
.propfind_with_headers("/dav/itip/jane/", ALL_DAV_PROPERTIES, [("depth", "1")])
|
||||
.propfind_with_headers(
|
||||
"/dav/itip/jane%40example.com/",
|
||||
ALL_DAV_PROPERTIES,
|
||||
[("depth", "1")],
|
||||
)
|
||||
.await;
|
||||
let properties = response
|
||||
.with_hrefs([
|
||||
"/dav/itip/jane/",
|
||||
"/dav/itip/jane/inbox/",
|
||||
"/dav/itip/jane/outbox/",
|
||||
"/dav/itip/jane%40example.com/",
|
||||
"/dav/itip/jane%40example.com/inbox/",
|
||||
"/dav/itip/jane%40example.com/outbox/",
|
||||
])
|
||||
.properties("/dav/itip/jane/inbox/");
|
||||
.properties("/dav/itip/jane%40example.com/inbox/");
|
||||
|
||||
// Validate schedule inbox properties
|
||||
properties
|
||||
@@ -56,7 +62,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
.get(DavProperty::CalDav(
|
||||
CalDavProperty::ScheduleDefaultCalendarURL,
|
||||
))
|
||||
.with_values(["D:href:/dav/cal/jane/default/"])
|
||||
.with_values(["D:href:/dav/cal/jane%40example.com/default/"])
|
||||
.with_status(StatusCode::OK);
|
||||
properties
|
||||
.get(DavProperty::WebDav(WebDavProperty::SupportedPrivilegeSet))
|
||||
@@ -101,7 +107,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
]);
|
||||
|
||||
// Validate schedule outbox properties
|
||||
let properties = response.properties("/dav/itip/jane/outbox/");
|
||||
let properties = response.properties("/dav/itip/jane%40example.com/outbox/");
|
||||
properties
|
||||
.get(DavProperty::WebDav(WebDavProperty::ResourceType))
|
||||
.with_values(["D:collection", "A:schedule-outbox"]);
|
||||
@@ -164,7 +170,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
john_client
|
||||
.request_with_headers(
|
||||
"PUT",
|
||||
"/dav/cal/john/default/itip.ics",
|
||||
"/dav/cal/john%40example.com/default/itip.ics",
|
||||
[("content-type", "text/calendar; charset=utf-8")],
|
||||
&test_itip,
|
||||
)
|
||||
@@ -174,28 +180,27 @@ pub async fn test(test: &WebDavTest) {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
|
||||
// Check that the invitation was received by Bill and Mike
|
||||
for client in [bill_client, jane_client] {
|
||||
for client in [&bill_client, &jane_client] {
|
||||
let messages = test
|
||||
.server
|
||||
.get_cached_messages(client.account_id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(messages.emails.items.len(), 1);
|
||||
let access_token = test
|
||||
.server
|
||||
.get_access_token(client.account_id)
|
||||
.await
|
||||
.unwrap();
|
||||
let events = test
|
||||
.server
|
||||
.fetch_dav_resources(&access_token, client.account_id, SyncCollection::Calendar)
|
||||
.fetch_dav_resources(
|
||||
client.account_id,
|
||||
client.account_id,
|
||||
SyncCollection::Calendar,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(events.resources.len(), 2);
|
||||
let events = test
|
||||
.server
|
||||
.fetch_dav_resources(
|
||||
&access_token,
|
||||
client.account_id,
|
||||
client.account_id,
|
||||
SyncCollection::CalendarEventNotification,
|
||||
)
|
||||
@@ -205,7 +210,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
}
|
||||
|
||||
// Validate iTIP
|
||||
let itips = fetch_and_remove_itips(jane_client).await;
|
||||
let itips = jane_client.fetch_and_remove_itips().await;
|
||||
assert_eq!(itips.len(), 1);
|
||||
let itip = itips.first().unwrap();
|
||||
assert!(
|
||||
@@ -214,7 +219,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
);
|
||||
|
||||
// Fetch added calendar entry
|
||||
let cals = fetch_icals(jane_client).await;
|
||||
let cals = jane_client.fetch_icals().await;
|
||||
assert_eq!(cals.len(), 1);
|
||||
let cal = cals.into_iter().next().unwrap();
|
||||
|
||||
@@ -252,14 +257,14 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// Make sure that the schedule has not changed
|
||||
assert_eq!(
|
||||
fetch_icals(jane_client).await[0].schedule_tag,
|
||||
jane_client.fetch_icals().await[0].schedule_tag,
|
||||
cal.schedule_tag
|
||||
);
|
||||
|
||||
// Check that John received the RSVP
|
||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
test.wait_for_tasks().await;
|
||||
let itips = fetch_and_remove_itips(john_client).await;
|
||||
let itips = john_client.fetch_and_remove_itips().await;
|
||||
assert_eq!(itips.len(), 1);
|
||||
assert!(
|
||||
itips[0].contains("METHOD:REPLY")
|
||||
@@ -267,7 +272,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
"failed for itip: {}",
|
||||
itips[0]
|
||||
);
|
||||
let cals = fetch_icals(john_client).await;
|
||||
let cals = john_client.fetch_icals().await;
|
||||
assert_eq!(cals.len(), 1);
|
||||
assert!(
|
||||
cals[0]
|
||||
@@ -290,7 +295,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
.with_status(StatusCode::NO_CONTENT);
|
||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
assert_eq!(
|
||||
fetch_and_remove_itips(john_client).await,
|
||||
john_client.fetch_and_remove_itips().await,
|
||||
Vec::<String>::new()
|
||||
);
|
||||
|
||||
@@ -300,7 +305,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
.await
|
||||
.with_status(StatusCode::NO_CONTENT);
|
||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
let itips = fetch_and_remove_itips(john_client).await;
|
||||
let itips = john_client.fetch_and_remove_itips().await;
|
||||
assert_eq!(itips.len(), 1);
|
||||
assert!(
|
||||
itips[0].contains("METHOD:REPLY")
|
||||
@@ -308,7 +313,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
"failed for itip: {}",
|
||||
itips[0]
|
||||
);
|
||||
let cals = fetch_icals(john_client).await;
|
||||
let cals = john_client.fetch_icals().await;
|
||||
assert_eq!(cals.len(), 1);
|
||||
let cal = cals.into_iter().next().unwrap();
|
||||
assert!(
|
||||
@@ -358,7 +363,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
response.contains("Lunch") && response.contains("RSVP has been recorded"),
|
||||
"failed for response: {response}"
|
||||
);
|
||||
let cals = fetch_icals(john_client).await;
|
||||
let cals = john_client.fetch_icals().await;
|
||||
assert_eq!(cals.len(), 1);
|
||||
let cal = cals.into_iter().next().unwrap();
|
||||
assert!(
|
||||
@@ -384,7 +389,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
let response = john_client
|
||||
.request_with_headers(
|
||||
"POST",
|
||||
"/dav/itip/john/outbox/",
|
||||
"/dav/itip/john%40example.com/outbox/",
|
||||
[("content-type", "text/calendar; charset=utf-8")],
|
||||
&test_outbox,
|
||||
)
|
||||
@@ -442,7 +447,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// Make sure that the schedule has changed
|
||||
assert_ne!(
|
||||
fetch_icals(john_client).await[0].schedule_tag,
|
||||
john_client.fetch_icals().await[0].schedule_tag,
|
||||
cal.schedule_tag
|
||||
);
|
||||
let main_event_href = cal.href;
|
||||
@@ -450,7 +455,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
// Check that Bill received the update
|
||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
test.wait_for_tasks().await;
|
||||
let mut itips = fetch_and_remove_itips(bill_client).await;
|
||||
let mut itips = bill_client.fetch_and_remove_itips().await;
|
||||
itips.sort_unstable_by(|a, _| {
|
||||
if a.contains("Lunch") {
|
||||
std::cmp::Ordering::Less
|
||||
@@ -469,7 +474,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
"failed for itip: {}",
|
||||
itips[1]
|
||||
);
|
||||
let cals = fetch_icals(bill_client).await;
|
||||
let cals = bill_client.fetch_icals().await;
|
||||
assert_eq!(cals.len(), 1);
|
||||
let cal = cals.into_iter().next().unwrap();
|
||||
assert!(
|
||||
@@ -480,7 +485,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
);
|
||||
let attendee_href = cal.href;
|
||||
assert_eq!(
|
||||
fetch_and_remove_itips(jane_client).await,
|
||||
jane_client.fetch_and_remove_itips().await,
|
||||
Vec::<String>::new()
|
||||
);
|
||||
|
||||
@@ -490,14 +495,14 @@ pub async fn test(test: &WebDavTest) {
|
||||
.await
|
||||
.with_status(StatusCode::NO_CONTENT);
|
||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
let itips = fetch_and_remove_itips(bill_client).await;
|
||||
let itips = bill_client.fetch_and_remove_itips().await;
|
||||
assert_eq!(itips.len(), 1);
|
||||
assert!(
|
||||
itips[0].contains("METHOD:CANCEL") && itips[0].contains("STATUS:CANCELLED"),
|
||||
"failed for itip: {}",
|
||||
itips[0]
|
||||
);
|
||||
let cals = fetch_icals(bill_client).await;
|
||||
let cals = bill_client.fetch_icals().await;
|
||||
assert_eq!(cals.len(), 1);
|
||||
let cal = cals.into_iter().next().unwrap();
|
||||
assert!(
|
||||
@@ -506,7 +511,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
cal.ical
|
||||
);
|
||||
assert_eq!(
|
||||
fetch_and_remove_itips(jane_client).await,
|
||||
jane_client.fetch_and_remove_itips().await,
|
||||
Vec::<String>::new()
|
||||
);
|
||||
|
||||
@@ -517,40 +522,43 @@ pub async fn test(test: &WebDavTest) {
|
||||
.with_status(StatusCode::NO_CONTENT);
|
||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
assert_eq!(
|
||||
fetch_and_remove_itips(john_client).await,
|
||||
john_client.fetch_and_remove_itips().await,
|
||||
Vec::<String>::new()
|
||||
);
|
||||
|
||||
for client in [bill_client, jane_client, john_client] {
|
||||
client.delete_default_containers().await;
|
||||
destroy_all_mailboxes_for_account(client.account_id).await;
|
||||
}
|
||||
for account in [bill, jane, john] {
|
||||
test.destroy_all_mailboxes(account).await;
|
||||
}
|
||||
|
||||
test.assert_is_empty().await;
|
||||
}
|
||||
|
||||
async fn fetch_and_remove_itips(client: &DummyWebDavClient) -> Vec<String> {
|
||||
let inbox_href = format!("/dav/itip/{}/inbox/", client.name);
|
||||
let response = client
|
||||
.propfind_with_headers(&inbox_href, ALL_DAV_PROPERTIES, [("depth", "1")])
|
||||
.await;
|
||||
let mut itips = vec![];
|
||||
impl DummyWebDavClient {
|
||||
async fn fetch_and_remove_itips(&self) -> Vec<String> {
|
||||
let inbox_href = format!("/dav/itip/{}/inbox/", self.name.replace('@', "%40"));
|
||||
let response = self
|
||||
.propfind_with_headers(&inbox_href, ALL_DAV_PROPERTIES, [("depth", "1")])
|
||||
.await;
|
||||
let mut itips = vec![];
|
||||
|
||||
for href in response.hrefs.keys().filter(|&href| href != &inbox_href) {
|
||||
let itip = client
|
||||
.request("GET", href, "")
|
||||
.await
|
||||
.with_status(StatusCode::OK)
|
||||
.body
|
||||
.expect("Missing body");
|
||||
client
|
||||
.request("DELETE", href, "")
|
||||
.await
|
||||
.with_status(StatusCode::NO_CONTENT);
|
||||
itips.push(itip);
|
||||
for href in response.hrefs.keys().filter(|&href| href != &inbox_href) {
|
||||
let itip = self
|
||||
.request("GET", href, "")
|
||||
.await
|
||||
.with_status(StatusCode::OK)
|
||||
.body
|
||||
.expect("Missing body");
|
||||
self.request("DELETE", href, "")
|
||||
.await
|
||||
.with_status(StatusCode::NO_CONTENT);
|
||||
itips.push(itip);
|
||||
}
|
||||
|
||||
itips
|
||||
}
|
||||
|
||||
itips
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -560,42 +568,46 @@ struct CalEntry {
|
||||
schedule_tag: String,
|
||||
}
|
||||
|
||||
async fn fetch_icals(client: &DummyWebDavClient) -> Vec<CalEntry> {
|
||||
let cal_inbox = format!("/dav/cal/{}/default/", client.name);
|
||||
let response = client
|
||||
.propfind_with_headers(&cal_inbox, ALL_DAV_PROPERTIES, [("depth", "1")])
|
||||
.await;
|
||||
let mut cals = vec![];
|
||||
impl DummyWebDavClient {
|
||||
async fn fetch_icals(&self) -> Vec<CalEntry> {
|
||||
let cal_inbox = format!("/dav/cal/{}/default/", self.name.replace('@', "%40"));
|
||||
let response = self
|
||||
.propfind_with_headers(&cal_inbox, ALL_DAV_PROPERTIES, [("depth", "1")])
|
||||
.await;
|
||||
let mut cals = vec![];
|
||||
|
||||
for href in response.hrefs.keys().filter(|&href| href != &cal_inbox) {
|
||||
let ical = client
|
||||
.request("GET", href, "")
|
||||
.await
|
||||
.with_status(StatusCode::OK)
|
||||
.body
|
||||
.expect("Missing body");
|
||||
let properties = response.properties(href);
|
||||
for href in response.hrefs.keys().filter(|&href| href != &cal_inbox) {
|
||||
let ical = self
|
||||
.request("GET", href, "")
|
||||
.await
|
||||
.with_status(StatusCode::OK)
|
||||
.body
|
||||
.expect("Missing body");
|
||||
let properties = response.properties(href);
|
||||
|
||||
assert!(
|
||||
!ical.contains("METHOD:"),
|
||||
"iTIP method found in calendar entry: {ical}"
|
||||
);
|
||||
assert!(
|
||||
!ical.contains("METHOD:"),
|
||||
"iTIP method found in calendar entry: {ical}"
|
||||
);
|
||||
|
||||
cals.push(CalEntry {
|
||||
href: href.to_string(),
|
||||
ical,
|
||||
schedule_tag: properties
|
||||
.get(DavProperty::CalDav(CalDavProperty::ScheduleTag))
|
||||
.value()
|
||||
.to_string(),
|
||||
});
|
||||
cals.push(CalEntry {
|
||||
href: href.to_string(),
|
||||
ical,
|
||||
schedule_tag: properties
|
||||
.get(DavProperty::CalDav(CalDavProperty::ScheduleTag))
|
||||
.value()
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
cals
|
||||
}
|
||||
|
||||
cals
|
||||
}
|
||||
|
||||
pub async fn test_build_itip_templates(server: &Server) {
|
||||
let dummy_access_token = AccessToken::from_id(0);
|
||||
pub async fn test_build_itip_templates(test: &TestServer) {
|
||||
let account = test.account("john@example.com");
|
||||
let account_id = account.id().document_id();
|
||||
let account_info = test.server.account_info(account_id).await.unwrap();
|
||||
|
||||
for (idx, summary) in [
|
||||
ItipSummary::Invite(vec![
|
||||
@@ -784,19 +796,14 @@ pub async fn test_build_itip_templates(server: &Server) {
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&summary)
|
||||
.unwrap()
|
||||
.to_vec();
|
||||
let summary = rkyv::access::<ArchivedItipSummary, rkyv::rancor::Error>(&bytes).unwrap();
|
||||
|
||||
let html = build_itip_template(
|
||||
server,
|
||||
&dummy_access_token,
|
||||
0,
|
||||
&test.server,
|
||||
&account_info,
|
||||
account_id,
|
||||
1,
|
||||
"john.doe@example.org",
|
||||
"jane.smith@example.net",
|
||||
summary,
|
||||
&summary,
|
||||
"124",
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -4,17 +4,18 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::WebDavTest;
|
||||
use crate::utils::server::TestServer;
|
||||
|
||||
use dav_proto::schema::property::{CardDavProperty, DavProperty, WebDavProperty};
|
||||
use groupware::DavResourceName;
|
||||
use hyper::StatusCode;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
pub async fn test(test: &TestServer) {
|
||||
println!("Running REPORT addressbook-query tests...");
|
||||
let client = test.client("john");
|
||||
let client = test.account("john@example.com").webdav_client();
|
||||
|
||||
// Create test data
|
||||
let default_path = format!("{}/john/default/", DavResourceName::Card.base_path());
|
||||
let default_path = format!("{}/john%40example.com/default/", DavResourceName::Card.base_path());
|
||||
let mut hrefs = Vec::with_capacity(3);
|
||||
for (i, vcard) in [VCARD1, VCARD2, VCARD3].iter().enumerate() {
|
||||
let href = format!("{default_path}contact-{i}.vcf",);
|
||||
|
||||
@@ -4,16 +4,20 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{DavResponse, WebDavTest};
|
||||
use crate::webdav::GenerateTestDavResource;
|
||||
use crate::utils::{
|
||||
server::TestServer,
|
||||
webdav::{DavResponse, GenerateTestDavResource},
|
||||
};
|
||||
use ahash::AHashSet;
|
||||
use dav_proto::Depth;
|
||||
use groupware::DavResourceName;
|
||||
use hyper::StatusCode;
|
||||
use registry::schema::structs::Action;
|
||||
|
||||
pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
let client = test.client("jane");
|
||||
let mike_noquota = test.client("mike");
|
||||
pub async fn test(test: &TestServer, assisted_discovery: bool) {
|
||||
let admin = test.account("admin@example.com");
|
||||
let client = test.account("jane@example.com").webdav_client();
|
||||
let mike_noquota = test.account("mike@example.com").webdav_client();
|
||||
|
||||
for resource_type in [
|
||||
DavResourceName::File,
|
||||
@@ -21,8 +25,8 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
DavResourceName::Card,
|
||||
] {
|
||||
println!("Running COPY/MOVE tests ({})...", resource_type.base_path());
|
||||
let user_base_path = format!("{}/jane", resource_type.base_path());
|
||||
let group_base_path = format!("{}/support", resource_type.base_path());
|
||||
let user_base_path = format!("{}/jane%40example.com", resource_type.base_path());
|
||||
let group_base_path = format!("{}/support%40example.com", resource_type.base_path());
|
||||
let default_test_depth = if resource_type == DavResourceName::File {
|
||||
2
|
||||
} else {
|
||||
@@ -73,7 +77,7 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
client.validate_values(&hierarchy).await;
|
||||
|
||||
// Delete cache an resync
|
||||
test.clear_cache();
|
||||
admin.registry_create_object(Action::InvalidateCaches).await;
|
||||
let response = client
|
||||
.sync_collection(
|
||||
&user_base_path,
|
||||
@@ -724,7 +728,10 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
|
||||
// Test 19: Quota enforcement (on CalDAV/CardDAV items are linked, not copied therefore there is no quota increase)
|
||||
if resource_type == DavResourceName::File {
|
||||
let path = format!("{}/mike/quota-test/", resource_type.base_path());
|
||||
let path = format!(
|
||||
"{}/mike%40example.com/quota-test/",
|
||||
resource_type.base_path()
|
||||
);
|
||||
let content = resource_type.generate();
|
||||
mike_noquota
|
||||
.mkcol("MKCOL", &path, [], [])
|
||||
@@ -744,7 +751,11 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
&path,
|
||||
[(
|
||||
"destination",
|
||||
format!("{}/mike/quota-test{i}", resource_type.base_path()).as_str(),
|
||||
format!(
|
||||
"{}/mike%40example.com/quota-test{i}",
|
||||
resource_type.base_path()
|
||||
)
|
||||
.as_str(),
|
||||
)],
|
||||
&content,
|
||||
)
|
||||
@@ -775,7 +786,10 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
mike_noquota
|
||||
.request(
|
||||
"DELETE",
|
||||
&format!("{}/mike/quota-test{i}", resource_type.base_path()),
|
||||
&format!(
|
||||
"{}/mike%40example.com/quota-test{i}",
|
||||
resource_type.base_path()
|
||||
),
|
||||
"",
|
||||
)
|
||||
.await
|
||||
@@ -785,7 +799,9 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
}
|
||||
|
||||
client.delete_default_containers().await;
|
||||
client.delete_default_containers_by_account("support").await;
|
||||
client
|
||||
.delete_default_containers_by_account("support@example.com")
|
||||
.await;
|
||||
mike_noquota.delete_default_containers().await;
|
||||
test.assert_is_empty().await;
|
||||
}
|
||||
@@ -796,7 +812,9 @@ fn assert_result(response: &DavResponse, hierarchy: &[(String, String)]) {
|
||||
.hrefs()
|
||||
.into_iter()
|
||||
.filter(|h| {
|
||||
!h.ends_with("/jane/") && !h.ends_with("/support/") && !h.ends_with("/default/")
|
||||
!h.ends_with("/jane%40example.com/")
|
||||
&& !h.ends_with("/support%40example.com/")
|
||||
&& !h.ends_with("/default/")
|
||||
})
|
||||
.collect::<AHashSet<_>>();
|
||||
let hierarchy = hierarchy
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{DavResponse, DummyWebDavClient, WebDavTest};
|
||||
use crate::webdav::GenerateTestDavResource;
|
||||
use crate::utils::{server::TestServer, webdav::GenerateTestDavResource};
|
||||
|
||||
use dav_proto::schema::property::{DavProperty, WebDavProperty};
|
||||
use groupware::DavResourceName;
|
||||
use hyper::StatusCode;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
let client = test.client("john");
|
||||
pub async fn test(test: &TestServer) {
|
||||
let client = test.account("john@example.com").webdav_client();
|
||||
|
||||
for resource_type in [
|
||||
DavResourceName::File,
|
||||
@@ -22,7 +22,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
"Running LOCK/UNLOCK tests ({})...",
|
||||
resource_type.base_path()
|
||||
);
|
||||
let base_path = format!("{}/john", resource_type.base_path());
|
||||
let base_path = format!("{}/john%40example.com", resource_type.base_path());
|
||||
|
||||
// Test 1: Creating a collection under an unmapped resource without providing a lock token should fail
|
||||
let path = format!("{base_path}/do-not-write");
|
||||
@@ -201,9 +201,3 @@ pub async fn test(test: &WebDavTest) {
|
||||
client.delete_default_containers().await;
|
||||
test.assert_is_empty().await;
|
||||
}
|
||||
|
||||
impl DavResponse {
|
||||
pub fn lock_token(&self) -> &str {
|
||||
self.value("D:prop.D:lockdiscovery.D:activelock.D:locktoken.D:href")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ use hyper::StatusCode;
|
||||
|
||||
use crate::webdav::{TEST_FILE_1, TEST_ICAL_1, TEST_VCARD_1, TEST_VTIMEZONE_1};
|
||||
|
||||
use super::{DavResponse, DummyWebDavClient, WebDavTest};
|
||||
use crate::utils::server::TestServer;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
|
||||
pub async fn test(test: &TestServer) {
|
||||
println!("Running MKCOL tests...");
|
||||
let client = test.client("john");
|
||||
let client = test.account("john@example.com").webdav_client();
|
||||
|
||||
// Creating collections in root elements is not allowed
|
||||
for path in [
|
||||
@@ -29,9 +30,9 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// Create collections using MKCOL (empty body)
|
||||
for path in [
|
||||
"/dav/file/john/my-files",
|
||||
"/dav/card/john/my-cards",
|
||||
"/dav/cal/john/my-events",
|
||||
"/dav/file/john%40example.com/my-files",
|
||||
"/dav/card/john%40example.com/my-cards",
|
||||
"/dav/cal/john%40example.com/my-events",
|
||||
] {
|
||||
client
|
||||
.request("MKCOL", path, "")
|
||||
@@ -41,9 +42,9 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// Create resources under the newly created collections
|
||||
for (path, content) in [
|
||||
("/dav/file/john/my-files/file1.txt", TEST_FILE_1),
|
||||
("/dav/card/john/my-cards/card1.vcf", TEST_VCARD_1),
|
||||
("/dav/cal/john/my-events/event1.ics", TEST_ICAL_1),
|
||||
("/dav/file/john%40example.com/my-files/file1.txt", TEST_FILE_1),
|
||||
("/dav/card/john%40example.com/my-cards/card1.vcf", TEST_VCARD_1),
|
||||
("/dav/cal/john%40example.com/my-events/event1.ics", TEST_ICAL_1),
|
||||
] {
|
||||
client
|
||||
.request("PUT", path, content)
|
||||
@@ -53,12 +54,12 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// Creating a collection on a mapped resource should fail
|
||||
for path in [
|
||||
"/dav/file/john/my-files",
|
||||
"/dav/card/john/my-cards",
|
||||
"/dav/cal/john/my-events",
|
||||
"/dav/file/john/my-files/file1.txt",
|
||||
"/dav/card/john/my-cards/card1.vcf",
|
||||
"/dav/cal/john/my-events/event1.ics",
|
||||
"/dav/file/john%40example.com/my-files",
|
||||
"/dav/card/john%40example.com/my-cards",
|
||||
"/dav/cal/john%40example.com/my-events",
|
||||
"/dav/file/john%40example.com/my-files/file1.txt",
|
||||
"/dav/card/john%40example.com/my-cards/card1.vcf",
|
||||
"/dav/cal/john%40example.com/my-events/event1.ics",
|
||||
] {
|
||||
client
|
||||
.request("MKCOL", path, "")
|
||||
@@ -68,13 +69,13 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// Creating a sub-collections is allowed in FileDAV but in CalDAV and CardDAV
|
||||
for (path, expected_status) in [
|
||||
("/dav/file/john/my-files/my-sub-files", StatusCode::CREATED),
|
||||
("/dav/file/john%40example.com/my-files/my-sub-files", StatusCode::CREATED),
|
||||
(
|
||||
"/dav/card/john/my-cards/my-sub-cards",
|
||||
"/dav/card/john%40example.com/my-cards/my-sub-cards",
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
),
|
||||
(
|
||||
"/dav/cal/john/my-events/my-sub-events",
|
||||
"/dav/cal/john%40example.com/my-events/my-sub-events",
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
),
|
||||
] {
|
||||
@@ -86,9 +87,9 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// Extended MKCOL with an unsupported resource types should fail
|
||||
for (path, resource_type) in [
|
||||
("/dav/file/john/my-named-files", "B:addressbook"),
|
||||
("/dav/card/john/my-named-cards", "A:calendar"),
|
||||
("/dav/cal/john/my-named-events", "B:addressbook"),
|
||||
("/dav/file/john%40example.com/my-named-files", "B:addressbook"),
|
||||
("/dav/card/john%40example.com/my-named-cards", "A:calendar"),
|
||||
("/dav/cal/john%40example.com/my-named-events", "B:addressbook"),
|
||||
] {
|
||||
client
|
||||
.mkcol("MKCOL", path, ["D:collection", resource_type], [])
|
||||
@@ -104,12 +105,12 @@ pub async fn test(test: &WebDavTest) {
|
||||
// Create using extended MKCOL
|
||||
for (path, expected_properties, resource_types) in [
|
||||
(
|
||||
"/dav/file/john/my-named-files/",
|
||||
"/dav/file/john%40example.com/my-named-files/",
|
||||
[("D:displayname", "Named Files")].as_slice(),
|
||||
["D:collection"].as_slice(),
|
||||
),
|
||||
(
|
||||
"/dav/card/john/my-named-cards/",
|
||||
"/dav/card/john%40example.com/my-named-cards/",
|
||||
[
|
||||
("D:displayname", "Named Cards"),
|
||||
("B:addressbook-description", "Some amazing contacts"),
|
||||
@@ -118,7 +119,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
["D:collection", "B:addressbook"].as_slice(),
|
||||
),
|
||||
(
|
||||
"/dav/cal/john/my-named-events/",
|
||||
"/dav/cal/john%40example.com/my-named-events/",
|
||||
[
|
||||
("D:displayname", "Named Events"),
|
||||
("A:calendar-description", "Some amazing events"),
|
||||
@@ -166,7 +167,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
client
|
||||
.mkcol(
|
||||
"MKCALENDAR",
|
||||
"/dav/cal/john/my-named-events2",
|
||||
"/dav/cal/john%40example.com/my-named-events2",
|
||||
[],
|
||||
[
|
||||
("D:displayname", "Named Events 2"),
|
||||
@@ -183,7 +184,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
client
|
||||
.mkcol(
|
||||
"MKCALENDAR",
|
||||
"/dav/cal/john/my-named-events3",
|
||||
"/dav/cal/john%40example.com/my-named-events3",
|
||||
[],
|
||||
[
|
||||
("D:displayname", "Named Events 3"),
|
||||
@@ -203,11 +204,11 @@ pub async fn test(test: &WebDavTest) {
|
||||
// Check the properties of the created calendars
|
||||
client
|
||||
.propfind(
|
||||
"/dav/cal/john/my-named-events2/",
|
||||
"/dav/cal/john%40example.com/my-named-events2/",
|
||||
["A:supported-calendar-component-set"],
|
||||
)
|
||||
.await
|
||||
.properties("/dav/cal/john/my-named-events2/")
|
||||
.properties("/dav/cal/john%40example.com/my-named-events2/")
|
||||
.get("A:supported-calendar-component-set")
|
||||
.with_status(StatusCode::OK)
|
||||
.with_values([
|
||||
@@ -227,25 +228,25 @@ pub async fn test(test: &WebDavTest) {
|
||||
]);
|
||||
client
|
||||
.propfind(
|
||||
"/dav/cal/john/my-named-events3/",
|
||||
"/dav/cal/john%40example.com/my-named-events3/",
|
||||
["A:supported-calendar-component-set"],
|
||||
)
|
||||
.await
|
||||
.properties("/dav/cal/john/my-named-events3/")
|
||||
.properties("/dav/cal/john%40example.com/my-named-events3/")
|
||||
.get("A:supported-calendar-component-set")
|
||||
.with_status(StatusCode::OK)
|
||||
.with_values(["A:comp.[name]:VEVENT", "A:comp.[name]:VTODO"]);
|
||||
|
||||
// Delete everything
|
||||
for path in [
|
||||
"/dav/file/john/my-files",
|
||||
"/dav/card/john/my-cards",
|
||||
"/dav/cal/john/my-events",
|
||||
"/dav/file/john/my-named-files",
|
||||
"/dav/card/john/my-named-cards",
|
||||
"/dav/cal/john/my-named-events",
|
||||
"/dav/cal/john/my-named-events2",
|
||||
"/dav/cal/john/my-named-events3",
|
||||
"/dav/file/john%40example.com/my-files",
|
||||
"/dav/card/john%40example.com/my-cards",
|
||||
"/dav/cal/john%40example.com/my-events",
|
||||
"/dav/file/john%40example.com/my-named-files",
|
||||
"/dav/card/john%40example.com/my-named-cards",
|
||||
"/dav/cal/john%40example.com/my-named-events",
|
||||
"/dav/cal/john%40example.com/my-named-events2",
|
||||
"/dav/cal/john%40example.com/my-named-events3",
|
||||
] {
|
||||
client
|
||||
.request("DELETE", path, "")
|
||||
@@ -255,4 +256,3 @@ pub async fn test(test: &WebDavTest) {
|
||||
client.delete_default_containers().await;
|
||||
test.assert_is_empty().await;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,53 +4,25 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
AssertConfig, TEST_USERS, add_test_certs,
|
||||
directory::internal::TestInternalDirectory,
|
||||
jmap::{assert_is_empty, wait_for_tasks},
|
||||
store::{
|
||||
TempDir, build_store_config,
|
||||
cleanup::{search_store_destroy, store_destroy},
|
||||
use crate::utils::server::TestServerBuilder;
|
||||
use ahash::AHashMap;
|
||||
use common::{DavResource, DavResources};
|
||||
use groupware::DavResourceName;
|
||||
use hyper::StatusCode;
|
||||
use registry::{
|
||||
schema::{
|
||||
enums::{Permission, StorageQuota},
|
||||
prelude::{ObjectType, Property},
|
||||
structs::{
|
||||
CalendarAlarm, CalendarScheduling, Expression, MtaStageAuth, Sharing, SystemSettings,
|
||||
WebDav,
|
||||
},
|
||||
},
|
||||
types::EnumImpl,
|
||||
};
|
||||
use ::managesieve::core::ManageSieveSessionManager;
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use base64::{Engine, engine::general_purpose::STANDARD};
|
||||
use common::{
|
||||
Caches, Core, Data, DavResource, DavResources, Inner, Server,
|
||||
config::{
|
||||
server::{Listeners, ServerProtocol},
|
||||
telemetry::Telemetry,
|
||||
},
|
||||
manager::boot::build_ipc,
|
||||
};
|
||||
use dav_proto::{
|
||||
schema::property::{DavProperty, WebDavProperty},
|
||||
xml_pretty_print,
|
||||
};
|
||||
use email::message::metadata::MessageMetadata;
|
||||
use groupware::{DavResourceName, cache::GroupwareCache};
|
||||
use http::HttpSessionManager;
|
||||
use hyper::{HeaderMap, Method, StatusCode, header::AUTHORIZATION};
|
||||
use imap::core::ImapSessionManager;
|
||||
use pop3::Pop3SessionManager;
|
||||
use quick_xml::Reader;
|
||||
use quick_xml::events::Event;
|
||||
use services::SpawnServices;
|
||||
use smtp::{SpawnQueueManager, core::SmtpSessionManager};
|
||||
use std::{borrow::Cow, str};
|
||||
use std::{
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use store::{
|
||||
ValueKey,
|
||||
rand::{Rng, distr::Alphanumeric, rng},
|
||||
write::{AlignedBytes, Archive},
|
||||
};
|
||||
use tokio::sync::watch;
|
||||
use types::{collection::Collection, field::EmailField};
|
||||
use utils::config::Config;
|
||||
use serde_json::json;
|
||||
use std::str;
|
||||
use std::time::Instant;
|
||||
|
||||
pub mod acl;
|
||||
pub mod basic;
|
||||
@@ -70,8 +42,6 @@ pub mod sync;
|
||||
|
||||
#[test]
|
||||
fn webdav_tests() {
|
||||
//test_build_itip_templates(&handle.server).await;
|
||||
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.thread_stack_size(8 * 1024 * 1024) // 8MB stack
|
||||
.enable_all()
|
||||
@@ -80,25 +50,156 @@ fn webdav_tests() {
|
||||
.block_on(async {
|
||||
// Prepare settings
|
||||
let assisted_discovery = std::env::var("ASSISTED_DISCOVERY").unwrap_or_default() == "1";
|
||||
let start_time = Instant::now();
|
||||
let delete = true;
|
||||
let handle = init_webdav_tests(assisted_discovery, delete).await;
|
||||
|
||||
basic::test(&handle).await;
|
||||
put_get::test(&handle).await;
|
||||
mkcol::test(&handle).await;
|
||||
copy_move::test(&handle, assisted_discovery).await;
|
||||
prop::test(&handle, assisted_discovery).await;
|
||||
multiget::test(&handle).await;
|
||||
sync::test(&handle).await;
|
||||
lock::test(&handle).await;
|
||||
principals::test(&handle, assisted_discovery).await;
|
||||
acl::test(&handle).await;
|
||||
card_query::test(&handle).await;
|
||||
cal_query::test(&handle).await;
|
||||
cal_alarm::test(&handle).await;
|
||||
let mut test = TestServerBuilder::new("webdav_tests")
|
||||
.await
|
||||
.with_default_listeners()
|
||||
.await
|
||||
.build()
|
||||
.await;
|
||||
|
||||
// Create admin account
|
||||
let admin = test.create_admin_account("admin@example.com").await;
|
||||
|
||||
// Create test users
|
||||
for (name, secret, description, aliases) in [
|
||||
(
|
||||
"john@example.com",
|
||||
"secret2 + some more text",
|
||||
"John Doe",
|
||||
&["jdoe@example.com"],
|
||||
),
|
||||
(
|
||||
"jane@example.com",
|
||||
"secret3 + some more text",
|
||||
"Jane Doe-Smith",
|
||||
&["jane.smith@example.com"],
|
||||
),
|
||||
(
|
||||
"bill@example.com",
|
||||
"secret4 + some more text",
|
||||
"Bill Foobar",
|
||||
&["bill@example.com"],
|
||||
),
|
||||
(
|
||||
"mike@example.com",
|
||||
"secret5 + some more text",
|
||||
"Mike Noquota",
|
||||
&["mike@example.com"],
|
||||
),
|
||||
] {
|
||||
let account = admin
|
||||
.create_user_account(
|
||||
name,
|
||||
secret,
|
||||
description,
|
||||
aliases,
|
||||
vec![
|
||||
Permission::UnlimitedRequests,
|
||||
Permission::UnlimitedUploads,
|
||||
Permission::DavPrincipalList,
|
||||
Permission::DavPrincipalSearch,
|
||||
],
|
||||
)
|
||||
.await;
|
||||
if name == "mike@example.com" {
|
||||
admin
|
||||
.registry_update_object(
|
||||
ObjectType::Account,
|
||||
account.id(),
|
||||
json!({
|
||||
Property::Quotas: { StorageQuota::MaxDiskQuota.as_str(): 1024}
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
test.insert_account(account);
|
||||
}
|
||||
|
||||
// Create test group
|
||||
test.insert_account(
|
||||
admin
|
||||
.create_group_account("support@example.com", "Support Group", &[])
|
||||
.await,
|
||||
);
|
||||
|
||||
// Add Jane to the Support group
|
||||
let support_id = test.account("support@example.com").id();
|
||||
admin
|
||||
.registry_update_object(
|
||||
ObjectType::Account,
|
||||
test.account("jane@example.com").id(),
|
||||
json!({
|
||||
"memberGroupIds": { support_id: true },
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Add test settings
|
||||
admin
|
||||
.registry_update_setting(
|
||||
SystemSettings {
|
||||
default_hostname: "webdav.example.org".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
&[Property::DefaultHostname],
|
||||
)
|
||||
.await;
|
||||
admin
|
||||
.registry_create_object(MtaStageAuth {
|
||||
require: Expression {
|
||||
else_: "false".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
admin
|
||||
.registry_create_object(CalendarAlarm {
|
||||
min_trigger_interval: 1000u64.into(),
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
admin
|
||||
.registry_create_object(Sharing {
|
||||
allow_directory_queries: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
admin
|
||||
.registry_create_object(CalendarScheduling {
|
||||
auto_add_invitations: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
admin
|
||||
.registry_create_object(WebDav {
|
||||
enable_assisted_discovery: assisted_discovery,
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
admin.reload_settings().await;
|
||||
|
||||
test.insert_account(admin);
|
||||
|
||||
let start_time = Instant::now();
|
||||
//test_build_itip_templates(&test).await;
|
||||
basic::test(&test).await;
|
||||
put_get::test(&test).await;
|
||||
mkcol::test(&test).await;
|
||||
copy_move::test(&test, assisted_discovery).await;
|
||||
prop::test(&test, assisted_discovery).await;
|
||||
multiget::test(&test).await;
|
||||
sync::test(&test).await;
|
||||
lock::test(&test).await;
|
||||
principals::test(&test, assisted_discovery).await;
|
||||
acl::test(&test).await;
|
||||
card_query::test(&test).await;
|
||||
cal_query::test(&test).await;
|
||||
cal_alarm::test(&test).await;
|
||||
cal_itip::test();
|
||||
cal_scheduling::test(&handle).await;
|
||||
cal_scheduling::test(&test).await;
|
||||
|
||||
// Print elapsed time
|
||||
let elapsed = start_time.elapsed();
|
||||
@@ -109,179 +210,12 @@ fn webdav_tests() {
|
||||
);
|
||||
|
||||
// Remove test data
|
||||
if delete {
|
||||
handle.temp_dir.delete();
|
||||
if test.is_reset() {
|
||||
test.temp_dir.delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct WebDavTest {
|
||||
server: Server,
|
||||
clients: AHashMap<&'static str, DummyWebDavClient>,
|
||||
temp_dir: TempDir,
|
||||
shutdown_tx: watch::Sender<bool>,
|
||||
}
|
||||
|
||||
async fn init_webdav_tests(assisted_discovery: bool, delete_if_exists: bool) -> WebDavTest {
|
||||
// Load and parse config
|
||||
let temp_dir = TempDir::new("webdav_tests", delete_if_exists);
|
||||
let mut config = Config::new(
|
||||
add_test_certs(&(build_store_config(&temp_dir.path.to_string_lossy()) + SERVER))
|
||||
.replace("{TMP}", &temp_dir.path.display().to_string())
|
||||
.replace("{ASSISTED_DISCOVERY}", &assisted_discovery.to_string())
|
||||
.replace(
|
||||
"{LEVEL}",
|
||||
&std::env::var("LOG").unwrap_or_else(|_| "disable".to_string()),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
config.resolve_all_macros().await;
|
||||
|
||||
// Parse servers
|
||||
let mut servers = Listeners::parse(&mut config);
|
||||
|
||||
// Bind ports and drop privileges
|
||||
servers.bind_and_drop_priv(&mut config);
|
||||
|
||||
// Build stores
|
||||
let stores = Stores::parse_all(&mut config, false).await;
|
||||
|
||||
// Parse core
|
||||
let tracers = Telemetry::parse(&mut config, &stores);
|
||||
let core = Core::parse(&mut config, stores, Default::default()).await;
|
||||
let data = Data::parse(&mut config);
|
||||
let cache = Caches::parse(&mut config);
|
||||
|
||||
let store = core.storage.data.clone();
|
||||
let search_store = core.storage.fts.clone();
|
||||
let (ipc, mut ipc_rxs) = build_ipc(false);
|
||||
let inner = Arc::new(Inner {
|
||||
shared_core: core.into_shared(),
|
||||
data,
|
||||
ipc,
|
||||
cache,
|
||||
});
|
||||
|
||||
// Parse acceptors
|
||||
servers.parse_tcp_acceptors(&mut config, inner.clone());
|
||||
|
||||
// Enable tracing
|
||||
tracers.enable(true);
|
||||
|
||||
// Start services
|
||||
config.assert_no_errors();
|
||||
ipc_rxs.spawn_queue_manager(inner.clone());
|
||||
ipc_rxs.spawn_services(inner.clone());
|
||||
|
||||
// Spawn servers
|
||||
let (shutdown_tx, _) = servers.spawn(|server, acceptor, shutdown_rx| {
|
||||
match &server.protocol {
|
||||
ServerProtocol::Smtp | ServerProtocol::Lmtp => server.spawn(
|
||||
SmtpSessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Http => server.spawn(
|
||||
HttpSessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Imap => server.spawn(
|
||||
ImapSessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Pop3 => server.spawn(
|
||||
Pop3SessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::ManageSieve => server.spawn(
|
||||
ManageSieveSessionManager::new(inner.clone()),
|
||||
inner.clone(),
|
||||
acceptor,
|
||||
shutdown_rx,
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
if delete_if_exists {
|
||||
store_destroy(&store).await;
|
||||
search_store_destroy(&search_store).await;
|
||||
}
|
||||
|
||||
// Create test accounts
|
||||
let mut clients = AHashMap::new();
|
||||
for (account, secret, name, email) in TEST_USERS {
|
||||
let account_id = store
|
||||
.create_test_user(account, secret, name, &[email])
|
||||
.await;
|
||||
clients.insert(
|
||||
*account,
|
||||
DummyWebDavClient::new(account_id, account, secret, email),
|
||||
);
|
||||
store
|
||||
.add_permissions(
|
||||
account,
|
||||
[Permission::DavPrincipalList, Permission::DavPrincipalSearch],
|
||||
)
|
||||
.await;
|
||||
if *account == "mike" {
|
||||
store.set_test_quota(account, 1024).await;
|
||||
}
|
||||
}
|
||||
store
|
||||
.create_test_group("support", "Support Group", &["support@example.com"])
|
||||
.await;
|
||||
store.add_to_group("jane", "support").await;
|
||||
|
||||
WebDavTest {
|
||||
server: inner.build_server(),
|
||||
clients,
|
||||
temp_dir,
|
||||
shutdown_tx,
|
||||
}
|
||||
}
|
||||
|
||||
impl WebDavTest {
|
||||
pub fn client(&self, name: &'static str) -> &DummyWebDavClient {
|
||||
self.clients.get(name).unwrap()
|
||||
}
|
||||
|
||||
pub async fn resources(&self, name: &'static str, collection: Collection) -> Arc<DavResources> {
|
||||
let account_id = self.client(name).account_id;
|
||||
let access_token = self.server.get_access_token(account_id).await.unwrap();
|
||||
self.server
|
||||
.fetch_dav_resources(&access_token, account_id, collection.into())
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn clear_cache(&self) {
|
||||
for cache in [
|
||||
&self.server.inner.cache.events,
|
||||
&self.server.inner.cache.contacts,
|
||||
&self.server.inner.cache.files,
|
||||
] {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn assert_is_empty(&self) {
|
||||
assert_is_empty(&self.server).await;
|
||||
self.clear_cache();
|
||||
}
|
||||
|
||||
pub async fn wait_for_tasks(&self) {
|
||||
wait_for_tasks(&self.server).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DavResourcesTest {
|
||||
fn items(&self) -> Vec<DavResource>;
|
||||
}
|
||||
@@ -388,153 +322,3 @@ END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
END:VCALENDAR
|
||||
"#;
|
||||
|
||||
impl WebDavTest {
|
||||
pub async fn fetch_email(&self, account_id: u32, document_id: u32) -> Vec<u8> {
|
||||
let metadata_ = self
|
||||
.server
|
||||
.store()
|
||||
.get_value::<Archive<AlignedBytes>>(ValueKey::property(
|
||||
account_id,
|
||||
Collection::Email,
|
||||
document_id,
|
||||
EmailField::Metadata,
|
||||
))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
self.server
|
||||
.blob_store()
|
||||
.get_blob(
|
||||
metadata_
|
||||
.unarchive::<MessageMetadata>()
|
||||
.unwrap()
|
||||
.blob_hash
|
||||
.0
|
||||
.as_slice(),
|
||||
0..usize::MAX,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
const SERVER: &str = r#"
|
||||
[server]
|
||||
hostname = "webdav.example.org"
|
||||
|
||||
[spam-filter]
|
||||
enable = false
|
||||
|
||||
[http]
|
||||
url = "'https://127.0.0.1:8899'"
|
||||
|
||||
[server.listener.webdav]
|
||||
bind = ["127.0.0.1:8899"]
|
||||
protocol = "http"
|
||||
max-connections = 81920
|
||||
tls.implicit = true
|
||||
|
||||
[server.socket]
|
||||
reuse-addr = true
|
||||
|
||||
[server.tls]
|
||||
enable = true
|
||||
implicit = false
|
||||
certificate = "default"
|
||||
|
||||
[session.ehlo]
|
||||
reject-non-fqdn = false
|
||||
|
||||
[session.rcpt]
|
||||
relay = [ { if = "!is_empty(authenticated_as)", then = true },
|
||||
{ else = false } ]
|
||||
|
||||
[session.rcpt.errors]
|
||||
total = 5
|
||||
wait = "1ms"
|
||||
|
||||
[resolver]
|
||||
type = "system"
|
||||
|
||||
[queue.strategy]
|
||||
route = [ { if = "rcpt_domain == 'example.com'", then = "'local'" },
|
||||
{ else = "'mx'" } ]
|
||||
|
||||
[session.data.add-headers]
|
||||
delivered-to = false
|
||||
|
||||
[session.extensions]
|
||||
future-release = [ { if = "!is_empty(authenticated_as)", then = "99999999d"},
|
||||
{ else = false } ]
|
||||
|
||||
[certificate.default]
|
||||
cert = "%{file:{CERT}}%"
|
||||
private-key = "%{file:{PK}}%"
|
||||
|
||||
[jmap.protocol]
|
||||
set.max-objects = 100000
|
||||
|
||||
[jmap.protocol.request]
|
||||
max-concurrent = 8
|
||||
|
||||
[jmap.protocol.upload]
|
||||
max-size = 5000000
|
||||
max-concurrent = 4
|
||||
ttl = "1m"
|
||||
|
||||
[jmap.protocol.upload.quota]
|
||||
files = 3
|
||||
size = 50000
|
||||
|
||||
[jmap.rate-limit]
|
||||
account = "1000/1m"
|
||||
authentication = "100/2s"
|
||||
anonymous = "100/1m"
|
||||
|
||||
[calendar.alarms]
|
||||
minimum-interval = "1s"
|
||||
|
||||
[calendar.scheduling.inbound]
|
||||
auto-add = true
|
||||
|
||||
[dav.collection]
|
||||
assisted-discovery = {ASSISTED_DISCOVERY}
|
||||
|
||||
[sharing]
|
||||
allow-directory-query = true
|
||||
|
||||
[store."auth"]
|
||||
type = "sqlite"
|
||||
path = "{TMP}/auth.db"
|
||||
|
||||
[store."auth".query]
|
||||
name = "SELECT name, type, secret, description, quota FROM accounts WHERE name = ? AND active = true"
|
||||
members = "SELECT member_of FROM group_members WHERE name = ?"
|
||||
recipients = "SELECT name FROM emails WHERE address = ?"
|
||||
emails = "SELECT address FROM emails WHERE name = ? AND type != 'list' ORDER BY type DESC, address ASC"
|
||||
verify = "SELECT address FROM emails WHERE address LIKE '%' || ? || '%' AND type = 'primary' ORDER BY address LIMIT 5"
|
||||
expand = "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ? AND l.type = 'list' ORDER BY p.address LIMIT 50"
|
||||
domains = "SELECT 1 FROM emails WHERE address LIKE '%@' || ? LIMIT 1"
|
||||
|
||||
[oauth]
|
||||
key = "parerga_und_paralipomena"
|
||||
|
||||
[oauth.auth]
|
||||
max-attempts = 1
|
||||
|
||||
[oauth.expiry]
|
||||
user-code = "1s"
|
||||
token = "1s"
|
||||
refresh-token = "3s"
|
||||
refresh-token-renew = "2s"
|
||||
|
||||
[tracer.console]
|
||||
type = "console"
|
||||
level = "{LEVEL}"
|
||||
multiline = false
|
||||
ansi = true
|
||||
disabled-events = ["network.*"]
|
||||
|
||||
"#;
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::WebDavTest;
|
||||
use crate::webdav::{DummyWebDavClient, GenerateTestDavResource, prop::DavMultiStatus};
|
||||
use crate::utils::{server::TestServer, webdav::GenerateTestDavResource};
|
||||
use dav_proto::schema::property::{CalDavProperty, CardDavProperty, DavProperty, WebDavProperty};
|
||||
use groupware::DavResourceName;
|
||||
use hyper::StatusCode;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
let client = test.client("john");
|
||||
pub async fn test(test: &TestServer) {
|
||||
let client = test.account("john@example.com").webdav_client();
|
||||
|
||||
for resource_type in [DavResourceName::Cal, DavResourceName::Card] {
|
||||
println!(
|
||||
@@ -22,7 +21,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
let mut paths = Vec::new();
|
||||
for name in ["file1", "file2"] {
|
||||
let contents = resource_type.generate();
|
||||
let path = format!("{}/john/default/{}", resource_type.base_path(), name);
|
||||
let path = format!("{}/john%40example.com/default/{}", resource_type.base_path(), name);
|
||||
let etag = client
|
||||
.request("PUT", &path, contents.as_str())
|
||||
.await
|
||||
@@ -33,7 +32,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
}
|
||||
|
||||
if resource_type == DavResourceName::Cal {
|
||||
let path = format!("{}/john", resource_type.base_path());
|
||||
let path = format!("{}/john%40example.com", resource_type.base_path());
|
||||
let response = client
|
||||
.multiget_calendar(&path, &[&paths[0].0, &paths[1].0])
|
||||
.await;
|
||||
@@ -49,7 +48,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
.with_values([contents.as_str()]);
|
||||
}
|
||||
} else {
|
||||
let path = format!("{}/john", resource_type.base_path());
|
||||
let path = format!("{}/john%40example.com", resource_type.base_path());
|
||||
let response = client
|
||||
.multiget_addressbook(&path, &[&paths[0].0, &paths[1].0])
|
||||
.await;
|
||||
|
||||
@@ -4,20 +4,29 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::WebDavTest;
|
||||
use crate::{TEST_USERS, webdav::prop::ALL_DAV_PROPERTIES};
|
||||
use crate::utils::server::TestServer;
|
||||
use crate::webdav::prop::ALL_DAV_PROPERTIES;
|
||||
use dav_proto::schema::property::{DavProperty, PrincipalProperty, WebDavProperty};
|
||||
use groupware::DavResourceName;
|
||||
use hyper::StatusCode;
|
||||
|
||||
pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
pub async fn test(test: &TestServer, assisted_discovery: bool) {
|
||||
println!("Running principals tests...");
|
||||
let client = test.client("jane");
|
||||
let client = test.account("jane@example.com").webdav_client();
|
||||
let principal_path = format!("D:href:{}/", DavResourceName::Principal.base_path());
|
||||
let jane_principal_path = format!("D:href:{}/jane/", DavResourceName::Principal.base_path());
|
||||
let jane_principal_path = format!(
|
||||
"D:href:{}/jane%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
);
|
||||
|
||||
let path_support_card = format!("D:href:{}/support/", DavResourceName::Card.base_path());
|
||||
let path_support_cal = format!("D:href:{}/support/", DavResourceName::Cal.base_path());
|
||||
let path_support_card = format!(
|
||||
"D:href:{}/support%40example.com/",
|
||||
DavResourceName::Card.base_path()
|
||||
);
|
||||
let path_support_cal = format!(
|
||||
"D:href:{}/support%40example.com/",
|
||||
DavResourceName::Cal.base_path()
|
||||
);
|
||||
|
||||
// Test 1: PROPFIND on /dav/pal should return all principals
|
||||
let response = client
|
||||
@@ -26,22 +35,34 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
ALL_DAV_PROPERTIES,
|
||||
)
|
||||
.await;
|
||||
for (account, _, name, email) in TEST_USERS {
|
||||
for account_ in test.accounts.values().filter(|a| a.name().contains('@')) {
|
||||
let account_name = account_.name().replace('@', "%40");
|
||||
let email = account_.name();
|
||||
let description = account_.description();
|
||||
|
||||
let props = response.properties(&format!(
|
||||
"{}/{}/",
|
||||
DavResourceName::Principal.base_path(),
|
||||
account
|
||||
account_name
|
||||
));
|
||||
let path_pal = format!(
|
||||
"D:href:{}/{}/",
|
||||
DavResourceName::Principal.base_path(),
|
||||
account
|
||||
account_name
|
||||
);
|
||||
let path_card = format!(
|
||||
"D:href:{}/{}/",
|
||||
DavResourceName::Card.base_path(),
|
||||
account_name
|
||||
);
|
||||
let path_cal = format!(
|
||||
"D:href:{}/{}/",
|
||||
DavResourceName::Cal.base_path(),
|
||||
account_name
|
||||
);
|
||||
let path_card = format!("D:href:{}/{}/", DavResourceName::Card.base_path(), account);
|
||||
let path_cal = format!("D:href:{}/{}/", DavResourceName::Cal.base_path(), account);
|
||||
props
|
||||
.get(DavProperty::WebDav(WebDavProperty::DisplayName))
|
||||
.with_values([*name])
|
||||
.with_values([description])
|
||||
.with_status(StatusCode::OK);
|
||||
props
|
||||
.get(DavProperty::WebDav(WebDavProperty::CurrentUserPrincipal))
|
||||
@@ -55,7 +76,7 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
.get(DavProperty::WebDav(WebDavProperty::Owner))
|
||||
.with_values([path_pal.as_str()])
|
||||
.with_status(StatusCode::OK);
|
||||
if *account == "jane" && !assisted_discovery {
|
||||
if account_name == "jane%40example.com" && !assisted_discovery {
|
||||
props
|
||||
.get(DavProperty::Principal(PrincipalProperty::CalendarHomeSet))
|
||||
.with_values([path_cal.as_str(), path_support_cal.as_str()])
|
||||
@@ -104,12 +125,16 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
.with_status(StatusCode::OK);
|
||||
props
|
||||
.get(DavProperty::Principal(PrincipalProperty::CalendarUserType))
|
||||
.with_values(["INDIVIDUAL"])
|
||||
.with_values([if account_name == "support%40example.com" {
|
||||
"GROUP"
|
||||
} else {
|
||||
"INDIVIDUAL"
|
||||
}])
|
||||
.with_status(StatusCode::OK);
|
||||
props
|
||||
.get(DavProperty::Principal(PrincipalProperty::ScheduleInboxURL))
|
||||
.with_values([format!(
|
||||
"D:href:{}/{account}/inbox/",
|
||||
"D:href:{}/{account_name}/inbox/",
|
||||
DavResourceName::Scheduling.base_path()
|
||||
)
|
||||
.as_str()])
|
||||
@@ -117,7 +142,7 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
props
|
||||
.get(DavProperty::Principal(PrincipalProperty::ScheduleOutboxURL))
|
||||
.with_values([format!(
|
||||
"D:href:{}/{account}/outbox/",
|
||||
"D:href:{}/{account_name}/outbox/",
|
||||
DavResourceName::Scheduling.base_path()
|
||||
)
|
||||
.as_str()])
|
||||
@@ -210,24 +235,36 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
if assisted_discovery {
|
||||
props
|
||||
.get(DavProperty::Principal(PrincipalProperty::CalendarHomeSet))
|
||||
.with_values(
|
||||
[format!("D:href:{}/jane/", DavResourceName::Cal.base_path()).as_str()],
|
||||
.with_values([format!(
|
||||
"D:href:{}/jane%40example.com/",
|
||||
DavResourceName::Cal.base_path()
|
||||
)
|
||||
.as_str()])
|
||||
.with_status(StatusCode::OK);
|
||||
props
|
||||
.get(DavProperty::Principal(
|
||||
PrincipalProperty::AddressbookHomeSet,
|
||||
))
|
||||
.with_values([
|
||||
format!("D:href:{}/jane/", DavResourceName::Card.base_path()).as_str(),
|
||||
])
|
||||
.with_values([format!(
|
||||
"D:href:{}/jane%40example.com/",
|
||||
DavResourceName::Card.base_path()
|
||||
)
|
||||
.as_str()])
|
||||
.with_status(StatusCode::OK);
|
||||
} else {
|
||||
props
|
||||
.get(DavProperty::Principal(PrincipalProperty::CalendarHomeSet))
|
||||
.with_values([
|
||||
format!("D:href:{}/jane/", DavResourceName::Cal.base_path()).as_str(),
|
||||
format!("D:href:{}/support/", DavResourceName::Cal.base_path()).as_str(),
|
||||
format!(
|
||||
"D:href:{}/jane%40example.com/",
|
||||
DavResourceName::Cal.base_path()
|
||||
)
|
||||
.as_str(),
|
||||
format!(
|
||||
"D:href:{}/support%40example.com/",
|
||||
DavResourceName::Cal.base_path()
|
||||
)
|
||||
.as_str(),
|
||||
])
|
||||
.with_status(StatusCode::OK);
|
||||
props
|
||||
@@ -235,28 +272,49 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
PrincipalProperty::AddressbookHomeSet,
|
||||
))
|
||||
.with_values([
|
||||
format!("D:href:{}/jane/", DavResourceName::Card.base_path()).as_str(),
|
||||
format!("D:href:{}/support/", DavResourceName::Card.base_path()).as_str(),
|
||||
format!(
|
||||
"D:href:{}/jane%40example.com/",
|
||||
DavResourceName::Card.base_path()
|
||||
)
|
||||
.as_str(),
|
||||
format!(
|
||||
"D:href:{}/support%40example.com/",
|
||||
DavResourceName::Card.base_path()
|
||||
)
|
||||
.as_str(),
|
||||
])
|
||||
.with_status(StatusCode::OK);
|
||||
}
|
||||
|
||||
for (account, _, name, _) in TEST_USERS
|
||||
.iter()
|
||||
.filter(|(account, _, _, _)| ["jane", "support"].contains(account))
|
||||
for account_ in test
|
||||
.accounts
|
||||
.values()
|
||||
.filter(|account| ["jane@example.com", "support@example.com"].contains(&account.name()))
|
||||
{
|
||||
let path_card = format!("D:href:{}/{}/", DavResourceName::Card.base_path(), account);
|
||||
let path_cal = format!("D:href:{}/{}/", DavResourceName::Cal.base_path(), account);
|
||||
let account_name = account_.name().replace('@', "%40");
|
||||
let description = account_.description();
|
||||
|
||||
let path_card = format!(
|
||||
"D:href:{}/{}/",
|
||||
DavResourceName::Card.base_path(),
|
||||
account_name
|
||||
);
|
||||
let path_cal = format!(
|
||||
"D:href:{}/{}/",
|
||||
DavResourceName::Cal.base_path(),
|
||||
account_name
|
||||
);
|
||||
let path_pal = format!(
|
||||
"D:href:{}/{}/",
|
||||
DavResourceName::Principal.base_path(),
|
||||
account
|
||||
account_name
|
||||
);
|
||||
let props = response.properties(&format!("{}/{account}/", resource_type.base_path()));
|
||||
let props =
|
||||
response.properties(&format!("{}/{account_name}/", resource_type.base_path()));
|
||||
|
||||
props
|
||||
.get(DavProperty::WebDav(WebDavProperty::DisplayName))
|
||||
.with_values([*name])
|
||||
.with_values([description])
|
||||
.with_status(StatusCode::OK);
|
||||
props
|
||||
.get(DavProperty::WebDav(WebDavProperty::ResourceType))
|
||||
@@ -286,7 +344,7 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
.get(DavProperty::WebDav(WebDavProperty::Owner))
|
||||
.with_values([path_pal.as_str()])
|
||||
.with_status(StatusCode::OK);
|
||||
if *account == "jane" && !assisted_discovery {
|
||||
if account_name == "jane%40example.com" && !assisted_discovery {
|
||||
props
|
||||
.get(DavProperty::Principal(PrincipalProperty::CalendarHomeSet))
|
||||
.with_values([path_cal.as_str(), path_support_cal.as_str()])
|
||||
@@ -334,8 +392,8 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
.with_status(StatusCode::MULTI_STATUS)
|
||||
.into_propfind_response(None);
|
||||
response.with_hrefs([
|
||||
format!("{}/jane/", resource_type.base_path()).as_str(),
|
||||
format!("{}/support/", resource_type.base_path()).as_str(),
|
||||
format!("{}/jane%40example.com/", resource_type.base_path()).as_str(),
|
||||
format!("{}/support%40example.com/", resource_type.base_path()).as_str(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -350,8 +408,16 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
.with_status(StatusCode::MULTI_STATUS)
|
||||
.into_propfind_response(None);
|
||||
response.with_hrefs([
|
||||
format!("{}/jane/", DavResourceName::Principal.base_path()).as_str(),
|
||||
format!("{}/support/", DavResourceName::Principal.base_path()).as_str(),
|
||||
format!(
|
||||
"{}/jane%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
)
|
||||
.as_str(),
|
||||
format!(
|
||||
"{}/support%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
)
|
||||
.as_str(),
|
||||
]);
|
||||
|
||||
// Test 5: principal-search-property-set REPORT
|
||||
@@ -384,17 +450,24 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
.with_status(StatusCode::MULTI_STATUS)
|
||||
.into_propfind_response(None);
|
||||
response.with_hrefs([
|
||||
format!("{}/jane/", DavResourceName::Principal.base_path()).as_str(),
|
||||
format!("{}/john/", DavResourceName::Principal.base_path()).as_str(),
|
||||
format!(
|
||||
"{}/jane%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
)
|
||||
.as_str(),
|
||||
format!(
|
||||
"{}/john%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
)
|
||||
.as_str(),
|
||||
]);
|
||||
response
|
||||
.properties(&format!("{}/jane/", DavResourceName::Principal.base_path()))
|
||||
.properties(&format!(
|
||||
"{}/jane%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
))
|
||||
.get(DavProperty::WebDav(WebDavProperty::DisplayName))
|
||||
.with_values([TEST_USERS
|
||||
.iter()
|
||||
.find(|(account, _, _, _)| *account == "jane")
|
||||
.unwrap()
|
||||
.2])
|
||||
.with_values([test.account("jane@example.com").description()])
|
||||
.with_status(StatusCode::OK);
|
||||
client
|
||||
.request(
|
||||
@@ -405,10 +478,16 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
.await
|
||||
.with_status(StatusCode::MULTI_STATUS)
|
||||
.into_propfind_response(None)
|
||||
.with_hrefs([format!("{}/support/", DavResourceName::Principal.base_path()).as_str()]);
|
||||
.with_hrefs([format!(
|
||||
"{}/support%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
)
|
||||
.as_str()]);
|
||||
|
||||
client.delete_default_containers().await;
|
||||
client.delete_default_containers_by_account("support").await;
|
||||
client
|
||||
.delete_default_containers_by_account("support@example.com")
|
||||
.await;
|
||||
test.assert_is_empty().await;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{DavResponse, DummyWebDavClient, WebDavTest};
|
||||
use crate::webdav::{GenerateTestDavResource, TEST_ICAL_2, TEST_VTIMEZONE_1};
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use crate::utils::server::TestServer;
|
||||
use crate::utils::webdav::GenerateTestDavResource;
|
||||
use crate::webdav::{TEST_ICAL_2, TEST_VTIMEZONE_1};
|
||||
use dav_proto::schema::property::{
|
||||
CalDavProperty, CardDavProperty, DavProperty, PrincipalProperty, WebDavProperty,
|
||||
};
|
||||
@@ -14,8 +14,8 @@ use groupware::DavResourceName;
|
||||
use hyper::StatusCode;
|
||||
use types::dead_property::DeadElementTag;
|
||||
|
||||
pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
let client = test.client("jane");
|
||||
pub async fn test(test: &TestServer, assisted_discovery: bool) {
|
||||
let client = test.account("jane@example.com").webdav_client();
|
||||
|
||||
for resource_type in [
|
||||
DavResourceName::File,
|
||||
@@ -26,8 +26,8 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
"Running PROPFIND/PROPPATCH tests ({})...",
|
||||
resource_type.base_path()
|
||||
);
|
||||
let user_base_path = format!("{}/jane", resource_type.base_path());
|
||||
let group_base_path = format!("{}/support", resource_type.base_path());
|
||||
let user_base_path = format!("{}/jane%40example.com", resource_type.base_path());
|
||||
let group_base_path = format!("{}/support%40example.com", resource_type.base_path());
|
||||
|
||||
// Create a new test container and file
|
||||
let test_base_path = format!("{user_base_path}/PropFind_Folder/");
|
||||
@@ -208,14 +208,18 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
]);
|
||||
properties
|
||||
.get(DavProperty::WebDav(WebDavProperty::CurrentUserPrincipal))
|
||||
.with_values([
|
||||
format!("D:href:{}/jane/", DavResourceName::Principal.base_path()).as_str(),
|
||||
]);
|
||||
.with_values([format!(
|
||||
"D:href:{}/jane%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
)
|
||||
.as_str()]);
|
||||
properties
|
||||
.get(DavProperty::WebDav(WebDavProperty::Owner))
|
||||
.with_values([
|
||||
format!("D:href:{}/jane/", DavResourceName::Principal.base_path()).as_str(),
|
||||
]);
|
||||
.with_values([format!(
|
||||
"D:href:{}/jane%40example.com/",
|
||||
DavResourceName::Principal.base_path()
|
||||
)
|
||||
.as_str()]);
|
||||
properties
|
||||
.get(DavProperty::WebDav(WebDavProperty::SupportedPrivilegeSet))
|
||||
.is_not_empty();
|
||||
@@ -440,7 +444,7 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
] {
|
||||
properties.get(prop).with_some_values([
|
||||
format!(
|
||||
"D:response.D:href:{}/jane/",
|
||||
"D:response.D:href:{}/jane%40example.com/",
|
||||
DavResourceName::Principal.base_path(),
|
||||
)
|
||||
.as_str(),
|
||||
@@ -702,7 +706,9 @@ pub async fn test(test: &WebDavTest, assisted_discovery: bool) {
|
||||
}
|
||||
|
||||
client.delete_default_containers().await;
|
||||
client.delete_default_containers_by_account("support").await;
|
||||
client
|
||||
.delete_default_containers_by_account("support@example.com")
|
||||
.await;
|
||||
test.assert_is_empty().await;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,35 +6,45 @@
|
||||
|
||||
use types::collection::Collection;
|
||||
|
||||
use super::WebDavTest;
|
||||
use crate::utils::server::TestServer;
|
||||
|
||||
use crate::utils::webdav::GenerateTestDavResource;
|
||||
use crate::webdav::*;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
pub async fn test(test: &TestServer) {
|
||||
println!("Running PUT/GET tests...");
|
||||
let client = test.client("john");
|
||||
let client = test.account("john@example.com").webdav_client();
|
||||
|
||||
// Simple PUT
|
||||
let mut files = AHashMap::new();
|
||||
for (path, ct, content) in [
|
||||
("/dav/file/john/file1.txt", "text/plain", TEST_FILE_1),
|
||||
("/dav/file/john/file2.txt", "text/x-other", TEST_FILE_2),
|
||||
(
|
||||
"/dav/card/john/default/card1.vcf",
|
||||
"/dav/file/john%40example.com/file1.txt",
|
||||
"text/plain",
|
||||
TEST_FILE_1,
|
||||
),
|
||||
(
|
||||
"/dav/file/john%40example.com/file2.txt",
|
||||
"text/x-other",
|
||||
TEST_FILE_2,
|
||||
),
|
||||
(
|
||||
"/dav/card/john%40example.com/default/card1.vcf",
|
||||
"text/vcard; charset=utf-8",
|
||||
TEST_VCARD_1,
|
||||
),
|
||||
(
|
||||
"/dav/card/john/default/card2.vcf",
|
||||
"/dav/card/john%40example.com/default/card2.vcf",
|
||||
"text/vcard; charset=utf-8",
|
||||
TEST_VCARD_2,
|
||||
),
|
||||
(
|
||||
"/dav/cal/john/default/event1.ics",
|
||||
"/dav/cal/john%40example.com/default/event1.ics",
|
||||
"text/calendar; charset=utf-8",
|
||||
TEST_ICAL_1,
|
||||
),
|
||||
(
|
||||
"/dav/cal/john/default/event2.ics",
|
||||
"/dav/cal/john%40example.com/default/event2.ics",
|
||||
"text/calendar; charset=utf-8",
|
||||
TEST_ICAL_2,
|
||||
),
|
||||
@@ -62,9 +72,9 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// PUT under a non-existing parent should fail
|
||||
for (path, contents) in [
|
||||
("/dav/file/john/foo/file1.txt", TEST_FILE_1),
|
||||
("/dav/card/john/foo/card1.vcf", TEST_VCARD_1),
|
||||
("/dav/cal/john/foo/event1.ics", TEST_ICAL_1),
|
||||
("/dav/file/john%40example.com/foo/file1.txt", TEST_FILE_1),
|
||||
("/dav/card/john%40example.com/foo/card1.vcf", TEST_VCARD_1),
|
||||
("/dav/cal/john%40example.com/foo/event1.ics", TEST_ICAL_1),
|
||||
] {
|
||||
client
|
||||
.request("PUT", path, contents)
|
||||
@@ -74,13 +84,16 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// PUT under resources should fail
|
||||
for (path, contents) in [
|
||||
("/dav/file/john/file1.txt/other-file.txt", TEST_FILE_1),
|
||||
(
|
||||
"/dav/card/john/default/card1.vcf/other-file.vcf",
|
||||
"/dav/file/john%40example.com/file1.txt/other-file.txt",
|
||||
TEST_FILE_1,
|
||||
),
|
||||
(
|
||||
"/dav/card/john%40example.com/default/card1.vcf/other-file.vcf",
|
||||
TEST_VCARD_1,
|
||||
),
|
||||
(
|
||||
"/dav/cal/john/default/event1.ics/other-file.ical",
|
||||
"/dav/cal/john%40example.com/default/event1.ics/other-file.ical",
|
||||
TEST_ICAL_1,
|
||||
),
|
||||
] {
|
||||
@@ -93,13 +106,13 @@ pub async fn test(test: &WebDavTest) {
|
||||
// PUT a non-vCard/iCalendar file should fail
|
||||
for (path, ct, content, precondition) in [
|
||||
(
|
||||
"/dav/card/john/card3.vcf",
|
||||
"/dav/card/john%40example.com/card3.vcf",
|
||||
"text/vcard; charset=utf-8",
|
||||
TEST_FILE_1,
|
||||
"B:supported-address-data",
|
||||
),
|
||||
(
|
||||
"/dav/cal/john/event3.ics",
|
||||
"/dav/cal/john%40example.com/event3.ics",
|
||||
"text/calendar; charset=utf-8",
|
||||
TEST_FILE_2,
|
||||
"A:supported-calendar-data",
|
||||
@@ -116,19 +129,19 @@ pub async fn test(test: &WebDavTest) {
|
||||
let conf = &test.server.core.groupware;
|
||||
for (path, contents, max_size, expect) in [
|
||||
(
|
||||
"/dav/file/john/chunky-file1.txt",
|
||||
"/dav/file/john%40example.com/chunky-file1.txt",
|
||||
TEST_FILE_1,
|
||||
conf.max_file_size,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"/dav/card/john/chunky-card1.vcf",
|
||||
"/dav/card/john%40example.com/chunky-card1.vcf",
|
||||
TEST_VCARD_1,
|
||||
conf.max_vcard_size,
|
||||
Some("B:max-resource-size"),
|
||||
),
|
||||
(
|
||||
"/dav/cal/john/chunky-event1.ics",
|
||||
"/dav/cal/john%40example.com/chunky-event1.ics",
|
||||
TEST_ICAL_1,
|
||||
conf.max_ical_size,
|
||||
Some("A:max-resource-size"),
|
||||
@@ -152,13 +165,16 @@ pub async fn test(test: &WebDavTest) {
|
||||
}
|
||||
|
||||
// PUT requests cannot exceed quota
|
||||
let mike_noquota = test.client("mike");
|
||||
let mike_noquota = test.account("mike@example.com").webdav_client();
|
||||
for resource_type in [
|
||||
DavResourceName::File,
|
||||
DavResourceName::Card,
|
||||
DavResourceName::Cal,
|
||||
] {
|
||||
let path = format!("{}/mike/quota-test/", resource_type.base_path());
|
||||
let path = format!(
|
||||
"{}/mike%40example.com/quota-test/",
|
||||
resource_type.base_path()
|
||||
);
|
||||
mike_noquota
|
||||
.mkcol("MKCOL", &path, [], [])
|
||||
.await
|
||||
@@ -199,25 +215,29 @@ pub async fn test(test: &WebDavTest) {
|
||||
|
||||
// PUT precondition enforcement
|
||||
let modseq = [
|
||||
test.resources("john", Collection::FileNode)
|
||||
test.resources("john@example.com", Collection::FileNode)
|
||||
.await
|
||||
.highest_change_id,
|
||||
test.resources("john", Collection::Calendar)
|
||||
test.resources("john@example.com", Collection::Calendar)
|
||||
.await
|
||||
.highest_change_id,
|
||||
test.resources("john", Collection::AddressBook)
|
||||
test.resources("john@example.com", Collection::AddressBook)
|
||||
.await
|
||||
.highest_change_id,
|
||||
];
|
||||
for (path, ct, content) in [
|
||||
("/dav/file/john/file1.txt", "text/plain", TEST_FILE_1),
|
||||
(
|
||||
"/dav/card/john/default/card1.vcf",
|
||||
"/dav/file/john%40example.com/file1.txt",
|
||||
"text/plain",
|
||||
TEST_FILE_1,
|
||||
),
|
||||
(
|
||||
"/dav/card/john%40example.com/default/card1.vcf",
|
||||
"text/vcard; charset=utf-8",
|
||||
TEST_VCARD_1,
|
||||
),
|
||||
(
|
||||
"/dav/cal/john/default/event1.ics",
|
||||
"/dav/cal/john%40example.com/default/event1.ics",
|
||||
"text/calendar; charset=utf-8",
|
||||
TEST_ICAL_1,
|
||||
),
|
||||
@@ -271,13 +291,13 @@ pub async fn test(test: &WebDavTest) {
|
||||
}
|
||||
assert_eq!(
|
||||
[
|
||||
test.resources("john", Collection::FileNode)
|
||||
test.resources("john@example.com", Collection::FileNode)
|
||||
.await
|
||||
.highest_change_id,
|
||||
test.resources("john", Collection::Calendar)
|
||||
test.resources("john@example.com", Collection::Calendar)
|
||||
.await
|
||||
.highest_change_id,
|
||||
test.resources("john", Collection::AddressBook)
|
||||
test.resources("john@example.com", Collection::AddressBook)
|
||||
.await
|
||||
.highest_change_id,
|
||||
],
|
||||
@@ -315,18 +335,18 @@ pub async fn test(test: &WebDavTest) {
|
||||
// PUT requests require unique UIDs
|
||||
for (path, ct, content, precond_key, precond_value) in [
|
||||
(
|
||||
"/dav/card/john/default/card5.vcf",
|
||||
"/dav/card/john%40example.com/default/card5.vcf",
|
||||
"text/vcard; charset=utf-8",
|
||||
TEST_VCARD_1,
|
||||
"B:no-uid-conflict.D:href",
|
||||
"/dav/card/john/default/card1.vcf",
|
||||
"/dav/card/john%40example.com/default/card1.vcf",
|
||||
),
|
||||
(
|
||||
"/dav/cal/john/default/event5.ics",
|
||||
"/dav/cal/john%40example.com/default/event5.ics",
|
||||
"text/calendar; charset=utf-8",
|
||||
TEST_ICAL_1,
|
||||
"A:no-uid-conflict.D:href",
|
||||
"/dav/cal/john/default/event1.ics",
|
||||
"/dav/cal/john%40example.com/default/event1.ics",
|
||||
),
|
||||
] {
|
||||
client
|
||||
@@ -345,7 +365,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
client
|
||||
.request_with_headers(
|
||||
"PUT",
|
||||
"/dav/cal/john/default/invalid.ics",
|
||||
"/dav/cal/john%40example.com/default/invalid.ics",
|
||||
[
|
||||
("content-type", "text/calendar; charset=utf-8"),
|
||||
("if-none-match", "*"),
|
||||
@@ -375,7 +395,7 @@ END:VCALENDAR
|
||||
client
|
||||
.request_with_headers(
|
||||
"PUT",
|
||||
"/dav/cal/john/default/invalid.ics",
|
||||
"/dav/cal/john%40example.com/default/invalid.ics",
|
||||
[
|
||||
("content-type", "text/calendar; charset=utf-8"),
|
||||
("if-none-match", "*"),
|
||||
@@ -403,13 +423,25 @@ END:VCALENDAR
|
||||
|
||||
// Deleting unknown/invalid destinations should fail
|
||||
for (path, expect) in [
|
||||
("/dav/file/john/unknown.txt", StatusCode::NOT_FOUND),
|
||||
("/dav/card/john/default/unknown.txt", StatusCode::NOT_FOUND),
|
||||
("/dav/cal/john/default/unknown.txt", StatusCode::NOT_FOUND),
|
||||
("/dav/file/john", StatusCode::FORBIDDEN),
|
||||
("/dav/cal/john", StatusCode::FORBIDDEN),
|
||||
("/dav/card/john", StatusCode::FORBIDDEN),
|
||||
("/dav/pal/john", StatusCode::METHOD_NOT_ALLOWED),
|
||||
(
|
||||
"/dav/file/john%40example.com/unknown.txt",
|
||||
StatusCode::NOT_FOUND,
|
||||
),
|
||||
(
|
||||
"/dav/card/john%40example.com/default/unknown.txt",
|
||||
StatusCode::NOT_FOUND,
|
||||
),
|
||||
(
|
||||
"/dav/cal/john%40example.com/default/unknown.txt",
|
||||
StatusCode::NOT_FOUND,
|
||||
),
|
||||
("/dav/file/john%40example.com", StatusCode::FORBIDDEN),
|
||||
("/dav/cal/john%40example.com", StatusCode::FORBIDDEN),
|
||||
("/dav/card/john%40example.com", StatusCode::FORBIDDEN),
|
||||
(
|
||||
"/dav/pal/john%40example.com",
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
),
|
||||
("/dav/file", StatusCode::FORBIDDEN),
|
||||
("/dav/cal", StatusCode::FORBIDDEN),
|
||||
("/dav/card", StatusCode::FORBIDDEN),
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{DavResponse, DummyWebDavClient, WebDavTest};
|
||||
use crate::webdav::GenerateTestDavResource;
|
||||
use crate::utils::{server::TestServer, webdav::GenerateTestDavResource};
|
||||
|
||||
use ahash::AHashSet;
|
||||
use dav_proto::Depth;
|
||||
use groupware::DavResourceName;
|
||||
use hyper::StatusCode;
|
||||
|
||||
pub async fn test(test: &WebDavTest) {
|
||||
let client = test.client("john");
|
||||
pub async fn test(test: &TestServer) {
|
||||
let client = test.account("john@example.com").webdav_client();
|
||||
|
||||
for resource_type in [
|
||||
DavResourceName::File,
|
||||
@@ -23,7 +23,7 @@ pub async fn test(test: &WebDavTest) {
|
||||
"Running REPORT sync-collection tests ({})...",
|
||||
resource_type.base_path()
|
||||
);
|
||||
let user_base_path = format!("{}/john/", resource_type.base_path());
|
||||
let user_base_path = format!("{}/john%40example.com/", resource_type.base_path());
|
||||
|
||||
// Test 1: Initial sync
|
||||
let response = client
|
||||
|
||||
Reference in New Issue
Block a user