def test_not_disabled(self): """ An anonymous user who provides the :verify-email: query string or user with is_disabled set to True should be redirected to the home page. An anonymous user who does not should see a 404. A user with is_active set to False should proceed to their destination. """ client = TestClient() user = UserFactory() #Anonymous user resp = client.get(reverse('view_profile')) path = resp.request.get('PATH_INFO') self.assertRedirects(resp, reverse('home')+'?next='+path) # This is ugly, but it is an artifact of the way Django redirects # users who fail the `user_passes_test` decorator. qs = '?verify-email=%s' % user.email next_qs = '?next=' + urlquote('/profile/view/%s' % qs) # Anonymous user navigates to url with :verify-email: in query string resp = client.get(reverse('view_profile') + qs) # Old path + qs is urlquoted and added to the url as the :next: param self.assertRedirects(resp, "http://testserver/" + next_qs) # Active user client.login_user(user) resp = client.get(reverse('view_profile')) self.assertTrue(resp.status_code, 200) #Disabled user user.is_disabled = True user.save() resp = client.get(reverse('view_profile')) self.assertRedirects(resp, "http://testserver/?next=/profile/view/")
def test_not_disabled(self): """ An anonymous user who provides the :verify-email: query string or user with is_disabled set to True should be redirected to the home page. An anonymous user who does not should see a 404. A user with is_active set to False should proceed to their destination. """ client = TestClient() user = UserFactory() #Anonymous user resp = client.get(reverse('view_profile')) path = resp.request.get('PATH_INFO') self.assertRedirects(resp, reverse('home') + '?next=' + path) # This is ugly, but it is an artifact of the way Django redirects # users who fail the `user_passes_test` decorator. qs = '?verify-email=%s' % user.email next_qs = '?next=' + urlquote('/profile/view/%s' % qs) # Anonymous user navigates to url with :verify-email: in query string resp = client.get(reverse('view_profile') + qs) # Old path + qs is urlquoted and added to the url as the :next: param self.assertRedirects(resp, "http://testserver/" + next_qs) # Active user client.login_user(user) resp = client.get(reverse('view_profile')) self.assertTrue(resp.status_code, 200) #Disabled user user.is_disabled = True user.save() resp = client.get(reverse('view_profile')) self.assertRedirects(resp, "http://testserver/?next=/profile/view/")
def test_is_active(self): """ A user with is_active set to False should be redirected to the home page, while a user with is_active set to True should proceed to their destination. """ client = TestClient() user = UserFactory() quoted_email = urllib.quote(user.email) # Active user client.login_user(user) resp = client.get(reverse('saved_search_main')) self.assertTrue(resp.status_code, 200) # Inactive user user.is_active = False user.save() resp = client.get(reverse('saved_search_main')) self.assertRedirects(resp, "http://testserver/?next=/saved-search/view/")
class UserResourceTests(TestCase): def setUp(self): self.user = UserFactory() self.client = TestClient() create_api_key(User, instance=self.user, created=True) self.data = { 'email': '*****@*****.**', 'username': self.user.email, 'api_key': self.user.api_key.key } def make_response(self, data): url = '/api/v1/user/' response = self.client.get(url, data) return response def test_create_new_user(self): response = self.make_response(self.data) content = json.loads(response.content) self.assertEqual(content, { 'user_created': True, 'email': '*****@*****.**' }) self.assertEqual(response.status_code, 200) self.assertEqual(User.objects.count(), 2) user = User.objects.get(email=self.data['email']) def test_no_email(self): self.data['email'] = '' response = self.make_response(self.data) content = json.loads(response.content) self.assertEqual(response.status_code, 200) self.assertEqual(content['email'], 'No email provided') def test_existing_user(self): for email in [self.user.email, self.user.email.upper()]: self.data['email'] = email response = self.make_response(self.data) content = json.loads(response.content) self.assertEqual(response.status_code, 200) self.assertFalse(content['user_created']) self.assertEqual(content['email'].lower(), '*****@*****.**')
class UserResourceTests(TestCase): def setUp(self): self.user = UserFactory() self.client = TestClient() create_api_key(User, instance=self.user, created=True) self.data = {'email': '*****@*****.**', 'username': self.user.email, 'api_key': self.user.api_key.key } def make_response(self, data): url = '/api/v1/user/' response = self.client.get(url, data) return response def test_create_new_user(self): response = self.make_response(self.data) content = json.loads(response.content) self.assertEqual(content, {'user_created':True, 'email':'*****@*****.**'}) self.assertEqual(response.status_code, 200) self.assertEqual(User.objects.count(), 2) user = User.objects.get(email=self.data['email']) def test_no_email(self): self.data['email'] = '' response = self.make_response(self.data) content = json.loads(response.content) self.assertEqual(response.status_code, 200) self.assertEqual(content['email'], 'No email provided') def test_existing_user(self): for email in [self.user.email, self.user.email.upper()]: self.data['email'] = email response = self.make_response(self.data) content = json.loads(response.content) self.assertEqual(response.status_code, 200) self.assertFalse(content['user_created']) self.assertEqual(content['email'].lower(), '*****@*****.**')
class MyProfileViewsTests(TestCase): def setUp(self): super(MyProfileViewsTests, self).setUp() self.user = UserFactory() self.client = TestClient() self.client.login_user(self.user) self.name = PrimaryNameFactory(user=self.user) def test_edit_profile(self): """ Going to the edit_profile view generates a list of existing profile items in the main content section and a list of profile sections that don't have data filled out in the sidebar. """ resp = self.client.get(reverse('view_profile')) soup = BeautifulSoup(resp.content) item_id = Name.objects.all()[0].id # The existing name object should be rendered on the main content # section self.assertIsNotNone(soup.find('tr', id='name-' + str(item_id) + '-item')) # profile-section contains the name of a profile section that has no # information filled out yet and shows up in the sidebar self.assertTrue(soup.findAll('tr', {'class': 'profile-section'})) def test_handle_form_get_new(self): """ Invoking the handle_form view without an id parameter returns an empty form with the correct form id """ resp = self.client.get(reverse('handle_form'), data={'module': 'Name'}) self.assertTemplateUsed(resp, 'myprofile/profile_form.html') soup = BeautifulSoup(resp.content) self.assertEquals(soup.form.attrs['id'], 'profile-unit-form') with self.assertRaises(KeyError): soup.find('input', id='id_name-given_name').attrs['value'] def test_handle_form_get_existing(self): """ Invoking the handle_form view with and id paraemeter returns a form filled out with the corresponding profile/ID combination """ resp = self.client.get(reverse('handle_form'), data={'module': 'Name', 'id': self.name.id}) self.assertTemplateUsed(resp, 'myprofile/profile_form.html') soup = BeautifulSoup(resp.content) self.assertEquals(soup.form.attrs['id'], 'profile-unit-form') self.assertEquals(soup.find('input', id='id_name-given_name') .attrs['value'], 'Alice') self.assertEquals(soup.find('input', id='id_name-family_name') .attrs['value'], 'Smith') self.assertEquals(soup.find('input', id='id_name-primary') .attrs['checked'], 'checked') def test_handle_form_post_new_valid(self): """ Invoking the handle_form view as a POST request for a new item creates that object in the database and returns the item snippet to be rendered on the page. """ resp = self.client.post(reverse('handle_form'), data={'module': 'Name', 'id': 'new', 'given_name': 'Susy', 'family_name': 'Smith'}) self.assertRedirects(resp, reverse('view_profile')) self.assertEqual(Name.objects.filter(given_name='Susy', family_name='Smith').count(), 1) def test_handle_form_post_invalid(self): """ Invoking the handle_form view as a POST request with an invalid form returns the list of form errors. """ resp = self.client.post(reverse('handle_form'), data={'module': 'Name', 'id': 'new', 'given_name': 'Susy'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(json.loads(resp.content), {u'family_name': [u'This field is required.']}) def test_handle_form_post_existing_valid(self): """ Invoking the handle_form view as a POST request for an existing item updates that item and returns the update item snippet. """ resp = self.client.post(reverse('handle_form'), data={'module': 'Name', 'id': self.name.id, 'given_name': 'Susy', 'family_name': 'Smith'}) self.assertRedirects(resp, reverse('view_profile')) self.assertEqual(Name.objects.filter(given_name='Susy', family_name='Smith').count(), 1) def test_handle_form_redirect_summary(self): """ When a user has a summary already if they try to make a new summary handle form should redirect the user to edit the summary they already have. User is only allowed one summary per account. """ summary_instance = SummaryFactory(user=self.user) summary_instance.save() resp = self.client.get(reverse('handle_form'), data={'module': 'Summary'}) self.assertRedirects(resp, reverse( 'handle_form')+'?id=%s&module=Summary' % summary_instance.id) def test_delete_item(self): """ Invoking the delete_item view deletes the item and returns the 'Deleted!' HttpResponse """ resp = self.client.post(reverse('delete_item')+'?item='+str(self.name.id)) self.assertEqual(resp.content, '') self.assertEqual(Name.objects.filter(id=self.name.id).count(), 0) def test_add_duplicate_primary_email(self): """ Attempting to add a secondary email with a value equal to the user's current primary email results in an error. Due to how the instance is constructed, this validation is form-level rather than model-level. """ resp = self.client.post(reverse('handle_form'), data={'module': 'SecondaryEmail', 'id': 'new', 'email': self.user.email}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(json.loads(resp.content), {u'email': [u'This email is already registered.']})
class SavedSearchResourceTests(TestCase): def setUp(self): super(SavedSearchResourceTests, self).setUp() self.user = UserFactory() self.client = TestClient() self.data = {'email':'*****@*****.**', 'url':'www.my.jobs/jobs'} create_api_key(User, instance=self.user, created=True) self.credentials = (self.user.email, self.user.api_key.key) self.r = Replacer() self.r.replace('urllib2.urlopen', return_file) def tearDown(self): self.r.restore() def make_response(self, data): """ The tests in this section use the following block of code a lot. This makes it somewhat easier to make modifications if something must change Inputs: :data: dict containing the data to be posted to the saved search api endpoint :credentials: api user's email and api key; used to authenticate the transaction """ url = '/api/v1/savedsearch/' response = self.client.get(url, data) return response def test_new_search_existing_user(self): for data in [('*****@*****.**', 'www.my.jobs/search?q=django'), ('*****@*****.**', 'www.my.jobs/search?q=python')]: self.data['email'] = data[0] self.data['url'] = data[1] self.data['username'] = self.user.email self.data['api_key'] = self.user.api_key.key response = self.make_response(self.data) self.assertEqual(response.status_code, 200) search = SavedSearch.objects.all()[0] self.assertEqual(search.user, self.user) content = json.loads(response.content) self.assertEqual(len(content), 3) self.assertTrue(content['new_search']) self.assertEqual(SavedSearch.objects.filter(user=self.user).count(), 2) self.data['url'] = 'http://www.my.jobs/jobs' response = self.make_response(self.data) for search in SavedSearch.objects.all(): self.assertTrue('www.my.jobs' in search.notes) def test_new_search_new_user(self): self.data['email'] = '*****@*****.**' self.data['username'] = self.user.email self.data['api_key'] = self.user.api_key.key response = self.make_response(self.data) self.assertEqual(response.status_code, 200) self.assertEqual(SavedSearch.objects.count(), 0) self.assertEqual(User.objects.count(), 1) content = json.loads(response.content) self.assertEqual(content['email'], 'No user with email %s exists' % \ self.data['email']) self.assertEqual(len(content), 1) def test_new_search_secondary_email(self): SecondaryEmail.objects.create(user=self.user, email='*****@*****.**') self.data['email'] = '*****@*****.**' self.data['username'] = self.user.email self.data['api_key'] = self.user.api_key.key response = self.make_response(self.data) self.assertEqual(response.status_code, 200) self.assertEqual(SavedSearch.objects.count(), 1) self.assertEqual(User.objects.count(), 1) search = SavedSearch.objects.all()[0] self.assertEqual(search.user, self.user) self.assertEqual(search.email, '*****@*****.**') content = json.loads(response.content) self.assertEqual(len(content), 3) def test_new_search_invalid_url(self): self.data['url'] = 'google.com' self.data['username'] = self.user.email self.data['api_key'] = self.user.api_key.key response = self.make_response(self.data) self.assertEqual(response.status_code, 200) content = json.loads(response.content) self.assertEqual(len(content), 1) self.assertEqual(content['url'], 'This is not a valid .JOBS feed') self.assertEqual(SavedSearch.objects.count(), 0) def test_new_search_no_url(self): del self.data['url'] self.data['username'] = self.user.email self.data['api_key'] = self.user.api_key.key response = self.make_response(self.data) self.assertEqual(response.status_code, 200) content = json.loads(response.content) self.assertEqual(len(content), 1) self.assertEqual(content['url'], 'No .JOBS feed provided') self.assertEqual(SavedSearch.objects.count(), 0) def test_no_email(self): del self.data['email'] self.data['username'] = self.user.email self.data['api_key'] = self.user.api_key.key response = self.make_response(self.data) self.assertEqual(response.status_code, 200) content = json.loads(response.content) self.assertEqual(len(content), 1) self.assertEqual(content['email'], 'No email provided') self.assertEqual(SavedSearch.objects.count(), 0) def test_no_auth(self): response = self.make_response(self.data) self.assertEqual(response.status_code, 401) self.assertEqual(response.content, '') self.assertEqual(SavedSearch.objects.count(), 0) def test_invalid_auth(self): headers = [(self.user.email, 'invalid_key'), ('*****@*****.**', self.user.api_key.key), ('*****@*****.**', 'invalid_key')] for header in headers: self.data['username'] = header[0] self.data['api_key'] = header[1] response = self.make_response(self.data) self.assertEqual(response.status_code, 401) self.assertEqual(response.content, '') self.assertEqual(SavedSearch.objects.count(), 0) def test_existing_search(self): self.data['username'] = self.user.email self.data['api_key'] = self.user.api_key.key response = self.make_response(self.data) self.assertEqual(response.status_code, 200) content = json.loads(response.content) self.assertEqual(content['new_search'], True) for email in [self.user.email, self.user.email.upper()]: self.data['email'] = email response = self.make_response(self.data) content = json.loads(response.content) self.assertEqual(len(content), 3) self.assertFalse(content['new_search']) self.assertEqual(SavedSearch.objects.count(), 1)
class AccountFormTests(TestCase): def setUp(self): self.user = UserFactory() self.name = PrimaryNameFactory(user=self.user) self.client = TestClient() def test_password_form(self): invalid_data = [ { 'data': {'password': '******', 'new_password1': 'newpassword', 'new_password2': 'newpassword'}, u'errors': [['password', [u"Wrong password."]]]}, { 'data': {'password': '******', 'new_password1': 'newpassword', 'new_password2': 'notnewpassword'}, u'errors': [[u'new_password2', [u'The new password fields did not match.']], [u'new_password1', [u'The new password fields did not match.']]], }, ] for item in invalid_data: form = ChangePasswordForm(user=self.user, data=item['data']) self.failIf(form.is_valid()) self.assertEqual(form.errors[item[u'errors'][0][0]], item[u'errors'][0][1]) form = ChangePasswordForm(user=self.user,data={'password': '******', 'new_password1': 'anothersecret', 'new_password2': 'anothersecret'}) self.failUnless(form.is_valid()) form.save() self.failUnless(self.user.check_password('anothersecret')) def test_no_name_account_form(self): """ Leaving both the first and last name fields blank produces a valid save. It also deletes the primary name object from the Name model. """ data = {"gravatar": "*****@*****.**", "user": self.user} form = EditAccountForm(data, **{'user':self.user}) self.assertTrue(form.is_valid()) form.save(self.user) self.assertEqual(Name.objects.count(), 0) def test_both_names_account_form(self): """ Filling out both name fields produces a valid save. """ data = {"given_name": "Alicia", "family_name": "Smith", "gravatar": "*****@*****.**"} form = EditAccountForm(data, **{'user':self.user}) self.assertTrue(form.is_valid()) def test_partial_name_account_form(self): """ Filling out only the first name or only the last name produces an error. """ data = {"given_name": "Alicia", "gravatar": "*****@*****.**", "user": self.user} form = EditAccountForm(data, **{'user':self.user}) self.assertFalse(form.is_valid()) self.assertEqual(form.errors['family_name'][0], "Both a first and last name required.") def test_gravatar_email_list(self): """ Dropdowns for selecting the user's preferred Gravatar email should be the only dropdowns that include "Do not use Gravatar" as an option - others should default to the user's primary email address. """ self.client.login_user(self.user) response = self.client.get(reverse('edit_communication')) soup = BeautifulSoup(response.content) options = soup.select('#id_digest_email option') self.assertEqual(len(options), 1) self.assertTrue(self.user.gravatar in options[0]) response = self.client.get(reverse('edit_basic')) soup = BeautifulSoup(response.content) options = soup.select('#id_gravatar option') self.assertEqual(len(options), 2) self.assertTrue('Do not use Gravatar' in options[0]) self.assertTrue(self.user.gravatar in options[1])
class RegistrationViewTests(TestCase): """ Test the registration views. """ def setUp(self): """ These tests use the default backend, since we know it's available; that needs to have ``ACCOUNT_ACTIVATION_DAYS`` set. """ super(RegistrationViewTests, self).setUp() self.client = TestClient() self.old_activation = getattr(settings, 'ACCOUNT_ACTIVATION_DAYS', None) if self.old_activation is None: settings.ACCOUNT_ACTIVATION_DAYS = 7 # pragma: no cover self.data = {'email': '*****@*****.**', 'password1': 'swordfish', 'password2': 'swordfish', 'action': 'register'} self.client.post(reverse('home'), data=self.data) self.user = User.objects.get(email=self.data['email']) def test_valid_activation(self): """ Test that the ``activate`` view properly handles a valid activation (in this case, based on the default backend's activation window). """ profile = ActivationProfile.objects.get(user__email=self.user.email) response = self.client.get(reverse('registration_activate', args=[profile.activation_key])) self.assertEqual(response.status_code, 200) self.failUnless(User.objects.get(email=self.user.email).is_active) def test_anonymous_activation(self): """ Test that the ``activate`` view properly handles activation when the user to be activated is not currently logged in. """ self.client.post(reverse('auth_logout')) profile = ActivationProfile.objects.get(user__email=self.user.email) response = self.client.get(reverse('registration_activate', args=[profile.activation_key]) + '?verify-email=%s' % self.user.email) self.assertEqual(response.status_code, 200) self.assertContains(response, self.data['email']) def test_invalid_activation(self): """ Test that the ``activate`` view properly handles an invalid activation (in this case, based on the default backend's activation window). """ expired_profile = ActivationProfile.objects.get(user=self.user) expired_profile.sent -= datetime.timedelta( days=settings.ACCOUNT_ACTIVATION_DAYS) expired_profile.save() response = self.client.get(reverse('registration_activate', args=[expired_profile.activation_key])) self.assertEqual(response.status_code, 200) self.assertNotEqual(response.context['activated'], expired_profile.activation_key_expired()) self.failIf(User.objects.get(email=self.user.email).is_active) def test_resend_activation(self): x, created = User.objects.create_inactive_user( **{'email': '*****@*****.**', 'password1': 'secret'}) self.client.login_user(x) self.assertEqual(len(mail.outbox), 1) resp = self.client.get(reverse('resend_activation')) self.assertEqual(resp.status_code, 200) # one email sent for creating an inactive user, another one for resend self.assertEqual(len(mail.outbox), 2) def test_resend_activation_with_secondary_emails(self): user, created = User.objects.create_inactive_user( **{'email': '*****@*****.**', 'password1': 'secret'}) self.assertEqual(ActivationProfile.objects.count(), 1) self.client.login_user(user) self.client.get(reverse('resend_activation')) self.assertEqual(len(mail.outbox), 2) SecondaryEmail.objects.create(user=user, email='*****@*****.**') self.assertEqual(len(mail.outbox), 3) self.assertEqual(ActivationProfile.objects.count(), 2) self.client.get(reverse('resend_activation')) self.assertEqual(len(mail.outbox), 4)
class MyProfileViewsTests(TestCase): def setUp(self): super(MyProfileViewsTests, self).setUp() self.user = UserFactory() self.client = TestClient() self.client.login_user(self.user) self.name = PrimaryNameFactory(user=self.user) def test_edit_profile(self): """ Going to the edit_profile view generates a list of existing profile items in the main content section and a list of profile sections that don't have data filled out in the sidebar. """ resp = self.client.get(reverse('view_profile')) soup = BeautifulSoup(resp.content) item_id = Name.objects.all()[0].id # The existing name object should be rendered on the main content # section self.assertIsNotNone( soup.find('tr', id='name-' + str(item_id) + '-item')) # profile-section contains the name of a profile section that has no # information filled out yet and shows up in the sidebar self.assertTrue(soup.findAll('tr', {'class': 'profile-section'})) def test_handle_form_get_new(self): """ Invoking the handle_form view without an id parameter returns an empty form with the correct form id """ resp = self.client.get(reverse('handle_form'), data={'module': 'Name'}) self.assertTemplateUsed(resp, 'myprofile/profile_form.html') soup = BeautifulSoup(resp.content) self.assertEquals(soup.form.attrs['id'], 'profile-unit-form') with self.assertRaises(KeyError): soup.find('input', id='id_name-given_name').attrs['value'] def test_handle_form_get_existing(self): """ Invoking the handle_form view with and id paraemeter returns a form filled out with the corresponding profile/ID combination """ resp = self.client.get(reverse('handle_form'), data={ 'module': 'Name', 'id': self.name.id }) self.assertTemplateUsed(resp, 'myprofile/profile_form.html') soup = BeautifulSoup(resp.content) self.assertEquals(soup.form.attrs['id'], 'profile-unit-form') self.assertEquals( soup.find('input', id='id_name-given_name').attrs['value'], 'Alice') self.assertEquals( soup.find('input', id='id_name-family_name').attrs['value'], 'Smith') self.assertEquals( soup.find('input', id='id_name-primary').attrs['checked'], 'checked') def test_handle_form_post_new_valid(self): """ Invoking the handle_form view as a POST request for a new item creates that object in the database and returns the item snippet to be rendered on the page. """ resp = self.client.post(reverse('handle_form'), data={ 'module': 'Name', 'id': 'new', 'given_name': 'Susy', 'family_name': 'Smith' }) self.assertRedirects(resp, reverse('view_profile')) self.assertEqual( Name.objects.filter(given_name='Susy', family_name='Smith').count(), 1) def test_handle_form_post_invalid(self): """ Invoking the handle_form view as a POST request with an invalid form returns the list of form errors. """ resp = self.client.post(reverse('handle_form'), data={ 'module': 'Name', 'id': 'new', 'given_name': 'Susy' }, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(json.loads(resp.content), {u'family_name': [u'This field is required.']}) def test_handle_form_post_existing_valid(self): """ Invoking the handle_form view as a POST request for an existing item updates that item and returns the update item snippet. """ resp = self.client.post(reverse('handle_form'), data={ 'module': 'Name', 'id': self.name.id, 'given_name': 'Susy', 'family_name': 'Smith' }) self.assertRedirects(resp, reverse('view_profile')) self.assertEqual( Name.objects.filter(given_name='Susy', family_name='Smith').count(), 1) def test_handle_form_redirect_summary(self): """ When a user has a summary already if they try to make a new summary handle form should redirect the user to edit the summary they already have. User is only allowed one summary per account. """ summary_instance = SummaryFactory(user=self.user) summary_instance.save() resp = self.client.get(reverse('handle_form'), data={'module': 'Summary'}) self.assertRedirects( resp, reverse('handle_form') + '?id=%s&module=Summary' % summary_instance.id) def test_delete_item(self): """ Invoking the delete_item view deletes the item and returns the 'Deleted!' HttpResponse """ resp = self.client.post( reverse('delete_item') + '?item=' + str(self.name.id)) self.assertEqual(resp.content, '') self.assertEqual(Name.objects.filter(id=self.name.id).count(), 0) def test_add_duplicate_primary_email(self): """ Attempting to add a secondary email with a value equal to the user's current primary email results in an error. Due to how the instance is constructed, this validation is form-level rather than model-level. """ resp = self.client.post(reverse('handle_form'), data={ 'module': 'SecondaryEmail', 'id': 'new', 'email': self.user.email }, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(json.loads(resp.content), {u'email': [u'This email is already registered.']})
class MySignOn(TestCase): def setUp(self): self.user = UserFactory() self.auth_callback_url = 'https://secure.my.jobs/account' self.auth_callback = '?auth_callback=%s' % self.auth_callback_url self.key_qs = '%s&key=%s' self.client = TestClient() self.client.login_user(self.user) def test_anonymous_auth(self): """ Anonymous users must first login before being redirected. This redirection happens automatically if JavaScript is disabled. JSON is returned and the redirect takes place via JavaScript otherwise. """ login_data = {'username': self.user.email, 'password': '******', 'auth_callback': self.auth_callback_url, 'action': 'login'} self.client.logout() self.assertEqual(AuthorizedClient.objects.count(), 0) self.assertTrue(self.client.session.get('key') is None) response = self.client.post(reverse('sso_authorize'), login_data) self.assertEqual(AuthorizedClient.objects.count(), 1) self.assertTrue(self.client.session.get('key') is not None) self.assertEqual(response.get('Location'), self.auth_callback_url + '?key=%s' % self.client.session.get('key')) self.client.logout() response = self.client.post(reverse('sso_authorize'), login_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') content = json.loads(response.content) self.assertEqual(content['url'], self.auth_callback_url + '?key=%s' % self.client.session['key']) def test_authenticated_auth(self): """ Users who are already logged in can simply click a button to authorize a given site and are then redirected to the given callback url. If a given user has already authorized a given site and that site provides the key that it was given, this redirect is automatic. """ self.assertEqual(AuthorizedClient.objects.count(), 0) self.assertTrue(self.client.session.get('key') is None) response = self.client.post(reverse('sso_authorize'), {'auth_callback': self.auth_callback_url, 'action': 'authorize'}) self.assertEqual(self.user.authorizedclient_set.count(), 1) self.assertTrue(self.client.session.get('key') is not None) self.assertEqual(response.get('Location'), self.auth_callback_url + '?key=%s' % self.client.session.get('key')) good_qs = self.key_qs % (self.auth_callback, self.client.session.get('key')) response = self.client.get(reverse('sso_authorize') + good_qs) self.assertEqual(response.get('Location'), self.auth_callback_url + '?key=%s' % self.client.session.get('key')) def test_bad_key(self): """ Providing a bad key will always cause the user to have to log back into their account. Bad keys are defined as providing a key that doesn't match the user's current key or providing a key when the user doesn't currently have a key defined. """ # no key no_key = self.key_qs % (self.auth_callback, AuthorizedClient.create_key(self.user)) response = self.client.get(reverse('sso_authorize') + no_key) self.assertEqual(AuthorizedClient.objects.count(), 0) # Ensure that user was logged out response = self.client.get(reverse('view_profile')) path = response.request.get('PATH_INFO') self.assertRedirects(response, reverse('home')+'?next='+path) # wrong key self.client.login_user(self.user) session = self.client.session session['key'] = AuthorizedClient.create_key(self.user) session.save() # key is a hex string; we can invalidate it by taking a substring wrong_key = self.key_qs % (self.auth_callback, AuthorizedClient.create_key(self.user)[:-1]) AuthorizedClientFactory(user=self.user) response = self.client.get(reverse('sso_authorize') + wrong_key) # Ensure that user was logged out again response = self.client.get(reverse('view_profile')) path = response.request.get('PATH_INFO') self.assertRedirects(response, reverse('home')+'?next='+path)
class RegistrationViewTests(TestCase): """ Test the registration views. """ def setUp(self): """ These tests use the default backend, since we know it's available; that needs to have ``ACCOUNT_ACTIVATION_DAYS`` set. """ super(RegistrationViewTests, self).setUp() self.client = TestClient() self.old_activation = getattr(settings, 'ACCOUNT_ACTIVATION_DAYS', None) if self.old_activation is None: settings.ACCOUNT_ACTIVATION_DAYS = 7 # pragma: no cover self.data = { 'email': '*****@*****.**', 'password1': 'swordfish', 'password2': 'swordfish', 'action': 'register' } self.client.post(reverse('home'), data=self.data) self.user = User.objects.get(email=self.data['email']) def test_valid_activation(self): """ Test that the ``activate`` view properly handles a valid activation (in this case, based on the default backend's activation window). """ profile = ActivationProfile.objects.get(user__email=self.user.email) response = self.client.get( reverse('registration_activate', args=[profile.activation_key])) self.assertEqual(response.status_code, 200) self.failUnless(User.objects.get(email=self.user.email).is_active) def test_anonymous_activation(self): """ Test that the ``activate`` view properly handles activation when the user to be activated is not currently logged in. """ self.client.post(reverse('auth_logout')) profile = ActivationProfile.objects.get(user__email=self.user.email) response = self.client.get( reverse('registration_activate', args=[profile.activation_key]) + '?verify-email=%s' % self.user.email) self.assertEqual(response.status_code, 200) self.assertContains(response, self.data['email']) def test_invalid_activation(self): """ Test that the ``activate`` view properly handles an invalid activation (in this case, based on the default backend's activation window). """ expired_profile = ActivationProfile.objects.get(user=self.user) expired_profile.sent -= datetime.timedelta( days=settings.ACCOUNT_ACTIVATION_DAYS) expired_profile.save() response = self.client.get( reverse('registration_activate', args=[expired_profile.activation_key])) self.assertEqual(response.status_code, 200) self.assertNotEqual(response.context['activated'], expired_profile.activation_key_expired()) self.failIf(User.objects.get(email=self.user.email).is_active) def test_resend_activation(self): x, created = User.objects.create_inactive_user(**{ 'email': '*****@*****.**', 'password1': 'secret' }) self.client.login_user(x) self.assertEqual(len(mail.outbox), 1) resp = self.client.get(reverse('resend_activation')) self.assertEqual(resp.status_code, 200) # one email sent for creating an inactive user, another one for resend self.assertEqual(len(mail.outbox), 2) def test_resend_activation_with_secondary_emails(self): user, created = User.objects.create_inactive_user(**{ 'email': '*****@*****.**', 'password1': 'secret' }) self.assertEqual(ActivationProfile.objects.count(), 1) self.client.login_user(user) self.client.get(reverse('resend_activation')) self.assertEqual(len(mail.outbox), 2) SecondaryEmail.objects.create(user=user, email='*****@*****.**') self.assertEqual(len(mail.outbox), 3) self.assertEqual(ActivationProfile.objects.count(), 2) self.client.get(reverse('resend_activation')) self.assertEqual(len(mail.outbox), 4)
class MySearchViewTests(TestCase): def setUp(self): super(MySearchViewTests, self).setUp() self.client = TestClient() self.user = UserFactory() self.client.login_user(self.user) self.new_form_data = { 'url': 'www.my.jobs/jobs', 'feed': 'http://www.my.jobs/jobsfeed/rss?', 'label': 'Jobs Label', 'email': self.user.email, 'frequency': 'D', 'is_active': 'True', 'sort_by': 'Relevance', } self.new_digest_data = { 'is_active': 'True', 'user': self.user, 'email': self.user.email, 'frequency': 'M', 'day_of_month': 1, } self.new_form = forms.SavedSearchForm(user=self.user, data=self.new_form_data) self.r = Replacer() self.r.replace('urllib2.urlopen', return_file) def tearDown(self): self.r.restore() def test_search_main(self): response = self.client.get(reverse('saved_search_main')) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'mysearches/saved_search_main.html') self.failUnless(isinstance(response.context['form'], forms.DigestForm)) self.failUnless(isinstance(response.context['add_form'], forms.SavedSearchForm)) def test_save_new_search_form(self): response = self.client.post(reverse('save_search_form'), data=self.new_form_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) self.assertEqual(response.content, '') def test_save_new_search_invalid(self): del self.new_form_data['frequency'] response = self.client.post(reverse('save_search_form'), data=self.new_form_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content).keys(), ['frequency']) def test_get_edit_page(self): self.new_form.save() search_id = self.new_form.instance.id response = self.client.get( reverse('edit_search')+'?id=%s' % search_id) self.assertEqual(response.status_code, 200) self.assertEqual(self.new_form.instance, response.context['form'].instance) self.assertTemplateUsed(response, 'mysearches/saved_search_edit.html') search_id += 1 response = self.client.get( reverse('edit_search')+'id=%s' % search_id) self.assertEqual(response.status_code, 404) def test_save_edit_form(self): self.new_form.save() search_id = self.new_form.instance.id self.new_form_data['frequency'] = 'W' self.new_form_data['day_of_week'] = 1 self.new_form_data['url'] = 'www.my.jobs/search?' self.new_form_data['search_id'] = search_id new_form = forms.SavedSearchForm(user=self.user, data=self.new_form_data) response = self.client.post(reverse('save_search_form'), data=self.new_form_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) self.assertEqual(response.content, '') del self.new_form_data['frequency'] response = self.client.post(reverse('save_search_form'), data=self.new_form_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content).keys(), ['frequency']) def test_validate_url(self): response = self.client.post(reverse('validate_url'), data={'url': self.new_form_data['url']}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) data = {'rss_url': 'http://www.my.jobs/jobs/feed/rss', 'feed_title': 'Jobs', 'url_status': 'valid'} self.assertEqual(json.loads(response.content), data) response = self.client.post(reverse('validate_url'), data={'url': 'google.com'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content), {'url_status': 'not valid'}) def test_save_digest_form(self): response = self.client.post(reverse('save_digest_form'), self.new_digest_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) self.assertEqual(response.content, '') del self.new_digest_data['email'] response = self.client.post(reverse('save_digest_form'), self.new_digest_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) self.assertEqual(response.content, '{"email": ["This field is required."]}') def test_unsubscribe_owned_search(self): """ Unsubscribing an owned saved search should result in that search being deactivated """ search = SavedSearchFactory(user=self.user) self.assertTrue(search.is_active) response = self.client.get(reverse('unsubscribe')+'?id=%s' % search.id) search = models.SavedSearch.objects.get(id=search.id) self.assertFalse(search.is_active) self.assertTemplateUsed(response, 'mysearches/saved_search_disable.html') def test_unsubscribe_unowned_search(self): """ Attempting to unsubscribe using a search that isn't yours should result in nothing happening to the search """ user = UserFactory(email='*****@*****.**') search = SavedSearchFactory(user=user) response = self.client.get(reverse('unsubscribe')+'?id=%s' % search.id) search = models.SavedSearch.objects.get(id=search.id) self.assertTrue(search.is_active) self.assertEqual(response.status_code, 404) def test_unsubscribe_digest(self): """ Unsubscribing a saved search digest should result in all of the owner's saved searches being disabled """ digest = SavedSearchDigestFactory(user=self.user) searches = [] for url in ['www.my.jobs/search?q=python', 'jobs.jobs/search?q=django']: searches.append(SavedSearchFactory(url=url, user=self.user)) for search in searches: self.assertTrue(search.is_active) response = self.client.get(reverse('unsubscribe')+'?id=digest') searches = list(models.SavedSearch.objects.all()) for search in searches: self.assertFalse(search.is_active) self.assertTemplateUsed(response, 'mysearches/saved_search_disable.html') self.assertEqual(response.status_code, 200) def test_anonymous_unsubscribe(self): search = SavedSearchFactory(user=self.user) Session.objects.all().delete() # Navigating to the 'unsubscribe' page while logged out... response = self.client.get( reverse('unsubscribe')+'?id='+str(search.id)) path = response.request.get('PATH_INFO') + "?id=" + str(search.id) self.assertRedirects(response, reverse('home')+'?next='+path) # or with the wrong email address... response = self.client.get( reverse('unsubscribe') + '?id='+str( search.id)+'&[email protected]') # results in being redirected to the login page and the searches # remaining unchanged self.assertRedirects(response, reverse('home')) search = models.SavedSearch.objects.get(id=search.id) self.assertTrue(search.is_active) response = self.client.get( reverse('unsubscribe') + '?id=%s&verify-email=%s' % ( search.id, self.user.email)) search = models.SavedSearch.objects.get(id=search.id) self.assertFalse(search.is_active) def test_delete_owned_search(self): search = SavedSearchFactory(user=self.user) self.assertEqual(models.SavedSearch.objects.count(), 1) response = self.client.get( reverse('delete_saved_search')+'?id=%s' % search.id) self.assertEqual(models.SavedSearch.objects.count(), 0) self.assertRedirects(response, reverse( 'saved_search_main_query')+'?d='+str(urllib2.quote( search.label.title()))) def test_delete_unowned_search(self): """ Attempting to delete a search that isn't yours should result in nothing happening to the search """ user = UserFactory(email='*****@*****.**') search = SavedSearchFactory(user=user) response = self.client.get( reverse('delete_saved_search')+'?id=%s' % search.id) self.assertEqual(models.SavedSearch.objects.count(), 1) self.assertEqual(response.status_code, 404) def test_delete_owned_searches_by_digest(self): """ Deleting with a saved search digest should result in all of the user's saved searches being deleted """ digest = SavedSearchDigestFactory(user=self.user) searches = [] for url in ['www.my.jobs/search?q=python', 'jobs.jobs/search?q=django']: searches.append(SavedSearchFactory(url=url, user=self.user)) self.assertEqual(models.SavedSearch.objects.count(), 2) response = self.client.get(reverse('delete_saved_search')+'?id=digest') self.assertEqual(models.SavedSearch.objects.count(), 0) self.assertRedirects(response, reverse( 'saved_search_main_query')+'?d=all') def test_anonymous_delete_searches(self): search = SavedSearchFactory(user=self.user) Session.objects.all().delete() # Navigating to the 'delete saved search' page while logged out... response = self.client.get( reverse('delete_saved_search')+'?id='+str(search.id)) path = response.request.get('PATH_INFO') + "?id=" + str(search.id) self.assertRedirects(response, reverse('home')+'?next='+path) self.assertEqual(models.SavedSearch.objects.count(), 1) # or with the wrong email address... response = self.client.get( reverse('delete_saved_search')+'?id='+str( search.id)+'&[email protected]') # results in being redirected to the login page and no searches being # deleted self.assertRedirects(response, reverse('home')) self.assertEqual(models.SavedSearch.objects.count(), 1) response = self.client.get( reverse('delete_saved_search')+'?id=%s&verify-email=%s' % ( search.id, self.user.email)) self.assertEqual(models.SavedSearch.objects.count(), 0) # assertRedirects follows any redirect and waits for a 200 status code; # anonymous users will always redirect, never returning a 200. self.client.login_user(self.user) self.assertRedirects(response, reverse( 'saved_search_main_query')+'?d='+str(urllib2.quote( search.label.title())))
class AccountFormTests(TestCase): def setUp(self): self.user = UserFactory() self.name = PrimaryNameFactory(user=self.user) self.client = TestClient() def test_password_form(self): invalid_data = [ { 'data': { 'password': '******', 'new_password1': 'newpassword', 'new_password2': 'newpassword' }, u'errors': [['password', [u"Wrong password."]]] }, { 'data': { 'password': '******', 'new_password1': 'newpassword', 'new_password2': 'notnewpassword' }, u'errors': [[ u'new_password2', [u'The new password fields did not match.'] ], [ u'new_password1', [u'The new password fields did not match.'] ]], }, ] for item in invalid_data: form = ChangePasswordForm(user=self.user, data=item['data']) self.failIf(form.is_valid()) self.assertEqual(form.errors[item[u'errors'][0][0]], item[u'errors'][0][1]) form = ChangePasswordForm(user=self.user, data={ 'password': '******', 'new_password1': 'anothersecret', 'new_password2': 'anothersecret' }) self.failUnless(form.is_valid()) form.save() self.failUnless(self.user.check_password('anothersecret')) def test_no_name_account_form(self): """ Leaving both the first and last name fields blank produces a valid save. It also deletes the primary name object from the Name model. """ data = {"gravatar": "*****@*****.**", "user": self.user} form = EditAccountForm(data, **{'user': self.user}) self.assertTrue(form.is_valid()) form.save(self.user) self.assertEqual(Name.objects.count(), 0) def test_both_names_account_form(self): """ Filling out both name fields produces a valid save. """ data = { "given_name": "Alicia", "family_name": "Smith", "gravatar": "*****@*****.**" } form = EditAccountForm(data, **{'user': self.user}) self.assertTrue(form.is_valid()) def test_partial_name_account_form(self): """ Filling out only the first name or only the last name produces an error. """ data = { "given_name": "Alicia", "gravatar": "*****@*****.**", "user": self.user } form = EditAccountForm(data, **{'user': self.user}) self.assertFalse(form.is_valid()) self.assertEqual(form.errors['family_name'][0], "Both a first and last name required.") def test_gravatar_email_list(self): """ Dropdowns for selecting the user's preferred Gravatar email should be the only dropdowns that include "Do not use Gravatar" as an option - others should default to the user's primary email address. """ self.client.login_user(self.user) response = self.client.get(reverse('edit_communication')) soup = BeautifulSoup(response.content) options = soup.select('#id_digest_email option') self.assertEqual(len(options), 1) self.assertTrue(self.user.gravatar in options[0]) response = self.client.get(reverse('edit_basic')) soup = BeautifulSoup(response.content) options = soup.select('#id_gravatar option') self.assertEqual(len(options), 2) self.assertTrue('Do not use Gravatar' in options[0]) self.assertTrue(self.user.gravatar in options[1])