def test_magic(self): pattern = AbsoluteYearlyPattern(month=FEBRUARY, day_of_month=28) self.assertEqual(str(pattern), 'Occurs on day 28 of February') pattern = RelativeYearlyPattern(month=AUGUST, week_number=SECOND, weekday=MONDAY) self.assertEqual( str(pattern), 'Occurs on weekday Monday in the Second week of August') pattern = AbsoluteMonthlyPattern(interval=3, day_of_month=31) self.assertEqual(str(pattern), 'Occurs on day 31 of every 3 month(s)') pattern = RelativeMonthlyPattern(interval=2, week_number=LAST, weekday=5) self.assertEqual( str(pattern), 'Occurs on weekday Friday in the Last week of every 2 month(s)') pattern = WeeklyPattern(interval=4, weekdays=WEEKEND_DAY, first_day_of_week=7) self.assertEqual( str(pattern), 'Occurs on weekdays WeekendDay of every 4 week(s) where the first day of the week is Sunday' ) pattern = DailyPattern(interval=6) self.assertEqual(str(pattern), 'Occurs every 6 day(s)')
def test_delete_occurrence_via_index(self): # Test deleting occurrences via occurrence index without knowing the ID of the occurrence. start = EWSDateTime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = EWSDateTime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, subject=get_random_string(16), recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() # Delete the third occurrence third_occurrence = master_item.occurrence(index=3) third_occurrence.refresh() # Test that GetItem works third_occurrence = master_item.occurrence(index=3) third_occurrence.delete() # Test that DeleteItem works # Test change on the master item master_item.refresh() self.assertEqual(master_item.modified_occurrences, None) self.assertEqual(len(master_item.deleted_occurrences), 1) deleted_occurrence = master_item.deleted_occurrences[0] self.assertIsInstance(deleted_occurrence, DeletedOccurrence) self.assertEqual(deleted_occurrence.start, start + datetime.timedelta(days=2))
def test_change_occurrence(self): # Test that we can make changes to individual occurrences and see the effect on the master item. start = datetime.datetime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = datetime.datetime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() master_item.refresh() # Test occurrences as full calendar items, unfolded from the master range_start, range_end = start, end + datetime.timedelta(days=3) unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] # Change the start and end of the second occurrence second_occurrence = unfolded[1] second_occurrence.start += datetime.timedelta(hours=1) second_occurrence.end += datetime.timedelta(hours=1) second_occurrence.save() # Test change on the master item master_item.refresh() self.assertEqual(len(master_item.modified_occurrences), 1) modified_occurrence = master_item.modified_occurrences[0] self.assertIsInstance(modified_occurrence, Occurrence) self.assertEqual(modified_occurrence.id, second_occurrence.id) self.assertEqual(modified_occurrence.start, second_occurrence.start) self.assertEqual(modified_occurrence.end, second_occurrence.end) self.assertEqual(modified_occurrence.original_start, second_occurrence.start - datetime.timedelta(hours=1)) self.assertEqual(master_item.deleted_occurrences, None) # Test change on the unfolded item unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] self.assertEqual(len(unfolded), 4) self.assertEqual(unfolded[1].type, EXCEPTION) self.assertEqual(unfolded[1].start, second_occurrence.start) self.assertEqual(unfolded[1].end, second_occurrence.end) self.assertEqual(unfolded[1].original_start, second_occurrence.start - datetime.timedelta(hours=1))
def test_magic(self): pattern = AbsoluteYearlyPattern(month=FEBRUARY, day_of_month=28) self.assertEqual(str(pattern), "Occurs on day 28 of February") pattern = RelativeYearlyPattern(month=AUGUST, week_number=SECOND, weekday=WEEKEND_DAY) self.assertEqual( str(pattern), "Occurs on weekday WeekendDay in the Second week of August") pattern = AbsoluteMonthlyPattern(interval=3, day_of_month=31) self.assertEqual(str(pattern), "Occurs on day 31 of every 3 month(s)") pattern = RelativeMonthlyPattern(interval=2, week_number=LAST, weekday=5) self.assertEqual( str(pattern), "Occurs on weekday Friday in the Last week of every 2 month(s)") pattern = WeeklyPattern(interval=4, weekdays=[1, 7], first_day_of_week=7) self.assertEqual( str(pattern), "Occurs on weekdays Monday, Sunday of every 4 week(s) where the first day of the week is Sunday", ) pattern = WeeklyPattern(interval=4, weekdays=[MONDAY, SUNDAY], first_day_of_week=7) self.assertEqual( str(pattern), "Occurs on weekdays Monday, Sunday of every 4 week(s) where the first day of the week is Sunday", ) pattern = DailyPattern(interval=6) self.assertEqual(str(pattern), "Occurs every 6 day(s)") pattern = YearlyRegeneration(interval=6) self.assertEqual(str(pattern), "Regenerates every 6 year(s)") pattern = MonthlyRegeneration(interval=6) self.assertEqual(str(pattern), "Regenerates every 6 month(s)") pattern = WeeklyRegeneration(interval=6) self.assertEqual(str(pattern), "Regenerates every 6 week(s)") pattern = DailyRegeneration(interval=6) self.assertEqual(str(pattern), "Regenerates every 6 day(s)") d_start = datetime.date(2017, 9, 1) d_end = datetime.date(2017, 9, 7) boundary = NoEndPattern(start=d_start) self.assertEqual(str(boundary), "Starts on 2017-09-01") boundary = EndDatePattern(start=d_start, end=d_end) self.assertEqual(str(boundary), "Starts on 2017-09-01, ends on 2017-09-07") boundary = NumberedPattern(start=d_start, number=1) self.assertEqual(str(boundary), "Starts on 2017-09-01 and occurs 1 time(s)")
def test_change_occurrence_via_index(self): # Test updating occurrences via occurrence index without knowing the ID of the occurrence. start = datetime.datetime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = datetime.datetime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, subject=get_random_string(16), recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() # Change the start and end of the second occurrence second_occurrence = master_item.occurrence(index=2) second_occurrence.start = start + datetime.timedelta(days=1, hours=1) second_occurrence.end = end + datetime.timedelta(days=1, hours=1) second_occurrence.save(update_fields=[ 'start', 'end' ]) # Test that UpdateItem works with only a few fields second_occurrence = master_item.occurrence(index=2) second_occurrence.refresh() self.assertEqual(second_occurrence.subject, master_item.subject) second_occurrence.start += datetime.timedelta(hours=1) second_occurrence.end += datetime.timedelta(hours=1) second_occurrence.save( update_fields=['start', 'end']) # Test that UpdateItem works after refresh # Test change on the master item master_item.refresh() self.assertEqual(len(master_item.modified_occurrences), 1) modified_occurrence = master_item.modified_occurrences[0] self.assertIsInstance(modified_occurrence, Occurrence) self.assertEqual(modified_occurrence.id, second_occurrence.id) self.assertEqual(modified_occurrence.start, second_occurrence.start) self.assertEqual(modified_occurrence.end, second_occurrence.end) self.assertEqual(modified_occurrence.original_start, second_occurrence.start - datetime.timedelta(hours=2)) self.assertEqual(master_item.deleted_occurrences, None)
def test_get_master_recurrence(self): # Test getting the master recurrence via an occurrence start = datetime.datetime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = datetime.datetime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) recurrence = Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, subject=get_random_string(16), recurrence=recurrence, categories=self.categories, ).save() # Get the master from an occurrence range_start, range_end = start, end + datetime.timedelta(days=3) unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] third_occurrence = unfolded[2] self.assertEqual(third_occurrence.recurrence, None) master_from_occurrence = third_occurrence.recurring_master() master_from_occurrence.refresh() # Test that GetItem works self.assertEqual(master_from_occurrence.recurrence, recurrence) self.assertEqual(master_from_occurrence.subject, master_item.subject) master_from_occurrence = third_occurrence.recurring_master() master_from_occurrence.subject = get_random_string(16) master_from_occurrence.save( update_fields=['subject']) # Test that UpdateItem works master_from_occurrence.delete() # Test that DeleteItem works with self.assertRaises(ErrorItemNotFound): master_item.delete( ) # Item is gone from the server, so this should fail with self.assertRaises(ErrorItemNotFound): third_occurrence.delete( ) # Item is gone from the server, so this should fail
def test_delete_occurrence(self): # Test that we can delete an occurrence and see the cange on the master item start = datetime.datetime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = datetime.datetime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() master_item.refresh() # Test occurrences as full calendar items, unfolded from the master range_start, range_end = start, end + datetime.timedelta(days=3) unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] # Delete the third occurrence third_occurrence = unfolded[2] third_occurrence.delete() # Test change on the master item master_item.refresh() self.assertEqual(master_item.modified_occurrences, None) self.assertEqual(len(master_item.deleted_occurrences), 1) deleted_occurrence = master_item.deleted_occurrences[0] self.assertIsInstance(deleted_occurrence, DeletedOccurrence) self.assertEqual(deleted_occurrence.start, third_occurrence.start) # Test change on the unfolded items unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] self.assertEqual(len(unfolded), 3)
def test_validation(self): p = DailyPattern(interval=3) d_start = EWSDate(2017, 9, 1) d_end = EWSDate(2017, 9, 7) with self.assertRaises(ValueError): Recurrence(pattern=p, boundary='foo', start='bar') # Specify *either* boundary *or* start, end and number with self.assertRaises(ValueError): Recurrence(pattern=p, start='foo', end='bar', number='baz') # number is invalid when end is present with self.assertRaises(ValueError): Recurrence(pattern=p, end='bar', number='baz') # Must have start r = Recurrence(pattern=p, start=d_start) self.assertEqual(r.boundary, NoEndPattern(start=d_start)) r = Recurrence(pattern=p, start=d_start, end=d_end) self.assertEqual(r.boundary, EndDatePattern(start=d_start, end=d_end)) r = Recurrence(pattern=p, start=d_start, number=1) self.assertEqual(r.boundary, NumberedPattern(start=d_start, number=1))
def test_recurring_item(self): # Create a recurring calendar item. Test that occurrence fields are correct on the master item # Create a master item with 4 daily occurrences from 8:00 to 10:00. 'start' and 'end' are values for the first # occurrence. start = datetime.datetime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = datetime.datetime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() master_item.refresh() self.assertEqual(master_item.is_recurring, False) self.assertEqual(master_item.type, RECURRING_MASTER) self.assertIsInstance(master_item.first_occurrence, FirstOccurrence) self.assertEqual(master_item.first_occurrence.start, start) self.assertEqual(master_item.first_occurrence.end, end) self.assertIsInstance(master_item.last_occurrence, LastOccurrence) self.assertEqual(master_item.last_occurrence.start, start + datetime.timedelta(days=3)) self.assertEqual(master_item.last_occurrence.end, end + datetime.timedelta(days=3)) self.assertEqual(master_item.modified_occurrences, None) self.assertEqual(master_item.deleted_occurrences, None) # Test occurrences as full calendar items, unfolded from the master range_start, range_end = start, end + datetime.timedelta(days=3) unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] self.assertEqual(len(unfolded), 4) for item in unfolded: self.assertEqual(item.type, OCCURRENCE) self.assertEqual(item.is_recurring, True) first_occurrence = unfolded[0] self.assertEqual(first_occurrence.id, master_item.first_occurrence.id) self.assertEqual(first_occurrence.start, master_item.first_occurrence.start) self.assertEqual(first_occurrence.end, master_item.first_occurrence.end) second_occurrence = unfolded[1] self.assertEqual(second_occurrence.start, master_item.start + datetime.timedelta(days=1)) self.assertEqual(second_occurrence.end, master_item.end + datetime.timedelta(days=1)) third_occurrence = unfolded[2] self.assertEqual(third_occurrence.start, master_item.start + datetime.timedelta(days=2)) self.assertEqual(third_occurrence.end, master_item.end + datetime.timedelta(days=2)) last_occurrence = unfolded[3] self.assertEqual(last_occurrence.id, master_item.last_occurrence.id) self.assertEqual(last_occurrence.start, master_item.last_occurrence.start) self.assertEqual(last_occurrence.end, master_item.last_occurrence.end)
def random_val(self, field): if isinstance(field, ExtendedPropertyField): if field.value_cls.property_type == 'StringArray': return [get_random_string(255) for _ in range(random.randint(1, 4))] if field.value_cls.property_type == 'IntegerArray': return [get_random_int(0, 256) for _ in range(random.randint(1, 4))] if field.value_cls.property_type == 'BinaryArray': return [get_random_string(255).encode() for _ in range(random.randint(1, 4))] if field.value_cls.property_type == 'String': return get_random_string(255) if field.value_cls.property_type == 'Integer': return get_random_int(0, 256) if field.value_cls.property_type == 'Binary': # In the test_extended_distinguished_property test, EWS rull return 4 NULL bytes after char 16 if we # send a longer bytes sequence. return get_random_string(16).encode() raise ValueError('Unsupported field %s' % field) if isinstance(field, URIField): return get_random_url() if isinstance(field, EmailAddressField): return get_random_email() if isinstance(field, ChoiceField): return get_random_choice(field.supported_choices(version=self.account.version)) if isinstance(field, CultureField): return get_random_choice(['da-DK', 'de-DE', 'en-US', 'es-ES', 'fr-CA', 'nl-NL', 'ru-RU', 'sv-SE']) if isinstance(field, BodyField): return get_random_string(400) if isinstance(field, CharListField): return [get_random_string(16) for _ in range(random.randint(1, 4))] if isinstance(field, TextListField): return [get_random_string(400) for _ in range(random.randint(1, 4))] if isinstance(field, CharField): return get_random_string(field.max_length) if isinstance(field, TextField): return get_random_string(400) if isinstance(field, MimeContentField): return get_random_string(400).encode('utf-8') if isinstance(field, Base64Field): return get_random_bytes(400) if isinstance(field, BooleanField): return get_random_bool() if isinstance(field, DecimalField): return get_random_decimal(field.min or 1, field.max or 99) if isinstance(field, IntegerField): return get_random_int(field.min or 0, field.max or 256) if isinstance(field, DateField): return get_random_date() if isinstance(field, DateTimeBackedDateField): return get_random_date() if isinstance(field, DateTimeField): return get_random_datetime(tz=self.account.default_timezone) if isinstance(field, AttachmentField): return [FileAttachment(name='my_file.txt', content=get_random_string(400).encode('utf-8'))] if isinstance(field, MailboxListField): # email_address must be a real account on the server(?) # TODO: Mailbox has multiple optional args but vals must match server account, so we can't easily test if get_random_bool(): return [Mailbox(email_address=self.account.primary_smtp_address)] return [self.account.primary_smtp_address] if isinstance(field, MailboxField): # email_address must be a real account on the server(?) # TODO: Mailbox has multiple optional args but vals must match server account, so we can't easily test if get_random_bool(): return Mailbox(email_address=self.account.primary_smtp_address) return self.account.primary_smtp_address if isinstance(field, AttendeesField): # Attendee must refer to a real mailbox on the server(?). We're only sure to have one if get_random_bool(): mbx = Mailbox(email_address=self.account.primary_smtp_address) else: mbx = self.account.primary_smtp_address with_last_response_time = get_random_bool() if with_last_response_time: return [ Attendee(mailbox=mbx, response_type='Accept', last_response_time=get_random_datetime(tz=self.account.default_timezone)) ] if get_random_bool(): return [Attendee(mailbox=mbx, response_type='Accept')] return [self.account.primary_smtp_address] if isinstance(field, EmailAddressesField): addrs = [] for label in EmailAddress.get_field_by_fieldname('label').supported_choices(version=self.account.version): addr = EmailAddress(email=get_random_email()) addr.label = label addrs.append(addr) return addrs if isinstance(field, PhysicalAddressField): addrs = [] for label in PhysicalAddress.get_field_by_fieldname('label')\ .supported_choices(version=self.account.version): addr = PhysicalAddress(street=get_random_string(32), city=get_random_string(32), state=get_random_string(32), country=get_random_string(32), zipcode=get_random_string(8)) addr.label = label addrs.append(addr) return addrs if isinstance(field, PhoneNumberField): pns = [] for label in PhoneNumber.get_field_by_fieldname('label').supported_choices(version=self.account.version): pn = PhoneNumber(phone_number=get_random_string(16)) pn.label = label pns.append(pn) return pns if isinstance(field, EWSElementField): if field.value_cls == Recurrence: return Recurrence(pattern=DailyPattern(interval=5), start=get_random_date(), number=7) if field.value_cls == TaskRecurrence: return TaskRecurrence(pattern=DailyRegeneration(interval=5), start=get_random_date(), number=7) if field.value_cls == ReminderMessageData: start = get_random_time() end = get_random_time(start_time=start) return ReminderMessageData( reminder_text=get_random_string(16), location=get_random_string(16), start_time=start, end_time=end, ) if field.value_cls == CompleteName: return CompleteName( title=get_random_string(16), first_name=get_random_string(16), middle_name=get_random_string(16), last_name=get_random_string(16), suffix=get_random_string(16), initials=get_random_string(16), full_name=get_random_string(16), nickname=get_random_string(16), yomi_first_name=get_random_string(16), yomi_last_name=get_random_string(16), ) if isinstance(field, TimeZoneField): while True: tz = zoneinfo.ZoneInfo(random.choice(tuple(zoneinfo.available_timezones()))) try: EWSTimeZone.from_zoneinfo(tz) except UnknownTimeZone: continue return tz if isinstance(field, PermissionSetField): return PermissionSet( permissions=[ Permission( user_id=UserId(primary_smtp_address=self.account.primary_smtp_address), ) ] ) raise ValueError('Unknown field %s' % field)
def test_recurring_item(self): """Changes to an occurrence of a recurring task cause one-off tasks to be generated when the following updates are made: * The status property of a regenerating or nonregenerating recurrent task is set to Completed. * The start date or end date of a nonregenerating recurrent task is changed. """ # Create a master non-regenerating item with 4 daily occurrences start = EWSDate(2016, 1, 1) recurrence = TaskRecurrence(pattern=DailyPattern(interval=1), start=start, number=4) nonregenerating_item = self.ITEM_CLASS( folder=self.test_folder, categories=self.categories, recurrence=recurrence, ).save() nonregenerating_item.refresh() master_item_id = nonregenerating_item.id self.assertEqual(nonregenerating_item.is_recurring, True) self.assertEqual(nonregenerating_item.change_count, 1) self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 1) # Change the start date. We should see a new task appear. master_item = self.get_item_by_id((master_item_id, None)) master_item.recurrence.boundary.start = EWSDate(2016, 2, 1) occurrence_item = master_item.save() occurrence_item.refresh() self.assertEqual(occurrence_item.is_recurring, False) # This is now the occurrence self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 2) # Check fields on the recurring item master_item = self.get_item_by_id((master_item_id, None)) self.assertEqual(master_item.change_count, 2) self.assertEqual(master_item.due_date, EWSDate(2016, 1, 2)) # This is the next occurrence self.assertEqual(master_item.recurrence.boundary.number, 3) # One less # Change the status to 'Completed'. We should see a new task appear. master_item.status = Task.COMPLETED occurrence_item = master_item.save() occurrence_item.refresh() self.assertEqual(occurrence_item.is_recurring, False) # This is now the occurrence self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 3) # Check fields on the recurring item master_item = self.get_item_by_id((master_item_id, None)) self.assertEqual(master_item.change_count, 3) self.assertEqual(master_item.due_date, EWSDate(2016, 2, 1)) # This is the next occurrence self.assertEqual(master_item.recurrence.boundary.number, 2) # One less self.test_folder.filter(categories__contains=self.categories).delete() # Create a master regenerating item with 4 daily occurrences recurrence = TaskRecurrence(pattern=DailyRegeneration(interval=1), start=start, number=4) regenerating_item = self.ITEM_CLASS( folder=self.test_folder, categories=self.categories, recurrence=recurrence, ).save() regenerating_item.refresh() master_item_id = regenerating_item.id self.assertEqual(regenerating_item.is_recurring, True) self.assertEqual(regenerating_item.change_count, 1) self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 1) # Change the start date. We should *not* see a new task appear. master_item = self.get_item_by_id((master_item_id, None)) master_item.recurrence.boundary.start = EWSDate(2016, 1, 2) occurrence_item = master_item.save() occurrence_item.refresh() self.assertEqual(occurrence_item.id, master_item.id) # This is not an occurrence. No new task was created self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 1) # Change the status to 'Completed'. We should see a new task appear. master_item.status = Task.COMPLETED occurrence_item = master_item.save() occurrence_item.refresh() self.assertEqual(occurrence_item.is_recurring, False) # This is now the occurrence self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 2) # Check fields on the recurring item master_item = self.get_item_by_id((master_item_id, None)) self.assertEqual(master_item.change_count, 2) # The due date is the next occurrence after today tz = self.account.default_timezone self.assertEqual(master_item.due_date, EWSDateTime.now(tz).date() + datetime.timedelta(days=1)) self.assertEqual(master_item.recurrence.boundary.number, 3) # One less