def test_mq_runner_can_load_plugins(self):
        queue_basedir = os.path.join(self.fs.root, 'mailqueue')
        create_maildir_directories(queue_basedir)
        inject_example_message(queue_basedir)

        mock_fn = mock.MagicMock(return_value=MQAction.DISCARD, spec={})
        signal_map = {MQSignal.delivery_failed: mock_fn}
        fake_plugin = create_fake_plugin(signal_map)
        inject_plugin_into_working_set('testplugin', fake_plugin)
        config_path = create_ini('host.example', port=12345, fs=self.fs)

        cmd = ['mq-run', config_path, queue_basedir]
        mailer = DebugMailer(simulate_failed_sending=True)
        with mock.patch('schwarz.mailqueue.queue_runner.init_smtp_mailer',
                        new=lambda s: mailer):
            rc = one_shot_queue_run_main(argv=cmd, return_rc_code=True)
        assert_equals(0, rc)

        assert_length(0, mailer.sent_mails)
        mock_fn.assert_called_once()
        assert_length(
            0,
            find_messages(queue_basedir, log=l_(None)),
            message=
            'plugin should have discarded the message after failed delivery')
示例#2
0
    def test_can_send_queued_message_to_multiple_recipients(self):
        mailer = DebugMailer()
        recipients = (b'*****@*****.**', b'*****@*****.**')
        inject_example_message(self.path_maildir, recipients=recipients)

        send_all_queued_messages(self.path_maildir, mailer)
        assert_is_empty(self.msg_files(folder='new'))
        sent_msg, = mailer.sent_mails
        _as_str = lambda values: tuple([v.decode('ascii') for v in values])
        assert_equals(_as_str(recipients), sent_msg.to_addrs)
示例#3
0
    def test_can_handle_failures_and_update_metadata(self):
        mailer = DebugMailer(simulate_failed_sending=True)
        queue_date = DateTime(2020, 2, 4, hour=14, minute=32, tzinfo=UTC)
        inject_example_message(self.path_maildir, queue_date=queue_date)

        send_all_queued_messages(self.path_maildir, mailer)
        assert_is_empty(mailer.sent_mails)

        msg_file, = self.msg_files(folder='new')
        msg = MaildirBackedMsg(msg_file)
        assert_equals(queue_date, msg.queue_date)
        assert_equals(1, msg.retries)
        assert_not_none(msg.last_delivery_attempt)
        assert_almost_now(msg.last_delivery_attempt)
示例#4
0
    def test_can_send_message(self, with_msg_id):
        mailer = DebugMailer()
        msg_header = b'X-Header: somevalue\n'
        if with_msg_id:
            msg_id = '*****@*****.**' % uuid.uuid4()
            msg_header += b'Message-ID: <%s>\n' % msg_id.encode('ascii')
        msg_body = b'MsgBody\n'
        msg_bytes = msg_header + b'\n' + msg_body
        msg = inject_example_message(
            self.path_maildir,
            sender=b'*****@*****.**',
            recipient=b'*****@*****.**',
            msg_bytes=msg_bytes,
        )
        assert_true(os.path.exists(msg.path))

        with LogCapture() as lc:
            mh = MessageHandler([mailer], info_logger(lc))
            was_sent = mh.send_message(msg)
        assert_trueish(was_sent)
        expected_log_msg = '%s => %s' % ('*****@*****.**',
                                         '*****@*****.**')
        if with_msg_id:
            expected_log_msg += ' <%s>' % msg_id
        assert_did_log_message(lc, expected_msg=expected_log_msg)

        assert_length(1, mailer.sent_mails)
        sent_msg, = mailer.sent_mails
        assert_equals('*****@*****.**', sent_msg.from_addr)
        assert_equals(('*****@*****.**', ), sent_msg.to_addrs)
        assert_equals(msg_nl(msg_bytes), sent_msg.msg_fp.read())
        assert_false(os.path.exists(msg.path))
        # ensure there are no left-overs/tmp files
        assert_length(0, self.list_all_files(self.path_maildir))
示例#5
0
    def test_can_move_stale_messages_back_to_new(self):
        mailer = DebugMailer()
        inject_example_message(self.path_maildir, target_folder='cur')

        send_all_queued_messages(self.path_maildir, mailer)
        assert_is_empty(mailer.sent_mails)
        assert_is_empty(self.msg_files(folder='new'))
        assert_length(1, self.msg_files(folder='cur'))

        dt_stale = DateTime.now() + TimeDelta(hours=1)
        # LogCapture: no logged warning about stale message on the command line
        with LogCapture():
            with freeze_time(dt_stale):
                send_all_queued_messages(self.path_maildir, mailer)
        assert_is_empty(self.msg_files(folder='new'))
        assert_is_empty(self.msg_files(folder='cur'))
        assert_length(1, mailer.sent_mails)
    def test_mq_runner_works_without_plugins(self):
        queue_basedir = os.path.join(self.fs.root, 'mailqueue')
        create_maildir_directories(queue_basedir)
        inject_example_message(queue_basedir)
        config_path = create_ini('host.example', port=12345, fs=self.fs)

        cmd = ['mq-run', config_path, queue_basedir]
        mailer = DebugMailer(simulate_failed_sending=True)
        with mock.patch('schwarz.mailqueue.queue_runner.init_smtp_mailer',
                        new=lambda s: mailer):
            rc = one_shot_queue_run_main(argv=cmd, return_rc_code=True)
        assert_equals(0, rc)

        assert_length(0, mailer.sent_mails)
        assert_length(
            1,
            find_messages(queue_basedir, log=l_(None)),
            message='message should have been queued for later delivery')
