def test_get_all_active_subscribers(self, tiny_db_storage): active = ( Registration( email=Email("*****@*****.**"), last_update=datetime.utcnow(), state=State.subscribed, ), Registration( email=Email("*****@*****.**"), last_update=datetime.now(), state=State.pending_unsubscribe, confirm_action=Action.unsubscribe, confirm_token=Token(b"token"), ), ) inactive = (Registration( email=Email("*****@*****.**"), last_update=datetime.now(), state=State.pending_subscribe, confirm_action=Action.subscribe, confirm_token=Token(b"token"), ), ) for r in active + inactive: tiny_db_storage.upsert(r) result = tiny_db_storage.get_all_active_subscribers() assert tuple(sorted(result, key=lambda r: r.email)) == active
def test_find_returns_matching_entity(self, tiny_db, tiny_db_storage): last_update = datetime(2019, 10, 25, 13, 37) tiny_db.insert({ "email": "*****@*****.**", "last_update": last_update.isoformat(), "state": "subscribed", "confirm_action": None, "confirm_token": None, }) assert tiny_db_storage.find(Email("*****@*****.**")) == Registration( email=Email("*****@*****.**"), last_update=last_update, state=State.subscribed, )
def test_confirm_link_substitutions(self, settings): template = "{{confirm_link}}" token = Token(b"token") provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ "subscribe.subject.txt": template, "subscribe.txt": template, "subscribe.html": template, }), binary_loader=MockBinaryLoader(dict()), ) msg = provider.get_confirmation_request_msg(Email("email@local"), action=Action.subscribe, confirm_token=token) expected_link = ( "https://test.local/subscribe/confirm/email%40local?token=" + quote(token.to_string())) assert msg["Subject"] == expected_link assert (msg.get_body("plain").get_content().strip() == expected_link # type: ignore ) assert (msg.get_body("html").get_content().strip() == expected_link # type: ignore )
def test_subscription_and_unsubscription_flow(client, db, confirmation_requester): given_email = Email("*****@*****.**") assert_success(client.post(f"/subscribe/{given_email}")) assert_success( client.post( f"/confirm/{given_email}", headers=[( "Authorization", "Bearer " + confirmation_requester.tokens[given_email], )], )) assert db.get(Query().email == given_email)["state"] == "subscribed" assert_success(client.post(f"/unsubscribe/{given_email}")) assert_success( client.post( f"/confirm/{given_email}", headers=[( "Authorization", "Bearer " + confirmation_requester.tokens[given_email], )], )) assert db.get(Query().email == given_email) is None
def test_if_subscription_pending_has_no_effect(self, registration_service, storage, confirmation_requester, utcnow): given_email = Email("*****@*****.**") storage.upsert( Registration( email=given_email, state=State.pending_subscribe, last_update=utcnow() - timedelta(days=1), confirm_token=Token(b"token"), confirm_action=Action.subscribe, )) registration_service.unsubscribe(given_email) stored = storage.find(given_email) assert not confirmation_requester.request_confirmation.called assert stored == Registration( email=given_email, state=State.pending_subscribe, last_update=utcnow() - timedelta(days=1), confirm_token=Token(b"token"), confirm_action=Action.subscribe, )
def test_confirm_unauthorized(client, db, confirmation_requester): given_email = Email("*****@*****.**") assert_success(client.post(f"/subscribe/{given_email}")) unknown_email_response = client.post( f"/confirm/non-{given_email}", headers=[("Authorization", "Bearer " + confirmation_requester.tokens[given_email])], ) assert unknown_email_response.status_code == http.HTTPStatus.UNAUTHORIZED missing_auth_response = client.post(f"/confirm/{given_email}") assert missing_auth_response.status_code == http.HTTPStatus.UNAUTHORIZED assert (client.post( f"/confirm/non-{given_email}", headers=[("Authorization", "Basic " + confirmation_requester.tokens[given_email])], )).status_code == http.HTTPStatus.UNAUTHORIZED known_email_response = client.post( f"/confirm/non-{given_email}", headers=[("Authorization", "Bearer invalid-token")], ) assert known_email_response.status_code == http.HTTPStatus.UNAUTHORIZED assert known_email_response.data == unknown_email_response.data
def test_if_subscription_is_pending_resends_confirm_mail( self, registration_service, storage, confirmation_requester, token_generator, utcnow, ): given_email = Email("*****@*****.**") storage.upsert( Registration( email=given_email, state=State.pending_subscribe, last_update=utcnow() - timedelta(days=1), confirm_token=next(token_generator), confirm_action=Action.subscribe, )) registration_service.subscribe(given_email) confirmation_requester.request_confirmation.assert_called_with( given_email, action=Action.subscribe, confirm_token=token_generator.generated_tokens[0], )
def test_email_notifier(): subscribers = [ Registration( email=Email("*****@*****.**"), last_update=datetime.utcnow(), state=State.subscribed, ), Registration( email=Email("*****@*****.**"), last_update=datetime.utcnow(), state=State.subscribed, ), ] message = EmailMessage() feed_item = FeedItem( title="title", link="link", pub_date=datetime.now(), description="description", image=None, ) storage = MagicMock() connection_manager = MagicMock() connection = MagicMock() message_provider = MagicMock() storage.get_all_active_subscribers.return_value = subscribers connection_manager.__enter__.return_value = connection message_provider.get_new_post_msg.return_value = message email_notifier = EmailNotifier(storage, lambda: connection_manager, message_provider) email_notifier(feed_item) message_provider.get_new_post_msg.assert_has_calls( ( call(feed_item, Email("*****@*****.**")), call(feed_item, Email("*****@*****.**")), ), any_order=True, ) connection.send_message.assert_has_calls((call(message), call(message)))
def test_if_email_unknown_sends_subscription_confirm_mail( self, registration_service, confirmation_requester, token_generator, utcnow): given_email = Email("*****@*****.**") registration_service.subscribe(given_email) confirmation_requester.request_confirmation.assert_called_with( given_email, action=Action.subscribe, confirm_token=token_generator.generated_tokens[0], )
def test_confirm_for_registration_without_confirm_action_raises_exception( self, registration_service, storage, utcnow): given_email = Email("*****@*****.**") storage.upsert( Registration( email=given_email, state=State.subscribed, last_update=utcnow() - timedelta(days=1), )) with pytest.raises(UnauthorizedException): registration_service.confirm(given_email, None)
def test_drop_old_unconfirmed(self, tiny_db_storage): reference_datetime = datetime(2019, 10, 25, 13, 18) fresh = ( Registration( email=Email("*****@*****.**"), last_update=reference_datetime - timedelta(days=1), state=State.pending_subscribe, confirm_action=Action.subscribe, confirm_token=Token(b"token"), ), Registration( email=Email("*****@*****.**"), last_update=reference_datetime - timedelta(days=3), state=State.subscribed, ), Registration( email=Email("*****@*****.**"), last_update=reference_datetime - timedelta(days=3), state=State.pending_unsubscribe, confirm_action=Action.unsubscribe, confirm_token=Token(b"token"), ), ) old = (Registration( email=Email("*****@*****.**"), last_update=reference_datetime - timedelta(days=3), state=State.pending_subscribe, confirm_action=Action.subscribe, confirm_token=Token(b"token"), ), ) for r in fresh + old: tiny_db_storage.upsert(r) tiny_db_storage.drop_old_unconfirmed(drop_before=reference_datetime - timedelta(days=2)) assert tuple(sorted(tiny_db_storage.all(), key=lambda r: r.email)) == fresh
def test_if_email_unknown_creates_registration(self, registration_service, storage, token_generator, utcnow): given_email = Email("*****@*****.**") registration_service.subscribe(given_email) stored = storage.find(given_email) assert stored == Registration( email=given_email, state=State.pending_subscribe, last_update=utcnow(), confirm_token=token_generator.generated_tokens[0], confirm_action=Action.subscribe, )
def test_urlquote(self, feed_item, settings): subject = "{{ '@' | urlquote }}" provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ f"new-post.subject.txt": subject, "new-post.txt": "", "new-post.html": "", }), binary_loader=MockBinaryLoader(dict()), ) msg = provider.get_new_post_msg(feed_item, Email("email")) assert msg["Subject"] == "%40"
def test_constructs_email_message_from_templates(self, feed_item, settings): to_email = Email("*****@*****.**") subject = "subject" plain_text = "plain text" html = "html" provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ f"new-post.subject.txt": subject, f"new-post.txt": plain_text, f"new-post.html": html, }), binary_loader=MockBinaryLoader(dict()), ) msg = provider.get_new_post_msg(feed_item, Email("email")) assert msg["Subject"] == subject assert msg.get_body( "plain").get_content().strip() == plain_text # type: ignore assert msg.get_body( "html").get_content().strip() == html # type: ignore
def test_include_binary_and_b64encode(self, feed_item, settings): binary_content = b"binary content" subject = "{{ include_binary('bin') | b64encode }}" provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ f"new-post.subject.txt": subject, "new-post.txt": "", "new-post.html": "", }), binary_loader=MockBinaryLoader({"bin": binary_content}), ) msg = provider.get_new_post_msg(feed_item, Email("email")) assert msg["Subject"] == b64encode(binary_content).decode("ascii")
def test_confirm_with_invalid_token_raises_exception( self, state, registration_service, storage, utcnow): given_email = Email("*****@*****.**") storage.upsert( Registration( email=given_email, state=state, last_update=utcnow() - timedelta(days=1), confirm_token=Token(b"actual token"), confirm_action=Action.subscribe if state == State.pending_subscribe else Action.unsubscribe, )) with pytest.raises(UnauthorizedException): registration_service.confirm(given_email, Token(b"invalid token"))
def test_subject_substitutions(self, feed_item, settings): subject = "{{display_name}} {{host}} {{to_email}} {{post.title}}" provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ "new-post.subject.txt": subject, "new-post.txt": "", "new-post.html": "", }), binary_loader=MockBinaryLoader(dict()), ) msg = provider.get_new_post_msg(feed_item, Email("email")) assert msg[ "Subject"] == f"{settings.display_name} {settings.host} email title"
def test_confirm_with_valid_token_removes_subscription( self, registration_service, storage, utcnow): given_email = Email("*****@*****.**") given_token = Token(b"token") storage.upsert( Registration( email=given_email, state=State.pending_unsubscribe, last_update=utcnow() - timedelta(days=1), confirm_token=given_token, confirm_action=Action.unsubscribe, )) registration_service.confirm(given_email, given_token) assert storage.find(given_email) is None
def test_delete_existing_entity(self, tiny_db, tiny_db_storage): tiny_db.insert({ "email": "*****@*****.**", "last_update": datetime(2019, 10, 25, 13, 37).isoformat(), "state": "subscribed", "confirm_action": None, "confirm_token": None, }) tiny_db_storage.delete(Email("*****@*****.**")) assert tiny_db.get(Query().email == "*****@*****.**") is None
def test_upsert_of_new_entity(self, tiny_db, tiny_db_storage): last_update = datetime(2019, 10, 25, 13, 37) registration = Registration( email=Email("*****@*****.**"), last_update=last_update, state=State.subscribed, ) tiny_db_storage.upsert(registration) assert tiny_db.get(Query().email == "*****@*****.**") == { "email": "*****@*****.**", "last_update": last_update.isoformat(), "state": "subscribed", "confirm_action": None, "confirm_token": None, }
def test_include_binary_and_b64encode(self, settings): binary_content = b"binary content" subject = "{{ include_binary('bin') | b64encode }}" provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ f"subscribe.subject.txt": subject, "subscribe.txt": "", "subscribe.html": "", }), binary_loader=MockBinaryLoader({"bin": binary_content}), ) msg = provider.get_confirmation_request_msg( Email("email"), action=Action.subscribe, confirm_token=Token(b"token")) assert msg["Subject"] == b64encode(binary_content).decode("ascii")
def test_subject_substitutions(self, settings): subject = "{{display_name}} {{host}} {{to_email}}" provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ "subscribe.subject.txt": subject, "subscribe.txt": "", "subscribe.html": "", }), binary_loader=MockBinaryLoader(dict()), ) msg = provider.get_confirmation_request_msg( Email("email"), action=Action.subscribe, confirm_token=Token(b"token")) assert msg[ "Subject"] == f"{settings.display_name} {settings.host} email"
def test_if_subscribed_sets_registration_to_pending_unsubscribe( self, state, registration_service, storage, token_generator, utcnow): given_email = Email("*****@*****.**") storage.upsert( Registration(email=given_email, state=state, last_update=utcnow() - timedelta(days=1))) registration_service.unsubscribe(given_email) stored = storage.find(given_email) assert stored == Registration( email=given_email, state=State.pending_unsubscribe, last_update=utcnow(), confirm_token=token_generator.generated_tokens[0], confirm_action=Action.unsubscribe, )
def test_body_substitutions(self, feed_item, settings): subject = "subject" template = "{{subject}} {{display_name}} {{host}} {{to_email}} {{post.title}}" provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ "new-post.subject.txt": subject, "new-post.txt": template, "new-post.html": template, }), binary_loader=MockBinaryLoader(dict()), ) msg = provider.get_new_post_msg(feed_item, Email("email")) assert ( msg.get_body("plain").get_content().strip() # type: ignore == f"{subject} {settings.display_name} {settings.host} email title" )
def test_body_substitutions(self, settings): subject = "subject" template = "{{subject}} {{display_name}} {{host}} {{to_email}}" provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ "subscribe.subject.txt": subject, "subscribe.txt": template, "subscribe.html": template, }), binary_loader=MockBinaryLoader(dict()), ) msg = provider.get_confirmation_request_msg( Email("email"), action=Action.subscribe, confirm_token=Token(b"token")) assert (msg.get_body("plain").get_content().strip() # type: ignore == f"{subject} {settings.display_name} {settings.host} email")
def test_if_subscribed_no_effect( self, state, registration_service, storage, confirmation_requester, token_generator, utcnow, ): given_email = Email("*****@*****.**") storage.upsert( Registration(email=given_email, state=state, last_update=utcnow() - timedelta(days=1))) registration_service.subscribe(given_email) stored = storage.find(given_email) assert not confirmation_requester.request_confirmation.called assert stored == Registration(email=given_email, state=state, last_update=utcnow() - timedelta(days=1))
def test_if_subscribed_requests_confirmation( self, state, registration_service, storage, confirmation_requester, token_generator, utcnow, ): given_email = Email("*****@*****.**") storage.upsert( Registration(email=given_email, state=state, last_update=utcnow() - timedelta(days=1))) registration_service.unsubscribe(given_email) confirmation_requester.request_confirmation.assert_called_with( given_email, action=Action.unsubscribe, confirm_token=token_generator.generated_tokens[0], )
def test_if_subscription_is_pending_bumps_last_update( self, registration_service, storage, token_generator, utcnow): given_email = Email("*****@*****.**") storage.upsert( Registration( email=given_email, state=State.pending_subscribe, last_update=utcnow() - timedelta(days=1), confirm_token=next(token_generator), confirm_action=Action.subscribe, )) registration_service.subscribe(given_email) stored = storage.find(given_email) assert stored == Registration( email=given_email, state=State.pending_subscribe, last_update=utcnow(), confirm_token=token_generator.generated_tokens[0], confirm_action=Action.subscribe, )
def test_email_confirmation_requester(): email = Email("email") action = Action.subscribe confirm_token = Token(b"token") message = EmailMessage() connection_manager = MagicMock() connection = MagicMock() message_provider = MagicMock() connection_manager.__enter__.return_value = connection message_provider.get_confirmation_request_msg.return_value = message requester = EmailConfirmationRequester( connection=lambda: connection_manager, message_provider=message_provider) requester.request_confirmation(email, action=action, confirm_token=confirm_token) message_provider.get_confirmation_request_msg.assert_called_once_with( email, action=action, confirm_token=confirm_token) connection.send_message.assert_called_once_with(message)
def test_constructs_email_message_from_templates(self, action, settings): to_email = Email("*****@*****.**") subject = "subject" plain_text = "plain text" html = "html" provider = EmailFromTemplateProvider( settings=settings, template_loader=MockTemplateLoader({ f"{action.name}.subject.txt": subject, f"{action.name}.txt": plain_text, f"{action.name}.html": html, }), binary_loader=MockBinaryLoader(dict()), ) msg = provider.get_confirmation_request_msg( to_email, action=action, confirm_token=Token(b"token")) assert msg["Subject"] == subject assert msg["To"] == to_email assert msg.get_body( "plain").get_content().strip() == plain_text # type: ignore assert msg.get_body( "html").get_content().strip() == html # type: ignore