import collections.abc import time import pathlib import pytest import notdb class TestMessage: MaildirMsg = collections.namedtuple('MaildirMsg', ['msgid', 'path']) @pytest.fixture def maildir_msg(self, maildir): msgid, path = maildir.deliver() return self.MaildirMsg(msgid, path) @pytest.fixture def db(self, maildir): with notdb.Database.create(maildir.path) as db: yield db @pytest.fixture def msg(self, db, maildir_msg): msg, dup = db.add(maildir_msg.path, sync_flags=False) yield msg def test_type(self, msg): assert isinstance(msg, notdb.NotmuchObject) assert isinstance(msg, notdb.Message) def test_alive(self, msg): assert msg.alive def test_hash(self, msg): assert hash(msg) def test_eq(self, db, msg): copy = db.get(msg.path) assert msg == copy def test_messageid_type(self, msg): assert isinstance(msg.messageid, str) assert isinstance(msg.messageid, notdb.BinString) assert isinstance(bytes(msg.messageid), bytes) def test_messageid(self, msg, maildir_msg): assert msg.messageid == maildir_msg.msgid def test_messageid_find(self, db, msg): copy = db.find(msg.messageid) assert msg.messageid == copy.messageid def test_threadid_type(self, msg): assert isinstance(msg.threadid, str) assert isinstance(msg.threadid, notdb.BinString) assert isinstance(bytes(msg.threadid), bytes) def test_path_type(self, msg): assert isinstance(msg.path, pathlib.Path) def test_path(self, msg, maildir_msg): assert msg.path == maildir_msg.path def test_pathb_type(self, msg): assert isinstance(msg.pathb, bytes) def test_pathb(self, msg, maildir_msg): assert msg.path == maildir_msg.path def test_filenames_type(self, msg): ifn = msg.filenames() assert isinstance(ifn, collections.abc.Iterator) def test_filenames(self, msg): ifn = msg.filenames() fn = next(ifn) assert fn == msg.path assert isinstance(fn, pathlib.Path) with pytest.raises(StopIteration): next(ifn) assert list(msg.filenames()) == [msg.path] def test_filenamesb_type(self, msg): ifn = msg.filenamesb() assert isinstance(ifn, collections.abc.Iterator) def test_filenamesb(self, msg): ifn = msg.filenamesb() fn = next(ifn) assert fn == msg.pathb assert isinstance(fn, bytes) with pytest.raises(StopIteration): next(ifn) assert list(msg.filenamesb()) == [msg.pathb] def test_ghost_no(self, msg): assert not msg.ghost def test_date(self, msg): # XXX Someone seems to treat things as local time instead of # UTC or the other way around. now = int(time.time()) assert abs(now - msg.date) < 3600*24 def test_header(self, msg): assert msg.header('from') == 'src@example.com' def test_header_not_present(self, msg): with pytest.raises(LookupError): msg.header('foo') def test_freeze(self, msg): with msg.frozen(): msg.tags.add('foo') msg.tags.add('bar') msg.tags.discard('foo') assert 'foo' not in msg.tags assert 'bar' in msg.tags def test_freeze_err(self, msg): msg.tags.add('foo') try: with msg.frozen(): msg.tags.clear() raise Exception('oops') except Exception: assert 'foo' in msg.tags else: pytest.fail('Context manager did not raise') def test_replies_type(self, msg): assert isinstance(msg.replies(), collections.abc.Iterator) def test_replies(self, msg): with pytest.raises(StopIteration): next(msg.replies()) class TestProperties: @pytest.fixture def props(self, maildir): msgid, path = maildir.deliver() with notdb.Database.create(maildir.path) as db: msg, dup = db.add(path, sync_flags=False) yield msg.properties def test_type(self, props): assert isinstance(props, collections.abc.MutableMapping) def test_add_single(self, props): props['foo'] = 'bar' assert props['foo'] == 'bar' props.add('bar', 'baz') assert props['bar'] == 'baz' def test_add_dup(self, props): props.add('foo', 'bar') props.add('foo', 'baz') assert props['foo'] == 'bar' assert (set(props.getall('foo', exact=True)) == {('foo', 'bar'), ('foo', 'baz')}) def test_len(self, props): props.add('foo', 'a') props.add('foo', 'b') props.add('bar', 'a') assert len(props) == 3 assert len(props.keys()) == 2 assert len(props.values()) == 2 assert len(props.items()) == 3 def test_del(self, props): props.add('foo', 'a') props.add('foo', 'b') del props['foo'] with pytest.raises(KeyError): props['foo'] def test_remove(self, props): props.add('foo', 'a') props.add('foo', 'b') props.remove('foo', 'a') assert props['foo'] == 'b' def test_view_abcs(self, props): assert isinstance(props.keys(), collections.abc.KeysView) assert isinstance(props.values(), collections.abc.ValuesView) assert isinstance(props.items(), collections.abc.ItemsView) def test_pop(self, props): props.add('foo', 'a') props.add('foo', 'b') val = props.pop('foo') assert val == 'a' def test_pop_default(self, props): with pytest.raises(KeyError): props.pop('foo') assert props.pop('foo', 'default') == 'default' def test_popitem(self, props): props.add('foo', 'a') assert props.popitem() == ('foo', 'a') with pytest.raises(KeyError): props.popitem() def test_clear(self, props): props.add('foo', 'a') props.clear() assert len(props) == 0 def test_getall(self, props): props.add('foo', 'a') assert set(props.getall('foo')) == {('foo', 'a')} def test_getall_prefix(self, props): props.add('foo', 'a') props.add('foobar', 'b') assert set(props.getall('foo')) == {('foo', 'a'), ('foobar', 'b')} def test_getall_exact(self, props): props.add('foo', 'a') props.add('foobar', 'b') assert set(props.getall('foo', exact=True)) == {('foo', 'a')}