コード例 #1
0
ファイル: state.py プロジェクト: dadbob/pymap
 async def do_list(self, cmd: ListCommand) -> _CommandRet:
     mailboxes, updates = await self.session.list_mailboxes(
         cmd.ref_name, cmd.filter, subscribed=cmd.only_subscribed,
         selected=self._selected)
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     resp_type = LSubResponse if cmd.only_subscribed else ListResponse
     for name, sep, attrs in mailboxes:
         resp.add_untagged(resp_type(name, sep, attrs))
     return resp, updates
コード例 #2
0
ファイル: state.py プロジェクト: icgood/pymap
 async def do_list(self, cmd: ListCommand):
     mailboxes, updates = await self.session.list_mailboxes(
         cmd.ref_name, cmd.filter, subscribed=cmd.only_subscribed,
         selected=self._selected)
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     resp_type = LSubResponse if cmd.only_subscribed else ListResponse
     for name, sep, attrs in mailboxes:
         resp.add_untagged(resp_type(name, sep, attrs))
     return resp, updates
コード例 #3
0
ファイル: state.py プロジェクト: icgood/pymap
 async def do_fetch(self, cmd: FetchCommand):
     if not cmd.uid:
         self.selected.hide_expunged = True
     messages, updates = await self.session.fetch_messages(
         self.selected, cmd.sequence_set, frozenset(cmd.attributes))
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     for msg_seq, msg in messages:
         if msg.expunged:
             resp.code = ResponseCode.of(b'EXPUNGEISSUED')
         msg_attrs = MessageAttributes(msg, self.selected)
         fetch_data = msg_attrs.get_all(cmd.attributes)
         resp.add_untagged(FetchResponse(msg_seq, fetch_data))
     return resp, updates
コード例 #4
0
ファイル: state.py プロジェクト: dadbob/pymap
 async def do_fetch(self, cmd: FetchCommand) -> _CommandRet:
     if not cmd.uid:
         self.selected.hide_expunged = True
     set_seen = any(attr.set_seen for attr in cmd.attributes)
     messages, updates = await self.session.fetch_messages(
         self.selected, cmd.sequence_set, set_seen)
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     for msg_seq, msg in messages:
         if msg.expunged:
             resp.code = ResponseCode.of(b'EXPUNGEISSUED')
         msg_attrs = MessageAttributes(msg, self.selected, cmd.attributes)
         fetch_resp = FetchResponse(msg_seq, msg_attrs,
                                    writing_hook=msg_attrs.load_hook())
         resp.add_untagged(fetch_resp)
     return resp, updates
コード例 #5
0
ファイル: state.py プロジェクト: icgood/pymap
 async def do_search(self, cmd: SearchCommand):
     if not cmd.uid:
         self.selected.hide_expunged = True
     messages, updates = await self.session.search_mailbox(
         self.selected, cmd.keys)
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     msg_ids: List[int] = []
     for msg_seq, msg in messages:
         if msg.expunged:
             resp.code = ResponseCode.of(b'EXPUNGEISSUED')
         if cmd.uid:
             msg_ids.append(msg.uid)
         else:
             msg_ids.append(msg_seq)
     resp.add_untagged(SearchResponse(msg_ids))
     return resp, updates
コード例 #6
0
ファイル: state.py プロジェクト: dadbob/pymap
 async def do_search(self, cmd: SearchCommand) -> _CommandRet:
     if not cmd.uid:
         self.selected.hide_expunged = True
     messages, updates = await self.session.search_mailbox(
         self.selected, cmd.keys)
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     msg_ids: List[int] = []
     for msg_seq, msg in messages:
         if msg.expunged:
             resp.code = ResponseCode.of(b'EXPUNGEISSUED')
         if cmd.uid:
             msg_ids.append(msg.uid)
         else:
             msg_ids.append(msg_seq)
     resp.add_untagged(SearchResponse(msg_ids))
     return resp, updates