示例#7
0
    def test_can_handle_sending_failure(self):
        mailer = DebugMailer(simulate_failed_sending=True)
        msg = inject_example_message(self.path_maildir)
        assert_true(os.path.exists(msg.path))

        was_sent = MessageHandler([mailer]).send_message(msg)
        assert_falseish(was_sent)
        assert_true(os.path.exists(msg.path))
        # no left-overs (e.g. in "tmp" folder) other than the initial message file
        assert_length(1, self.list_all_files(self.path_maildir))
示例#8
0
    def test_can_handle_concurrent_sends(self):
        mailer = DebugMailer()
        msg = inject_example_message(self.path_maildir)
        locked_msg = lock_file(msg.path, timeout=0.1)

        send_all_queued_messages(self.path_maildir, mailer)
        assert_length(1, self.msg_files(folder='new'))
        assert_is_empty(mailer.sent_mails)

        locked_msg.close()
        send_all_queued_messages(self.path_maildir, mailer)
        assert_is_empty(self.msg_files(folder='new'))
        assert_length(1, mailer.sent_mails)
示例#9
0
    def test_can_handle_duplicate_file_in_cur_before_send(self):
        msg = inject_example_message(self.path_maildir)
        path_in_progress = msg.path.replace('new', 'cur')
        # this can happen on Unix/Posix because Python does not provide an
        # atomic "move without overwrite". Linux provides the "renameat2"
        # system call (with RENAME_NOREPLACE flag) but Python does not expose
        # that API.
        shutil.copy(msg.path, path_in_progress)
        mailer = DebugMailer()

        was_sent = MessageHandler([mailer]).send_message(msg)
        assert_none(was_sent)
        assert_length(0, mailer.sent_mails)
        assert_length(2, self.list_all_files(self.path_maildir))
示例#10
0
    def test_can_handle_vanished_file_after_successful_send(self):
        if IS_WINDOWS:
            self.skipTest('unable to unlink open file on Windows')
        msg = inject_example_message(self.path_maildir)
        path_in_progress = msg.path.replace('new', 'cur')

        def delete_on_send(*args):
            os.unlink(path_in_progress)
            return True

        mailer = DebugMailer(send_callback=delete_on_send)

        was_sent = MessageHandler([mailer]).send_message(msg)
        assert_true(was_sent)
        assert_length(1, mailer.sent_mails)
        assert_length(0, self.list_all_files(self.path_maildir))
示例#11
0
    def test_tries_to_lock_message_while_sending(self):
        mailer = DebugMailer()
        msg = inject_example_message(self.path_maildir)
        locked_msg = lock_file(msg.path, timeout=0.1)
        mh = MessageHandler([mailer])

        was_sent = mh.send_message(msg)
        assert_none(was_sent)
        assert_length(1, self.msg_files(folder='new'))
        assert_is_empty(mailer.sent_mails)

        locked_msg.close()
        was_sent = mh.send_message(msg)
        assert_trueish(was_sent)
        assert_is_empty(self.msg_files(folder='new'))
        assert_length(1, mailer.sent_mails)
示例#12
0
    def test_can_handle_duplicate_file_in_new_after_failed_send(self):
        msg = inject_example_message(self.path_maildir)
        path_in_progress = msg.path.replace('new', 'cur')

        # again: can happen because Python provides not atomic "move without
        # overwrite" on Linux (see also "renameat2" system call)
        def duplicate_on_failed_send(*args):
            shutil.copy(path_in_progress, msg.path)
            return False

        mailer = DebugMailer(send_callback=duplicate_on_failed_send)

        was_sent = MessageHandler([mailer]).send_message(msg)
        assert_false(was_sent)
        assert_length(0, mailer.sent_mails)
        assert_length(2, self.list_all_files(self.path_maildir))
示例#13
0
    def test_plugin_can_access_number_of_failed_deliveries(self):
        registry = SignalRegistry()

        def discard_after_two_attempts(sender, msg, send_result):
            return MQAction.DISCARD if (msg.retries > 1) else None

        connect_signals({MQSignal.delivery_failed: discard_after_two_attempts},
                        registry.namespace)

        msg = inject_example_message(self.path_maildir)
        mailer = DebugMailer(simulate_failed_sending=True)
        mh = MessageHandler([mailer], plugins=registry)

        mh.send_message(msg)
        assert_length(1, find_messages(self.path_maildir, log=l_(None)))

        send_result = mh.send_message(msg)
        assert_falseish(send_result)
        assert_length(0, mailer.sent_mails)
        assert_length(0, find_messages(self.path_maildir, log=l_(None)))
        assert_true(send_result.discarded)