Skip to content

Commit

Permalink
tests on admin and forms
Browse files Browse the repository at this point in the history
  • Loading branch information
rachidatecs committed Dec 21, 2024
1 parent d27352b commit 8326bbb
Show file tree
Hide file tree
Showing 3 changed files with 451 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/registrar/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1492,7 +1492,7 @@ def save_model(self, request, obj, form, change):
Emails sent to requested user / email.
When exceptions are raised, return without saving model.
"""
if not change: # Only send email if this is a new PortfolioInvitation(creation)
if not change: # Only send email if this is a new PortfolioInvitation (creation)
portfolio = obj.portfolio
requested_email = obj.email
requestor = request.user
Expand Down Expand Up @@ -1537,7 +1537,7 @@ def _handle_exceptions(self, exception, request, obj):
)

else:
logger.warning("Could not send email invitation (Other Exception)", obj.portfolio, exc_info=True)
logger.warning("Could not send email invitation (Other Exception)", exc_info=True)
messages.error(request, "Could not send email invitation. Portfolio invitation not saved.")

def response_add(self, request, obj, post_url_continue=None):
Expand Down
254 changes: 254 additions & 0 deletions src/registrar/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from django.utils import timezone
from django.test import TestCase, RequestFactory, Client
from django.contrib.admin.sites import AdminSite
from registrar.utility.email import EmailSendingError
from registrar.utility.errors import MissingEmailError
from waffle.testutils import override_flag
from django_webtest import WebTest # type: ignore
from api.tests.common import less_console_noise_decorator
Expand Down Expand Up @@ -277,6 +279,29 @@ def test_get_forbidden_permissions_with_multiple_roles(self):
# Should return the forbidden permissions for member role
self.assertEqual(member_only_permissions, set(member_forbidden))

@less_console_noise_decorator
def test_has_change_form_description(self):
"""Tests if this model has a model description on the change form view"""
self.client.force_login(self.superuser)

user_portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
user=self.superuser, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)

response = self.client.get(
"/admin/registrar/userportfoliopermission/{}/change/".format(user_portfolio_permission.pk),
follow=True,
)

# Make sure that the page is loaded correctly
self.assertEqual(response.status_code, 200)

# Test for a description snippet
self.assertContains(
response,
"If you add someone to a portfolio here, it will not trigger an invitation email.",
)


class TestPortfolioInvitationAdmin(TestCase):
"""Tests for the PortfolioInvitationAdmin class as super user
Expand Down Expand Up @@ -432,6 +457,30 @@ def test_has_model_description(self):
)
self.assertContains(response, "Show more")

@less_console_noise_decorator
def test_has_change_form_description(self):
"""Tests if this model has a model description on the change form view"""
self.client.force_login(self.superuser)

invitation, _ = PortfolioInvitation.objects.get_or_create(
email=self.superuser.email, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)

response = self.client.get(
"/admin/registrar/portfolioinvitation/{}/change/".format(invitation.pk),
follow=True,
)

# Make sure that the page is loaded correctly
self.assertEqual(response.status_code, 200)

# Test for a description snippet
self.assertContains(
response,
"If you add someone to a portfolio here, it will trigger an invitation email when you click",
)

@less_console_noise_decorator
def test_get_filters(self):
"""Ensures that our filters are displaying correctly"""
with less_console_noise():
Expand All @@ -456,6 +505,211 @@ def test_get_filters(self):
self.assertContains(response, invited_html, count=1)
self.assertContains(response, retrieved_html, count=1)

@less_console_noise_decorator
@patch("registrar.admin.send_portfolio_invitation_email")
@patch("django.contrib.messages.success") # Mock the `messages.warning` call
def test_save_sends_email(self, mock_messages_warning, mock_send_email):
"""On save_model, an email is NOT sent if an invitation already exists."""
self.client.force_login(self.superuser)

# Create an instance of the admin class
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)

# Create a PortfolioInvitation instance
portfolio_invitation = PortfolioInvitation(
email="[email protected]",
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
)

# Create a request object
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
request.user = self.superuser

# Call the save_model method
admin_instance.save_model(request, portfolio_invitation, None, None)

# Assert that send_portfolio_invitation_email is not called
mock_send_email.assert_called()

# Get the arguments passed to send_portfolio_invitation_email
_, called_kwargs = mock_send_email.call_args

# Assert the email content
self.assertEqual(called_kwargs["email"], "[email protected]")
self.assertEqual(called_kwargs["requestor"], self.superuser)
self.assertEqual(called_kwargs["portfolio"], self.portfolio)

# Assert that a warning message was triggered
mock_messages_warning.assert_called_once_with(request, "[email protected] has been invited.")

@less_console_noise_decorator
@patch("registrar.admin.send_portfolio_invitation_email")
@patch("django.contrib.messages.warning") # Mock the `messages.warning` call
def test_save_does_not_send_email_if_requested_user_exists(self, mock_messages_warning, mock_send_email):
"""On save_model, an email is NOT sent if an the requested email belongs to an existing user.
It also throws a warning."""
self.client.force_login(self.superuser)

