def test_last_sent(self, db_session): to = "*****@*****.**" subject = "I care about this" # Send some random emails aws_client = pretend.stub(send_raw_email=pretend.call_recorder( lambda *a, **kw: {"MessageId": str(uuid.uuid4()) + "-ses"})) sender = SESEmailSender(aws_client, sender="DevPyPI <*****@*****.**>", db=db_session) for address in [to, "*****@*****.**"]: for subject in [subject, "I do not care about this"]: sender.send( f"Foobar <{ to }>", EmailMessage(subject=subject, body_text="This is a plain text body"), ) # Send the last email that we care about resp = {"MessageId": str(uuid.uuid4()) + "-ses"} aws_client = pretend.stub( send_raw_email=pretend.call_recorder(lambda *a, **kw: resp)) sender = SESEmailSender(aws_client, sender="DevPyPI <*****@*****.**>", db=db_session) sender.send( f"Foobar <{ to }>", EmailMessage(subject=subject, body_text="This is a plain text body"), ) em = (db_session.query(SESEmailMessage).filter_by( message_id=resp["MessageId"]).one()) assert sender.last_sent(to, subject) == em.created
def test_send_email_success(self, monkeypatch): class FakeMailSender: def __init__(self): self.emails = [] def send(self, recipient, msg): self.emails.append({ "subject": msg.subject, "body": msg.body_text, "html": msg.body_html, "recipient": recipient, }) sender = FakeMailSender() task = pretend.stub() request = pretend.stub( find_service=pretend.call_recorder(lambda *a, **kw: sender)) msg = EmailMessage(subject="subject", body_text="body") email.send_email(task, request, "recipient", attr.asdict(msg)) assert request.find_service.calls == [pretend.call(IEmailSender)] assert sender.emails == [{ "subject": "subject", "body": "body", "html": None, "recipient": "recipient", }]
def test_renders_plaintext(self, pyramid_config, pyramid_request, monkeypatch): real_render = email_services.render def render(template, *args, **kwargs): if template.endswith(".html"): raise TemplateNotFound(template) return real_render(template, *args, **kwargs) monkeypatch.setattr(email_services, "render", render) subject_renderer = pyramid_config.testing_add_renderer( "email/foo/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer( "email/foo/body.txt") body_renderer.string_response = "Email Body" msg = EmailMessage.from_template("foo", {"my_var": "my value"}, request=pyramid_request) subject_renderer.assert_(my_var="my value") body_renderer.assert_(my_var="my value") assert msg.subject == "Email Subject" assert msg.body_text == "Email Body" assert msg.body_html is None
def wrapper(request, user_or_users, **kwargs): if isinstance(user_or_users, (list, set)): recipients = user_or_users else: recipients = [user_or_users] context = fn(request, user_or_users, **kwargs) msg = EmailMessage.from_template(name, context, request=request) for recipient in recipients: if isinstance(recipient, tuple): user, email = recipient else: user, email = recipient, None _send_email_to_user( request, user, msg, email=email, allow_unverified=allow_unverified, repeat_window=repeat_window, ) return context
def test_renders_html(self, pyramid_config, pyramid_request): subject_renderer = pyramid_config.testing_add_renderer( "email/foo/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer( "email/foo/body.txt") body_renderer.string_response = "Email Body" html_renderer = pyramid_config.testing_add_renderer( "email/foo/body.html") html_renderer.string_response = "<p>Email HTML Body</p>" msg = EmailMessage.from_template("foo", {"my_var": "my value"}, request=pyramid_request) subject_renderer.assert_(my_var="my value") body_renderer.assert_(my_var="my value") html_renderer.assert_(my_var="my value") assert msg.subject == "Email Subject" assert msg.body_text == "Email Body" assert msg.body_html == ( "<html>\n<head></head>\n<body><p>Email HTML Body</p></body>\n</html>\n" )
def test_sends_to_user_with_verified(self, name, username, primary_email, address, expected): task = pretend.stub(delay=pretend.call_recorder(lambda *a, **kw: None)) request = pretend.stub(task=pretend.call_recorder(lambda x: task)) user = pretend.stub( name=name, username=username, primary_email=pretend.stub(email=primary_email, verified=True), ) if address is not None: address = pretend.stub(email=address, verified=True) msg = EmailMessage(subject="My Subject", body_text="My Body") email._send_email_to_user(request, user, msg, email=address) assert request.task.calls == [pretend.call(email.send_email)] assert task.delay.calls == [ pretend.call( expected, { "subject": "My Subject", "body_text": "My Body", "body_html": None }, ) ]
def test_renders_plaintext(self, pyramid_config, pyramid_request, monkeypatch): real_render = email_services.render def render(template, *args, **kwargs): if template.endswith(".html"): raise TemplateNotFound(template) return real_render(template, *args, **kwargs) monkeypatch.setattr(email_services, "render", render) subject_renderer = pyramid_config.testing_add_renderer("email/foo/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer("email/foo/body.txt") body_renderer.string_response = "Email Body" msg = EmailMessage.from_template( "foo", {"my_var": "my value"}, request=pyramid_request ) subject_renderer.assert_(my_var="my value") body_renderer.assert_(my_var="my value") assert msg.subject == "Email Subject" assert msg.body_text == "Email Body" assert msg.body_html is None
def send_email(task, request, recipient, msg): msg = EmailMessage(**msg) sender = request.find_service(IEmailSender) try: sender.send(recipient, msg) except Exception as exc: task.retry(exc=exc)
def test_two_factor_email( self, pyramid_request, pyramid_config, monkeypatch, action, method, pretty_method, ): stub_user = pretend.stub( username="******", name="", email="*****@*****.**", primary_email=pretend.stub(email="*****@*****.**", verified=True), ) subject_renderer = pyramid_config.testing_add_renderer( f"email/two-factor-{action}/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer( f"email/two-factor-{action}/body.txt") body_renderer.string_response = "Email Body" html_renderer = pyramid_config.testing_add_renderer( f"email/two-factor-{action}/body.html") html_renderer.string_response = "Email HTML Body" send_email = pretend.stub( delay=pretend.call_recorder(lambda *args, **kwargs: None)) pyramid_request.task = pretend.call_recorder( lambda *args, **kwargs: send_email) monkeypatch.setattr(email, "send_email", send_email) send_method = getattr(email, f"send_two_factor_{action}_email") result = send_method(pyramid_request, stub_user, method=method) assert result == { "method": pretty_method, "username": stub_user.username } subject_renderer.assert_() body_renderer.assert_(method=pretty_method, username=stub_user.username) html_renderer.assert_(method=pretty_method, username=stub_user.username) assert pyramid_request.task.calls == [pretend.call(send_email)] assert send_email.delay.calls == [ pretend.call( f"{stub_user.username} <{stub_user.email}>", attr.asdict( EmailMessage( subject="Email Subject", body_text="Email Body", body_html=( "<html>\n<head></head>\n" "<body><p>Email HTML Body</p></body>\n</html>\n"), )), ) ]
def send_email(task, request, recipient, msg, success_event): msg = EmailMessage(**msg) sender = request.find_service(IEmailSender) try: sender.send(recipient, msg) user_service = request.find_service(IUserService, context=None) user_service.record_event(**success_event) except Exception as exc: task.retry(exc=exc)
def test_primary_email_change_email(self, pyramid_request, pyramid_config, monkeypatch): stub_user = pretend.stub(email="*****@*****.**", username="******", name="") subject_renderer = pyramid_config.testing_add_renderer( "email/primary-email-change/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer( "email/primary-email-change/body.txt") body_renderer.string_response = "Email Body" html_renderer = pyramid_config.testing_add_renderer( "email/primary-email-change/body.html") html_renderer.string_response = "Email HTML Body" send_email = pretend.stub( delay=pretend.call_recorder(lambda *args, **kwargs: None)) pyramid_request.task = pretend.call_recorder( lambda *args, **kwargs: send_email) monkeypatch.setattr(email, "send_email", send_email) result = email.send_primary_email_change_email( pyramid_request, (stub_user, pretend.stub(email="*****@*****.**", verified=True)), ) assert result == { "username": stub_user.username, "old_email": "*****@*****.**", "new_email": stub_user.email, } subject_renderer.assert_() body_renderer.assert_(username=stub_user.username) html_renderer.assert_(username=stub_user.username) assert pyramid_request.task.calls == [pretend.call(send_email)] assert send_email.delay.calls == [ pretend.call( "username <*****@*****.**>", attr.asdict( EmailMessage( subject="Email Subject", body_text="Email Body", body_html=( "<html>\n<head></head>\n" "<body><p>Email HTML Body</p></body>\n</html>\n"), )), ) ]
def test_account_deletion_email(self, pyramid_request, pyramid_config, monkeypatch): stub_user = pretend.stub( username="******", name="", email="*****@*****.**", primary_email=pretend.stub(email="*****@*****.**", verified=True), ) subject_renderer = pyramid_config.testing_add_renderer( "email/account-deleted/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer( "email/account-deleted/body.txt") body_renderer.string_response = "Email Body" html_renderer = pyramid_config.testing_add_renderer( "email/account-deleted/body.html") html_renderer.string_response = "Email HTML Body" send_email = pretend.stub( delay=pretend.call_recorder(lambda *args, **kwargs: None)) pyramid_request.task = pretend.call_recorder( lambda *args, **kwargs: send_email) monkeypatch.setattr(email, "send_email", send_email) result = email.send_account_deletion_email(pyramid_request, stub_user) assert result == {"username": stub_user.username} subject_renderer.assert_() body_renderer.assert_(username=stub_user.username) html_renderer.assert_(username=stub_user.username) assert pyramid_request.task.calls == [pretend.call(send_email)] assert send_email.delay.calls == [ pretend.call( f"{stub_user.username} <{stub_user.email}>", attr.asdict( EmailMessage( subject="Email Subject", body_text="Email Body", body_html=( "<html>\n<head></head>\n" "<body><p>Email HTML Body</p></body>\n</html>\n"), )), ) ]
def test_doesnt_send_with_unverified(self, primary_email, address): task = pretend.stub(delay=pretend.call_recorder(lambda *a, **kw: None)) request = pretend.stub(task=pretend.call_recorder(lambda x: task)) user = pretend.stub(primary_email=pretend.stub( email=primary_email, verified=True if address is not None else False)) if address is not None: address = pretend.stub(email=address, verified=False) msg = EmailMessage(subject="My Subject", body_text="My Body") email._send_email_to_user(request, user, msg, email=address) assert request.task.calls == [] assert task.delay.calls == []
def test_send_with_plaintext(self, db_session): resp = {"MessageId": str(uuid.uuid4()) + "-ses"} aws_client = pretend.stub( send_raw_email=pretend.call_recorder(lambda *a, **kw: resp) ) sender = SESEmailSender( aws_client, sender="DevPyPI <*****@*****.**>", db=db_session ) sender.send( "Foobar <*****@*****.**>", EmailMessage( subject="This is a Subject", body_text="This is a plain text body" ), ) assert aws_client.send_raw_email.calls == [ pretend.call( Source="DevPyPI <*****@*****.**>", Destinations=["Foobar <*****@*****.**>"], RawMessage={ "Data": ( b"Subject: This is a Subject\n" b"From: DevPyPI <*****@*****.**>\n" b"To: Foobar <*****@*****.**>\n" b'Content-Type: text/plain; charset="utf-8"\n' b"Content-Transfer-Encoding: 7bit\n" b"MIME-Version: 1.0\n" b"\n" b"This is a plain text body\n" ) }, ) ] em = ( db_session.query(SESEmailMessage) .filter_by(message_id=resp["MessageId"]) .one() ) assert em.from_ == "*****@*****.**" assert em.to == "*****@*****.**" assert em.subject == "This is a Subject"
def wrapper(request, user_or_users, **kwargs): if isinstance(user_or_users, (list, set)): recipients = user_or_users else: recipients = [user_or_users] context = fn(request, user_or_users, **kwargs) msg = EmailMessage.from_template(name, context, request=request) for recipient in recipients: if isinstance(recipient, tuple): user, email = recipient else: user, email = recipient, None _send_email_to_user( request, user, msg, email=email, allow_unverified=allow_unverified ) return context
def test_send(self, capsys): mailer = DummyMailer() service = ConsoleAndSMTPEmailSender( mailer, sender="DevPyPI <*****@*****.**>") service.send( "*****@*****.**", EmailMessage(subject="a subject", body_text="a body", body_html="a html body"), ) captured = capsys.readouterr() expected = """ Email sent Subject: a subject From: DevPyPI <*****@*****.**> To: [email protected] HTML: Visualize at http://localhost:1080 Text: a body""" assert captured.out.strip() == expected.strip()
def test_send(self, sender_class): mailer = DummyMailer() service = sender_class(mailer, sender="DevPyPI <*****@*****.**>") service.send( "*****@*****.**", EmailMessage(subject="a subject", body_text="a body", body_html="a html body"), ) assert len(mailer.outbox) == 1 msg = mailer.outbox[0] assert msg.subject == "a subject" assert msg.body == "a body" assert msg.html == "a html body" assert msg.recipients == ["*****@*****.**"] assert msg.sender == "DevPyPI <*****@*****.**>"
def test_send_email_failure(self, monkeypatch): exc = Exception() class FakeMailSender: def send(self, recipient, msg): raise exc class Task: @staticmethod @pretend.call_recorder def retry(exc): raise celery.exceptions.Retry sender, task = FakeMailSender(), Task() request = pretend.stub(find_service=lambda *a, **kw: sender) msg = EmailMessage(subject="subject", body_text="body") with pytest.raises(celery.exceptions.Retry): email.send_email(task, request, "recipient", attr.asdict(msg)) assert task.retry.calls == [pretend.call(exc=exc)]
def test_renders_html(self, pyramid_config, pyramid_request): subject_renderer = pyramid_config.testing_add_renderer("email/foo/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer("email/foo/body.txt") body_renderer.string_response = "Email Body" html_renderer = pyramid_config.testing_add_renderer("email/foo/body.html") html_renderer.string_response = "<p>Email HTML Body</p>" msg = EmailMessage.from_template( "foo", {"my_var": "my value"}, request=pyramid_request ) subject_renderer.assert_(my_var="my value") body_renderer.assert_(my_var="my value") html_renderer.assert_(my_var="my value") assert msg.subject == "Email Subject" assert msg.body_text == "Email Body" assert msg.body_html == ( "<html>\n<head></head>\n<body><p>Email HTML Body</p></body>\n</html>\n" )
def test_send_with_unicode_and_html(self, db_session): # Determine what the random boundary token will be import random import sys random.seed(42) token = random.randrange(sys.maxsize) random.seed(42) resp = {"MessageId": str(uuid.uuid4()) + "-ses"} aws_client = pretend.stub( send_raw_email=pretend.call_recorder(lambda *a, **kw: resp)) sender = SESEmailSender(aws_client, sender="DevPyPI <*****@*****.**>", db=db_session) sender.send( "Fööbar <*****@*****.**>", EmailMessage( subject="This is a Subject", body_text="This is a plain text body", body_html="<p>This is a html body! 💩</p>", ), ) assert aws_client.send_raw_email.calls == [ pretend.call( Source="DevPyPI <*****@*****.**>", Destinations=["Fööbar <*****@*****.**>"], RawMessage={ "Data": (b"Subject: This is a Subject\n" b"From: DevPyPI <*****@*****.**>\n" b"To: =?utf-8?q?F=C3=B6=C3=B6bar?= <*****@*****.**>\n" b"MIME-Version: 1.0\n" b"Content-Type: multipart/alternative;\n" b' boundary="===============%(token)d=="\n' b"\n" b"--===============%(token)d==\n" b'Content-Type: text/plain; charset="utf-8"\n' b"Content-Transfer-Encoding: 7bit\n" b"\n" b"This is a plain text body\n" b"\n" b"--===============%(token)d==\n" b'Content-Type: text/html; charset="utf-8"\n' b"Content-Transfer-Encoding: 8bit\n" b"MIME-Version: 1.0\n" b"\n" b"<p>This is a html body! \xf0\x9f\x92\xa9</p>\n" b"\n" b"--===============%(token)d==--\n") % { b"token": token } }, ) ] em = (db_session.query(SESEmailMessage).filter_by( message_id=resp["MessageId"]).one()) assert em.from_ == "*****@*****.**" assert em.to == "*****@*****.**" assert em.subject == "This is a Subject"
def test_added_as_collaborator_email(self, pyramid_request, pyramid_config, monkeypatch): stub_user = pretend.stub( username="******", name="", email="*****@*****.**", primary_email=pretend.stub(email="*****@*****.**", verified=True), ) stub_submitter_user = pretend.stub(username="******", email="submiteremail") subject_renderer = pyramid_config.testing_add_renderer( "email/added-as-collaborator/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer( "email/added-as-collaborator/body.txt") body_renderer.string_response = "Email Body" html_renderer = pyramid_config.testing_add_renderer( "email/added-as-collaborator/body.html") html_renderer.string_response = "Email HTML Body" send_email = pretend.stub( delay=pretend.call_recorder(lambda *args, **kwargs: None)) pyramid_request.task = pretend.call_recorder( lambda *args, **kwargs: send_email) monkeypatch.setattr(email, "send_email", send_email) result = email.send_added_as_collaborator_email( pyramid_request, stub_user, submitter=stub_submitter_user, project_name="test_project", role="Owner", ) assert result == { "project": "test_project", "role": "Owner", "submitter": stub_submitter_user.username, } subject_renderer.assert_() body_renderer.assert_(submitter=stub_submitter_user.username) body_renderer.assert_(project="test_project") body_renderer.assert_(role="Owner") html_renderer.assert_(submitter=stub_submitter_user.username) html_renderer.assert_(project="test_project") html_renderer.assert_(role="Owner") assert pyramid_request.task.calls == [pretend.call(send_email)] assert send_email.delay.calls == [ pretend.call( "username <*****@*****.**>", attr.asdict( EmailMessage( subject="Email Subject", body_text="Email Body", body_html=( "<html>\n<head></head>\n" "<body><p>Email HTML Body</p></body>\n</html>\n"), )), ) ]
def test_send_password_reset_email( self, verified, email_addr, pyramid_request, pyramid_config, token_service, monkeypatch, ): stub_user = pretend.stub( id="id", email="*****@*****.**", primary_email=pretend.stub(email="*****@*****.**", verified=verified), username="******", name="name_value", last_login="******", password_date="password_date", ) if email_addr is None: stub_email = None else: stub_email = pretend.stub(email=email_addr, verified=verified) pyramid_request.method = "POST" token_service.dumps = pretend.call_recorder(lambda a: "TOKEN") pyramid_request.find_service = pretend.call_recorder( lambda *a, **kw: token_service) subject_renderer = pyramid_config.testing_add_renderer( "email/password-reset/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer( "email/password-reset/body.txt") body_renderer.string_response = "Email Body" html_renderer = pyramid_config.testing_add_renderer( "email/password-reset/body.html") html_renderer.string_response = "Email HTML Body" send_email = pretend.stub( delay=pretend.call_recorder(lambda *args, **kwargs: None)) pyramid_request.task = pretend.call_recorder( lambda *args, **kwargs: send_email) monkeypatch.setattr(email, "send_email", send_email) result = email.send_password_reset_email(pyramid_request, (stub_user, stub_email)) assert result == { "token": "TOKEN", "username": stub_user.username, "n_hours": token_service.max_age // 60 // 60, } subject_renderer.assert_() body_renderer.assert_(token="TOKEN", username=stub_user.username) html_renderer.assert_(token="TOKEN", username=stub_user.username) assert token_service.dumps.calls == [ pretend.call({ "action": "password-reset", "user.id": str(stub_user.id), "user.last_login": str(stub_user.last_login), "user.password_date": str(stub_user.password_date), }) ] assert pyramid_request.find_service.calls == [ pretend.call(ITokenService, name="password") ] assert pyramid_request.task.calls == [pretend.call(send_email)] assert send_email.delay.calls == [ pretend.call( "name_value <" + (stub_user.email if email_addr is None else email_addr) + ">", attr.asdict( EmailMessage( subject="Email Subject", body_text="Email Body", body_html=( "<html>\n<head></head>\n" "<body><p>Email HTML Body</p></body>\n</html>\n"), )), ) ]
def test_email_verification_email(self, pyramid_request, pyramid_config, token_service, monkeypatch): stub_email = pretend.stub(id="id", email="*****@*****.**", verified=False) pyramid_request.method = "POST" token_service.dumps = pretend.call_recorder(lambda a: "TOKEN") pyramid_request.find_service = pretend.call_recorder( lambda *a, **kw: token_service) subject_renderer = pyramid_config.testing_add_renderer( "email/verify-email/subject.txt") subject_renderer.string_response = "Email Subject" body_renderer = pyramid_config.testing_add_renderer( "email/verify-email/body.txt") body_renderer.string_response = "Email Body" html_renderer = pyramid_config.testing_add_renderer( "email/verify-email/body.html") html_renderer.string_response = "Email HTML Body" send_email = pretend.stub( delay=pretend.call_recorder(lambda *args, **kwargs: None)) pyramid_request.task = pretend.call_recorder( lambda *args, **kwargs: send_email) monkeypatch.setattr(email, "send_email", send_email) result = email.send_email_verification_email( pyramid_request, ( pretend.stub(username=None, name=None, email="*****@*****.**"), stub_email, ), ) assert result == { "token": "TOKEN", "email_address": stub_email.email, "n_hours": token_service.max_age // 60 // 60, } subject_renderer.assert_() body_renderer.assert_(token="TOKEN", email_address=stub_email.email) html_renderer.assert_(token="TOKEN", email_address=stub_email.email) assert token_service.dumps.calls == [ pretend.call({ "action": "email-verify", "email.id": str(stub_email.id) }) ] assert pyramid_request.find_service.calls == [ pretend.call(ITokenService, name="email") ] assert pyramid_request.task.calls == [pretend.call(send_email)] assert send_email.delay.calls == [ pretend.call( stub_email.email, attr.asdict( EmailMessage( subject="Email Subject", body_text="Email Body", body_html=( "<html>\n<head></head>\n" "<body><p>Email HTML Body</p></body>\n</html>\n"), )), ) ]