Esempio n. 1
0
    def add_emails(items=None, *, tag='\\All', fetch=True, parse=True):
        gmail.client()
        if items is None:
            items = [{}]
        gm_client.fetch = [('OK', [])]
        for item in items:
            gm_client.uid += 1
            uid = gm_client.uid
            gid = 100 * uid
            raw = item.get('raw')
            if raw:
                msg = raw
            else:
                txt = item.get('txt', '42')
                msg = message.binary(txt)

                subj = item.get('subj')
                if not subj:
                    subj = 'Subj %s' % uid
                msg.add_header('Subject', subj)

                date = item.get('date')
                if not date:
                    date = gm_client.time + uid
                msg.add_header('Date', formatdate(date))

                mid = item.get('mid')
                if not mid:
                    mid = '<%s@mlr>' % uid
                msg.add_header('Message-ID', mid)

                in_reply_to = item.get('in_reply_to')
                if in_reply_to:
                    msg.add_header('In-Reply-To', in_reply_to)
                refs = item.get('refs')
                if refs:
                    msg.add_header('References', refs)

                msg = msg.as_bytes()
            flags = item.get('flags', '').encode()
            labels = item.get('labels', '').encode()
            res = gm_client.con.append(local.ALL, gmail.MAP_LABELS.get(tag),
                                       None, msg)
            if res[0] != 'OK':
                raise Exception(res)
            gm_client.fetch[0][1].extend([
                (b'1 (X-GM-MSGID %d X-GM-THRID %d X-GM-LABELS (%s) UID %d '
                 b'INTERNALDATE "08-Jul-2017 09:08:30 +0000" FLAGS (%s) '
                 b'BODY[] {%d}' % (gid, gid, labels, uid, flags, len(msg)),
                 msg), b')'
            ])

        if fetch:
            gmail.fetch_folder(tag)
        if parse:
            local.parse()
Esempio n. 2
0
 def add_emails(items=None, *, tag='\\All', fetch=True, parse=True):
     if items is None:
         items = [{}]
     gm_client.fetch = [('OK', []), ('OK', [])]
     for item in items:
         add_email(item, tag)
     if fetch:
         remote.fetch_folder(tag=tag)
     if parse:
         local.parse()
Esempio n. 3
0
def test_parsed_msg(clean_users, gm_client, load_file, latest):
    gm_client.add_emails([{'flags': '\\Flagged'}])
    msg = latest()
    assert 'X-UID' in msg['body']
    assert re.match('<\d+>', msg['body']['X-UID'])
    assert '\\Flagged' in msg['flags']

    # `email.policy.default` is not working with long addresses.
    # Exits with: "segmentation fault (core dumped)"
    # when running in threads.
    gm_client.add_emails([{
        'txt': 'some text'
    }, {
        'raw':
        load_file('msg-header-with-long-addresses.txt')
    }])
    msg = latest()['body']
    assert msg['to'].startswith('primary discussion list')

    # should be decoding of headers during parsing
    gm_client.add_emails([{
        'raw': load_file('msg-header-with-encoding.txt')
    }],
                         parse=False)
    local.parse(batch=1)
    msg = latest(raw=True)['body'].decode()
    expect = '\r\n'.join([
        'X-UID: <4>',
        'Message-Id: <with-encoding@test>',
        'Subject: Re: не пора ли подкрепиться?',
        'Date: Wed, 07 Jan 2015 13:23:22 +0000',
        'From: "Катя К." <*****@*****.**>',
        'To: Grisha <*****@*****.**>',
    ])
    assert msg.startswith(expect)

    gm_client.add_emails([{
        'raw': load_file('msg-header-with-no-encoding.txt')
    }],
                         parse=False)
    local.parse(batch=1)
    msg = latest(raw=True)['body'].decode()
    expect = '\r\n'.join([
        'X-UID: <5>',
        'Message-Id: <with-no-encoding@test>',
        'Subject: Re: не пора ли подкрепиться?',
        'Date: Wed, 07 Jan 2015 13:23:22 +0000',
        'From: "Катя К." <*****@*****.**>',
        'To: Гриша <*****@*****.**>',
    ])
    assert msg.startswith(expect)
Esempio n. 4
0
def test_batched_uids(gm_client):
    con = local.client()
    bsize = 25000
    assert [] == con.fetch([str(i) for i in range(1, 100, 2)], 'FLAGS')
    assert [] == con.fetch([str(i) for i in range(1, bsize, 2)], 'FLAGS')

    con.select(local.ALL, readonly=False)
    assert [] == con.store([str(i) for i in range(1, 100, 2)], '+FLAGS', '#')
    assert [] == con.store([str(i) for i in range(1, bsize, 2)], '+FLAGS', '#')

    # with one message
    msg = message.binary('42')
    msg.add_header('Message-Id', message.gen_msgid())
    con.append(local.SRC, None, None, msg.as_bytes())
    con.select(local.SRC, readonly=True)
    assert [b'1 (UID 1 FLAGS (\\Recent))'
            ] == (con.fetch([str(i) for i in range(1, 100, 2)], 'FLAGS'))
    assert [b'1 (UID 1 FLAGS (\\Recent))'
            ] == (con.fetch([str(i) for i in range(1, bsize, 2)], 'FLAGS'))

    con.select(local.SRC, readonly=False)
    assert [b'1 (UID 1 FLAGS (\\Recent #1))'
            ] == (con.store([str(i) for i in range(1, 100, 2)], '+FLAGS',
                            '#1'))
    assert [b'1 (UID 1 FLAGS (\\Recent #1 #2))'
            ] == (con.store([str(i) for i in range(1, bsize, 2)], '+FLAGS',
                            '#2'))

    gm_client.add_emails([{} for i in range(1, 22)], parse=False)
    assert local.parse(batch=10) is None
Esempio n. 5
0
def test_update_metadata(gm_client, msgs, patch, call):
    gm_client.add_emails([{}, {}])
    assert ['1', '2'] == [i['uid'] for i in msgs(local.SRC)]
    local.link_threads(['1', '2'])
    thrids, thrs = local.data_threads.get()
    assert {'1': '2', '2': '2'} == thrids

    # emulate that metadata wasn't updated when messages were re-parsed
    with patch.object(local, 'update_metadata') as m:
        local.parse('1:*')
        m.assert_called_with('3:*')

    # refresh metadata
    local.update_metadata('1:*')
    thrids, thrs = local.data_threads.get()
    assert {'3': '4', '4': '4'} == thrids
Esempio n. 6
0
def test_batched_uids(gm_client):
    con = local.client()
    bsize = 25000
    assert [] == con.fetch([str(i) for i in range(1, 100, 2)], 'FLAGS')
    assert [] == con.fetch([str(i) for i in range(1, bsize, 2)], 'FLAGS')

    con.select(local.ALL, readonly=False)
    assert [] == con.store([str(i) for i in range(1, 100, 2)], '+FLAGS', '#')
    assert [] == con.store([str(i) for i in range(1, bsize, 2)], '+FLAGS', '#')

    # with one message
    msg = message.binary('42')
    msg.add_header('Message-Id', message.gen_msgid())
    con.append(local.SRC, None, None, msg.as_bytes())
    con.select(local.SRC, readonly=True)
    assert [b'1 (UID 1 FLAGS (\\Recent))'] == (
        con.fetch([str(i) for i in range(1, 100, 2)], 'FLAGS')
    )
    assert [b'1 (UID 1 FLAGS (\\Recent))'] == (
        con.fetch([str(i) for i in range(1, bsize, 2)], 'FLAGS')
    )

    con.select(local.SRC, readonly=False)
    assert [b'1 (UID 1 FLAGS (\\Recent #1))'] == (
        con.store([str(i) for i in range(1, 100, 2)], '+FLAGS', '#1')
    )
    assert [b'1 (UID 1 FLAGS (\\Recent #1 #2))'] == (
        con.store([str(i) for i in range(1, bsize, 2)], '+FLAGS', '#2')
    )

    gm_client.add_emails([{} for i in range(1, 22)], parse=False)
    assert local.parse(batch=10) is None
