def test_not_disabled(self): """ An anonymous user who provides the :verify: 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(email="*****@*****.**") # 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=%s' % user.user_guid next_qs = '?next=' + urlquote('/profile/view/%s' % qs) # Anonymous user navigates to url with :verify: 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/")
class SavedSearchTemplateTagTests(MyJobsBase): def setUp(self): super(SavedSearchTemplateTagTests, self).setUp() self.user = UserFactory(is_active=True) self.search = SavedSearchFactory(user=self.user) def test_confirm_creation_active_user(self): expected = reverse('view_full_feed') + '?id={id}&verify={guid}'.format( id=self.search.pk, guid=self.user.user_guid) actual = get_created_url(self.search) self.assertEqual(actual, expected) def test_confirm_creation_inactive_user(self): self.user.is_active = False self.user.save() user_with_profile = UserFactory(email='*****@*****.**', is_active=False) profile_search = SavedSearchFactory(user=user_with_profile) ActivationProfile.objects.create(user=user_with_profile, email=user_with_profile.email) for saved_search in [self.search, profile_search]: actual = get_created_url(saved_search) profile = ActivationProfile.objects.get(user=saved_search.user) expected = reverse('registration_activate', args=[profile.activation_key]) + \ '?verify={guid}'.format(guid=saved_search.user.user_guid) self.assertEqual(actual, expected)
def test_search_updates_facet_counts(self): # Add ProfileData to the candidate_user EducationFactory(user=self.candidate_user) AddressFactory(user=self.candidate_user) LicenseFactory(user=self.candidate_user) self.candidate_user.save() # Create a new user with ProfileData user = UserFactory(email="*****@*****.**") SavedSearchFactory(user=user, url='http://test.jobs/search?q=python', feed='http://test.jobs/jobs/feed/rss?', label='Python Jobs') EducationFactory(user=user) AddressFactory(user=user) LicenseFactory(user=user) user.save() update_solr_task(settings.TEST_SOLR_INSTANCE) # Assert there are two users with country codes country_tag = '#Country-details-table .facet-count' q = '?company={company}' q = q.format(company=str(self.company.id)) response = self.client.post(reverse('dashboard') + q) soup = BeautifulSoup(response.content) self.assertEqual(int(soup.select(country_tag)[0].text), 2) # When we search, the facet count updates. q = '?company={company}&search={search}' q = q.format(company=str(self.company.id), search='find') response = self.client.post(reverse('dashboard') + q) soup = BeautifulSoup(response.content) self.assertEqual(int(soup.select(country_tag)[0].text), 1)
def test_not_disabled(self): """ An anonymous user who provides the :verify: 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=%s' % user.user_guid next_qs = '?next=' + urlquote('/profile/view/%s' % qs) # Anonymous user navigates to url with :verify: 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/")
class NewUserTests(SeleniumTestCase): """Tests Account creation""" def setUp(self): self.user = UserFactory(first_name="John", last_name="Doe") def test_home_page_works(self): """ As John, navigating to https://secure.my.jobs should send me to a page titled "My.jobs". """ self.browser.get(self.live_server_url) self.assertIn(self.browser.title, 'My.jobs') def test_cant_log_in_without_account(self): """ As John, I shouldn't be able to log into My.jobs without registering first. """ self.browser.get('/'.join([self.live_server_url, 'prm', 'view'])) # attempt to log in username = self.find('id_username') username.send_keys(self.user.email) self.find('id_password').send_keys(self.user.password) self.find('login').click() self.assertEqual(username.get_attribute('placeholder'), 'Please enter a correct email.') def test_user_registration(self): """ As John, I should be able to register on My.jobs and log in. """ user = UserFactory.build(email='*****@*****.**') self.browser.get('/'.join([self.live_server_url, 'prm', 'view'])) # register self.find('id_email').send_keys(user.email) self.find('id_password1').send_keys(user.password) self.find('id_password2').send_keys(user.password) self.find('register').click() self.assertEqual(self.find('profile').get_attribute( 'innerHTML'), 'Skip: Take me to my profile') def test_user_login(self): self.user.set_password("test") self.user.save() self.find('id_username').send_keys(self.user.email) self.find('id_password').send_keys("test") self.find('login').click()
def test_presave_ignore(self): user = UserFactory(email="*****@*****.**") update_solr_task(self.test_solr) user.last_login = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC) user.save() self.assertEqual(Update.objects.all().count(), 0) user.last_login = datetime.datetime(2013, 8, 15, 8, 15, 12, 0, pytz.UTC) user.email = "[email protected]" user.save() self.assertEqual(Update.objects.all().count(), 1)
class RedirectMiddlewareTests(MyJobsBase): def setUp(self): super(RedirectMiddlewareTests, self).setUp() self.user = UserFactory() self.redirect_middleware = PasswordChangeRedirectMiddleware() self.request_factory = RequestFactory() def test_logged_in_no_redirect(self): """ A logged in user whose password_change flag is not set should proceed to their original destination """ request = self.request_factory.get(reverse('edit_account')) request.user = self.user response = self.redirect_middleware.process_request(request) self.assertEqual(response, None) def test_logged_in_autocreated_user_redirects(self): """ A logged in user whose password_change flag is set should be redirected to the password change form """ self.user.password_change = True self.user.save() request = self.request_factory.get(reverse('saved_search_main')) request.user = self.user response = self.redirect_middleware.process_request(request) self.assertEqual(response.status_code, 302) self.assertEqual(response.get('location'), reverse('edit_account')+"#as-password") def test_not_logged_in_returns_forbidden(self): """ An anonymous user that tries to post to a private url should receive a 403 Forbidden status """ request = self.request_factory.get(reverse('saved_search_main'), HTTP_X_REQUESTED_WITH='XMLHttpRequest') new_request = request.GET.copy() new_request['next'] = reverse('home') request.GET = new_request request.REQUEST.dicts = (new_request, request.POST) request.user = AnonymousUser() response = self.redirect_middleware.process_request(request) self.assertEqual(response.status_code, 403)
class RedirectMiddlewareTests(TestCase): def setUp(self): self.user = UserFactory() self.redirect_middleware = RedirectMiddleware() self.request_factory = RequestFactory() def test_logged_in_no_redirect(self): """ A logged in user whose password_change flag is not set should proceed to their original destination """ request = self.request_factory.get(reverse('edit_account')) request.user = self.user response = self.redirect_middleware.process_request(request) self.assertEqual(response, None) def test_logged_in_autocreated_user_redirects(self): """ A logged in user whose password_change flag is set should be redirected to the password change form """ self.user.password_change = True self.user.save() request = self.request_factory.get(reverse('saved_search_main')) request.user = self.user response = self.redirect_middleware.process_request(request) self.assertEqual(response.status_code, 302) self.assertEqual(response.get('location'), reverse('edit_account')) def test_not_logged_in_returns_forbidden(self): """ An anonymous user that tries to post to a private url should receive a 403 Forbidden status """ request = self.request_factory.get(reverse('saved_search_main'), HTTP_X_REQUESTED_WITH='XMLHttpRequest') new_request = request.GET.copy() new_request['next'] = reverse('home') request.GET = new_request request.REQUEST.dicts = (new_request, request.POST) request.user = AnonymousUser() response = self.redirect_middleware.process_request(request) self.assertEqual(response.status_code, 403)
def test_search_domain(self): """We should be able to search for domain.""" user = UserFactory(email="*****@*****.**") SavedSearchFactory(user=user, url='http://test.jobs/search?q=python', feed='http://test.jobs/jobs/feed/rss?', label='Python Jobs') user.save() update_solr_task(settings.TEST_SOLR_INSTANCE) q = '?company={company}&search={search}' q = q.format(company=str(self.company.id), search='shouldWork.com') url = reverse('dashboard') + q response = self.client.post(url) soup = BeautifulSoup(response.content) self.assertEqual(len(soup.select('#row-link-table tr')), 1)
def test_inactive_user_sees_message(self): """ A user with is_verified or is_active set to False should see an activation message instead of the content they were originally meaning to see. """ client = TestClient(path=reverse('saved_search_main')) user = UserFactory() # Active user client.login_user(user) resp = client.get() self.assertIn('Saved Search', resp.content) # Inactive user user.is_verified= False user.save() resp = client.get() self.assertIn('unavailable', resp.content)
def test_inactive_user_sees_message(self): """ A user with is_verified or is_active set to False should see an activation message instead of the content they were originally meaning to see. """ client = TestClient(path=reverse('saved_search_main')) user = UserFactory(email="*****@*****.**") # Active user client.login_user(user) resp = client.get() self.assertIn('Saved Search', resp.content) # Inactive user user.is_verified = False user.save() resp = client.get() self.assertIn('unavailable', resp.content)
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 AdminTests(MyJobsBase): def setUp(self): super(AdminTests, self).setUp() self.site = AdminSite() settings.SITE = SeoSite.objects.first() self.request = RequestFactory().get("/") self.user = UserFactory(is_superuser=True) self.request.user = self.user def test_admin_request_form(self): """ The forms used by ValueEventAdmin and CronEventAdmin should have the current request as attributes. """ for Admin, Model in [(ValueEventAdmin, ValueEvent), (CronEventAdmin, CronEvent)]: admin = Admin(Model, self.site) form = admin.get_form(self.request)() self.assertEqual(form.request, self.request) def test_non_superuser_form(self): """ The email_template queryset should have an appropriate WHERE clause if the current user is not a company user. """ company = CompanyUserFactory(user=self.user).company admin = ValueEventAdmin(ValueEvent, self.site) for superuser in [True, False]: self.user.is_superuser = superuser self.user.save() form = admin.get_form(self.request)() email_template = form.fields["email_template"] query = str(email_template.queryset.query) if superuser: self.assertFalse("WHERE" in query) else: if connection.vendor == "sqlite": test = 'WHERE ("myemails_emailtemplate"."owner_id" = %s' else: test = "WHERE (`myemails_emailtemplate`.`owner_id` = %s" self.assertTrue(test % company.pk in query)
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/")
def test_group_status(self): """ Should return True if user.groups contains the group specified and False if it does not. """ user = UserFactory() user.groups.all().delete() for group in Group.objects.all(): # Makes a list of all group names, excluding the one that the # user will be a member of names = map(lambda group: group.name, Group.objects.filter(~Q(name=group.name))) user.groups.add(group.pk) user.save() for name in names: self.assertFalse(User.objects.is_group_member(user, name)) self.assertTrue(User.objects.is_group_member(user, group.name)) user.groups.all().delete()
def test_group_status(self): """ Should return True if user.groups contains the group specified and False if it does not. """ client = TestClient() user = UserFactory() user.groups.all().delete() for group in Group.objects.all(): # Makes a list of all group names, excluding the one that the # user will be a member of names = map(lambda group: group.name, Group.objects.filter(~Q(name=group.name))) user.groups.add(group.pk) user.save() for name in names: self.assertFalse(User.objects.is_group_member(user, name)) self.assertTrue(User.objects.is_group_member(user, group.name)) user.groups.all().delete()
class SavedSearchModelsTests(MyJobsBase): def setUp(self): super(SavedSearchModelsTests, self).setUp() self.user = UserFactory() self.patcher = patch('urllib2.urlopen', return_file()) self.mock_urlopen = self.patcher.start() def tearDown(self): super(SavedSearchModelsTests, self).tearDown() try: self.patcher.stop() except RuntimeError: # patcher was stopped in a test pass def test_send_search_email(self): SavedSearchDigestFactory(user=self.user, is_active=False) search = SavedSearchFactory(user=self.user, is_active=True, frequency='D', url='www.my.jobs/jobs?q=new+search') send_search_digests() self.assertEqual(len(mail.outbox), 1) self.assertEqual(SavedSearchLog.objects.count(), 1) log = SavedSearchLog.objects.get() self.assertTrue(log.was_sent) email = mail.outbox.pop() self.assertEqual(email.from_email, 'My.jobs Saved Search <*****@*****.**>') self.assertEqual(email.to, [self.user.email]) self.assertEqual(email.subject, search.label) self.assertTrue("table" in email.body) self.assertTrue(email.to[0] in email.body) self.assertNotEqual(email.body.find(search.url), -1, "Search url was not found in email body") self.assertTrue("Your resume is %s%% complete" % self.user.profile_completion in email.body) def test_send_search_digest_email(self): SavedSearchDigestFactory(user=self.user) send_search_digests() self.assertEqual(len(mail.outbox), 0) self.assertEqual(SavedSearchLog.objects.count(), 1) log = SavedSearchLog.objects.get() self.assertTrue('No saved searches' in log.reason) self.assertFalse(log.was_sent) search1 = SavedSearchFactory(user=self.user) self.assertIsNone(SavedSearch.objects.get(pk=search1.pk).last_sent) send_search_digests() self.assertIsNotNone(SavedSearch.objects.get(pk=search1.pk).last_sent) self.assertEqual(len(mail.outbox), 1) self.assertEqual(SavedSearchLog.objects.count(), 2) log = SavedSearchLog.objects.last() self.assertTrue(log.was_sent) search2 = SavedSearchFactory(user=self.user) self.assertIsNone(SavedSearch.objects.get(pk=search2.pk).last_sent) send_search_digests() self.assertIsNotNone(SavedSearch.objects.get(pk=search2.pk).last_sent) self.assertEqual(len(mail.outbox), 2) self.assertEqual(SavedSearchLog.objects.count(), 3) log = SavedSearchLog.objects.last() self.assertTrue(log.was_sent) email = mail.outbox.pop() self.assertEqual(email.from_email, 'My.jobs Saved Search <*****@*****.**>') self.assertEqual(email.to, [self.user.email]) self.assertEqual(email.subject, "Your Saved Search Digest") self.assertTrue("table" in email.body) self.assertTrue(email.to[0] in email.body) def test_send_search_digest_send_if_none(self): SavedSearchDigestFactory(user=self.user, send_if_none=True) send_search_digests() self.assertEqual(len(mail.outbox), 0) SavedSearchFactory(user=self.user) send_search_digests() self.assertEqual(len(mail.outbox), 1) def test_initial_email(self): search = SavedSearchFactory(user=self.user, is_active=False, url='www.my.jobs/search?q=new+search') search.initial_email() self.assertEqual(len(mail.outbox), 1) self.assertEqual(SavedSearchLog.objects.count(), 1) log = SavedSearchLog.objects.get() self.assertTrue('Jobs are not sent' in log.reason) self.assertTrue(log.was_sent) email = mail.outbox.pop() self.assertEqual(email.from_email, 'My.jobs Saved Search <*****@*****.**>') self.assertEqual(email.to, [self.user.email]) self.assertEqual("My.jobs New Saved Search" in email.subject, True) self.assertTrue("table" in email.body) self.assertTrue(email.to[0] in email.body) # Search url appears twice - once displaying the saved search source # and once in the view link. self.assertEqual(email.body.count(search.url), 2) def test_send_update_email(self): search = SavedSearchFactory(user=self.user, is_active=False, url='www.my.jobs/search?q=new+search') search.send_update_email('Your search is updated') self.assertEqual(len(mail.outbox), 1) log = SavedSearchLog.objects.get() self.assertTrue('Jobs are not sent' in log.reason) self.assertTrue(log.was_sent) email = mail.outbox.pop() self.assertEqual(email.from_email, 'My.jobs Saved Search <*****@*****.**>') self.assertEqual(email.to, [self.user.email]) self.assertEqual("My.jobs Saved Search Updated" in email.subject, True) self.assertTrue("table" in email.body) self.assertTrue("Your search is updated" in email.body) self.assertTrue(email.to[0] in email.body) def test_saved_search_all_jobs_link(self): search = SavedSearchFactory(user=self.user) search.send_email() email = mail.outbox.pop() # When search.url does not start with my.jobs, use it as the all jobs # link self.assertFalse(search.url.startswith('http://my.jobs')) self.assertNotEqual(email.body.find(search.url), -1) # When search.url starts with my.jobs, strip /feed/rss from search.feed # if it exists and use that as the all jobs link search.url = 'http://my.jobs/' + '1'*32 search.save() search.send_email() email = mail.outbox.pop() self.assertEqual(email.body.find(search.url), -1) self.assertNotEqual(email.body.find(search.feed.replace('/feed/rss', '')), -1) def assert_modules_in_hrefs(self, modules): """ Assert that each module in :modules: is in the set of HTML elements matched by li > a in an email """ email = mail.outbox.pop() soup = BeautifulSoup(email.body) lis = soup.findAll('li') # .attrs is a dictionary, where the key is the attribute hrefs = [li.find('a').attrs['href'] for li in lis] self.assertEqual(len(hrefs), len(modules)) # We can do self because the list of modules in settings and the list # of recommendations should be in the same order mapping = zip(modules, hrefs) for pair in mapping: # Saved search emails should have one li per required profile unit # that the owner does not currently have self.assertTrue(pair[0] in pair[1].lower()) def test_email_profile_completion(self): search = SavedSearchFactory(user=self.user) search.send_email() self.assertEqual(len(settings.PROFILE_COMPLETION_MODULES), 6) self.assert_modules_in_hrefs(settings.PROFILE_COMPLETION_MODULES) PrimaryNameFactory(user=self.user) search.send_email() new_modules = [module for module in settings.PROFILE_COMPLETION_MODULES if module != 'name'] self.assertEqual(len(new_modules), 5) self.assert_modules_in_hrefs(new_modules) def test_email_contains_activate_link(self): search = SavedSearchFactory(user=self.user) self.assertTrue(self.user.is_active) search.send_email() email = mail.outbox.pop() self.assertFalse('activate your account' in email.body) self.user.is_active = False self.user.save() search.send_email() email = mail.outbox.pop() self.assertTrue('activate your account' in email.body) def test_fix_fixable_search(self): self.patcher.stop() SavedSearchDigestFactory(user=self.user) search = SavedSearchFactory(user=self.user, feed='') self.assertFalse(search.feed) # Celery raises a retry that makes the test fail. In reality # everything is fine, so ignore the retry-fail. try: send_search_digests() except RetryTaskError: pass self.assertEqual(len(mail.outbox), 0) search = SavedSearch.objects.get(pk=search.pk) self.assertTrue(search.is_active) self.assertTrue(search.feed) def test_disable_bad_search(self): self.patcher.stop() SavedSearchDigestFactory(user=self.user) search = SavedSearchFactory(user=self.user, feed='', url='http://example.com') self.assertFalse(search.feed) # Celery raises a retry that makes the test fail. In reality # everything is fine, so ignore the retry-fail. try: send_search_digests() except RetryTaskError: pass email = mail.outbox.pop() search = SavedSearch.objects.get(pk=search.pk) self.assertFalse(search.is_active) self.assertTrue('has failed URL validation' in email.body) def test_get_unsent_jobs(self): """ When sending a saved search email, we should retrieve all new jobs since last send, not all new jobs based on frequency. """ self.patcher.stop() self.patcher = patch('urllib2.urlopen', return_file(time_=datetime.datetime.now() - datetime.timedelta(days=3))) self.mock_urlopen = self.patcher.start() last_sent = datetime.datetime.now() - datetime.timedelta(days=3) search = SavedSearchFactory(frequency='D', last_sent=last_sent, user=self.user, email=self.user.email) search.send_email() self.assertEqual(len(mail.outbox), 1) def test_inactive_user_receives_saved_search(self): self.assertEqual(len(mail.outbox), 0) self.user.is_active = False self.user.save() saved_search = SavedSearchFactory(user=self.user) saved_search.send_email() self.assertEqual(len(mail.outbox), 1) def test_saved_search_no_jobs(self): search = SavedSearchFactory(feed='http://google.com', user=self.user) search.send_email() self.assertEqual(len(mail.outbox), 0) def test_saved_search_digest_no_jobs(self): self.digest = SavedSearchDigestFactory(user=self.user, is_active=True) for x in range(0, 5): SavedSearchFactory(user=self.user, feed='http://google.com') self.digest.send_email() self.assertEqual(SavedSearchLog.objects.count(), 1) log = SavedSearchLog.objects.get() self.assertTrue('saved searches have no jobs' in log.reason) self.assertFalse(log.was_sent) self.assertEqual(len(mail.outbox), 0)
class SavedSearchModelsTests(MyJobsBase): def setUp(self): super(SavedSearchModelsTests, self).setUp() self.user = UserFactory() self.patcher = patch('urllib2.urlopen', return_file()) self.mock_urlopen = self.patcher.start() def tearDown(self): super(SavedSearchModelsTests, self).tearDown() try: self.patcher.stop() except RuntimeError: # patcher was stopped in a test pass def test_send_search_email(self): SavedSearchDigestFactory(user=self.user, is_active=False) search = SavedSearchFactory(user=self.user, is_active=True, frequency='D', url='www.my.jobs/jobs?q=new+search') send_search_digests() self.assertEqual(len(mail.outbox), 1) self.assertEqual(SavedSearchLog.objects.count(), 1) log = SavedSearchLog.objects.get() self.assertTrue(log.was_sent) email = mail.outbox.pop() self.assertEqual(email.from_email, 'My.jobs Saved Search <*****@*****.**>') self.assertEqual(email.to, [self.user.email]) self.assertEqual(email.subject, search.label) self.assertTrue("table" in email.body) self.assertTrue(email.to[0] in email.body) self.assertNotEqual(email.body.find(search.url), -1, "Search url was not found in email body") self.assertTrue("Your profile is %s%% complete" % self.user.profile_completion in email.body) def requeue(self, search, digest): """ Asserts that the given search has a last_sent of None and there are no emails in mail.outbox. Requeues the provided search and then asserts that last_sent was updated and a mail was sent if the provided digest is not active, otherwise reasserts that last_sent is None and no emails have been sent. """ self.assertIsNone(search.last_sent) self.assertEqual(len(mail.outbox), 0) requeue_missed_searches() search = SavedSearch.objects.get(pk=search.pk) date = datetime.date.today() outbox_count = 1 if digest.is_active: date = None outbox_count = 0 self.assertEqual(search.last_sent.date() if search.last_sent else None, date) self.assertEqual(len(mail.outbox), outbox_count) def test_requeue_weekly_saved_search(self): """ Tests that weekly saved searches are requeued correctly individually in addition to as part of a digest. """ today = datetime.date.today().isoweekday() two_days_ago = today - 2 if two_days_ago <= 0: two_days_ago += 7 digest = SavedSearchDigestFactory(user=self.user, is_active=True) search = SavedSearchFactory(user=self.user, is_active=True, frequency='W', day_of_week=two_days_ago) self.requeue(search, digest) digest.is_active = False digest.save() search.last_sent = None search.save() mail.outbox = [] self.requeue(search, digest) def test_requeue_monthly_saved_search(self): """ Tests that monthly saved searches are requeued correctly individually in addition to as part of a digest. """ today = datetime.date.today().day last_week = today - 7 if last_week <= 0: last_week += 31 digest = SavedSearchDigestFactory(user=self.user, is_active=True) search = SavedSearchFactory(user=self.user, is_active=True, frequency='M', day_of_month=last_week) self.requeue(search, digest) digest.is_active = False digest.save() search.last_sent = None search.save() mail.outbox = [] self.requeue(search, digest) def test_send_search_digest_email(self): SavedSearchDigestFactory(user=self.user) send_search_digests() self.assertEqual(len(mail.outbox), 0) self.assertEqual(SavedSearchLog.objects.count(), 1) log = SavedSearchLog.objects.get() self.assertTrue('No saved searches' in log.reason) self.assertFalse(log.was_sent) search1 = SavedSearchFactory(user=self.user) self.assertIsNone(search1.last_sent) send_search_digests() self.assertIsNotNone(SavedSearch.objects.get(pk=search1.pk).last_sent) self.assertEqual(len(mail.outbox), 1) self.assertEqual(SavedSearchLog.objects.count(), 2) log = SavedSearchLog.objects.last() self.assertTrue(log.was_sent) search2 = SavedSearchFactory(user=self.user) self.assertIsNone(search2.last_sent) send_search_digests() self.assertIsNotNone(SavedSearch.objects.get(pk=search2.pk).last_sent) self.assertEqual(len(mail.outbox), 2) self.assertEqual(SavedSearchLog.objects.count(), 3) log = SavedSearchLog.objects.last() self.assertTrue(log.was_sent) email = mail.outbox.pop() self.assertEqual(email.from_email, 'My.jobs Saved Search <*****@*****.**>') self.assertEqual(email.to, [self.user.email]) self.assertEqual(email.subject, "Your Saved Search Digest") self.assertTrue("table" in email.body) self.assertTrue(email.to[0] in email.body) def test_send_search_digest_send_if_none(self): SavedSearchDigestFactory(user=self.user, send_if_none=True) send_search_digests() self.assertEqual(len(mail.outbox), 0) SavedSearchFactory(user=self.user) send_search_digests() self.assertEqual(len(mail.outbox), 1) def test_initial_email(self): search = SavedSearchFactory(user=self.user, is_active=False, url='www.my.jobs/search?q=new+search') search.initial_email() self.assertEqual(len(mail.outbox), 1) self.assertEqual(SavedSearchLog.objects.count(), 1) log = SavedSearchLog.objects.get() self.assertTrue('Jobs are not sent' in log.reason) self.assertTrue(log.was_sent) email = mail.outbox.pop() self.assertEqual(email.from_email, 'My.jobs Saved Search <*****@*****.**>') self.assertEqual(email.to, [self.user.email]) self.assertEqual("My.jobs New Saved Search" in email.subject, True) self.assertTrue("table" in email.body) self.assertTrue(email.to[0] in email.body) # Search url appears twice - once displaying the saved search source # and once in the view link. self.assertEqual(email.body.count(search.url), 2) def test_send_update_email(self): search = SavedSearchFactory(user=self.user, is_active=False, url='www.my.jobs/search?q=new+search') search.send_update_email('Your search is updated') self.assertEqual(len(mail.outbox), 1) log = SavedSearchLog.objects.get() self.assertTrue('Jobs are not sent' in log.reason) self.assertTrue(log.was_sent) email = mail.outbox.pop() self.assertEqual(email.from_email, 'My.jobs Saved Search <*****@*****.**>') self.assertEqual(email.to, [self.user.email]) self.assertEqual("My.jobs Saved Search Updated" in email.subject, True) self.assertTrue("table" in email.body) self.assertTrue("Your search is updated" in email.body) self.assertTrue(email.to[0] in email.body) def test_saved_search_all_jobs_link(self): search = SavedSearchFactory(user=self.user) search.send_email() email = mail.outbox.pop() # When search.url does not start with my.jobs, use it as the all jobs # link self.assertFalse(search.url.startswith('http://my.jobs')) self.assertNotEqual(email.body.find(search.url), -1) # When search.url starts with my.jobs, strip /feed/rss from search.feed # if it exists and use that as the all jobs link search.url = 'http://my.jobs/' + '1'*32 search.save() search.send_email() email = mail.outbox.pop() self.assertEqual(email.body.find(search.url), -1) self.assertNotEqual( email.body.find(search.feed.replace('/feed/rss', '')), -1) def test_unicode_in_saved_search(self): """Tests that saved search urls with unicode don't cause errors.""" search = SavedSearchFactory( user=self.user, url=u"warehouse.jobs/search?location=Roswell%2C+GA&q=Delivery+I" "+%E2%80%93+Material+Handler%2FDriver+Helper+%E2%80%93+3rd" "+Shift%2C+Part-time") try: search.send_email() except UnicodeEncodeError as e: self.fail(e) def test_pss_contact_record_tagged(self): """ When a contact record is created from a saved search being sent, that record should have the saved search's tag. """ company = CompanyFactory() partner = PartnerFactory(owner=company) tag = TagFactory(name="Test Tag") search = PartnerSavedSearchFactory( user=self.user, created_by=self.user, provider=company, partner=partner) search.tags.add(tag) search.send_email() record = ContactRecord.objects.get(tags__name=tag.name) self.assertTrue(record.contactlogentry.successful) @patch('mysearches.models.send_email') def test_send_pss_fails(self, mock_send_email): """ When a partner saved search fails to send, we should not imply that it was successful. """ company = CompanyFactory() partner = PartnerFactory(owner=company) search = PartnerSavedSearchFactory(user=self.user, created_by=self.user, provider=company, partner=partner) e = SMTPAuthenticationError(418, 'Toot toot') mock_send_email.side_effect = e self.assertEqual(ContactRecord.objects.count(), 0) self.assertEqual(SavedSearchLog.objects.count(), 0) search.send_email() record = ContactRecord.objects.get() log = SavedSearchLog.objects.get() self.assertFalse(log.was_sent) self.assertEqual(log.reason, "Toot toot") self.assertTrue(record.notes.startswith(log.reason)) self.assertFalse(record.contactlogentry.successful) def assert_modules_in_hrefs(self, modules): """ Assert that each module in :modules: is in the set of HTML elements matched by li > a in an email """ email = mail.outbox.pop() soup = BeautifulSoup(email.body) lis = soup.findAll('li') # .attrs is a dictionary, where the key is the attribute hrefs = [li.find('a').attrs['href'] for li in lis] self.assertEqual(len(hrefs), len(modules)) # We can do self because the list of modules in settings and the list # of recommendations should be in the same order mapping = zip(modules, hrefs) for pair in mapping: # Saved search emails should have one li per required profile unit # that the owner does not currently have self.assertTrue(pair[0] in pair[1].lower()) def test_email_profile_completion(self): search = SavedSearchFactory(user=self.user) search.send_email() self.assertEqual(len(settings.PROFILE_COMPLETION_MODULES), 6) self.assert_modules_in_hrefs(settings.PROFILE_COMPLETION_MODULES) PrimaryNameFactory(user=self.user) search.send_email() new_modules = [module for module in settings.PROFILE_COMPLETION_MODULES if module != 'name'] self.assertEqual(len(new_modules), 5) self.assert_modules_in_hrefs(new_modules) def test_email_contains_activate_link(self): search = SavedSearchFactory(user=self.user) self.assertTrue(self.user.is_active) search.send_email() email = mail.outbox.pop() self.assertFalse('activate your account' in email.body) self.user.is_active = False self.user.save() search.send_email() email = mail.outbox.pop() self.assertTrue('activate your account' in email.body) def test_errors_dont_disable_searches(self): """ We should retry sending saved searches but exceeding our maximum number of retries should not disable those searches. """ self.mock_urlopen.side_effect = ValueError("bork bork bork") SavedSearchDigestFactory(user=self.user) search = SavedSearchFactory(user=self.user, feed='www.my.jobs') # Celery raises a retry that makes the test fail. In reality # everything is fine, so ignore the retry. try: send_search_digests() except RetryTaskError: pass self.assertEqual(len(mail.outbox), 0) search = SavedSearch.objects.get(pk=search.pk) self.assertTrue(search.is_active) def test_get_unsent_jobs(self): """ When sending a saved search email, we should retrieve all new jobs since last send, not all new jobs based on frequency. """ self.patcher.stop() self.patcher = patch('urllib2.urlopen', return_file(time_=datetime.datetime.now() - datetime.timedelta(days=3))) self.mock_urlopen = self.patcher.start() last_sent = datetime.datetime.now() - datetime.timedelta(days=3) search = SavedSearchFactory(frequency='D', last_sent=last_sent, user=self.user, email=self.user.email) search.send_email() self.assertEqual(len(mail.outbox), 1) def test_inactive_user_receives_saved_search(self): self.assertEqual(len(mail.outbox), 0) self.user.is_active = False self.user.save() saved_search = SavedSearchFactory(user=self.user) saved_search.send_email() self.assertEqual(len(mail.outbox), 1) def test_saved_search_no_jobs(self): search = SavedSearchFactory(feed='http://google.com', user=self.user) search.send_email() self.assertEqual(len(mail.outbox), 0) def test_saved_search_digest_no_jobs(self): self.digest = SavedSearchDigestFactory(user=self.user, is_active=True) for x in range(0, 5): SavedSearchFactory(user=self.user, feed='http://google.com') self.digest.send_email() self.assertEqual(SavedSearchLog.objects.count(), 1) log = SavedSearchLog.objects.get() self.assertTrue('saved searches have no jobs' in log.reason) self.assertFalse(log.was_sent) self.assertEqual(len(mail.outbox), 0)
class MyDashboardViewsTests(TestCase): def setUp(self): self.staff_user = UserFactory() group = Group.objects.get(name=CompanyUser.GROUP_NAME) self.staff_user.groups.add(group) self.staff_user.save() self.company = CompanyFactory() self.company.save() self.admin = CompanyUserFactory(user=self.staff_user, company=self.company) self.admin.save() self.microsite = MicrositeFactory(company=self.company) self.microsite.save() self.client = TestClient() self.client.login_user(self.staff_user) self.candidate_user = UserFactory(email="*****@*****.**") SavedSearchFactory(user=self.candidate_user, url='http://test.jobs/search?q=django', label='test Jobs') self.candidate_user.save() for i in range(5): # Create 5 new users user = UserFactory(email='*****@*****.**' % i) for search in SEARCH_OPTS: # Create 15 new searches and assign three per user SavedSearchFactory(user=user, url='http://test.jobs/search?q=%s' % search, label='%s Jobs' % search) def test_number_of_searches_and_users_is_correct(self): response = self.client.post( reverse('dashboard') + '?company=' + str(self.company.id), {'microsite': 'test.jobs'}) soup = BeautifulSoup(response.content) # 10 searches total, two rows per search self.assertEqual(len(soup.select('#row-link-table tr')), 20) old_search = SavedSearch.objects.all()[0] old_search.created_on -= timedelta(days=31) old_search.save() response = self.client.post( reverse('dashboard') + '?company=' + str(self.company.id), {'microsite': 'test.jobs'}) soup = BeautifulSoup(response.content) self.assertEqual(len(soup.select('#row-link-table tr')), 20) # Tests to see if redirect from /candidates/ goes to candidates/view/ def test_redirect_to_candidates_views_default_page(self): response = self.client.post('/candidates/') # response returns HttpResponsePermanentRedirect which returns a 301 # status code instead of the normal 302 redirect status code self.assertRedirects(response, '/candidates/view/', status_code=301, target_status_code=200) response = self.client.post(reverse('dashboard')) self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content) company_name = soup.find('h1') company_name = company_name.next self.assertEqual(company_name, self.company.name) # Eventually these opted-in/out will be changed to # track if user is part of company's activity feed def test_candidate_has_opted_in(self): response = self.client.post( reverse('candidate_information', ) + '?company=' + str(self.company.id) + '&user='******'candidate_information', ) + '?company=' + str(self.company.id) + '&user='******'candidate_information', ) + '?company=' + str(self.company.id) + '&user='******'div', { 'id': 'candidate-content' }).findAll('a', {'class': 'accordion-toggle'}) info = soup.find('div', {'id': 'candidate-content'}).findAll('li') self.assertEqual(len(titles), 6) self.assertEqual(len(info), 16) self.assertEqual(response.status_code, 200) def test_candidate_page_load_without_profileunits_with_activites(self): response = self.client.post( reverse('candidate_information', ) + '?company=' + str(self.company.id) + '&user='******'div', { 'id': 'candidate-content' }).findAll('a', {'class': 'accordion-toggle'}) info = soup.find('div', {'id': 'candidate-content'}).findAll('li') self.assertEqual(len(titles), 1) self.assertEqual(len(info), 3) self.assertEqual(response.status_code, 200) def test_candidate_page_load_without_profileunits_and_activites(self): saved_search = SavedSearch.objects.get(user=self.candidate_user) saved_search.delete() response = self.client.post( reverse('candidate_information', ) + '?company=' + str(self.company.id) + '&user='******'div', {'id': 'candidate-content'}) self.assertFalse(info) self.assertEqual(response.status_code, 404) def test_export_csv(self): response = self.client.post( reverse('export_candidates') + '?company=' + str(self.company.id) + '&ex-t=csv') self.assertTrue(response.content) self.assertEqual(response.status_code, 200) def test_export_pdf(self): response = self.client.post( reverse('export_candidates') + '?company=' + str(self.company.id) + '&ex-t=pdf') self.assertTrue(response.content.index('PDF')) self.assertEqual(response.templates[0].name, 'mydashboard/export/candidate_listing.html') self.assertEqual(response.status_code, 200) def test_export_xml(self): response = self.client.post( reverse('export_candidates') + '?company=' + str(self.company.id) + '&ex-t=xml') self.assertTrue(response.content.index('candidates')) self.assertEqual(response.status_code, 200) def test_export_json(self): response = self.client.post( reverse('export_candidates') + '?company=' + str(self.company.id) + '&ex-t=json') self.assertTrue(response.content.index('candidates')) self.assertEqual(response.status_code, 200)
class NewUserTests(SeleniumTestCase): """Tests Account creation""" def setUp(self): super(NewUserTests, self).setUp() company = CompanyFactory() self.user = UserFactory(first_name="John", last_name="Doe") admin_role = RoleFactory(company=company, name='Admin') self.user.roles.add(self.admin_role) def test_home_page_works(self): """ As John, navigating to https://secure.my.jobs should send me to a page titled "My.jobs". """ self.browser.get(self.live_server_url) self.assertIn(self.browser.title, 'My.jobs') def test_cant_log_in_without_account(self): """ As John, I shouldn't be able to log into My.jobs without registering first. """ self.browser.get('/'.join([self.live_server_url, 'prm', 'view'])) # We're trying to access a private page while unauthenticated, which # should result in a next parameter being added. self.assertTrue('next=' in self.browser.current_url) # attempt to log in username = self.find('id_username') username.send_keys(self.user.email) self.find('id_password').send_keys(self.user.password) self.find('login').click() # If we've logged in, the next parameter should have went away. We # aren't expecting to be logged in right now as the password was bad. self.assertTrue('next=' in self.browser.current_url) def test_user_registration(self): """ As John, I should be able to register on My.jobs and log in. """ self.browser.get('/'.join([self.live_server_url, 'prm', 'view'])) # register self.find('id_email').send_keys('*****@*****.**') self.find('id_password1').send_keys('aaAA11..') self.find('id_password2').send_keys('aaAA11..') self.find('register').click() try: WebDriverWait(self.browser, 10).until( expected_conditions.presence_of_element_located( (By.ID, 'profile'))) finally: self.assertEqual( self.find('profile').get_attribute('innerHTML'), 'Skip: Take me to my profile') def test_user_login(self): self.user.set_password("test") self.user.save() self.find('id_username').send_keys(self.user.email) self.find('id_password').send_keys("test") self.find('login').click()
class MyJobsAdminTests(MyJobsBase): def setUp(self): super(MyJobsAdminTests, self).setUp() self.user.set_password('5UuYquA@') self.user.is_superuser = True self.user.save() self.account_owner = UserFactory(email='*****@*****.**') SeoSiteFactory(domain='secure.my.jobs') mail.outbox = [] self.data = { '_selected_action': [unicode(self.account_owner.pk)], 'action': 'request_account_access' } def test_request_access_to_staff(self): """ Requesting access to a staff/superuser account is not allowed. """ self.account_owner.is_staff = True self.account_owner.save() # Selecting the action in the User changelist and pressing "OK" should # immediately show a notification at the top of the page. response = self.client.post(reverse('admin:myjobs_user_changelist'), self.data, follow=True) self.assertEqual(len(mail.outbox), 0) self.assertContains(response, ("Requesting access to staff or " "superusers is not supported.")) # Manually constructing a post to the relevant url should redirect # to the User changelist and not send notification emails. response = self.client.post( reverse('request-account-access', kwargs={'uid': self.account_owner.pk}), {'reason': 'reason here'}) self.assertRedirects(response, reverse('admin:myjobs_user_changelist')) self.assertEqual(len(mail.outbox), 0) def test_request_access_to_non_staff(self): """ Requesting access to a non-staff/superuser account succeeds if the target is not a staff/superuser account and the requesting staff member provides a reason. """ # Request access to an account to get to the request form. response = self.client.post(reverse('admin:myjobs_user_changelist'), self.data, follow=True) self.assertContains(response, "What is the nature of this request?", msg_prefix="Did not redirect to the request form") url = reverse('request-account-access', kwargs={'uid': self.account_owner.pk}) last_redirect = response.redirect_chain[-1][0] # If the admin action determines that it is valid, it redirects. Ensure # we redirected to the expected location. self.assertTrue(last_redirect.endswith(url), msg="Did not redirect as expected") # Try submitting the request form without a reason. response = self.client.post(url) self.assertContains(response, "This field is required.", msg_prefix=("Form error not present on invalid " "submission")) self.assertEqual(len(mail.outbox), 0, msg="Mail sent despite form errors") # # Submit again, providing a reason. self.client.post(url, {'reason': 'reason here'}) self.assertEqual(len(mail.outbox), 1, msg="Mail did not send on successful form submission") email = mail.outbox[0] self.assertTrue(self.account_owner.email in email.to, msg="Email was sent to the wrong user") self.assertTrue('reason here' in email.body, msg="Account access reason was not in the sent email") def test_export_as_csv_admin_action(self): """ Tests the ability to export the list of destination manipulations as a CSV. """ email = '*****@*****.**' password = '******' UserFactory(email=email, password=password, is_staff=True, is_superuser=True) # only superusers are allowed to use Django amin self.client.login(username=email, password=password) manipulations = [ DestinationManipulationFactory(view_source=i) for i in range(200, 210) ] # this is the format we expect results to be in if deserializing CSV # into a list of dicts formatted_manipulations = [{ u'View Source': unicode(m.view_source), u'View Source Name': '', u'BUID': unicode(m.buid), u'Action Type': unicode(m.action_type), u'Value 1': unicode(m.value_1), u'Value 2': unicode(m.value_2), u'Action': unicode(m.action) } for m in manipulations] args = { 'action': 'export_as_csv', '_selected_action': [unicode(m.pk) for m in manipulations[:5]] } changelist_url = reverse( "admin:redirect_destinationmanipulation_changelist") # asking to export as csv when selected items should serialize only # those items response = self.client.post(changelist_url, args) reader = csv.DictReader(response.content.split('\r\n')) self.assertItemsEqual(list(reader), formatted_manipulations[:5]) args['select_across'] = '1' # choosing "select all" should export all records in the queryset, # regardless of the list of selected items passed response = self.client.post(changelist_url, args) reader = csv.DictReader(response.content.split('\r\n')) self.assertItemsEqual(list(reader), formatted_manipulations)
class SavedSearchHelperTests(MyJobsBase): def setUp(self): super(SavedSearchHelperTests, self).setUp() self.user = UserFactory() self.valid_url = 'http://www.my.jobs/jobs?location=chicago&q=nurse' self.patcher = patch('urllib2.urlopen', return_file()) self.patcher.start() def tearDown(self): super(SavedSearchHelperTests, self).tearDown() self.patcher.stop() def test_valid_dotjobs_url(self): url, soup = validate_dotjobs_url(self.valid_url, self.user) self.assertIsNotNone(url) self.assertIsNotNone(soup) no_netloc = 'www.my.jobs/jobs?location=chicago&q=nurse' title, url = validate_dotjobs_url(no_netloc, self.user) self.assertIsNotNone(title) self.assertIsNotNone(url) expected = urlparse( 'http://www.my.jobs/jobs/feed/rss?q=nurse&location=chicago') actual = urlparse(url.replace('amp;', '')) self.assertEqual(actual.path, expected.path) self.assertEqual(parse_qs(actual.query), parse_qs(expected.query)) valid_filter_url = 'www.my.jobs/jobs/' title, url = validate_dotjobs_url(valid_filter_url, self.user) self.assertIsNotNone(title) self.assertIsNotNone(url) def test_validate_dotjobs_url_with_special_chars(self): urls = [ ('http://www.my.jobs/jobs/?q=query with spaces', 'http://www.my.jobs/jobs/feed/rss?q=query+with+spaces'), ('http://www.my.jobs/jobs/?q=яы', 'http://www.my.jobs/jobs/feed/rss?q=%D1%8F%D1%8B') ] for url_set in urls: label, feed = validate_dotjobs_url(url_set[0], self.user) self.assertEqual(feed, url_set[1]) self.assertIsNotNone(label) def test_invalid_dotjobs_url(self): urls = ['http://google.com', # url does not contain a feed '', # url not provided 'http://'] # invalid url provided for url in urls: title, url = validate_dotjobs_url(url, self.user) self.assertIsNone(title) self.assertIsNone(url) def test_date_in_range(self): start = datetime.date(month=1, day=1, year=2013) end = datetime.date(month=12, day=1, year=2013) x = datetime.date(month=6, day=1, year=2013) is_in_range = date_in_range(start, end, x) self.assertTrue(is_in_range) start = datetime.date(month=1, day=1, year=2013) end = datetime.date(month=12, day=1, year=2013) x = datetime.date(month=6, day=1, year=2010) is_in_range = date_in_range(start, end, x) self.assertFalse(is_in_range) def test_parse_feed(self): feed_url = 'http://www.my.jobs/feed/rss' for use_json, count in [(True, 2), (False, 1)]: items = parse_feed(feed_url, use_json=use_json) # The second value in the items list is the total count from a # feed, which may not equal the number of items returned self.assertEqual(items[1], len(items[0])) item = items[0][0] for element in ['pubdate', 'title', 'description', 'link']: self.assertTrue(item[element]) def test_parse_feed_with_count(self): feed_url = 'http://www.my.jobs/feed/rss' num_items = 1 items, count = parse_feed(feed_url, num_items=num_items) self.assertEqual(count, num_items) def test_url_sort_options(self): feed = 'http://www.my.jobs/jobs/feed/rss?date_sort=False' # Test to make sure sort by "Relevance" has '&date_sort=False' added # a single time feed_url = url_sort_options(feed, "Relevance") parsed = urlparse(feed_url) query = parse_qs(parsed.query) self.assertEquals(parsed.path, "/jobs/feed/rss") self.assertEquals(query['date_sort'], [u'False']) # If a frequency isn't specified, days_ago should be missing from # the url. self.assertNotIn('days_ago', query) # Test to make sure sort by "Date" doesn't have anything added feed_url = url_sort_options(feed, "Date") self.assertEquals(feed_url, "http://www.my.jobs/jobs/feed/rss") # Test to make sure that passing in a frequency does in fact # add the frequency to the feed url. feed_url = url_sort_options(feed, "Relevance", frequency='D') query = parse_qs(urlparse(feed_url).query) self.assertEquals(query['days_ago'][0], '1') feed_url = url_sort_options(feed, "Relevance", frequency='W') query = parse_qs(urlparse(feed_url).query) self.assertEquals(query['days_ago'][0], '7') feed_url = url_sort_options(feed, "Relevance", frequency='M') query = parse_qs(urlparse(feed_url).query) self.assertEqual(query['days_ago'][0], '30') def test_unicode_in_search(self): search = SavedSearch(url=u"http://www.my.jobs/search?q=%E2%80%93", user=self.user, feed=u"http://www.my.jobs/search/feed/rss?q=%E2%80%93", sort_by=u'Relevance') search.save() feed_url = url_sort_options(search.feed, search.sort_by) old = parse_qs(urlparse(search.feed).query) new = parse_qs(urlparse(feed_url).query) self.assertFalse(old.get('date_sort')) self.assertTrue(new['date_sort'][0]) del new['date_sort'] self.assertEqual(new, old) def test_feed_on_protected_site_no_access(self): from mydashboard.tests.factories import SeoSiteFactory site_id = settings.PROTECTED_SITES.keys()[0] site = SeoSiteFactory(pk=site_id, id=site_id) url = "http://%s?q=query" % site.domain result = update_url_if_protected(url, self.user) self.assertEqual(result, url) def test_feed_on_protected_site_with_access(self): from mydashboard.tests.factories import SeoSiteFactory site_id = settings.PROTECTED_SITES.keys()[0] site = SeoSiteFactory(pk=site_id, id=site_id) group_id = settings.PROTECTED_SITES.values()[0][0] Group.objects.create(pk=group_id, name='Test Group') self.user.groups.add(group_id) self.user.save() url = "http://%s?q=query" % site.domain expected_result = "%s&key=%s" % (url, settings.SEARCH_API_KEY) result = update_url_if_protected(url, self.user) self.assertEqual(result, expected_result)
class MyProfileTests(TestCase): user_info = { 'password1': 'complicated_password', 'email': '*****@*****.**' } def setUp(self): super(MyProfileTests, self).setUp() self.user = UserFactory() def test_primary_name_save(self): """ Saving a primary name when one already exists replaces it with the new primary name. """ initial_name = PrimaryNameFactory(user=self.user) self.assertTrue(initial_name.primary) new_name = NewPrimaryNameFactory(user=self.user) initial_name = Name.objects.get(given_name='Alice') self.assertTrue(new_name.primary) self.assertFalse(initial_name.primary) def test_primary_name_save_multiuser(self): """ Saving primary names when multiple users are present accurately sets and retrieves the correct name """ self.user_2 = UserFactory(email='*****@*****.**') user_2_initial_name = PrimaryNameFactory(user=self.user_2) user_2_new_name = NewPrimaryNameFactory(user=self.user_2) initial_name = PrimaryNameFactory(user=self.user) new_name = NewPrimaryNameFactory(user=self.user) user_2_initial_name = Name.objects.get(given_name='Alice', user=self.user_2) user_2_new_name = Name.objects.get(given_name='Alicia', user=self.user_2) initial_name = Name.objects.get(given_name='Alice', user=self.user) self.assertTrue(new_name.primary) self.assertFalse(initial_name.primary) self.assertTrue(user_2_new_name.primary) self.assertFalse(user_2_initial_name.primary) with self.assertRaises(MultipleObjectsReturned): Name.objects.get(primary=True) Name.objects.get(primary=False) Name.objects.get(given_name='Alice') Name.objects.get(given_name='Alicia') Name.objects.get(primary=True, user=self.user_2) def test_email_activation_creation(self): """ Creating a new secondary email creates a corresponding unactivated ActivationProfile. """ secondary_email = SecondaryEmailFactory(user=self.user) activation = ActivationProfile.objects.get(email=secondary_email.email) self.assertEqual(secondary_email.email, activation.email) def test_send_activation(self): """ The send_activation method in SecondaryEmail should send an activation link to the email address """ secondary_email = SecondaryEmailFactory(user=self.user) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, [secondary_email.email]) self.assertTrue('secondary email' in mail.outbox[0].body) def test_verify_email(self): """ Clicking the activation link sets the ActivationProfile object to activated and sets the SecondaryEmail object to verified. """ secondary_email = SecondaryEmailFactory(user=self.user) activation = ActivationProfile.objects.get(user=self.user, email=secondary_email.email) response = self.client.get( reverse('registration_activate', args=[activation.activation_key]) + '?verify-email=%s' % self.user.email) secondary_email = SecondaryEmail.objects.get( user=self.user, email=secondary_email.email) activation = ActivationProfile.objects.get(user=self.user, email=secondary_email.email) self.assertEqual(response.status_code, 200) self.assertTrue(secondary_email.verified) def test_set_primary_email(self): """ Calling the set_as_primary method in the SecondaryEmail removes it from SecondaryEmail, replaces the current address on the User model, and adds the replaced address to the SecondaryEmail table. """ old_primary = self.user.email secondary_email = SecondaryEmailFactory(user=self.user) new_primary = secondary_email.email for email in [old_primary, new_primary]: # Emails must be verified to make them primary. activation = ActivationProfile.objects.get_or_create( user=self.user, email=email)[0] ActivationProfile.objects.activate_user(activation.activation_key) secondary_email = SecondaryEmail.objects.get(email=new_primary) secondary_email.set_as_primary() with self.assertRaises(SecondaryEmail.DoesNotExist): SecondaryEmail.objects.get(email=new_primary) old_email = SecondaryEmail.objects.get(email=old_primary) self.assertTrue(old_email.verified) user = User.objects.get(email=new_primary) def test_duplicate_same_primary_name(self): """ Makes sure that one can not create duplicate primary names. """ primary_name1 = PrimaryNameFactory(user=self.user) primary_name2 = PrimaryNameFactory(user=self.user) num_results = self.user.profileunits_set.filter( content_type__name='name').count() self.assertEqual(num_results, 1) def test_different_primary_name(self): primary_name1 = PrimaryNameFactory(user=self.user) primary_name2 = NewPrimaryNameFactory(user=self.user) primary_name_count = Name.objects.filter(user=self.user, primary=True).count() non_primary_name_count = Name.objects.filter(user=self.user, primary=False).count() self.assertEqual(primary_name_count, 1) self.assertEqual(non_primary_name_count, 1) def test_non_primary_name_to_primary(self): name = NewNameFactory(user=self.user) primary_name1 = PrimaryNameFactory(user=self.user) primary_name_count = Name.objects.filter(user=self.user, primary=True).count() non_primary_name_count = Name.objects.filter(user=self.user, primary=False).count() self.assertEqual(primary_name_count, 1) self.assertEqual(non_primary_name_count, 0) def test_primary_name_to_non_primary(self): primary_name = PrimaryNameFactory(user=self.user) primary_name.primary = False primary_name.save() primary_name_count = Name.objects.filter(user=self.user, primary=True).count() non_primary_name_count = Name.objects.filter(user=self.user, primary=False).count() self.assertEqual(primary_name_count, 0) self.assertEqual(non_primary_name_count, 1) def test_duplicate_name(self): """ Makes sure that duplicate names is not saving. """ name1 = NewNameFactory(user=self.user) name2 = NewNameFactory(user=self.user) num_results = Name.objects.filter(user=self.user).count() self.assertEqual(num_results, 1) def test_unverified_primary_email(self): """ Only verified emails can be set as the primary email """ old_primary = self.user.email secondary_email = SecondaryEmailFactory(user=self.user) primary = secondary_email.set_as_primary() with self.assertRaises(SecondaryEmail.DoesNotExist): SecondaryEmail.objects.get(email=old_primary) self.assertFalse(primary) user = User.objects.get(email=old_primary) self.assertEqual(user.email, old_primary) def test_maintain_verification_state(self): """ For security reasons, the state of verification of the user email should be the same as it is when it is transferred into SecondaryEmail """ old_primary = self.user.email self.user.is_active = False self.user.save() secondary_email = SecondaryEmailFactory(user=self.user) activation = ActivationProfile.objects.get(user=self.user, email=secondary_email.email) ActivationProfile.objects.activate_user(activation.activation_key) secondary_email = SecondaryEmail.objects.get( user=self.user, email=secondary_email.email) new_primary = secondary_email.email secondary_email.set_as_primary() old_email = SecondaryEmail.objects.get(email=old_primary) self.assertFalse(old_email.verified) user = User.objects.get(email=new_primary) def test_same_secondary_email(self): """ All emails are unique. If an email is used as a user's primary email or another secondary email, it may not be used as a secondary email again. """ secondary_email = SecondaryEmailFactory(user=self.user) with self.assertRaises(IntegrityError): new_secondary_email = SecondaryEmailFactory(user=self.user) new_secondary_email = SecondaryEmailFactory(user=self.user, email='*****@*****.**') def test_delete_secondary_email(self): """ Deleting a secondary email should also delete its activation profile """ self.assertEqual(ActivationProfile.objects.count(), 0) secondary_email = SecondaryEmailFactory(user=self.user) self.assertEqual(ActivationProfile.objects.count(), 1) secondary_email.delete() self.assertEqual(ActivationProfile.objects.count(), 0) def test_add_military_service(self): military_service = MilitaryServiceFactory(user=self.user) military_service.save() ms_object = ProfileUnits.objects.filter( content_type__name="military service").count() self.assertEqual(ms_object, 1) def test_add_license(self): license_form = LicenseFactory(user=self.user) license_form.save() ms_object = ProfileUnits.objects.filter( content_type__name="license").count() self.assertEqual(ms_object, 1) def test_add_website(self): website_instance = WebsiteFactory(user=self.user) website_instance.save() ms_object = ProfileUnits.objects.filter( content_type__name="website").count() self.assertEqual(ms_object, 1) def test_add_summary(self): summary_instance = SummaryFactory(user=self.user) summary_instance.save() ms_object = ProfileUnits.objects.filter( content_type__name="summary").count() self.assertEqual(ms_object, 1) def test_add_volunteer_history(self): vh_instance = VolunteerHistoryFactory(user=self.user) vh_instance.save() ms_object = ProfileUnits.objects.filter( content_type__name="volunteer history").count() self.assertEqual(ms_object, 1)
class MyDashboardViewsTests(MyJobsBase): def setUp(self): super(MyDashboardViewsTests, self).setUp() group = Group.objects.get(name=CompanyUser.GROUP_NAME) self.user.groups.add(group) self.business_unit = BusinessUnitFactory() self.company.job_source_ids.add(self.business_unit) self.admin = CompanyUserFactory(user=self.user, company=self.company) self.microsite = SeoSiteFactory() self.microsite.business_units.add(self.business_unit) self.candidate_user = UserFactory(email="*****@*****.**") SavedSearchFactory(user=self.candidate_user, feed='http://test.jobs/jobs/feed/rss?', url='http://test.jobs/search?q=django', label='test Jobs') for i in range(5): # Create 5 new users user = UserFactory(email='*****@*****.**' % i) for search in SEARCH_OPTS: # Create 15 new searches and assign three per user SavedSearchFactory(user=user, url='http://test.jobs/search?q=%s' % search, feed='http://test.jobs/jobs/feed/rss?', label='%s Jobs' % search) update_solr_task(settings.TEST_SOLR_INSTANCE) def tearDown(self): super(MyDashboardViewsTests, self).tearDown() for location in settings.TEST_SOLR_INSTANCE.values(): solr = pysolr.Solr(location) solr.delete(q='*:*') def add_analytics_data(self, type_, num_to_add=2): """ Adds testing analytics data to Solr. Adds two entries per page category, one for unauthenticated and one for authenticated hits. """ dicts = [] base_dict = { 'domain': self.microsite.domain, 'view_date': datetime.now(), 'company_id': self.company.pk, } home_dict = { 'page_category': 'home' } view_dict = { 'job_view_guid': '1'*32, 'job_view_buid': self.business_unit.pk, 'page_category': 'listing' } search_dict = { 'page_category': 'results' } apply_dict = { 'job_view_guid': '2'*32, 'job_view_buid': self.business_unit.pk, 'page_category': 'redirect' } if type_ == 'home': analytics_dict = home_dict elif type_ == 'listing': analytics_dict = view_dict elif type_ == 'results': analytics_dict = search_dict else: analytics_dict = apply_dict analytics_dict.update(base_dict) for _ in range(num_to_add): dicts.append(analytics_dict.copy()) for analytics_dict in dicts: analytics_dict['aguid'] = uuid.uuid4().hex analytics_dict['uid'] = 'analytics##%s#%s' % ( analytics_dict['view_date'], analytics_dict['aguid'] ) for location in settings.TEST_SOLR_INSTANCE.values(): solr = pysolr.Solr(location) solr.add(dicts) @unittest.skip("Correct behavior undefined with respect to duplicates.") def test_number_of_searches_and_users_is_correct(self): response = self.client.post( reverse('dashboard')+'?company='+str(self.company.id)) # 6 users total self.assertEqual(response.context['total_candidates'], 6) old_search = SavedSearch.objects.all()[0] old_search.created_on -= timedelta(days=31) old_search.save() response = self.client.post( reverse('dashboard')+'?company='+str(self.company.id), {'microsite': 'test.jobs'}) self.assertEqual(response.context['total_candidates'], 6) def test_facets(self): education = EducationFactory(user=self.candidate_user) adr = AddressFactory(user=self.candidate_user) license = LicenseFactory(user=self.candidate_user) self.candidate_user.save() update_solr_task(settings.TEST_SOLR_INSTANCE) base_url = 'http://testserver/candidates/view?company={company}' country_str = base_url + '&location={country}' education_str = base_url + '&education={education}' license_str = base_url + '&license={license_name}' country_str = country_str.format( company=self.company.pk, country=adr.country_code) education_str = education_str.format( company=self.company.pk, education=education.education_level_code) license_str = license_str.format( company=self.company.pk, license_name=license.license_name) q = '?company={company}' q = q.format(company=str(self.company.id)) response = self.client.post(reverse('dashboard')+q) soup = BeautifulSoup(response.content) types = ['Country', 'Education', 'License'] hrefs = [] for facet_type in types: container = soup.select('#%s-details-table' % facet_type)[0] href = container.select('a')[0].attrs['href'] hrefs.append(href) self.assertIn(country_str, hrefs) self.assertIn(education_str, hrefs) self.assertIn(license_str, hrefs) def test_filters(self): adr = AddressFactory(user=self.candidate_user) self.candidate_user.save() update_solr_task(settings.TEST_SOLR_INSTANCE) country_str = 'http://testserver/candidates/view?company={company}&location={country}' country_filter_str = '<a class="applied-filter" href="http://testserver/candidates/view?company={company}"><span>✖</span> {country_long}</a><br>' region_str = 'http://testserver/candidates/view?company={company}&location={country}-{region}' region_filter_str = '<a class="applied-filter" href="http://testserver/candidates/view?company={company}&location={country}"><span>✖</span> {region}, {country}</a>' city_str = 'http://testserver/candidates/view?company={company}&location={country}-{region}-{city}' city_filter_str = '<a class="applied-filter" href="http://testserver/candidates/view?company={company}&location={country}-{region}"><span>✖</span> {city}, {region}, {country}</a>' country_str = country_str.format(company=self.company.pk, country=adr.country_code) country_filter_str = country_filter_str.format(company=self.company.pk, country=adr.country_code, country_long=country_codes[adr.country_code]) region_str = region_str.format(company=self.company.pk, country=adr.country_code, region=adr.country_sub_division_code) region_filter_str = region_filter_str.format(company=self.company.pk, region=adr.country_sub_division_code, country=adr.country_code, country_long=country_codes[adr.country_code]) city_str = city_str.format(company=self.company.pk, country=adr.country_code, region=adr.country_sub_division_code, city=adr.city_name) city_filter_str = city_filter_str.format(company=self.company.pk, country=adr.country_code, region=adr.country_sub_division_code, city=adr.city_name, country_long=country_codes[adr.country_code]) q = '?company={company}' q = q.format(company=str(self.company.id)) response = self.client.post(reverse('dashboard')+q) self.assertIn(country_str, response.content) q = '?company={company}&location={country}' q = q.format(company=str(self.company.id), country=adr.country_code) response = self.client.post(reverse('dashboard')+q) self.assertIn(country_filter_str, response.content) self.assertIn(region_str, response.content) q = '?company={company}&location={country}-{region}' q = q.format(company=str(self.company.id), country=adr.country_code, region=adr.country_sub_division_code) response = self.client.post(reverse('dashboard')+q) self.assertIn(region_filter_str, response.content) self.assertIn(city_str, response.content) q = '?company={company}&location={country}-{region}-{city}' q = q.format(company=str(self.company.id), country=adr.country_code, region=adr.country_sub_division_code, city=adr.city_name) response = self.client.post(reverse('dashboard')+q) self.assertIn(city_filter_str, response.content) @unittest.skip("Correct behavior undefined with respect to duplicates.") def test_search_field(self): # Build url def build_url(search): q = '?company={company}&search={search}' q = q.format(company=str(self.company.id), search=search) return reverse('dashboard') + q # assert it finds all 5 searches. response = self.client.post(build_url('python')) soup = BeautifulSoup(response.content) count_box = soup.select('.count-box-left') count = int(count_box[0].text) self.assertEqual(count, 5) # 6 users total response = self.client.post(build_url('example')) soup = BeautifulSoup(response.content) count_box = soup.select('.count-box-left') count = int(count_box[0].text) self.assertIn(count, [6, 7]) def test_search_email(self): """We should be able to search for an exact email.""" user = UserFactory(email="*****@*****.**") SavedSearchFactory(user=user, url='http://test.jobs/search?q=python', feed='http://test.jobs/jobs/feed/rss?', label='Python Jobs') user.save() update_solr_task(settings.TEST_SOLR_INSTANCE) q = '?company={company}&search={search}' q = q.format(company=str(self.company.id), search='*****@*****.**') url = reverse('dashboard') + q response = self.client.post(url) soup = BeautifulSoup(response.content) self.assertEqual(len(soup.select('#row-link-table tr')), 1) def test_search_domain(self): """We should be able to search for domain.""" user = UserFactory(email="*****@*****.**") SavedSearchFactory(user=user, url='http://test.jobs/search?q=python', feed='http://test.jobs/jobs/feed/rss?', label='Python Jobs') user.save() update_solr_task(settings.TEST_SOLR_INSTANCE) q = '?company={company}&search={search}' q = q.format(company=str(self.company.id), search='shouldWork.com') url = reverse('dashboard') + q response = self.client.post(url) soup = BeautifulSoup(response.content) self.assertEqual(len(soup.select('#row-link-table tr')), 1) def test_search_updates_facet_counts(self): # Add ProfileData to the candidate_user EducationFactory(user=self.candidate_user) AddressFactory(user=self.candidate_user) LicenseFactory(user=self.candidate_user) self.candidate_user.save() # Create a new user with ProfileData user = UserFactory(email="*****@*****.**") SavedSearchFactory(user=user, url='http://test.jobs/search?q=python', feed='http://test.jobs/jobs/feed/rss?', label='Python Jobs') EducationFactory(user=user) AddressFactory(user=user) LicenseFactory(user=user) user.save() update_solr_task(settings.TEST_SOLR_INSTANCE) # Assert there are two users with country codes country_tag = '#Country-details-table .facet-count' q = '?company={company}' q = q.format(company=str(self.company.id)) response = self.client.post(reverse('dashboard') + q) soup = BeautifulSoup(response.content) self.assertEqual(int(soup.select(country_tag)[0].text), 2) # When we search, the facet count updates. q = '?company={company}&search={search}' q = q.format(company=str(self.company.id), search='find') response = self.client.post(reverse('dashboard') + q) soup = BeautifulSoup(response.content) self.assertEqual(int(soup.select(country_tag)[0].text), 1) # Tests to see if redirect from /candidates/ goes to candidates/view/ def test_redirect_to_candidates_views_default_page(self): response = self.client.post('/candidates/') # response returns HttpResponsePermanentRedirect which returns a 301 # status code instead of the normal 302 redirect status code self.assertRedirects(response, '/candidates/view/', status_code=301, target_status_code=200) response = self.client.post(reverse('dashboard')) self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content) company_name = soup.find('h1') company_name = company_name.next self.assertEqual(company_name, self.company.name) # Eventually these opted-in/out will be changed to # track if user is part of company's activity feed def test_candidate_has_opted_in(self): response = self.client.post( reverse('candidate_information', )+'?company='+str(self.company.id)+'&user='******'candidate_information', )+'?company='+str(self.company.id)+'&user='******'candidate_information', )+'?company='+str(self.company.id)+'&user='******'div', {'id': 'candidate-content'}).findAll( 'a', {'class': 'accordion-toggle'}) info = soup.find('div', {'id': 'candidate-content'}).findAll('li') self.assertEqual(len(titles), 6) self.assertEqual(len(info), 16) self.assertEqual(response.status_code, 200) def test_candidate_page_load_without_profileunits_with_activites(self): response = self.client.post( reverse('candidate_information', )+'?company='+str(self.company.id)+'&user='******'div', {'id': 'candidate-content'}).findAll( 'a', {'class': 'accordion-toggle'}) info = soup.find('div', {'id': 'candidate-content'}).findAll('li') self.assertEqual(len(titles), 1) self.assertEqual(len(info), 3) self.assertEqual(response.status_code, 200) def test_candidate_page_load_without_profileunits_and_activites(self): saved_search = SavedSearch.objects.get(user=self.candidate_user) saved_search.delete() response = self.client.post( reverse('candidate_information', )+'?company='+str(self.company.id)+'&user='******'div', {'id': 'candidate-content'}) self.assertFalse(info) self.assertEqual(response.status_code, 404) def test_export_csv(self): response = self.client.post( reverse('export_candidates')+'?company=' + str(self.company.id)+'&ex-t=csv') self.assertTrue(response.content) self.assertEqual(response.status_code, 200) def test_export_xml(self): response = self.client.post( reverse('export_candidates')+'?company=' + str(self.company.id)+'&ex-t=xml') self.assertTrue(response.content.index('candidates')) self.assertEqual(response.status_code, 200) def test_export_json(self): response = self.client.post( reverse('export_candidates')+'?company=' + str(self.company.id)+'&ex-t=json') self.assertTrue(response.content.index('candidates')) self.assertEqual(response.status_code, 200) def test_dashboard_analytics_no_data(self): response = self.client.post( reverse('dashboard')+'?company='+str(self.company.id), {'microsite': 'test.jobs'}) soup = BeautifulSoup(response.content) for selector in ['#total-clicks', '#total-home', '#total-job-views', '#total-search']: container = soup.select(selector) # Empty list means no elements were found. self.assertEqual(container, []) def test_dashboard_analytics_with_data(self): for type_ in ['home', 'listing', 'results']: self.add_analytics_data(type_) num_clicks = 1234 self.add_analytics_data('redirect', num_to_add=num_clicks) response = self.client.post( reverse('dashboard')+'?company='+str(self.company.id)) soup = BeautifulSoup(response.content) for selector in ['#total-clicks', '#total-home', '#total-job-views', '#total-search']: # This should be the parent container for all analytics data # of this type container = soup.select(selector)[0] # All hits, humanized all_hits = container.select('span')[0] if selector == '#total-clicks': expected = '1.2k' else: expected = '2' self.assertEqual(all_hits.text.strip(), expected) # All hits, raw number if selector == '#total-clicks': full = container.attrs['data-original-title'] self.assertEqual(full.strip(), '1,234') else: with self.assertRaises(KeyError): # This is marked as having no effect, which is intended container.attrs['data-original-title'] def test_dashboard_with_no_microsites(self): """ Trying to access the dashboard of a company that has no microsites associated with it should not create malformed solr queries. """ self.microsite.delete() response = self.client.post( reverse('dashboard')+'?company='+str(self.company.id)) self.assertEqual(response.status_code, 200)
class MyProfileTests(MyJobsBase): user_info = {'password1': 'complicated_password', 'email': '*****@*****.**'} def setUp(self): super(MyProfileTests, self).setUp() self.user = UserFactory() def test_primary_name_save(self): """ Saving a primary name when one already exists replaces it with the new primary name. """ initial_name = PrimaryNameFactory(user=self.user) self.assertTrue(initial_name.primary) new_name = NewPrimaryNameFactory(user=self.user) initial_name = Name.objects.get(given_name='Alice') self.assertTrue(new_name.primary) self.assertFalse(initial_name.primary) def test_primary_name_save_multiuser(self): """ Saving primary names when multiple users are present accurately sets and retrieves the correct name """ self.user_2 = UserFactory(email='*****@*****.**') user_2_initial_name = PrimaryNameFactory(user=self.user_2) user_2_new_name = NewPrimaryNameFactory(user=self.user_2) initial_name = PrimaryNameFactory(user=self.user) new_name = NewPrimaryNameFactory(user=self.user) user_2_initial_name = Name.objects.get(given_name='Alice', user=self.user_2) user_2_new_name = Name.objects.get(given_name='Alicia', user=self.user_2) initial_name = Name.objects.get(given_name='Alice', user=self.user) self.assertTrue(new_name.primary) self.assertFalse(initial_name.primary) self.assertTrue(user_2_new_name.primary) self.assertFalse(user_2_initial_name.primary) with self.assertRaises(MultipleObjectsReturned): Name.objects.get(primary=True) Name.objects.get(primary=False) Name.objects.get(given_name='Alice') Name.objects.get(given_name='Alicia') Name.objects.get(primary=True, user=self.user_2) def test_email_activation_creation(self): """ Creating a new secondary email creates a corresponding unactivated ActivationProfile. """ secondary_email = SecondaryEmailFactory(user=self.user) activation = ActivationProfile.objects.get(email=secondary_email.email) self.assertEqual(secondary_email.email, activation.email) def test_send_activation(self): """ The send_activation method in SecondaryEmail should send an activation link to the email address """ secondary_email = SecondaryEmailFactory(user=self.user) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, [secondary_email.email]) self.assertTrue('secondary email' in mail.outbox[0].body) def test_verify(self): """ Clicking the activation link sets the ActivationProfile object to activated and sets the SecondaryEmail object to verified. """ secondary_email = SecondaryEmailFactory(user=self.user) activation = ActivationProfile.objects.get(user=self.user, email=secondary_email.email) response = self.client.get(reverse('registration_activate', args=[activation.activation_key]) + '?verify=%s' % self.user.user_guid) secondary_email = SecondaryEmail.objects.get(user=self.user, email=secondary_email.email) activation = ActivationProfile.objects.get(user=self.user, email=secondary_email.email) self.assertEqual(response.status_code, 200) self.assertTrue(secondary_email.verified) def test_set_primary_email(self): """ Calling the set_as_primary method in the SecondaryEmail removes it from SecondaryEmail, replaces the current address on the User model, and adds the replaced address to the SecondaryEmail table. """ old_primary = self.user.email secondary_email = SecondaryEmailFactory(user=self.user) new_primary = secondary_email.email for email in [old_primary, new_primary]: # Emails must be verified to make them primary. activation = ActivationProfile.objects.get_or_create(user=self.user, email=email)[0] ActivationProfile.objects.activate_user(activation.activation_key) secondary_email = SecondaryEmail.objects.get(email=new_primary) secondary_email.set_as_primary() with self.assertRaises(SecondaryEmail.DoesNotExist): SecondaryEmail.objects.get(email=new_primary) old_email = SecondaryEmail.objects.get(email=old_primary) self.assertTrue(old_email.verified) user = User.objects.get(email=new_primary) def test_duplicate_same_primary_name(self): """ Makes sure that one can not create duplicate primary names. """ primary_name1 = PrimaryNameFactory(user=self.user) primary_name2 = PrimaryNameFactory(user=self.user) num_results = self.user.profileunits_set.filter( content_type__name='name').count() self.assertEqual(num_results, 1) def test_different_primary_name(self): primary_name1 = PrimaryNameFactory(user=self.user) primary_name2 = NewPrimaryNameFactory(user=self.user) primary_name_count = Name.objects.filter(user=self.user, primary=True).count() non_primary_name_count = Name.objects.filter(user=self.user, primary=False).count() self.assertEqual(primary_name_count, 1) self.assertEqual(non_primary_name_count, 1) def test_non_primary_name_to_primary(self): name = NewNameFactory(user=self.user) primary_name1 = PrimaryNameFactory(user=self.user) primary_name_count = Name.objects.filter(user=self.user, primary=True).count() non_primary_name_count = Name.objects.filter(user=self.user, primary=False).count() self.assertEqual(primary_name_count, 1) self.assertEqual(non_primary_name_count, 0) def test_primary_name_to_non_primary(self): primary_name = PrimaryNameFactory(user=self.user) primary_name.primary = False primary_name.save() primary_name_count = Name.objects.filter(user=self.user, primary=True).count() non_primary_name_count = Name.objects.filter(user=self.user, primary=False).count() self.assertEqual(primary_name_count, 0) self.assertEqual(non_primary_name_count, 1) def test_duplicate_name(self): """ Makes sure that duplicate names is not saving. """ name1 = NewNameFactory(user=self.user) name2 = NewNameFactory(user=self.user) num_results = Name.objects.filter(user=self.user).count() self.assertEqual(num_results, 1) def test_unverified_primary_email(self): """ Only verified emails can be set as the primary email """ old_primary = self.user.email secondary_email = SecondaryEmailFactory(user=self.user) primary = secondary_email.set_as_primary() with self.assertRaises(SecondaryEmail.DoesNotExist): SecondaryEmail.objects.get(email=old_primary) self.assertFalse(primary) user = User.objects.get(email=old_primary) self.assertEqual(user.email, old_primary) def test_maintain_verification_state(self): """ For security reasons, the state of verification of the user email should be the same as it is when it is transferred into SecondaryEmail """ old_primary = self.user.email self.user.is_active = False self.user.save() secondary_email = SecondaryEmailFactory(user=self.user) activation = ActivationProfile.objects.get(user=self.user, email=secondary_email.email) ActivationProfile.objects.activate_user(activation.activation_key) secondary_email = SecondaryEmail.objects.get(user=self.user, email=secondary_email.email) new_primary = secondary_email.email secondary_email.set_as_primary() old_email = SecondaryEmail.objects.get(email=old_primary) self.assertFalse(old_email.verified) user = User.objects.get(email=new_primary) def test_same_secondary_email(self): """ All emails are unique. If an email is used as a user's primary email or another secondary email, it may not be used as a secondary email again. """ secondary_email = SecondaryEmailFactory(user=self.user) with transaction.atomic(): with self.assertRaises(IntegrityError): new_secondary_email = SecondaryEmailFactory(user=self.user) new_secondary_email = SecondaryEmailFactory(user=self.user, email='*****@*****.**') def test_delete_secondary_email(self): """ Deleting a secondary email should also delete its activation profile """ self.assertEqual(ActivationProfile.objects.count(), 0) secondary_email = SecondaryEmailFactory(user=self.user) self.assertEqual(ActivationProfile.objects.count(), 1) secondary_email.delete() self.assertEqual(ActivationProfile.objects.count(), 0) def test_add_military_service(self): military_service = MilitaryServiceFactory(user=self.user) military_service.save() ms_object = ProfileUnits.objects.filter( content_type__name="military service").count() self.assertEqual(ms_object, 1) def test_add_license(self): license_form = LicenseFactory(user=self.user) license_form.save() ms_object = ProfileUnits.objects.filter( content_type__name="license").count() self.assertEqual(ms_object, 1) def test_add_website(self): website_instance = WebsiteFactory(user=self.user) website_instance.save() ms_object = ProfileUnits.objects.filter( content_type__name="website").count() self.assertEqual(ms_object, 1) def test_add_summary(self): summary_instance = SummaryFactory(user=self.user) summary_instance.save() ms_object = ProfileUnits.objects.filter( content_type__name="summary").count() self.assertEqual(ms_object, 1) def test_add_volunteer_history(self): vh_instance = VolunteerHistoryFactory(user=self.user) vh_instance.save() ms_object = ProfileUnits.objects.filter( content_type__name="volunteer history").count() self.assertEqual(ms_object, 1)
class MyJobsAdminTests(MyJobsBase): def setUp(self): super(MyJobsAdminTests, self).setUp() self.user.set_password('5UuYquA@') self.user.is_superuser = True self.user.save() self.account_owner = UserFactory(email='*****@*****.**') SeoSiteFactory(domain='secure.my.jobs') mail.outbox = [] self.data = {'_selected_action': [unicode(self.account_owner.pk)], 'action': 'request_account_access'} def test_request_access_to_staff(self): """ Requesting access to a staff/superuser account is not allowed. """ self.account_owner.is_staff = True self.account_owner.save() # Selecting the action in the User changelist and pressing "OK" should # immediately show a notification at the top of the page. response = self.client.post( reverse('admin:myjobs_user_changelist'), self.data, follow=True) self.assertEqual(len(mail.outbox), 0) self.assertContains(response, ("Requesting access to staff or " "superusers is not supported.")) # Manually constructing a post to the relevant url should redirect # to the User changelist and not send notification emails. response = self.client.post( reverse('request-account-access', kwargs={'uid': self.account_owner.pk}), {'reason': 'reason here'}) self.assertRedirects(response, reverse('admin:myjobs_user_changelist')) self.assertEqual(len(mail.outbox), 0) def test_request_access_to_non_staff(self): """ Requesting access to a non-staff/superuser account succeeds if the target is not a staff/superuser account and the requesting staff member provides a reason. """ # Request access to an account to get to the request form. response = self.client.post( reverse('admin:myjobs_user_changelist'), self.data, follow=True) self.assertContains(response, "What is the nature of this request?", msg_prefix="Did not redirect to the request form") url = reverse('request-account-access', kwargs={'uid': self.account_owner.pk}) last_redirect = response.redirect_chain[-1][0] # If the admin action determines that it is valid, it redirects. Ensure # we redirected to the expected location. self.assertTrue(last_redirect.endswith(url), msg="Did not redirect as expected") # Try submitting the request form without a reason. response = self.client.post(url) self.assertContains(response, "This field is required.", msg_prefix=("Form error not present on invalid " "submission")) self.assertEqual(len(mail.outbox), 0, msg="Mail sent despite form errors") # # Submit again, providing a reason. self.client.post(url, {'reason': 'reason here'}) self.assertEqual(len(mail.outbox), 1, msg="Mail did not send on successful form submission") email = mail.outbox[0] self.assertTrue(self.account_owner.email in email.to, msg="Email was sent to the wrong user") self.assertTrue('reason here' in email.body, msg="Account access reason was not in the sent email") def test_export_as_csv_admin_action(self): """ Tests the ability to export the list of destination manipulations as a CSV. """ email = '*****@*****.**' password = '******' UserFactory( email=email, password=password, is_staff=True, is_superuser=True) # only superusers are allowed to use Django amin self.client.login(username=email, password=password) manipulations = [DestinationManipulationFactory(view_source=i) for i in range(200, 210)] # this is the format we expect results to be in if deserializing CSV # into a list of dicts formatted_manipulations = [{ u'View Source': unicode(m.view_source), u'View Source Name': '', u'BUID': unicode(m.buid), u'Action Type': unicode(m.action_type), u'Value 1': unicode(m.value_1), u'Value 2': unicode(m.value_2), u'Action': unicode(m.action) } for m in manipulations] args = { 'action': 'export_as_csv', '_selected_action': [ unicode(m.pk) for m in manipulations[:5] ] } changelist_url = reverse( "admin:redirect_destinationmanipulation_changelist") # asking to export as csv when selected items should serialize only # those items response = self.client.post(changelist_url, args) reader = csv.DictReader(response.content.split('\r\n')) self.assertItemsEqual(list(reader), formatted_manipulations[:5]) args['select_across'] = '1' # choosing "select all" should export all records in the queryset, # regardless of the list of selected items passed response = self.client.post(changelist_url, args) reader = csv.DictReader(response.content.split('\r\n')) self.assertItemsEqual(list(reader), formatted_manipulations)
class MyDashboardViewsTests(TestCase): def setUp(self): self.staff_user = UserFactory() group = Group.objects.get(name=CompanyUser.GROUP_NAME) self.staff_user.groups.add(group) self.staff_user.save() self.company = CompanyFactory() self.company.save() self.admin = CompanyUserFactory(user=self.staff_user, company=self.company) self.admin.save() self.microsite = MicrositeFactory(company=self.company) self.microsite.save() self.client = TestClient() self.client.login_user(self.staff_user) self.candidate_user = UserFactory(email="*****@*****.**") SavedSearchFactory(user=self.candidate_user, url='http://test.jobs/search?q=django', label='test Jobs') self.candidate_user.save() for i in range(5): # Create 5 new users user = UserFactory(email='*****@*****.**' % i) for search in SEARCH_OPTS: # Create 15 new searches and assign three per user SavedSearchFactory(user=user, url='http://test.jobs/search?q=%s' % search, label='%s Jobs' % search) def test_number_of_searches_and_users_is_correct(self): response = self.client.post( reverse('dashboard')+'?company='+str(self.company.id), {'microsite': 'test.jobs'}) soup = BeautifulSoup(response.content) # 10 searches total, two rows per search self.assertEqual(len(soup.select('#row-link-table tr')), 20) old_search = SavedSearch.objects.all()[0] old_search.created_on -= timedelta(days=31) old_search.save() response = self.client.post( reverse('dashboard')+'?company='+str(self.company.id), {'microsite': 'test.jobs'}) soup = BeautifulSoup(response.content) self.assertEqual(len(soup.select('#row-link-table tr')), 20) # Tests to see if redirect from /candidates/ goes to candidates/view/ def test_redirect_to_candidates_views_default_page(self): response = self.client.post('/candidates/') # response returns HttpResponsePermanentRedirect which returns a 301 # status code instead of the normal 302 redirect status code self.assertRedirects(response, '/candidates/view/', status_code=301, target_status_code=200) response = self.client.post(reverse('dashboard')) self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content) company_name = soup.find('h1') company_name = company_name.next self.assertEqual(company_name, self.company.name) # Eventually these opted-in/out will be changed to # track if user is part of company's activity feed def test_candidate_has_opted_in(self): response = self.client.post( reverse('candidate_information', )+'?company='+str(self.company.id)+'&user='******'candidate_information', )+'?company='+str(self.company.id)+'&user='******'candidate_information', )+'?company='+str(self.company.id)+'&user='******'div', {'id': 'candidate-content'}).findAll( 'a', {'class': 'accordion-toggle'}) info = soup.find('div', {'id': 'candidate-content'}).findAll('li') self.assertEqual(len(titles), 6) self.assertEqual(len(info), 16) self.assertEqual(response.status_code, 200) def test_candidate_page_load_without_profileunits_with_activites(self): response = self.client.post( reverse('candidate_information', )+'?company='+str(self.company.id)+'&user='******'div', {'id': 'candidate-content'}).findAll( 'a', {'class': 'accordion-toggle'}) info = soup.find('div', {'id': 'candidate-content'}).findAll('li') self.assertEqual(len(titles), 1) self.assertEqual(len(info), 3) self.assertEqual(response.status_code, 200) def test_candidate_page_load_without_profileunits_and_activites(self): saved_search = SavedSearch.objects.get(user=self.candidate_user) saved_search.delete() response = self.client.post( reverse('candidate_information', )+'?company='+str(self.company.id)+'&user='******'div', {'id': 'candidate-content'}) self.assertFalse(info) self.assertEqual(response.status_code, 404) def test_export_csv(self): response = self.client.post( reverse('export_candidates')+'?company=' + str(self.company.id)+'&ex-t=csv') self.assertTrue(response.content) self.assertEqual(response.status_code, 200) def test_export_pdf(self): response = self.client.post( reverse('export_candidates')+'?company=' + str(self.company.id)+'&ex-t=pdf') self.assertTrue(response.content.index('PDF')) self.assertEqual(response.templates[0].name, 'mydashboard/export/candidate_listing.html') self.assertEqual(response.status_code, 200) def test_export_xml(self): response = self.client.post( reverse('export_candidates')+'?company=' + str(self.company.id)+'&ex-t=xml') self.assertTrue(response.content.index('candidates')) self.assertEqual(response.status_code, 200) def test_export_json(self): response = self.client.post( reverse('export_candidates')+'?company=' + str(self.company.id)+'&ex-t=json') self.assertTrue(response.content.index('candidates')) self.assertEqual(response.status_code, 200)
def test_analytics_log_parsing(self): """ Ensure that analytics logs are parsed and stored in solr correctly """ company = CompanyFactory(id=1) business_unit = BusinessUnitFactory(id=1000) company.job_source_ids.add(business_unit) # match and no_match will be used later to ensure that the correct # number of documents were associated with a company or associated # with the default company match = Mock( wraps=lambda: self.assertEqual(doc['company_id'], company.pk)) no_match = Mock( wraps=lambda: self.assertEqual(doc['company_id'], 999999)) for log_type in ['analytics', 'redirect']: log = MockLog(log_type=log_type) parse_log([log], self.test_solr) solr = Solr() results = solr.search(q='uid:analytics*') # fake logs contain two lines - one human and one bot hit # If it is getting processed correctly, there should be only one # hit recorded self.assertEqual(results.hits, 1) multi_field = 'facets' if log_type == 'redirect': with self.assertRaises(KeyError): results.docs[0][multi_field] else: self.assertEqual(len(results.docs[0][multi_field]), 2) for field in results.docs[0].keys(): if field != multi_field: self.assertTrue(type(results.docs[0][field] != list)) uuid.UUID(results.docs[0]['aguid']) with self.assertRaises(KeyError): results.docs[0]['User_user_guid'] for doc in results.docs: if doc['job_view_buid'] == business_unit.pk: # If business units match, company ids should match match() else: # Business units don't match; company id should be set to # the default company no_match() solr.delete() user = UserFactory(email="*****@*****.**") user.user_guid = '1e5f7e122156483f98727366afe06e0b' user.save() parse_log([log], self.test_solr) results = solr.search(q='uid:analytics*') for guid in ['aguid', 'User_user_guid']: uuid.UUID(results.docs[0][guid]) solr.delete() user.delete() # We have already determined that there are only two documents. # Ensure that there is exactly one document that matches a specific # company and one document that was given the default company self.assertEqual(match.call_count, 1) self.assertEqual(no_match.call_count, 1)
class MySearchViewTests(MyJobsBase): 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', 'jobs_per_email': 5, '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.patcher = patch('urllib2.urlopen', return_file()) self.patcher.start() def tearDown(self): super(MySearchViewTests, self).tearDown() try: self.patcher.stop() except RuntimeError: # patcher was stopped in a test pass 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_saved_search_digest_options(self): response = self.client.get(reverse('saved_search_main')) self.assertTrue('Digest Options' in response.content) self.assertFalse('digest-option' in response.content) self.assertTrue(self.new_form.is_valid()) self.new_form.save() response = self.client.get(reverse('saved_search_main')) self.assertTrue('Digest Options' in response.content) self.assertTrue('digest-option' in response.content) 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.assertTrue(self.new_form.is_valid()) 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.assertTrue(self.new_form.is_valid()) 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 = {u'rss_url': u'http://www.my.jobs/jobs/feed/rss', u'feed_title': u'My.jobs - Jobs', u'url_status': u'valid'} content = json.loads(response.content) self.assertEqual(content['rss_url'], data['rss_url']) self.assertEqual(content['url_status'], data['url_status']) 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=%s' % ( search.id, self.user.user_guid)) 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))) def test_delete_all_searches(self): """ Deleting all searches should only remove regular saved searches if the partner saved searches weren't created by the user trying to use it. """ user = UserFactory(email='*****@*****.**') company = CompanyFactory(id=2423, name="Bacon Factory", user_created=False) SavedSearchFactory(user=self.user) pss = PartnerSavedSearchFactory(user=self.user, created_by=user, provider=company) response = self.client.get(reverse('delete_saved_search') + '?id=ALL') self.assertEqual(response.status_code, 302) # partner saved search should still exist... self.assertTrue(models.PartnerSavedSearch.objects.filter( pk=pss.pk).exists()) # ... but the regular saved search shouldn't self.assertFalse(models.SavedSearch.objects.filter( partnersavedsearch__isnull=True).exists()) 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=%s' % ( search.id, self.user.user_guid)) 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))) def test_widget_with_saved_search(self): search = SavedSearchFactory(user=self.user) response = self.client.get(reverse('saved_search_widget') + '?url=%s&callback=callback' % ( search.url, )) edit_url = '\\"https://secure.my.jobs%s?id=%s\\"' % ( reverse('edit_search'), search.pk) self.assertTrue(edit_url in response.content) def test_widget_with_partner_saved_search(self): company = CompanyFactory() partner = PartnerFactory(owner=company) ContactFactory(user=self.user, partner=partner) search = PartnerSavedSearchFactory(user=self.user, created_by=self.user, provider=company, partner=partner) response = self.client.get(reverse('saved_search_widget') + '?url=%s&callback=callback' % ( search.url, )) edit_url = '\\"https://secure.my.jobs%s?id=%s&pss=True\\"' % ( reverse('edit_search'), search.pk) self.assertTrue(edit_url in response.content) def test_viewing_feed_on_bad_search(self): search = SavedSearchFactory(user=self.user, url='http://404.com', feed='http://404.com/feed/json') response = self.client.get(reverse( 'view_full_feed') + '?id=%s' % search.id) self.assertIn('The domain for this saved search is no longer valid.', response.content) def test_send_link_appearance(self): """ The button to manually send a saved search should not be displayed when DEBUG=False. If the url is guessed, nothing bad should happen. """ self.user.is_superuser = True self.user.save() saved_search = SavedSearchFactory(user=self.user) partner_search = PartnerSavedSearchFactory(user=self.user, created_by=self.user) ContactFactory(partner=partner_search.partner, user=self.user) for search in [saved_search, partner_search]: full_feed = reverse('view_full_feed') + '?id=%s' % search.id send_url = reverse('send_saved_search') + '?id=%s' % search.id if hasattr(search, 'partnersavedsearch'): send_url += '&is_pss=True' self.client.login_user(self.user) response = self.client.get(full_feed) self.assertNotIn('>Send</a>', response.content) send = self.client.get(send_url) self.assertEqual(send.status_code, 404) self.assertEqual(len(mail.outbox), 0) settings.DEBUG = True self.client.login_user(self.user) response = self.client.get(full_feed) self.assertIn('>Send</a>', response.content) send = self.client.get(send_url) self.assertEqual(send.status_code, 302) self.assertEqual(len(mail.outbox), 1) mail.outbox = [] settings.DEBUG = False def test_send_link_respects_permissions(self): # The send_saved_search view requires that DEBUG be enabled. settings.DEBUG = True self.user.is_superuser = True self.user.save() search = SavedSearchFactory(user=self.user) search_2 = SavedSearchFactory(user=UserFactory(email='*****@*****.**')) send_url = reverse('send_saved_search') + '?id=%s' self.assertEqual(len(mail.outbox), 0) response = self.client.get(send_url % search.pk) self.assertEqual(response.status_code, 302) self.assertEqual(len(mail.outbox), 1) response = self.client.get(send_url % search_2.pk) self.assertEqual(response.status_code, 302) self.assertEqual(len(mail.outbox), 2) settings.DEBUG = False
class PartnerSavedSearchTests(MyJobsBase): def setUp(self): super(PartnerSavedSearchTests, self).setUp() self.user = UserFactory() self.digest = SavedSearchDigestFactory(user=self.user) self.company = CompanyFactory() self.partner = PartnerFactory(owner=self.company) self.contact = ContactFactory(user=self.user, partner=self.partner) self.partner_search = PartnerSavedSearchFactory(user=self.user, created_by=self.user, provider=self.company, partner=self.partner) # Partner searches are normally created with a form, which creates # invitations as a side effect. We're not testing the form, so we # can fake an invitation here. Invitation(invitee_email=self.partner_search.email, invitee=self.partner_search.user, inviting_user=self.partner_search.created_by, inviting_company=self.partner_search.partner.owner, added_saved_search=self.partner_search).save() self.patcher = patch('urllib2.urlopen', return_file()) self.mock_urlopen = self.patcher.start() self.num_occurrences = lambda text, search_str: [match.start() for match in re.finditer( search_str, text)] # classes and ids may get stripped out when pynliner inlines css. # all jobs contain a default (blank) icon, so we can search for that if # we want a job count self.job_icon = 'http://png.nlx.org/100x50/logo.gif' def tearDown(self): super(PartnerSavedSearchTests, self).tearDown() try: self.patcher.stop() except RuntimeError: # patcher was stopped in a test pass def test_send_partner_saved_search_as_saved_search(self): """ When we send saved searches, we assume they are instances of SavedSearch and disregard any subclasses. Ensure that partner saved searches are correctly recorded as sent when this happens. """ search = SavedSearch.objects.get(pk=self.partner_search.pk) mail.outbox = [] self.assertEqual(ContactRecord.objects.count(), 1) self.partner_search.send_email() self.assertEqual(SavedSearchLog.objects.count(), 2) self.assertEqual(ContactRecord.objects.count(), 2) partner_record = ContactRecord.objects.all()[1] partner_email = mail.outbox.pop() search.send_email() self.assertEqual(SavedSearchLog.objects.count(), 3) self.assertEqual(ContactRecord.objects.count(), 3) search_record = ContactRecord.objects.all()[2] search_email = mail.outbox.pop() self.assertEqual(partner_record.notes, search_record.notes) self.assertEqual(partner_email.body, search_email.body) self.assertEqual(partner_record.notes, partner_email.body) self.assertFalse("Your resume is %s%% complete" % self.user.profile_completion in partner_email.body) logs = SavedSearchLog.objects.all()[1:] for log in logs: self.assertTrue(log.was_sent) self.assertIsNotNone(log.contact_record) # These are separate contact records (different pks), but the notes # attached to each are identical. self.assertEqual(logs[0].contact_record.notes, logs[1].contact_record.notes) def test_send_partner_saved_search_in_digest(self): """ Saved search digests bypass the SavedSearch.send_email method. Ensure that partner saved searches are recorded when sent in a digest. """ SavedSearchFactory(user=self.user) self.assertEqual(ContactRecord.objects.count(), 1) self.digest.send_email() self.assertEqual(SavedSearchLog.objects.count(), 2) self.assertEqual(ContactRecord.objects.count(), 2) email = mail.outbox[0] self.assertFalse("Your resume is %s%% complete" % self.user.profile_completion in email.body) log = SavedSearchLog.objects.last() self.assertTrue(log.was_sent) def test_send_partner_saved_search_with_inactive_user(self): self.user.is_active = False self.user.save() mail.outbox = [] self.partner_search.initial_email() email = mail.outbox.pop() self.assertTrue('Your account is not currently active.' in email.body) verify_url = get_activation_link(self.user) profile = ActivationProfile.objects.get(user=self.user, email=self.user.email) self.assertTrue(profile.activation_key in verify_url) self.assertTrue(self.user.user_guid in verify_url) self.assertTrue('https://secure.my.jobs%s' % verify_url in email.body) def test_contact_record_created_by(self): ContactRecord.objects.all().delete() self.partner_search.initial_email() record = ContactRecord.objects.get() self.assertEqual(record.created_by, self.partner_search.created_by) def test_num_occurrences_instance_method(self): # quick sanity checks; searching for a string that doesn't exist # returns an empty list not_found = self.num_occurrences( 'this is not the string you are looking for', self.job_icon) self.assertEqual(not_found, []) # searching for a string that does exist returns all starting indices # for the string found = self.num_occurrences(self.job_icon, self.job_icon) self.assertEqual(found, [0]) def test_partner_saved_search_pads_results(self): """ If a partner saved search results in less than the desired number of results, it should be padded with additional older results. """ self.partner_search.send_email() partner_search_email = mail.outbox.pop() job_count = self.num_occurrences(partner_search_email.body, self.job_icon) self.assertEqual(len(job_count), 2) log = SavedSearchLog.objects.last() self.assertEqual(log.new_jobs, 1) self.assertEqual(log.backfill_jobs, 1) def test_saved_search_new_job_indicator(self): """ Partner saved searches should include indicators for unseen jobs, while job seeker saved searches should not. """ new_job_indicator = '>New! <' search = SavedSearchFactory(user=self.user) search.send_email() search_email = mail.outbox.pop() new_jobs = self.num_occurrences(search_email.body, new_job_indicator) self.assertEqual(len(new_jobs), 0) self.partner_search.send_email() partner_search_email = mail.outbox.pop() new_jobs = self.num_occurrences(partner_search_email.body, new_job_indicator) self.assertEqual(len(new_jobs), 1) def test_partner_saved_search_no_jobs(self): self.partner_search.feed = 'http://google.com' self.partner_search.save() self.partner_search.send_email() email = mail.outbox.pop() self.assertIn('There are no results for today!', email.body) # Confirm last_sent was updated even though there were no jobs. updated_search = SavedSearch.objects.get(pk=self.partner_search.pk) new_last_sent = updated_search.last_sent.replace(tzinfo=None) self.assertNotEqual(self.partner_search.last_sent, new_last_sent) def test_partner_saved_search_digest_no_jobs(self): self.digest.is_active = True self.digest.save() self.partner_search.feed = 'http://google.com' self.partner_search.save() self.partner_search.send_email() for x in range(1, 5): PartnerSavedSearchFactory(user=self.user, created_by=self.user, provider=self.company, feed='http://google.com', partner=self.partner) self.digest.send_email() email = mail.outbox.pop() self.assertEqual(email.body.count('There are no results for today!'), 5) # Confirm last_sent was updated on all searches even though there were # no jobs. kwargs = { 'user': self.user, 'last_sent__isnull': True, } self.assertEqual(SavedSearch.objects.filter(**kwargs).count(), 0)
class MyJobsViewsTests(MyJobsBase): def setUp(self): super(MyJobsViewsTests, self).setUp() self.user = UserFactory() self.client = TestClient() self.client.login_user(self.user) self.events = ['open', 'delivered', 'click'] self.email_user = UserFactory(email='*****@*****.**') self.auth = '%s:%s' % (settings.SENDGRID_BATCH_POST_USER, settings.SENDGRID_BATCH_POST_PASSWORD) self.auth = base64.b64encode(self.auth) def make_messages(self, when, apiversion=2, categories=None): """ Creates test api messages for sendgrid tests. Inputs: :self: the calling object :when: timestamp :apiversion: the version of the API to mimic :category: list of categories that the originating email was sent with; Optional Returns: JSON-esque object if apiversion<3 JSON object is apiversion >=3 """ message = '{{"email":"*****@*****.**","timestamp":"{0}",' \ '"event":"{1}"{2}}}' messages = [] category = '' if categories: categories = ['"%s"' % cat for cat in categories] if len(categories) > 1: category = ',"category":[%s]' % ','.join(categories) else: category = ',"category":%s' % categories[0] for event in self.events: messages.append(message.format(time.mktime(when.timetuple()), event, category)) if apiversion < 3: return '\r\n'.join(messages) else: return_json = ','.join(messages) return '['+return_json+']' def test_change_password_success(self): resp = self.client.post(reverse('edit_account')+'?password', data={'password': '******', 'new_password1': '7dY=Ybtk', 'new_password2': '7dY=Ybtk'}, follow=True) user = User.objects.get(id=self.user.id) self.assertEqual(resp.status_code, 200) self.assertTrue(user.check_password('7dY=Ybtk')) def test_change_password_failure(self): resp = self.client.post(reverse('edit_account')+'?password', data={'password': '******', 'new_password1': '7dY=Ybtk', 'new_password2': 'notNew'}, follow=True) errors = {'new_password2': [u'The new password fields did not match.'], 'new_password1': [u'The new password fields did not match.']} response_errors = resp.context['password_form'].errors self.assertItemsEqual(response_errors, errors) def test_password_without_lowercase_failure(self): resp = self.client.post(reverse('edit_account')+'?password', data={'password': '******', 'new_password1': 'SECRET', 'new_password2': 'SECRET'}, follow=True) errors = {'new_password1': [ u'Invalid Length (Must be 8 characters or more)', u'Based on a common sequence of characters', u'Must be more complex (Must contain 1 or more lowercase ' u'characters)']} response_errors = resp.context['password_form'].errors self.assertItemsEqual(response_errors, errors) def test_password_without_uppercase_failure(self): resp = self.client.post(reverse('edit_account')+'?password', data={'password': '******', 'new_password1': 'secret', 'new_password2': 'secret'}, follow=True) errors = {'new_password1': [ u'Invalid Length (Must be 8 characters or more)', u'Based on a common sequence of characters', u'Must be more complex (Must contain 1 or more uppercase ' u'characers)']} response_errors = resp.context['password_form'].errors self.assertItemsEqual(response_errors, errors) def test_password_without_digit_failure(self): resp = self.client.post(reverse('edit_account')+'?password', data={'password': '******', 'new_password1': 'Secret', 'new_password2': 'Secret'}, follow=True) errors = {'new_password1': [ u'Invalid Length (Must be 8 characters or more)', u'Based on a common sequence of characters', u'Must be more complex (Must contain 1 or more digits)']} response_errors = resp.context['password_form'].errors self.assertItemsEqual(response_errors, errors) def test_password_without_punctuation_failure(self): resp = self.client.post(reverse('edit_account')+'?password', data={'password': '******', 'new_password1': 'S3cr37', 'new_password2': 'S3cr37'}, follow=True) errors = {'new_password1': [ u'Invalid Length (Must be 8 characters or more)', u'Based on a common sequence of characters', u'Must be more complex (Must contain 1 or more punctuation ' u'character)']} response_errors = resp.context['password_form'].errors self.assertItemsEqual(response_errors, errors) def test_partial_successful_profile_form(self): resp = self.client.post(reverse('home'), data={'name-given_name': 'Alice', 'name-family_name': 'Smith', 'name-primary': False, 'action': 'save_profile'}, follow=True) self.assertEquals(resp.content, 'valid') def test_complete_successful_profile_form(self): # Form with only some sections completely filled out should # save successfully resp = self.client.post( reverse('home'), data={'name-given_name': 'Alice', 'name-family_name': 'Smith', 'edu-organization_name': 'Stanford University', 'edu-degree_date': '2012-01-01', 'edu-education_level_code': '6', 'edu-degree_major': 'Basket Weaving', 'work-position_title': 'Rocket Scientist', 'work-organization_name': 'Blamco Inc.', 'work-start_date': '2013-01-01', 'ph-use_code': 'Home', 'ph-area_dialing': '999', 'ph-number': '1234567', 'addr-address_line_one': '123 Easy St.', 'addr-city_name': 'Pleasantville', 'addr-country_sub_division_code': 'IN', 'addr-country_code': 'USA', 'addr-postal_code': '99999', 'action': 'save_profile'}, follow=True) self.assertEquals(resp.content, 'valid') def test_incomplete_profile_form(self): # Form with incomplete sections should return a page with "This field # "is required" errors resp = self.client.post(reverse('home'), data={'name-given_name': 'Alice', 'action': 'save_profile'}, follow=True) self.failIf(resp.context['name_form'].is_valid()) self.assertContains(resp, 'This field is required.') def test_no_profile_duplicates(self): # An initial registration form with errors should save those parts # that are valid. self.client.post(reverse('home'), data={'name-given_name': 'Alice', 'name-family_name': 'Smith', 'name-primary': False, 'education-organization_name': 'U', 'action': 'save_profile'}, follow=True) self.assertEqual(Name.objects.count(), 1) self.assertEqual(Education.objects.count(), 0) self.client.post(reverse('home'), data={'name-given_name': 'Alice', 'name-family_name': 'Smith', 'name-primary': False, 'edu-organization_name': 'U', 'edu-degree_date': '2012-01-01', 'edu-education_level_code': 6, 'edu-degree_major': 'Basket Weaving', 'action': 'save_profile'}, follow=True) self.assertEqual(Name.objects.count(), 1) self.assertEqual(Education.objects.count(), 1) def test_delete_account(self): """ Going to the delete_account view removes a user and their data completely """ self.assertEqual(User.objects.count(), 2) self.client.get(reverse('delete_account'), follow=True) self.assertEqual(User.objects.count(), 1) def test_disable_account(self): """ Going to the disabled account view disables the account, meaning that (1) a new activation key is created, (2) User is set to not active and (3) User is set to disabled. """ custom_signals.create_activation_profile(sender=self, user=self.user, email=self.user.email) profile = ActivationProfile.objects.get(user=self.user) ActivationProfile.objects.activate_user(profile.activation_key) profile = ActivationProfile.objects.get(user=self.user) self.assertEqual(profile.activation_key, 'ALREADY ACTIVATED') self.client.get(reverse('disable_account'), follow=True) user = User.objects.get(id=self.user.id) profile = ActivationProfile.objects.get(user=user) self.assertNotEqual(profile.activation_key, 'ALREADY ACTIVATED') self.assertTrue(user.is_disabled) def test_about_template(self): # About page should return a status code of 200 response = self.client.get(reverse('about')) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'about.html') def test_batch_recent_message_digest(self): """ Posting data created recently should result in one EmailLog instance being created per message and no emails being sent This test is for sendgrid APIs prior to version 3. """ # Create activation profile for user; Used when disabling an account custom_signals.create_activation_profile(sender=self, user=self.user, email=self.user.email) now = date.today() # Submit a batch of three events created recently messages = self.make_messages(now, 2) response = self.client.post(reverse('batch_message_digest'), data=messages, content_type="text/json", HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertEqual(response.status_code, 200) self.assertEqual(EmailLog.objects.count(), 3) process_batch_events() self.assertEqual(len(mail.outbox), 0) for log in EmailLog.objects.all(): self.assertTrue(log.event in self.events) def test_batch_recent_message_digest_api_version_3(self): """ Posting data created recently should result in one EmailLog instance being created per message and no emails being sent This test is for version 3 of the sendgrid API. """ # Create activation profile for user; Used when disabling an account custom_signals.create_activation_profile(sender=self, user=self.user, email=self.user.email) now = date.today() # Submit a batch of three events created recently messages = self.make_messages(now, 3) response = self.client.post(reverse('batch_message_digest'), data=messages, content_type="text/json", HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertEqual(response.status_code, 200) self.assertEqual(EmailLog.objects.count(), 3) process_batch_events() self.assertEqual(len(mail.outbox), 0) for log in EmailLog.objects.all(): self.assertTrue(log.event in self.events) def test_batch_with_one_event(self): """ Version 1 and Version 2 posts that contain a single event are valid JSON and do not play well with our batch digest method. This tests both forms of post to ensure they work. """ now = date.today() # make_messages makes len(self.events) messages. We only want one self.events = ['open'] for api_ver in [2, 3]: messages = self.make_messages(now, api_ver) response = self.client.post(reverse('batch_message_digest'), data=messages, content_type='text/json', HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertEqual(response.status_code, 200) process_batch_events() self.assertEqual(EmailLog.objects.count(), 2) def test_batch_with_category(self): """ When a batch submission contains categories, we should try to link the relevant events with a saved search log. """ now = date.today() log_uuid = uuid.uuid4().hex SavedSearchLog.objects.create(was_sent=True, recipient=self.user, recipient_email=self.user.email, uuid=log_uuid, new_jobs=0, backfill_jobs=0) self.events = ['open'] category = '(stuff|%s)' % log_uuid message = self.make_messages(now, 3, [category]) self.client.post(reverse('batch_message_digest'), data=message, content_type='text/json', HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertEqual(EmailLog.objects.count(), 1) email_log = EmailLog.objects.get() self.assertIn(log_uuid, email_log.category) saved_search_log = SavedSearchLog.objects.get() self.assertEqual(saved_search_log.uuid, log_uuid) # One saved search log can handle multiple sendgrid responses # (multiple bounces, clicks, opens, etc per email). self.assertTrue(email_log in saved_search_log.sendgrid_response.all()) self.assertTrue(saved_search_log.was_received) def test_batch_with_multiple_categories(self): """ Manually-sent saved search emails will contain multiple categories. Ensure nothing breaks horribly when multiple categories are used. If one of the categories is Production, remove it and continue processing (creating EmailLogs, etc). If one of the categories is QC, Staging, Jenkins, or Local, stop processing the event. """ self.assertEqual(EmailLog.objects.count(), 0) now = date.today() categories = ["My.jobs Email", "Production"] message = self.make_messages(now, categories=categories) self.client.post(reverse('batch_message_digest'), data=message, content_type='text/json', HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertEqual(EmailLog.objects.count(), 3) for category in ['QC', 'Staging', 'Jenkins', 'Local']: categories[1] = category message = self.make_messages(now, categories=categories) self.client.post(reverse('batch_message_digest'), data=message, content_type='text/json', HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertEqual(EmailLog.objects.count(), 3) def test_batch_bounce_message_digest(self): now = date.today() message = '[{{"email":"*****@*****.**","timestamp":"{0}",' \ '"event":"bounce","category":"My.jobs email redirect",' \ '"status":418,"reason":"I\'m a teapot!",' \ '"type":"bounced"}}]'.format( time.mktime(now.timetuple())) response = self.client.post(reverse('batch_message_digest'), data=message, content_type='text/json', HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertEqual(response.status_code, 200) self.assertEqual(len(mail.outbox), 1) email = mail.outbox.pop() self.assertEqual(email.subject, 'My.jobs email redirect failure') self.assertEqual(email.from_email, self.user.email) self.assertEqual(email.to, [settings.EMAIL_TO_ADMIN]) for text in ["I'm a teapot!", '418']: self.assertTrue(text in email.body) def test_batch_month_old_message_digest_with_searches(self): """ Posting data created a month ago should result in one EmailLog instance being created per message and one email being sent per user """ # Create activation profile for user; Used when disabling an account custom_signals.create_activation_profile(sender=self, user=self.user, email=self.user.email) week_before_expiration = date.today() - timedelta(days=172) self.user.last_response = week_before_expiration - timedelta(days=1) self.user.save() SavedSearch(user=self.user).save() # Submit a batch of events created a month ago # The owners of these addresses should be sent an email messages = self.make_messages(week_before_expiration) response = self.client.post(reverse('batch_message_digest'), data=messages, content_type="text/json", HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertTrue(response.status_code, 200) self.assertEqual(EmailLog.objects.count(), 3) self.assertEqual( EmailLog.objects.filter( received=week_before_expiration ).count(), 3 ) process_batch_events() self.assertEqual(len(mail.outbox), 1) user = User.objects.get(pk=self.user.pk) self.assertEqual(user.last_response, week_before_expiration) def test_batch_month_old_message_digest_no_searches(self): """ Posting data created a month ago should result in no emails being sent if the user has no saved searches """ # Create activation profile for user custom_signals.create_activation_profile(sender=self, user=self.user, email=self.user.email) month_ago = date.today() - timedelta(days=30) self.user.last_response = month_ago - timedelta(days=1) self.user.save() messages = self.make_messages(month_ago) response = self.client.post(reverse('batch_message_digest'), data=messages, content_type="text/json", HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertTrue(response.status_code, 200) self.assertEqual(EmailLog.objects.count(), 3) self.assertEqual( EmailLog.objects.filter( received=month_ago ).count(), 3 ) process_batch_events() self.assertEqual(len(mail.outbox), 0) def test_batch_month_and_week_old_message_digest(self): """ Posting data created a month and a week ago should result in one EmailLog instance being created per message, no emails being sent, and the user's opt-in status being set to False """ # Create activation profile for user; Used when disabling an account custom_signals.create_activation_profile(sender=self, user=self.user, email=self.user.email) six_months_ago = date.today() - timedelta(days=180) self.user.last_response = six_months_ago - timedelta(days=1) self.user.save() # Submit a batch of events created a month and a week ago # The owners of these addresses should no longer receive email messages = self.make_messages(six_months_ago) response = self.client.post(reverse('batch_message_digest'), data=messages, content_type="text/json", HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertTrue(response.status_code, 200) self.assertEqual(EmailLog.objects.count(), 3) self.assertEqual( EmailLog.objects.filter( received__lte=(date.today() - timedelta(days=180)) ).count(), 3 ) process_batch_events() self.assertEqual(len(mail.outbox), 0) user = User.objects.get(pk=self.user.pk) self.assertFalse(user.opt_in_myjobs) self.assertTrue(user.last_response, six_months_ago) def test_invalid_batch_post(self): response = self.client.post(reverse('batch_message_digest'), data='this is invalid', content_type="text/json", HTTP_AUTHORIZATION='BASIC %s' % self.auth) self.assertEqual(response.status_code, 200) self.assertEqual(EmailLog.objects.count(), 0) def test_invalid_user(self): now = date.today() messages = self.make_messages(now) response = self.client.post(reverse('batch_message_digest'), data=messages, content_type="text/json") self.assertEqual(response.status_code, 403) response = self.client.post(reverse('batch_message_digest'), data=messages, content_type="text/json", HTTP_AUTHORIZATION='BASIC %s' % base64.b64encode( 'does%40not.exist:wrong_pass')) self.assertEqual(response.status_code, 403) def test_redirect_query_params(self): """ If a user is redirected, the next parameter should not be missing query parameters. """ # log out to force redirects self.client.post(reverse('auth_logout')) response = self.client.get(reverse('prm') + '?company=1') self.assertIn(urlencode({'next': '/prm/view?company=1'}), response.get('Location')) def test_anonymous_continue_sending_mail(self): Session.objects.all().delete() self.user.last_response = date.today() - timedelta(days=7) self.user.save() # Navigating to the 'continue sending email' page while logged out... response = self.client.get(reverse('continue_sending_mail')) path = response.request.get('PATH_INFO') self.assertRedirects(response, reverse('home')+'?next='+path) # or with the wrong email address... response = self.client.get(reverse('continue_sending_mail') + '[email protected]') self.assertRedirects(response, reverse('home')) # should result in redirecting to the login page response = self.client.get(reverse('continue_sending_mail') + '?verify=%s' % self.user.user_guid) self.assertRedirects(response, reverse('home')) self.user = User.objects.get(pk=self.user.pk) self.assertEqual(self.user.last_response, date.today()) def test_continue_sending_mail(self): self.user.last_response = date.today() - timedelta(days=7) self.user.save() response = self.client.get(reverse('continue_sending_mail'), data={'user': self.user}, follow=True) self.assertEqual(self.user.last_response, date.today() - timedelta(days=7)) self.assertRedirects(response, '/') self.user = User.objects.get(pk=self.user.pk) self.assertEqual(self.user.last_response, date.today()) def test_redirect_autocreated_user(self): """ When users are created with no password, their password_change flag is set to true; If this is the case, all pages except for a select few should redirect to the password change form """ self.user.password_change = True self.user.save() self.user = User.objects.get(email=self.user.email) response = self.client.get(reverse('saved_search_main')) self.assertEqual(response.status_code, 302) self.assertRedirects(response, reverse('edit_account')+"#as-password") profile = ActivationProfile.objects.get_or_create( user=self.user, email=self.user.email)[0] response = self.client.get(reverse('registration_activate', args=[profile.activation_key])) self.assertEqual(response.status_code, 200) response = self.client.post(reverse('edit_account')+'?password', data={'password': '******', 'new_password1': '7dY=Ybtk', 'new_password2': '7dY=Ybtk'}) # When models are updated, instances still reference old data self.user = User.objects.get(email=self.user.email) self.assertFalse(self.user.password_change) response = self.client.get(reverse('saved_search_main')) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'mysearches/saved_search_main.html') def test_inactive_user_nav(self): """ Test that inactive users can't access restricted apps""" inactive_user = UserFactory(email='*****@*****.**', is_active=False) self.client.login_user(inactive_user) response = self.client.get("/") soup = BeautifulSoup(response.content) self.assertFalse(soup.findAll('a', {'id': 'savedsearch-link'})) def test_user_account_settings(self): """ Test that the communication portion of account settings is not present for inactive users """ def assert_communication_settings_presence(is_verified, contents): """ If is_active is True, assert that div#as-communication exists Else, assert that it does not exist """ communication_div = contents.find('div', {'id': 'as-communication'}) if is_verified is True: self.assertTrue(communication_div) else: self.assertFalse(communication_div) unverified_user = UserFactory(email='*****@*****.**', is_verified=False) for user in [self.user, unverified_user]: self.client.login_user(user) response = self.client.get(reverse('edit_account')) soup = BeautifulSoup(response.content) assert_communication_settings_presence(user.is_verified, soup) def test_case_insensitive_login(self): """ Test that emails are case-insensitive when logging in """ for email in [self.user.email, self.user.email.upper()]: response = self.client.post(reverse('home'), data={'username': email, 'password': '******', 'action': 'login'}) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, '{"url": null,' + ' "units": false,' + ' "gravatar_url": "' + self.user.get_gravatar_url( size=100)+'",' + ' "validation": "valid"}') self.client.get(reverse('auth_logout')) def test_guid_cookies_login_and_off(self): """ Tests logging in and recieving a guid cookie. Logging out deletes guid cookie. """ response = self.client.post(reverse('home'), data={'username': self.user.email, 'password': '******', 'action': 'login'}) self.assertTrue(response.cookies['myguid']) cookie_guid = response.cookies['myguid'] guid = cookie_guid.value self.assertEqual(guid, self.user.user_guid) resp_logoff = self.client.post(reverse('auth_logout')) cookie_guid_off = resp_logoff.cookies['myguid'] guid_off = cookie_guid_off.value self.assertEqual(guid_off, '') def test_jira_login(self): jira = JIRA(options=options, basic_auth=my_agent_auth) self.assertIsNotNone(jira) def test_anonymous_unsubscribe_all_myjobs_emails(self): Session.objects.all().delete() self.assertTrue(self.user.opt_in_myjobs) # Navigating to the unsubscribe page while logged out... response = self.client.get(reverse('unsubscribe_all')) path = response.request.get('PATH_INFO') self.assertRedirects(response, reverse('home')+'?next='+path) # or with the wrong email address... response = self.client.get(reverse('unsubscribe_all') + '[email protected]') # should result in the user's status remaining unchanged # and the user should be redirected to the login page self.assertRedirects(response, reverse('home')) self.user = User.objects.get(id=self.user.id) self.assertTrue(self.user.opt_in_myjobs) # Navigating to the unsubscribe page while logged out # and with the correct email address... self.client.get(reverse('unsubscribe_all') + '?verify=%s' % self.user.user_guid) self.user = User.objects.get(id=self.user.id) # should result in the user's :opt_in_myjobs: attribute being # set to False self.assertFalse(self.user.opt_in_myjobs) def test_unsubscribe_all_myjobs_emails(self): self.assertTrue(self.user.opt_in_myjobs) self.client.get(reverse('unsubscribe_all')) self.user = User.objects.get(id=self.user.id) self.assertFalse(self.user.opt_in_myjobs) def test_opt_out_sends_notifications(self): """ When a user creates a saved search for another user and that user opts out of My.jobs communications, the creator should get a My.jobs message and email notifying them of the opt-out. """ # required fields for saved search company = CompanyFactory() partner = PartnerFactory(owner=company) creator = UserFactory(id=3, email='*****@*****.**') # should not have any messages self.assertFalse(creator.message_set.all()) PartnerSavedSearch.objects.create(user=self.user, provider=company, created_by=creator, partner=partner) # simulate a user opting out self.user.opt_in_myjobs = False self.user.save() self.client.get(reverse('unsubscribe_all')) # creator should have a My.jobs message and email for body in [creator.message_set.first().body, mail.outbox[0].body]: self.assertIn(self.user.email, body) self.assertIn('unsubscribed from one or more saved search emails', body) # email should be sent to right person self.assertIn(creator.email, mail.outbox[0].to) def test_unsubscribe_sends_notifications(self): """ When a user unsubscribes from one or more saved searches, the user who created the saved search should recieve an email and notification. """ # required fields for saved search company = CompanyFactory() partner = PartnerFactory(owner=company) creator = UserFactory(id=3, email='*****@*****.**') # should not have any messages self.assertFalse(creator.message_set.all()) PartnerSavedSearch.objects.create(user=self.user, provider=company, created_by=creator, partner=partner) self.client.get(reverse('unsubscribe_all')) # creator should have a My.jobs message and email for body in [creator.message_set.first().body, mail.outbox[0].body]: self.assertIn(self.user.email, body) self.assertIn('unsubscribed from one or more saved search emails', body) # email should be sent to right person self.assertIn(creator.email, mail.outbox[0].to) def test_toolbar_logged_in(self): self.client.login_user(self.user) response = self.client.get(reverse('toolbar')) expected_response = '"user_fullname": "*****@*****.**"' self.assertIn(expected_response, response.content) def test_toolbar_not_logged_in(self): Session.objects.all().delete() response = self.client.get(reverse('toolbar')) expected_response = '({"user_fullname": "", "user_gravatar": '\ '"", "employer": ""});' self.assertEqual(response.content, expected_response) def test_p3p(self): """ make sure the P3P headers are being set """ self.client.login_user(self.user) response = self.client.get(reverse('toolbar')) p3p = str(response["P3P"]) self.assertEqual('CP="ALL' in p3p, True) def test_topbar_with_invalid_session(self): response = self.client.get( reverse('topbar'), HTTP_X_REQUESTED_WITH='XMLHttpRequest') # ensure topbar shows logged in options self.assertIn(self.user.email, response.content) Session.objects.all().delete() response = self.client.get( reverse('topbar'), HTTP_X_REQUESTED_WITH='XMLHttpRequest') # ensure topbar shows logged out options self.assertIn("Login", response.content) def test_referring_site_in_topbar(self): self.client.get( reverse('toolbar') + '?site_name=Indianapolis%20Jobs&site=http%3A' '%2F%2Findianapolis.jobs&callback=foo', HTTP_REFERER='http://indianapolis.jobs') last_site = self.client.cookies.get('lastmicrosite').value last_name = self.client.cookies.get('lastmicrositename').value response = self.client.get(reverse('home')) self.assertIn(last_site, response.content) self.assertIn(last_name, response.content) def test_messages_in_topbar(self): self.client.login_user(self.user) for num_messages in range(1, 5): # The indicator in the topbar will display a max of three messages. # Test that the correct number of messages is displayed for all # possible counts. infos = MessageInfoFactory.create_batch(size=num_messages, user=self.user) # Mark the first message as read to show that read messages are # not shown. infos[0].mark_read() response = self.client.get(reverse('home')) self.assertTrue('id="menu-inbox">%s<' % (num_messages-1, ) in response.content) if num_messages == 1: # The only message has been read in this instance; it should # not have been displayed. self.assertTrue('No new unread messages' in response.content, 'Iteration %s' % num_messages) for info in infos[1:4]: # Ensure that the 1-3 messages we expect are appearing on # the page. self.assertTrue('message=%s' % info.message.pk in response.content, 'Iteration %s, %s not found' % ( num_messages, 'message=%s' % info.message.pk)) for info in infos[4:]: # Ensure that any additional unread messages beyond 3 are not # displayed. self.assertFalse('message=%s' % info.message.pk in response.content, "Iteration %s, %s exists but shouldn't" % ( num_messages, 'message=%s' % info.message.pk)) Message.objects.all().delete() def test_cas_logged_in(self): response = self.client.get(reverse('cas'), follow=True) self.assertEqual(response.redirect_chain[-1][0].split("?")[0], 'http://www.my.jobs/') def test_cas_not_logged_in(self): self.client.post(reverse('auth_logout')) response = self.client.get(reverse('cas'), follow=True) self.assertEqual(response.redirect_chain[-1][0], 'https://secure.my.jobs/?next=http://www.my.jobs/') def test_user_creation_source(self): """ User.source should be set to the last microsite a new user visited, an explicity defined source, the current site, or https://secure.my.jobs. """ self.client.post(reverse('home'), {'action': 'register', 'email': '*****@*****.**', 'password1': '5UuYquA@', 'password2': '5UuYquA@'}) user = User.objects.get(email='*****@*****.**') # settings.SITE.domain == jobs.directemployers.org. self.assertEqual(user.source, 'jobs.directemployers.org') self.client.get( reverse('toolbar') + '?site_name=Indianapolis%20Jobs&site=http' '%3A%2F%2Findianapolis.jobs&callback=foo', HTTP_REFERER='http://indianapolis.jobs') last_site = self.client.cookies.get('lastmicrosite').value self.assertEqual(last_site, 'http://indianapolis.jobs') self.client.post(reverse('home'), {'action': 'register', 'email': '*****@*****.**', 'password1': '5UuYquA@', 'password2': '5UuYquA@'}) user = User.objects.get(email='*****@*****.**') self.assertEqual(user.source, last_site) def test_contact_FAQ(self): """ Tests the redirect feature when no FAQ is created or visible. Then creates FAQ and checks to make sure it does show. """ response = self.client.post(reverse('contact_faq')) # checks redirect self.assertEqual(response.status_code, 302) faq = FAQ(question="1101", answer="13") faq.save() response = self.client.post(reverse('contact_faq')) self.assertEqual(response.status_code, 200) faq.is_visible = False faq.save() response = self.client.post(reverse('contact_faq')) # checks redirect self.assertEqual(response.status_code, 302) def test_changing_title_content(self): """ Tests that the title logo on the login page changes based on url. """ self.client.logout() response = self.client.get(reverse('home')) content = BeautifulSoup(response.content) title = content.select('div#title')[0] self.assertTrue('The Right Place for' in title.text) response = self.client.get(reverse('home') + '?next=/prm/view/') content = BeautifulSoup(response.content) title = content.select('div#title')[0] self.assertTrue('The new OFCCP regulations' in title.text) def test_manual_account_creation(self): self.client.logout() self.assertEqual(len(mail.outbox), 0) self.client.post(reverse('home'), data={'email': '*****@*****.**', 'password1': '5UuYquA@', 'password2': '5UuYquA@', 'action': 'register'}) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, 'Account Activation for my.jobs')
class NewUserTests(SeleniumTestCase): """Tests Account creation""" def setUp(self): super(NewUserTests, self).setUp() company = CompanyFactory() self.user = UserFactory(first_name="John", last_name="Doe") admin_role = RoleFactory(company=company, name='Admin') self.user.roles.add(self.admin_role) def test_home_page_works(self): """ As John, navigating to https://secure.my.jobs should send me to a page titled "My.jobs". """ self.browser.get(self.live_server_url) self.assertIn(self.browser.title, 'My.jobs') def test_cant_log_in_without_account(self): """ As John, I shouldn't be able to log into My.jobs without registering first. """ self.browser.get('/'.join([self.live_server_url, 'prm', 'view'])) # We're trying to access a private page while unauthenticated, which # should result in a next parameter being added. self.assertTrue('next=' in self.browser.current_url) # attempt to log in username = self.find('id_username') username.send_keys(self.user.email) self.find('id_password').send_keys(self.user.password) self.find('login').click() # If we've logged in, the next parameter should have went away. We # aren't expecting to be logged in right now as the password was bad. self.assertTrue('next=' in self.browser.current_url) def test_user_registration(self): """ As John, I should be able to register on My.jobs and log in. """ self.browser.get('/'.join([self.live_server_url, 'prm', 'view'])) # register self.find('id_email').send_keys('*****@*****.**') self.find('id_password1').send_keys('aaAA11..') self.find('id_password2').send_keys('aaAA11..') self.find('register').click() try: WebDriverWait(self.browser, 10).until( expected_conditions.presence_of_element_located( (By.ID, 'profile'))) finally: self.assertEqual(self.find('profile').get_attribute( 'innerHTML'), 'Skip: Take me to my profile') def test_user_login(self): self.user.set_password("test") self.user.save() self.find('id_username').send_keys(self.user.email) self.find('id_password').send_keys("test") self.find('login').click()
class EmailForwardTests(RedirectBase): def setUp(self): super(EmailForwardTests, self).setUp() self.redirect_guid = JOB['guid'] self.redirect = RedirectFactory(buid=JOB['buid'], guid='{%s}' % uuid.UUID(self.redirect_guid)) self.password = '******' self.user = UserFactory(email='*****@*****.**') self.user.set_password(self.password) self.user.save() self.contact = CompanyEmail.objects.create( buid=self.redirect.buid, email=self.user.email) self.email = self.user.email.replace('@', '%40') self.auth = { 'bad': [ '', 'Basic %s' % base64.b64encode('bad%40email:wrong_pass')], 'good': 'Basic %s' % base64.b64encode('%s:%s' % (self.user.email.\ replace('@', '%40'), self.password))} self.post_dict = {'to': '*****@*****.**', 'from': '*****@*****.**', 'text': 'Questions about stuff', 'html': '<b>Questions about stuff</b>', 'subject': 'Bad Email', 'attachments': 0} self.r = Replacer() self.r.replace('pysolr.Solr.search', mock_search) def tearDown(self): super(EmailForwardTests, self).tearDown() self.r.restore() def submit_email(self, use_data=True): """ Helper method for submitting parsed emails. Ensures that the request returns a status of 200. Inputs: :use_data: Should we include post data; Default: True Outputs: :response: HttpResponse from email redirect view """ auth = self.auth.get('good') kwargs = {'HTTP_AUTHORIZATION': auth} if use_data: kwargs['data'] = self.post_dict response = self.client.post(reverse('email_redirect'), **kwargs) self.assertEqual(response.status_code, 200) return response def assert_guid_email_responses_are_correct(self, redirect, job=None): """ Helper method for validating parsed [email protected] emails. Inputs: :redirect: Redirect instance to use if a job is old :job: Solr result for a new job """ email = mail.outbox.pop(0) self.assertEqual(email.from_email, settings.DEFAULT_FROM_EMAIL) self.assertEqual(email.to, [self.post_dict['from']]) self.assertEqual(email.subject, self.post_dict['subject']) # Emails turn lots of characters into HTML entities. Results from # Solr and the database do not. Unescape the email body so we can # compare the two. parser = HTMLParser.HTMLParser() body = parser.unescape(email.body) if job is not None: self.assertTrue(markdown.markdown(job['description']) in body) else: self.assertTrue(redirect.job_title in body) def test_jira_login(self): jira = JIRA(options=settings.options, basic_auth=settings.my_agent_auth) self.assertIsNotNone(jira) def test_bad_authorization(self): for auth in self.auth.get('bad'): kwargs = {} if auth: # auth_value has a value, so we can pass an HTTP_AUTHORIZATION # header kwargs['HTTP_AUTHORIZATION'] = auth response = self.client.post(reverse('email_redirect'), **kwargs) self.assertTrue(response.status_code, 403) def test_good_authorization(self): self.submit_email(use_data=False) def test_bad_email(self): self.submit_email() self.assertEqual(len(mail.outbox), 0) def test_bad_guid_email(self): self.post_dict['to'] = '*****@*****.**' % ('1'*32) self.post_dict['text'] = 'This address is not in the database' self.submit_email() email = mail.outbox.pop() self.assertEqual(email.subject, 'Email forward failure') self.assertTrue('There is no job associated with this address' in email.body) def test_good_guid_email_new_job(self): self.post_dict['to'] = ['*****@*****.**' % self.redirect_guid] self.post_dict['subject'] = 'Email forward success' self.submit_email() self.assert_guid_email_responses_are_correct(self.redirect, JOB) email = mail.outbox.pop() # email.alternatives is a list of sets as follows: # [('html body', 'text/html')] alternatives = dict((part[1], part[0]) for part in email.alternatives) self.assertTrue(JOB['html_description'] in alternatives['text/html']) self.assertTrue(email.subject.startswith('[ReqID: %s]' % JOB['reqid'])) def test_good_guid_email_new_job_no_user(self): self.contact.delete() self.post_dict['to'] = ['*****@*****.**' % self.redirect_guid] self.post_dict['subject'] = 'Email forward success' self.submit_email() self.assert_guid_email_responses_are_correct(self.redirect, JOB) def test_good_guid_email_old_job(self): guid = '1'*32 redirect = RedirectFactory(guid='{%s}' % uuid.UUID(guid), buid=self.redirect.buid, uid=1) self.post_dict['to'] = ['*****@*****.**' % guid] self.post_dict['subject'] = 'Email forward success' self.submit_email() self.assert_guid_email_responses_are_correct(redirect) email = mail.outbox.pop() self.assertTrue('This job (%s) has expired.' % ( redirect.job_title, ) in email.body) self.assertTrue(email.subject.startswith('[ReqID: Expired]')) def test_good_guid_email_old_job_no_user(self): self.contact.delete() guid = '1'*32 redirect = RedirectFactory(guid='{%s}' % uuid.UUID(guid), buid=self.redirect.buid, uid=1) self.post_dict['to'] = ['*****@*****.**' % guid] self.post_dict['subject'] = 'Email forward success' self.submit_email() self.assert_guid_email_responses_are_correct(redirect) def test_email_with_name(self): self.post_dict['to'] = 'User <*****@*****.**>' % self.redirect_guid self.post_dict['subject'] = 'Email forward success' self.submit_email() email = mail.outbox.pop() def test_no_emails(self): self.post_dict.pop('to') self.submit_email() self.assertEqual(len(mail.outbox), 0) def test_too_many_emails(self): self.post_dict['to'] = '[email protected], [email protected]' self.submit_email() self.assertEqual(len(mail.outbox), 0) def test_prm_email(self): """ If [email protected] is included as a recipient, we repost this email to My.jobs. This is a straight post, which we don't want to do in a testing environment. If we receive a 200 status code and no emails were sent, this was reasonably likely to have completed successfully. """ prm_list = ['*****@*****.**', '*****@*****.**'] for email in prm_list: # SendGrid adds [email protected] to the 'envelope' JSON string # if it appears as a BCC self.post_dict['envelope'] = '{"to":["%s"]}' % email response = self.submit_email() self.assertEqual(response.content, 'reposted') self.assertEqual(len(mail.outbox), 0) del self.post_dict['envelope'] for email in prm_list: self.post_dict['to'] = email response = self.submit_email() self.assertEqual(response.content, 'reposted') self.assertEqual(len(mail.outbox), 0)