def test_email_address_get(clean_db): """Email addresses can be loaded using EmailAddress.get""" db = clean_db ea1 = EmailAddress('*****@*****.**') ea2 = EmailAddress('*****@*****.**') ea3 = EmailAddress('*****@*****.**') db.session.add_all([ea1, ea2, ea3]) db.session.commit() get1 = EmailAddress.get('*****@*****.**') assert get1 == ea1 get2 = EmailAddress.get('*****@*****.**') assert get2 == ea2 # Can also get by hash get3 = EmailAddress.get(blake2b160=hash_map['*****@*****.**']) assert get3 == ea1 # Or by Base58 representation of hash get4 = EmailAddress.get(email_hash='2EGz72jxcsYjvXxF7r5rqfAgikor') assert get4 == ea1 # Will return nothing if given garbage input, or a non-existent email address assert EmailAddress.get('invalid') is None assert EmailAddress.get('*****@*****.**') is None
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 == '*****@*****.**'