1 # Copyright (C) 2012-2023 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <https://www.gnu.org/licenses/>.
18 """Tests for the Task runner."""
22 from datetime
import timedelta
23 from lazr
.config
import as_timedelta
24 from mailman
.app
.bounces
import PENDABLE_LIFETIME
25 from mailman
.app
.lifecycle
import create_list
26 from mailman
.app
.moderator
import hold_message
27 from mailman
.config
import config
28 from mailman
.database
.transaction
import dbconnection
29 from mailman
.interfaces
.cache
import ICacheManager
30 from mailman
.interfaces
.messages
import IMessageStore
31 from mailman
.interfaces
.pending
import IPendable
, IPendings
32 from mailman
.interfaces
.requests
import IListRequests
33 from mailman
.interfaces
.workflow
import IWorkflowStateManager
34 from mailman
.model
.bounce
import BounceEvent
35 from mailman
.runners
.task
import TaskRunner
36 from mailman
.testing
.helpers
import (
39 specialized_message_from_string
as mfs
,
41 from mailman
.testing
.layers
import ConfigLayer
42 from mailman
.utilities
.datetime
import factory
43 from zope
.component
import getUtility
44 from zope
.interface
import implementer
47 @implementer(IPendable
)
48 class MyPendable(dict):
49 PEND_TYPE
= 'my pended'
52 class TestTask(unittest
.TestCase
):
53 """Test various aspects of the Task runner."""
58 self
._pendings
= getUtility(IPendings
)
59 self
._events
= BounceEvent
60 self
._wfmanager
= getUtility(IWorkflowStateManager
)
61 self
._cachemanager
= getUtility(ICacheManager
)
62 self
._messages
= getUtility(IMessageStore
)
63 self
._mlist
= create_list('ant@example.com')
66 From: anne@example.com
74 From: anne@example.com
80 self
._listrequests
= IListRequests(self
._mlist
)
81 self
._runner
= make_testable_runner(TaskRunner
)
82 self
._cachemanager
.add('cache1', 'xxx1', lifetime
=timedelta(days
=1))
83 self
._cachemanager
.add('cache2', 'xxx2', lifetime
=timedelta(days
=3))
84 pendable
= MyPendable(id=1)
85 self
._token
1 = self
._pendings
.add(pendable
, lifetime
=timedelta(days
=1))
86 self
._token
2 = self
._pendings
.add(pendable
, lifetime
=timedelta(days
=3))
87 self
._wfmanager
.save(self
._token
1)
88 self
._wfmanager
.save(self
._token
2)
89 self
._requestid
1 = hold_message(
90 self
._mlist
, self
._msg
1, reason
='Testing')
91 self
._requestid
2 = hold_message(
92 self
._mlist
, self
._msg
2, reason
='Testing')
94 def test_task_runner(self
):
95 # Test that the task runner deletes expired cache, pendings and
96 # associated workflows.
97 self
.assertEqual(self
._cachemanager
.get('cache1'), 'xxx1')
98 self
.assertEqual(self
._cachemanager
.get('cache2'), 'xxx2')
99 self
.assertEqual(self
._pendings
.count(), 4)
100 self
.assertEqual(self
._wfmanager
.count
, 2)
101 mark
= LogFileMark('mailman.task')
102 factory
.fast_forward(days
=2)
104 self
.assertIsNone(self
._cachemanager
.get('cache1'))
105 self
.assertEqual(self
._cachemanager
.get('cache2'), 'xxx2')
106 self
.assertEqual(self
._pendings
.count(), 3)
107 pended
= self
._pendings
.confirm(self
._token
2, expunge
=False)
108 self
.assertEqual(pended
['type'], 'my pended')
109 self
.assertEqual(list(self
._wfmanager
.get_all_tokens()),
112 self
.assertIn('Task runner evicted 1 expired pendings', log
)
113 self
.assertIn('Task runner deleted 1 orphaned workflows', log
)
114 self
.assertIn('Task runner deleted 0 orphaned requests', log
)
115 self
.assertIn('Task runner evicted expired cache entries', log
)
117 def test_task_runner_request(self
):
118 # Test that the task runner deletes orphaned requests.
119 self
.assertEqual(self
._pendings
.count(), 4)
120 self
.assertEqual(self
._listrequests
.count
, 2)
121 life
= as_timedelta(config
.mailman
.moderator_request_life
)
122 mark
= LogFileMark('mailman.task')
123 factory
.fast_forward(days
=life
.days
+1)
125 self
.assertEqual(self
._pendings
.count(), 0)
126 self
.assertEqual(self
._listrequests
.count
, 0)
128 self
.assertIn('Task runner deleted 2 orphaned requests', log
)
130 def test_task_runner_messages(self
):
131 # Test that the task runner deletes orphaned messages from the
133 # Initially, there are 2 messages in the store and 4 pendings.
134 self
.assertEqual(len(list(self
._messages
.messages
)), 2)
135 self
.assertEqual(self
._pendings
.count(), 4)
136 # Deleting the first request removes the pending but not the message.
137 self
._listrequests
.delete_request(self
._requestid
1)
138 self
.assertEqual(self
._pendings
.count(), 3)
139 self
.assertEqual(len(list(self
._messages
.messages
)), 2)
140 mark
= LogFileMark('mailman.task')
142 # Now there's only msg2.
143 self
.assertEqual(len(list(self
._messages
.messages
)), 1)
144 self
.assertIsNotNone(self
._messages
.get_message_by_id('<msg2>'))
146 self
.assertIn('Task runner deleted 1 orphaned messages', log
)
149 def test_task_runner_bounce_events_old_unprocessed(self
, store
):
150 # Test that the task runner deletes processed bounce events older than
151 # PENDABLE_LIFETIME, but not newer ones or unprocessed ones.
152 # Set one old but unprocessed.
153 event
= self
._events
(self
._mlist
.list_id
,
157 event
.timestamp
-= as_timedelta(PENDABLE_LIFETIME
) + as_timedelta('1d')
159 mark
= LogFileMark('mailman.task')
162 self
.assertIn('Task runner evicted 0 expired bounce events', log
)
165 def test_task_runner_bounce_events_old_processed(self
, store
):
166 # Test that the task runner deletes processed bounce events older than
167 # PENDABLE_LIFETIME, but not newer ones or unprocessed ones.
168 # Set one old and processed.
169 event
= self
._events
(self
._mlist
.list_id
,
173 event
.timestamp
-= as_timedelta(PENDABLE_LIFETIME
) + as_timedelta('1d')
174 event
.processed
= True
176 mark
= LogFileMark('mailman.task')
179 self
.assertIn('Task runner evicted 1 expired bounce events', log
)
182 def test_task_runner_bounce_events_processed(self
, store
):
183 # Test that the task runner deletes processed bounce events older than
184 # PENDABLE_LIFETIME, but not newer ones or unprocessed ones.
186 event
= self
._events
(self
._mlist
.list_id
,
190 event
.processed
= True
192 mark
= LogFileMark('mailman.task')
195 self
.assertIn('Task runner evicted 0 expired bounce events', log
)