Esempio n. 7
0
def test_link_threads_part1(gm_client, msgs):
    gm_client.add_emails([{}, {}])
    refs = [
        '<*****@*****.**>',
        '<*****@*****.**>'
    ]
    assert local.data_threads.get()[1] == {'2': ['2'], '1': ['1']}
    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    local.link_threads(['1', '2'])
    assert local.data_threads.get()[1] == {'2': ['1', '2']}
    res = msgs(local.SRC)
    assert [i['body']['references'] for i in res] == [None, None]

    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    local.parse('all')
    assert local.data_threads.get()[1] == {'4': ['3', '4']}
    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    gm_client.add_emails([{}])
    refs += ['<*****@*****.**>']
    assert local.search_thrs('all') == ['5', '4']
    assert local.data_threads.get()[1] == {'4': ['3', '4'], '5': ['5']}
    local.link_threads(['4', '5'])
    assert local.search_thrs('all') == ['5']
    assert local.data_threads.get()[1] == {'5': ['3', '4', '5']}
    res = msgs(local.SRC)
    assert [i['body']['references'] for i in res] == [None, None, None]
    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    gm_client.add_emails([{'refs': '<101@mlr>'}])
    refs += ['<*****@*****.**> <101@mlr>']
    assert local.search_thrs('all') == ['6']
    assert local.data_threads.get()[1] == {'6': ['3', '4', '5', '6']}
    res = msgs(local.SRC)
    assert [i['body']['references'] for i in res] == [
        None, None, None, '<101@mlr>'
    ]
    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    assert local.search_thrs('uid 4') == [i[0] for i in local.thrs_info(['4'])]
Esempio n. 8
0
def test_link_threads_part1(gm_client, msgs):
    gm_client.add_emails([{}, {}])
    refs = [
        '<*****@*****.**>',
        '<*****@*****.**>'
    ]
    assert local.data_threads.get()[1] == {'2': ['2'], '1': ['1']}
    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    local.link_threads(['1', '2'])
    assert local.data_threads.get()[1] == {'2': ['1', '2']}
    res = msgs(local.SRC)
    assert [i['body']['references'] for i in res] == [None, None]

    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    local.parse('all')
    assert local.data_threads.get()[1] == {'4': ['3', '4']}
    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    gm_client.add_emails([{}])
    refs += ['<*****@*****.**>']
    assert local.search_thrs('all') == ['5', '4']
    assert local.data_threads.get()[1] == {'4': ['3', '4'], '5': ['5']}
    local.link_threads(['4', '5'])
    assert local.search_thrs('all') == ['5']
    assert local.data_threads.get()[1] == {'5': ['3', '4', '5']}
    res = msgs(local.SRC)
    assert [i['body']['references'] for i in res] == [None, None, None]
    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    gm_client.add_emails([{'refs': '<101@mlr>'}])
    refs += ['<*****@*****.**> <101@mlr>']
    assert local.search_thrs('all') == ['6']
    assert local.data_threads.get()[1] == {'6': ['3', '4', '5', '6']}
    res = msgs(local.SRC)
    assert [i['body']['references'] for i in res] == [
        None, None, None, '<101@mlr>'
    ]
    res = msgs()
    assert [i['body']['references'] for i in res] == refs

    assert local.search_thrs('uid 4') == [i[0] for i in local.thrs_info(['4'])]
Esempio n. 9
0
def test_cli_idle(gm_client, msgs, login, patch):
    spawn(lambda: cli.main('sync %s --timeout=300' % login.user1))
    sleep(2)

    gm_client.add_emails([{}] * 4, fetch=False, parse=False)
    sleep(2)
    assert len(msgs(local.SRC)) == 4
    assert len(msgs()) == 4

    local.parse('all')

    gm_client.add_emails([{}], fetch=False, parse=False)
    sleep(2)
    assert len(msgs(local.SRC)) == 5
    assert len(msgs()) == 5

    con_src = local.client(local.SRC, readonly=False)
    con_src.store('1:*', '+FLAGS', '#1')
    sleep(2)
    assert [i['flags'] for i in msgs(local.SRC)] == ['#1'] * 5
    assert [i['flags'] for i in msgs()] == ['#1'] * 5
Esempio n. 10
0
def test_cli_idle(gm_client, msgs, login, patch):
    spawn(lambda: cli.main('sync %s --timeout=300' % login.user1))
    sleep(2)

    gm_client.add_emails([{}] * 4, fetch=False, parse=False)
    sleep(2)
    assert len(msgs(local.SRC)) == 4
    assert len(msgs()) == 4

    local.parse('all')

    gm_client.add_emails([{}], fetch=False, parse=False)
    sleep(2)
    assert len(msgs(local.SRC)) == 5
    assert len(msgs()) == 5

    con_src = local.client(local.SRC, readonly=False)
    con_src.store('1:*', '+FLAGS', '#1')
    sleep(2)
    assert [i['flags'] for i in msgs(local.SRC)] == ['#1'] * 5
    assert [i['flags'] for i in msgs()] == ['#1'] * 5
Esempio n. 11
0
def test_fetch_and_parse(clean_users, gm_client, some):
    lm = local.client()
    gmail.fetch_folder()
    local.parse()

    def gm_uidnext():
        res = lm.getmetadata(local.SRC, 'gmail/uidnext/all')
        assert res == [(b'Src (/private/gmail/uidnext/all {12}', some), b')']
        return some.value

    def mlr_uidnext():
        res = lm.getmetadata(local.ALL, 'uidnext')
        assert res == [(b'All (/private/uidnext {1}', some), b')']
        return some.value

    assert gm_uidnext().endswith(b',1')
    assert lm.getmetadata(local.ALL,
                          'uidnext') == [b'All (/private/uidnext NIL)']

    gm_client.add_emails()
    assert gm_uidnext().endswith(b',2')
    assert mlr_uidnext() == b'2'
    assert lm.select(local.SRC) == [b'1']
    assert lm.select(local.ALL) == [b'1']

    gm_client.add_emails([{'txt': '1'}, {'txt': '2'}])
    assert gm_uidnext().endswith(b',4')
    assert mlr_uidnext() == b'4'
    assert lm.select(local.SRC) == [b'3']
    assert lm.select(local.ALL) == [b'3']

    gmail.fetch_folder()
    local.parse('all')
    assert gm_uidnext().endswith(b',4')
    assert mlr_uidnext() == b'4'
    assert lm.select(local.SRC) == [b'3']
    assert lm.select(local.ALL) == [b'3']
    assert lm.status(local.ALL, '(UIDNEXT)') == [b'All (UIDNEXT 7)']
Esempio n. 12
0
def test_cli_idle(gm_client, msgs, login, patch):
    with patch('mailur.gmail.get_credentials') as m:
        m.return_value = login.user2, 'user'
        spawn(lambda: cli.main('sync %s --timeout=300' % login.user1))
        sleep(3)

    gm_client.add_emails([{}] * 4, fetch=False, parse=False)
    sleep(2)
    assert len(msgs(local.SRC)) == 4
    assert len(msgs()) == 4

    local.parse('all')

    gm_client.add_emails([{}], fetch=False, parse=False)
    sleep(1)
    assert len(msgs(local.SRC)) == 5
    assert len(msgs()) == 5

    con_src = local.client(local.SRC, readonly=False)
    con_src.store('1:*', '+FLAGS', '#1')
    sleep(1)
    assert [i['flags'] for i in msgs(local.SRC)] == ['#1'] * 5
    assert [i['flags'] for i in msgs()] == ['#latest #1'] * 5
