bgneal@565: """
bgneal@565: View tests for the accounts application.
bgneal@565: 
bgneal@565: """
bgneal@565: import datetime
bgneal@762: import re
bgneal@565: 
bgneal@565: from django.test import TestCase
bgneal@565: from django.core.urlresolvers import reverse
bgneal@659: from django.core import mail
bgneal@576: from django.contrib.auth.models import User
bgneal@576: from django.contrib.auth.hashers import check_password
bgneal@565: 
bgneal@565: from accounts.models import PendingUser
bgneal@565: from accounts.models import IllegalUsername
bgneal@565: from accounts.models import IllegalEmail
bgneal@565: 
bgneal@565: 
bgneal@565: class RegistrationTest(TestCase):
bgneal@565: 
bgneal@565:     def setUp(self):
bgneal@565:         u = User.objects.create_user('existing_user', 'existing_user@example.com', 'pw')
bgneal@565:         u.save()
bgneal@565: 
bgneal@565:         # a 2nd user has the same email as another
bgneal@565:         u = User.objects.create_user('existing_user2', 'existing_user@example.com', 'pw')
bgneal@565:         u.save()
bgneal@565: 
bgneal@565:         PendingUser.objects.create(username='pending_user',
bgneal@565:             email='pending_user@example.com',
bgneal@565:             password='pw',
bgneal@565:             date_joined=datetime.datetime.now(),
bgneal@565:             key='key')
bgneal@565: 
bgneal@565:         IllegalUsername.objects.create(username='illegalusername')
bgneal@565:         IllegalEmail.objects.create(email='illegal@example.com')
bgneal@565: 
bgneal@782:         self.post_vals = {
bgneal@782:             'username': 'a_new_user',
bgneal@782:             'email': 'test@example.com',
bgneal@782:             'password1': 'my_password',
bgneal@782:             'password2': 'my_password',
bgneal@782:             'agree_age': 'on',
bgneal@782:             'agree_tos': 'on',
bgneal@782:             'agree_privacy': 'on',
bgneal@782:             'question1': '101',
bgneal@782:             'question2': '',
bgneal@782:             'question3': '',
bgneal@782:         }
bgneal@782: 
bgneal@565:     def test_get_view(self):
bgneal@565:         """
bgneal@565:         Test a simple get of the registration view
bgneal@565: 
bgneal@565:         """
bgneal@565:         response = self.client.get(reverse('accounts-register'))
bgneal@565:         self.assertEqual(response.status_code, 200)
bgneal@565: 
bgneal@565:     def test_existing_user(self):
bgneal@565:         """
bgneal@565:         Ensure we can't register with an existing username.
bgneal@565: 
bgneal@565:         """
bgneal@565:         response = self.client.post(reverse('accounts-register'), {
bgneal@565:             'username': 'existing_user',
bgneal@565:             'email': 'test@example.com',
bgneal@565:             'password1': 'my_password',
bgneal@565:             'password2': 'my_password',
bgneal@565:             'agree_age': 'on',
bgneal@565:             'agree_tos': 'on',
bgneal@565:             'agree_privacy': 'on',
bgneal@565:             'question1': '101',
bgneal@565:             'question2': '',
bgneal@905:             'question3': '',
bgneal@565:             })
bgneal@565: 
bgneal@565:         self.assertEqual(response.status_code, 200)
bgneal@565:         self.assertContains(response, 'A user with that username already exists')
bgneal@565: 
bgneal@565:     def test_pending_user(self):
bgneal@565:         """
bgneal@565:         Ensure we can't register with a pending username.
bgneal@565: 
bgneal@565:         """
bgneal@782:         self.post_vals['username'] = 'pending_user'
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:             self.post_vals)
bgneal@565:         self.assertEqual(response.status_code, 200)
bgneal@565:         self.assertContains(response, 'A pending user with that username already exists')
bgneal@565: 
bgneal@565:     def test_illegal_username(self):
bgneal@565:         """
bgneal@565:         Ensure we can't register with a banned username.
bgneal@565: 
bgneal@565:         """
bgneal@782:         self.post_vals['username'] = 'illegalusername'
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:             self.post_vals)
bgneal@565:         self.assertEqual(response.status_code, 200)
bgneal@565:         self.assertContains(response, 'That username is not allowed')
bgneal@565: 
bgneal@565:     def test_duplicate_existing_email(self):
bgneal@565:         """
bgneal@565:         Ensure we can't register with a duplicate email address.
bgneal@565: 
bgneal@565:         """
bgneal@782:         self.post_vals['email'] = 'existing_user@example.com'
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:             self.post_vals)
bgneal@565:         self.assertEqual(response.status_code, 200)
bgneal@565:         self.assertContains(response, 'A user with that email address already exists')
bgneal@565: 
bgneal@565:     def test_duplicate_pending_email(self):
bgneal@565:         """
bgneal@565:         Ensure we can't register with a duplicate email address.
bgneal@565: 
bgneal@565:         """
bgneal@782:         self.post_vals['email'] = 'pending_user@example.com'
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:             self.post_vals)
bgneal@565:         self.assertEqual(response.status_code, 200)
bgneal@565:         self.assertContains(response, 'A pending user with that email address already exists')
bgneal@565: 
bgneal@565:     def test_illegal_email(self):
bgneal@565:         """
bgneal@565:         Ensure we can't register with a banned email address.
bgneal@565: 
bgneal@565:         """
bgneal@782:         self.post_vals['email'] = 'illegal@example.com'
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:             self.post_vals)
bgneal@565:         self.assertEqual(response.status_code, 200)
bgneal@565:         self.assertContains(response, 'That email address is not allowed')
bgneal@565: 
bgneal@565:     def test_password_match(self):
bgneal@565:         """
bgneal@565:         Ensure the passwords match.
bgneal@565: 
bgneal@565:         """
bgneal@782:         self.post_vals['password2'] = "doesn't match"
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:             self.post_vals)
bgneal@565:         self.assertEqual(response.status_code, 200)
bgneal@565:         self.assertContains(response, "The two password fields didn't match")
bgneal@565: 
bgneal@565:     def test_question1(self):
bgneal@565:         """
bgneal@565:         Ensure our anti-spam question is answered.
bgneal@565: 
bgneal@565:         """
bgneal@782:         self.post_vals['question1'] = 'huh'
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:              self.post_vals)
bgneal@565:         self.assertEqual(response.status_code, 200)
bgneal@565:         self.assertContains(response, "Incorrect answer to our anti-spam question")
bgneal@565: 
bgneal@565:     def test_question2(self):
bgneal@565:         """
bgneal@565:         Ensure our honeypot question check works.
bgneal@565: 
bgneal@565:         """
bgneal@782:         self.post_vals['question2'] = 'non blank'
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:              self.post_vals)
bgneal@782:         self.assertEqual(response.status_code, 200)
bgneal@565: 
bgneal@782:     def test_question3(self):
bgneal@782:         """
bgneal@782:         Ensure our non-hidden honeypot question check works.
bgneal@782: 
bgneal@782:         """
bgneal@782:         self.post_vals['question3'] = 'non blank'
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:              self.post_vals)
bgneal@782:         self.assertEqual(response.status_code, 200)
bgneal@782: 
bgneal@565:     def test_success(self):
bgneal@565:         """
bgneal@565:         Ensure we can successfully register.
bgneal@565: 
bgneal@565:         """
bgneal@782:         response = self.client.post(reverse('accounts-register'),
bgneal@782:                 self.post_vals)
bgneal@905:         self.assertRedirects(response, reverse('accounts-register1'))
bgneal@905: 
bgneal@905:         # No pending user should exist yet
bgneal@905:         try:
bgneal@905:             pending = PendingUser.objects.get(username='a_new_user')
bgneal@905:         except PendingUser.DoesNotExist:
bgneal@905:             pass
bgneal@905:         else:
bgneal@905:             self.fail("PendingUser was created early")
bgneal@905: 
bgneal@905:         # Should have created a reg_info dict in the session
bgneal@905:         reg_info = self.client.session.get('reg_info')
bgneal@905:         self.assertEqual(reg_info, {
bgneal@905:             'username': self.post_vals['username'],
bgneal@905:             'email': self.post_vals['email'],
bgneal@905:             'password': self.post_vals['password1'],
bgneal@905:             'code': reg_info['code'],
bgneal@905:             })
bgneal@905:         code = reg_info['code']
bgneal@905:         match = re.match(r'\w+-\d{3}', code)
bgneal@905:         self.assertIsNotNone(match)
bgneal@905: 
bgneal@905:         # Get the next page
bgneal@905:         response = self.client.get(reverse('accounts-register2'))
bgneal@905:         self.assertEqual(response.status_code, 200)
bgneal@905: 
bgneal@905:         # No pending user should exist yet
bgneal@905:         try:
bgneal@905:             pending = PendingUser.objects.get(username='a_new_user')
bgneal@905:         except PendingUser.DoesNotExist:
bgneal@905:             pass
bgneal@905:         else:
bgneal@905:             self.fail("PendingUser was created early")
bgneal@905: 
bgneal@905:         # Try bad code
bgneal@905:         response = self.client.post(reverse('accounts-register2'),
bgneal@905:                                     {'code': code + code })
bgneal@905:         self.assertEqual(response.status_code, 200)
bgneal@905:         self.assertContains(response, "The registration code does not match")
bgneal@905: 
bgneal@905:         # No pending user should exist yet
bgneal@905:         try:
bgneal@905:             pending = PendingUser.objects.get(username='a_new_user')
bgneal@905:         except PendingUser.DoesNotExist:
bgneal@905:             pass
bgneal@905:         else:
bgneal@905:             self.fail("PendingUser was created early")
bgneal@905: 
bgneal@905:         # Try good code
bgneal@905:         response = self.client.post(reverse('accounts-register2'),
bgneal@905:                                     {'code': code })
bgneal@905:         self.assertRedirects(response, reverse('accounts-register_thanks'))
bgneal@905:         self.assertIsNone(self.client.session.get('reg_info'))
bgneal@565: 
bgneal@565:         try:
bgneal@565:             pending = PendingUser.objects.get(username='a_new_user')
bgneal@565:         except PendingUser.DoesNotExist:
bgneal@565:             self.fail("PendingUser was not created")
bgneal@565: 
bgneal@565:         self.assertEqual(pending.email, 'test@example.com')
bgneal@565:         self.assertTrue(datetime.datetime.now() - pending.date_joined <
bgneal@565:                 datetime.timedelta(minutes=1))
bgneal@565:         self.assertTrue(check_password('my_password', pending.password))
bgneal@659: 
bgneal@905:         self.assertEqual(len(mail.outbox), 1)
bgneal@905:         msg = mail.outbox[0]
bgneal@905:         self.assertTrue(msg.subject.startswith('Registration Confirmation'))
bgneal@905:         self.assertTrue(len(msg.to) == 1 and msg.to[0] == pending.email)
bgneal@905:         msg_text = msg.message().as_string()
bgneal@905: 
bgneal@905:         activation_link = 'http://example.com%s' % (
bgneal@905:                 reverse('accounts-register_confirm',
bgneal@905:                         kwargs={'username': pending.username,
bgneal@905:                                 'key': pending.key}))
bgneal@905:         self.assertTrue(activation_link in msg_text)
bgneal@905: 
bgneal@905:         # Vist confirm link
bgneal@905:         response = self.client.get(reverse('accounts-register_confirm',
bgneal@905:                                            kwargs={'username': pending.username,
bgneal@905:                                                    'key': pending.key}))
bgneal@905:         self.assertEqual(response.status_code, 200)
bgneal@905: 
bgneal@905:         try:
bgneal@905:             pending = PendingUser.objects.get(username='a_new_user')
bgneal@905:         except PendingUser.DoesNotExist:
bgneal@905:             pass
bgneal@905:         else:
bgneal@905:             self.fail("PendingUser was not deleted upon confirmation")
bgneal@905: 
bgneal@905:         user = User.objects.get(username=pending.username)
bgneal@905:         self.assertEqual(user.email, pending.email)
bgneal@905:         now = datetime.datetime.now()
bgneal@905:         delta = datetime.timedelta(seconds=10)
bgneal@905:         self.assertTrue(now - user.last_login < delta)
bgneal@905:         self.assertTrue(now - user.date_joined < delta)
bgneal@905:         self.assertEqual(user.password, pending.password)
bgneal@905:         self.assertEqual(user.first_name, '')
bgneal@905:         self.assertEqual(user.last_name, '')
bgneal@905:         self.assertFalse(user.is_staff)
bgneal@905:         self.assertTrue(user.is_active)
bgneal@905:         self.assertFalse(user.is_superuser)
bgneal@905: 
bgneal@659: 
bgneal@659: class ForgotUsernameTest(TestCase):
bgneal@659: 
bgneal@659:     def setUp(self):
bgneal@659:         u = User.objects.create_user('existing_user', 'existing_user@example.com', 'pw')
bgneal@659:         u.save()
bgneal@659: 
bgneal@659:     def test_get_query_view(self):
bgneal@659:         """Test a simple get of the username query view"""
bgneal@659:         response = self.client.get(reverse('accounts-username_query'))
bgneal@659:         self.assertEqual(response.status_code, 200)
bgneal@659: 
bgneal@659:     def test_get_username_sent_view(self):
bgneal@659:         """Test a simple get of the username sent view"""
bgneal@659:         response = self.client.get(reverse('accounts-username_sent'))
bgneal@659:         self.assertEqual(response.status_code, 200)
bgneal@659: 
bgneal@659:     def test_invalid_email(self):
bgneal@659:         """Test form submittal of unknown email address."""
bgneal@659:         response = self.client.post(reverse('accounts-username_query'), {
bgneal@659:             'email': 'bad_address@example.com',
bgneal@659:             },
bgneal@659:             follow=True)
bgneal@659: 
bgneal@659:         self.assertRedirects(response, reverse('accounts-username_sent'))
bgneal@659: 
bgneal@659:         self.assertEqual(len(mail.outbox), 0)
bgneal@659: 
bgneal@659:     def test_valid_email(self):
bgneal@659:         """Test form submittal of valid email address."""
bgneal@659:         response = self.client.post(reverse('accounts-username_query'), {
bgneal@659:             'email': 'existing_user@example.com',
bgneal@659:             },
bgneal@659:             follow=True)
bgneal@659: 
bgneal@659:         self.assertRedirects(response, reverse('accounts-username_sent'))
bgneal@659: 
bgneal@659:         self.assertEqual(len(mail.outbox), 1)
bgneal@659:         if len(mail.outbox):
bgneal@659:             self.assertTrue(mail.outbox[0].subject.startswith('Forgotten username'))
bgneal@762: 
bgneal@762: 
bgneal@762: class ForgotEmailTest(TestCase):
bgneal@762:     """Because we use a custom URL its important to test this. This got broken
bgneal@762:     in Django 1.6 when the URL pattern changed.
bgneal@762: 
bgneal@762:     """
bgneal@762: 
bgneal@762:     def setUp(self):
bgneal@762:         u = User.objects.create_user('user1', 'user1@example.com', 'pw')
bgneal@762:         u.save()
bgneal@762: 
bgneal@762:     def test_nominal_case(self):
bgneal@762:         """Test a full forgot password scenario."""
bgneal@762: 
bgneal@762:         # GET password reset page
bgneal@762:         response = self.client.get(reverse('accounts-password_reset'))
bgneal@762:         self.assertEqual(response.status_code, 200)
bgneal@762: 
bgneal@762:         # POST email address
bgneal@762:         args = {'email': 'user1@example.com'}
bgneal@762:         response = self.client.post(reverse('accounts-password_reset'), args,
bgneal@762:                 follow=True)
bgneal@762:         self.assertRedirects(response, reverse('accounts-password_reset_sent'))
bgneal@762: 
bgneal@762:         # Ensure the email was sent
bgneal@762:         self.assertEqual(len(mail.outbox), 1)
bgneal@762:         if (len(mail.outbox)):
bgneal@762:             msg = mail.outbox[0]
bgneal@762:             self.assertTrue(msg.subject.startswith('Password reset'))
bgneal@762:             self.assertTrue(len(msg.to) == 1 and msg.to[0] == 'user1@example.com')
bgneal@762:             msg_text = msg.message().as_string()
bgneal@762:             m = re.search(r'http://example.com/accounts/password/reset/confirm/'
bgneal@762:                 r'(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9a-z]+-\w+)/',
bgneal@762:                 msg_text)
bgneal@762:             self.assertTrue(m is not None)
bgneal@762:             if m:
bgneal@762:                 uidb64, token = m.group('uidb64'), m.group('token')
bgneal@762: 
bgneal@762:             # visit the password reset page
bgneal@762:             response = self.client.get(
bgneal@762:                     reverse('accounts-password_reset_confirm',
bgneal@762:                             kwargs={'uidb64': uidb64, 'token': token}))
bgneal@762:             self.assertEqual(response.status_code, 200)
bgneal@762: 
bgneal@762:             # POST new password
bgneal@762:             args = {'new_password1': 'pw2', 'new_password2': 'pw2'}
bgneal@762:             response = self.client.post(
bgneal@762:                     reverse('accounts-password_reset_confirm',
bgneal@762:                             kwargs={'uidb64': uidb64, 'token': token}),
bgneal@762:                     args, follow=True)
bgneal@762:             self.assertRedirects(response,
bgneal@762:                     reverse('accounts-password_reset_success'))
bgneal@762:             self.assertEqual(response.status_code, 200)
bgneal@762: 
bgneal@762:             # Check new password
bgneal@762:             u = User.objects.get(username='user1')
bgneal@762:             self.assertTrue(check_password('pw2', u.password))