]> git.ozlabs.org Git - patchwork/blob - apps/patchwork/tests/mail_settings.py
Add email opt-out system
[patchwork] / apps / patchwork / tests / mail_settings.py
1 # Patchwork - automated patch tracking system
2 # Copyright (C) 2010 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 unittest
21 import re
22 from django.test import TestCase
23 from django.test.client import Client
24 from django.core import mail
25 from django.core.urlresolvers import reverse
26 from django.contrib.auth.models import User
27 from patchwork.models import EmailOptout, EmailConfirmation, Person
28 from patchwork.tests.utils import create_user
29
30 class MailSettingsTest(TestCase):
31     view = 'patchwork.views.mail.settings'
32     url = reverse(view)
33
34     def testMailSettingsGET(self):
35         response = self.client.get(self.url)
36         self.assertEquals(response.status_code, 200)
37         self.assertTrue(response.context['form'])
38
39     def testMailSettingsPOST(self):
40         email = u'foo@example.com'
41         response = self.client.post(self.url, {'email': email})
42         self.assertEquals(response.status_code, 200)
43         self.assertTemplateUsed(response, 'patchwork/mail-settings.html')
44         self.assertEquals(response.context['email'], email)
45
46     def testMailSettingsPOSTEmpty(self):
47         response = self.client.post(self.url, {'email': ''})
48         self.assertEquals(response.status_code, 200)
49         self.assertTemplateUsed(response, 'patchwork/mail-form.html')
50         self.assertFormError(response, 'form', 'email',
51                 'This field is required.')
52
53     def testMailSettingsPOSTInvalid(self):
54         response = self.client.post(self.url, {'email': 'foo'})
55         self.assertEquals(response.status_code, 200)
56         self.assertTemplateUsed(response, 'patchwork/mail-form.html')
57         self.assertFormError(response, 'form', 'email',
58                 'Enter a valid e-mail address.')
59
60     def testMailSettingsPOSTOptedIn(self):
61         email = u'foo@example.com'
62         response = self.client.post(self.url, {'email': email})
63         self.assertEquals(response.status_code, 200)
64         self.assertTemplateUsed(response, 'patchwork/mail-settings.html')
65         self.assertEquals(response.context['is_optout'], False)
66         self.assertTrue('<strong>may</strong>' in response.content)
67         optout_url = reverse('patchwork.views.mail.optout')
68         self.assertTrue(('action="%s"' % optout_url) in response.content)
69
70     def testMailSettingsPOSTOptedOut(self):
71         email = u'foo@example.com'
72         EmailOptout(email = email).save()
73         response = self.client.post(self.url, {'email': email})
74         self.assertEquals(response.status_code, 200)
75         self.assertTemplateUsed(response, 'patchwork/mail-settings.html')
76         self.assertEquals(response.context['is_optout'], True)
77         self.assertTrue('<strong>may not</strong>' in response.content)
78         optin_url = reverse('patchwork.views.mail.optin')
79         self.assertTrue(('action="%s"' % optin_url) in response.content)
80
81 class OptoutRequestTest(TestCase):
82     view = 'patchwork.views.mail.optout'
83     url = reverse(view)
84
85     def testOptOutRequestGET(self):
86         response = self.client.get(self.url)
87         self.assertRedirects(response, reverse('patchwork.views.mail.settings'))
88
89     def testOptoutRequestValidPOST(self):
90         email = u'foo@example.com'
91         response = self.client.post(self.url, {'email': email})
92
93         # check for a confirmation object
94         self.assertEquals(EmailConfirmation.objects.count(), 1)
95         conf = EmailConfirmation.objects.get(email = email)
96
97         # check confirmation page
98         self.assertEquals(response.status_code, 200)
99         self.assertEquals(response.context['confirmation'], conf)
100         self.assertTrue(email in response.content)
101
102         # check email
103         url = reverse('patchwork.views.confirm', kwargs = {'key': conf.key})
104         self.assertEquals(len(mail.outbox), 1)
105         msg = mail.outbox[0]
106         self.assertEquals(msg.to, [email])
107         self.assertEquals(msg.subject, 'Patchwork opt-out confirmation')
108         self.assertTrue(url in msg.body)
109
110     def testOptoutRequestInvalidPOSTEmpty(self):
111         response = self.client.post(self.url, {'email': ''})
112         self.assertEquals(response.status_code, 200)
113         self.assertFormError(response, 'form', 'email',
114                 'This field is required.')
115         self.assertTrue(response.context['error'])
116         self.assertTrue('email_sent' not in response.context)
117         self.assertEquals(len(mail.outbox), 0)
118
119     def testOptoutRequestInvalidPOSTNonEmail(self):
120         response = self.client.post(self.url, {'email': 'foo'})
121         self.assertEquals(response.status_code, 200)
122         self.assertFormError(response, 'form', 'email',
123                 'Enter a valid e-mail address.')
124         self.assertTrue(response.context['error'])
125         self.assertTrue('email_sent' not in response.context)
126         self.assertEquals(len(mail.outbox), 0)
127
128 class OptoutTest(TestCase):
129     view = 'patchwork.views.mail.optout'
130     url = reverse(view)
131
132     def setUp(self):
133         self.email = u'foo@example.com'
134         self.conf = EmailConfirmation(type = 'optout', email = self.email)
135         self.conf.save()
136
137     def testOptoutValidHash(self):
138         url = reverse('patchwork.views.confirm',
139                         kwargs = {'key': self.conf.key})
140         response = self.client.get(url)
141
142         self.assertEquals(response.status_code, 200)
143         self.assertTemplateUsed(response, 'patchwork/optout.html')
144         self.assertTrue(self.email in response.content)
145
146         # check that we've got an optout in the list
147         self.assertEquals(EmailOptout.objects.count(), 1)
148         self.assertEquals(EmailOptout.objects.all()[0].email, self.email)
149
150         # check that the confirmation is now inactive
151         self.assertFalse(EmailConfirmation.objects.get(
152                                     pk = self.conf.pk).active)
153
154
155 class OptoutPreexistingTest(OptoutTest):
156     """Test that a duplicated opt-out behaves the same as the initial one"""
157     def setUp(self):
158         super(OptoutPreexistingTest, self).setUp()
159         EmailOptout(email = self.email).save()
160
161 class OptinRequestTest(TestCase):
162     view = 'patchwork.views.mail.optin'
163     url = reverse(view)
164
165     def setUp(self):
166         self.email = u'foo@example.com'
167         EmailOptout(email = self.email).save()
168
169     def testOptInRequestGET(self):
170         response = self.client.get(self.url)
171         self.assertRedirects(response, reverse('patchwork.views.mail.settings'))
172
173     def testOptInRequestValidPOST(self):
174         response = self.client.post(self.url, {'email': self.email})
175
176         # check for a confirmation object
177         self.assertEquals(EmailConfirmation.objects.count(), 1)
178         conf = EmailConfirmation.objects.get(email = self.email)
179
180         # check confirmation page
181         self.assertEquals(response.status_code, 200)
182         self.assertEquals(response.context['confirmation'], conf)
183         self.assertTrue(self.email in response.content)
184
185         # check email
186         url = reverse('patchwork.views.confirm', kwargs = {'key': conf.key})
187         self.assertEquals(len(mail.outbox), 1)
188         msg = mail.outbox[0]
189         self.assertEquals(msg.to, [self.email])
190         self.assertEquals(msg.subject, 'Patchwork opt-in confirmation')
191         self.assertTrue(url in msg.body)
192
193     def testOptoutRequestInvalidPOSTEmpty(self):
194         response = self.client.post(self.url, {'email': ''})
195         self.assertEquals(response.status_code, 200)
196         self.assertFormError(response, 'form', 'email',
197                 'This field is required.')
198         self.assertTrue(response.context['error'])
199         self.assertTrue('email_sent' not in response.context)
200         self.assertEquals(len(mail.outbox), 0)
201
202     def testOptoutRequestInvalidPOSTNonEmail(self):
203         response = self.client.post(self.url, {'email': 'foo'})
204         self.assertEquals(response.status_code, 200)
205         self.assertFormError(response, 'form', 'email',
206                 'Enter a valid e-mail address.')
207         self.assertTrue(response.context['error'])
208         self.assertTrue('email_sent' not in response.context)
209         self.assertEquals(len(mail.outbox), 0)
210
211 class OptinTest(TestCase):
212
213     def setUp(self):
214         self.email = u'foo@example.com'
215         self.optout = EmailOptout(email = self.email)
216         self.optout.save()
217         self.conf = EmailConfirmation(type = 'optin', email = self.email)
218         self.conf.save()
219
220     def testOptinValidHash(self):
221         url = reverse('patchwork.views.confirm',
222                         kwargs = {'key': self.conf.key})
223         response = self.client.get(url)
224
225         self.assertEquals(response.status_code, 200)
226         self.assertTemplateUsed(response, 'patchwork/optin.html')
227         self.assertTrue(self.email in response.content)
228
229         # check that there's no optout remaining
230         self.assertEquals(EmailOptout.objects.count(), 0)
231
232         # check that the confirmation is now inactive
233         self.assertFalse(EmailConfirmation.objects.get(
234                                     pk = self.conf.pk).active)
235
236 class OptinWithoutOptoutTest(TestCase):
237     """Test an opt-in with no existing opt-out"""
238     view = 'patchwork.views.mail.optin'
239     url = reverse(view)
240
241     def testOptInWithoutOptout(self):
242         email = u'foo@example.com'
243         response = self.client.post(self.url, {'email': email})
244
245         # check for an error message
246         self.assertEquals(response.status_code, 200)
247         self.assertTrue(bool(response.context['error']))
248         self.assertTrue('not on the patchwork opt-out list' in response.content)
249
250 class UserProfileOptoutFormTest(TestCase):
251     """Test that the correct optin/optout forms appear on the user profile
252        page, for logged-in users"""
253
254     view = 'patchwork.views.user.profile'
255     url = reverse(view)
256     optout_url = reverse('patchwork.views.mail.optout')
257     optin_url = reverse('patchwork.views.mail.optin')
258     form_re_template = ('<form\s+[^>]*action="%(url)s"[^>]*>'
259                         '.*?<input\s+[^>]*value="%(email)s"[^>]*>.*?'
260                         '</form>')
261     secondary_email = 'test2@example.com'
262
263     def setUp(self):
264         self.user = create_user()
265         self.client.login(username = self.user.username,
266                 password = self.user.username)
267
268     def _form_re(self, url, email):
269         return re.compile(self.form_re_template % {'url': url, 'email': email},
270                           re.DOTALL)
271
272     def testMainEmailOptoutForm(self):
273         form_re = self._form_re(self.optout_url, self.user.email)
274         response = self.client.get(self.url)
275         self.assertEquals(response.status_code, 200)
276         self.assertTrue(form_re.search(response.content) is not None)
277
278     def testMainEmailOptinForm(self):
279         EmailOptout(email = self.user.email).save()
280         form_re = self._form_re(self.optin_url, self.user.email)
281         response = self.client.get(self.url)
282         self.assertEquals(response.status_code, 200)
283         self.assertTrue(form_re.search(response.content) is not None)
284
285     def testSecondaryEmailOptoutForm(self):
286         p = Person(email = self.secondary_email, user = self.user)
287         p.save()
288         
289         form_re = self._form_re(self.optout_url, p.email)
290         response = self.client.get(self.url)
291         self.assertEquals(response.status_code, 200)
292         self.assertTrue(form_re.search(response.content) is not None)
293
294     def testSecondaryEmailOptinForm(self):
295         p = Person(email = self.secondary_email, user = self.user)
296         p.save()
297         EmailOptout(email = p.email).save()
298
299         form_re = self._form_re(self.optin_url, self.user.email)
300         response = self.client.get(self.url)
301         self.assertEquals(response.status_code, 200)
302         self.assertTrue(form_re.search(response.content) is not None)