Esempio n. 13
0
def test_fetch_and_parse(gm_client, some, raises):
    lm = local.client()
    remote.fetch()
    local.parse()

    def gm_uidnext():
        account = remote.data_account.get()
        key = ':'.join((account['imap_host'], account['username'], '\\All'))
        res = remote.data_uidnext.key(key)
        assert res
        return res[1]

    def mlr_uidnext():
        return local.data_uidnext.get()

    assert gm_uidnext() == 1
    assert mlr_uidnext() is None

    gm_client.add_emails()
    assert gm_uidnext() == 2
    assert mlr_uidnext() == 2
    assert lm.select(local.SRC) == [b'1']
    assert lm.select(local.ALL) == [b'1']

    gm_client.add_emails([{'txt': '1'}, {'txt': '2'}])
    assert gm_uidnext() == 4
    assert mlr_uidnext() == 4
    assert lm.select(local.SRC) == [b'3']
    assert lm.select(local.ALL) == [b'3']

    remote.fetch()
    local.parse('all')
    assert gm_uidnext() == 4
    assert mlr_uidnext() == 4
    assert lm.select(local.SRC) == [b'3']
    assert lm.select(local.ALL) == [b'3']
    assert lm.status(local.ALL, '(UIDNEXT)') == [b'mlr/All (UIDNEXT 7)']
Esempio n. 14
0
def test_fetch_and_parse(gm_client, some, raises):
    lm = local.client()
    remote.fetch()
    local.parse()

    def gm_uidnext():
        account = remote.data_account.get()
        key = ':'.join((account['imap_host'], account['username'], '\\All'))
        res = remote.data_uidnext.key(key)
        assert res
        return res[1]

    def mlr_uidnext():
        return local.data_uidnext.get()

    assert gm_uidnext() == 1
    assert mlr_uidnext() is None

    gm_client.add_emails()
    assert gm_uidnext() == 2
    assert mlr_uidnext() == 2
    assert lm.select(local.SRC) == [b'1']
    assert lm.select(local.ALL) == [b'1']

    gm_client.add_emails([{'txt': '1'}, {'txt': '2'}])
    assert gm_uidnext() == 4
    assert mlr_uidnext() == 4
    assert lm.select(local.SRC) == [b'3']
    assert lm.select(local.ALL) == [b'3']

    remote.fetch()
    local.parse('all')
    assert gm_uidnext() == 4
    assert mlr_uidnext() == 4
    assert lm.select(local.SRC) == [b'3']
    assert lm.select(local.ALL) == [b'3']
    assert lm.status(local.ALL, '(UIDNEXT)') == [b'mlr/All (UIDNEXT 7)']
Esempio n. 15
0
def test_uidpairs(gm_client, msgs, patch, call):
    gm_client.add_emails([{}, {}], parse=False)
    assert ['1', '2'] == [i['uid'] for i in msgs(local.SRC)]

    assert local.pair_origin_uids(['1', '2']) == ()
    assert local.pair_parsed_uids(['1', '2']) == ()

    local.parse()
    assert local.data_uidpairs.get() == {'1': '1', '2': '2'}
    assert local.pair_origin_uids(['1', '2']) == ('1', '2')
    assert local.pair_parsed_uids(['1', '2']) == ('1', '2')

    local.parse('uid 1')
    assert ['2', '3'] == [i['uid'] for i in msgs()]
    assert local.data_uidpairs.get() == {'1': '3', '2': '2'}
    assert local.pair_origin_uids(['1', '2']) == ('3', '2')
    assert local.pair_parsed_uids(['2', '3']) == ('2', '1')

    local.parse('all')
    assert ['4', '5'] == [i['uid'] for i in msgs()]
    assert local.data_uidpairs.get() == {'1': '4', '2': '5'}
    assert local.pair_origin_uids(['1', '2']) == ('4', '5')
    assert local.pair_parsed_uids(['4', '5']) == ('1', '2')
    assert local.pair_origin_uids(['2']) == ('5',)
    assert local.pair_parsed_uids(['5']) == ('2',)

    # warm cache up
    local.data_addresses.get()
    local.data_uidpairs.get()
    local.data_threads.get()
    local.data_msgids.get()
    with patch('imaplib.IMAP4.uid') as m:
        m.return_value = 'OK', []
        local.update_metadata('4')
        assert m.called
        assert m.call_args_list == [
            call('FETCH', '4', '(FLAGS BINARY.PEEK[1])'),
            call('FETCH', '1:*', '(UID BODY[HEADER.FIELDS (Subject)])'),
            call('THREAD', 'REFS UTF-8 INTHREAD REFS UID 4'),
        ]

    patched = {'wraps': local.update_metadata}
    with patch('mailur.local.update_metadata', **patched) as m:
        local.parse('uid 1')
        assert m.called
        assert m.call_args == call('6')

        m.reset_mock()
        local.parse('all')
        assert m.called
        assert m.call_args == call('1:*')

        m.reset_mock()
        local.parse()
        assert not m.called

        m.reset_mock()
        gm_client.add_emails([{}])
        assert m.called
        assert m.call_args == call('9')
Esempio n. 16
0
def test_general(gm_client, load_file, latest, load_email):
    gm_client.add_emails([{'flags': '\\Flagged'}])
    msg = latest()
    assert 'X-UID' in msg['body']
    assert re.match(r'<\d+>', msg['body']['X-UID'])
    assert '\\Flagged' in msg['flags']

    # `email.policy.default` is not working with long addresses.
    # Exits with: "segmentation fault (core dumped)"
    # when running in threads.
    gm_client.add_emails([
        {'txt': 'some text'},
        {'raw': load_file('msg-header-with-long-addresses.txt')}
    ])
    msg = latest()['body']
    assert msg['to'].startswith('"primary discussion list')

    # should be decoding of headers during parsing
    gm_client.add_emails([
        {'raw': load_file('msg-header-with-encoding.txt')}
    ], parse=False)
    local.parse(batch=1)
    msg = latest(raw=True)['body'].decode()
    expect = '\r\n'.join([
        'X-UID: <4>',
        'Message-ID: <with-encoding@test>',
        'Subject: Re: не пора ли подкрепиться?',
        'Date: Wed, 07 Jan 2015 13:23:22 +0000',
        'From: "Катя К." <*****@*****.**>',
        'To: "Grisha" <*****@*****.**>',
    ])
    assert msg.startswith(expect), '%s\n\n%s' % (expect, msg)

    gm_client.add_emails([
        {'raw': load_file('msg-header-with-no-encoding.txt')}
    ], parse=False)
    local.parse(batch=1)
    msg = latest(raw=True)['body'].decode()
    expect = '\r\n'.join([
        'X-UID: <5>',
        'Message-ID: <with-no-encoding@test>',
        'Subject: Re: не пора ли подкрепиться?',
        'Date: Wed, 07 Jan 2015 13:23:22 +0000',
        'From: "Катя К." <*****@*****.**>',
        'To: "Гриша" <*****@*****.**>',
    ])
    assert msg.startswith(expect), '%s\n\n%s' % (expect, msg)

    # msg with UnicodeDecodeError
    raw = b'\r\n'.join([
        b'Message-ID: <with-bad-symbol@test>',
        b'Subject: bad symbol?',
        b'Date: Wed, 07 Jan 2015 13:23:22 +0000',
        b'From: [email protected]',
        b'To: [email protected]',
        b'Content-type: text/html; charset=utf-8',
        b'Content-Transfer-Encoding: 8bit',
        b'MIME-Version: 1.0',
        b'',
        b'',
        b'\xd0\xb2\xd0\xbe\xd0\xb7\xd0\xbc\xd0\xbe\xd0\xb6\xd0\xbd\xd0\r\n '
        b'\xbe\xd1\x81\xd1\x82\xd0\xb8,'
    ])
    gm_client.add_emails([{'raw': raw}])
    msg = latest(parsed=True)
    assert msg['meta']['preview'] == 'возможн� �сти,'
    assert msg['body'] == '<p>возможн�\r\n �сти,</p>'
    assert msg['meta']['errors']
    assert (
        "error on 'text/html()': [UnicodeDecodeError] 'utf-8' codec can't "
        'decode byte 0xd0 in position 16: invalid continuation byte'
        in msg['meta']['errors'][0]
    )

    m = load_email('msg-lookup-error.txt', parsed=True)
    assert m['meta']['preview'] == 'test'
    assert m['body'] == '<p>test</p>'
    assert m['meta']['errors']
    assert (
        '[LookupError] unknown encoding: iso-2022-int-1'
        in m['meta']['errors'][0]
    )

    # date can't be parsed
    raw = b'\r\n'.join([
        b'Message-ID: <with-bad-symbol@test>',
        b'Subject: bad symbol?',
        b'Date: 15.06.2018 09:24:13 +0800',
        b'From: [email protected]',
        b'To: [email protected]',
        b'Content-type: text/html; charset=utf-8',
        b'Content-Transfer-Encoding: 8bit',
        b'MIME-Version: 1.0',
        b'',
        b'',
        b'Hello!'
    ])
    gm_client.add_emails([{'raw': raw}])
    msg = latest(parsed=True)
    assert msg['meta']['date'] == msg['meta']['arrived']
    assert msg['meta']['errors'] == [
        'error on date: val=\'15.06.2018 09:24:13 +0800\' '
        'err=TypeError("\'NoneType\' object is not iterable",)'
    ]

    # ending @ symbol and address without @ symbol at all
    m = load_email('msg-from-ending-snail.txt', parsed=True)
    assert m['meta']['from'] == {
        'addr': 'grrr@', 'name': 'grrr', 'title': 'grrr@',
        'hash': '8ea2bc312c94c9596ad95772d6cd579c',
    }
    assert m['meta']['reply-to'] == [{
        'addr': 'grrr', 'name': 'grrr', 'title': 'grrr',
        'hash': 'd4468c0c805f9a0e200c0e916824547a',
    }]
    assert m['meta']['to']
    assert m['meta']['reply-to']
    assert m['body_full']['to'] == 'katya@'
    assert m['body_full']['from'] == 'grrr@'
    assert m['body_full']['reply-to'] == 'grrr'

    raw = load_email('msg-from-rss2email.txt', parsed=True)['raw'].decode()
    assert 'From: "БлоGнот: Gray" <*****@*****.**>' in raw, raw

    # test links
    m = load_email('msg-links.txt', parsed=True)
    link = '<a href="{0}" target="_blank">'
    l1 = link.format('https://github.com/naspeh/mailur')
    # l2 = link.format('http://bottlepy.org/docs/dev/routing.html#rule-syntax')
    l2 = link.format('http://bottlepy.org/docs/dev/routing.html')
    assert m['body'].count(l1) == 3, '%s\n%s' % (l1, m['body'])
    assert m['body'].count(l2) == 2, '%s\n%s' % (l2, m['body'])
