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_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_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_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_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_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_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_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_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
def request_confirmation(self, email: Email, *, action: Action, confirm_token: Token): self.tokens[email] = confirm_token.to_string()
conn = smtp_connection(args.host[0], user, getpass.getpass(f"Password for {user}:")) settings = EmailFromTemplateProvider.Settings( display_name="Doveseed template test", sender=args.from_mail[0], host="doveseed.local", confirm_url_format="https://{host}/confirm/{email}?token={token}", ) message_provider = EmailFromTemplateProvider( settings=settings, template_loader=FileSystemLoader(args.template[0]), binary_loader=FileSystemBinaryLoader(args.template[0]), ) token = Token(b"token") dispatch = { "subscribe": lambda: message_provider.get_confirmation_request_msg( args.to_mail[0], action=Action.subscribe, confirm_token=token ), "unsubscribe": lambda: message_provider.get_confirmation_request_msg( args.to_mail[0], action=Action.unsubscribe, confirm_token=token ), "new-post": lambda: message_provider.get_new_post_msg( FeedItem( title="Post title", description=LoremIpsum, link="https://doveseed.local/post", pub_date=datetime.now(), image=args.image[0], ),
class TestTinyDbStorage: 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_upsert_of_existing_entity(self, tiny_db, tiny_db_storage): first_update = datetime(2019, 10, 25, 13, 37) registration = Registration( email=Email("*****@*****.**"), last_update=first_update, state=State.subscribed, ) tiny_db_storage.upsert(registration) last_update = first_update + timedelta(days=1) registration.last_update = last_update 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_find_of_non_exsting_entity_returns_none(self, tiny_db_storage): assert tiny_db_storage.find(Email("*****@*****.**")) is None 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_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_delete_non_existing_entity(self, tiny_db, tiny_db_storage): tiny_db_storage.delete(Email("*****@*****.**")) assert tiny_db.get(Query().email == "*****@*****.**") is None @pytest.mark.parametrize( "instance", ( Registration( email=Email("*****@*****.**"), last_update=datetime(2019, 10, 25, 13, 37), state=State.subscribed, confirm_action=None, confirm_token=None, ), Registration( email=Email("*****@*****.**"), last_update=datetime(2019, 10, 25, 13, 38), state=State.pending_subscribe, confirm_action=Action.subscribe, confirm_token=Token(b"token"), ), ), ) def test_roundtrip(self, instance, tiny_db_storage): tiny_db_storage.upsert(instance) assert tiny_db_storage.find(instance.email) == instance 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_get_unset_last_seen_storage(self, tiny_db_storage): assert tiny_db_storage.get_last_seen() is None def test_last_seen_storage(self, tiny_db_storage): now = datetime.now(tz=timezone(timedelta(hours=1))) tiny_db_storage.set_last_seen(now) assert tiny_db_storage.get_last_seen() == now def test_last_seen_storage_with_legacy_timezone_unaware_date( self, tiny_db, tiny_db_storage): now = datetime.now(tz=timezone.utc) tiny_db.insert({ "key": "last_seen", "value": now.replace(tzinfo=None).isoformat() }) assert tiny_db_storage.get_last_seen() == now 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 __next__(self): self.generated_tokens.append( Token(b"token" + bytes(len(self.generated_tokens)))) return self.generated_tokens[-1]
def test_confirm_for_unknown_email_raises_exception( self, registration_service): given_email = Email("*****@*****.**") with pytest.raises(UnauthorizedException): registration_service.confirm(given_email, Token(b"token"))