Ejemplo n.º 1
0
 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)')
Ejemplo n.º 2
0
    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))
Ejemplo n.º 3
0
    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)")
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
 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))
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
 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)
Ejemplo n.º 11
0
    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