# Create an instance of the admin class
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)

# Mock the UserPortfolioPermission query to simulate the invitation already existing
existing_user = create_user()
UserPortfolioPermission.objects.create(user=existing_user, portfolio=self.portfolio)

# Create a PortfolioInvitation instance
portfolio_invitation = PortfolioInvitation(
email=existing_user.email,
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
)

# Create a request object
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
request.user = self.superuser

# Call the save_model method
admin_instance.save_model(request, portfolio_invitation, None, None)

# Assert that send_portfolio_invitation_email is not called
mock_send_email.assert_not_called()

# Assert that a warning message was triggered
mock_messages_warning.assert_called_once_with(request, "User is already a member of this portfolio.")

@less_console_noise_decorator
@patch("registrar.admin.send_portfolio_invitation_email")
@patch("django.contrib.messages.error") # Mock the `messages.error` call
def test_save_exception_email_sending_error(self, mock_messages_error, mock_send_email):
"""Handle EmailSendingError correctly when sending the portfolio invitation fails."""
self.client.force_login(self.superuser)

# Mock the email sending function to raise EmailSendingError
mock_send_email.side_effect = EmailSendingError("Email service unavailable")

# Create an instance of the admin class
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)

# Create a PortfolioInvitation instance
portfolio_invitation = PortfolioInvitation(
email="[email protected]",
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
)

# Create a request object
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
request.user = self.superuser

# Call the save_model method
admin_instance.save_model(request, portfolio_invitation, None, None)

# Assert that messages.error was called with the correct message
mock_messages_error.assert_called_once_with(
request, "Could not send email invitation. Portfolio invitation not saved."
)

@less_console_noise_decorator
@patch("registrar.admin.send_portfolio_invitation_email")
@patch("django.contrib.messages.error") # Mock the `messages.error` call
def test_save_exception_missing_email_error(self, mock_messages_error, mock_send_email):
"""Handle MissingEmailError correctly when no email exists for the requestor."""
self.client.force_login(self.superuser)

# Mock the email sending function to raise MissingEmailError
mock_send_email.side_effect = MissingEmailError("Email-less Rachid")

# Create an instance of the admin class
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)

# Create a PortfolioInvitation instance
portfolio_invitation = PortfolioInvitation(
email="[email protected]",
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
)

# Create a request object
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
request.user = self.superuser

# Call the save_model method
admin_instance.save_model(request, portfolio_invitation, None, None)

# Assert that messages.error was called with the correct message
mock_messages_error.assert_called_once_with(
request,
"Can't send invitation email. No email is associated with the account for 'Email-less Rachid'.",
)

@less_console_noise_decorator
@patch("registrar.admin.send_portfolio_invitation_email")
@patch("django.contrib.messages.error") # Mock the `messages.error` call
def test_save_exception_generic_error(self, mock_messages_error, mock_send_email):
"""Handle generic exceptions correctly during portfolio invitation."""
self.client.force_login(self.superuser)

# Mock the email sending function to raise a generic exception
mock_send_email.side_effect = Exception("Unexpected error")

# Create an instance of the admin class
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)

# Create a PortfolioInvitation instance
portfolio_invitation = PortfolioInvitation(
email="[email protected]",
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
)

# Create a request object
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
request.user = self.superuser

# Call the save_model method
admin_instance.save_model(request, portfolio_invitation, None, None)

# Assert that messages.error was called with the correct message
mock_messages_error.assert_called_once_with(
request, "Could not send email invitation. Portfolio invitation not saved."
)

# @patch("django.contrib.messages.get_messages")
# def test_form_rerenders_if_errors_or_warnings(self, mock_get_messages):
# """Ensure the form is re-rendered when errors or warnings are present."""
# self.client.force_login(self.superuser)

# # Mock the presence of an error message
# mock_message = Mock()
# mock_message.level_tag = "error"
# mock_message.message = "Simulated error message"
# mock_get_messages.return_value = [mock_message]

# # Create an instance of the admin class
# admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=AdminSite())

# # Create a PortfolioInvitation instance
# portfolio_invitation = PortfolioInvitation(
# email="[email protected]",
# portfolio=self.portfolio,
# roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
# )

# # Create a request object
# request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
# request.user = self.superuser

# # Trigger response_add
# response = admin_instance.response_add(request, portfolio_invitation)

# # Assert that the response status code is 200 (indicating the form was re-rendered)
# self.assertEqual(response.status_code, 200, msg="Expected form to re-render due to errors.")

# # Assert that the mocked error message is included in the response
# self.assertContains(response, "Simulated error message", msg_prefix="Expected error message not found.")



class TestHostAdmin(TestCase):
"""Tests for the HostAdmin class as super user
Expand Down
Loading

0 comments on commit 8326bbb

Please sign in to comment.