]> git.ozlabs.org Git - patchwork/blob - patchwork/tests/test_notifications.py
tests/test_user: Add "profile POST" tests
[patchwork] / patchwork / tests / test_notifications.py
1 # Patchwork - automated patch tracking system
2 # Copyright (C) 2011 Jeremy Kerr <jk@ozlabs.org>
3 #
4 # This file is part of the Patchwork package.
5 #
6 # Patchwork is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # Patchwork is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with Patchwork; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 import datetime
21 from django.test import TestCase
22 from django.core.urlresolvers import reverse
23 from django.core import mail
24 from django.conf import settings
25 from django.db.utils import IntegrityError
26 from patchwork.models import Patch, State, PatchChangeNotification, EmailOptout
27 from patchwork.tests.utils import defaults, create_maintainer
28 from patchwork.utils import send_notifications
29
30 class PatchNotificationModelTest(TestCase):
31     fixtures = ['default_states']
32
33     """Tests for the creation & update of the PatchChangeNotification model"""
34
35     def setUp(self):
36         self.project = defaults.project
37         self.project.send_notifications = True
38         self.project.save()
39         self.submitter = defaults.patch_author_person
40         self.submitter.save()
41         self.patch = Patch(project = self.project, msgid = 'testpatch',
42                         name = 'testpatch', content = '',
43                         submitter = self.submitter)
44
45     def tearDown(self):
46         self.patch.delete()
47         self.submitter.delete()
48         self.project.delete()
49
50     def testPatchCreation(self):
51         """Ensure we don't get a notification on create"""
52         self.patch.save()
53         self.assertEqual(PatchChangeNotification.objects.count(), 0)
54
55     def testPatchUninterestingChange(self):
56         """Ensure we don't get a notification for "uninteresting" changes"""
57         self.patch.save()
58         self.patch.archived = True
59         self.patch.save()
60         self.assertEqual(PatchChangeNotification.objects.count(), 0)
61
62     def testPatchChange(self):
63         """Ensure we get a notification for interesting patch changes"""
64         self.patch.save()
65         oldstate = self.patch.state
66         state = State.objects.exclude(pk = oldstate.pk)[0]
67
68         self.patch.state = state
69         self.patch.save()
70         self.assertEqual(PatchChangeNotification.objects.count(), 1)
71         notification = PatchChangeNotification.objects.all()[0]
72         self.assertEqual(notification.patch, self.patch)
73         self.assertEqual(notification.orig_state, oldstate)
74
75     def testNotificationCancelled(self):
76         """Ensure we cancel notifications that are no longer valid"""
77         self.patch.save()
78         oldstate = self.patch.state
79         state = State.objects.exclude(pk = oldstate.pk)[0]
80
81         self.patch.state = state
82         self.patch.save()
83         self.assertEqual(PatchChangeNotification.objects.count(), 1)
84
85         self.patch.state = oldstate
86         self.patch.save()
87         self.assertEqual(PatchChangeNotification.objects.count(), 0)
88
89     def testNotificationUpdated(self):
90         """Ensure we update notifications when the patch has a second change,
91            but keep the original patch details"""
92         self.patch.save()
93         oldstate = self.patch.state
94         newstates = State.objects.exclude(pk = oldstate.pk)[:2]
95
96         self.patch.state = newstates[0]
97         self.patch.save()
98         self.assertEqual(PatchChangeNotification.objects.count(), 1)
99         notification = PatchChangeNotification.objects.all()[0]
100         self.assertEqual(notification.orig_state, oldstate)
101         orig_timestamp = notification.last_modified
102                          
103         self.patch.state = newstates[1]
104         self.patch.save()
105         self.assertEqual(PatchChangeNotification.objects.count(), 1)
106         notification = PatchChangeNotification.objects.all()[0]
107         self.assertEqual(notification.orig_state, oldstate)
108         self.assertTrue(notification.last_modified >= orig_timestamp)
109
110     def testProjectNotificationsDisabled(self):
111         """Ensure we don't see notifications created when a project is
112            configured not to send them"""
113         self.project.send_notifications = False
114         self.project.save()
115
116         self.patch.save()
117         oldstate = self.patch.state
118         state = State.objects.exclude(pk = oldstate.pk)[0]
119
120         self.patch.state = state
121         self.patch.save()
122         self.assertEqual(PatchChangeNotification.objects.count(), 0)
123
124 class PatchNotificationEmailTest(TestCase):
125     fixtures = ['default_states']
126
127     def setUp(self):
128         self.project = defaults.project
129         self.project.send_notifications = True
130         self.project.save()
131         self.submitter = defaults.patch_author_person
132         self.submitter.save()
133         self.patch = Patch(project = self.project, msgid = 'testpatch',
134                         name = 'testpatch', content = '',
135                         submitter = self.submitter)
136         self.patch.save()
137
138     def tearDown(self):
139         self.patch.delete()
140         self.submitter.delete()
141         self.project.delete()
142
143     def _expireNotifications(self, **kwargs):
144         timestamp = datetime.datetime.now() - \
145                     datetime.timedelta(minutes =
146                             settings.NOTIFICATION_DELAY_MINUTES + 1)
147
148         qs = PatchChangeNotification.objects.all()
149         if kwargs:
150             qs = qs.filter(**kwargs)
151
152         qs.update(last_modified = timestamp)
153
154     def testNoNotifications(self):
155         self.assertEquals(send_notifications(), [])
156
157     def testNoReadyNotifications(self):
158         """ We shouldn't see immediate notifications"""
159         PatchChangeNotification(patch = self.patch,
160                                orig_state = self.patch.state).save()
161
162         errors = send_notifications()
163         self.assertEquals(errors, [])
164         self.assertEquals(len(mail.outbox), 0)
165
166     def testNotifications(self):
167         PatchChangeNotification(patch = self.patch,
168                                orig_state = self.patch.state).save()
169         self._expireNotifications()
170
171         errors = send_notifications()
172         self.assertEquals(errors, [])
173         self.assertEquals(len(mail.outbox), 1)
174         msg = mail.outbox[0]
175         self.assertEquals(msg.to, [self.submitter.email])
176         self.assertTrue(self.patch.get_absolute_url() in msg.body)
177
178     def testNotificationEscaping(self):
179         self.patch.name = 'Patch name with " character'
180         self.patch.save()
181         PatchChangeNotification(patch = self.patch,
182                                orig_state = self.patch.state).save()
183         self._expireNotifications()
184
185         errors = send_notifications()
186         self.assertEquals(errors, [])
187         self.assertEquals(len(mail.outbox), 1)
188         msg = mail.outbox[0]
189         self.assertEquals(msg.to, [self.submitter.email])
190         self.assertFalse('&quot;' in msg.body)
191
192     def testNotificationOptout(self):
193         """ensure opt-out addresses don't get notifications"""
194         PatchChangeNotification(patch = self.patch,
195                                orig_state = self.patch.state).save()
196         self._expireNotifications()
197
198         EmailOptout(email = self.submitter.email).save()
199
200         errors = send_notifications()
201         self.assertEquals(errors, [])
202         self.assertEquals(len(mail.outbox), 0)
203
204     def testNotificationMerge(self):
205         patches = [self.patch,
206                    Patch(project = self.project, msgid = 'testpatch-2',
207                          name = 'testpatch 2', content = '',
208                          submitter = self.submitter)]
209
210         for patch in patches:
211             patch.save()
212             PatchChangeNotification(patch = patch,
213                                    orig_state = patch.state).save()
214
215         self.assertEquals(PatchChangeNotification.objects.count(), len(patches))
216         self._expireNotifications()
217         errors = send_notifications()
218         self.assertEquals(errors, [])
219         self.assertEquals(len(mail.outbox), 1)
220         msg = mail.outbox[0]
221         self.assertTrue(patches[0].get_absolute_url() in msg.body)
222         self.assertTrue(patches[1].get_absolute_url() in msg.body)
223
224     def testUnexpiredNotificationMerge(self):
225         """Test that when there are multiple pending notifications, with
226            at least one within the notification delay, that other notifications
227            are held"""
228         patches = [self.patch,
229                    Patch(project = self.project, msgid = 'testpatch-2',
230                          name = 'testpatch 2', content = '',
231                          submitter = self.submitter)]
232
233         for patch in patches:
234             patch.save()
235             PatchChangeNotification(patch = patch,
236                                    orig_state = patch.state).save()
237
238         self.assertEquals(PatchChangeNotification.objects.count(), len(patches))
239         self._expireNotifications()
240
241         # update one notification, to bring it out of the notification delay
242         patches[0].state = State.objects.exclude(pk = patches[0].state.pk)[0]
243         patches[0].save()
244
245         # the updated notification should prevent the other from being sent
246         errors = send_notifications()
247         self.assertEquals(errors, [])
248         self.assertEquals(len(mail.outbox), 0)
249
250         # expire the updated notification
251         self._expireNotifications()
252
253         errors = send_notifications()
254         self.assertEquals(errors, [])
255         self.assertEquals(len(mail.outbox), 1)
256         msg = mail.outbox[0]
257         self.assertTrue(patches[0].get_absolute_url() in msg.body)
258         self.assertTrue(patches[1].get_absolute_url() in msg.body)