Exemplo n.º 1
0
    def test_item_attachments(self):
        item = self.get_test_item(folder=self.test_folder)
        item.attachments = []

        attached_item1 = self.get_test_item(folder=self.test_folder)
        attached_item1.attachments = []
        attached_item1.save()
        attachment1 = ItemAttachment(name='attachment1', item=attached_item1)
        item.attach(attachment1)

        self.assertEqual(len(item.attachments), 1)
        item.save()
        fresh_item = list(self.account.fetch(ids=[item]))[0]
        self.assertEqual(len(fresh_item.attachments), 1)
        fresh_attachments = sorted(fresh_item.attachments,
                                   key=lambda a: a.name)
        self.assertEqual(fresh_attachments[0].name, 'attachment1')
        self.assertIsInstance(fresh_attachments[0].item, self.ITEM_CLASS)

        for f in self.ITEM_CLASS.FIELDS:
            with self.subTest(f=f):
                # Normalize some values we don't control
                if f.is_read_only:
                    continue
                if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
                ):
                    # Timezone fields will (and must) be populated automatically from the timestamp
                    continue
                if isinstance(f, ExtendedPropertyField):
                    # Attachments don't have these values. It may be possible to request it if we can find the FieldURI
                    continue
                if f.name == 'is_read':
                    # This is always true for item attachments?
                    continue
                if f.name == 'reminder_due_by':
                    # EWS sets a default value if it is not set on insert. Ignore
                    continue
                if f.name == 'mime_content':
                    # This will change depending on other contents fields
                    continue
                old_val = getattr(attached_item1, f.name)
                new_val = getattr(fresh_attachments[0].item, f.name)
                if f.is_list:
                    old_val, new_val = set(old_val or ()), set(new_val or ())
                self.assertEqual(old_val, new_val, (f.name, old_val, new_val))

        # Test attach on saved object
        attached_item2 = self.get_test_item(folder=self.test_folder)
        attached_item2.attachments = []
        attached_item2.save()
        attachment2 = ItemAttachment(name='attachment2', item=attached_item2)
        item.attach(attachment2)

        self.assertEqual(len(item.attachments), 2)
        fresh_item = list(self.account.fetch(ids=[item]))[0]
        self.assertEqual(len(fresh_item.attachments), 2)
        fresh_attachments = sorted(fresh_item.attachments,
                                   key=lambda a: a.name)
        self.assertEqual(fresh_attachments[0].name, 'attachment1')
        self.assertIsInstance(fresh_attachments[0].item, self.ITEM_CLASS)

        for f in self.ITEM_CLASS.FIELDS:
            with self.subTest(f=f):
                # Normalize some values we don't control
                if f.is_read_only:
                    continue
                if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
                ):
                    # Timezone fields will (and must) be populated automatically from the timestamp
                    continue
                if isinstance(f, ExtendedPropertyField):
                    # Attachments don't have these values. It may be possible to request it if we can find the FieldURI
                    continue
                if f.name == 'reminder_due_by':
                    # EWS sets a default value if it is not set on insert. Ignore
                    continue
                if f.name == 'is_read':
                    # This is always true for item attachments?
                    continue
                if f.name == 'mime_content':
                    # This will change depending on other contents fields
                    continue
                old_val = getattr(attached_item1, f.name)
                new_val = getattr(fresh_attachments[0].item, f.name)
                if f.is_list:
                    old_val, new_val = set(old_val or ()), set(new_val or ())
                self.assertEqual(old_val, new_val, (f.name, old_val, new_val))

        self.assertEqual(fresh_attachments[1].name, 'attachment2')
        self.assertIsInstance(fresh_attachments[1].item, self.ITEM_CLASS)

        for f in self.ITEM_CLASS.FIELDS:
            # Normalize some values we don't control
            if f.is_read_only:
                continue
            if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
            ):
                # Timezone fields will (and must) be populated automatically from the timestamp
                continue
            if isinstance(f, ExtendedPropertyField):
                # Attachments don't have these values. It may be possible to request it if we can find the FieldURI
                continue
            if f.name == 'reminder_due_by':
                # EWS sets a default value if it is not set on insert. Ignore
                continue
            if f.name == 'is_read':
                # This is always true for item attachments?
                continue
            if f.name == 'mime_content':
                # This will change depending on other contents fields
                continue
            old_val = getattr(attached_item2, f.name)
            new_val = getattr(fresh_attachments[1].item, f.name)
            if f.is_list:
                old_val, new_val = set(old_val or ()), set(new_val or ())
            self.assertEqual(old_val, new_val, (f.name, old_val, new_val))

        # Test detach
        item.detach(attachment2)
        self.assertTrue(attachment2.attachment_id is None)
        self.assertTrue(attachment2.parent_item is None)
        fresh_item = list(self.account.fetch(ids=[item]))[0]
        self.assertEqual(len(fresh_item.attachments), 1)
        fresh_attachments = sorted(fresh_item.attachments,
                                   key=lambda a: a.name)

        for f in self.ITEM_CLASS.FIELDS:
            with self.subTest(f=f):
                # Normalize some values we don't control
                if f.is_read_only:
                    continue
                if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
                ):
                    # Timezone fields will (and must) be populated automatically from the timestamp
                    continue
                if isinstance(f, ExtendedPropertyField):
                    # Attachments don't have these values. It may be possible to request it if we can find the FieldURI
                    continue
                if f.name == 'reminder_due_by':
                    # EWS sets a default value if it is not set on insert. Ignore
                    continue
                if f.name == 'is_read':
                    # This is always true for item attachments?
                    continue
                if f.name == 'mime_content':
                    # This will change depending on other contents fields
                    continue
                old_val = getattr(attached_item1, f.name)
                new_val = getattr(fresh_attachments[0].item, f.name)
                if f.is_list:
                    old_val, new_val = set(old_val or ()), set(new_val or ())
                self.assertEqual(old_val, new_val, (f.name, old_val, new_val))

        # Test attach with non-saved item
        attached_item3 = self.get_test_item(folder=self.test_folder)
        attached_item3.attachments = []
        attachment3 = ItemAttachment(name='attachment2', item=attached_item3)
        item.attach(attachment3)
        item.detach(attachment3)
