additional tests for email_invitations

This commit is contained in:
David Kennedy 2025-02-05 17:13:02 -05:00
parent 350508f9c1
commit cbc9cdbe34
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
7 changed files with 111 additions and 31 deletions

View file

@ -82,6 +82,7 @@ class MemberPermissionDisplay(StrEnum):
VIEWER = "Viewer" VIEWER = "Viewer"
NONE = "None" NONE = "None"
def get_role_display(roles): def get_role_display(roles):
""" """
Returns a user-friendly display name for a given list of user roles. Returns a user-friendly display name for a given list of user roles.
@ -103,6 +104,7 @@ def get_role_display(roles):
else: else:
return "-" return "-"
def get_domains_display(roles, permissions): def get_domains_display(roles, permissions):
""" """
Determines the display name for a user's domain viewing permissions. Determines the display name for a user's domain viewing permissions.
@ -124,6 +126,7 @@ def get_domains_display(roles, permissions):
else: else:
return "Viewer, limited" return "Viewer, limited"
def get_domain_requests_display(roles, permissions): def get_domain_requests_display(roles, permissions):
""" """
Determines the display name for a user's domain request permissions. Determines the display name for a user's domain request permissions.
@ -148,6 +151,7 @@ def get_domain_requests_display(roles, permissions):
else: else:
return "No access" return "No access"
def get_members_display(roles, permissions): def get_members_display(roles, permissions):
""" """
Determines the display name for a user's member management permissions. Determines the display name for a user's member management permissions.
@ -172,6 +176,7 @@ def get_members_display(roles, permissions):
else: else:
return "No access" return "No access"
def validate_user_portfolio_permission(user_portfolio_permission): def validate_user_portfolio_permission(user_portfolio_permission):
""" """
Validates a UserPortfolioPermission instance. Located in portfolio_helper to avoid circular imports Validates a UserPortfolioPermission instance. Located in portfolio_helper to avoid circular imports

View file

@ -16,6 +16,7 @@ from registrar.utility.email_invitations import (
send_portfolio_admin_addition_emails, send_portfolio_admin_addition_emails,
send_portfolio_admin_removal_emails, send_portfolio_admin_removal_emails,
send_portfolio_invitation_email, send_portfolio_invitation_email,
send_portfolio_member_permission_update_email,
) )
from api.tests.common import less_console_noise_decorator from api.tests.common import less_console_noise_decorator
@ -522,7 +523,6 @@ class PortfolioInvitationEmailTests(unittest.TestCase):
"registrar.utility.email_invitations._get_requestor_email", "registrar.utility.email_invitations._get_requestor_email",
side_effect=MissingEmailError("Requestor has no email"), side_effect=MissingEmailError("Requestor has no email"),
) )
@less_console_noise_decorator
def test_send_portfolio_invitation_email_missing_requestor_email(self, mock_get_email): def test_send_portfolio_invitation_email_missing_requestor_email(self, mock_get_email):
"""Test when requestor has no email""" """Test when requestor has no email"""
is_admin_invitation = False is_admin_invitation = False
@ -888,3 +888,77 @@ class SendPortfolioAdminRemovalEmailsTests(unittest.TestCase):
mock_get_requestor_email.assert_called_once_with(self.requestor, portfolio=self.portfolio) mock_get_requestor_email.assert_called_once_with(self.requestor, portfolio=self.portfolio)
mock_send_removal_emails.assert_called_once_with(self.email, self.requestor.email, self.portfolio) mock_send_removal_emails.assert_called_once_with(self.email, self.requestor.email, self.portfolio)
self.assertFalse(result) self.assertFalse(result)
class TestSendPortfolioMemberPermissionUpdateEmail(unittest.TestCase):
"""Unit tests for send_portfolio_member_permission_update_email function."""
@patch("registrar.utility.email_invitations.send_templated_email")
@patch("registrar.utility.email_invitations._get_requestor_email")
def test_send_email_success(self, mock_get_requestor_email, mock_send_email):
"""Test that the email is sent successfully when there are no errors."""
# Mock data
requestor = MagicMock()
permissions = MagicMock(spec=UserPortfolioPermission)
permissions.user.email = "user@example.com"
permissions.portfolio.organization_name = "Test Portfolio"
mock_get_requestor_email.return_value = "requestor@example.com"
# Call function
result = send_portfolio_member_permission_update_email(requestor, permissions)
# Assertions
mock_get_requestor_email.assert_called_once_with(requestor, portfolio=permissions.portfolio)
mock_send_email.assert_called_once_with(
"emails/portfolio_update.txt",
"emails/portfolio_update_subject.txt",
to_address="user@example.com",
context={
"requested_user": permissions.user,
"portfolio": permissions.portfolio,
"requestor_email": "requestor@example.com",
"permissions": permissions,
},
)
self.assertTrue(result)
@patch("registrar.utility.email_invitations.send_templated_email", side_effect=EmailSendingError("Email failed"))
@patch("registrar.utility.email_invitations._get_requestor_email")
@patch("registrar.utility.email_invitations.logger")
def test_send_email_failure(self, mock_logger, mock_get_requestor_email, mock_send_email):
"""Test that the function returns False and logs an error when email sending fails."""
# Mock data
requestor = MagicMock()
permissions = MagicMock(spec=UserPortfolioPermission)
permissions.user.email = "user@example.com"
permissions.portfolio.organization_name = "Test Portfolio"
mock_get_requestor_email.return_value = "requestor@example.com"
# Call function
result = send_portfolio_member_permission_update_email(requestor, permissions)
# Assertions
mock_logger.warning.assert_called_once_with(
"Could not send email organization member update notification to %s for portfolio: %s",
permissions.user.email,
permissions.portfolio.organization_name,
exc_info=True,
)
self.assertFalse(result)
@patch("registrar.utility.email_invitations._get_requestor_email", side_effect=Exception("Unexpected error"))
@patch("registrar.utility.email_invitations.logger")
def test_requestor_email_retrieval_failure(self, mock_logger, mock_get_requestor_email):
"""Test that an exception in retrieving requestor email is logged."""
# Mock data
requestor = MagicMock()
permissions = MagicMock(spec=UserPortfolioPermission)
# Call function
with self.assertRaises(Exception):
send_portfolio_member_permission_update_email(requestor, permissions)
# Assertions
mock_logger.warning.assert_not_called() # Function should fail before logging email failure