Esempio n. 17
0
def test_general(gm_client, load_email, latest, login, some):
    web = login()
    res = web.search({'q': '', 'preload': 10})
    assert res == {
        'uids': [],
        'msgs': {},
        'msgs_info': '/msgs/info',
    }

    msg = {'labels': '\\Inbox'}
    gm_client.add_emails([msg, dict(msg, refs='<101@mlr>')])
    res = web.search({'q': '', 'preload': 10})
    assert res == {
        'uids': ['2', '1'],
        'msgs': {
            '1': {
                'arrived': 1499504910,
                'count': 0,
                'date': some,
                'errors': [],
                'files': [],
                'from_list': [],
                'is_draft': False,
                'is_link': False,
                'is_pinned': False,
                'is_unread': True,
                'msgid': '<101@mlr>',
                'origin_uid': '1',
                'parent': None,
                'preview': '42',
                'query_msgid': 'ref:<101@mlr>',
                'query_subject': ':threads subj:"Subj 101"',
                'query_thread': 'thread:1',
                'subject': 'Subj 101',
                'tags': ['#inbox'],
                'time_human': some,
                'time_title': some,
                'thrid': '<*****@*****.**>',
                'uid': '1',
                'url_raw': '/raw/1/original-msg.eml',
                'url_reply': '/reply/1',
            },
            '2': {
                'arrived': 1499504910,
                'count': 0,
                'date': some,
                'errors': [],
                'files': [],
                'from_list': [],
                'is_draft': False,
                'is_link': False,
                'is_pinned': False,
                'is_unread': True,
                'msgid': '<102@mlr>',
                'origin_uid': '2',
                'parent': '<101@mlr>',
                'preview': '42',
                'query_msgid': 'ref:<102@mlr>',
                'query_subject': ':threads subj:"Subj 102"',
                'query_thread': 'thread:2',
                'subject': 'Subj 102',
                'tags': ['#inbox'],
                'time_human': some,
                'time_title': some,
                'uid': '2',
                'url_raw': '/raw/2/original-msg.eml',
                'url_reply': '/reply/2',
            }
        },
        'msgs_info': '/msgs/info',
    }

    web.post_json('/msgs/body', {'uids': ['1']}, status=200)
    res = web.search({'q': 'in:#inbox'})
    assert [i['is_unread'] for i in res['msgs'].values()] == [False, True]
    web.post_json('/msgs/body', {'uids': ['1']}, status=200)

    res = web.search({'q': ':threads'})
    assert res == {
        'uids': ['2'],
        'msgs': {
            '2': {
                'arrived': 1499504910,
                'count': 2,
                'date': some,
                'errors': [],
                'files': [],
                'from_list': [],
                'is_draft': False,
                'is_link': False,
                'is_pinned': False,
                'is_unread': True,
                'msgid': '<102@mlr>',
                'origin_uid': '2',
                'parent': '<101@mlr>',
                'preview': '42',
                'query_msgid': 'ref:<102@mlr>',
                'query_subject': ':threads subj:"Subj 102"',
                'query_thread': 'thread:2',
                'subject': 'Subj 102',
                'tags': ['#inbox'],
                'time_human': some,
                'time_title': some,
                'uid': '2',
                'uids': ['1', '2'],
                'url_raw': '/raw/2/original-msg.eml',
                'url_reply': '/reply/2',
            }
        },
        'msgs_info': '/thrs/info',
        'threads': True
    }
    web.flag({'uids': ['2'], 'new': ['\\Seen']})
    res = web.search({'q': ':threads in:#inbox'})
    assert not res['msgs']['2']['is_unread']

    res = web.get('/raw/2')
    assert res.content_type == 'text/plain'
    assert 'Message-ID: <102@mlr>' in res.text
    res = web.get('/raw/2/1')
    assert res.content_type == 'text/plain'
    assert '42' in res.text

    m = load_email('msg-attachments-two-gmail.txt')
    q = 'thread:%s' % m['uid']
    res = web.search({'q': q})
    assert res == {
        'uids': ['3'],
        'edit': None,
        'has_link': False,
        'msgs_info': '/msgs/info',
        'msgs': {
            '3': some
        },
        'same_subject': [],
        'tags': [],
        'thread': True,
    }
    assert some['files'] == [{
        'filename': '08.png',
        'image': True,
        'path': '2',
        'size': 553,
        'url': '/raw/3/2/08.png',
    }, {
        'filename': '09.png',
        'image': True,
        'path': '3',
        'size': 520,
        'url': '/raw/3/3/09.png',
    }]
    assert some['preview'] == (
        'ответ на тело 2014-03-03 18:09 GMT+02:00 Ne Greh '
        '&lt; [email protected] &gt; : тело  [08.png, 09.png]')
    res = web.get(some['files'][0]['url'], status=200)
    assert res.content_type == 'image/png'
    res = web.get(some['files'][1]['url'], status=200)
    assert res.content_type == 'image/png'
    res = web.get('/raw/3')
    assert res.content_type == 'text/plain'

    res = web.search({'q': 'tag:#inbox'})
    assert res['tags'] == ['#inbox']
    assert [i['tags'] for i in res['msgs'].values()] == [[], []]

    # one message from thread is going to #trash
    gm_client.add_emails([{'labels': '\\Inbox', 'from': '*****@*****.**'}])
    res = web.search({'q': ':threads tag:#inbox'})
    assert res['uids'] == ['4', '2']
    res = web.post_json('/thrs/link', {'uids': res['uids']}).json
    assert res == {'uid': '5'}
    res = web.search({'q': ':threads tag:#inbox'})
    assert res['uids'] == ['4']
    web.flag({'uids': ['4'], 'new': ['#trash']})
    res = web.search({'q': ':threads tag:#inbox'})
    assert res['uids'] == ['4']
    assert res['msgs']['4']['subject'] == 'Subj 102'
    assert 'from' not in res['msgs']['4']
    res = web.search({'q': 'thread:4'})
    assert res['uids'] == ['1', '2']
    res = web.search({'q': ':threads tag:#trash'})
    assert res['uids'] == ['4']
    assert res['msgs']['4']['subject'] == 'Subj 104'
    assert res['msgs']['4']['from'] == {
        'addr': '*****@*****.**',
        'hash': 'bc11cf997156ef71c34c23457e67fd65',
        'name': 'one',
        'query': 'tag:#trash :threads from:[email protected]',
        'title': '*****@*****.**'
    }
    res = web.search({'q': 'tag:#trash thread:4'})
    assert res['uids'] == ['4']

    # thread in #trash
    gm_client.add_emails([{'labels': '\\Trash'}])
    m = latest(parsed=True)
    res = web.post_json('/thrs/info', {'uids': [m['uid']]}, status=200).json
    assert res == {}

    # unlink thread
    gm_client.add_emails([{'labels': 'test'}] * 2)
    local.parse('all')
    res = web.search({'q': ':threads tag:test'})
    assert res['uids'] == ['16', '15']
    res = web.post_json('/thrs/link', {'uids': res['uids']}).json
    res = web.search({'q': ':threads tag:test'})
    assert res['uids'] == ['16']
    res = web.post_json('/thrs/unlink', {'uids': res['uids']}).json
    assert res == {'query': ':threads uid:16,15'}
    res = web.search({'q': res['query']})
    assert res['uids'] == ['16', '15']