Exemplo n.º 2
0
    def test_item(self):
        # Test insert
        insert_kwargs = self.get_random_insert_kwargs()
        insert_kwargs['categories'] = self.categories
        item = self.ITEM_CLASS(folder=self.test_folder, **insert_kwargs)
        # Test with generator as argument
        insert_ids = self.test_folder.bulk_create(items=(i for i in [item]))
        self.assertEqual(len(insert_ids), 1)
        self.assertIsInstance(insert_ids[0], BaseItem)
        find_ids = list(
            self.test_folder.filter(
                categories__contains=item.categories).values_list(
                    'id', 'changekey'))
        self.assertEqual(len(find_ids), 1)
        self.assertEqual(len(find_ids[0]), 2, find_ids[0])
        self.assertEqual(insert_ids, find_ids)
        # Test with generator as argument
        item = list(self.account.fetch(ids=(i for i in find_ids)))[0]
        for f in self.ITEM_CLASS.FIELDS:
            with self.subTest(f=f):
                if not f.supports_version(self.account.version):
                    # Cannot be used with this EWS version
                    continue
                if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
                ):
                    # Timezone fields will (and must) be populated automatically from the timestamp
                    continue
                if f.is_read_only:
                    continue
                if f.name == 'reminder_due_by':
                    # EWS sets a default value if it is not set on insert. Ignore
                    continue
                if f.name == 'mime_content':
                    # This will change depending on other contents fields
                    continue
                old, new = getattr(item, f.name), insert_kwargs[f.name]
                if f.is_list:
                    old, new = set(old or ()), set(new or ())
                self.assertEqual(old, new, (f.name, old, new))

        # Test update
        update_kwargs = self.get_random_update_kwargs(
            item=item, insert_kwargs=insert_kwargs)
        if self.ITEM_CLASS in (Contact, DistributionList):
            # Contact and DistributionList don't support mime_type updates at all
            update_kwargs.pop('mime_content', None)
        update_fieldnames = [
            f for f in update_kwargs.keys() if f != 'attachments'
        ]
        for k, v in update_kwargs.items():
            setattr(item, k, v)
        # Test with generator as argument
        update_ids = self.account.bulk_update(
            items=(i for i in [(item, update_fieldnames)]))
        self.assertEqual(len(update_ids), 1)
        self.assertEqual(len(update_ids[0]), 2, update_ids)
        self.assertEqual(insert_ids[0].id,
                         update_ids[0][0])  # ID should be the same
        self.assertNotEqual(
            insert_ids[0].changekey,
            update_ids[0][1])  # Changekey should change when item is updated
        item = list(self.account.fetch(update_ids))[0]
        for f in self.ITEM_CLASS.FIELDS:
            with self.subTest(f=f):
                if not f.supports_version(self.account.version):
                    # Cannot be used with this EWS version
                    continue
                if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
                ):
                    # Timezone fields will (and must) be populated automatically from the timestamp
                    continue
                if f.is_read_only or f.is_read_only_after_send:
                    # These cannot be changed
                    continue
                if f.name == 'mime_content':
                    # This will change depending on other contents fields
                    continue
                old, new = getattr(item, f.name), update_kwargs[f.name]
                if f.name == 'reminder_due_by':
                    if old is None:
                        # EWS does not always return a value if reminder_is_set is False. Set one now
                        item.reminder_due_by = new
                        continue
                    if new is not None:
                        # EWS sometimes randomly sets the new reminder due date to one month before or after we
                        # wanted it, and sometimes 30 days before or after. But only sometimes...
                        old_date = old.astimezone(
                            self.account.default_timezone).date()
                        new_date = new.astimezone(
                            self.account.default_timezone).date()
                        if getattr(item, 'is_all_day',
                                   False) and old_date == new_date:
                            # There is some weirdness with the time part of the reminder_due_by value for all-day events
                            item.reminder_due_by = new
                            continue
                        if relativedelta(month=1) + new_date == old_date:
                            item.reminder_due_by = new
                            continue
                        if relativedelta(month=1) + old_date == new_date:
                            item.reminder_due_by = new
                            continue
                        if abs(old_date -
                               new_date) == datetime.timedelta(days=30):
                            item.reminder_due_by = new
                            continue
                if f.is_list:
                    old, new = set(old or ()), set(new or ())
                self.assertEqual(old, new, (f.name, old, new))

        # Test wiping or removing fields
        wipe_kwargs = {}
        for f in self.ITEM_CLASS.FIELDS:
            if not f.supports_version(self.account.version):
                # Cannot be used with this EWS version
                continue
            if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
            ):
                # Timezone fields will (and must) be populated automatically from the timestamp
                continue
            if f.is_required or f.is_required_after_save:
                # These cannot be deleted
                continue
            if f.is_read_only or f.is_read_only_after_send:
                # These cannot be changed
                continue
            wipe_kwargs[f.name] = None
        for k, v in wipe_kwargs.items():
            setattr(item, k, v)
        wipe_ids = self.account.bulk_update([
            (item, update_fieldnames),
        ])
        self.assertEqual(len(wipe_ids), 1)
        self.assertEqual(len(wipe_ids[0]), 2, wipe_ids)
        self.assertEqual(insert_ids[0].id,
                         wipe_ids[0][0])  # ID should be the same
        self.assertNotEqual(
            insert_ids[0].changekey, wipe_ids[0]
            [1])  # Changekey should not be the same when item is updated
        item = list(self.account.fetch(wipe_ids))[0]
        for f in self.ITEM_CLASS.FIELDS:
            with self.subTest(f=f):
                if not f.supports_version(self.account.version):
                    # Cannot be used with this EWS version
                    continue
                if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
                ):
                    # Timezone fields will (and must) be populated automatically from the timestamp
                    continue
                if f.is_required or f.is_required_after_save:
                    continue
                if f.is_read_only or f.is_read_only_after_send:
                    continue
                old, new = getattr(item, f.name), wipe_kwargs[f.name]
                if f.is_list:
                    old, new = set(old or ()), set(new or ())
                self.assertEqual(old, new, (f.name, old, new))

        try:
            self.ITEM_CLASS.register('extern_id', ExternId)
            # Test extern_id = None, which deletes the extended property entirely
            extern_id = None
            item.extern_id = extern_id
            wipe2_ids = self.account.bulk_update([
                (item, ['extern_id']),
            ])
            self.assertEqual(len(wipe2_ids), 1)
            self.assertEqual(len(wipe2_ids[0]), 2, wipe2_ids)
            self.assertEqual(insert_ids[0].id,
                             wipe2_ids[0][0])  # ID must be the same
            self.assertNotEqual(
                insert_ids[0].changekey,
                wipe2_ids[0][1])  # Changekey must change when item is updated
            item = list(self.account.fetch(wipe2_ids))[0]
            self.assertEqual(item.extern_id, extern_id)
        finally:
            self.ITEM_CLASS.deregister('extern_id')