コード例 #7
0
ファイル: state.py プロジェクト: icgood/pymap
 async def do_store(self, cmd: StoreCommand):
     if not cmd.uid:
         self.selected.hide_expunged = True
     if cmd.silent:
         self.selected.silence(cmd.sequence_set, cmd.flag_set, cmd.mode)
     messages, updates = await self.session.update_flags(
         self.selected, cmd.sequence_set, cmd.flag_set, cmd.mode)
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     session_flags = self.selected.session_flags
     for msg_seq, msg in messages:
         if msg.expunged:
             resp.code = ResponseCode.of(b'EXPUNGEISSUED')
         elif cmd.silent:
             continue
         flags = msg.get_flags(session_flags)
         fetch_data: List[Tuple[FetchAttribute, MaybeBytes]] = [
             (_flags_attr, ListP(flags, sort=True))]
         if cmd.uid:
             fetch_data.append((_uid_attr, Number(msg.uid)))
         resp.add_untagged(FetchResponse(msg_seq, fetch_data))
     return resp, updates
コード例 #8
0
ファイル: state.py プロジェクト: icgood/pymap
 async def do_status(self, cmd: StatusCommand):
     mailbox, updates = await self.session.get_mailbox(
         cmd.mailbox, selected=self._selected)
     data: Dict[StatusAttribute, Number] = OrderedDict()
     for attr in cmd.status_list:
         if attr == b'MESSAGES':
             data[attr] = Number(mailbox.exists)
         elif attr == b'RECENT':
             if updates and updates.guid == mailbox.guid:
                 data[attr] = Number(updates.session_flags.recent)
             else:
                 data[attr] = Number(mailbox.recent)
         elif attr == b'UNSEEN':
             data[attr] = Number(mailbox.unseen)
         elif attr == b'UIDNEXT':
             data[attr] = Number(mailbox.next_uid)
         elif attr == b'UIDVALIDITY':
             data[attr] = Number(mailbox.uid_validity)
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     resp.add_untagged(StatusResponse(cmd.mailbox, data))
     return resp, updates
コード例 #9
0
ファイル: state.py プロジェクト: icgood/pymap
 async def do_select(self, cmd: SelectCommand):
     self._selected = None
     mailbox, updates = await self.session.select_mailbox(
         cmd.mailbox, cmd.readonly)
     if updates.readonly:
         num_recent = mailbox.recent
         resp = ResponseOk(cmd.tag, b'Selected mailbox.',
                           ResponseCode.of(b'READ-ONLY'))
         resp.add_untagged_ok(b'Read-only mailbox.', PermanentFlags([]))
     else:
         num_recent = updates.session_flags.recent
         resp = ResponseOk(cmd.tag, b'Selected mailbox.',
                           ResponseCode.of(b'READ-WRITE'))
         resp.add_untagged_ok(b'Flags permitted.',
                              PermanentFlags(mailbox.permanent_flags))
     messages = updates.messages
     resp.add_untagged(FlagsResponse(mailbox.flags))
     resp.add_untagged(ExistsResponse(messages.exists))
     resp.add_untagged(RecentResponse(num_recent))
     resp.add_untagged_ok(b'Predicted next UID.',
                          UidNext(mailbox.next_uid))
     resp.add_untagged_ok(b'UIDs valid.',
                          UidValidity(mailbox.uid_validity))
     if mailbox.first_unseen:
         resp.add_untagged_ok(b'First unseen message.',
                              Unseen(mailbox.first_unseen))
     return resp, updates
コード例 #10
0
ファイル: state.py プロジェクト: dadbob/pymap
 async def do_select(self, cmd: SelectCommand) -> _CommandRet:
     self._selected = None
     mailbox, updates = await self.session.select_mailbox(
         cmd.mailbox, cmd.readonly)
     if updates.readonly:
         num_recent = mailbox.recent
         resp = ResponseOk(cmd.tag, b'Selected mailbox.',
                           ResponseCode.of(b'READ-ONLY'))
         resp.add_untagged_ok(b'Read-only mailbox.', PermanentFlags([]))
     else:
         num_recent = updates.session_flags.recent
         resp = ResponseOk(cmd.tag, b'Selected mailbox.',
                           ResponseCode.of(b'READ-WRITE'))
         resp.add_untagged_ok(b'Flags permitted.',
                              PermanentFlags(mailbox.permanent_flags))
     messages = updates.messages
     resp.add_untagged(FlagsResponse(mailbox.flags))
     resp.add_untagged(ExistsResponse(messages.exists))
     resp.add_untagged(RecentResponse(num_recent))
     resp.add_untagged_ok(b'Predicted next UID.',
                          UidNext(mailbox.next_uid))
     resp.add_untagged_ok(b'UIDs valid.',
                          UidValidity(mailbox.uid_validity))
     if mailbox.first_unseen:
         resp.add_untagged_ok(b'First unseen message.',
                              Unseen(mailbox.first_unseen))
     resp.add_untagged_ok(b'Object ID.', MailboxId(mailbox.mailbox_id))
     return resp, updates
