From d3ffef3407691ae57e07161be7519590b9fb3d76 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Fri, 17 Oct 2025 12:56:15 +0200 Subject: [PATCH] Pull latest fixed from devel --- ivatar/ivataraccount/test_views.py | 324 ++++++++++++++++++++++++++++- 1 file changed, 319 insertions(+), 5 deletions(-) diff --git a/ivatar/ivataraccount/test_views.py b/ivatar/ivataraccount/test_views.py index 7c6e8e7..a3fc2b9 100644 --- a/ivatar/ivataraccount/test_views.py +++ b/ivatar/ivataraccount/test_views.py @@ -575,14 +575,12 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods # rb => Read binary with open(TEST_IMAGE_FILE, "rb") as photo_file: photo_data = photo_file.read() - + from django.core.files.uploadedfile import SimpleUploadedFile + uploaded_file = SimpleUploadedFile( - "deadbeef.png", - photo_data, - content_type="image/png" + "deadbeef.png", photo_data, content_type="image/png" ) - response = self.client.post( url, { @@ -2028,3 +2026,319 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods "This mail address has been taken already and cannot be confirmed", "This should return an error message!", ) + + +class OpenIDErrorHandlingTestCase(TestCase): + """ + Test cases for OpenID error handling and error.html template coverage + """ + + def setUp(self): + """Set up test user and client""" + self.username = random_string() + self.password = random_string() + self.user = User.objects.create_user( + username=self.username, + password=self.password, + ) + self.client = Client() + + def login(self): + """Login as test user""" + self.client.login(username=self.username, password=self.password) + + def test_openid_discovery_failure_renders_error_template(self): + """ + Test that OpenID discovery failure renders error.html template + """ + from unittest.mock import patch, MagicMock + from openid.consumer import consumer + from ivatar.ivataraccount.models import UnconfirmedOpenId + + self.login() + + # Create an unconfirmed OpenID + unconfirmed = UnconfirmedOpenId.objects.create( + user=self.user, + openid="http://invalid-openid-provider.example.com/", + ) + + # Mock the OpenID consumer to raise DiscoveryFailure + with patch( + "ivatar.ivataraccount.views.consumer.Consumer" + ) as mock_consumer_class: + mock_consumer = MagicMock() + mock_consumer_class.return_value = mock_consumer + # Create a proper DiscoveryFailure with required http_response parameter + mock_response = MagicMock() + mock_response.status_code = 404 + discovery_failure = consumer.DiscoveryFailure( + "Invalid provider", mock_response + ) + mock_consumer.begin.side_effect = discovery_failure + + # Make request to openid_redirection view + response = self.client.get( + reverse("openid_redirection", args=[unconfirmed.id]), follow=True + ) + + # Verify we get redirected to profile with error message + self.assertEqual(response.status_code, 200) + self.assertRedirects(response, reverse("profile")) + + # Check that error message is in the response + messages = list(response.context[0]["messages"]) + self.assertTrue( + any("OpenID discovery failed" in str(msg) for msg in messages) + ) + + def test_openid_confirmation_failure_renders_error_template(self): + """ + Test that OpenID confirmation failure renders error.html template + """ + from unittest.mock import patch, MagicMock + from openid.consumer import consumer + from ivatar.ivataraccount.models import UnconfirmedOpenId + + self.login() + + # Create an unconfirmed OpenID + unconfirmed = UnconfirmedOpenId.objects.create( + user=self.user, + openid="http://test-provider.example.com/", + ) + + # Mock the OpenID consumer to return FAILURE status + with patch( + "ivatar.ivataraccount.views.consumer.Consumer" + ) as mock_consumer_class: + mock_consumer = MagicMock() + mock_consumer_class.return_value = mock_consumer + + # Create a mock response with FAILURE status + mock_response = MagicMock() + mock_response.status = consumer.FAILURE + mock_response.message = "Authentication failed" + mock_consumer.complete.return_value = mock_response + + # Make request to confirm_openid view + response = self.client.get( + reverse("confirm_openid", args=[unconfirmed.id]), follow=True + ) + + # Verify we get redirected to profile with error message + self.assertEqual(response.status_code, 200) + self.assertRedirects(response, reverse("profile")) + + # Check that error message is in the response + messages = list(response.context[0]["messages"]) + self.assertTrue(any("Confirmation failed" in str(msg) for msg in messages)) + + def test_openid_cancellation_renders_error_template(self): + """ + Test that OpenID cancellation renders error.html template + """ + from unittest.mock import patch, MagicMock + from openid.consumer import consumer + from ivatar.ivataraccount.models import UnconfirmedOpenId + + self.login() + + # Create an unconfirmed OpenID + unconfirmed = UnconfirmedOpenId.objects.create( + user=self.user, + openid="http://test-provider.example.com/", + ) + + # Mock the OpenID consumer to return CANCEL status + with patch( + "ivatar.ivataraccount.views.consumer.Consumer" + ) as mock_consumer_class: + mock_consumer = MagicMock() + mock_consumer_class.return_value = mock_consumer + + # Create a mock response with CANCEL status + mock_response = MagicMock() + mock_response.status = consumer.CANCEL + mock_consumer.complete.return_value = mock_response + + # Make request to confirm_openid view + response = self.client.get( + reverse("confirm_openid", args=[unconfirmed.id]), follow=True + ) + + # Verify we get redirected to profile with error message + self.assertEqual(response.status_code, 200) + self.assertRedirects(response, reverse("profile")) + + # Check that error message is in the response + messages = list(response.context[0]["messages"]) + self.assertTrue(any("Cancelled by user" in str(msg) for msg in messages)) + + def test_openid_unknown_error_renders_error_template(self): + """ + Test that unknown OpenID verification error renders error.html template + """ + from unittest.mock import patch, MagicMock + from ivatar.ivataraccount.models import UnconfirmedOpenId + + self.login() + + # Create an unconfirmed OpenID + unconfirmed = UnconfirmedOpenId.objects.create( + user=self.user, + openid="http://test-provider.example.com/", + ) + + # Mock the OpenID consumer to return unknown status + with patch( + "ivatar.ivataraccount.views.consumer.Consumer" + ) as mock_consumer_class: + mock_consumer = MagicMock() + mock_consumer_class.return_value = mock_consumer + + # Create a mock response with unknown status + mock_response = MagicMock() + mock_response.status = "UNKNOWN_STATUS" + mock_consumer.complete.return_value = mock_response + + # Make request to confirm_openid view + response = self.client.get( + reverse("confirm_openid", args=[unconfirmed.id]), follow=True + ) + + # Verify we get redirected to profile with error message + self.assertEqual(response.status_code, 200) + self.assertRedirects(response, reverse("profile")) + + # Check that error message is in the response + messages = list(response.context[0]["messages"]) + self.assertTrue( + any("Unknown verification error" in str(msg) for msg in messages) + ) + + def test_openid_nonexistent_id_error(self): + """ + Test that accessing non-existent OpenID ID shows error message + """ + self.login() + + # Try to access a non-existent OpenID ID + response = self.client.get( + reverse("openid_redirection", args=[99999]), follow=True + ) + + # Verify we get redirected to profile with error message + self.assertEqual(response.status_code, 200) + self.assertRedirects(response, reverse("profile")) + + # Check that error message is in the response + messages = list(response.context[0]["messages"]) + self.assertTrue(any("ID does not exist" in str(msg) for msg in messages)) + + def test_django_openid_auth_failure_template_coverage(self): + """ + Test that django-openid-auth failure template uses error.html + This test verifies the OpenID login page renders correctly + """ + # Try to access the OpenID login page + response = self.client.get(reverse("openid-login")) + self.assertEqual(response.status_code, 200) + + # The login page should render successfully + self.assertContains(response, "OpenID Login") + + def test_error_template_direct_rendering(self): + """ + Test error.html template directly to ensure it renders correctly + """ + from django.test import RequestFactory + from django.template import Context, Template + from django.contrib.auth.models import AnonymousUser + + # Test with authenticated user + factory = RequestFactory() + request = factory.get("/") + request.user = self.user + + # Test template with error message + template_content = """ + {% extends 'error.html' %} + {% load i18n %} + {% block errormessage %} + {% trans 'Test error message:' %} {{ errormessage }} + {% endblock errormessage %} + """ + + template = Template(template_content) + context = Context( + { + "request": request, + "errormessage": "This is a test error", + "user": self.user, + } + ) + + rendered = template.render(context) + + # Verify the template renders without errors + self.assertIn("Error!", rendered) + self.assertIn("This is a test error", rendered) + # Check for the profile link in the navbar (not in the backlink block) + self.assertIn("/accounts/profile/", rendered) + + # Test with anonymous user + request.user = AnonymousUser() + context = Context( + { + "request": request, + "errormessage": "This is a test error", + "user": AnonymousUser(), + } + ) + + rendered = template.render(context) + + # Verify the template renders without errors for anonymous users + self.assertIn("Error!", rendered) + self.assertIn("This is a test error", rendered) + # Should not contain profile link for anonymous users + self.assertNotIn("/accounts/profile/", rendered) + + def test_openid_failure_template_inheritance(self): + """ + Test that openid/failure.html properly extends error.html + """ + from django.test import RequestFactory + from django.template import Context, Template + + factory = RequestFactory() + request = factory.get("/") + request.user = self.user + + # Test the openid/failure.html template + template_content = """ + {% extends 'error.html' %} + {% load i18n %} + {% block errormessage %} + {% trans 'OpenID error:' %} {{ message }} + {% endblock errormessage %} + """ + + template = Template(template_content) + context = Context( + { + "request": request, + "message": "Authentication failed", + "user": self.user, + } + ) + + rendered = template.render(context) + + # Verify the template renders correctly + self.assertIn("Error!", rendered) + self.assertIn("OpenID error:", rendered) + self.assertIn("Authentication failed", rendered) + # Check for the profile link in the navbar (not in the backlink block) + self.assertIn("/accounts/profile/", rendered)