Exemplo n.º 3
0
 def get_random_update_kwargs(self, item, insert_kwargs):
     update_kwargs = {}
     now = UTC_NOW()
     for f in self.ITEM_CLASS.FIELDS:
         if not f.supports_version(self.account.version):
             # Cannot be used with this EWS version
             continue
         if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
         ):
             # Timezone fields will (and must) be populated automatically from the timestamp
             continue
         if f.is_read_only:
             # These cannot be changed
             continue
         if not item.is_draft and f.is_read_only_after_send:
             # These cannot be changed when the item is no longer a draft
             continue
         if f.name == 'message_id' and f.is_read_only_after_send:
             # Cannot be updated, regardless of draft status
             continue
         if f.name == 'attachments':
             # Testing attachments is heavy. Leave this to specific tests
             update_kwargs[f.name] = []
             continue
         if f.name == 'resources':
             # The test server doesn't have any resources
             update_kwargs[f.name] = []
             continue
         if isinstance(f, AttachmentField):
             # Attachments are handled separately
             continue
         if f.name == 'start':
             start = get_random_date(start_date=insert_kwargs['end'].date())
             update_kwargs[f.name], update_kwargs['end'] = \
                 get_random_datetime_range(start_date=start, end_date=start, tz=self.account.default_timezone)
             update_kwargs['recurrence'] = self.random_val(
                 self.ITEM_CLASS.get_field_by_fieldname('recurrence'))
             update_kwargs['recurrence'].boundary.start = update_kwargs[
                 f.name].date()
             continue
         if f.name == 'end':
             continue
         if f.name == 'recurrence':
             continue
         if f.name == 'due_date':
             # start_date must be before due_date, and before complete_date which must be in the past
             update_kwargs['start_date'], update_kwargs[f.name] = \
                 get_random_datetime_range(end_date=now.date(), tz=self.account.default_timezone)
             continue
         if f.name == 'start_date':
             continue
         if f.name == 'status':
             # Update task to a completed state. complete_date must be a date in the past, and < than start_date
             update_kwargs[f.name] = Task.COMPLETED
             update_kwargs['percent_complete'] = Decimal(100)
             continue
         if f.name == 'percent_complete':
             continue
         if f.name == 'reminder_is_set':
             if self.ITEM_CLASS == Task:
                 # Task type doesn't allow updating 'reminder_is_set' to True
                 update_kwargs[f.name] = False
             else:
                 update_kwargs[f.name] = not insert_kwargs[f.name]
             continue
         if isinstance(f, BooleanField):
             update_kwargs[f.name] = not insert_kwargs[f.name]
             continue
         if f.value_cls in (Mailbox, Attendee):
             if insert_kwargs[f.name] is None:
                 update_kwargs[f.name] = self.random_val(f)
             else:
                 update_kwargs[f.name] = None
             continue
         update_kwargs[f.name] = self.random_val(f)
     if self.ITEM_CLASS == CalendarItem:
         # EWS always sets due date to 'start'
         update_kwargs['reminder_due_by'] = update_kwargs['start']
     if update_kwargs.get('is_all_day', False):
         # For is_all_day items, EWS will remove the time part of start and end values
         update_kwargs['start'] = update_kwargs['start'].date()
         update_kwargs['end'] = (update_kwargs['end'] +
                                 datetime.timedelta(days=1)).date()
     return update_kwargs
