JMAP for Calendat tests (Calendars and Events)
This commit is contained in:
@@ -4,11 +4,369 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::jmap::{JMAPTest, JmapUtils};
|
||||
use jmap_proto::request::method::MethodObject;
|
||||
use crate::jmap::{ChangeType, JMAPTest, JmapUtils};
|
||||
use jmap_proto::{object::calendar::CalendarProperty, request::method::MethodObject};
|
||||
use serde_json::json;
|
||||
|
||||
pub async fn test(params: &mut JMAPTest) {
|
||||
println!("Running tests...");
|
||||
println!("Running Calendar tests...");
|
||||
let account = params.account("jdoe@example.com");
|
||||
|
||||
// Make sure the default calendar exists
|
||||
let response = account
|
||||
.jmap_get(
|
||||
MethodObject::Calendar,
|
||||
[
|
||||
CalendarProperty::Id,
|
||||
CalendarProperty::Name,
|
||||
CalendarProperty::Description,
|
||||
CalendarProperty::SortOrder,
|
||||
CalendarProperty::Color,
|
||||
CalendarProperty::TimeZone,
|
||||
CalendarProperty::IsSubscribed,
|
||||
CalendarProperty::IsDefault,
|
||||
CalendarProperty::IsVisible,
|
||||
CalendarProperty::IncludeInAvailability,
|
||||
CalendarProperty::DefaultAlertsWithTime,
|
||||
CalendarProperty::DefaultAlertsWithoutTime,
|
||||
],
|
||||
Vec::<&str>::new(),
|
||||
)
|
||||
.await;
|
||||
let list = response.list();
|
||||
assert_eq!(list.len(), 1);
|
||||
let default_calendar_id = list[0].id().to_string();
|
||||
assert_eq!(
|
||||
list[0],
|
||||
json!({
|
||||
"id": default_calendar_id,
|
||||
"name": "Stalwart Calendar (jdoe@example.com)",
|
||||
"description": null,
|
||||
"sortOrder": 0,
|
||||
"isSubscribed": false,
|
||||
"isDefault": true,
|
||||
"color": null,
|
||||
"timeZone": null,
|
||||
"isVisible": true,
|
||||
"includeInAvailability": "all",
|
||||
"defaultAlertsWithTime": {},
|
||||
"defaultAlertsWithoutTime": {}
|
||||
})
|
||||
);
|
||||
let change_id = response.state();
|
||||
|
||||
// Create Calendar
|
||||
let calendar_id = account
|
||||
.jmap_create(
|
||||
MethodObject::Calendar,
|
||||
[json!({
|
||||
"name": "Test calendar",
|
||||
"description": "My personal calendar",
|
||||
"sortOrder": 1,
|
||||
"isSubscribed": true,
|
||||
"color": "#ff0000",
|
||||
"timeZone": "Indian/Christmas",
|
||||
"isVisible": false,
|
||||
"includeInAvailability": "attending",
|
||||
"defaultAlertsWithTime": {
|
||||
"0": {
|
||||
"action": "display",
|
||||
"trigger": {
|
||||
"relativeTo": "start",
|
||||
"offset": "PT15M"
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
"action": "email",
|
||||
"trigger": {
|
||||
"relativeTo": "end",
|
||||
"offset": "PT30M"
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultAlertsWithoutTime": {
|
||||
"0": {
|
||||
"action": "display",
|
||||
"trigger": {
|
||||
"relativeTo": "start",
|
||||
"offset": "P1D"
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
"action": "email",
|
||||
"trigger": {
|
||||
"relativeTo": "end",
|
||||
"offset": "P2D"
|
||||
}
|
||||
}
|
||||
}
|
||||
})],
|
||||
)
|
||||
.await
|
||||
.created(0)
|
||||
.id()
|
||||
.to_string();
|
||||
|
||||
// Validate changes
|
||||
assert_eq!(
|
||||
account
|
||||
.jmap_changes(MethodObject::Calendar, change_id)
|
||||
.await
|
||||
.changes()
|
||||
.collect::<Vec<_>>(),
|
||||
[ChangeType::Created(&calendar_id)]
|
||||
);
|
||||
|
||||
// Get Calendar
|
||||
let response = account
|
||||
.jmap_get(
|
||||
MethodObject::Calendar,
|
||||
[
|
||||
CalendarProperty::Id,
|
||||
CalendarProperty::Name,
|
||||
CalendarProperty::Description,
|
||||
CalendarProperty::SortOrder,
|
||||
CalendarProperty::Color,
|
||||
CalendarProperty::TimeZone,
|
||||
CalendarProperty::IsSubscribed,
|
||||
CalendarProperty::IsDefault,
|
||||
CalendarProperty::IsVisible,
|
||||
CalendarProperty::IncludeInAvailability,
|
||||
CalendarProperty::DefaultAlertsWithTime,
|
||||
CalendarProperty::DefaultAlertsWithoutTime,
|
||||
],
|
||||
[&calendar_id],
|
||||
)
|
||||
.await;
|
||||
|
||||
response.list()[0].assert_is_equal(json!({
|
||||
"name": "Test calendar",
|
||||
"description": "My personal calendar",
|
||||
"sortOrder": 1,
|
||||
"isSubscribed": true,
|
||||
"isVisible": false,
|
||||
"isDefault": false,
|
||||
"color": "#ff0000",
|
||||
"timeZone": "Indian/Christmas",
|
||||
"includeInAvailability": "attending",
|
||||
"defaultAlertsWithTime": {
|
||||
"0": {
|
||||
"@type": "Alert",
|
||||
"action": "display",
|
||||
"trigger": {
|
||||
"@type": "OffsetTrigger",
|
||||
"relativeTo": "start",
|
||||
"offset": "PT15M"
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
"@type": "Alert",
|
||||
"action": "email",
|
||||
"trigger": {
|
||||
"@type": "OffsetTrigger",
|
||||
"relativeTo": "end",
|
||||
"offset": "PT30M"
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultAlertsWithoutTime": {
|
||||
"0": {
|
||||
"@type": "Alert",
|
||||
"action": "display",
|
||||
"trigger": {
|
||||
"@type": "OffsetTrigger",
|
||||
"relativeTo": "start",
|
||||
"offset": "P1D"
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
"@type": "Alert",
|
||||
"action": "email",
|
||||
"trigger": {
|
||||
"@type": "OffsetTrigger",
|
||||
"relativeTo": "end",
|
||||
"offset": "P2D"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": calendar_id,
|
||||
}));
|
||||
|
||||
// Update Calendar and set it as default
|
||||
account
|
||||
.jmap_update(
|
||||
MethodObject::Calendar,
|
||||
[(
|
||||
calendar_id.as_str(),
|
||||
json!({
|
||||
"name": "Updated calendar",
|
||||
"description": "My updated personal calendar",
|
||||
"sortOrder": 2,
|
||||
"isSubscribed": false,
|
||||
"isVisible": true,
|
||||
"timeZone": null,
|
||||
"color": null,
|
||||
"includeInAvailability": "none",
|
||||
"defaultAlertsWithTime": {
|
||||
"0": {
|
||||
"action": "email",
|
||||
"trigger": {
|
||||
"relativeTo": "start",
|
||||
"offset": "PT10M"
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultAlertsWithoutTime/0": {
|
||||
"action": "email",
|
||||
"trigger": {
|
||||
"relativeTo": "start",
|
||||
"offset": "P3D"
|
||||
}
|
||||
},
|
||||
"defaultAlertsWithoutTime/1": null,
|
||||
"defaultAlertsWithoutTime/2": {
|
||||
"action": "display",
|
||||
"trigger": {
|
||||
"relativeTo": "end",
|
||||
"offset": "P1W"
|
||||
}
|
||||
}
|
||||
}),
|
||||
)],
|
||||
[("onSuccessSetIsDefault", calendar_id.as_str())],
|
||||
)
|
||||
.await
|
||||
.updated(&calendar_id);
|
||||
|
||||
// Validate changes
|
||||
let response = account
|
||||
.jmap_get(
|
||||
MethodObject::Calendar,
|
||||
[
|
||||
CalendarProperty::Id,
|
||||
CalendarProperty::Name,
|
||||
CalendarProperty::Description,
|
||||
CalendarProperty::SortOrder,
|
||||
CalendarProperty::Color,
|
||||
CalendarProperty::TimeZone,
|
||||
CalendarProperty::IsSubscribed,
|
||||
CalendarProperty::IsDefault,
|
||||
CalendarProperty::IsVisible,
|
||||
CalendarProperty::IncludeInAvailability,
|
||||
CalendarProperty::DefaultAlertsWithTime,
|
||||
CalendarProperty::DefaultAlertsWithoutTime,
|
||||
],
|
||||
[&calendar_id, &default_calendar_id],
|
||||
)
|
||||
.await;
|
||||
response.list()[0].assert_is_equal(json!({
|
||||
"id": calendar_id,
|
||||
"name": "Updated calendar",
|
||||
"description": "My updated personal calendar",
|
||||
"sortOrder": 2,
|
||||
"isSubscribed": false,
|
||||
"isDefault": true,
|
||||
"color": null,
|
||||
"timeZone": null,
|
||||
"isVisible": true,
|
||||
"includeInAvailability": "none",
|
||||
"defaultAlertsWithTime": {
|
||||
"0": {
|
||||
"@type": "Alert",
|
||||
"action": "email",
|
||||
"trigger": {
|
||||
"@type": "OffsetTrigger",
|
||||
"relativeTo": "start",
|
||||
"offset": "PT10M"
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultAlertsWithoutTime": {
|
||||
"0": {
|
||||
"@type": "Alert",
|
||||
"action": "email",
|
||||
"trigger": {
|
||||
"@type": "OffsetTrigger",
|
||||
"relativeTo": "start",
|
||||
"offset": "P3D"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"@type": "Alert",
|
||||
"action": "display",
|
||||
"trigger": {
|
||||
"@type": "OffsetTrigger",
|
||||
"relativeTo": "end",
|
||||
"offset": "P1W"
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
response.list()[1].assert_is_equal(json!({
|
||||
"id": default_calendar_id,
|
||||
"name": "Stalwart Calendar (jdoe@example.com)",
|
||||
"description": (),
|
||||
"sortOrder": 0,
|
||||
"isSubscribed": false,
|
||||
"isDefault": false,
|
||||
"color": null,
|
||||
"timeZone": null,
|
||||
"isVisible": true,
|
||||
"includeInAvailability": "all",
|
||||
"defaultAlertsWithTime": {},
|
||||
"defaultAlertsWithoutTime": {}
|
||||
}));
|
||||
|
||||
// Create an event
|
||||
let _ = account
|
||||
.jmap_create(
|
||||
MethodObject::CalendarEvent,
|
||||
[json!({
|
||||
"calendarIds": {
|
||||
&calendar_id: true
|
||||
},
|
||||
"@type": "Event",
|
||||
"uid": "a8df6573-0474-496d-8496-033ad45d7fea",
|
||||
"updated": "2020-01-02T18:23:04Z",
|
||||
"title": "Some event",
|
||||
"start": "2020-01-15T13:00:00",
|
||||
"timeZone": "America/New_York",
|
||||
"duration": "PT1H"
|
||||
})],
|
||||
)
|
||||
.await
|
||||
.created(0)
|
||||
.id();
|
||||
|
||||
// Try destroying the calendar (should fail)
|
||||
assert_eq!(
|
||||
account
|
||||
.jmap_destroy(
|
||||
MethodObject::Calendar,
|
||||
[&calendar_id],
|
||||
Vec::<(&str, &str)>::new(),
|
||||
)
|
||||
.await
|
||||
.not_destroyed(&calendar_id)
|
||||
.typ(),
|
||||
"calendarHasEvent"
|
||||
);
|
||||
|
||||
// Destroy using force
|
||||
assert_eq!(
|
||||
account
|
||||
.jmap_destroy(
|
||||
MethodObject::Calendar,
|
||||
[&calendar_id],
|
||||
[("onDestroyRemoveEvents", true)],
|
||||
)
|
||||
.await
|
||||
.destroyed()
|
||||
.collect::<Vec<_>>(),
|
||||
vec![&calendar_id]
|
||||
);
|
||||
|
||||
// Destroy all mailboxes
|
||||
account.destroy_all_calendars().await;
|
||||
params.assert_is_empty().await;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,780 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::jmap::{JMAPTest, JmapUtils};
|
||||
use crate::{
|
||||
jmap::{ChangeType, IntoJmapSet, JMAPTest, JmapUtils},
|
||||
webdav::DummyWebDavClient,
|
||||
};
|
||||
use ahash::AHashSet;
|
||||
use calcard::jscalendar::JSCalendarProperty;
|
||||
use groupware::cache::GroupwareCache;
|
||||
use hyper::StatusCode;
|
||||
use jmap_proto::request::method::MethodObject;
|
||||
use serde_json::json;
|
||||
use serde_json::{Value, json};
|
||||
use types::{collection::SyncCollection, id::Id};
|
||||
|
||||
pub async fn test(params: &mut JMAPTest) {
|
||||
println!("Running tests...");
|
||||
let account = params.account("jdoe@example.com");
|
||||
|
||||
// Create test calendars
|
||||
let response = account
|
||||
.jmap_create(
|
||||
MethodObject::Calendar,
|
||||
[
|
||||
json!({
|
||||
"name": "Holy Calendar, Batman!",
|
||||
"timeZone": "Europe/Vatican",
|
||||
}),
|
||||
json!({
|
||||
"name": "Calendar with Alerts",
|
||||
"defaultAlertsWithTime": {
|
||||
"abc": {
|
||||
"action": "display",
|
||||
"trigger": {
|
||||
"relativeTo": "start",
|
||||
"offset": "PT15M"
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
let calendar1_id = response.created(0).id().to_string();
|
||||
let calendar2_id = response.created(1).id().to_string();
|
||||
|
||||
// Obtain state
|
||||
let change_id = account
|
||||
.jmap_get(
|
||||
MethodObject::CalendarEvent,
|
||||
Vec::<&str>::new(),
|
||||
Vec::<&str>::new(),
|
||||
)
|
||||
.await
|
||||
.state()
|
||||
.to_string();
|
||||
|
||||
// Create test events
|
||||
let event_1 = test_jscalendar_1().with_property(
|
||||
JSCalendarProperty::<Id>::CalendarIds,
|
||||
[calendar1_id.as_str()].into_jmap_set(),
|
||||
);
|
||||
let event_2 = test_jscalendar_2().with_property(
|
||||
JSCalendarProperty::<Id>::CalendarIds,
|
||||
[calendar2_id.as_str()].into_jmap_set(),
|
||||
);
|
||||
let event_3 = test_jscalendar_3().with_property(
|
||||
JSCalendarProperty::<Id>::CalendarIds,
|
||||
[calendar1_id.as_str(), calendar2_id.as_str()].into_jmap_set(),
|
||||
);
|
||||
let response = account
|
||||
.jmap_create(
|
||||
MethodObject::CalendarEvent,
|
||||
[
|
||||
event_1
|
||||
.clone()
|
||||
.with_property(JSCalendarProperty::<Id>::IsDraft, true)
|
||||
.with_property(JSCalendarProperty::<Id>::MayInviteSelf, true)
|
||||
.with_property(JSCalendarProperty::<Id>::MayInviteOthers, true)
|
||||
.with_property(JSCalendarProperty::<Id>::HideAttendees, true),
|
||||
event_2
|
||||
.clone()
|
||||
.with_property(JSCalendarProperty::<Id>::UseDefaultAlerts, true),
|
||||
event_3.clone(),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
let event_1_id = response.created(0).id().to_string();
|
||||
let event_2_id = response.created(1).id().to_string();
|
||||
let event_3_id = response.created(2).id().to_string();
|
||||
|
||||
// Validate changes
|
||||
assert_eq!(
|
||||
account
|
||||
.jmap_changes(MethodObject::CalendarEvent, &change_id)
|
||||
.await
|
||||
.changes()
|
||||
.collect::<AHashSet<_>>(),
|
||||
[
|
||||
ChangeType::Created(&event_1_id),
|
||||
ChangeType::Created(&event_2_id),
|
||||
ChangeType::Created(&event_3_id)
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<AHashSet<_>>(),
|
||||
);
|
||||
|
||||
// Verify event contents
|
||||
let response = account
|
||||
.jmap_get(
|
||||
MethodObject::CalendarEvent,
|
||||
Vec::<&str>::new(),
|
||||
[&event_1_id, &event_2_id, &event_3_id],
|
||||
)
|
||||
.await;
|
||||
|
||||
response.list()[0].assert_is_equal(
|
||||
event_1
|
||||
.with_property(JSCalendarProperty::<Id>::Id, event_1_id.as_str())
|
||||
.with_property(JSCalendarProperty::<Id>::IsDraft, true)
|
||||
.with_property(JSCalendarProperty::<Id>::IsOrigin, true),
|
||||
);
|
||||
response.list()[1].assert_is_equal(
|
||||
event_2
|
||||
.with_property(JSCalendarProperty::<Id>::Id, event_2_id.as_str())
|
||||
.with_property(JSCalendarProperty::<Id>::IsDraft, false)
|
||||
.with_property(JSCalendarProperty::<Id>::IsOrigin, true)
|
||||
.with_property(
|
||||
JSCalendarProperty::<Id>::Alerts,
|
||||
json!({
|
||||
"k1": {
|
||||
"action": "display",
|
||||
"trigger": {
|
||||
"@type": "OffsetTrigger",
|
||||
"offset": "PT15M"
|
||||
},
|
||||
"@type": "Alert"
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
response.list()[2].assert_is_equal(
|
||||
event_3
|
||||
.with_property(JSCalendarProperty::<Id>::Id, event_3_id.as_str())
|
||||
.with_property(JSCalendarProperty::<Id>::IsDraft, false)
|
||||
.with_property(JSCalendarProperty::<Id>::IsOrigin, false),
|
||||
);
|
||||
|
||||
// Verify JMAP for Calendars properties
|
||||
let response = account
|
||||
.jmap_get(
|
||||
MethodObject::CalendarEvent,
|
||||
[
|
||||
JSCalendarProperty::<Id>::Id,
|
||||
JSCalendarProperty::MayInviteSelf,
|
||||
JSCalendarProperty::MayInviteOthers,
|
||||
JSCalendarProperty::HideAttendees,
|
||||
JSCalendarProperty::UtcStart,
|
||||
JSCalendarProperty::UtcEnd,
|
||||
],
|
||||
[&event_1_id, &event_2_id, &event_3_id],
|
||||
)
|
||||
.await;
|
||||
response.list()[0].assert_is_equal(json!({
|
||||
"id": &event_1_id,
|
||||
"mayInviteSelf": true,
|
||||
"mayInviteOthers": true,
|
||||
"hideAttendees": true,
|
||||
"utcStart": "2006-01-02T15:00:00Z",
|
||||
"utcEnd": "2006-01-02T16:00:00Z"
|
||||
}));
|
||||
response.list()[1].assert_is_equal(json!({
|
||||
"id": &event_2_id,
|
||||
"mayInviteSelf": false,
|
||||
"mayInviteOthers": false,
|
||||
"hideAttendees": false,
|
||||
"utcStart": "2006-01-02T17:00:00Z",
|
||||
"utcEnd": "2006-01-02T18:00:00Z"
|
||||
}));
|
||||
response.list()[2].assert_is_equal(json!({
|
||||
"id": &event_3_id,
|
||||
"mayInviteSelf": false,
|
||||
"mayInviteOthers": false,
|
||||
"hideAttendees": false,
|
||||
"utcStart": "2006-01-04T15:00:00Z",
|
||||
"utcEnd": "2006-01-04T16:00:00Z"
|
||||
}));
|
||||
|
||||
// Test /get parameters
|
||||
let response = account
|
||||
.jmap_method_calls(json!([[
|
||||
"CalendarEvent/get",
|
||||
{
|
||||
"properties": ["id", "title", "recurrenceOverrides", "participants"],
|
||||
"ids": [&event_2_id, &event_3_id],
|
||||
"recurrenceOverridesBefore": "2006-01-07T00:00:00Z",
|
||||
"recurrenceOverridesAfter": "2006-01-06T00:00:00Z",
|
||||
"reduceParticipants": true,
|
||||
},
|
||||
"0"
|
||||
]]))
|
||||
.await;
|
||||
response.list_array().assert_is_equal(json!([
|
||||
{
|
||||
"title": "Event #2",
|
||||
"recurrenceOverrides": {
|
||||
"2006-01-06T12:00:00": {
|
||||
"updated": "2006-02-06T00:11:21Z",
|
||||
"start": "2006-01-06T14:00:00",
|
||||
"title": "Event #2 bis bis",
|
||||
"duration": "PT1H"
|
||||
}
|
||||
},
|
||||
"id": "c"
|
||||
},
|
||||
{
|
||||
"title": "Event #3",
|
||||
"participants": {
|
||||
"3f5bc8c0-c722-5345-b7d9-5a899db08a30": {
|
||||
"calendarAddress": "mailto:cyrus@example.com",
|
||||
"@type": "Participant"
|
||||
}
|
||||
},
|
||||
"id": "d"
|
||||
}
|
||||
]));
|
||||
|
||||
// Creating an event without calendar should fail
|
||||
assert_eq!(
|
||||
account
|
||||
.jmap_create(
|
||||
MethodObject::CalendarEvent,
|
||||
[json!({
|
||||
"title": "Event #5",
|
||||
"start": "2006-01-22T10:00:00",
|
||||
"duration": "PT1H",
|
||||
"timeZone": "US/Eastern",
|
||||
"calendarIds": {},
|
||||
}),],
|
||||
)
|
||||
.await
|
||||
.not_created(0)
|
||||
.description(),
|
||||
"Event has to belong to at least one calendar."
|
||||
);
|
||||
|
||||
// Creating an event with a duplicate UID should fail
|
||||
assert_eq!(
|
||||
account
|
||||
.jmap_create(
|
||||
MethodObject::CalendarEvent,
|
||||
[json!({
|
||||
"title": "Event #5",
|
||||
"start": "2006-01-22T10:00:00",
|
||||
"duration": "PT1H",
|
||||
"timeZone": "US/Eastern",
|
||||
"uid": "00959BC664CA650E933C892C@example.com",
|
||||
"calendarIds": {
|
||||
&calendar1_id: true
|
||||
},
|
||||
})],
|
||||
)
|
||||
.await
|
||||
.not_created(0)
|
||||
.description(),
|
||||
"An event with UID 00959BC664CA650E933C892C@example.com already exists."
|
||||
);
|
||||
|
||||
// Patching tests
|
||||
let response = account
|
||||
.jmap_update(
|
||||
MethodObject::CalendarEvent,
|
||||
[
|
||||
(
|
||||
&event_1_id,
|
||||
json!({
|
||||
"isDraft": false,
|
||||
"mayInviteSelf": false,
|
||||
"mayInviteOthers": false,
|
||||
"hideAttendees": false,
|
||||
"description": null,
|
||||
"title": "Event one",
|
||||
"keywords": {"work": true},
|
||||
format!("calendarIds/{calendar2_id}"): true
|
||||
}),
|
||||
),
|
||||
(
|
||||
&event_2_id,
|
||||
json!({
|
||||
"calendarIds": {
|
||||
&calendar1_id: true,
|
||||
&calendar2_id: true
|
||||
},
|
||||
"title": "Event two",
|
||||
"description": "Updated description",
|
||||
"recurrenceOverrides/2006-01-04T12:00:00/title":
|
||||
"Event two overridden",
|
||||
"recurrenceOverrides/2006-01-06T12:00:00/title":
|
||||
"Event two overridden twice",
|
||||
|
||||
}),
|
||||
),
|
||||
(
|
||||
&event_3_id,
|
||||
json!({
|
||||
format!("calendarIds/{calendar2_id}"): false,
|
||||
"title": "Event three",
|
||||
"utcStart": "2006-01-04T14:00:00Z",
|
||||
"utcEnd": "2006-01-04T16:00:00Z",
|
||||
"participants/3f5bc8c0-c722-5345-b7d9-5a899db08a30/roles/chair": false,
|
||||
"participants/3f5bc8c0-c722-5345-b7d9-5a899db08a30/roles/owner": true,
|
||||
"participants/ec5e7db5-22a3-5ed5-89bf-c8894ab86805" : null,
|
||||
"participants/7f2bd210-6c66-5b64-8562-0176b74462b1": {
|
||||
"calendarAddress": "mailto:rupert@example.com",
|
||||
"@type": "Participant",
|
||||
"roles": {
|
||||
"attendee": true
|
||||
},
|
||||
"participationStatus": "needs-action"
|
||||
}
|
||||
}),
|
||||
),
|
||||
],
|
||||
Vec::<(&str, &str)>::new(),
|
||||
)
|
||||
.await;
|
||||
|
||||
response.updated(&event_1_id);
|
||||
response.updated(&event_2_id);
|
||||
response.updated(&event_3_id);
|
||||
|
||||
// Verify patches
|
||||
let response = account
|
||||
.jmap_get(
|
||||
MethodObject::CalendarEvent,
|
||||
[
|
||||
JSCalendarProperty::<Id>::Id,
|
||||
JSCalendarProperty::CalendarIds,
|
||||
JSCalendarProperty::Title,
|
||||
JSCalendarProperty::Start,
|
||||
JSCalendarProperty::Description,
|
||||
JSCalendarProperty::Keywords,
|
||||
JSCalendarProperty::RecurrenceOverrides,
|
||||
JSCalendarProperty::Participants,
|
||||
JSCalendarProperty::MayInviteOthers,
|
||||
JSCalendarProperty::MayInviteSelf,
|
||||
JSCalendarProperty::HideAttendees,
|
||||
JSCalendarProperty::IsDraft,
|
||||
],
|
||||
[&event_1_id, &event_2_id, &event_3_id],
|
||||
)
|
||||
.await;
|
||||
|
||||
response.list()[0].assert_is_equal(json!({
|
||||
"id": &event_1_id,
|
||||
"calendarIds": {
|
||||
&calendar1_id: true,
|
||||
&calendar2_id: true
|
||||
},
|
||||
"isDraft": false,
|
||||
"mayInviteSelf": false,
|
||||
"mayInviteOthers": false,
|
||||
"hideAttendees": false,
|
||||
"title": "Event one",
|
||||
"start": "2006-01-02T10:00:00",
|
||||
"keywords": {
|
||||
"work": true
|
||||
}
|
||||
}));
|
||||
|
||||
response.list()[1].assert_is_equal(json!({
|
||||
"id": &event_2_id,
|
||||
"calendarIds": {
|
||||
&calendar1_id: true,
|
||||
&calendar2_id: true
|
||||
},
|
||||
"title": "Event two",
|
||||
"start": "2006-01-02T12:00:00",
|
||||
"description": "Updated description",
|
||||
"recurrenceOverrides": {
|
||||
"2006-01-04T12:00:00": {
|
||||
"title": "Event two overridden",
|
||||
"start": "2006-01-04T14:00:00",
|
||||
"duration": "PT1H",
|
||||
"updated": "2006-02-06T00:11:21Z"
|
||||
},
|
||||
"2006-01-06T12:00:00": {
|
||||
"title": "Event two overridden twice",
|
||||
"start": "2006-01-06T14:00:00",
|
||||
"duration": "PT1H",
|
||||
"updated": "2006-02-06T00:11:21Z"
|
||||
}
|
||||
},
|
||||
"title": "Event two",
|
||||
"start": "2006-01-02T12:00:00",
|
||||
"mayInviteOthers": false,
|
||||
"mayInviteSelf": false,
|
||||
"hideAttendees": false,
|
||||
"isDraft": false
|
||||
}));
|
||||
|
||||
response.list()[2].assert_is_equal(json!({
|
||||
"id": event_3_id,
|
||||
"calendarIds": {
|
||||
&calendar1_id: true,
|
||||
},
|
||||
"title": "Event three",
|
||||
"start": "2006-01-04T09:00:00",
|
||||
"participants": {
|
||||
"3f5bc8c0-c722-5345-b7d9-5a899db08a30": {
|
||||
"calendarAddress": "mailto:cyrus@example.com",
|
||||
"@type": "Participant",
|
||||
"roles": {
|
||||
"attendee": true,
|
||||
"owner": true
|
||||
},
|
||||
"participationStatus": "accepted"
|
||||
},
|
||||
"7f2bd210-6c66-5b64-8562-0176b74462b1": {
|
||||
"calendarAddress": "mailto:rupert@example.com",
|
||||
"@type": "Participant",
|
||||
"roles": {
|
||||
"attendee": true
|
||||
},
|
||||
"participationStatus": "needs-action"
|
||||
}
|
||||
},
|
||||
"mayInviteOthers": false,
|
||||
"mayInviteSelf": false,
|
||||
"hideAttendees": false,
|
||||
"isDraft": false
|
||||
}));
|
||||
|
||||
// Query tests
|
||||
assert_eq!(
|
||||
account
|
||||
.jmap_query(
|
||||
MethodObject::CalendarEvent,
|
||||
[
|
||||
("text", "Event one"),
|
||||
("inCalendar", calendar1_id.as_str()),
|
||||
("uid", "74855313FA803DA593CD579A@example.com"),
|
||||
("after", "2006-01-02T10:59:59"),
|
||||
("before", "2006-01-02T10:00:01"),
|
||||
],
|
||||
["start"],
|
||||
[("timeZone", "US/Eastern")],
|
||||
)
|
||||
.await
|
||||
.ids()
|
||||
.collect::<AHashSet<_>>(),
|
||||
[event_1_id.as_str()].into_iter().collect::<AHashSet<_>>()
|
||||
);
|
||||
|
||||
// Recurrence expansion tests
|
||||
let response = account
|
||||
.jmap_query(
|
||||
MethodObject::CalendarEvent,
|
||||
[
|
||||
("after", "2006-01-01T00:00:00"),
|
||||
("before", "2006-01-08T00:00:00"),
|
||||
],
|
||||
["start"],
|
||||
[
|
||||
("timeZone", Value::String("US/Eastern".into())),
|
||||
("expandRecurrences", Value::Bool(true)),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
let ids = response.ids().collect::<Vec<_>>();
|
||||
assert_eq!(ids.len(), 7);
|
||||
account
|
||||
.jmap_get(
|
||||
MethodObject::CalendarEvent,
|
||||
[
|
||||
JSCalendarProperty::<Id>::Id,
|
||||
JSCalendarProperty::BaseEventId,
|
||||
JSCalendarProperty::Start,
|
||||
JSCalendarProperty::Duration,
|
||||
JSCalendarProperty::Title,
|
||||
JSCalendarProperty::RecurrenceId,
|
||||
],
|
||||
ids.clone(),
|
||||
)
|
||||
.await
|
||||
.list_array()
|
||||
.assert_is_equal(json!([
|
||||
{
|
||||
"duration": "PT1H",
|
||||
"title": "Event one",
|
||||
"start": "2006-01-02T15:00:00",
|
||||
"id": &ids[0],
|
||||
"baseEventId": &event_1_id
|
||||
},
|
||||
{
|
||||
"recurrenceId": "2006-01-02T17:00:00",
|
||||
"title": "Event two",
|
||||
"duration": "PT1H",
|
||||
"start": "2006-01-02T17:00:00",
|
||||
"id": &ids[1],
|
||||
"baseEventId": &event_2_id
|
||||
},
|
||||
{
|
||||
"duration": "PT1H",
|
||||
"start": "2006-01-03T17:00:00",
|
||||
"title": "Event two",
|
||||
"recurrenceId": "2006-01-03T17:00:00",
|
||||
"id": &ids[2],
|
||||
"baseEventId": &event_2_id
|
||||
},
|
||||
{
|
||||
"start": "2006-01-04T14:00:00",
|
||||
"duration": "PT2H",
|
||||
"title": "Event three",
|
||||
"id": &ids[3],
|
||||
"baseEventId": &event_3_id
|
||||
},
|
||||
{
|
||||
"recurrenceId": "2006-01-04T19:00:00",
|
||||
"title": "Event two overridden",
|
||||
"start": "2006-01-04T19:00:00",
|
||||
"duration": "PT1H",
|
||||
"id": &ids[4],
|
||||
"baseEventId": &event_2_id
|
||||
},
|
||||
{
|
||||
"recurrenceId": "2006-01-05T17:00:00",
|
||||
"duration": "PT1H",
|
||||
"start": "2006-01-05T17:00:00",
|
||||
"title": "Event two",
|
||||
"id": &ids[5],
|
||||
"baseEventId": &event_2_id
|
||||
},
|
||||
{
|
||||
"recurrenceId": "2006-01-06T19:00:00",
|
||||
"duration": "PT1H",
|
||||
"title": "Event two overridden twice",
|
||||
"start": "2006-01-06T19:00:00",
|
||||
"id": &ids[6],
|
||||
"baseEventId": &event_2_id
|
||||
}
|
||||
]));
|
||||
|
||||
// Parse tests
|
||||
account
|
||||
.jmap_method_calls(json!([
|
||||
[
|
||||
"Blob/upload",
|
||||
{
|
||||
"create": {
|
||||
"ical": {
|
||||
"data": [
|
||||
{
|
||||
"data:asText": r#"BEGIN:VCALENDAR
|
||||
PRODID:-//xyz Corp//NONSGML PDA Calendar Version 1.0//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
DTSTAMP:19960704T120000Z
|
||||
UID:uid1@example.com
|
||||
ORGANIZER:mailto:jsmith@example.com
|
||||
DTSTART:19960918T143000Z
|
||||
DTEND:19960920T220000Z
|
||||
STATUS:CONFIRMED
|
||||
CATEGORIES:CONFERENCE
|
||||
SUMMARY:Networld+Interop Conference
|
||||
DESCRIPTION:Networld+Interop Conference
|
||||
and Exhibit\nAtlanta World Congress Center\n
|
||||
Atlanta\, Georgia
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
"#
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"S4"
|
||||
],
|
||||
[
|
||||
"CalendarEvent/parse",
|
||||
{
|
||||
"blobIds": [
|
||||
"#ical"
|
||||
]
|
||||
},
|
||||
"G4"
|
||||
]
|
||||
]))
|
||||
.await
|
||||
.pointer("/methodResponses/1/1/parsed")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
.1
|
||||
.assert_is_equal(json!([
|
||||
{
|
||||
"updated": "1996-07-04T12:00:00Z",
|
||||
"title": "Networld+Interop Conference",
|
||||
"description": "Networld+Interop Conferenceand Exhibit\nAtlanta World Congress Center\n",
|
||||
"timeZone": "Etc/UTC",
|
||||
"start": "1996-09-18T14:30:00",
|
||||
"status": "confirmed",
|
||||
"iCalComponent": {
|
||||
"convertedProperties": {
|
||||
"duration": {
|
||||
"name": "DTEND"
|
||||
}
|
||||
},
|
||||
"name": "vevent"
|
||||
},
|
||||
"@type": "Event",
|
||||
"uid": "uid1@example.com",
|
||||
"participants": {
|
||||
"25d7647e-52fc-559b-88df-d66f08da079c": {
|
||||
"calendarAddress": "mailto:jsmith@example.com",
|
||||
"@type": "Participant"
|
||||
}
|
||||
},
|
||||
"keywords": {
|
||||
"CONFERENCE": true
|
||||
},
|
||||
"organizerCalendarAddress": "mailto:jsmith@example.com",
|
||||
"duration": "P2DT7H30M"
|
||||
}
|
||||
]));
|
||||
|
||||
// Deletion tests
|
||||
assert_eq!(
|
||||
account
|
||||
.jmap_destroy(
|
||||
MethodObject::CalendarEvent,
|
||||
[event_2_id.as_str(), event_3_id.as_str()],
|
||||
Vec::<(&str, &str)>::new()
|
||||
)
|
||||
.await
|
||||
.destroyed()
|
||||
.collect::<AHashSet<_>>(),
|
||||
[event_2_id.as_str(), event_3_id.as_str()]
|
||||
.into_iter()
|
||||
.collect::<AHashSet<_>>()
|
||||
);
|
||||
|
||||
// CardDAV compatibility tests
|
||||
let account_id = account.id().document_id();
|
||||
let dav_client = DummyWebDavClient::new(
|
||||
u32::MAX,
|
||||
account.name(),
|
||||
account.secret(),
|
||||
account.emails()[0],
|
||||
);
|
||||
let resources = params
|
||||
.server
|
||||
.fetch_dav_resources(
|
||||
¶ms.server.get_access_token(account_id).await.unwrap(),
|
||||
account_id,
|
||||
SyncCollection::Calendar,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let path = format!(
|
||||
"{}{}",
|
||||
resources.base_path,
|
||||
resources
|
||||
.paths
|
||||
.iter()
|
||||
.find(|v| v.parent_id.is_some())
|
||||
.unwrap()
|
||||
.path
|
||||
);
|
||||
|
||||
let ical = dav_client
|
||||
.request("GET", &path, "")
|
||||
.await
|
||||
.with_status(StatusCode::OK)
|
||||
.expect_body()
|
||||
.lines()
|
||||
.map(String::from)
|
||||
.collect::<AHashSet<_>>();
|
||||
let expected_ical = TEST_ICAL_1
|
||||
.lines()
|
||||
.map(String::from)
|
||||
.collect::<AHashSet<_>>();
|
||||
assert_eq!(ical, expected_ical);
|
||||
|
||||
// Clean up
|
||||
account.destroy_all_calendars().await;
|
||||
params.assert_is_empty().await;
|
||||
}
|
||||
|
||||
pub fn test_jscalendar_1() -> Value {
|
||||
json!({
|
||||
"duration": "PT1H",
|
||||
"@type": "Event",
|
||||
"description": "Go Steelers!",
|
||||
"updated": "2006-02-06T00:11:02Z",
|
||||
"timeZone": "US/Eastern",
|
||||
"start": "2006-01-02T10:00:00",
|
||||
"title": "Event #1",
|
||||
"uid": "74855313FA803DA593CD579A@example.com"
|
||||
})
|
||||
}
|
||||
|
||||
pub fn test_jscalendar_2() -> Value {
|
||||
json!({
|
||||
"title": "Event #2",
|
||||
"duration": "PT1H",
|
||||
"updated": "2006-02-06T00:11:21Z",
|
||||
"recurrenceRule": {
|
||||
"frequency": "daily",
|
||||
"count": 5
|
||||
},
|
||||
"start": "2006-01-02T12:00:00",
|
||||
"uid": "00959BC664CA650E933C892C@example.com",
|
||||
"@type": "Event",
|
||||
"timeZone": "US/Eastern",
|
||||
"recurrenceOverrides": {
|
||||
"2006-01-04T12:00:00": {
|
||||
"title": "Event #2 bis",
|
||||
"start": "2006-01-04T14:00:00",
|
||||
"updated": "2006-02-06T00:11:21Z",
|
||||
"duration": "PT1H"
|
||||
},
|
||||
"2006-01-06T12:00:00": {
|
||||
"title": "Event #2 bis bis",
|
||||
"start": "2006-01-06T14:00:00",
|
||||
"updated": "2006-02-06T00:11:21Z",
|
||||
"duration": "PT1H"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn test_jscalendar_3() -> Value {
|
||||
json!({
|
||||
"duration": "PT1H",
|
||||
"organizerCalendarAddress": "mailto:cyrus@example.com",
|
||||
"@type": "Event",
|
||||
"start": "2006-01-04T10:00:00",
|
||||
"status": "tentative",
|
||||
"uid": "DC6C50A017428C5216A2F1CD@example.com",
|
||||
"sequence": 1,
|
||||
"participants": {
|
||||
"3f5bc8c0-c722-5345-b7d9-5a899db08a30": {
|
||||
"calendarAddress": "mailto:cyrus@example.com",
|
||||
"@type": "Participant",
|
||||
"roles": {
|
||||
"attendee": true,
|
||||
"chair": true
|
||||
},
|
||||
"participationStatus": "accepted"
|
||||
},
|
||||
"ec5e7db5-22a3-5ed5-89bf-c8894ab86805": {
|
||||
"calendarAddress": "mailto:lisa@example.com",
|
||||
"@type": "Participant",
|
||||
"roles": {
|
||||
"attendee": true
|
||||
},
|
||||
"participationStatus": "needs-action"
|
||||
}
|
||||
},
|
||||
"title": "Event #3",
|
||||
"updated": "2006-02-06T00:12:20Z",
|
||||
"timeZone": "US/Eastern"
|
||||
})
|
||||
}
|
||||
|
||||
const TEST_ICAL_1: &str = r#"BEGIN:VCALENDAR
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID=US/Eastern:20060102T100000
|
||||
UID:74855313FA803DA593CD579A@example.com
|
||||
DURATION:PT1H
|
||||
SUMMARY:Event one
|
||||
DTSTAMP:20060206T001102Z
|
||||
CATEGORIES:work
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
"#;
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
*/
|
||||
|
||||
pub mod acl;
|
||||
pub mod availability;
|
||||
pub mod calendars;
|
||||
pub mod event;
|
||||
pub mod identity;
|
||||
pub mod notification;
|
||||
pub mod principal;
|
||||
|
||||
@@ -49,13 +49,25 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
.to_string();
|
||||
|
||||
// Create test contacts
|
||||
let sarah_contact = test_jscontact_1().with_property(
|
||||
JSContactProperty::<Id>::AddressBookIds,
|
||||
[book1_id.as_str()].into_jmap_set(),
|
||||
);
|
||||
let carlos_contact = test_jscontact_2().with_property(
|
||||
JSContactProperty::<Id>::AddressBookIds,
|
||||
[book2_id.as_str()].into_jmap_set(),
|
||||
);
|
||||
let acme_contact = test_jscontact_3().with_property(
|
||||
JSContactProperty::<Id>::AddressBookIds,
|
||||
[book1_id.as_str(), book2_id.as_str()].into_jmap_set(),
|
||||
);
|
||||
let response = account
|
||||
.jmap_create(
|
||||
MethodObject::ContactCard,
|
||||
[
|
||||
test_jscontact_1([book1_id.as_str()]),
|
||||
test_jscontact_2([book2_id.as_str()]),
|
||||
test_jscontact_3([book1_id.as_str(), book2_id.as_str()]),
|
||||
sarah_contact.clone(),
|
||||
carlos_contact.clone(),
|
||||
acme_contact.clone(),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
@@ -83,97 +95,18 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
let response = account
|
||||
.jmap_get(
|
||||
MethodObject::ContactCard,
|
||||
[
|
||||
JSContactProperty::<Id>::Id,
|
||||
JSContactProperty::AddressBookIds,
|
||||
JSContactProperty::Name,
|
||||
],
|
||||
Vec::<&str>::new(),
|
||||
[&sarah_contact_id, &carlos_contact_id, &acme_contact_id],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
response.list()[0],
|
||||
json!({
|
||||
"id": &sarah_contact_id,
|
||||
"name": {
|
||||
"full": "Sarah Johnson",
|
||||
"components": [
|
||||
{
|
||||
"kind": "surname",
|
||||
"value": "Johnson"
|
||||
},
|
||||
{
|
||||
"kind": "given",
|
||||
"value": "Sarah"
|
||||
},
|
||||
{
|
||||
"kind": "given2",
|
||||
"value": "Marie"
|
||||
},
|
||||
{
|
||||
"kind": "title",
|
||||
"value": "Dr."
|
||||
},
|
||||
{
|
||||
"kind": "credential",
|
||||
"value": "Ph.D."
|
||||
}
|
||||
],
|
||||
"isOrdered": true
|
||||
},
|
||||
"addressBookIds": {
|
||||
&book1_id: true
|
||||
},
|
||||
})
|
||||
response.list()[0].assert_is_equal(
|
||||
sarah_contact.with_property(JSContactProperty::<Id>::Id, sarah_contact_id.as_str()),
|
||||
);
|
||||
assert_eq!(
|
||||
response.list()[1],
|
||||
json!({
|
||||
"id": &carlos_contact_id,
|
||||
"name": {
|
||||
"components": [
|
||||
{
|
||||
"kind": "surname",
|
||||
"value": "Rodriguez-Martinez"
|
||||
},
|
||||
{
|
||||
"kind": "given",
|
||||
"value": "Carlos"
|
||||
},
|
||||
{
|
||||
"kind": "given2",
|
||||
"value": "Alberto"
|
||||
},
|
||||
{
|
||||
"kind": "title",
|
||||
"value": "Mr."
|
||||
},
|
||||
{
|
||||
"kind": "credential",
|
||||
"value": "Jr."
|
||||
}
|
||||
],
|
||||
"isOrdered": true,
|
||||
"full": "Carlos Rodriguez-Martinez"
|
||||
},
|
||||
"addressBookIds": {
|
||||
&book2_id: true
|
||||
},
|
||||
})
|
||||
response.list()[1].assert_is_equal(
|
||||
carlos_contact.with_property(JSContactProperty::<Id>::Id, carlos_contact_id.as_str()),
|
||||
);
|
||||
assert_eq!(
|
||||
response.list()[2],
|
||||
json!({
|
||||
"id": acme_contact_id,
|
||||
"addressBookIds": {
|
||||
&book1_id: true,
|
||||
&book2_id: true
|
||||
},
|
||||
"name": {
|
||||
"full": "Acme Business Solutions Ltd."
|
||||
},
|
||||
})
|
||||
response.list()[2].assert_is_equal(
|
||||
acme_contact.with_property(JSContactProperty::<Id>::Id, acme_contact_id.as_str()),
|
||||
);
|
||||
|
||||
// Creating a contact without address book should fail
|
||||
@@ -390,6 +323,7 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
("email", "sarah.johnson@example.com"),
|
||||
],
|
||||
["created"],
|
||||
Vec::<(&str, &str)>::new(),
|
||||
)
|
||||
.await
|
||||
.ids()
|
||||
@@ -535,11 +469,10 @@ END:VCARD"#
|
||||
params.assert_is_empty().await;
|
||||
}
|
||||
|
||||
fn test_jscontact_1(ids: impl IntoJmapSet) -> Value {
|
||||
fn test_jscontact_1() -> Value {
|
||||
json!({
|
||||
"uid": "urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
|
||||
"@type": "Card",
|
||||
"addressBookIds": ids.into_jmap_set(),
|
||||
"preferredLanguages": {
|
||||
"k1": {
|
||||
"language": "en",
|
||||
@@ -579,7 +512,8 @@ fn test_jscontact_1(ids: impl IntoJmapSet) -> Value {
|
||||
"kind": "credential",
|
||||
"value": "Ph.D."
|
||||
}
|
||||
]
|
||||
],
|
||||
"isOrdered": true
|
||||
},
|
||||
"cryptoKeys": {
|
||||
"k1": {
|
||||
@@ -713,7 +647,8 @@ fn test_jscontact_1(ids: impl IntoJmapSet) -> Value {
|
||||
}
|
||||
],
|
||||
"timeZone": "Etc/GMT+5",
|
||||
"coordinates": "40.7128;-74.0060"
|
||||
"coordinates": "40.7128;-74.0060",
|
||||
"isOrdered": true
|
||||
},
|
||||
"k2": {
|
||||
"contexts": {
|
||||
@@ -742,7 +677,8 @@ fn test_jscontact_1(ids: impl IntoJmapSet) -> Value {
|
||||
"kind": "country",
|
||||
"value": "USA"
|
||||
}
|
||||
]
|
||||
],
|
||||
"isOrdered": true
|
||||
}
|
||||
},
|
||||
"titles": {
|
||||
@@ -770,9 +706,8 @@ fn test_jscontact_1(ids: impl IntoJmapSet) -> Value {
|
||||
})
|
||||
}
|
||||
|
||||
fn test_jscontact_2(ids: impl IntoJmapSet) -> Value {
|
||||
fn test_jscontact_2() -> Value {
|
||||
json!({
|
||||
"addressBookIds": ids.into_jmap_set(),
|
||||
"phones": {
|
||||
"k1": {
|
||||
"number": "+34-611-234-567",
|
||||
@@ -861,7 +796,8 @@ fn test_jscontact_2(ids: impl IntoJmapSet) -> Value {
|
||||
"value": "Jr."
|
||||
}
|
||||
],
|
||||
"full": "Carlos Rodriguez-Martinez"
|
||||
"full": "Carlos Rodriguez-Martinez",
|
||||
"isOrdered": true
|
||||
},
|
||||
"nicknames": {
|
||||
"k1": {
|
||||
@@ -991,7 +927,8 @@ fn test_jscontact_2(ids: impl IntoJmapSet) -> Value {
|
||||
}
|
||||
],
|
||||
"timeZone": "Etc/GMT-1",
|
||||
"coordinates": "40.4168;-3.7038"
|
||||
"coordinates": "40.4168;-3.7038",
|
||||
"isOrdered": true
|
||||
},
|
||||
"k2": {
|
||||
"contexts": {
|
||||
@@ -1016,7 +953,8 @@ fn test_jscontact_2(ids: impl IntoJmapSet) -> Value {
|
||||
"kind": "country",
|
||||
"value": "Spain"
|
||||
}
|
||||
]
|
||||
],
|
||||
"isOrdered": true
|
||||
}
|
||||
},
|
||||
"organizations": {
|
||||
@@ -1032,9 +970,8 @@ fn test_jscontact_2(ids: impl IntoJmapSet) -> Value {
|
||||
})
|
||||
}
|
||||
|
||||
fn test_jscontact_3(ids: impl IntoJmapSet) -> Value {
|
||||
fn test_jscontact_3() -> Value {
|
||||
json!({
|
||||
"addressBookIds": ids.into_jmap_set(),
|
||||
"kind": "org",
|
||||
"organizations": {
|
||||
"k1": {
|
||||
@@ -1098,8 +1035,7 @@ fn test_jscontact_3(ids: impl IntoJmapSet) -> Value {
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"full": "Acme Business Solutions Ltd.",
|
||||
"components": []
|
||||
"full": "Acme Business Solutions Ltd."
|
||||
},
|
||||
"notes": {
|
||||
"k1": {
|
||||
@@ -1180,7 +1116,8 @@ fn test_jscontact_3(ids: impl IntoJmapSet) -> Value {
|
||||
}
|
||||
],
|
||||
"timeZone": "Etc/UTC",
|
||||
"coordinates": "51.5074;-0.1278"
|
||||
"coordinates": "51.5074;-0.1278",
|
||||
"isOrdered": true
|
||||
},
|
||||
"k2": {
|
||||
"contexts": {
|
||||
@@ -1204,7 +1141,8 @@ fn test_jscontact_3(ids: impl IntoJmapSet) -> Value {
|
||||
"kind": "country",
|
||||
"value": "United Kingdom"
|
||||
}
|
||||
]
|
||||
],
|
||||
"isOrdered": true
|
||||
}
|
||||
},
|
||||
"updated": "2023-04-15T15:30:00Z",
|
||||
|
||||
@@ -65,6 +65,7 @@ pub mod contacts;
|
||||
pub mod core;
|
||||
pub mod files;
|
||||
pub mod mail;
|
||||
pub mod principal;
|
||||
pub mod server;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@@ -106,12 +107,15 @@ async fn jmap_tests() {
|
||||
server::purge::test(&mut params).await;
|
||||
server::enterprise::test(&mut params).await;*/
|
||||
|
||||
//contacts::addressbook::test(&mut params).await;
|
||||
//contacts::contact::test(&mut params).await;
|
||||
/*contacts::addressbook::test(&mut params).await;
|
||||
contacts::contact::test(&mut params).await;
|
||||
contacts::acl::test(&mut params).await;
|
||||
|
||||
//files::node::test(&mut params).await;
|
||||
files::acl::test(&mut params).await;
|
||||
files::node::test(&mut params).await;
|
||||
files::acl::test(&mut params).await;*/
|
||||
|
||||
//calendar::calendars::test(&mut params).await;
|
||||
calendar::event::test(&mut params).await;
|
||||
|
||||
if delete {
|
||||
params.temp_dir.delete();
|
||||
@@ -564,6 +568,7 @@ impl Account {
|
||||
object: impl Display,
|
||||
filter: impl IntoIterator<Item = (impl Display, impl Into<Value>)>,
|
||||
sort_by: impl IntoIterator<Item = impl Display>,
|
||||
arguments: impl IntoIterator<Item = (impl Display, impl Into<Value>)>,
|
||||
) -> JmapResponse {
|
||||
let filter = filter
|
||||
.into_iter()
|
||||
@@ -577,15 +582,20 @@ impl Account {
|
||||
})
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
self.jmap_method_calls(json!([[
|
||||
format!("{object}/query"),
|
||||
{
|
||||
"filter": filter,
|
||||
"sort": sort_by
|
||||
},
|
||||
"0"
|
||||
]]))
|
||||
.await
|
||||
let arguments = [
|
||||
("filter".to_string(), Value::Object(filter)),
|
||||
("sort".to_string(), Value::Array(sort_by)),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(
|
||||
arguments
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_string(), v.into())),
|
||||
)
|
||||
.collect::<serde_json::Map<_, _>>();
|
||||
|
||||
self.jmap_method_calls(json!([[format!("{object}/query"), arguments, "0"]]))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn jmap_create(
|
||||
@@ -798,6 +808,33 @@ impl Account {
|
||||
]))
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn destroy_all_calendars(&self) {
|
||||
self.jmap_method_calls(json!([[
|
||||
"Calendar/get",
|
||||
{
|
||||
"ids" : (),
|
||||
"properties" : [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"R1"
|
||||
],
|
||||
[
|
||||
"Calendar/set",
|
||||
{
|
||||
"#destroy" : {
|
||||
"resultOf": "R1",
|
||||
"name": "Calendar/get",
|
||||
"path": "/list/*/id"
|
||||
},
|
||||
"onDestroyRemoveEvents" : true
|
||||
},
|
||||
"R2"
|
||||
]
|
||||
]))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
impl JmapResponse {
|
||||
@@ -837,6 +874,12 @@ impl JmapResponse {
|
||||
.unwrap_or_else(|| panic!("Missing method response in response: {self:?}"))
|
||||
}
|
||||
|
||||
pub fn list_array(&self) -> &Value {
|
||||
self.0
|
||||
.pointer("/methodResponses/0/1/list")
|
||||
.unwrap_or_else(|| panic!("Missing list in response: {self:?}"))
|
||||
}
|
||||
|
||||
pub fn list(&self) -> &[Value] {
|
||||
self.0
|
||||
.pointer("/methodResponses/0/1/list")
|
||||
@@ -933,6 +976,8 @@ pub trait JmapUtils {
|
||||
self.text_field("description")
|
||||
}
|
||||
|
||||
fn with_property(self, field: impl Display, value: impl Into<Value>) -> Self;
|
||||
|
||||
fn text_field(&self, field: &str) -> &str;
|
||||
|
||||
fn assert_is_equal(&self, other: Value);
|
||||
@@ -953,6 +998,14 @@ impl JmapUtils for Value {
|
||||
);
|
||||
}
|
||||
}
|
||||
fn with_property(mut self, field: impl Display, value: impl Into<Value>) -> Self {
|
||||
if let Value::Object(map) = &mut self {
|
||||
map.insert(field.to_string(), value.into());
|
||||
} else {
|
||||
panic!("Not an object: {self:?}");
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
||||
8
tests/src/jmap/principal/mod.rs
Normal file
8
tests/src/jmap/principal/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
pub mod availability;
|
||||
pub mod get;
|
||||
Reference in New Issue
Block a user