Пример #1
0
def test_email_address_blocked(clean_db):
    """A blocked email address cannot be used via EmailAddress.add"""
    ea1 = EmailAddress.add('*****@*****.**')
    ea2 = EmailAddress.add('*****@*****.**')
    ea3 = EmailAddress.add('*****@*****.**')

    EmailAddress.mark_blocked(ea2.email)

    assert ea1.is_blocked is True
    assert ea2.is_blocked is True
    assert ea3.is_blocked is False

    with pytest.raises(EmailAddressBlockedError):
        EmailAddress.add('*****@*****.**')
Пример #2
0
def test_email_address_validate_for(email_models, clean_mixin_db):
    """EmailAddress.validate_for can be used to determine availability"""
    db = clean_mixin_db
    models = email_models

    user1 = models.EmailUser()
    user2 = models.EmailUser()
    anon_user = None
    db.session.add_all([user1, user2])

    # A new email address is available to all
    assert EmailAddress.validate_for(user1, '*****@*****.**') is True
    assert EmailAddress.validate_for(user2, '*****@*****.**') is True
    assert EmailAddress.validate_for(anon_user, '*****@*****.**') is True

    # Once it's assigned to a user, availability changes
    link = models.EmailLink(emailuser=user1, email='*****@*****.**')
    db.session.add(link)

    assert EmailAddress.validate_for(user1, '*****@*****.**') is True
    assert EmailAddress.validate_for(user2, '*****@*****.**') is False
    assert EmailAddress.validate_for(anon_user, '*****@*****.**') is False

    # An address in use is not available to claim as new
    assert (EmailAddress.validate_for(user1, '*****@*****.**',
                                      new=True) == 'not_new')
    assert EmailAddress.validate_for(user2, '*****@*****.**',
                                     new=True) is False
    assert (EmailAddress.validate_for(
        anon_user, '*****@*****.**', new=True) is False)

    # When delivery state changes, validate_for's result changes too
    ea = link.email_address
    assert ea.delivery_state.UNKNOWN

    ea.mark_sent()
    assert ea.delivery_state.SENT
    assert EmailAddress.validate_for(user1, '*****@*****.**') is True
    assert EmailAddress.validate_for(user2, '*****@*****.**') is False
    assert EmailAddress.validate_for(anon_user, '*****@*****.**') is False

    ea.mark_soft_fail()
    assert ea.delivery_state.SOFT_FAIL
    assert EmailAddress.validate_for(user1,
                                     '*****@*****.**') == 'soft_fail'
    assert EmailAddress.validate_for(user2, '*****@*****.**') is False
    assert EmailAddress.validate_for(anon_user, '*****@*****.**') is False

    ea.mark_hard_fail()
    assert ea.delivery_state.HARD_FAIL
    assert EmailAddress.validate_for(user1,
                                     '*****@*****.**') == 'hard_fail'
    assert EmailAddress.validate_for(user2, '*****@*****.**') is False
    assert EmailAddress.validate_for(anon_user, '*****@*****.**') is False

    # A blocked address is available to no one
    db.session.add(EmailAddress('*****@*****.**'))
    EmailAddress.mark_blocked('*****@*****.**')
    assert EmailAddress.validate_for(user1, '*****@*****.**') is False
    assert EmailAddress.validate_for(user2, '*****@*****.**') is False
    assert EmailAddress.validate_for(anon_user, '*****@*****.**') is False

    # An invalid address is available to no one
    assert EmailAddress.validate_for(user1, 'invalid') == 'invalid'
    assert EmailAddress.validate_for(user2, 'invalid') == 'invalid'
    assert EmailAddress.validate_for(anon_user, 'invalid') == 'invalid'
