bunch of new test cases and code mods
This commit is contained in:
parent
b660e4d533
commit
9243fad2e6
4 changed files with 207 additions and 24 deletions
|
|
@ -59,9 +59,9 @@ class CalendarEvent(models.Model):
|
|||
@api.depends("user_id", "partner_ids", "partner_ids.user_id")
|
||||
def _compute_caldav_users(self):
|
||||
for rec in self:
|
||||
rec.caldav_user_ids = (
|
||||
rec.user_id | rec.partner_ids.mapped("user_id")
|
||||
).filtered(lambda user: user.is_caldav_enabled)
|
||||
rec.caldav_user_ids = (rec.user_id | rec.partner_ids.user_ids).filtered(
|
||||
"is_caldav_enabled"
|
||||
)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
|
|
@ -249,7 +249,8 @@ class CalendarEvent(models.Model):
|
|||
"""Match the fields from calendar.event (recurring fields) to the fields specified in RRULE at
|
||||
https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html"""
|
||||
|
||||
rrule = component.get("rrule")
|
||||
rrule = [item[1] for item in component.property_items() if item[0] == "RRULE"]
|
||||
rrule = rrule[0] if rrule else None
|
||||
if not rrule:
|
||||
if not self.recurrency:
|
||||
# No change, this was already not a recurring event
|
||||
|
|
@ -268,7 +269,7 @@ class CalendarEvent(models.Model):
|
|||
return {"recurrence_update": "all_events", "recurrency": False}
|
||||
rrule_str = rrule.to_ical().decode("utf-8")
|
||||
sequence = component.get("sequence")
|
||||
if sequence and sequence != 0:
|
||||
if sequence and sequence != 1:
|
||||
# This is not the base event so we can't change recurrence properties
|
||||
return {}
|
||||
|
||||
|
|
@ -315,15 +316,19 @@ class CalendarEvent(models.Model):
|
|||
partner_ids = self._get_attendee_partners(component, current_user_email)
|
||||
|
||||
existing_instance = self._get_existing_instance(uid, recurrence_id)
|
||||
outdated = False
|
||||
last_modified = component.decoded("last-modified")
|
||||
if existing_instance and last_modified:
|
||||
last_modified = last_modified.astimezone(utc).replace(tzinfo=None)
|
||||
if last_modified < existing_instance.write_date:
|
||||
_logger.info(
|
||||
f"Last modified date {last_modified} is before most recent "
|
||||
f"write date {existing_instance.write_date}. Skipping."
|
||||
)
|
||||
continue
|
||||
# _logger.info(
|
||||
# f"Last modified date {last_modified} is before most recent "
|
||||
# f"write date {existing_instance.write_date}. Skipping."
|
||||
# )
|
||||
outdated = True
|
||||
owned = (
|
||||
existing_instance and existing_instance.partner_id == user.partner_id
|
||||
)
|
||||
values, recurrency_vals = (
|
||||
self._get_vals_recurrency_vals_from_ical_component(
|
||||
partner_ids, component, user
|
||||
|
|
@ -333,6 +338,14 @@ class CalendarEvent(models.Model):
|
|||
if not existing_instance:
|
||||
_logger.info(f"Creating with vals: {values}")
|
||||
self.with_context(caldav_no_sync=True).create(values)
|
||||
elif outdated or not owned:
|
||||
_logger.info(
|
||||
f"Event {existing_instance.caldav_uid} "
|
||||
f"{'outdated ' if outdated else ''}"
|
||||
f"{'not owned by user ' + user.name if not owned else ''}."
|
||||
f" Skipping."
|
||||
)
|
||||
pass # Do nothing, it's not this user's event to modify or it's outdated
|
||||
else:
|
||||
# Don't update partner_ids if no change
|
||||
if partner_ids != existing_instance.partner_ids:
|
||||
|
|
@ -473,13 +486,3 @@ class CalendarEvent(models.Model):
|
|||
"tentative": "TENTATIVE",
|
||||
}
|
||||
return mapping.get(state, "NEEDS-ACTION")
|
||||
|
||||
@staticmethod
|
||||
def _map_ical_status(ical_status):
|
||||
mapping = {
|
||||
"NEEDS-ACTION": "needsAction",
|
||||
"ACCEPTED": "accepted",
|
||||
"DECLINED": "declined",
|
||||
"TENTATIVE": "tentative",
|
||||
}
|
||||
return mapping.get(ical_status, "needsAction")
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@ class CaldavTestCommon:
|
|||
def _generate_user(
|
||||
cls, name, caldav_username=None, caldav_password=None, caldav_url=None
|
||||
):
|
||||
groups_ids = cls.env.ref("base.group_user") | cls.env.ref(
|
||||
"base.group_partner_manager"
|
||||
)
|
||||
vals = {
|
||||
"name": name,
|
||||
"login": name,
|
||||
"password": name,
|
||||
"email": name + "@example.com",
|
||||
"groups_id": [Command.set(cls.env.ref("base.group_user").ids)],
|
||||
"groups_id": [Command.set(groups_ids.ids)],
|
||||
}
|
||||
if caldav_username:
|
||||
vals.update(caldav_username=caldav_username)
|
||||
|
|
|
|||
38
caldav_sync/tests/data/test_recurring.ics
Normal file
38
caldav_sync/tests/data/test_recurring.ics
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
BEGIN:VCALENDAR
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
X-WR-CALNAME:test recurring
|
||||
METHOD:PUBLISH
|
||||
PRODID:-//Apple Inc.//macOS 14.5//EN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:America/Toronto
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:-0500
|
||||
DTSTART:20070311T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
||||
TZNAME:EDT
|
||||
TZOFFSETTO:-0400
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:-0400
|
||||
DTSTART:20071104T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
||||
TZNAME:EST
|
||||
TZOFFSETTO:-0500
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
TRANSP:OPAQUE
|
||||
DTEND;TZID=America/Toronto:20241008T040000
|
||||
X-APPLE-CREATOR-TEAM-IDENTITY:0000000000
|
||||
UID:F777C16F-2F14-4326-9681-69C6043DB8C0
|
||||
DTSTAMP:20241007T195255Z
|
||||
X-APPLE-CREATOR-IDENTITY:com.apple.calendar
|
||||
SEQUENCE:1
|
||||
SUMMARY:test recurring
|
||||
DTSTART;TZID=America/Toronto:20241008T030000
|
||||
LAST-MODIFIED:20241007T195252Z
|
||||
CREATED:20241007T195223Z
|
||||
RRULE:FREQ=WEEKLY;COUNT=10
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
|
@ -8,6 +8,16 @@ from .common import CaldavTestCommon
|
|||
from contextlib import contextmanager
|
||||
from datetime import datetime, UTC, timedelta
|
||||
|
||||
WEEKDAY_MAP = {
|
||||
0: "SUN",
|
||||
1: "MON",
|
||||
2: "TUE",
|
||||
3: "WED",
|
||||
4: "THU",
|
||||
5: "FRI",
|
||||
6: "SAT",
|
||||
}
|
||||
|
||||
|
||||
def _get_ics_path(filename):
|
||||
return Path(__file__).parent / "data" / filename
|
||||
|
|
@ -23,10 +33,13 @@ def _patch_caldav_with_events_from_ics(ics_paths, user, last_modified=None):
|
|||
mock_client = MockDAVClient.return_value
|
||||
mock_calendar = MockCalendar.return_value
|
||||
mock_client.calendar = mock_calendar
|
||||
mock_calendars = {}
|
||||
|
||||
def calendar_side_effect(url):
|
||||
if url not in mock_calendars:
|
||||
mock_calendars[url] = MockCalendar()
|
||||
if url == user.caldav_calendar_url:
|
||||
return mock_calendar
|
||||
return mock_calendars[url]
|
||||
raise Exception("Calendar does not exist.")
|
||||
|
||||
mock_calendar.side_effect = calendar_side_effect
|
||||
|
|
@ -109,6 +122,16 @@ class TestCalendarEvent(TransactionCase, CaldavTestCommon):
|
|||
event = self.env["calendar.event"].search([("user_id", "=", user.id)])
|
||||
self.assertFalse(event)
|
||||
|
||||
def test_recurring_from_server_create(self):
|
||||
user = self.user_1
|
||||
ics_path = _get_ics_path("test_recurring.ics")
|
||||
with _patch_caldav_with_events_from_ics(ics_path, user):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
events = self.env["calendar.event"].search(
|
||||
[("partner_id", "=", user.partner_id.id)]
|
||||
)
|
||||
self.assertEqual(len(events), 10)
|
||||
|
||||
def test_multiple_attendees_event_from_server_create(self):
|
||||
user = self.user_1
|
||||
ics_path = _get_ics_path("test_multi_attendee.ics")
|
||||
|
|
@ -154,9 +177,125 @@ class TestCalendarEvent(TransactionCase, CaldavTestCommon):
|
|||
ics_path = _get_ics_path("test_multi_user.ics")
|
||||
with _patch_caldav_with_events_from_ics(ics_path, user1):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
with _patch_caldav_with_events_from_ics(ics_path, user2):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
with _patch_caldav_with_events_from_ics(ics_path, user3):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
event = self.env["calendar.event"].search(
|
||||
[("caldav_uid", "=", "2495546B-5C9A-4632-AAD3-A179EF83CF20")]
|
||||
)
|
||||
self.assertEqual(len(event), 1)
|
||||
# Make sure the event wasn't duplicated all over the place
|
||||
other_user_events = self.env["calendar.event"].search(
|
||||
[("user_id", "in", [user2.id, user3.id])]
|
||||
)
|
||||
self.assertFalse(other_user_events)
|
||||
self.assertIn(user2.partner_id, event.partner_ids)
|
||||
self.assertIn(user3.partner_id, event.partner_ids)
|
||||
|
||||
def test_multiple_user_attendees_event_from_server_update(self):
|
||||
pass
|
||||
"""Test event has (as in above test):
|
||||
Organizer: user1 (test1@example.com)
|
||||
Attendees: user2 and user3 (test2@example.com, test3@example.com)
|
||||
"""
|
||||
user1 = self.user_1
|
||||
user2 = self.user_2
|
||||
user3 = self.user_3
|
||||
ics_path = _get_ics_path("test_multi_user.ics")
|
||||
with _patch_caldav_with_events_from_ics(ics_path, user1):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
with _patch_caldav_with_events_from_ics(ics_path, user2):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
with _patch_caldav_with_events_from_ics(ics_path, user3):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
# Now update it to remove one attendee
|
||||
# Shuffle the user polling order just to test more robustly
|
||||
ics_path = _get_ics_path("test_multi_user_update.ics")
|
||||
with _patch_caldav_with_events_from_ics(
|
||||
ics_path, user2, last_modified=datetime.now(UTC)
|
||||
):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
with _patch_caldav_with_events_from_ics(
|
||||
ics_path, user3, last_modified=datetime.now(UTC)
|
||||
):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
with _patch_caldav_with_events_from_ics(
|
||||
ics_path, user1, last_modified=datetime.now(UTC)
|
||||
):
|
||||
self.env["calendar.event"].poll_caldav_server()
|
||||
event = self.env["calendar.event"].search(
|
||||
[("caldav_uid", "=", "2495546B-5C9A-4632-AAD3-A179EF83CF20")]
|
||||
)
|
||||
self.assertIn(user3.partner_id, event.partner_ids)
|
||||
self.assertNotIn(user2.partner_id, event.partner_ids)
|
||||
self.assertEqual(len(event.attendee_ids), 2)
|
||||
|
||||
def _create_multi_user_test_event(self):
|
||||
return (
|
||||
self.env["calendar.event"]
|
||||
.with_user(self.user_1)
|
||||
.create(
|
||||
{
|
||||
"name": "Test event",
|
||||
"partner_ids": [
|
||||
Command.set(
|
||||
[
|
||||
self.user_2.partner_id.id,
|
||||
self.user_3.partner_id.id,
|
||||
self.user_1.partner_id.id,
|
||||
]
|
||||
),
|
||||
Command.create(
|
||||
{
|
||||
"name": "Test partner",
|
||||
"email": "testpartner@example.com",
|
||||
}
|
||||
),
|
||||
],
|
||||
"start": datetime.now() + timedelta(days=2),
|
||||
"stop": datetime.now() + timedelta(days=2, hours=1),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def test_multiple_user_attendees_event_to_server_create(self):
|
||||
pass
|
||||
with self._patch_all_3_users_davclients() as (_, mock_calendar):
|
||||
self._create_multi_user_test_event()
|
||||
self.assertEqual(mock_calendar.add_event.call_count, 3)
|
||||
|
||||
def test_event_to_server_delete(self):
|
||||
with self._patch_all_3_users_davclients() as (_, mock_calendar):
|
||||
self._create_multi_user_test_event().unlink()
|
||||
self.assertEqual(
|
||||
mock_calendar.event_by_uid.return_value.delete.call_count, 3
|
||||
)
|
||||
|
||||
def test_event_to_server_update(self):
|
||||
with self._patch_all_3_users_davclients() as (_, mock_calendar):
|
||||
self._create_multi_user_test_event().write(
|
||||
{"start": datetime.now() + timedelta(days=14)}
|
||||
)
|
||||
self.assertEqual(mock_calendar.save_event.call_count, 3)
|
||||
|
||||
def test_recurrent_event_to_server(self):
|
||||
with self._patch_all_3_users_davclients() as (_, mock_calendar):
|
||||
self._create_multi_user_test_event().write(
|
||||
{
|
||||
"recurrency": True,
|
||||
}
|
||||
)
|
||||
args = mock_calendar.save_event.call_args
|
||||
self.assertEqual(mock_calendar.save_event.call_count, 3)
|
||||
|
||||
@contextmanager
|
||||
def _patch_all_3_users_davclients(self):
|
||||
with patch("caldav.DAVClient") as MockDAVClient:
|
||||
(self.user_1 | self.user_2 | self.user_3)._compute_is_caldav_enabled()
|
||||
|
||||
mock_client = MockDAVClient.return_value
|
||||
mock_calendar = MagicMock()
|
||||
mock_event_by_uid = MagicMock()
|
||||
mock_client.calendar.return_value = mock_calendar
|
||||
mock_calendar.events.return_value = []
|
||||
mock_calendar.event_by_uid.return_value = mock_event_by_uid
|
||||
yield mock_client, mock_calendar
|
||||
|
|
|
|||
Loading…
Reference in a new issue