def test_renamed_label_refresh(db, default_account, thread, message, imapuid,
                               folder, mock_imapclient, monkeypatch):
    # Check that imapuids see their labels refreshed after running
    # the LabelRenameHandler.
    msg_uid = imapuid.msg_uid
    uid_dict = {msg_uid: GmailFlags((), ('stale label', ), ('23', ))}

    update_metadata(default_account.id, folder.id, folder.canonical_name,
                    uid_dict, db.session)

    new_flags = {
        msg_uid: {
            'FLAGS': ('\\Seen', ),
            'X-GM-LABELS': ('new label', ),
            'MODSEQ': ('23', )
        }
    }
    mock_imapclient._data['[Gmail]/All mail'] = new_flags

    mock_imapclient.add_folder_data(folder.name, new_flags)

    monkeypatch.setattr(MockIMAPClient, 'search', lambda x, y: [msg_uid])

    semaphore = Semaphore(value=1)

    rename_handler = LabelRenameHandler(default_account.id,
                                        default_account.namespace.id,
                                        'new label', semaphore)

    # Acquire the semaphore to check that LabelRenameHandlers block if
    # the semaphore is in-use.
    semaphore.acquire()
    rename_handler.start()

    # Wait 10 secs and check that the data hasn't changed.
    gevent.sleep(10)

    labels = list(imapuid.labels)
    assert len(labels) == 1
    assert labels[0].name == 'stale label'
    semaphore.release()
    rename_handler.join()

    db.session.refresh(imapuid)
    # Now check that the label got updated.
    labels = list(imapuid.labels)
    assert len(labels) == 1
    assert labels[0].name == 'new label'
def test_renamed_label_refresh(db, default_account, thread, message, imapuid,
                               folder, mock_imapclient, monkeypatch):
    # Check that imapuids see their labels refreshed after running
    # the LabelRenameHandler.
    msg_uid = imapuid.msg_uid
    uid_dict = {msg_uid: GmailFlags((), ("stale label", ), ("23", ))}

    update_metadata(default_account.id, folder.id, folder.canonical_name,
                    uid_dict, db.session)

    new_flags = {
        msg_uid: {
            b"FLAGS": (b"\\Seen", ),
            b"X-GM-LABELS": (b"new label", ),
            b"MODSEQ": (23, ),
        }
    }
    mock_imapclient._data["[Gmail]/All mail"] = new_flags

    mock_imapclient.add_folder_data(folder.name, new_flags)

    monkeypatch.setattr(MockIMAPClient, "search", lambda x, y: [msg_uid])

    semaphore = Semaphore(value=1)

    rename_handler = LabelRenameHandler(default_account.id,
                                        default_account.namespace.id,
                                        "new label", semaphore)

    # Acquire the semaphore to check that LabelRenameHandlers block if
    # the semaphore is in-use.
    semaphore.acquire()
    rename_handler.start()

    gevent.sleep(0)  # yield to the handler

    labels = list(imapuid.labels)
    assert len(labels) == 1
    assert labels[0].name == "stale label"
    semaphore.release()
    rename_handler.join()

    db.session.refresh(imapuid)
    # Now check that the label got updated.
    labels = list(imapuid.labels)
    assert len(labels) == 1
    assert labels[0].name == "new label"
Example #3
0
    def save_folder_names(self, db_session, raw_folders):
        """
        Save the folders, labels present on the remote backend for an account.

        * Create Folder/ Label objects.
        * Delete Folders/ Labels that no longer exist on the remote.

        Notes
        -----
        Gmail uses IMAP folders and labels.
        Canonical folders ('all', 'trash', 'spam') are therefore mapped to both
        Folder and Label objects, everything else is created as a Label only.

        We don't canonicalize names to lowercase when saving because
        different backends may be case-sensitive or otherwise - code that
        references saved names should canonicalize if needed when doing
        comparisons.

        """
        account = db_session.query(Account).get(self.account_id)

        current_labels = set()
        old_labels = {label for label in db_session.query(Label).filter(
            Label.account_id == self.account_id,
            Label.deleted_at == None)}  # noqa

        # Is it the first time we've been syncing folders?
        # It's important to know this because we don't want to
        # be refreshing the labels for every message at the very
        # beginning of the initial sync.
        first_time_syncing_folders = len(old_labels) == 0

        # Create new labels, folders
        for raw_folder in raw_folders:
            if raw_folder.role == 'starred':
                # The starred state of messages is tracked separately
                # (we set Message.is_starred from the '\\Flagged' flag)
                continue

            if raw_folder.role in ('all', 'spam', 'trash'):
                self.handle_raw_folder_change(db_session, account, raw_folder)

            label = Label.find_or_create(db_session, account,
                                         raw_folder.display_name,
                                         raw_folder.role)
            if label.deleted_at is not None:
                # This is a label which was previously marked as deleted
                # but which mysteriously reappeared. Unmark it.
                log.info('Deleted label recreated on remote',
                         name=raw_folder.display_name)
                label.deleted_at = None
                label.category.deleted_at = None

            current_labels.add(label)

        new_labels = current_labels - old_labels
        db_session.commit()

        if not first_time_syncing_folders:
            # Try to see if a label has been renamed.
            for label in new_labels:
                db_session.refresh(label)
                db_session.expunge(label)

                rename_handler = LabelRenameHandler(
                     account_id=self.account_id,
                     namespace_id=self.namespace_id,
                     label_name=label.name,
                     semaphore=self.label_rename_semaphore)

                rename_handler.start()

        self.set_sync_should_run_bit(account)

        deleted_labels = old_labels - current_labels
        self.mark_deleted_labels(db_session, deleted_labels)

        db_session.commit()