Esempio n. 18
0
def test_data_threads(gm_client):
    gm_client.add_emails([{'subj': 'new subj'}])
    assert local.data_threads.get()[1] == {'1': ['1']}
    assert local.search_thrs('all') == ['1']

    local.parse('all')
    assert local.data_threads.get()[1] == {'2': ['2']}
    assert local.search_thrs('all') == ['2']

    gm_client.add_emails([{'subj': 'new subj'}])
    assert local.data_threads.get()[1] == {'3': ['2', '3']}
    assert local.search_thrs('all') == ['3']

    gm_client.add_emails([{'in_reply_to': '<101@mlr>'}])
    assert local.data_threads.get()[1] == {'4': ['2', '3', '4']}
    assert local.search_thrs('all') == ['4']

    gm_client.add_emails([{'refs': '<101@mlr> <102@mlr>'}])
    assert local.data_threads.get()[1] == {'5': ['2', '3', '4', '5']}
    assert local.search_thrs('all') == ['5']

    local.parse('all')
    assert local.data_threads.get()[1] == {'9': ['6', '7', '8', '9']}
    assert local.search_thrs('all') == ['9']

    local.parse('uid *')
    assert local.data_threads.get()[1] == {'10': ['6', '7', '8', '10']}
    assert local.search_thrs('all') == ['10']

    local.update_threads('1:*')
    assert local.data_threads.get()[1] == {'10': ['6', '7', '8', '10']}
    assert local.search_thrs('all') == ['10']

    local.update_threads('1')
    assert local.data_threads.get()[1] == {'10': ['6', '7', '8', '10']}
    assert local.search_thrs('all') == ['10']

    local.update_threads('1:*')
    assert local.data_threads.get()[1] == {'10': ['6', '7', '8', '10']}
    assert local.search_thrs('all') == ['10']

    gm_client.add_emails([
        {'refs': '<non-exist@mlr>'},
        {'refs': '<non-exist@mlr> <101@mlr>'}
    ])
    assert local.data_threads.get()[1] == {
        '11': ['11'],
        '12': ['6', '7', '8', '10', '12']
    }
    assert local.search_thrs('all') == ['12', '11']

    gm_client.add_emails([{'labels': 't1'}, {'labels': 't2'}], parse=False)
    local.parse('UID 6:*')
    assert local.data_threads.get()[1] == {
        '11': ['11'],
        '13': ['6', '7', '8', '10', '13'],
        '14': ['14'],
        '15': ['15']
    }
    assert local.search_thrs('all') == ['15', '14', '13', '11']

    local.update_threads('*')
    assert local.data_threads.get()[1] == {
        '11': ['11'],
        '13': ['6', '7', '8', '10', '13'],
        '14': ['14'],
        '15': ['15']
    }
    assert local.search_thrs('all') == ['15', '14', '13', '11']
Esempio n. 19
0
def test_update_threads(clean_users, gm_client, msgs):
    gm_client.add_emails([{}])
    res = msgs()
    assert ['#latest'] == [i['flags'] for i in res]

    local.parse('all')
    res = msgs()
    assert ['#latest'] == [i['flags'] for i in res]

    gm_client.add_emails([{}])
    res = msgs()
    assert ['#latest', '#latest'] == [i['flags'] for i in res]

    gm_client.add_emails([{'in_reply_to': '<101@mlr>'}])
    res = msgs()
    assert ['', '#latest', '#latest'] == [i['flags'] for i in res]

    gm_client.add_emails([{'refs': '<101@mlr> <102@mlr>'}])
    res = msgs()
    assert ['', '', '', '#latest'] == [i['flags'] for i in res]

    local.parse('all')
    res = msgs()
    assert ['', '', '', '#latest'] == [i['flags'] for i in res]

    local.parse('uid *')
    res = msgs()
    assert ['', '', '', '#latest'] == [i['flags'] for i in res]

    con = local.client()
    local.update_threads(con, 'all')
    res = msgs()
    assert ['', '', '', '#latest'] == [i['flags'] for i in res]

    local.update_threads(con, 'UID 1')
    res = msgs()
    assert ['', '', '', '#latest'] == [i['flags'] for i in res]

    local.update_threads(con)
    res = msgs()
    assert ['', '', '', '#latest'] == [i['flags'] for i in res]

    gm_client.add_emails([{
        'refs': '<non-exist@mlr>'
    }, {
        'refs': '<non-exist@mlr> <101@mlr>'
    }])
    res = msgs()
    assert [i['flags'] for i in res] == ['', '', '', '', '#latest', '#latest']

    gm_client.add_emails([{'labels': 't1'}, {'labels': 't2'}], parse=False)
    local.parse('UID 6:*')
    res = msgs()
    assert [i['flags'] for i in res] == [
        '', '', '', '', '#latest', '#latest', '#latest t1', '#latest t2'
    ]

    local.update_threads(con, 'UID *')
    res = msgs()
    assert [i['flags'] for i in res] == [
        '', '', '', '', '#latest', '#latest', '#latest t1', '#latest t2'
    ]