View file

@ -225,6 +225,7 @@ def send_portfolio_invitation_email(email: str, requestor, portfolio, is_admin_i
) )
return all_admin_emails_sent return all_admin_emails_sent
def send_portfolio_member_permission_update_email(requestor, permissions: UserPortfolioPermission): def send_portfolio_member_permission_update_email(requestor, permissions: UserPortfolioPermission):
""" """
Sends an email notification to a portfolio member when their permissions are updated. Sends an email notification to a portfolio member when their permissions are updated.
@ -254,18 +255,19 @@ def send_portfolio_member_permission_update_email(requestor, permissions: UserPo
"portfolio": permissions.portfolio, "portfolio": permissions.portfolio,
"requestor_email": requestor_email, "requestor_email": requestor_email,
"permissions": permissions, "permissions": permissions,
} },
) )
except EmailSendingError as err: except EmailSendingError:
logger.warning( logger.warning(
"Could not send email organization member update notification to %s " "for portfolio: %s", "Could not send email organization member update notification to %s " "for portfolio: %s",
permissions.user.email, permissions.user.email,
permissions.portfolio.organization_name, permissions.portfolio.organization_name,
exc_info=True, exc_info=True,
) )
return False return False
return True return True
def send_portfolio_admin_addition_emails(email: str, requestor, portfolio: Portfolio): def send_portfolio_admin_addition_emails(email: str, requestor, portfolio: Portfolio):
""" """
Notifies all portfolio admins of the provided portfolio of a newly invited portfolio admin Notifies all portfolio admins of the provided portfolio of a newly invited portfolio admin

View file

@ -215,8 +215,7 @@ class PortfolioMemberEditView(PortfolioMemberEditPermissionView, View):
try: try:
if form.is_change(): if form.is_change():
if not send_portfolio_member_permission_update_email( if not send_portfolio_member_permission_update_email(
requestor=request.user, requestor=request.user, permissions=form.instance
permissions=form.instance
): ):
messages.warning(self.request, f"Could not send email notification to {user.email}.") messages.warning(self.request, f"Could not send email notification to {user.email}.")
if form.is_change_from_member_to_admin(): if form.is_change_from_member_to_admin():