コード例 #11
0
ファイル: state.py プロジェクト: dadbob/pymap
 async def do_store(self, cmd: StoreCommand) -> _CommandRet:
     if not cmd.uid:
         self.selected.hide_expunged = True
     if cmd.silent:
         self.selected.silence(cmd.sequence_set, cmd.flag_set, cmd.mode)
     messages, updates = await self.session.update_flags(
         self.selected, cmd.sequence_set, cmd.flag_set, cmd.mode)
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     session_flags = self.selected.session_flags
     for msg_seq, msg in messages:
         if msg.expunged:
             resp.code = ResponseCode.of(b'EXPUNGEISSUED')
         elif cmd.silent:
             continue
         flags = msg.get_flags(session_flags)
         fetch_data: List[FetchValue] = [
             FetchValue.of(_flags_attr, ListP(flags, sort=True))]
         if cmd.uid:
             fetch_data.append(
                 FetchValue.of(_uid_attr, Number(msg.uid)))
         resp.add_untagged(FetchResponse(msg_seq, fetch_data))
     return resp, updates
コード例 #12
0
ファイル: state.py プロジェクト: dadbob/pymap
 async def do_status(self, cmd: StatusCommand) -> _CommandRet:
     mailbox, updates = await self.session.get_mailbox(
         cmd.mailbox, selected=self._selected)
     data: Dict[StatusAttribute, MaybeBytes] = OrderedDict()
     for attr in cmd.status_list:
         if attr == b'MESSAGES':
             data[attr] = Number(mailbox.exists)
         elif attr == b'RECENT':
             if updates and updates.mailbox_id == mailbox.mailbox_id:
                 data[attr] = Number(updates.session_flags.recent)
             else:
                 data[attr] = Number(mailbox.recent)
         elif attr == b'UNSEEN':
             data[attr] = Number(mailbox.unseen)
         elif attr == b'UIDNEXT':
             data[attr] = Number(mailbox.next_uid)
         elif attr == b'UIDVALIDITY':
             data[attr] = Number(mailbox.uid_validity)
         elif attr == b'MAILBOXID':
             data[attr] = mailbox.mailbox_id.parens
     resp = ResponseOk(cmd.tag, cmd.command + b' completed.')
     resp.add_untagged(StatusResponse(cmd.mailbox, data))
     return resp, updates
コード例 #13
0
ファイル: state.py プロジェクト: icgood/pymap
 async def do_capability(self, cmd: CapabilityCommand):
     response = ResponseOk(cmd.tag, b'Capabilities listed.')
     response.add_untagged(Response(b'*', self.capability.string))
     return response, None
コード例 #14
0
ファイル: state.py プロジェクト: dadbob/pymap
 async def do_capability(self, cmd: CapabilityCommand) -> _CommandRet:
     response = ResponseOk(cmd.tag, b'Capabilities listed.')
     response.add_untagged(UntaggedResponse(self.capability.string))
     return response, None