Esempio n. 20
0
def test_link_threads_part1(clean_users, gm_client, msgs):
    gm_client.add_emails([{}, {}])
    res = msgs()
    assert ['1', '2'] == [i['uid'] for i in res]
    assert ['#latest', '#latest'] == [i['flags'] for i in res]
    assert [i['body']['references'] for i in res] == [None, None]

    local.link_threads(['1', '2'])
    res = msgs(local.SRC)
    assert (('1', '2', '3'), ) == thread()
    assert ['', '', '#link'] == [i['flags'] for i in res]
    assert [i['body']['references']
            for i in res] == [None, None, '<101@mlr> <102@mlr>']

    res = msgs()
    assert ['1', '2', '3'] == [i['uid'] for i in res]
    assert ['', '#latest', '#link'] == [i['flags'] for i in res]
    assert [i['body']['references']
            for i in res] == [None, None, '<101@mlr> <102@mlr>']

    local.parse('all')
    res = msgs(local.SRC)
    assert (('1', '2', '3'), ) == thread()
    assert ['', '', '#link'] == [i['flags'] for i in res]
    res = msgs()
    assert ['4', '5', '6'] == [i['uid'] for i in res]
    assert ['', '#latest', '#link'] == [i['flags'] for i in res]
    assert [i['body']['references']
            for i in res] == [None, None, '<101@mlr> <102@mlr>']

    gm_client.add_emails([{}])
    local.link_threads(['4', '7'])
    res = msgs(local.SRC)
    assert (('1', '2', '4', '5', '3'), ) == thread()
    assert ['1', '2', '3', '4', '5'] == [i['uid'] for i in res]
    assert [i['flags']
            for i in res] == ['', '', '\\Deleted #link', '', '#link']
    assert [i['body']['references'] for i in res] == [
        None, None, '<101@mlr> <102@mlr>', None,
        '<101@mlr> <102@mlr> <103@mlr>'
    ]
    res = msgs()
    assert ['4', '5', '7', '8'] == [i['uid'] for i in res]
    assert ['', '', '#latest', '#link'] == [i['flags'] for i in res]
    assert [i['body']['references'] for i in res
            ] == [None, None, None, '<101@mlr> <102@mlr> <103@mlr>']

    gm_client.add_emails([{'refs': '<101@mlr>'}])
    res = msgs(local.SRC)
    assert (('1', '2', '4', '5', '3', '6'), ) == thread()
    assert ['1', '2', '3', '4', '5', '6'] == [i['uid'] for i in res]
    assert [i['flags']
            for i in res] == ['', '', '\\Deleted #link', '', '#link', '']
    assert [i['body']['references'] for i in res] == [
        None, None, '<101@mlr> <102@mlr>', None,
        '<101@mlr> <102@mlr> <103@mlr>', '<101@mlr>'
    ]
    res = msgs()
    assert ['4', '5', '7', '8', '9'] == [i['uid'] for i in res]
    assert ['', '', '', '#link', '#latest'] == [i['flags'] for i in res]
    assert [i['body']['references'] for i in res] == [
        None, None, None, '<101@mlr> <102@mlr> <103@mlr>', '<101@mlr>'
    ]
Esempio n. 21
0
def test_uid_pairs(clean_users, gm_client, msgs, patch):
    gm_client.add_emails([{}, {}], parse=False)
    assert ['1', '2'] == [i['uid'] for i in msgs(local.SRC)]

    assert local.pair_origin_uids(['1', '2']) == ()
    assert local.pair_parsed_uids(['1', '2']) == ()

    local.parse()
    assert local.uid_pairs() == {'1': '1', '2': '2'}
    assert local.pair_origin_uids(['1', '2']) == ('1', '2')
    assert local.pair_parsed_uids(['1', '2']) == ('1', '2')

    local.parse('uid 1')
    assert ['2', '3'] == [i['uid'] for i in msgs()]
    assert local.uid_pairs() == {'1': '3', '2': '2'}
    assert local.pair_origin_uids(['1', '2']) == ('3', '2')
    assert local.pair_parsed_uids(['2', '3']) == ('2', '1')

    local.parse('all')
    assert ['4', '5'] == [i['uid'] for i in msgs()]
    assert local.uid_pairs() == {'1': '4', '2': '5'}
    assert local.pair_origin_uids(['1', '2']) == ('4', '5')
    assert local.pair_parsed_uids(['4', '5']) == ('1', '2')
    assert local.pair_origin_uids(['2']) == ('5', )
    assert local.pair_parsed_uids(['5']) == ('2', )

    with patch('imaplib.IMAP4.uid') as m:
        m.return_value = 'OK', []
        local.save_uid_pairs('4')
        assert m.called
        assert m.call_args[0][1] == '4'

    with patch('mailur.local.save_uid_pairs', wraps=local.save_uid_pairs) as m:
        local.parse('uid 1')
        assert m.called
        assert m.call_args[0][0] == '6'

        m.reset_mock()
        local.parse('all')
        assert m.called
        assert m.call_args[0][0] == '1:*'

        m.reset_mock()
        local.parse()
        assert not m.called

        m.reset_mock()
        gm_client.add_emails([{}])
        assert m.called
        assert m.call_args[0][0] == '9'
Esempio n. 22
0
def test_link_threads_part1(gm_client, msgs):
    gm_client.add_emails([{}, {}])
    res = msgs()
    assert ['1', '2'] == [i['uid'] for i in res]
    assert ['#latest', '#latest'] == [i['flags'] for i in res]
    assert [i['body']['references'] for i in res] == [
        '<*****@*****.**>',
        '<*****@*****.**>'
    ]

    local.link_threads(['1', '2'])
    res = msgs(local.SRC)
    assert (('1', '2', '3'), ) == thread()
    assert ['', '', '\\Seen #link'] == [i['flags'] for i in res]
    assert [i['body']['references'] for i in res] == [
        None, None,
        ('<*****@*****.**> <101@mlr> '
         '<*****@*****.**> <102@mlr>')
    ]

    res = msgs()
    assert ['1', '2', '3'] == [i['uid'] for i in res]
    assert ['', '#latest', '\\Seen #link'] == [i['flags'] for i in res]
    assert [i['body']['references'] for i in res] == [
        '<*****@*****.**>',
        '<*****@*****.**>',
        ('<*****@*****.**> <101@mlr> '
         '<*****@*****.**> <102@mlr>')
    ]

    local.parse('all')
    res = msgs(local.SRC)
    assert (('1', '2', '3'), ) == thread()
    assert ['', '', '\\Seen #link'] == [i['flags'] for i in res]
    res = msgs()
    assert ['4', '5', '6'] == [i['uid'] for i in res]
    assert ['', '#latest', '\\Seen #link'] == [i['flags'] for i in res]
    assert [i['body']['references'] for i in res] == [
        '<*****@*****.**>',
        '<*****@*****.**>',
        ('<*****@*****.**> <101@mlr> '
         '<*****@*****.**> <102@mlr>')
    ]

    gm_client.add_emails([{}])
    local.link_threads(['4', '7'])
    res = msgs(local.SRC)
    assert (('1', '2', '4', '5'), ) == thread()
    assert ['1', '2', '4', '5'] == [i['uid'] for i in res]
    assert [i['flags'] for i in res] == ['', '', '', '\\Seen #link']
    assert [i['body']['references'] for i in res] == [
        None, None, None,
        ('<*****@*****.**> <101@mlr> '
         '<*****@*****.**> <102@mlr> '
         '<*****@*****.**> <103@mlr>')
    ]
    res = msgs()
    assert ['4', '5', '7', '8'] == [i['uid'] for i in res]
    assert ['', '', '#latest', '\\Seen #link'] == [i['flags'] for i in res]
    assert [i['body']['references'] for i in res] == [
        '<*****@*****.**>',
        '<*****@*****.**>',
        '<*****@*****.**>',
        ('<*****@*****.**> <101@mlr> '
         '<*****@*****.**> <102@mlr> '
         '<*****@*****.**> <103@mlr>')
    ]

    gm_client.add_emails([{'refs': '<101@mlr>'}])
    res = msgs(local.SRC)
    assert (('1', '2', '4', '5', '6'), ) == thread()
    assert ['1', '2', '4', '5', '6'] == [i['uid'] for i in res]
    assert [i['flags'] for i in res] == ['', '', '', '\\Seen #link', '']
    assert [i['body']['references'] for i in res] == [
        None, None, None,
        ('<*****@*****.**> <101@mlr> '
         '<*****@*****.**> <102@mlr> '
         '<*****@*****.**> <103@mlr>'),
        '<101@mlr>'
    ]
    res = msgs()
    assert ['4', '5', '7', '8', '9'] == [i['uid'] for i in res]
    assert [i['flags'] for i in res] == ['', '', '', '\\Seen #link', '#latest']
    assert [i['body']['references'] for i in res] == [
        '<*****@*****.**>',
        '<*****@*****.**>',
        '<*****@*****.**>',
        ('<*****@*****.**> <101@mlr> '
         '<*****@*****.**> <102@mlr> '
         '<*****@*****.**> <103@mlr>'),
        '<101@mlr>'
    ]
    assert local.search_thrs('uid 4') == [i[0] for i in local.thrs_info(['4'])]

    local.update_links()
    res = msgs(local.SRC)
    assert (('1', '2', '4', '6', '7'), ) == thread()
    assert ['1', '2', '4', '6', '7'] == [i['uid'] for i in res]
    assert [i['flags'] for i in res] == ['', '', '', '', '\\Seen #link']
    res = msgs()
    assert ['4', '5', '7', '9', '10'] == [i['uid'] for i in res]
    assert [i['flags'] for i in res] == ['', '', '', '#latest', '\\Seen #link']