Exemplo n.º 4
0
 def get_random_insert_kwargs(self):
     insert_kwargs = {}
     for f in self.ITEM_CLASS.FIELDS:
         if not f.supports_version(self.account.version):
             # Cannot be used with this EWS version
             continue
         if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
         ):
             # Timezone fields will (and must) be populated automatically from the timestamp
             continue
         if f.is_read_only:
             # These cannot be created
             continue
         if f.name == 'mime_content':
             # This needs special formatting. See separate test_mime_content() test
             continue
         if f.name == 'attachments':
             # Testing attachments is heavy. Leave this to specific tests
             insert_kwargs[f.name] = []
             continue
         if f.name == 'resources':
             # The test server doesn't have any resources
             insert_kwargs[f.name] = []
             continue
         if f.name == 'optional_attendees':
             # 'optional_attendees' and 'required_attendees' are mutually exclusive
             insert_kwargs[f.name] = None
             continue
         if f.name == 'start':
             start = get_random_date()
             insert_kwargs[f.name], insert_kwargs['end'] = \
                 get_random_datetime_range(start_date=start, end_date=start, tz=self.account.default_timezone)
             insert_kwargs['recurrence'] = self.random_val(
                 self.ITEM_CLASS.get_field_by_fieldname('recurrence'))
             insert_kwargs['recurrence'].boundary.start = insert_kwargs[
                 f.name].date()
             continue
         if f.name == 'end':
             continue
         if f.name == 'is_all_day':
             # For CalendarItem instances, the 'is_all_day' attribute affects the 'start' and 'end' values. Changing
             # from 'false' to 'true' removes the time part of these datetimes.
             insert_kwargs['is_all_day'] = False
             continue
         if f.name == 'recurrence':
             continue
         if f.name == 'due_date':
             # start_date must be before due_date
             insert_kwargs['start_date'], insert_kwargs[f.name] = \
                 get_random_datetime_range(tz=self.account.default_timezone)
             continue
         if f.name == 'start_date':
             continue
         if f.name == 'status':
             # Start with an incomplete task
             status = get_random_choice(
                 set(f.supported_choices(version=self.account.version)) -
                 {Task.COMPLETED})
             insert_kwargs[f.name] = status
             if status == Task.NOT_STARTED:
                 insert_kwargs['percent_complete'] = Decimal(0)
             else:
                 insert_kwargs['percent_complete'] = get_random_decimal(
                     1, 99)
             continue
         if f.name == 'percent_complete':
             continue
         insert_kwargs[f.name] = self.random_val(f)
     return insert_kwargs
 def get_random_update_kwargs(self, item, insert_kwargs):
     update_kwargs = {}
     for f in self.ITEM_CLASS.FIELDS:
         if not f.supports_version(self.account.version):
             # Cannot be used with this EWS version
             continue
         if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields(
         ):
             # Timezone fields will (and must) be populated automatically from the timestamp
             continue
         if f.is_read_only:
             # These cannot be changed
             continue
         if not item.is_draft and f.is_read_only_after_send:
             # These cannot be changed when the item is no longer a draft
             continue
         if f.name == "message_id" and f.is_read_only_after_send:
             # Cannot be updated, regardless of draft status
             continue
         if f.name == "attachments":
             # Testing attachments is heavy. Leave this to specific tests
             update_kwargs[f.name] = []
             continue
         if f.name == "resources":
             # The test server doesn't have any resources
             update_kwargs[f.name] = []
             continue
         if isinstance(f, AttachmentField):
             # Attachments are handled separately
             continue
         if f.name == "start":
             start = get_random_date(start_date=insert_kwargs["end"].date())
             update_kwargs[
                 f.name], update_kwargs["end"] = get_random_datetime_range(
                     start_date=start,
                     end_date=start,
                     tz=self.account.default_timezone)
             update_kwargs["recurrence"] = self.random_val(
                 self.ITEM_CLASS.get_field_by_fieldname("recurrence"))
             update_kwargs["recurrence"].boundary.start = update_kwargs[
                 f.name].date()
             continue
         if f.name == "start_date":
             update_kwargs[f.name] = get_random_datetime().date()
             update_kwargs["due_date"] = update_kwargs[f.name]
             # Don't set 'recurrence' here. It's difficult to test updates so we'll test task recurrence separately
             update_kwargs["recurrence"] = None
             continue
         if f.name == "end":
             continue
         if f.name == "recurrence":
             continue
         if f.name == "due_date":
             continue
         if f.name == "start_date":
             continue
         if f.name == "status":
             # Update task to a completed state
             update_kwargs[f.name] = Task.COMPLETED
             update_kwargs["percent_complete"] = Decimal(100)
             continue
         if f.name == "percent_complete":
             continue
         if f.name == "reminder_is_set":
             if self.ITEM_CLASS == Task:
                 # Task type doesn't allow updating 'reminder_is_set' to True
                 update_kwargs[f.name] = False
             else:
                 update_kwargs[f.name] = not insert_kwargs[f.name]
             continue
         if isinstance(f, BooleanField):
             update_kwargs[f.name] = not insert_kwargs[f.name]
             continue
         if f.value_cls in (Mailbox, Attendee):
             if insert_kwargs[f.name] is None:
                 update_kwargs[f.name] = self.random_val(f)
             else:
                 update_kwargs[f.name] = None
             continue
         update_kwargs[f.name] = self.random_val(f)
     if self.ITEM_CLASS == CalendarItem:
         # EWS always sets due date to 'start'
         update_kwargs["reminder_due_by"] = update_kwargs["start"]
     if update_kwargs.get("is_all_day", False):
         # For is_all_day items, EWS will remove the time part of start and end values
         update_kwargs["start"] = update_kwargs["start"].date()
         update_kwargs["end"] = (update_kwargs["end"] +
                                 datetime.timedelta(days=1)).date()
     return update_kwargs