コード例 #15
0
class TestSelectedMailbox(unittest.TestCase):
    def setUp(self) -> None:
        self.response = ResponseOk(b'.', b'testing')

    @classmethod
    def new_selected(cls, guid: bytes = b'test') -> SelectedMailbox:
        return SelectedMailbox(ObjectId(guid), False,
                               PermanentFlags([Seen, Flagged]),
                               SessionFlags([_Keyword]))

    @classmethod
    def set_messages(cls, selected: SelectedMailbox, expunged,
                     messages) -> None:
        updates = [
            _Message(uid, datetime.now(), flags) for uid, flags in messages
        ]
        selected.add_updates(updates, expunged)

    @property
    def command(self) -> SearchCommand:
        return SearchCommand(b'.', [], None)

    @property
    def uid_command(self) -> SearchCommand:
        return UidSearchCommand(b'.', [], None)

    def test_add_untagged_recent_equal(self) -> None:
        selected = self.new_selected()
        selected.session_flags.add_recent(1)
        selected.session_flags.add_recent(2)
        self.set_messages(selected, [], [(1, []), (2, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [], [(1, []), (2, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_recent_increase(self) -> None:
        selected = self.new_selected()
        selected.session_flags.add_recent(1)
        selected.session_flags.add_recent(2)
        self.set_messages(selected, [], [(1, []), (2, []), (3, [])])
        forked, _ = selected.fork(self.command)
        forked.session_flags.add_recent(3)
        self.set_messages(forked, [], [])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* 3 RECENT\r\n'
            b'* 3 FETCH (FLAGS (\\Recent))\r\n'
            b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_session_flag_add(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [], [(1, []), (2, [Seen]), (3, [])])
        forked, _ = selected.fork(self.command)
        forked.session_flags.update(2, [_Keyword], FlagOp.ADD)
        self.set_messages(forked, [], [])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* 2 FETCH (FLAGS (\\Seen $Keyword))\r\n'
            b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_recent_expunge(self) -> None:
        selected = self.new_selected()
        selected.session_flags.add_recent(1)
        selected.session_flags.add_recent(2)
        self.set_messages(selected, [], [(1, []), (2, []), (3, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [2, 3], [(1, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* 3 EXPUNGE\r\n'
            b'* 2 EXPUNGE\r\n'
            b'* 1 RECENT\r\n'
            b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_equal(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [], [(1, []), (2, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [], [(1, []), (2, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_fetch(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [], [(1, []), (2, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [], [(2, [Seen]), (3, [Seen, Flagged]),
                                       (4, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* 4 EXISTS\r\n'
            b'* 2 FETCH (FLAGS (\\Seen))\r\n'
            b'* 3 FETCH (FLAGS (\\Flagged \\Seen))\r\n'
            b'* 4 FETCH (FLAGS ())\r\n'
            b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_fetch_uid(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [], [(1, []), (2, [])])
        forked, _ = selected.fork(self.uid_command)
        self.set_messages(forked, [], [(2, [Seen]), (3, [Seen, Flagged]),
                                       (4, [])])
        _, untagged = forked.fork(self.uid_command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* 4 EXISTS\r\n'
            b'* 2 FETCH (FLAGS (\\Seen) UID 2)\r\n'
            b'* 3 FETCH (FLAGS (\\Flagged \\Seen) UID 3)\r\n'
            b'* 4 FETCH (FLAGS () UID 4)\r\n'
            b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_fetch_silenced(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [], [(1, []), (2, []),
                                         (3, [Seen, Flagged]),
                                         (4, [Seen, Flagged]), (5, [Flagged]),
                                         (6, [Seen])])
        forked, _ = selected.fork(self.uid_command)
        forked.silence(SequenceSet.build([1, 2]), frozenset([Seen]),
                       FlagOp.ADD)
        forked.silence(SequenceSet.build([3, 4]), frozenset([Seen]),
                       FlagOp.DELETE)
        forked.silence(SequenceSet.build([5, 6]), frozenset([Seen]),
                       FlagOp.REPLACE)
        self.set_messages(forked, [], [(1, [Seen, Flagged]), (2, [Seen]),
                                       (3, []), (4, [Flagged]),
                                       (5, [Seen, Flagged]), (6, [Seen])])
        _, untagged = forked.fork(self.uid_command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* 1 FETCH (FLAGS (\\Flagged \\Seen) UID 1)\r\n'
            b'* 3 FETCH (FLAGS () UID 3)\r\n'
            b'* 5 FETCH (FLAGS (\\Flagged \\Seen) UID 5)\r\n'
            b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_expunge_hidden(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [], [(1, []), (2, []), (3, []), (4, [])])
        forked, _ = selected.fork(self.command)
        forked.hide_expunged = True
        self.set_messages(forked, [2, 3], [(5, [Flagged])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* 5 EXISTS\r\n'
            b'* 5 FETCH (FLAGS (\\Flagged))\r\n'
            b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_expunge(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [], [(1, []), (2, []), (3, []), (4, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [2, 3], [(5, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* 3 EXPUNGE\r\n'
            b'* 2 EXPUNGE\r\n'
            b'* 3 EXISTS\r\n'
            b'* 3 FETCH (FLAGS ())\r\n'
            b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_all(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [], [(1, [Flagged]), (2, []), (3, [])])
        forked, _ = selected.fork(self.uid_command)
        selected.session_flags.add_recent(6)
        self.set_messages(forked, [2, 3], [(1, [Seen, Flagged]), (4, [Seen]),
                                           (5, [Seen]), (6, [Flagged]),
                                           (7, [Seen])])
        _, untagged = forked.fork(self.uid_command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* 3 EXPUNGE\r\n'
            b'* 2 EXPUNGE\r\n'
            b'* 5 EXISTS\r\n'
            b'* 1 RECENT\r\n'
            b'* 1 FETCH (FLAGS (\\Flagged \\Seen) UID 1)\r\n'
            b'* 2 FETCH (FLAGS (\\Seen) UID 4)\r\n'
            b'* 3 FETCH (FLAGS (\\Seen) UID 5)\r\n'
            b'* 4 FETCH (FLAGS (\\Flagged \\Recent) UID 6)\r\n'
            b'* 5 FETCH (FLAGS (\\Seen) UID 7)\r\n'
            b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_deleted_bye(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [], [(1, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [1], [(2, [])])
        forked.set_deleted()
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(
            b'* BYE Selected mailbox no longer exists.\r\n'
            b'. OK testing\r\n', bytes(self.response))
コード例 #16
0
ファイル: test_selected.py プロジェクト: icgood/pymap
class TestSelectedMailbox(unittest.TestCase):

    def setUp(self) -> None:
        self.response = ResponseOk(b'.', b'testing')

    @classmethod
    def new_selected(cls, guid: bytes = b'test') -> SelectedMailbox:
        return SelectedMailbox(guid, False,
                               PermanentFlags([Seen, Flagged]),
                               SessionFlags([_Keyword]))

    @classmethod
    def set_messages(cls, selected: SelectedMailbox,
                     expunged, messages) -> None:
        updates = [BaseMessage(uid, flags, datetime.now())
                   for uid, flags in messages]
        selected.add_updates(updates, expunged)

    @property
    def command(self) -> SearchCommand:
        return SearchCommand(b'.', [], None)

    @property
    def uid_command(self) -> SearchCommand:
        return UidSearchCommand(b'.', [], None)

    def test_add_untagged_recent_equal(self) -> None:
        selected = self.new_selected()
        selected.session_flags.add_recent(1)
        selected.session_flags.add_recent(2)
        self.set_messages(selected, [],
                          [(1, []), (2, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [],
                          [(1, []), (2, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_recent_increase(self) -> None:
        selected = self.new_selected()
        selected.session_flags.add_recent(1)
        selected.session_flags.add_recent(2)
        self.set_messages(selected, [],
                          [(1, []), (2, []), (3, [])])
        forked, _ = selected.fork(self.command)
        forked.session_flags.add_recent(3)
        self.set_messages(forked, [], [])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* 3 RECENT\r\n'
                         b'* 3 FETCH (FLAGS (\\Recent))\r\n'
                         b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_session_flag_add(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [],
                          [(1, []), (2, [Seen]), (3, [])])
        forked, _ = selected.fork(self.command)
        forked.session_flags.update(2, [_Keyword], FlagOp.ADD)
        self.set_messages(forked, [], [])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* 2 FETCH (FLAGS (\\Seen $Keyword))\r\n'
                         b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_recent_expunge(self) -> None:
        selected = self.new_selected()
        selected.session_flags.add_recent(1)
        selected.session_flags.add_recent(2)
        self.set_messages(selected, [],
                          [(1, []), (2, []), (3, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [2, 3],
                          [(1, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* 3 EXPUNGE\r\n'
                         b'* 2 EXPUNGE\r\n'
                         b'* 1 RECENT\r\n'
                         b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_equal(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [],
                          [(1, []), (2, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [],
                          [(1, []), (2, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_fetch(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [],
                          [(1, []), (2, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [],
                          [(2, [Seen]), (3, [Seen, Flagged]), (4, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* 4 EXISTS\r\n'
                         b'* 2 FETCH (FLAGS (\\Seen))\r\n'
                         b'* 3 FETCH (FLAGS (\\Flagged \\Seen))\r\n'
                         b'* 4 FETCH (FLAGS ())\r\n'
                         b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_fetch_uid(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [],
                          [(1, []), (2, [])])
        forked, _ = selected.fork(self.uid_command)
        self.set_messages(forked, [],
                          [(2, [Seen]), (3, [Seen, Flagged]), (4, [])])
        _, untagged = forked.fork(self.uid_command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* 4 EXISTS\r\n'
                         b'* 2 FETCH (FLAGS (\\Seen) UID 2)\r\n'
                         b'* 3 FETCH (FLAGS (\\Flagged \\Seen) UID 3)\r\n'
                         b'* 4 FETCH (FLAGS () UID 4)\r\n'
                         b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_fetch_silenced(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [],
                          [(1, []), (2, []), (3, [Seen, Flagged]),
                           (4, [Seen, Flagged]), (5, [Flagged]), (6, [Seen])])
        forked, _ = selected.fork(self.uid_command)
        forked.silence(SequenceSet.build([1, 2]),
                       frozenset([Seen]), FlagOp.ADD)
        forked.silence(SequenceSet.build([3, 4]),
                       frozenset([Seen]), FlagOp.DELETE)
        forked.silence(SequenceSet.build([5, 6]),
                       frozenset([Seen]), FlagOp.REPLACE)
        self.set_messages(forked, [],
                          [(1, [Seen, Flagged]), (2, [Seen]), (3, []),
                           (4, [Flagged]), (5, [Seen, Flagged]), (6, [Seen])])
        _, untagged = forked.fork(self.uid_command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* 1 FETCH (FLAGS (\\Flagged \\Seen) UID 1)\r\n'
                         b'* 3 FETCH (FLAGS () UID 3)\r\n'
                         b'* 5 FETCH (FLAGS (\\Flagged \\Seen) UID 5)\r\n'
                         b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_expunge_hidden(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [],
                          [(1, []), (2, []), (3, []), (4, [])])
        forked, _ = selected.fork(self.command)
        forked.hide_expunged = True
        self.set_messages(forked, [2, 3],
                          [(5, [Flagged])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* 5 EXISTS\r\n'
                         b'* 5 FETCH (FLAGS (\\Flagged))\r\n'
                         b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_expunge(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [],
                          [(1, []), (2, []), (3, []), (4, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [2, 3],
                          [(5, [])])
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* 3 EXPUNGE\r\n'
                         b'* 2 EXPUNGE\r\n'
                         b'* 3 EXISTS\r\n'
                         b'* 3 FETCH (FLAGS ())\r\n'
                         b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_all(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [],
                          [(1, [Flagged]), (2, []), (3, [])])
        forked, _ = selected.fork(self.uid_command)
        selected.session_flags.add_recent(6)
        self.set_messages(forked, [2, 3],
                          [(1, [Seen, Flagged]), (4, [Seen]), (5, [Seen]),
                           (6, [Flagged]), (7, [Seen])])
        _, untagged = forked.fork(self.uid_command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* 3 EXPUNGE\r\n'
                         b'* 2 EXPUNGE\r\n'
                         b'* 5 EXISTS\r\n'
                         b'* 1 RECENT\r\n'
                         b'* 1 FETCH (FLAGS (\\Flagged \\Seen) UID 1)\r\n'
                         b'* 2 FETCH (FLAGS (\\Seen) UID 4)\r\n'
                         b'* 3 FETCH (FLAGS (\\Seen) UID 5)\r\n'
                         b'* 4 FETCH (FLAGS (\\Flagged \\Recent) UID 6)\r\n'
                         b'* 5 FETCH (FLAGS (\\Seen) UID 7)\r\n'
                         b'. OK testing\r\n', bytes(self.response))

    def test_add_untagged_deleted_bye(self) -> None:
        selected = self.new_selected()
        self.set_messages(selected, [],
                          [(1, [])])
        forked, _ = selected.fork(self.command)
        self.set_messages(forked, [1],
                          [(2, [])])
        forked.set_deleted()
        _, untagged = forked.fork(self.command)
        self.response.add_untagged(*untagged)
        self.assertEqual(b'* BYE Selected mailbox no longer exists.\r\n'
                         b'. OK testing\r\n', bytes(self.response))