Esempio n. 23
0
def test_general(gm_client, load_file, latest, load_email):
    gm_client.add_emails([{'flags': '\\Flagged'}])
    msg = latest()
    assert 'X-UID' in msg['body']
    assert re.match(r'<\d+>', msg['body']['X-UID'])
    assert '\\Flagged' in msg['flags']

    # `email.policy.default` is not working with long addresses.
    # Exits with: "segmentation fault (core dumped)"
    # when running in threads.
    gm_client.add_emails([{
        'txt': 'some text'
    }, {
        'raw':
        load_file('msg-header-with-long-addresses.txt')
    }])
    msg = latest()['body']
    assert msg['to'].startswith('"primary discussion list')

    # should be decoding of headers during parsing
    gm_client.add_emails([{
        'raw': load_file('msg-header-with-encoding.txt')
    }],
                         parse=False)
    local.parse(batch=1)
    msg = latest(raw=True)['body'].decode()
    expect = '\r\n'.join([
        'X-UID: <4>',
        'Message-ID: <with-encoding@test>',
        'Subject: Re: не пора ли подкрепиться?',
        'Date: Wed, 07 Jan 2015 13:23:22 +0000',
        'From: "Катя К." <*****@*****.**>',
        'To: "Grisha" <*****@*****.**>',
    ])
    assert msg.startswith(expect), '%s\n\n%s' % (expect, msg)

    gm_client.add_emails([{
        'raw': load_file('msg-header-with-no-encoding.txt')
    }],
                         parse=False)
    local.parse(batch=1)
    msg = latest(raw=True)['body'].decode()
    expect = '\r\n'.join([
        'X-UID: <5>',
        'Message-ID: <with-no-encoding@test>',
        'Subject: Re: не пора ли подкрепиться?',
        'Date: Wed, 07 Jan 2015 13:23:22 +0000',
        'From: "Катя К." <*****@*****.**>',
        'To: "Гриша" <*****@*****.**>',
    ])
    assert msg.startswith(expect), '%s\n\n%s' % (expect, msg)

    # msg with UnicodeDecodeError
    raw = b'\r\n'.join([
        b'Message-ID: <with-bad-symbol@test>', b'Subject: bad symbol?',
        b'Date: Wed, 07 Jan 2015 13:23:22 +0000', b'From: [email protected]',
        b'To: [email protected]', b'Content-type: text/html; charset=utf-8',
        b'Content-Transfer-Encoding: 8bit', b'MIME-Version: 1.0', b'', b'',
        b'\xd0\xb2\xd0\xbe\xd0\xb7\xd0\xbc\xd0\xbe\xd0\xb6\xd0\xbd\xd0\r\n '
        b'\xbe\xd1\x81\xd1\x82\xd0\xb8,'
    ])
    gm_client.add_emails([{'raw': raw}])
    msg = latest(parsed=True)
    assert msg['meta']['preview'] == 'возможн� �сти,'
    assert msg['body'] == '<p>возможн�\r\n �сти,</p>'
    assert msg['meta']['errors']
    assert ("error on 'text/html()': [UnicodeDecodeError] 'utf-8' codec can't "
            'decode byte 0xd0 in position 16: invalid continuation byte'
            in msg['meta']['errors'][0])

    m = load_email('msg-lookup-error.txt', parsed=True)
    assert m['meta']['preview'] == 'test'
    assert m['body'] == '<p>test</p>'
    assert m['meta']['errors']
    assert ('[LookupError] unknown encoding: iso-2022-int-1'
            in m['meta']['errors'][0])

    # date can't be parsed
    raw = b'\r\n'.join([
        b'Message-ID: <with-bad-symbol@test>', b'Subject: bad symbol?',
        b'Date: 15.06.2018 09:24:13 +0800', b'From: [email protected]',
        b'To: [email protected]', b'Content-type: text/html; charset=utf-8',
        b'Content-Transfer-Encoding: 8bit', b'MIME-Version: 1.0', b'', b'',
        b'Hello!'
    ])
    gm_client.add_emails([{'raw': raw}])
    msg = latest(parsed=True)
    assert msg['meta']['date'] == msg['meta']['arrived']
    assert msg['meta']['errors'] == [
        'error on date: val=\'15.06.2018 09:24:13 +0800\' '
        'err=TypeError("\'NoneType\' object is not iterable",)'
    ]

    # ending @ symbol and address without @ symbol at all
    m = load_email('msg-from-ending-snail.txt', parsed=True)
    assert m['meta']['from'] == {
        'addr': 'grrr@',
        'name': 'grrr',
        'title': 'grrr@',
        'hash': '8ea2bc312c94c9596ad95772d6cd579c',
    }
    assert m['meta']['reply-to'] == [{
        'addr':
        'grrr',
        'name':
        'grrr',
        'title':
        'grrr',
        'hash':
        'd4468c0c805f9a0e200c0e916824547a',
    }]
    assert m['meta']['to']
    assert m['meta']['reply-to']
    assert m['body_full']['to'] == 'katya@'
    assert m['body_full']['from'] == 'grrr@'
    assert m['body_full']['reply-to'] == 'grrr'

    raw = load_email('msg-from-rss2email.txt', parsed=True)['raw'].decode()
    assert 'From: "БлоGнот: Gray" <*****@*****.**>' in raw, raw

    # test links
    m = load_email('msg-links.txt', parsed=True)
    link = '<a href="{0}" target="_blank">'
    l1 = link.format('https://github.com/naspeh/mailur')
    # l2 = link.format('http://bottlepy.org/docs/dev/routing.html#rule-syntax')
    l2 = link.format('http://bottlepy.org/docs/dev/routing.html')
    assert m['body'].count(l1) == 3, '%s\n%s' % (l1, m['body'])
    assert m['body'].count(l2) == 2, '%s\n%s' % (l2, m['body'])
