diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/google_calendar/tests | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/google_calendar/tests')
| -rw-r--r-- | addons/google_calendar/tests/__init__.py | 6 | ||||
| -rw-r--r-- | addons/google_calendar/tests/test_sync_common.py | 63 | ||||
| -rw-r--r-- | addons/google_calendar/tests/test_sync_google2odoo.py | 682 | ||||
| -rw-r--r-- | addons/google_calendar/tests/test_sync_odoo2google.py | 400 |
4 files changed, 1151 insertions, 0 deletions
diff --git a/addons/google_calendar/tests/__init__.py b/addons/google_calendar/tests/__init__.py new file mode 100644 index 00000000..7eafa029 --- /dev/null +++ b/addons/google_calendar/tests/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import test_sync_common +from . import test_sync_google2odoo +from . import test_sync_odoo2google diff --git a/addons/google_calendar/tests/test_sync_common.py b/addons/google_calendar/tests/test_sync_common.py new file mode 100644 index 00000000..d3d6062d --- /dev/null +++ b/addons/google_calendar/tests/test_sync_common.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from datetime import datetime, date +from dateutil.relativedelta import relativedelta +from unittest.mock import MagicMock, patch + +from odoo.tests.common import SavepointCase +from odoo.addons.google_calendar.utils.google_calendar import GoogleCalendarService +from odoo.addons.google_calendar.models.res_users import User +from odoo.addons.google_calendar.models.google_sync import GoogleSync +from odoo.addons.google_account.models.google_service import TIMEOUT + + +def patch_api(func): + @patch.object(GoogleSync, '_google_insert', MagicMock()) + @patch.object(GoogleSync, '_google_delete', MagicMock()) + @patch.object(GoogleSync, '_google_patch', MagicMock()) + def patched(self, *args, **kwargs): + return func(self, *args, **kwargs) + return patched + +@patch.object(User, '_get_google_calendar_token', lambda user: 'dummy-token') +class TestSyncGoogle(SavepointCase): + + def setUp(self): + super().setUp() + self.google_service = GoogleCalendarService(self.env['google.service']) + + def assertGoogleEventDeleted(self, google_id): + GoogleSync._google_delete.assert_called() + args, kwargs = GoogleSync._google_delete.call_args + self.assertEqual(args[1], google_id, "Event should have been deleted") + + def assertGoogleEventNotDeleted(self): + GoogleSync._google_delete.assert_not_called() + + def assertGoogleEventInserted(self, values, timeout=None): + expected_args = (values,) + expected_kwargs = {'timeout': timeout} if timeout else {} + GoogleSync._google_insert.assert_called_once() + args, kwargs = GoogleSync._google_insert.call_args + self.assertEqual(args[1:], expected_args) # skip Google service arg + self.assertEqual(kwargs, expected_kwargs) + + def assertGoogleEventNotInserted(self): + GoogleSync._google_insert.assert_not_called() + + def assertGoogleEventPatched(self, google_id, values, timeout=None): + expected_args = (google_id, values) + expected_kwargs = {'timeout': timeout} if timeout else {} + GoogleSync._google_patch.assert_called_once() + args, kwargs = GoogleSync._google_patch.call_args + self.assertEqual(args[1:], expected_args) # skip Google service arg + self.assertEqual(kwargs, expected_kwargs) + + def assertGoogleEventNotPatched(self): + GoogleSync._google_patch.assert_not_called() + + def assertGoogleAPINotCalled(self): + self.assertGoogleEventNotPatched() + self.assertGoogleEventNotInserted() + self.assertGoogleEventNotDeleted() diff --git a/addons/google_calendar/tests/test_sync_google2odoo.py b/addons/google_calendar/tests/test_sync_google2odoo.py new file mode 100644 index 00000000..d6c998fc --- /dev/null +++ b/addons/google_calendar/tests/test_sync_google2odoo.py @@ -0,0 +1,682 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.google_calendar.utils.google_calendar import GoogleEvent +import pytz +from datetime import datetime, date +from dateutil.relativedelta import relativedelta +from odoo.tests.common import new_test_user +from odoo.addons.google_calendar.tests.test_sync_common import TestSyncGoogle, patch_api + + +class TestSyncGoogle2Odoo(TestSyncGoogle): + + @property + def now(self): + return pytz.utc.localize(datetime.now()).isoformat() + + def sync(self, events): + events.clear_type_ambiguity(self.env) + google_recurrence = events.filter(GoogleEvent.is_recurrence) + self.env['calendar.recurrence']._sync_google2odoo(google_recurrence) + self.env['calendar.event']._sync_google2odoo(events - google_recurrence) + + @patch_api + def test_new_google_event(self): + values = { + 'id': 'oj44nep1ldf8a3ll02uip0c9aa', + 'description': 'Small mini desc', + 'organizer': {'email': 'odoocalendarref@gmail.com', 'self': True}, + 'summary': 'Pricing new update', + 'visibility': 'public', + 'attendees': [{ + 'displayName': 'Mitchell Admin', + 'email': 'admin@yourcompany.example.com', + 'responseStatus': 'needsAction' + },], + 'reminders': {'useDefault': True}, + 'start': { + 'dateTime': '2020-01-13T16:55:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + 'end': { + 'dateTime': '2020-01-13T19:55:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + } + self.env['calendar.event']._sync_google2odoo(GoogleEvent([values])) + event = self.env['calendar.event'].search([('google_id', '=', values.get('id'))]) + self.assertTrue(event, "It should have created an event") + self.assertEqual(event.name, values.get('summary')) + self.assertFalse(event.allday) + self.assertEqual(event.description, values.get('description')) + self.assertEqual(event.start, datetime(2020, 1, 13, 15, 55)) + self.assertEqual(event.stop, datetime(2020, 1, 13, 18, 55)) + admin_attendee = event.attendee_ids.filtered(lambda e: e.email == 'admin@yourcompany.example.com') + self.assertEqual('admin@yourcompany.example.com', admin_attendee.email) + self.assertEqual('Mitchell Admin', admin_attendee.partner_id.name) + self.assertEqual(event.partner_ids, event.attendee_ids.partner_id) + self.assertEqual('needsAction', admin_attendee.state) + self.assertGoogleAPINotCalled() + + @patch_api + def test_invalid_owner_property(self): + values = { + 'id': 'oj44nep1ldf8a3ll02uip0c9aa', + 'description': 'Small mini desc', + 'organizer': {'email': 'odoocalendarref@gmail.com', 'self': True}, + 'summary': 'Pricing new update', + 'visibility': 'public', + 'attendees': [], + 'reminders': {'useDefault': True}, + 'start': { + 'dateTime': '2020-01-13T16:55:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + 'extendedProperties': { + 'shared': {'%s_owner_id' % self.env.cr.dbname: "invalid owner id"} + }, + 'end': { + 'dateTime': '2020-01-13T19:55:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + } + self.env['calendar.event']._sync_google2odoo(GoogleEvent([values])) + event = self.env['calendar.event'].search([('google_id', '=', values.get('id'))]) + self.assertEqual(event.user_id, self.env.user) + self.assertGoogleAPINotCalled() + + @patch_api + def test_valid_owner_property(self): + user = new_test_user(self.env, login='calendar-user') + values = { + 'id': 'oj44nep1ldf8a3ll02uip0c9aa', + 'description': 'Small mini desc', + 'organizer': {'email': 'odoocalendarref@gmail.com', 'self': True}, + 'summary': 'Pricing new update', + 'visibility': 'public', + 'attendees': [], + 'reminders': {'useDefault': True}, + 'start': { + 'dateTime': '2020-01-13T16:55:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + 'extendedProperties': { + 'shared': {'%s_owner_id' % self.env.cr.dbname: str(user.id)} + }, + 'end': { + 'dateTime': '2020-01-13T19:55:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + } + self.env['calendar.event']._sync_google2odoo(GoogleEvent([values])) + event = self.env['calendar.event'].search([('google_id', '=', values.get('id'))]) + self.assertEqual(event.user_id, user) + self.assertGoogleAPINotCalled() + + @patch_api + def test_cancelled(self): + google_id = 'oj44nep1ldf8a3ll02uip0c9aa' + event = self.env['calendar.event'].create({ + 'name': 'coucou', + 'start': date(2020, 1, 6), + 'stop': date(2020, 1, 6), + 'google_id': google_id, + 'user_id': self.env.user.id, + 'need_sync': False, + 'partner_ids': [(6, 0, self.env.user.partner_id.ids)] # current user is attendee + }) + gevent = GoogleEvent([{ + 'id': google_id, + 'status': 'cancelled', + }]) + self.sync(gevent) + self.assertFalse(event.exists()) + self.assertGoogleAPINotCalled() + + @patch_api + def test_attendee_cancelled(self): + google_id = 'oj44nep1ldf8a3ll02uip0c9aa' + event = self.env['calendar.event'].create({ + 'name': 'coucou', + 'start': date(2020, 1, 6), + 'stop': date(2020, 1, 6), + 'allday': True, + 'google_id': google_id, + 'need_sync': False, + 'user_id': False, # Not the current user + 'partner_ids': [(6, 0, self.env.user.partner_id.ids)] # current user is attendee + }) + gevent = GoogleEvent([{ + 'id': google_id, + 'status': 'cancelled', + }]) + self.sync(gevent) + self.assertTrue(event.active) + user_attendee = event.attendee_ids + self.assertTrue(user_attendee) + self.assertEqual(user_attendee.state, 'declined') + # To avoid 403 errors, we send a limited dictionnary when we don't have write access. + # guestsCanModify property is not properly handled yet + self.assertGoogleEventPatched(event.google_id, { + 'id': event.google_id, + 'start': {'date': str(event.start_date)}, + 'end': {'date': str(event.stop_date + relativedelta(days=1))}, + 'attendees': [{'email': 'odoobot@example.com', 'responseStatus': 'declined'}], + 'extendedProperties': {'private': {'%s_odoo_id' % self.env.cr.dbname: event.id}}, + 'reminders': {'overrides': [], 'useDefault': False}, + }) + + @patch_api + def test_attendee_removed(self): + user = new_test_user(self.env, login='calendar-user') + google_id = 'oj44nep1ldf8a3ll02uip0c9aa' + event = self.env['calendar.event'].with_user(user).create({ + 'name': 'coucou', + 'start': date(2020, 1, 6), + 'stop': date(2020, 1, 6), + 'google_id': google_id, + 'user_id': False, # user is not owner + 'need_sync': False, + 'partner_ids': [(6, 0, user.partner_id.ids)], # but user is attendee + }) + gevent = GoogleEvent([{ + 'id': google_id, + 'description': 'Small mini desc', + "updated": self.now, + 'organizer': {'email': 'odoocalendarref@gmail.com', 'self': True}, + 'summary': 'Pricing new update', + 'visibility': 'public', + 'attendees': [], # <= attendee removed in Google + 'reminders': {'useDefault': True}, + 'start': { + 'dateTime': '2020-01-13T16:55:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + 'end': { + 'dateTime': '2020-01-13T19:55:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + }]) + self.assertEqual(event.partner_ids, user.partner_id) + self.assertEqual(event.attendee_ids.partner_id, user.partner_id) + self.sync(gevent) + # User attendee removed but gevent owner might be added after synch. + self.assertNotEqual(event.attendee_ids.partner_id, user.partner_id) + self.assertNotEqual(event.partner_ids, user.partner_id) + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence(self): + recurrence_id = 'oj44nep1ldf8a3ll02uip0c9aa' + values = { + 'id': recurrence_id, + 'description': 'Small mini desc', + 'organizer': {'email': 'odoocalendarref@gmail.com', 'self': True}, + 'summary': 'Pricing new update', + 'visibility': 'public', + 'recurrence': ['RRULE:FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=MO'], + 'reminders': {'useDefault': True}, + 'start': {'date': '2020-01-6'}, + 'end': {'date': '2020-01-7'}, + } + self.env['calendar.recurrence']._sync_google2odoo(GoogleEvent([values])) + recurrence = self.env['calendar.recurrence'].search([('google_id', '=', values.get('id'))]) + self.assertTrue(recurrence, "it should have created a recurrence") + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 3, "it should have created a recurrence with 3 events") + self.assertTrue(all(events.mapped('recurrency'))) + self.assertEqual(events[0].start_date, date(2020, 1, 6)) + self.assertEqual(events[1].start_date, date(2020, 1, 13)) + self.assertEqual(events[2].start_date, date(2020, 1, 20)) + self.assertEqual(events[0].start_date, date(2020, 1, 6)) + self.assertEqual(events[1].start_date, date(2020, 1, 13)) + self.assertEqual(events[2].start_date, date(2020, 1, 20)) + self.assertEqual(events[0].google_id, '%s_20200106' % recurrence_id) + self.assertEqual(events[1].google_id, '%s_20200113' % recurrence_id) + self.assertEqual(events[2].google_id, '%s_20200120' % recurrence_id) + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence_datetime(self): + recurrence_id = 'oj44nep1ldf8a3ll02uip0c9aa' + values = { + 'id': recurrence_id, + 'description': 'Small mini desc', + 'organizer': {'email': 'odoocalendarref@gmail.com', 'self': True}, + 'summary': 'Pricing new update', + 'visibility': 'public', + 'recurrence': ['RRULE:FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=MO'], + 'reminders': {'useDefault': True}, + 'start': {'dateTime': '2020-01-06T18:00:00+01:00'}, + 'end': {'dateTime': '2020-01-06T19:00:00+01:00'}, + } + self.env['calendar.recurrence']._sync_google2odoo(GoogleEvent([values])) + recurrence = self.env['calendar.recurrence'].search([('google_id', '=', values.get('id'))]) + self.assertTrue(recurrence, "it should have created a recurrence") + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 3, "it should have created a recurrence with 3 events") + self.assertTrue(all(events.mapped('recurrency'))) + self.assertEqual(events[0].start, datetime(2020, 1, 6, 17, 0)) + self.assertEqual(events[1].start, datetime(2020, 1, 13, 17, 0)) + self.assertEqual(events[2].start, datetime(2020, 1, 20, 17, 0)) + self.assertEqual(events[0].google_id, '%s_20200106T170000Z' % recurrence_id) + self.assertEqual(events[1].google_id, '%s_20200113T170000Z' % recurrence_id) + self.assertEqual(events[2].google_id, '%s_20200120T170000Z' % recurrence_id) + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence_exdate(self): + recurrence_id = 'oj44nep1ldf8a3ll02uip0c9aa' + events = GoogleEvent([{ + 'id': recurrence_id, + 'summary': 'Pricing new update', + 'organizer': {'email': self.env.user.email, 'self': True}, + 'recurrence': ['RRULE:FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=MO'], + 'reminders': {'useDefault': True}, + 'start': {'date': '2020-01-6'}, + 'end': {'date': '2020-01-7'}, + }, { # Third event has been deleted + 'id': '%s_20200113' % recurrence_id, + 'originalStartTime': {'dateTime': '2020-01-13'}, + 'recurringEventId': 'oj44nep1ldf8a3ll02uip0c9pk', + 'reminders': {'useDefault': True}, + 'status': 'cancelled', + }]) + self.sync(events) + recurrence = self.env['calendar.recurrence'].search([('google_id', '=', recurrence_id)]) + self.assertTrue(recurrence, "it should have created a recurrence") + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 2, "it should have created a recurrence with 2 events") + self.assertEqual(events[0].start_date, date(2020, 1, 6)) + self.assertEqual(events[1].start_date, date(2020, 1, 20)) + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence_first_exdate(self): + recurrence_id = "4c0de517evkk3ra294lmut57vm" + events = GoogleEvent([{ + "id": recurrence_id, + "updated": "2020-01-13T16:17:03.806Z", + "summary": "r rul", + "start": {"date": "2020-01-6"}, + 'organizer': {'email': self.env.user.email, 'self': True}, + "end": {"date": "2020-01-7"}, + 'reminders': {'useDefault': True}, + "recurrence": ["RRULE:FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=MO"], + }, { + "id": "%s_20200106" % recurrence_id, + "status": "cancelled", + "recurringEventId": "4c0de517evkk3ra294lmut57vm", + 'reminders': {'useDefault': True}, + "originalStartTime": { + "date": "2020-01-06" + } + }]) + self.sync(events) + recurrence = self.env['calendar.recurrence'].search([('google_id', '=', recurrence_id)]) + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 2, "it should have created a recurrence with 2 events") + self.assertEqual(events[0].start_date, date(2020, 1, 13)) + self.assertEqual(events[1].start_date, date(2020, 1, 20)) + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrencde_first_updated(self): + recurrence_id = "4c0de517evkk3ra294lmut57vm" + events = GoogleEvent([{ + 'id': recurrence_id, + 'recurrence': ['RRULE:FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=WE'], + 'start': {'date': '2020-01-01'}, + 'end': {'date': '2020-01-02'}, + 'status': 'confirmed', + 'summary': 'rrule', + 'reminders': {'useDefault': True}, + 'updated': self.now + }, { + 'summary': 'edited', # Name changed + 'id': '%s_20200101' % recurrence_id, + 'originalStartTime': {'date': '2020-01-01'}, + 'recurringEventId': recurrence_id, + 'start': {'date': '2020-01-01'}, + 'end': {'date': '2020-01-02'}, + 'reminders': {'useDefault': True}, + 'updated': self.now, + }]) + self.sync(events) + recurrence = self.env['calendar.recurrence'].search([('google_id', '=', recurrence_id)]) + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 3, "it should have created a recurrence with 3 events") + self.assertEqual(events[0].name, 'edited') + self.assertEqual(events[1].name, 'rrule') + self.assertEqual(events[2].name, 'rrule') + self.assertGoogleAPINotCalled() + + @patch_api + def test_existing_recurrence_first_updated(self): + recurrence_id = "4c0de517evkk3ra294lmut57vm" + base_event = self.env['calendar.event'].create({ + 'name': 'coucou', + 'allday': True, + 'start': datetime(2020, 1, 6), + 'stop': datetime(2020, 1, 6), + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': recurrence_id, + 'rrule': 'FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=MO', + 'need_sync': False, + 'base_event_id': base_event.id, + }) + recurrence._apply_recurrence() + values = [{ + 'summary': 'edited', # Name changed + 'id': '%s_20200106' % recurrence_id, + 'originalStartTime': {'date': '2020-01-06'}, + 'recurringEventId': recurrence_id, + 'start': {'date': '2020-01-06'}, + 'end': {'date': '2020-01-07'}, + 'reminders': {'useDefault': True}, + 'updated': self.now, + }] + self.env['calendar.event']._sync_google2odoo(GoogleEvent(values)) + recurrence = self.env['calendar.recurrence'].search([('google_id', '=', recurrence_id)]) + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 3, "it should have created a recurrence with 3 events") + self.assertEqual(events[0].name, 'edited') + self.assertEqual(events[1].name, 'coucou') + self.assertEqual(events[2].name, 'coucou') + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence_outlier(self): + recurrence_id = 'oj44nep1ldf8a3ll02uip0c9aa' + events = GoogleEvent([{ + 'id': recurrence_id, + 'summary': 'Pricing new update', + 'recurrence': ['RRULE:FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=MO'], + 'start': {'date': '2020-01-6'}, + 'end': {'date': '2020-01-7'}, + 'reminders': {'useDefault': True}, + 'updated': self.now, + }, + { # Third event has been moved + 'id': '%s_20200113' % recurrence_id, + 'summary': 'Pricing new update', + 'start': {'date': '2020-01-18'}, + 'end': {'date': '2020-01-19'}, + 'originalStartTime': {'date': '2020-01-13'}, + 'reminders': {'useDefault': True}, + 'updated': self.now, + }]) + self.sync(events) + recurrence = self.env['calendar.recurrence'].search([('google_id', '=', recurrence_id)]) + self.assertTrue(recurrence, "it should have created a recurrence") + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 3, "it should have created a recurrence with 3 events") + self.assertEqual(events[0].start_date, date(2020, 1, 6)) + self.assertEqual(events[1].start_date, date(2020, 1, 18), "It should not be in sync with the recurrence") + self.assertEqual(events[2].start_date, date(2020, 1, 20)) + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence_moved(self): + google_id = 'oj44nep1ldf8a3ll02uip0c9aa' + base_event = self.env['calendar.event'].create({ + 'name': 'coucou', + 'allday': True, + 'start': datetime(2020, 1, 6), + 'stop': datetime(2020, 1, 6), + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': google_id, + 'rrule': 'FREQ=WEEKLY;COUNT=2;BYDAY=MO', + 'need_sync': False, + 'base_event_id': base_event.id, + 'calendar_event_ids': [(4, base_event.id)], + }) + recurrence._apply_recurrence() + values = { + 'id': google_id, + 'summary': 'coucou', + 'recurrence': ['RRULE:FREQ=WEEKLY;COUNT=2;BYDAY=WE'], # Now wednesday + 'start': {'date': '2020-01-08'}, + 'end': {'date': '2020-01-09'}, + 'reminders': {'useDefault': True}, + 'updated': self.now, + } + self.env['calendar.recurrence']._sync_google2odoo(GoogleEvent([values])) + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 2) + self.assertEqual(recurrence.rrule, 'FREQ=WEEKLY;COUNT=2;BYDAY=WE') + self.assertEqual(events[0].start_date, date(2020, 1, 8)) + self.assertEqual(events[1].start_date, date(2020, 1, 15)) + self.assertEqual(events[0].google_id, '%s_20200108' % google_id) + self.assertEqual(events[1].google_id, '%s_20200115' % google_id) + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence_name_updated(self): + google_id = 'oj44nep1ldf8a3ll02uip0c9aa' + base_event = self.env['calendar.event'].create({ + 'name': 'coucou', + 'allday': True, + 'start': datetime(2020, 1, 6), + 'stop': datetime(2020, 1, 6), + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': google_id, + 'rrule': 'FREQ=WEEKLY;COUNT=2;BYDAY=MO', + 'need_sync': False, + 'base_event_id': base_event.id, + 'calendar_event_ids': [(4, base_event.id)], + }) + recurrence._apply_recurrence() + + values = { + 'id': google_id, + 'summary': 'coucou again', + 'recurrence': ['RRULE:FREQ=WEEKLY;COUNT=2;BYDAY=MO'], + 'start': {'date': '2020-01-06'}, + 'end': {'date': '2020-01-07'}, + 'reminders': {'useDefault': True}, + 'updated': self.now, + } + self.env['calendar.recurrence']._sync_google2odoo(GoogleEvent([values])) + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 2) + self.assertEqual(recurrence.rrule, 'FREQ=WEEKLY;COUNT=2;BYDAY=MO') + self.assertEqual(events.mapped('name'), ['coucou again','coucou again']) + self.assertEqual(events[0].start_date, date(2020, 1, 6)) + self.assertEqual(events[1].start_date, date(2020, 1, 13)) + self.assertEqual(events[0].google_id, '%s_20200106' % google_id) + self.assertEqual(events[1].google_id, '%s_20200113' % google_id) + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence_write_with_outliers(self): + google_id = 'oj44nep1ldf8a3ll02uip0c9aa' + base_event = self.env['calendar.event'].create({ + 'name': 'coucou', + 'start': datetime(2021, 2, 15, 8, 0, 0), + 'stop': datetime(2021, 2, 15, 10, 0, 0), + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': google_id, + 'rrule': 'FREQ=WEEKLY;COUNT=3;BYDAY=MO', + 'need_sync': False, + 'base_event_id': base_event.id, + 'calendar_event_ids': [(4, base_event.id)], + }) + recurrence._apply_recurrence() + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(events[0].google_id, '%s_20210215T080000Z' % google_id) + self.assertEqual(events[1].google_id, '%s_20210222T080000Z' % google_id) + self.assertEqual(events[2].google_id, '%s_20210301T080000Z' % google_id) + # Modify start of one of the events. + middle_event = recurrence.calendar_event_ids.filtered(lambda e: e.start == datetime(2021, 2, 22, 8, 0, 0)) + middle_event.write({ + 'start': datetime(2021, 2, 22, 16, 0, 0), + 'need_sync': False, + }) + + values = { + 'id': google_id, + 'summary': 'coucou again', + 'recurrence': ['RRULE:FREQ=WEEKLY;COUNT=3;BYDAY=MO'], + 'start': {'dateTime': '2021-02-15T09:00:00+01:00'}, # 8:00 UTC + 'end': {'dateTime': '2021-02-15-T11:00:00+01:00'}, + 'reminders': {'useDefault': True}, + 'updated': self.now, + } + self.env['calendar.recurrence']._sync_google2odoo(GoogleEvent([values])) + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 3) + self.assertEqual(recurrence.rrule, 'FREQ=WEEKLY;COUNT=3;BYDAY=MO') + self.assertEqual(events.mapped('name'), ['coucou again', 'coucou again', 'coucou again']) + self.assertEqual(events[0].start, datetime(2021, 2, 15, 8, 0, 0)) + self.assertEqual(events[1].start, datetime(2021, 2, 22, 16, 0, 0)) + self.assertEqual(events[2].start, datetime(2021, 3, 1, 8, 0, 0)) + # the google_id of recurrent events should not be modified when events start is modified. + # the original start date or datetime should always be present. + self.assertEqual(events[0].google_id, '%s_20210215T080000Z' % google_id) + self.assertEqual(events[1].google_id, '%s_20210222T080000Z' % google_id) + self.assertEqual(events[2].google_id, '%s_20210301T080000Z' % google_id) + self.assertGoogleAPINotCalled() + + + @patch_api + def test_recurrence_write_time_fields(self): + google_id = 'oj44nep1ldf8a3ll02uip0c9aa' + base_event = self.env['calendar.event'].create({ + 'name': 'coucou', + 'start': datetime(2021, 2, 15, 8, 0, 0), + 'stop': datetime(2021, 2, 15, 10, 0, 0), + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': google_id, + 'rrule': 'FREQ=WEEKLY;COUNT=3;BYDAY=MO', + 'need_sync': False, + 'base_event_id': base_event.id, + 'calendar_event_ids': [(4, base_event.id)], + }) + recurrence._apply_recurrence() + # Google modifies the start/stop of the base event + # When the start/stop or all day values are updated, the recurrence should reapplied. + + values = { + 'id': google_id, + 'summary': "It's me again", + 'recurrence': ['RRULE:FREQ=WEEKLY;COUNT=4;BYDAY=MO'], + 'start': {'dateTime': '2021-02-15T12:00:00+01:00'}, # 11:00 UTC + 'end': {'dateTime': '2021-02-15-T15:00:00+01:00'}, + 'reminders': {'useDefault': True}, + 'updated': self.now, + } + + self.env['calendar.recurrence']._sync_google2odoo(GoogleEvent([values])) + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(events[0].start, datetime(2021, 2, 15, 11, 0, 0)) + self.assertEqual(events[1].start, datetime(2021, 2, 22, 11, 0, 0)) + self.assertEqual(events[2].start, datetime(2021, 3, 1, 11, 0, 0)) + self.assertEqual(events[3].start, datetime(2021, 3, 8, 11, 0, 0)) + # We ensure that our modifications are pushed + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence_deleted(self): + google_id = 'oj44nep1ldf8a3ll02uip0c9aa' + base_event = self.env['calendar.event'].create({ + 'name': 'coucou', + 'start': datetime(2021, 2, 15, 8, 0, 0), + 'stop': datetime(2021, 2, 15, 10, 0, 0), + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': google_id, + 'rrule': 'FREQ=WEEKLY;COUNT=3;BYDAY=MO', + 'need_sync': False, + 'base_event_id': base_event.id, + 'calendar_event_ids': [(4, base_event.id)], + }) + recurrence._apply_recurrence() + events = recurrence.calendar_event_ids + values = { + 'id': google_id, + 'status': 'cancelled', + } + self.sync(GoogleEvent([values])) + self.assertFalse(recurrence.exists(), "The recurrence should be deleted") + self.assertFalse(events.exists(), "All events should be deleted") + self.assertGoogleAPINotCalled() + + @patch_api + def test_recurrence_timezone(self): + """ Ensure that the timezone of the base_event is saved on the recurrency + Google save the TZ on the event and we save it on the recurrency. + """ + recurrence_id = 'oj44nep1ldf8a3ll02uip0c9aa' + values = { + 'id': recurrence_id, + 'description': '', + 'organizer': {'email': 'odoocalendarref@gmail.com', 'self': True}, + 'summary': 'Event with ', + 'visibility': 'public', + 'recurrence': ['RRULE:FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=MO'], + 'reminders': {'useDefault': True}, + 'start': {'dateTime': '2020-01-06T18:00:00+01:00', 'timeZone': 'Pacific/Auckland'}, + 'end': {'dateTime': '2020-01-06T19:00:00+01:00', 'timeZone': 'Pacific/Auckland'}, + } + self.env['calendar.recurrence']._sync_google2odoo(GoogleEvent([values])) + recurrence = self.env['calendar.recurrence'].search([('google_id', '=', values.get('id'))]) + self.assertEqual(recurrence.event_tz, 'Pacific/Auckland', "The Google event Timezone should be saved on the recurrency") + self.assertGoogleAPINotCalled() + + @patch_api + def test_simple_event_into_recurrency(self): + """ Synched single events should be converted in recurrency without problems""" + google_id = 'aaaaaaaaaaaa' + values = { + 'id': google_id, + 'description': 'Small mini desc', + 'organizer': {'email': 'odoocalendarref@gmail.com', 'self': True}, + 'summary': 'Pricing new update', + 'visibility': 'public', + 'attendees': [{ + 'displayName': 'Mitchell Admin', + 'email': 'admin@yourcompany.example.com', + 'responseStatus': 'needsAction' + }, ], + 'reminders': {'useDefault': True}, + 'start': { + 'dateTime': '2020-01-06T18:00:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + 'end': { + 'dateTime': '2020-01-13T19:55:00+01:00', + 'timeZone': 'Europe/Brussels' + }, + } + self.env['calendar.event']._sync_google2odoo(GoogleEvent([values])) + # The event is transformed into a recurrency on google + values = { + 'id': google_id, + 'description': '', + 'organizer': {'email': 'odoocalendarref@gmail.com', 'self': True}, + 'summary': 'Event with ', + 'visibility': 'public', + 'recurrence': ['RRULE:FREQ=WEEKLY;WKST=SU;COUNT=3;BYDAY=MO'], + 'reminders': {'useDefault': True}, + 'start': {'dateTime': '2020-01-06T18:00:00+01:00', 'timeZone': 'Europe/Brussels'}, + 'end': {'dateTime': '2020-01-06T19:00:00+01:00', 'timeZone': 'Europe/Brussels'}, + } + recurrence = self.env['calendar.recurrence']._sync_google2odoo(GoogleEvent([values])) + events = recurrence.calendar_event_ids.sorted('start') + self.assertEqual(len(events), 3, "it should have created a recurrence with 3 events") + event = self.env['calendar.event'].search([('google_id', '=', values.get('id'))]) + self.assertFalse(event.exists(), "The old event should not exits anymore") + self.assertGoogleAPINotCalled() diff --git a/addons/google_calendar/tests/test_sync_odoo2google.py b/addons/google_calendar/tests/test_sync_odoo2google.py new file mode 100644 index 00000000..3571b6bd --- /dev/null +++ b/addons/google_calendar/tests/test_sync_odoo2google.py @@ -0,0 +1,400 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from datetime import datetime, date +from dateutil.relativedelta import relativedelta +from unittest.mock import MagicMock, patch + +from odoo.addons.google_calendar.utils.google_calendar import GoogleCalendarService +from odoo.addons.google_calendar.models.res_users import User +from odoo.addons.google_calendar.models.google_sync import GoogleSync +from odoo.modules.registry import Registry +from odoo.addons.google_account.models.google_service import TIMEOUT +from odoo.addons.google_calendar.tests.test_sync_common import TestSyncGoogle, patch_api + + +@patch.object(User, '_get_google_calendar_token', lambda user: 'dummy-token') +class TestSyncOdoo2Google(TestSyncGoogle): + + def setUp(self): + super().setUp() + self.google_service = GoogleCalendarService(self.env['google.service']) + + @patch_api + def test_event_creation(self): + partner = self.env['res.partner'].create({'name': 'Jean-Luc', 'email': 'jean-luc@opoo.com'}) + alarm = self.env['calendar.alarm'].create({ + 'name': 'Notif', + 'alarm_type': 'notification', + 'interval': 'minutes', + 'duration': 18, + }) + event = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2020, 1, 15, 8, 0), + 'stop': datetime(2020, 1, 15, 18, 0), + 'partner_ids': [(4, partner.id)], + 'alarm_ids': [(4, alarm.id)], + 'privacy': 'private', + 'need_sync': False, + }) + event._sync_odoo2google(self.google_service) + self.assertGoogleEventInserted({ + 'id': False, + 'start': {'dateTime': '2020-01-15T08:00:00+00:00'}, + 'end': {'dateTime': '2020-01-15T18:00:00+00:00'}, + 'summary': 'Event', + 'description': '', + 'location': '', + 'visibility': 'private', + 'guestsCanModify': True, + 'reminders': {'useDefault': False, 'overrides': [{'method': 'popup', 'minutes': alarm.duration_minutes}]}, + 'organizer': {'email': 'odoobot@example.com', 'self': True}, + 'attendees': [{'email': 'jean-luc@opoo.com', 'responseStatus': 'needsAction'}], + 'extendedProperties': {'shared': {'%s_odoo_id' % self.env.cr.dbname: event.id}} + }) + + def test_event_without_user(self): + event = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2020, 1, 15, 8, 0), + 'stop': datetime(2020, 1, 15, 18, 0), + 'user_id': False, + 'privacy': 'private', + 'need_sync': False, + }) + values = event._google_values() + self.assertFalse('%s_owner_id' % self.env.cr.dbname in values.get('extendedProperties', {}).get('shared', {})) + + @patch_api + def test_event_allday_creation(self): + event = self.env['calendar.event'].create({ + 'name': "Event", + 'allday': True, + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'need_sync': False, + }) + event._sync_odoo2google(self.google_service) + self.assertGoogleEventInserted({ + 'id': False, + 'start': {'date': '2020-01-15'}, + 'end': {'date': '2020-01-16'}, + 'summary': 'Event', + 'description': '', + 'location': '', + 'visibility': 'public', + 'guestsCanModify': True, + 'reminders': {'overrides': [], 'useDefault': False}, + 'organizer': {'email': 'odoobot@example.com', 'self': True}, + 'attendees': [], + 'extendedProperties': {'shared': {'%s_odoo_id' % self.env.cr.dbname: event.id}} + }) + + @patch_api + def test_inactive_event(self): + event = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'active': False, + 'need_sync': False, + }) + event._sync_odoo2google(self.google_service) + self.assertGoogleEventNotInserted() + self.assertGoogleEventNotDeleted() + + @patch_api + def test_synced_inactive_event(self): + google_id = 'aaaaaaaaa' + event = self.env['calendar.event'].create({ + 'google_id': google_id, + 'name': "Event", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'active': False, + 'need_sync': False, + }) + event._sync_odoo2google(self.google_service) + self.assertGoogleEventDeleted(google_id) + + @patch_api + def test_recurrence(self): + google_id = 'aaaaaaaaa' + event = self.env['calendar.event'].create({ + 'google_id': google_id, + 'name': "Event", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'allday': True, + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'rrule': 'FREQ=WEEKLY;COUNT=2;BYDAY=WE', + 'calendar_event_ids': [(4, event.id)], + 'need_sync': False, + }) + recurrence._sync_odoo2google(self.google_service) + self.assertGoogleEventInserted({ + 'id': False, + 'start': {'date': '2020-01-15'}, + 'end': {'date': '2020-01-16'}, + 'summary': 'Event', + 'description': '', + 'location': '', + 'visibility': 'public', + 'guestsCanModify': True, + 'reminders': {'overrides': [], 'useDefault': False}, + 'organizer': {'email': 'odoobot@example.com', 'self': True}, + 'attendees': [], + 'recurrence': ['RRULE:FREQ=WEEKLY;COUNT=2;BYDAY=WE'], + 'extendedProperties': {'shared': {'%s_odoo_id' % self.env.cr.dbname: recurrence.id}} + }) + + @patch_api + def test_event_added_to_recurrence(self): + google_id = 'aaaaaaaaa' + event = self.env['calendar.event'].create({ + 'google_id': google_id, + 'name': "Event", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'allday': True, + 'need_sync': False, + }) + event.write({ + 'recurrency': True, + 'rrule': 'FREQ=WEEKLY;COUNT=2;BYDAY=WE', + }) + to_delete = self.env['calendar.event'].with_context(active_test=False).search([('google_id', '=', google_id)]) + self.assertTrue(to_delete) + self.assertFalse(to_delete.active) + self.assertFalse(event.google_id, "The google id will be set after the API call") + self.assertGoogleEventInserted({ + 'id': False, + 'start': {'date': '2020-01-15'}, + 'end': {'date': '2020-01-16'}, + 'summary': 'Event', + 'description': '', + 'location': '', + 'visibility': 'public', + 'guestsCanModify': True, + 'reminders': {'overrides': [], 'useDefault': False}, + 'organizer': {'email': 'odoobot@example.com', 'self': True}, + 'attendees': [], + 'recurrence': ['RRULE:FREQ=WEEKLY;COUNT=2;BYDAY=WE'], + 'extendedProperties': {'shared': {'%s_odoo_id' % self.env.cr.dbname: event.recurrence_id.id}} + }, timeout=3) + + self.assertGoogleEventDeleted(google_id) + + @patch_api + def test_following_event_updated(self): + google_id = 'aaaaaaaaa' + event_1 = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'allday': True, + 'need_sync': False, + }) + event_2 = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2020, 1, 22), + 'stop': datetime(2020, 1, 22), + 'allday': True, + 'need_sync': False, + }) + self.env['calendar.recurrence'].create({ + 'google_id': google_id, + 'rrule': 'FREQ=WEEKLY;COUNT=2;BYDAY=WE', + 'calendar_event_ids': [(4, event_1.id), (4, event_2.id)], + 'need_sync': False, + }) + event = event_2 + + # Update only some events in the recurrence + event.write({ + 'name': 'New name', + 'recurrence_update': 'future_events', + }) + self.assertGoogleEventPatched(event.google_id, { + 'id': event.google_id, + 'start': {'date': str(event.start_date)}, + 'end': {'date': str(event.stop_date + relativedelta(days=1))}, + 'summary': 'New name', + 'description': '', + 'location': '', + 'guestsCanModify': True, + 'organizer': {'email': 'odoobot@example.com', 'self': True}, + 'attendees': [], + 'extendedProperties': {'shared': {'%s_odoo_id' % self.env.cr.dbname: event.id}}, + 'reminders': {'overrides': [], 'useDefault': False}, + 'visibility': 'public', + }, timeout=3) + + @patch_api + def test_all_event_updated(self): + google_id = 'aaaaaaaaa' + event = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'allday': True, + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': google_id, + 'rrule': 'FREQ=WEEKLY;COUNT=2;BYDAY=WE', + 'base_event_id': event.id, + 'need_sync': False, + }) + recurrence._apply_recurrence() + event.write({ + 'name': 'New name', + 'recurrence_update': 'all_events', + }) + self.assertGoogleEventPatched(recurrence.google_id, { + 'id': recurrence.google_id, + 'start': {'date': str(event.start_date)}, + 'end': {'date': str(event.stop_date + relativedelta(days=1))}, + 'summary': 'New name', + 'description': '', + 'location': '', + 'guestsCanModify': True, + 'organizer': {'email': 'odoobot@example.com', 'self': True}, + 'attendees': [], + 'recurrence': ['RRULE:FREQ=WEEKLY;COUNT=2;BYDAY=WE'], + 'extendedProperties': {'shared': {'%s_odoo_id' % self.env.cr.dbname: recurrence.id}}, + 'reminders': {'overrides': [], 'useDefault': False}, + 'visibility': 'public', + }, timeout=3) + + @patch_api + def test_event_need_sync(self): + event = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'allday': True, + 'recurrence_id': False, + 'recurrency': True, + }) + self.assertFalse(event.need_sync, + "Event created with True recurrency should not be synched to avoid " + "duplicate event on google") + + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': False, + 'rrule': 'FREQ=WEEKLY;COUNT=2;BYDAY=WE', + 'base_event_id': event.id, + 'need_sync': False, + }) + event_2 = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'allday': True, + 'recurrence_id': recurrence.id, + }) + self.assertFalse(event_2.need_sync, + "Event created with recurrence_id should not be synched to avoid " + "duplicate event on google") + + self.assertGoogleEventNotInserted() + self.assertGoogleEventNotDeleted() + + + @patch_api + def test_event_until_utc(self): + """ UNTIl rrule value must be in UTC: ending with a 'Z """ + google_id = 'aaaaaaaaa' + event = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'allday': True, + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': google_id, + 'rrule': 'FREQ=DAILY;UNTIL=20200117T235959', + 'base_event_id': event.id, + 'need_sync': False, + }) + recurrence._apply_recurrence() + self.assertEqual(recurrence._google_values()['recurrence'][0], 'RRULE:FREQ=DAILY;UNTIL=20200117T235959Z', + "The rrule sent to google should be in UTC: end with Z") + # Add it even if it is not the end of the string + recurrence.write({'rrule': 'FREQ=DAILY;UNTIL=20200118T235959;INTERVAL=3'}) + recurrence._apply_recurrence() + self.assertEqual(recurrence._google_values()['recurrence'][0], + 'RRULE:FREQ=DAILY;UNTIL=20200118T235959Z;INTERVAL=3', + "The rrule sent to google should be in UTC: end with Z and preserve the following parameters") + # Don't add two Z at the end of the UNTIL value + recurrence.write({'rrule': 'FREQ=DAILY;UNTIL=20200119T235959Z'}) + recurrence._apply_recurrence() + self.assertEqual(recurrence._google_values()['recurrence'][0], 'RRULE:FREQ=DAILY;UNTIL=20200119T235959Z', + "The rrule sent to google should be in UTC: end with one Z") + + @patch_api + def test_write_unsynced_field(self): + google_id = 'aaaaaaaaa' + event = self.env['calendar.event'].create({ + 'name': "Event", + 'start': datetime(2021, 3, 10), + 'stop': datetime(2021, 3, 10), + 'allday': True, + 'need_sync': False, + }) + recurrence = self.env['calendar.recurrence'].create({ + 'google_id': google_id, + 'rrule': 'FREQ=WEEKLY;COUNT=2;BYDAY=WE', + 'base_event_id': event.id, + 'need_sync': False, + }) + recurrence._apply_recurrence() + event.write({ + 'start': datetime(2021, 3, 11), + 'stop': datetime(2021, 3, 11), + 'need_sync': False, + }) + event_type = self.env['calendar.event.type'].create({'name': 'type'}) + event.write({ + 'recurrence_update': 'all_events', + 'categ_ids': [(4, event_type.id)] + }) + self.assertTrue(all(e.categ_ids == event_type for e in recurrence.calendar_event_ids)) + self.assertGoogleAPINotCalled() + + @patch_api + def test_attendee_state(self): + "Sync attendee state immediately" + partner = self.env['res.partner'].create({'name': 'Jean-Luc', 'email': 'jean-luc@opoo.com'}) + event = self.env['calendar.event'].create({ + 'name': "Event with attendees", + 'start': datetime(2020, 1, 15), + 'stop': datetime(2020, 1, 15), + 'allday': True, + 'need_sync': False, + 'partner_ids': [(4, partner.id)], + 'google_id': 'aaaaaaaaa', + }) + self.assertEqual(event.attendee_ids.state, 'needsAction', + "The attendee state should be 'needsAction") + + event.attendee_ids.write({'state': 'declined'}) + self.assertGoogleEventPatched(event.google_id, { + 'id': event.google_id, + 'start': {'date': str(event.start_date)}, + 'end': {'date': str(event.stop_date + relativedelta(days=1))}, + 'summary': 'Event with attendees', + 'description': '', + 'location': '', + 'guestsCanModify': True, + 'organizer': {'email': 'odoobot@example.com', 'self': True}, + 'attendees': [{'email': 'jean-luc@opoo.com', 'responseStatus': 'declined'}], + 'extendedProperties': {'shared': {'%s_odoo_id' % self.env.cr.dbname: event.id}}, + 'reminders': {'overrides': [], 'useDefault': False}, + 'visibility': 'public', + }) |
