# -*- 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 TransactionCase 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 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 TestSyncOdoo2Google(TransactionCase): 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): GoogleSync._google_insert.assert_called_once_with(self.google_service, values) 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) @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}} }) @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.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_stop_synchronization(self): self.env.user.stop_google_synchronization() self.assertTrue(self.env.user.google_synchronization_stopped, "The google synchronization flag should be switched on") self.assertFalse(self.env.user._sync_google_calendar(self.google_service), "The google synchronization should be stopped") # If synchronization stopped, creating a new event should not call _google_insert. self.env['calendar.event'].create({ 'name': "Event", 'start': datetime(2020, 1, 15, 8, 0), 'stop': datetime(2020, 1, 15, 18, 0), 'privacy': 'private', }) self.assertGoogleEventNotInserted() @patch_api def test_restart_synchronization(self): # Test new event created after stopping synchronization are correctly patched when restarting sync. google_id = 'aaaaaaaaa' partner = self.env['res.partner'].create({'name': 'Jean-Luc', 'email': 'jean-luc@opoo.com'}) user = self.env['res.users'].create({ 'name': 'Test user Calendar', 'login': 'jean-luc@opoo.com', 'partner_id': partner.id, }) user.stop_google_synchronization() event = self.env['calendar.event'].with_user(user).create({ 'google_id': google_id, 'name': "Event", 'start': datetime(2020, 1, 15, 8, 0), 'stop': datetime(2020, 1, 15, 18, 0), 'partner_ids': [(4, partner.id)], }) user.with_user(user).restart_google_synchronization() self.assertGoogleEventPatched(event.google_id, { 'id': event.google_id, 'start': {'dateTime': '2020-01-15T08:00:00+00:00'}, 'end': {'dateTime': '2020-01-15T18:00:00+00:00'}, 'summary': 'Event', 'description': '', 'location': '', 'visibility': 'public', 'guestsCanModify': True, 'reminders': {'overrides': [], 'useDefault': False}, 'organizer': {'email': 'jean-luc@opoo.com', 'self': True}, 'attendees': [{'email': 'jean-luc@opoo.com', 'responseStatus': 'accepted'}], 'extendedProperties': {'shared': {'%s_odoo_id' % self.env.cr.dbname: event.id}} }, timeout=3)