Пример #3
0
def test_email_address_mixin(email_models, clean_mixin_db):
    """The EmailAddressMixin class adds safety checks for using an email address"""
    db = clean_mixin_db
    models = email_models

    blocked_email = EmailAddress('*****@*****.**')

    user1 = models.EmailUser()
    user2 = models.EmailUser()

    doc1 = models.EmailDocument()
    doc2 = models.EmailDocument()

    db.session.add_all([user1, user2, doc1, doc2, blocked_email])

    EmailAddress.mark_blocked('*****@*****.**')

    # Mixin-based classes can simply specify an email parameter to link to an
    # EmailAddress instance
    link1 = models.EmailLink(emailuser=user1, email='*****@*****.**')
    db.session.add(link1)
    ea1 = EmailAddress.get('*****@*****.**')
    assert link1.email == '*****@*****.**'
    assert link1.email_address == ea1
    assert link1.transport_hash == ea1.transport_hash
    assert bool(link1.transport_hash)

    # Link an unrelated email address to another user to demonstrate that it works
    link2 = models.EmailLink(emailuser=user2, email='*****@*****.**')
    db.session.add(link2)
    ea2 = EmailAddress.get('*****@*****.**')
    assert link2.email == '*****@*****.**'
    assert link2.email_address == ea2
    assert link2.transport_hash == ea2.transport_hash
    assert bool(link1.transport_hash)

    db.session.commit()

    # '*****@*****.**' is now exclusive to user2. Attempting it to assign it to
    # user1 will raise an exception, even if the case is changed.
    with pytest.raises(EmailAddressInUseError):
        models.EmailLink(emailuser=user1, email='*****@*****.**')

    # This safety catch works even if the email_address column is used:
    with pytest.raises(EmailAddressInUseError):
        models.EmailLink(emailuser=user1, email_address=ea2)

    db.session.rollback()

    # Blocked addresses cannot be used either
    with pytest.raises(EmailAddressBlockedError):
        models.EmailLink(emailuser=user1, email='*****@*****.**')

    with pytest.raises(EmailAddressBlockedError):
        models.EmailLink(emailuser=user1, email_address=blocked_email)

    db.session.rollback()

    # Attempting to assign '*****@*****.**' to user2 a second time will cause a
    # SQL integrity error because EmailLink.__email_unique__ is True.
    link3 = models.EmailLink(emailuser=user2, email='*****@*****.**')
    db.session.add(link3)
    with pytest.raises(IntegrityError):
        db.session.flush()

    del link3
    db.session.rollback()

    # The EmailDocument model, in contrast, has no requirement of availability to a
    # specific user, so it won't be blocked here despite being exclusive to user1
    assert doc1.email is None
    assert doc2.email is None
    assert doc1.email_address is None
    assert doc2.email_address is None

    doc1.email = '*****@*****.**'
    doc2.email = '*****@*****.**'

    assert doc1.email == '*****@*****.**'
    assert doc2.email == '*****@*****.**'
    assert doc1.email_address == ea1
    assert doc2.email_address == ea1

    # ea1 now has three references, while ea2 has 1
    assert ea1.refcount() == 3
    assert ea2.refcount() == 1

    # Setting the email property on EmailDocument will mutate
    # EmailDocument.email_address and not EmailDocument.email_address.email
    assert ea1.email == '*****@*****.**'
    doc1.email = None
    assert ea1.email == '*****@*****.**'
    assert doc1.email_address is None
    doc2.email = '*****@*****.**'
    assert ea1.email == '*****@*****.**'
    assert doc2.email_address == ea2

    # EmailLinkedDocument takes the complexity up a notch

    # A document linked to a user can use any email linked to that user
    ldoc1 = models.EmailLinkedDocument(emailuser=user1,
                                       email='*****@*****.**')
    db.session.add(ldoc1)
    assert ldoc1.emailuser == user1
    assert ldoc1.email_address == ea1

    # But another user can't use this email address
    with pytest.raises(EmailAddressInUseError):
        models.EmailLinkedDocument(emailuser=user2,
                                   email='*****@*****.**')

    # This restriction also applies when user is not specified. Here, this email is
    # claimed by user2 above
    with pytest.raises(EmailAddressInUseError):
        models.EmailLinkedDocument(emailuser=None, email='*****@*****.**')

    # But it works with an unaffiliated email address
    ldoc2 = models.EmailLinkedDocument(email='*****@*****.**')
    db.session.add(ldoc2)
    assert ldoc2.emailuser is None
    assert ldoc2.email == '*****@*****.**'

    ldoc3 = models.EmailLinkedDocument(emailuser=user2,
                                       email='*****@*****.**')
    db.session.add(ldoc3)
    assert ldoc3.emailuser is user2
    assert ldoc3.email == '*****@*****.**'

    # Setting the email to None on the document removes the link to the EmailAddress,
    # but does not blank out the EmailAddress

    assert ldoc1.email_address == ea1
    assert ea1.email == '*****@*****.**'
    ldoc1.email = None
    assert ldoc1.email_address is None
    assert ea1.email == '*****@*****.**'