Esempio n. 24
0
def test_cli_idle_gmail(gm_client, msgs, login, patch):
    actions = []

    def fetch(con, uids, fields):
        responces = getattr(gm_client, 'fetch', None)
        if 'CHANGEDSINCE' in fields:
            index = fields.split()[-1]
            if index == '5)':
                return ('OK', [
                    b'4 (X-GM-MSGID 10400 '
                    b'X-GM-LABELS ("\\Inbox" "\\Starred" "mlr/thrid/777") '
                    b'FLAGS (\\Seen) UID 104 MODSEQ (427368))'
                ])
            return ('OK', [])
        elif responces:
            return responces.pop()
        elif 'X-GM-LABELS' in fields:
            if con.current_box != local.SRC:
                return ('OK', [
                    b'1 (X-GM-MSGID 10100 X-GM-LABELS () '
                    b'FLAGS (\\Seen) UID 101 MODSEQ (427368))'
                ])
            return ('OK', [
                (b'2 (X-GM-MSGID 10200 X-GM-LABELS () '
                 b'FLAGS (\\Seen) UID 102 MODSEQ (427368))'),
                (b'3 (X-GM-MSGID 10300 X-GM-LABELS () '
                 b'FLAGS (\\Seen) UID 103 MODSEQ (427368))'),
                (b'4 (X-GM-MSGID 10400 X-GM-LABELS () '
                 b'FLAGS (\\Seen) UID 104 MODSEQ (427368))'),
                (b'6 (X-GM-MSGID 10600 X-GM-LABELS (\\Draft) '
                 b'FLAGS (\\Seen) UID 106 MODSEQ (427368))'),
            ])
        return con._uid('FETCH', uids, fields)

    def search(con, charset, *criteria):
        if 'X-GM-MSGID' in criteria[0]:
            uid = int(criteria[0].split()[-1]) // 100
            return ('OK', [(b'%d' % uid)])
        return con._uid('SEARCH', charset, *criteria)

    def store(con, uids, cmd, flags):
        if 'X-GM-LABELS' in cmd:
            actions.append((uids, cmd, sorted(flags.split())))
            return ('OK', [])
        return con._uid('STORE', uids, cmd, flags)

    gm_client.fake_fetch = fetch
    gm_client.fake_search = search
    gm_client.fake_store = store

    spawn(lambda: cli.main('%s sync --timeout=300' % login.user1))
    sleep(5)

    gm_client.add_emails([{}] * 4, fetch=False, parse=False)
    sleep(3)
    assert len(msgs(local.SRC)) == 4
    assert len(msgs()) == 4

    local.parse('all')

    gm_client.add_emails([{}], fetch=False, parse=False)
    sleep(3)
    assert len(msgs(local.SRC)) == 5
    assert len(msgs()) == 5
    expected_flags = ['', '', '', '\\Flagged \\Seen #inbox', '']
    assert [i['flags'] for i in msgs(local.SRC)] == expected_flags
    assert [i['flags'] for i in msgs()] == expected_flags

    con_src = local.client(local.SRC, readonly=False)
    con_src.store('1:*', '+FLAGS', '#1')
    sleep(3)
    expected_flags = [(f + ' #1').strip() for f in expected_flags]
    assert [i['flags'] for i in msgs(local.SRC)] == expected_flags
    assert [i['flags'] for i in msgs()] == expected_flags

    assert actions == [
        ('101', '-X-GM-LABELS', ['\\Spam']),
        ('101', '+X-GM-LABELS', ['\\Inbox']),  # move to \\All
        ('101', '-X-GM-LABELS', ['\\Trash']),
        ('101', '+X-GM-LABELS', ['\\Inbox']),  # move to \\All
        ('104', '+X-GM-LABELS', ['\\Inbox', '\\Starred']),
        ('104', '+X-GM-LABELS', ['\\Inbox', '\\Starred']),
        ('101', '-X-GM-LABELS', ['\\Spam']),
        ('101', '+X-GM-LABELS', ['\\Inbox']),  # move to \\All
        ('101', '-X-GM-LABELS', ['\\Trash']),
        ('101', '+X-GM-LABELS', ['\\Inbox']),  # move to \\All
    ]

    actions.clear()
    con_src = local.client(local.SRC, readonly=False)
    con_src.store('2', '+FLAGS', '#inbox')
    sleep(3)
    con_src.store('2', '+FLAGS', '#trash')
    sleep(3)
    expected_flags[1] = '#inbox #1 #trash'
    assert [i['flags'] for i in msgs(local.SRC)] == expected_flags
    assert [i['flags'] for i in msgs()] == expected_flags
    assert actions == [
        ('102', '+X-GM-LABELS', ['\\Inbox']),
        ('102', '-X-GM-LABELS', ['\\Inbox']),
        ('102', '+X-GM-LABELS', ['\\Trash']),
    ]
Esempio n. 25
0
def test_uidpairs(gm_client, msgs, patch, call):
    gm_client.add_emails([{}, {}], parse=False)
    assert ['1', '2'] == [i['uid'] for i in msgs(local.SRC)]

    assert local.pair_origin_uids(['1', '2']) == ()
    assert local.pair_parsed_uids(['1', '2']) == ()

    local.parse()
    assert local.data_uidpairs.get() == {'1': '1', '2': '2'}
    assert local.pair_origin_uids(['1', '2']) == ('1', '2')
    assert local.pair_parsed_uids(['1', '2']) == ('1', '2')

    local.parse('uid 1')
    assert ['2', '3'] == [i['uid'] for i in msgs()]
    assert local.data_uidpairs.get() == {'1': '3', '2': '2'}
    assert local.pair_origin_uids(['1', '2']) == ('3', '2')
    assert local.pair_parsed_uids(['2', '3']) == ('2', '1')

    local.parse('all')
    assert ['4', '5'] == [i['uid'] for i in msgs()]
    assert local.data_uidpairs.get() == {'1': '4', '2': '5'}
    assert local.pair_origin_uids(['1', '2']) == ('4', '5')
    assert local.pair_parsed_uids(['4', '5']) == ('1', '2')
    assert local.pair_origin_uids(['2']) == ('5',)
    assert local.pair_parsed_uids(['5']) == ('2',)

    # warm cache up
    local.data_addresses.get()
    local.data_uidpairs.get()
    local.data_threads.get()
    local.data_msgids.get()
    with patch('imaplib.IMAP4.uid') as m:
        m.return_value = 'OK', []
        local.update_metadata('4')
        assert m.called
        assert m.call_args_list == [
            call('FETCH', '4', '(FLAGS BINARY.PEEK[1])'),
            call('FETCH', '1:*', '(UID BODY[HEADER.FIELDS (Subject)])'),
            call('THREAD', 'REFS UTF-8 INTHREAD REFS UID 4'),
        ]

    patched = {'wraps': local.update_metadata}
    with patch('mailur.local.update_metadata', **patched) as m:
        local.parse('uid 1')
        assert m.called
        assert m.call_args == call('6')

        m.reset_mock()
        local.parse('all')
        assert m.called
        assert m.call_args == call('1:*')

        m.reset_mock()
        local.parse()
        assert not m.called

        m.reset_mock()
        gm_client.add_emails([{}])
        assert m.called
        assert m.call_args == call('9')
Esempio n. 26
0
def test_data_threads(gm_client):
    gm_client.add_emails([{'subj': 'new subj'}])
    assert local.data_threads.get()[1] == {'1': ['1']}
    assert local.search_thrs('all') == ['1']

    local.parse('all')
    assert local.data_threads.get()[1] == {'2': ['2']}
    assert local.search_thrs('all') == ['2']

    gm_client.add_emails([{'subj': 'new subj'}])
    assert local.data_threads.get()[1] == {'3': ['2', '3']}
    assert local.search_thrs('all') == ['3']

    gm_client.add_emails([{'in_reply_to': '<101@mlr>'}])
    assert local.data_threads.get()[1] == {'4': ['2', '3', '4']}
    assert local.search_thrs('all') == ['4']

    gm_client.add_emails([{'refs': '<101@mlr> <102@mlr>'}])
    assert local.data_threads.get()[1] == {'5': ['2', '3', '4', '5']}
    assert local.search_thrs('all') == ['5']

    local.parse('all')
    assert local.data_threads.get()[1] == {'9': ['6', '7', '8', '9']}
    assert local.search_thrs('all') == ['9']

    local.parse('uid *')
    assert local.data_threads.get()[1] == {'10': ['6', '7', '8', '10']}
    assert local.search_thrs('all') == ['10']

    local.update_threads('1:*')
    assert local.data_threads.get()[1] == {'10': ['6', '7', '8', '10']}
    assert local.search_thrs('all') == ['10']

    local.update_threads('1')
    assert local.data_threads.get()[1] == {'10': ['6', '7', '8', '10']}
    assert local.search_thrs('all') == ['10']

    local.update_threads('1:*')
    assert local.data_threads.get()[1] == {'10': ['6', '7', '8', '10']}
    assert local.search_thrs('all') == ['10']

    gm_client.add_emails([
        {'refs': '<non-exist@mlr>'},
        {'refs': '<non-exist@mlr> <101@mlr>'}
    ])
    assert local.data_threads.get()[1] == {
        '11': ['11'],
        '12': ['6', '7', '8', '10', '12']
    }
    assert local.search_thrs('all') == ['12', '11']

    gm_client.add_emails([{'labels': 't1'}, {'labels': 't2'}], parse=False)
    local.parse('UID 6:*')
    assert local.data_threads.get()[1] == {
        '11': ['11'],
        '13': ['6', '7', '8', '10', '13'],
        '14': ['14'],
        '15': ['15']
    }
    assert local.search_thrs('all') == ['15', '14', '13', '11']

    local.update_threads('*')
    assert local.data_threads.get()[1] == {
        '11': ['11'],
        '13': ['6', '7', '8', '10', '13'],
        '14': ['14'],
        '15': ['15']
    }
    assert local.search_thrs('all') == ['15', '14', '13', '11']