def test_no_action_defaults_to_site_wide_action(self): # If the list-specific header check matches, but there is no defined # action, the site-wide antispam action is used. msg = mfs("""\ From: [email protected] To: [email protected] Subject: A message Message-ID: <ant> Foo: foo MIME-Version: 1.0 A message body. """) header_matches = IHeaderMatchList(self._mlist) header_matches.append('Foo', 'foo') # This event subscriber records the event that occurs when the message # is processed by the owner chain, which holds its for approval. events = [] def record_holds(event): # noqa if not isinstance(event, HoldEvent): return events.append(event) with event_subscribers(record_holds): # Set the site-wide antispam action to hold the message. with configuration('antispam', header_checks=""" Spam: [*]{3,} """, jump_chain='hold'): # noqa process(self._mlist, msg, {}, start_chain='header-match') self.assertEqual(len(events), 1) event = events[0] self.assertIsInstance(event, HoldEvent) self.assertEqual(event.chain, config.chains['hold']) self.assertEqual(event.mlist, self._mlist) self.assertEqual(event.msg, msg) events = [] def record_discards(event): # noqa if not isinstance(event, DiscardEvent): return events.append(event) with event_subscribers(record_discards): # Set the site-wide default to discard the message. msg.replace_header('Message-Id', '<bee>') with configuration('antispam', header_checks=""" Spam: [*]{3,} """, jump_chain='discard'): # noqa process(self._mlist, msg, {}, start_chain='header-match') self.assertEqual(len(events), 1) event = events[0] self.assertIsInstance(event, DiscardEvent) self.assertEqual(event.chain, config.chains['discard']) self.assertEqual(event.mlist, self._mlist) self.assertEqual(event.msg, msg)
def test_crash_event(self): runner = make_testable_runner(CrashingRunner, 'in') # When an exception occurs in Runner._process_one_file(), a zope.event # gets triggered containing the exception object. msg = mfs("""\ From: [email protected] To: [email protected] Message-ID: <ant> """) config.switchboards['in'].enqueue(msg, listid='test.example.com') with event_subscribers(self._got_event): runner.run() # We should now have exactly one event, which will contain the # exception, plus additional metadata containing the mailing list, # message, and metadata. self.assertEqual(len(self._events), 1) event = self._events[0] self.assertTrue(isinstance(event, RunnerCrashEvent)) self.assertEqual(event.mailing_list, self._mlist) self.assertEqual(event.message['message-id'], '<ant>') self.assertEqual(event.metadata['listid'], 'test.example.com') self.assertTrue(isinstance(event.error, RuntimeError)) self.assertEqual(str(event.error), 'borked') self.assertTrue(isinstance(event.runner, CrashingRunner)) # The message should also have ended up in the shunt queue. shunted = get_queue_messages('shunt') self.assertEqual(len(shunted), 1) self.assertEqual(shunted[0].msg['message-id'], '<ant>')
def test_priority_site_over_list(self): # Test that the site-wide checks take precedence over the list-specific # checks. msg = mfs("""\ From: [email protected] To: [email protected] Subject: A message Message-ID: <ant> Foo: foo MIME-Version: 1.0 A message body. """) msgdata = {} header_matches = IHeaderMatchList(self._mlist) header_matches.append('Foo', 'foo', 'accept') # This event subscriber records the event that occurs when the message # is processed by the owner chain. events = [] with event_subscribers(events.append): process(self._mlist, msg, msgdata, start_chain='header-match') self.assertEqual(len(events), 1) event = events[0] # Site-wide wants to hold the message, the list wants to accept it. self.assertIsInstance(event, HoldEvent) self.assertEqual(event.chain, config.chains['hold'])
def test_create_list_event(self): # Test that creating a list in the list manager propagates the # expected events. with event_subscribers(self._record_event): mlist = getUtility(IListManager).create('*****@*****.**') self.assertEqual(len(self._events), 2) self.assertTrue(isinstance(self._events[0], ListCreatingEvent)) self.assertEqual(self._events[0].fqdn_listname, '*****@*****.**') self.assertTrue(isinstance(self._events[1], ListCreatedEvent)) self.assertEqual(self._events[1].mailing_list, mlist)
def test_create_list_event(self): # Test that creating a list in the list manager propagates the # expected events. with event_subscribers(self._record_event): mlist = getUtility(IListManager).create('*****@*****.**') self.assertEqual(len(self._events), 2) self.assertIsInstance(self._events[0], ListCreatingEvent) self.assertEqual(self._events[0].fqdn_listname, '*****@*****.**') self.assertIsInstance(self._events[1], ListCreatedEvent) self.assertEqual(self._events[1].mailing_list, mlist)
def test_create_domain_event(self): # Test that creating a domain in the domain manager propagates the # expected events. with event_subscribers(self._record_event): domain = self._manager.add('example.org') self.assertEqual(len(self._events), 2) self.assertTrue(isinstance(self._events[0], DomainCreatingEvent)) self.assertEqual(self._events[0].mail_host, 'example.org') self.assertTrue(isinstance(self._events[1], DomainCreatedEvent)) self.assertEqual(self._events[1].domain, domain)
def test_create_domain_event(self): # Test that creating a domain in the domain manager propagates the # expected events. with event_subscribers(self._record_event): domain = getUtility(IDomainManager).add('example.org') self.assertEqual(len(self._events), 2) self.assertTrue(isinstance(self._events[0], DomainCreatingEvent)) self.assertEqual(self._events[0].mail_host, 'example.org') self.assertTrue(isinstance(self._events[1], DomainCreatedEvent)) self.assertEqual(self._events[1].domain, domain)
def test_rule_hits(self): config.chains['mine'] = MyChain() self.addCleanup(config.chains.pop, 'mine') hits = None def handler(event): # noqa nonlocal hits if isinstance(event, AcceptEvent): hits = event.msg['x-mailman-rule-hits'] with event_subscribers(handler): process_chain(self._mlist, self._msg, {}, start_chain='mine') self.assertEqual(hits, 'first; second; third')
def test_delete_list_event(self): # Test that deleting a list in the list manager propagates the # expected event. mlist = create_list('*****@*****.**') with event_subscribers(self._record_event): getUtility(IListManager).delete(mlist) self.assertEqual(len(self._events), 2) self.assertTrue(isinstance(self._events[0], ListDeletingEvent)) self.assertEqual(self._events[0].mailing_list, mlist) self.assertTrue(isinstance(self._events[1], ListDeletedEvent)) self.assertEqual(self._events[1].fqdn_listname, '*****@*****.**')
def test_rule_hits(self): config.chains['mine'] = MyChain() self.addCleanup(config.chains.pop, 'mine') hits = None def handler(event): # noqa: E306 nonlocal hits if isinstance(event, AcceptEvent): hits = event.msg['x-mailman-rule-hits'] with event_subscribers(handler): process_chain(self._mlist, self._msg, {}, start_chain='mine') self.assertEqual(hits, 'first; second; third')
def test_delete_list_event(self): # Test that deleting a list in the list manager propagates the # expected event. mlist = create_list('*****@*****.**') with event_subscribers(self._record_event): getUtility(IListManager).delete(mlist) self.assertEqual(len(self._events), 2) self.assertIsInstance(self._events[0], ListDeletingEvent) self.assertEqual(self._events[0].mailing_list, mlist) self.assertIsInstance(self._events[1], ListDeletedEvent) self.assertEqual(self._events[1].fqdn_listname, '*****@*****.**')
def test_delete_domain_event(self): # Test that deleting a domain in the domain manager propagates the # expected event. domain = self._manager.add('example.org') with event_subscribers(self._record_event): self._manager.remove('example.org') self.assertEqual(len(self._events), 2) self.assertIsInstance(self._events[0], DomainDeletingEvent) self.assertEqual(self._events[0].domain, domain) self.assertIsInstance(self._events[1], DomainDeletedEvent) self.assertEqual(self._events[1].mail_host, 'example.org')
def test_delete_domain_event(self): # Test that deleting a domain in the domain manager propagates the # expected event. domain = self._manager.add('example.org') with event_subscribers(self._record_event): self._manager.remove('example.org') self.assertEqual(len(self._events), 2) self.assertTrue(isinstance(self._events[0], DomainDeletingEvent)) self.assertEqual(self._events[0].domain, domain) self.assertTrue(isinstance(self._events[1], DomainDeletedEvent)) self.assertEqual(self._events[1].mail_host, 'example.org')
def test_token(self): # Registering the email address returns a token, and this token links # back to the pendable. captured_events = [] def capture_event(event): captured_events.append(event) with event_subscribers(capture_event): token = self.registrar.register(self.mlist, '*****@*****.**') self.assertEqual(len(captured_events), 1) event = captured_events[0] self.assertEqual(event.token, token) pending = getUtility(IPendings).confirm(token) self.assertEqual(pending, event.pendable)
def test_event_pendable(self): # The event has an IPendable which contains additional information. def capture_event(event): pendable = event.pendable self.assertEqual(pendable['type'], 'registration') self.assertEqual(pendable['email'], '*****@*****.**') # The key is present, but the value is None. self.assertIsNone(pendable['display_name']) # The default is regular delivery. self.assertEqual(pendable['delivery_mode'], 'regular') self.assertEqual(pendable['list_id'], 'alpha.example.com') with event_subscribers(capture_event): self.registrar.register(self.mlist, '*****@*****.**')
def test_push_and_pop_trigger_events(self): # Pushing a new configuration onto the stack triggers a # post-processing event. events = [] def on_event(event): if isinstance(event, ConfigurationUpdatedEvent): # Record both the event and the top overlay. events.append(event.config.overlays[0].name) # Do two pushes, and then pop one of them. with event_subscribers(on_event): with configuration('test', _configname='first'): with configuration('test', _configname='second'): pass self.assertEqual(events, ['first', 'second', 'first'])
def test_discard(self): msgdata = dict(dmarc_action='discard') # When a message is discarded, the only artifacts are a log message # and an event. Catch the event to prove it happened. events = [] def handler(event): # noqa: E306 if isinstance(event, DiscardEvent): events.append(event) with event_subscribers(handler): process_chain(self._mlist, self._msg, msgdata, start_chain='dmarc') self.assertEqual(len(events), 1) self.assertIs(events[0].msg, self._msg)
def test_push_and_pop_trigger_events(self): # Pushing a new configuration onto the stack triggers a # post-processing event. events = [] def on_event(event): # noqa: E306 if isinstance(event, ConfigurationUpdatedEvent): # Record both the event and the top overlay. events.append(event.config.overlays[0].name) # Do two pushes, and then pop one of them. with event_subscribers(on_event): with configuration('test', _configname='first'): with configuration('test', _configname='second'): pass self.assertEqual(events, ['first', 'second', 'first'])
def test_push_and_pop_trigger_events(self): # Pushing a new configuration onto the stack triggers a # post-processing event. events = [] def on_event(event): if isinstance(event, ConfigurationUpdatedEvent): # Record both the event and the top overlay. events.append(event.config.overlays[0].name) with event_subscribers(on_event): with configuration('test', _configname='my test'): pass # There should be two pushed configuration names on the list now, one # for the push leaving 'my test' on the top of the stack, and one for # the pop, leaving the ConfigLayer's 'test config' on top. self.assertEqual(events, ['my test', 'test config'])
def test_owner_pipeline(self): # Messages processed through the default owners chain end up in the # pipeline queue, and an event gets sent. # # This event subscriber records the event that occurs when the message # is processed by the owner chain. events = [] def catch_event(event): # noqa: E306 if isinstance(event, AcceptOwnerEvent): events.append(event) with event_subscribers(catch_event): process(self._mlist, self._msg, {}, 'default-owner-chain') self.assertEqual(len(events), 1) event = events[0] self.assertIsInstance(event, AcceptOwnerEvent) self.assertEqual(event.mlist, self._mlist) self.assertEqual(event.msg['message-id'], '<ant>') self.assertIsInstance(event.chain, BuiltInOwnerChain) items = get_queue_messages('pipeline', expected_count=1) message = items[0].msg self.assertEqual(message['message-id'], '<ant>')
def test_owner_pipeline(self): # Messages processed through the default owners chain end up in the # pipeline queue, and an event gets sent. # # This event subscriber records the event that occurs when the message # is processed by the owner chain. events = [] def catch_event(event): # noqa if isinstance(event, AcceptOwnerEvent): events.append(event) with event_subscribers(catch_event): process(self._mlist, self._msg, {}, 'default-owner-chain') self.assertEqual(len(events), 1) event = events[0] self.assertIsInstance(event, AcceptOwnerEvent) self.assertEqual(event.mlist, self._mlist) self.assertEqual(event.msg['message-id'], '<ant>') self.assertIsInstance(event.chain, BuiltInOwnerChain) items = get_queue_messages('pipeline', expected_count=1) message = items[0].msg self.assertEqual(message['message-id'], '<ant>')
def test_header_in_subpart(self): # Test that headers in sub-parts are also matched. msg = mfs("""\ From: [email protected] To: [email protected] Subject: A message Message-ID: <ant> Foo: foo MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="================12345==" --================12345== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit A message body. --================12345== Content-Type: application/junk MIME-Version: 1.0 Content-Transfer-Encoding: 7bit This is junk --================12345==-- """) msgdata = {} header_matches = IHeaderMatchList(self._mlist) header_matches.append('Content-Type', 'application/junk', 'hold') # This event subscriber records the event that occurs when the message # is processed by the owner chain. events = [] with event_subscribers(events.append): process(self._mlist, msg, msgdata, start_chain='header-match') self.assertEqual(len(events), 1) event = events[0] self.assertIsInstance(event, HoldEvent) self.assertEqual(event.chain, config.chains['hold'])
def test_get_all_returns_non_string(self): # Test case where msg.get_all() returns header instance. msg = message_from_bytes(b"""\ From: [email protected] To: [email protected] Subject: Bad \x96 subject Message-ID: <ant> body """, Message) msgdata = {} header_matches = IHeaderMatchList(self._mlist) header_matches.append('Subject', 'Bad', 'hold') # This event subscriber records the event that occurs when the message # is processed by the owner chain. events = [] with event_subscribers(events.append): process(self._mlist, msg, msgdata, start_chain='header-match') self.assertEqual(len(events), 1) event = events[0] self.assertIsInstance(event, HoldEvent) self.assertEqual(event.chain, config.chains['hold'])
def test_rfc2047_encodedheader(self): # Test case where msg.get_all() returns raw rfc2047 encoded string. msg = message_from_bytes( b"""\ From: [email protected] To: [email protected] Subject: =?utf-8?b?SSBsaWtlIElrZQo=?= Message-ID: <ant> body """, Message) msgdata = {} header_matches = IHeaderMatchList(self._mlist) header_matches.append('Subject', 'I Like Ike', 'hold') # This event subscriber records the event that occurs when the message # is processed by the owner chain. events = [] with event_subscribers(events.append): process(self._mlist, msg, msgdata, start_chain='header-match') self.assertEqual(len(events), 1) event = events[0] self.assertIsInstance(event, HoldEvent) self.assertEqual(event.chain, config.chains['hold'])
def test_confirmation_event_received(self): # Registering an email address generates an event. def capture_event(event): self.assertIsInstance(event, ConfirmationNeededEvent) with event_subscribers(capture_event): self.registrar.register(self.mlist, '*****@*****.**')
def test_event_mlist(self): # The event has a reference to the mailing list being subscribed to. def capture_event(event): self.assertIs(event.mlist, self.mlist) with event_subscribers(capture_event): self.registrar.register(self.mlist, '*****@*****.**')