From 69614fab18025b4ece99576a146599f7f0e3dbf3 Mon Sep 17 00:00:00 2001 From: Paul Kuruvilla Date: Sat, 15 Jul 2017 19:48:18 +0530 Subject: [PATCH] Extract out test_participant_emails.py --- tests/py/test_email.py | 887 +--------------------------- tests/py/test_participant_emails.py | 868 +++++++++++++++++++++++++++ 2 files changed, 886 insertions(+), 869 deletions(-) create mode 100644 tests/py/test_participant_emails.py diff --git a/tests/py/test_email.py b/tests/py/test_email.py index c162824f27..1f9f44713e 100644 --- a/tests/py/test_email.py +++ b/tests/py/test_email.py @@ -1,356 +1,20 @@ from __future__ import absolute_import, division, print_function, unicode_literals -import json -import Queue -import sys -import threading import time -import urllib import mock from pytest import raises -from gratipay.exceptions import CannotRemovePrimaryEmail, EmailTaken, EmailNotVerified -from gratipay.exceptions import TooManyEmailAddresses, Throttled, EmailAlreadyVerified -from gratipay.exceptions import EmailNotOnFile, ProblemChangingEmail -from gratipay.testing import P, Harness -from gratipay.testing.email import QueuedEmailHarness, SentEmailHarness -from gratipay.models.package import NPM, Package -from gratipay.models.participant import email as _email -from gratipay.utils import encode_for_querystring -from gratipay.cli import queue_branch_email as _queue_branch_email +from gratipay.exceptions import Throttled +from gratipay.testing import Harness +from gratipay.testing.email import SentEmailHarness -class Alice(QueuedEmailHarness): +class TestPut(SentEmailHarness): def setUp(self): - QueuedEmailHarness.setUp(self) - self.alice = self.make_participant('alice', claimed_time='now') - - def add(self, participant, address, _flush=False): - participant.start_email_verification(address) - nonce = participant.get_email(address).nonce - result = participant.finish_email_verification(address, nonce) - assert result == (_email.VERIFICATION_SUCCEEDED, [], None) - if _flush: - self.app.email_queue.flush() - - -class TestEndpoints(Alice): - - def hit_email_spt(self, action, address, user='alice', package_ids=[], should_fail=False): - f = self.client.PxST if should_fail else self.client.POST - - # Aspen's test client should really support URL-encoding POST data for - # us, but it doesn't (it only supports multipart, which I think maybe - # doesn't work because of other Aspen bugs around multiple package_id - # values in the same POST body in that case?), so let's do that - # ourselves. - - data = [ ('action', action) - , ('address', address) - ] + [('package_id', str(p)) for p in package_ids] - body = urllib.urlencode(data) - - response = f( '/~alice/emails/modify.json' - , body=body - , content_type=b'application/x-www-form-urlencoded' - , auth_as=user - , HTTP_ACCEPT_LANGUAGE=b'en' - ) - if issubclass(response.__class__, (Throttled, ProblemChangingEmail)): - response.render_body({'_': lambda a: a}) - return response - - def hit_verify_spt(self, email, nonce, username='alice', should_fail=False): - # Email address is encoded in url. - url = '/~%s/emails/verify.html?email2=%s&nonce=%s' - url %= (username, encode_for_querystring(email), nonce) - f = self.client.GxT if should_fail else self.client.GET - return f(url, auth_as=username) - - def verify_and_change_email(self, old_email, new_email, username='alice', _flush=True): - self.hit_email_spt('add-email', old_email) - nonce = P(username).get_email(old_email).nonce - self.hit_verify_spt(old_email, nonce) - self.hit_email_spt('add-email', new_email) - if _flush: - self.app.email_queue.flush() - - def test_participant_can_start_email_verification(self): - response = self.hit_email_spt('add-email', 'alice@gratipay.com') - assert json.loads(response.body) == 'Check your inbox for a verification link.' - - def test_starting_email_verification_triggers_verification_email(self): - self.hit_email_spt('add-email', 'alice@gratipay.com') - assert self.count_email_messages() == 1 - last_email = self.get_last_email() - assert last_email['to'] == 'alice ' - expected = "We've received a request to connect alice@gratipay.com to the alice account" - assert expected in last_email['body_text'] - - def test_email_address_is_encoded_in_sent_verification_link(self): - address = 'alice@gratipay.com' - encoded = encode_for_querystring(address) - self.hit_email_spt('add-email', address) - last_email = self.get_last_email() - assert "~alice/emails/verify.html?email2="+encoded in last_email['body_text'] - - def test_verification_email_doesnt_contain_unsubscribe(self): - self.hit_email_spt('add-email', 'alice@gratipay.com') - last_email = self.get_last_email() - assert "To stop receiving" not in last_email['body_text'] - - def test_verifying_second_email_sends_verification_notice(self): - self.verify_and_change_email('alice1@example.com', 'alice2@example.com', _flush=False) - assert self.count_email_messages() == 3 - last_email = self.get_last_email() - self.app.email_queue.flush() - assert last_email['to'] == 'alice ' - expected = "We are connecting alice2@example.com to the alice account on Gratipay" - assert expected in last_email['body_text'] - - def test_post_anon_returns_401(self): - response = self.hit_email_spt('add-email', 'anon@example.com', user=None, should_fail=True) - assert response.code == 401 - - def test_post_with_no_at_symbol_is_400(self): - response = self.hit_email_spt('add-email', 'gratipay.com', should_fail=True) - assert response.code == 400 - - def test_post_with_no_period_symbol_is_400(self): - response = self.hit_email_spt('add-email', 'test@gratipay', should_fail=True) - assert response.code == 400 - - def test_post_with_long_address_is_okay(self): - response = self.hit_email_spt('add-email', ('a'*242) + '@example.com') - assert response.code == 200 - - def test_post_with_looooong_address_is_400(self): - response = self.hit_email_spt('add-email', ('a'*243) + '@example.com', should_fail=True) - assert response.code == 400 - - def test_post_too_quickly_is_400(self): - self.hit_email_spt('add-email', 'alice@example.com') - self.hit_email_spt('add-email', 'alice+a@example.com') - self.hit_email_spt('add-email', 'alice+b@example.com') - response = self.hit_email_spt('add-email', 'alice+c@example.com', should_fail=True) - assert response.code == 400 - assert 'too quickly' in response.body - - def test_verify_email_without_adding_email(self): - response = self.hit_verify_spt('', 'sample-nonce') - assert 'Bad Info' in response.body - - def test_verify_email_wrong_nonce(self): - self.hit_email_spt('add-email', 'alice@example.com') - nonce = 'fake-nonce' - result = self.alice.finish_email_verification('alice@gratipay.com', nonce) - assert result == (_email.VERIFICATION_FAILED, None, None) - self.hit_verify_spt('alice@example.com', nonce) - expected = None - actual = P('alice').email_address - assert expected == actual - - def test_verify_email_a_second_time_returns_redundant(self): - address = 'alice@example.com' - self.hit_email_spt('add-email', address) - nonce = self.alice.get_email(address).nonce - self.alice.finish_email_verification(address, nonce) - result = self.alice.finish_email_verification(address, nonce) - assert result == (_email.VERIFICATION_REDUNDANT, None, None) - - def test_verify_email_expired_nonce_fails(self): - address = 'alice@example.com' - self.hit_email_spt('add-email', address) - self.db.run(""" - UPDATE emails - SET verification_start = (now() - INTERVAL '25 hours') - WHERE participant_id = %s; - """, (self.alice.id,)) - nonce = self.alice.get_email(address).nonce - result = self.alice.finish_email_verification(address, nonce) - assert result == (_email.VERIFICATION_FAILED, None, None) - actual = P('alice').email_address - assert actual == None - - def test_finish_email_verification(self): - self.hit_email_spt('add-email', 'alice@example.com') - nonce = self.alice.get_email('alice@example.com').nonce - assert self.hit_verify_spt('alice@example.com', nonce).code == 200 - assert P('alice').email_address == 'alice@example.com' - - def test_empty_email_fails(self): - for empty in ('', ' '): - result = self.alice.finish_email_verification(empty, 'foobar') - assert result == (_email.VERIFICATION_FAILED, None, None) - - def test_empty_nonce_fails(self): - for empty in ('', ' '): - result = self.alice.finish_email_verification('foobar', empty) - assert result == (_email.VERIFICATION_FAILED, None, None) - - def test_email_verification_is_backwards_compatible(self): - """Test email verification still works with unencoded email in verification link. - """ - self.hit_email_spt('add-email', 'alice@example.com') - nonce = self.alice.get_email('alice@example.com').nonce - url = '/~alice/emails/verify.html?email=alice@example.com&nonce='+nonce - self.client.GET(url, auth_as='alice') - expected = 'alice@example.com' - actual = P('alice').email_address - assert expected == actual - - def test_verified_email_is_not_changed_after_update(self): - self.verify_and_change_email('alice@example.com', 'alice@example.net') - expected = 'alice@example.com' - actual = P('alice').email_address - assert expected == actual - - def test_get_emails(self): - self.verify_and_change_email('alice@example.com', 'alice@example.net') - emails = self.alice.get_emails() - assert len(emails) == 2 - - def test_verify_email_after_update(self): - self.verify_and_change_email('alice@example.com', 'alice@example.net') - nonce = self.alice.get_email('alice@example.net').nonce - self.hit_verify_spt('alice@example.net', nonce) - expected = 'alice@example.com' - actual = P('alice').email_address - assert expected == actual - - def test_nonce_is_not_reused_when_resending_email(self): - self.hit_email_spt('add-email', 'alice@example.com') - nonce1 = self.alice.get_email('alice@example.com').nonce - self.hit_email_spt('resend', 'alice@example.com') - nonce2 = self.alice.get_email('alice@example.com').nonce - assert nonce1 != nonce2 - - def test_emails_page_shows_emails(self): - self.verify_and_change_email('alice@example.com', 'alice@example.net') - body = self.client.GET("/~alice/emails/", auth_as="alice").body - assert 'alice@example.com' in body - assert 'alice@example.net' in body - - def test_set_primary(self): - self.verify_and_change_email('alice@example.com', 'alice@example.net') - self.verify_and_change_email('alice@example.net', 'alice@example.org') - self.hit_email_spt('set-primary', 'alice@example.com') - - def test_cannot_set_primary_to_unverified(self): - with self.assertRaises(EmailNotVerified): - self.hit_email_spt('set-primary', 'alice@example.com') - - def test_remove_email(self): - # Can remove unverified - self.hit_email_spt('add-email', 'alice@example.com') - self.hit_email_spt('remove', 'alice@example.com') - - # Can remove verified - self.verify_and_change_email('alice@example.com', 'alice@example.net') - self.verify_and_change_email('alice@example.net', 'alice@example.org') - self.hit_email_spt('remove', 'alice@example.net') - - # Cannot remove primary - with self.assertRaises(CannotRemovePrimaryEmail): - self.hit_email_spt('remove', 'alice@example.com') - - - def test_participant_can_verify_a_package_along_with_email(self): - foo = self.make_package(name='foo', emails=['alice@gratipay.com']) - response = self.hit_email_spt( 'start-verification' - , 'alice@gratipay.com' - , package_ids=[foo.id] - ) - assert json.loads(response.body) == 'Check your inbox for a verification link.' - assert self.db.all('select package_id from claims order by package_id') == [foo.id] - - def test_participant_cant_verify_packages_with_add_email_or_resend(self): - foo = self.make_package(name='foo', emails=['alice@gratipay.com']) - for action in ('add-email', 'resend'): - assert self.hit_email_spt( action - , 'alice@gratipay.com' - , package_ids=[foo.id] - , should_fail=True - ).code == 400 - - def test_participant_can_verify_multiple_packages_along_with_email(self): - package_ids = [self.make_package(name=name, emails=['alice@gratipay.com']).id - for name in ('foo', 'bar', 'baz', 'buz')] - response = self.hit_email_spt( 'start-verification' - , 'alice@gratipay.com' - , package_ids=package_ids - ) - assert json.loads(response.body) == 'Check your inbox for a verification link.' - assert self.db.all('select package_id from claims order by package_id') == package_ids - - def test_package_verification_fails_if_email_not_listed(self): - foo = self.make_package() - response = self.hit_email_spt( 'start-verification' - , 'bob@gratipay.com' - , package_ids=[foo.id] - , should_fail=True - ) - assert response.code == 400 - assert self.db.all('select package_id from claims order by package_id') == [] - - def test_package_verification_fails_if_package_id_is_garbage(self): - response = self.hit_email_spt( 'start-verification' - , 'bob@gratipay.com' - , package_ids=['cheese monkey'] - , should_fail=True - ) - assert response.code == 400 - assert self.db.all('select package_id from claims order by package_id') == [] - - def test_package_reverification_succeeds_if_package_is_already_claimed_by_self(self): - foo = self.make_package() - self.claim_package('alice', foo) - response = self.hit_email_spt( 'start-verification' - , 'alice@example.com' - , package_ids=[foo.id] - ) - assert response.code == 200 - - def test_package_verification_fails_if_package_is_already_claimed_by_other(self): - self.make_participant('bob', claimed_time='now', email_address='bob@example.com') - foo = self.make_package(emails=['alice@example.com', 'bob@example.com']) - self.claim_package('bob', foo) - response = self.hit_email_spt( 'start-verification' - , 'alice@example.com' - , package_ids=[foo.id] - , should_fail=True - ) - assert response.code == 400 - - -class TestFunctions(Alice): - - def test_cannot_update_email_to_already_verified(self): - bob = self.make_participant('bob', claimed_time='now') - self.add(self.alice, 'alice@gratipay.com') - - with self.assertRaises(EmailTaken): - bob.start_email_verification('alice@gratipay.com') - nonce = bob.get_email('alice@gratipay.com').nonce - bob.finish_email_verification('alice@gratipay.com', nonce) - - email_alice = P('alice').email_address - assert email_alice == 'alice@gratipay.com' - - def test_html_escaping(self): - self.alice.start_email_verification("foo'bar@example.com") - last_email = self.get_last_email() - assert 'foo'bar' in last_email['body_html'] - assert ''' not in last_email['body_text'] - - def test_npm_package_name_is_handled_safely(self): - foo = self.make_package(name='