def test_no_detectable_bounce_addresses(self): # A bounce message was received, but no addresses could be detected. # A message will be logged in the bounce log though, and the message # can be forwarded to someone who can do something about it. self._mlist.forward_unrecognized_bounces_to = ( UnrecognizedBounceDisposition.site_owner) bogus = message_from_string("""\ From: [email protected] To: [email protected] Message-Id: <third> """) self._bounceq.enqueue(bogus, self._msgdata) mark = LogFileMark('mailman.bounce') self._runner.run() self.assertEqual(len(get_queue_messages('bounces')), 0) events = list(self._processor.events) self.assertEqual(len(events), 0) line = mark.readline() self.assertEqual( line[-51:-1], 'Bounce message w/no discernable addresses: <third>') # Here's the forwarded message to the site owners. forwards = get_queue_messages('virgin') self.assertEqual(len(forwards), 1) self.assertEqual(forwards[0].msg['to'], '*****@*****.**')
def test_no_detectable_bounce_addresses(self): # A bounce message was received, but no addresses could be detected. # A message will be logged in the bounce log though, and the message # can be forwarded to someone who can do something about it. self._mlist.forward_unrecognized_bounces_to = ( UnrecognizedBounceDisposition.site_owner) bogus = message_from_string("""\ From: [email protected] To: [email protected] Message-Id: <third> """) self._bounceq.enqueue(bogus, self._msgdata) mark = LogFileMark('mailman.bounce') self._runner.run() get_queue_messages('bounces', expected_count=0) events = list(self._processor.events) self.assertEqual(len(events), 0) line = mark.readline() self.assertEqual( line[-51:-1], 'Bounce message w/no discernable addresses: <third>') # Here's the forwarded message to the site owners. items = get_queue_messages('virgin', expected_count=1) self.assertEqual(items[0].msg['to'], '*****@*****.**')
def test_connect_with_socket_failure(self, class_mock): self._nntpq.enqueue(self._msg, {}, listname='*****@*****.**') mark = LogFileMark('mailman.error') self._runner.run() log_message = mark.readline()[:-1] self.assertTrue(log_message.endswith( 'NNTP socket error for [email protected]'))
def test_bad_regexp(self): # Take a mark on the error log file. mark = LogFileMark('mailman.error') # A bad regexp in header_checks should just get ignored, but # with an error message logged. header_matches = IHeaderMatchList(self._mlist) header_matches.append('Foo', '+a bad regexp', 'reject') msg = mfs("""\ From: [email protected] To: [email protected] Subject: A message Message-ID: <ant> Foo: foo MIME-Version: 1.0 A message body. """) msgdata = {} # This event subscriber records the event that occurs when the message # is processed by the header-match chain. events = [] with event_subscribers(events.append): process(self._mlist, msg, msgdata, start_chain='header-match') self.assertEqual(len(events), 0) # Check the error log. self.assertEqual(mark.readline()[-89:-1], "Invalid regexp '+a bad regexp' in header_matches " 'for test.example.com: nothing to repeat')
def test_connect_with_socket_failure(self, class_mock): self._nntpq.enqueue(self._msg, {}, listid='test.example.com') mark = LogFileMark('mailman.error') self._runner.run() log_message = mark.readline()[:-1] self.assertTrue( log_message.endswith('NNTP socket error for [email protected]'))
def test_bad_configuration_line(self): # Take a mark on the error log file. mark = LogFileMark('mailman.error') # A bad value in [antispam]header_checks should just get ignored, but # with an error message logged. chain = config.chains['header-match'] # The links are created dynamically; the rule names will all start # with the same prefix, but have a variable suffix. The actions will # all be to jump to the named chain. Do these checks now, while we # collect other useful information. post_checks = [] saw_any_rule = False for link in chain.get_links(self._mlist, Message(), {}): if link.rule.name == 'any': saw_any_rule = True self.assertEqual(link.action, LinkAction.jump) elif saw_any_rule: raise AssertionError("'any' rule was not last") else: self.assertEqual(link.rule.name[:13], 'header-match-') self.assertEqual(link.action, LinkAction.defer) post_checks.append((link.rule.header, link.rule.pattern)) self.assertListEqual(post_checks, [ ('Foo', 'foo'), ('Bar', 'bar'), ]) # Check the error log. self.assertEqual(mark.readline()[-77:-1], 'Configuration error: [antispam]header_checks ' 'contains bogus line: A-bad-line')
def test_archive_lock_used(self): # Test that locking the maildir when adding works as a failure here # could mean we lose mail. lock_file = os.path.join( config.LOCK_DIR, '{0}-maildir.lock'.format( self._mlist.fqdn_listname)) with Lock(lock_file): # Acquire the archiver lock, then make sure the archiver logs the # fact that it could not acquire the lock. archive_thread = threading.Thread( target=Prototype.archive_message, args=(self._mlist, self._msg)) mark = LogFileMark('mailman.error') archive_thread.run() # Test that the archiver output the correct error. line = mark.readline() # XXX 2012-03-15 BAW: we really should remove timestamp prefixes # from the loggers when under test. self.assertTrue(line.endswith( 'Unable to acquire prototype archiver lock for {0}, ' 'discarding: {1}\n'.format( self._mlist.fqdn_listname, self._msg.get('message-id')))) # Check that the message didn't get archived. created_files = self._find(config.ARCHIVE_DIR) self.assertEqual(self._expected_dir_structure, created_files)
def test_bad_configuration_line(self): # Take a mark on the error log file. mark = LogFileMark('mailman.error') # A bad value in [antispam]header_checks should just get ignored, but # with an error message logged. chain = config.chains['header-match'] # The links are created dynamically; the rule names will all start # with the same prefix, but have a variable suffix. The actions will # all be to jump to the named chain. Do these checks now, while we # collect other useful information. post_checks = [] saw_any_rule = False for link in chain.get_links(self._mlist, Message(), {}): if link.rule.name == 'any': saw_any_rule = True self.assertEqual(link.action, LinkAction.jump) elif saw_any_rule: raise AssertionError("'any' rule was not last") else: self.assertEqual(link.rule.name[:13], 'header-match-') self.assertEqual(link.action, LinkAction.defer) post_checks.append((link.rule.header, link.rule.pattern)) self.assertListEqual(post_checks, [ ('Foo', 'foo'), ('Bar', 'bar'), ]) # Check the error log. self.assertEqual(mark.readline()[-77:-1], 'Configuration error: [antispam]header_checks ' 'contains bogus line: A-bad-line')
def test_archive_lock_used(self): # Test that locking the maildir when adding works as a failure here # could mean we lose mail. lock_file = os.path.join( config.LOCK_DIR, '{0}-maildir.lock'.format( self._mlist.fqdn_listname)) with Lock(lock_file): # Acquire the archiver lock, then make sure the archiver logs the # fact that it could not acquire the lock. archive_thread = threading.Thread( target=Prototype.archive_message, args=(self._mlist, self._msg)) mark = LogFileMark('mailman.error') archive_thread.run() # Test that the archiver output the correct error. line = mark.readline() # XXX 2012-03-15 BAW: we really should remove timestamp prefixes # from the loggers when under test. self.assertTrue(line.endswith( 'Unable to acquire prototype archiver lock for {0}, ' 'discarding: {1}\n'.format( self._mlist.fqdn_listname, self._msg.get('message-id')))) # Check that the message didn't get archived. created_files = self._find(config.ARCHIVE_DIR) self.assertEqual(self._expected_dir_structure, created_files)
def test_discarding_pipeline(self): # If a handler in the pipeline raises DiscardMessage, the message will # be thrown away, but with a log message. mark = LogFileMark('mailman.vette') process(self._mlist, self._msg, {}, 'test-discarding') line = mark.readline()[:-1] self.assertTrue(line.endswith( '<ant> discarded by "test-discarding" pipeline handler ' '"discarding": by test handler'))
def test_discarding_pipeline(self): # If a handler in the pipeline raises DiscardMessage, the message will # be thrown away, but with a log message. mark = LogFileMark('mailman.vette') process(self._mlist, self._msg, {}, 'test-discarding') line = mark.readline()[:-1] self.assertTrue(line.endswith( '<ant> discarded by "test-discarding" pipeline handler ' '"discarding": by test handler'))
def test_header_matches_anything(self): # Check that a wild card header pattern is skipped. self._pckdict['header_filter_rules'] = [ ('.*', 7, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual(self._mlist.header_matches, []) self.assertIn('Unsupported header_filter_rules pattern', error_log.readline())
def test_header_matches_header_only(self): # Check that an empty pattern is skipped. self._pckdict['header_filter_rules'] = [ ('SomeHeaderName', 3, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual(self._mlist.header_matches, []) self.assertIn('Unsupported header_filter_rules pattern', error_log.readline())
def test_header_matches_anything(self): # Check that a wild card header pattern is skipped. self._pckdict['header_filter_rules'] = [ ('.*', 7, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual(self._mlist.header_matches, []) self.assertIn('Unsupported header_filter_rules pattern', error_log.readline())
def test_header_matches_invalid_re(self): # Check that an invalid regular expression pattern is skipped. self._pckdict['header_filter_rules'] = [ ('SomeHeaderName: *invalid-re', 3, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual(self._mlist.header_matches, []) self.assertIn('Skipping header_filter rule because of an invalid ' 'regular expression', error_log.readline())
def test_header_matches_invalid_re(self): # Check that an invalid regular expression pattern is skipped. self._pckdict['header_filter_rules'] = [ ('SomeHeaderName: *invalid-re', 3, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual(self._mlist.header_matches, []) self.assertIn('Skipping header_filter rule because of an invalid ' 'regular expression', error_log.readline())
def test_header_matches_header_only(self): # Check that an empty pattern is skipped. self._pckdict['header_filter_rules'] = [ ('SomeHeaderName', 3, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual(self._mlist.header_matches, []) self.assertIn('Unsupported header_filter_rules pattern', error_log.readline())
def test_maybe_forward_discard(self): # When forward_unrecognized_bounces_to is set to discard, no bounce # messages are forwarded. self._mlist.forward_unrecognized_bounces_to = ( UnrecognizedBounceDisposition.discard) # The only artifact of this call is a log file entry. mark = LogFileMark('mailman.bounce') maybe_forward(self._mlist, self._msg) get_queue_messages('virgin', expected_count=0) line = mark.readline() self.assertEqual(line[-40:-1], 'Discarding unrecognized bounce: <first>')
def test_header_matches_duplicate(self): # Check that duplicate patterns don't cause tracebacks. self._pckdict['header_filter_rules'] = [ ('SomeHeaderName: test-pattern', 3, False), ('SomeHeaderName: test-pattern', 2, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual([(hm.header, hm.pattern, hm.chain) for hm in self._mlist.header_matches], [('someheadername', 'test-pattern', 'discard')]) self.assertIn('Skipping duplicate header_filter rule', error_log.readline())
def test_maybe_forward_discard(self): # When forward_unrecognized_bounces_to is set to discard, no bounce # messages are forwarded. self._mlist.forward_unrecognized_bounces_to = ( UnrecognizedBounceDisposition.discard) # The only artifact of this call is a log file entry. mark = LogFileMark('mailman.bounce') maybe_forward(self._mlist, self._msg) get_queue_messages('virgin', expected_count=0) line = mark.readline() self.assertEqual( line[-40:-1], 'Discarding unrecognized bounce: <first>')
def test_bad_config_listname_chars(self): mark = LogFileMark('mailman.error') # This list create should succeed but log an error mlist = create_list('*****@*****.**') # Check the error log. self.assertRegex( mark.readline(), r'^.*Bad config\.mailman\.listname_chars setting: ' r'\[a-z0-9-\+\\]: ' '(unterminated character set|' 'unexpected end of regular expression)$') # Check that the list was actually created. self.assertIs(os.path.isdir(mlist.data_path), True)
def test_rejecting_pipeline(self): # If a handler in the pipeline raises DiscardMessage, the message will # be thrown away, but with a log message. mark = LogFileMark('mailman.vette') process(self._mlist, self._msg, {}, 'test-rejecting') line = mark.readline()[:-1] self.assertTrue(line.endswith( '<ant> rejected by "test-rejecting" pipeline handler ' '"rejecting": by test handler')) # In the rejection case, the original message will also be in the # virgin queue. items = get_queue_messages('virgin', expected_count=1) self.assertEqual(str(items[0].msg['subject']), 'a test')
def test_rejecting_pipeline(self): # If a handler in the pipeline raises DiscardMessage, the message will # be thrown away, but with a log message. mark = LogFileMark('mailman.vette') process(self._mlist, self._msg, {}, 'test-rejecting') line = mark.readline()[:-1] self.assertTrue( line.endswith( '<ant> rejected by "test-rejecting" pipeline handler ' '"rejecting": by test handler')) # In the rejection case, the original message will also be in the # virgin queue. items = get_queue_messages('virgin', expected_count=1) self.assertEqual(str(items[0].msg['subject']), 'a test')
def test_error_with_numeric_port(self): # Test the code path where a socket.error is raised in the delivery # function, and the MTA port is set to zero. The only real effect of # that is a log message. Start by opening the error log and reading # the current file position. mark = LogFileMark('mailman.error') self._outq.enqueue(self._msg, {}, listid='test.example.com') with configuration('mta', smtp_port=2112): self._runner.run() line = mark.readline() # The log line will contain a variable timestamp, the PID, and a # trailing newline. Ignore these. self.assertEqual( line[-53:-1], 'Cannot connect to SMTP server localhost on port 2112')
def test_get_moderator_approval_log_on_hold(self): # When the subscription is held for moderator approval, a message is # logged. mark = LogFileMark('mailman.subscribe') self._mlist.subscription_policy = SubscriptionPolicy.moderate anne = self._user_manager.create_address(self._anne) workflow = SubscriptionWorkflow(self._mlist, anne, pre_verified=True, pre_confirmed=True) # Consume the entire state machine. list(workflow) self.assertIn( '[email protected]: held subscription request from [email protected]', mark.readline())
def test_get_moderator_approval_log_on_hold(self): # When the subscription is held for moderator approval, a message is # logged. mark = LogFileMark('mailman.subscribe') self._mlist.subscription_policy = SubscriptionPolicy.moderate anne = self._user_manager.create_address(self._anne) workflow = SubscriptionWorkflow(self._mlist, anne, pre_verified=True, pre_confirmed=True) # Consume the entire state machine. list(workflow) self.assertIn( '[email protected]: held subscription request from [email protected]', mark.readline() )
def test_header_matches_duplicate(self): # Check that duplicate patterns don't cause tracebacks. self._pckdict['header_filter_rules'] = [ ('SomeHeaderName: test-pattern', 3, False), ('SomeHeaderName: test-pattern', 2, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual( [(hm.header, hm.pattern, hm.chain) for hm in self._mlist.header_matches], [('someheadername', 'test-pattern', 'discard')] ) self.assertIn('Skipping duplicate header_filter rule', error_log.readline())
def test_error_with_numeric_port(self): # Test the code path where a socket.error is raised in the delivery # function, and the MTA port is set to zero. The only real effect of # that is a log message. Start by opening the error log and reading # the current file position. mark = LogFileMark('mailman.error') self._outq.enqueue(self._msg, {}, listid='test.example.com') with configuration('mta', smtp_port=2112): self._runner.run() line = mark.readline() # The log line will contain a variable timestamp, the PID, and a # trailing newline. Ignore these. self.assertEqual( line[-53:-1], 'Cannot connect to SMTP server localhost on port 2112')
def test_connect_with_other_failure(self, class_mock): # In this failure mode, the message stays queued, so we can only run # the nntp runner once. def once(runner): # I.e. stop immediately, since the queue will not be empty. return True runner = make_testable_runner(nntp.NNTPRunner, 'nntp', predicate=once) self._nntpq.enqueue(self._msg, {}, listid='test.example.com') mark = LogFileMark('mailman.error') runner.run() log_message = mark.readline()[:-1] self.assertTrue(log_message.endswith( 'NNTP unexpected exception for [email protected]')) items = get_queue_messages('nntp', expected_count=1) self.assertEqual(items[0].msgdata['listid'], 'test.example.com') self.assertEqual(items[0].msg['subject'], 'A newsgroup posting')
def test_bad_action(self): # This should never happen, but what if it does? # FilterAction.accept, FilterAction.hold, and FilterAction.defer are # not valid. They are treated as discard actions, but the problem is # also logged. for action in (FilterAction.accept, FilterAction.hold, FilterAction.defer): self._mlist.filter_action = action mark = LogFileMark('mailman.error') with self.assertRaises(errors.DiscardMessage) as cm: mime_delete.dispose(self._mlist, self._msg, {}, 'bad action') self.assertEqual(cm.exception.message, 'bad action') line = mark.readline()[:-1] self.assertTrue( line.endswith('{0} invalid FilterAction: [email protected]. ' 'Treating as discard'.format(action.name)))
def test_connect_with_other_failure(self, class_mock): # In this failure mode, the message stays queued, so we can only run # the nntp runner once. def once(runner): # I.e. stop immediately, since the queue will not be empty. return True runner = make_testable_runner(nntp.NNTPRunner, 'nntp', predicate=once) self._nntpq.enqueue(self._msg, {}, listid='test.example.com') mark = LogFileMark('mailman.error') runner.run() log_message = mark.readline()[:-1] self.assertTrue(log_message.endswith( 'NNTP unexpected exception for [email protected]')) items = get_queue_messages('nntp', expected_count=1) self.assertEqual(items[0].msgdata['listid'], 'test.example.com') self.assertEqual(items[0].msg['subject'], 'A newsgroup posting')
def test_header_matches_unsupported_action(self): # Check that unsupported actions are skipped. for action_num in (1, 4, 5): self._pckdict['header_filter_rules'] = [ ('HeaderName: test-re', action_num, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual(self._mlist.header_matches, []) self.assertIn('Unsupported header_filter_rules action', error_log.readline()) # Avoid a useless warning. for member in self._mlist.members.members: member.unsubscribe() for member in self._mlist.owners.members: member.unsubscribe()
def test_header_matches_unsupported_action(self): # Check that unsupported actions are skipped. for action_num in (1, 4, 5): self._pckdict['header_filter_rules'] = [ ('HeaderName: test-re', action_num, False), ] error_log = LogFileMark('mailman.error') self._import() self.assertListEqual(self._mlist.header_matches, []) self.assertIn('Unsupported header_filter_rules action', error_log.readline()) # Avoid a useless warning. for member in self._mlist.members.members: member.unsubscribe() for member in self._mlist.owners.members: member.unsubscribe()
def test_get_moderator_approval_log_on_hold(self): # When the unsubscription is held for moderator approval, a message is # logged. mark = LogFileMark('mailman.subscribe') self._mlist.unsubscription_policy = SubscriptionPolicy.moderate workflow = UnSubscriptionWorkflow(self._mlist, self.anne, pre_confirmed=True) # Run the entire workflow. list(workflow) self.assertIn( '[email protected]: held unsubscription request from [email protected]', mark.readline()) # The state machine stopped at the moderator approval step so there # will be one token still in the database. self._expected_pendings_count = 1
def test_bad_action(self): # This should never happen, but what if it does? # FilterAction.accept, FilterAction.hold, and FilterAction.defer are # not valid. They are treated as discard actions, but the problem is # also logged. for action in (FilterAction.accept, FilterAction.hold, FilterAction.defer): self._mlist.filter_action = action mark = LogFileMark('mailman.error') with self.assertRaises(errors.DiscardMessage) as cm: mime_delete.dispose(self._mlist, self._msg, {}, 'bad action') self.assertEqual(cm.exception.message, 'bad action') line = mark.readline()[:-1] self.assertTrue(line.endswith( '{0} invalid FilterAction: [email protected]. ' 'Treating as discard'.format(action.name)))
def test_multiple_records(self): mlist = create_list('*****@*****.**') # Use action reject. The rule only hits on reject and discard. mlist.dmarc_mitigate_action = DMARCMitigateAction.reject msg = mfs("""\ From: [email protected] To: [email protected] """) mark = LogFileMark('mailman.error') rule = dmarc.DMARCMitigation() with get_dns_resolver(rmult=True): self.assertTrue(rule.check(mlist, msg, {})) line = mark.readline() self.assertEqual( line[-85:], 'RRset of TXT records for _dmarc.example.biz has 2 ' 'v=DMARC1 entries; testing them all\n')
def test_dmarc_nonameservers_exception(self): mlist = create_list('*****@*****.**') # Use action reject. The rule only hits on reject and discard. mlist.dmarc_mitigate_action = DMARCMitigateAction.reject msg = mfs("""\ From: [email protected] To: [email protected] """) mark = LogFileMark('mailman.error') rule = dmarc.DMARCMitigation() with get_dns_resolver(): self.assertTrue(rule.check(mlist, msg, {})) line = mark.readline() self.assertEqual( line[-84:], 'DNSException: No Nameservers available for ' '[email protected] (_dmarc.example.home).\n')
def test_rejecting_pipeline(self): # If a handler in the pipeline raises RejectMessage, the post will # be bounced with a log message. mark = LogFileMark('mailman.vette') process(self._mlist, self._msg, {}, 'test-rejecting') line = mark.readline()[:-1] self.assertEqual( line[-80:], '<ant> rejected by "test-rejecting" pipeline handler ' '"rejecting": by test handler', line) # In the rejection case, the original message will also be in the # virgin queue. items = get_queue_messages('virgin', expected_count=1) self.assertEqual( str(items[0].msg.get_payload(1).get_payload(0)['subject']), 'a test') # The first payload contains the rejection reason. payload = items[0].msg.get_payload(0).get_payload() self.assertEqual(payload, 'by test handler')
def test_dmarc_dns_exception(self): mlist = create_list('*****@*****.**') # Use action reject. The rule only hits on reject and discard. mlist.dmarc_mitigate_action = DMARCMitigateAction.reject msg = mfs("""\ From: [email protected] To: [email protected] """) mark = LogFileMark('mailman.error') rule = dmarc.DMARCMitigation() with get_dns_resolver(): self.assertTrue(rule.check(mlist, msg, {})) line = mark.readline() self.assertEqual( line[-144:], 'DNSException: Unable to query DMARC policy for ' '[email protected] (_dmarc.example.info). ' 'Abstract base class shared by all dnspython exceptions.\n')
def test_looping_cnames(self): mlist = create_list('*****@*****.**') # Use action reject. The rule only hits on reject and discard. mlist.dmarc_mitigate_action = DMARCMitigateAction.reject msg = mfs("""\ From: [email protected] To: [email protected] """) mark = LogFileMark('mailman.vette') rule = dmarc.DMARCMitigation() with get_dns_resolver(cloop=True): self.assertTrue(rule.check(mlist, msg, {})) line = mark.readline() self.assertEqual( line[-120:], 'ant: DMARC lookup for [email protected] (_dmarc.example.biz) ' 'found p=reject in _dmarc.example.org. = v=DMARC1; p=reject;\n')
def test_connection_got_two_quit_after_post_failure(self, class_mock): # The NNTP connection does get closed after a unsuccessful post. # Add a side-effect to the instance mock's .post() method. conn_mock = class_mock() conn_mock.post.side_effect = nntplib.NNTPTemporaryError self._nntpq.enqueue(self._msg, {}, listid='test.example.com') mark = LogFileMark('mailman.error') self._runner.run() # The connection object's post() method was called twice with a # file-like object containing the message's bytes. The # NNTPTemporaryError is retried with a munged Message-ID because it # might have been due to a duplicate Message-ID. Check that quit is # called twice. self.assertEqual(conn_mock.quit.call_count, 2) # There should be a log message with the original Message-ID. log_message = mark.readline()[:-1] self.assertTrue( log_message.endswith('<ant> NNTP error for [email protected]'), log_message)
def test_missing_html_to_plain_text_command(self): # Calling a missing html_to_plain_text_command is properly logged. msg = mfs("""\ From: [email protected] Content-Type: text/html MIME-Version: 1.0 <html><head></head> <body></body></html> """) process = config.handlers['mime-delete'].process mark = LogFileMark('mailman.error') with dummy_script('nonexist'): process(self._mlist, msg, {}) line = mark.readline()[:-1] self.assertTrue(line.endswith('HTML -> text/plain command error')) self.assertEqual(msg.get_content_type(), 'text/html') self.assertIsNone(msg['x-content-filtered-by']) payload_lines = msg.get_payload().splitlines() self.assertEqual(payload_lines[0], '<html><head></head>')
def test_rejecting_pipeline_without_message(self): # Similar to above, but without a rejection message. pipeline = config.pipelines['test-rejecting'] message = pipeline.message self.addCleanup(setattr, pipeline, 'message', message) pipeline.message = None mark = LogFileMark('mailman.vette') process(self._mlist, self._msg, {}, 'test-rejecting') line = mark.readline()[:-1] self.assertEqual( line[-91:], '<ant> rejected by "test-rejecting" pipeline handler ' '"rejecting": [No details are available]', line) # In the rejection case, the original message will also be in the # virgin queue. items = get_queue_messages('virgin', expected_count=1) self.assertEqual( str(items[0].msg.get_payload(1).get_payload(0)['subject']), 'a test') # The first payload contains the rejection reason. payload = items[0].msg.get_payload(0).get_payload() self.assertEqual(payload, '[No details are available]')
def test_no_progress_on_retries_with_expired_retry_period(self): # We've had temporary failures with no progress, and the retry period # has expired. In that case, a log entry is written and message is # discarded. There's nothing more that can be done. temporary_failures.append('*****@*****.**') temporary_failures.append('*****@*****.**') retry_period = as_timedelta(config.mta.delivery_retry_period) deliver_until = datetime(2005, 8, 1, 7, 49, 23) + retry_period msgdata = dict(last_recip_count=2, deliver_until=deliver_until) self._outq.enqueue(self._msg, msgdata, listid='test.example.com') # Before the runner runs, several days pass. factory.fast_forward(retry_period.days + 1) mark = LogFileMark('mailman.smtp') self._runner.run() # There should be no message in the retry or outgoing queues. get_queue_messages('retry', expected_count=0) get_queue_messages('out', expected_count=0) # There should be a log message in the smtp log indicating that the # message has been discarded. line = mark.readline() self.assertEqual( line[-63:-1], 'Discarding message with persistent temporary failures: <first>')
def test_no_progress_on_retries_with_expired_retry_period(self): # We've had temporary failures with no progress, and the retry period # has expired. In that case, a log entry is written and message is # discarded. There's nothing more that can be done. temporary_failures.append('*****@*****.**') temporary_failures.append('*****@*****.**') retry_period = as_timedelta(config.mta.delivery_retry_period) deliver_until = datetime(2005, 8, 1, 7, 49, 23) + retry_period msgdata = dict(last_recip_count=2, deliver_until=deliver_until) self._outq.enqueue(self._msg, msgdata, listid='test.example.com') # Before the runner runs, several days pass. factory.fast_forward(retry_period.days + 1) mark = LogFileMark('mailman.smtp') self._runner.run() # There should be no message in the retry or outgoing queues. get_queue_messages('retry', expected_count=0) get_queue_messages('out', expected_count=0) # There should be a log message in the smtp log indicating that the # message has been discarded. line = mark.readline() self.assertEqual( line[-63:-1], 'Discarding message with persistent temporary failures: <first>')
def test_dispose_discard_no_spurious_log(self): self._mlist.filter_action = FilterAction.discard mark = LogFileMark('mailman.error') with self.assertRaises(DiscardMessage): mime_delete.dispose(self._mlist, self._msg, {}, 'discarding') self.assertEqual(mark.readline(), '')