diff --git a/changelog.d/1738-admin-tests-mypy.changed.md b/changelog.d/1738-admin-tests-mypy.changed.md new file mode 100644 index 000000000..92437d779 --- /dev/null +++ b/changelog.d/1738-admin-tests-mypy.changed.md @@ -0,0 +1,29 @@ +- **Graduated the `tests.test_admin*` chunk out of the mypy baseline + (#1738).** Removed the two `[mypy-opencontractserver.tests.test_admin]` + and `[mypy-opencontractserver.tests.test_admin_auth]` `ignore_errors` + blocks and pruned the corresponding 122 lines from + `docs/typing/mypy_baseline.txt` (2942 → 2820). Fixes follow the established + patterns from earlier #1738 graduation passes: + - `test_admin.py` (6 errors): swapped the module-level `User = + get_user_model()` alias for the concrete + `from opencontractserver.users.models import User` import (mypy rejects a + `get_user_model()` variable as a type annotation); widened two helper + signatures from `obj: object` to `obj: Model` so `_meta` / `pk` resolve; + added `# type: ignore[arg-type]` on `CorpusAdmin(Corpus, None)` (the + Django admin's `ModelAdmin` second-arg `AdminSite` is genuinely optional + at runtime but typed as required) and `# type: ignore[method-assign]` on + the `Mock()` reassignment of `message_user` (intentional test isolation). + - `test_admin_auth.py` (116 errors): same `get_user_model()` → direct + import swap, plus class-level annotations for the seven `TestCase` + classes that store `User` instances on `setUpTestData(cls)` — + `TestAdminClaimsSync.user`, `TestAuth0SuperuserAllowlist.user`, + `TestAuth0AdminBackend.{staff_user, non_staff_user, inactive_staff, + superuser}`, `TestAdminLoginView.{staff_user, regular_user}`, + `TestAdminLogoutView.staff_user`, `TestGetUserByPayloadWithClaimSync.user`, + `TestAdminLoginRateLimit.staff_user`. 17 `# type: ignore[attr-defined]` + on `response.url` redirects (django-stubs' `_MonkeyPatchedWSGIResponse` + doesn't expose `.url` even though Django's test client always sets it on + 302s). + No production code changed. The full project surface + (`mypy --config-file mypy.ini opencontractserver config`) stays clean under + both the pre-commit pin (`mypy==2.0.0`) and CI's pin (`mypy==2.1.0`). diff --git a/docs/typing/mypy_baseline.txt b/docs/typing/mypy_baseline.txt index 923b440e0..f689115b6 100644 --- a/docs/typing/mypy_baseline.txt +++ b/docs/typing/mypy_baseline.txt @@ -334,128 +334,6 @@ opencontractserver/tests/performance_optimizations/test_base.py:166: error: "Dir opencontractserver/tests/performance_optimizations/test_base.py:22: error: "setUp" undefined in superclass [misc] opencontractserver/tests/performance_optimizations/test_base.py:52: error: "NoEmbeddingsMixin" has no attribute "addCleanup" [attr-defined] opencontractserver/tests/performance_optimizations/test_pdf_hash_graphql.py:109: error: Value of type "Any | None" is not indexable [index] -opencontractserver/tests/test_admin.py:148: error: Cannot assign to a method [method-assign] -opencontractserver/tests/test_admin.py:19: error: "object" has no attribute "_meta" [attr-defined] -opencontractserver/tests/test_admin.py:20: error: "object" has no attribute "pk" [attr-defined] -opencontractserver/tests/test_admin.py:27: error: "object" has no attribute "_meta" [attr-defined] -opencontractserver/tests/test_admin.py:29: error: "object" has no attribute "pk" [attr-defined] -opencontractserver/tests/test_admin.py:94: error: Argument 2 to "CorpusAdmin" has incompatible type "None"; expected "AdminSite" [arg-type] -opencontractserver/tests/test_admin_auth.py:105: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:106: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:113: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:115: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:117: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:132: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:134: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:136: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:137: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:147: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:148: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:152: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:154: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:156: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:166: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:167: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:168: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:176: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:190: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:192: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:194: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:208: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:210: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:212: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:221: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:222: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:229: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:231: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:233: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:247: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:249: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:251: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:25: error: "type[TestAdminClaimsSync]" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:265: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:267: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:269: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:278: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:279: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:286: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:288: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:290: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:299: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:300: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:307: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:309: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:311: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:328: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:34: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:35: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:36: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:37: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:439: error: "type[TestAuth0AdminBackend]" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:445: error: "type[TestAuth0AdminBackend]" has no attribute "non_staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:451: error: "type[TestAuth0AdminBackend]" has no attribute "inactive_staff" [attr-defined] -opencontractserver/tests/test_admin_auth.py:457: error: "type[TestAuth0AdminBackend]" has no attribute "superuser" [attr-defined] -opencontractserver/tests/test_admin_auth.py:470: error: "TestAuth0AdminBackend" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:480: error: "TestAuth0AdminBackend" has no attribute "superuser" [attr-defined] -opencontractserver/tests/test_admin_auth.py:51: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:538: error: "TestAuth0AdminBackend" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:53: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:540: error: "TestAuth0AdminBackend" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:558: error: "type[TestAdminLoginView]" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:55: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:564: error: "type[TestAdminLoginView]" has no attribute "regular_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:625: error: "TestAdminLoginView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:630: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:635: error: "TestAdminLoginView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:650: error: "TestAdminLoginView" has no attribute "regular_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:65: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:66: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:685: error: "TestAdminLoginView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:696: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:710: error: "TestAdminLoginView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:711: error: "TestAdminLoginView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:713: error: "TestAdminLoginView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:715: error: "TestAdminLoginView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:728: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:731: error: "TestAdminLoginView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:732: error: "TestAdminLoginView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:73: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:75: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:764: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:765: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:77: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:781: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:782: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:802: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:803: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:818: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:834: error: "type[TestAdminLogoutView]" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:846: error: "TestAdminLogoutView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:855: error: "TestAdminLogoutView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:866: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:871: error: "TestAdminLogoutView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:876: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:885: error: "TestAdminLogoutView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:890: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:891: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:901: error: "TestAdminLogoutView" has no attribute "staff_user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:907: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:909: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:911: error: "_MonkeyPatchedWSGIResponse" has no attribute "url" [attr-defined] -opencontractserver/tests/test_admin_auth.py:91: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:939: error: "type[TestGetUserByPayloadWithClaimSync]" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:93: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:948: error: "TestGetUserByPayloadWithClaimSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:949: error: "TestGetUserByPayloadWithClaimSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:950: error: "TestGetUserByPayloadWithClaimSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:951: error: "TestGetUserByPayloadWithClaimSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:95: error: "TestAdminClaimsSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:966: error: "TestGetUserByPayloadWithClaimSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:972: error: "TestGetUserByPayloadWithClaimSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:975: error: "TestGetUserByPayloadWithClaimSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:976: error: "TestGetUserByPayloadWithClaimSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:977: error: "TestGetUserByPayloadWithClaimSync" has no attribute "user" [attr-defined] -opencontractserver/tests/test_admin_auth.py:994: error: "type[TestAdminLoginRateLimit]" has no attribute "staff_user" [attr-defined] opencontractserver/tests/test_analysis_annotation_import.py:126: error: Argument "analysis_results" to "import_annotations_from_analysis" has incompatible type "dict[str, object]"; expected "OpenContractsGeneratedCorpusPythonType" [arg-type] opencontractserver/tests/test_analysis_annotation_import.py:170: error: Argument 1 to "set_permissions_for_obj_to_user" has incompatible type "User"; expected "int | str | type[User?]" [arg-type] opencontractserver/tests/test_analysis_annotation_import.py:171: error: Argument 1 to "set_permissions_for_obj_to_user" has incompatible type "User"; expected "int | str | type[User?]" [arg-type] diff --git a/mypy.ini b/mypy.ini index 68edad88f..b33b03073 100644 --- a/mypy.ini +++ b/mypy.ini @@ -177,12 +177,6 @@ ignore_errors = True [mypy-opencontractserver.tests.test_url_annotation] ignore_errors = True -[mypy-opencontractserver.tests.test_admin] -ignore_errors = True - -[mypy-opencontractserver.tests.test_admin_auth] -ignore_errors = True - [mypy-opencontractserver.tests.test_analysis_annotation_import] ignore_errors = True diff --git a/opencontractserver/tests/test_admin.py b/opencontractserver/tests/test_admin.py index 84ea9b3c9..4373b9f3f 100644 --- a/opencontractserver/tests/test_admin.py +++ b/opencontractserver/tests/test_admin.py @@ -1,27 +1,26 @@ import logging from unittest.mock import Mock, patch -from django.contrib.auth import get_user_model from django.contrib.auth.models import Group +from django.db.models import Model from django.test import Client, TestCase from django.urls import reverse from opencontractserver.corpuses.admin import CorpusAdmin from opencontractserver.corpuses.models import Corpus - -User = get_user_model() +from opencontractserver.users.models import User logger = logging.getLogger(__name__) -def get_admin_change_view_url(obj: object) -> str: +def get_admin_change_view_url(obj: Model) -> str: return reverse( f"admin:{obj._meta.app_label}_{type(obj).__name__.lower()}_change", args=(obj.pk,), ) -def get_admin_changelist_view_url(obj: object) -> str: +def get_admin_changelist_view_url(obj: Model) -> str: return reverse( "admin:{}_{}_changelist".format( obj._meta.app_label, type(obj).__name__.lower() @@ -91,7 +90,7 @@ def setUp(self) -> None: self.admin_client = Client() self.admin_client.login(username="superuser", password="secret") - self.corpus_admin = CorpusAdmin(Corpus, None) + self.corpus_admin = CorpusAdmin(Corpus, None) # type: ignore[arg-type] def test_gremlin_changelist(self): url = reverse("admin:analyzer_gremlinengine_changelist") @@ -145,7 +144,7 @@ def test_make_public(self, mock_task): corpus2.save() request = Mock() - self.corpus_admin.message_user = Mock() + self.corpus_admin.message_user = Mock() # type: ignore[method-assign] # Only pass the corpuses we created in this test test_corpuses = Corpus.objects.filter(id__in=[corpus1.pk, corpus2.pk]) diff --git a/opencontractserver/tests/test_admin_auth.py b/opencontractserver/tests/test_admin_auth.py index 8257b7da3..93609aceb 100644 --- a/opencontractserver/tests/test_admin_auth.py +++ b/opencontractserver/tests/test_admin_auth.py @@ -10,16 +10,17 @@ from unittest.mock import patch -from django.contrib.auth import get_user_model from django.core.cache import cache from django.test import Client, TestCase, override_settings -User = get_user_model() +from opencontractserver.users.models import User class TestAdminClaimsSync(TestCase): """Tests for syncing admin claims from Auth0 tokens.""" + user: User + @classmethod def setUpTestData(cls): cls.user = User.objects.create_user( @@ -338,6 +339,8 @@ def test_sync_returns_false_on_save_exception(self, mock_save): class TestAuth0SuperuserAllowlist(TestCase): """Tests for the AUTH0_SUPERUSER_SUB_ALLOWLIST defense-in-depth check.""" + user: User + @classmethod def setUpTestData(cls): cls.user = User.objects.create_user( @@ -574,6 +577,11 @@ def test_parse_invalid_string(self): class TestAuth0AdminBackend(TestCase): """Tests for the Auth0 admin authentication backend.""" + staff_user: User + non_staff_user: User + inactive_staff: User + superuser: User + @classmethod def setUpTestData(cls): cls.staff_user = User.objects.create_user( @@ -693,6 +701,9 @@ def test_get_user_not_found(self): class TestAdminLoginView(TestCase): """Tests for the custom admin login view.""" + staff_user: User + regular_user: User + @classmethod def setUpTestData(cls): cls.staff_user = User.objects.create_user( @@ -767,7 +778,7 @@ def test_already_authenticated_redirects(self): response = self.client.get("/admin/login/") self.assertEqual(response.status_code, 302) - self.assertIn("admin", response.url) + self.assertIn("admin", response.url) # type: ignore[attr-defined] @patch("config.jwt_utils.get_user_from_jwt_token") def test_token_login_success(self, mock_get_user): @@ -833,7 +844,7 @@ def test_token_login_fails_on_claim_sync_error(self, mock_sync, mock_get_user): # Login should fail when claim sync returns False self.assertEqual(response.status_code, 302) - self.assertIn("login", response.url) + self.assertIn("login", response.url) # type: ignore[attr-defined] mock_get_user.assert_called_once_with("valid_test_token") mock_sync.assert_called_once() @@ -865,7 +876,7 @@ def test_admin_login_revokes_staff_when_claim_false( # Should be denied (no longer staff) self.assertEqual(response.status_code, 302) - self.assertIn("login", response.url) + self.assertIn("login", response.url) # type: ignore[attr-defined] # Verify is_staff was actually revoked self.staff_user.refresh_from_db() @@ -936,8 +947,8 @@ def test_open_redirect_blocked_external_url(self): self.assertEqual(response.status_code, 302) # Should redirect to admin, not evil.com - self.assertNotIn("evil.com", response.url) - self.assertIn("admin", response.url) + self.assertNotIn("evil.com", response.url) # type: ignore[attr-defined] + self.assertIn("admin", response.url) # type: ignore[attr-defined] def test_open_redirect_blocked_protocol_relative(self): """Protocol-relative URL in next parameter should be blocked.""" @@ -953,8 +964,8 @@ def test_open_redirect_blocked_protocol_relative(self): self.assertEqual(response.status_code, 302) # Should redirect to admin, not evil.com - self.assertNotIn("evil.com", response.url) - self.assertIn("admin", response.url) + self.assertNotIn("evil.com", response.url) # type: ignore[attr-defined] + self.assertIn("admin", response.url) # type: ignore[attr-defined] def test_open_redirect_blocked_backslash(self): """Backslash-prefixed URL in next parameter should be blocked. @@ -974,8 +985,8 @@ def test_open_redirect_blocked_backslash(self): self.assertEqual(response.status_code, 302) # Should redirect to admin, not evil.com - self.assertNotIn("evil.com", response.url) - self.assertIn("admin", response.url) + self.assertNotIn("evil.com", response.url) # type: ignore[attr-defined] + self.assertIn("admin", response.url) # type: ignore[attr-defined] def test_valid_internal_redirect_allowed(self): """Valid internal URL in next parameter should be allowed.""" @@ -990,7 +1001,7 @@ def test_valid_internal_redirect_allowed(self): ) self.assertEqual(response.status_code, 302) - self.assertEqual(response.url, "/admin/users/") + self.assertEqual(response.url, "/admin/users/") # type: ignore[attr-defined] def test_open_redirect_blocked_in_get_next_param(self): """External URL in GET next parameter should be sanitized in context.""" @@ -1004,6 +1015,8 @@ def test_open_redirect_blocked_in_get_next_param(self): class TestAdminLogoutView(TestCase): """Tests for the custom admin logout view.""" + staff_user: User + @classmethod def setUpTestData(cls): cls.staff_user = User.objects.create_user( @@ -1038,7 +1051,7 @@ def test_logout_get_redirects_for_unauthenticated_user(self): response = self.client.get("/admin/logout/") self.assertEqual(response.status_code, 302) - self.assertIn("admin", response.url) + self.assertIn("admin", response.url) # type: ignore[attr-defined] @override_settings(USE_AUTH0=False) def test_logout_redirects_to_admin_when_auth0_disabled(self): @@ -1048,7 +1061,7 @@ def test_logout_redirects_to_admin_when_auth0_disabled(self): response = self.client.post("/admin/logout/") self.assertEqual(response.status_code, 302) - self.assertIn("admin", response.url) + self.assertIn("admin", response.url) # type: ignore[attr-defined] @override_settings( USE_AUTH0=True, @@ -1062,8 +1075,8 @@ def test_logout_redirects_to_auth0_when_enabled(self): response = self.client.post("/admin/logout/") self.assertEqual(response.status_code, 302) - self.assertIn("test.auth0.com", response.url) - self.assertIn("logout", response.url) + self.assertIn("test.auth0.com", response.url) # type: ignore[attr-defined] + self.assertIn("logout", response.url) # type: ignore[attr-defined] @override_settings( USE_AUTH0=True, @@ -1079,11 +1092,11 @@ def test_logout_uses_safe_return_url(self): self.assertEqual(response.status_code, 302) # returnTo should be URL-encoded and use a safe host - self.assertIn("returnTo=", response.url) + self.assertIn("returnTo=", response.url) # type: ignore[attr-defined] # Should prefer the request host when it is allowed - self.assertIn("testserver", response.url) + self.assertIn("testserver", response.url) # type: ignore[attr-defined] # Should redirect to admin login page, not root - self.assertIn("admin%2Flogin", response.url) + self.assertIn("admin%2Flogin", response.url) # type: ignore[attr-defined] @override_settings(ALLOWED_HOSTS=[]) def test_get_safe_logout_return_url_raises_without_valid_hosts(self): @@ -1109,6 +1122,8 @@ def test_get_safe_logout_return_url_raises_without_valid_hosts(self): class TestGetUserByPayloadWithClaimSync(TestCase): """Integration tests for get_user_by_payload with claim syncing.""" + user: User + @classmethod def setUpTestData(cls): cls.user = User.objects.create_user( @@ -1221,6 +1236,8 @@ def test_admin_claims_cached_noop_when_auth0_disabled(self): class TestAdminLoginRateLimit(TestCase): """Tests for rate limiting on the admin login endpoint.""" + staff_user: User + @classmethod def setUpTestData(cls): cls.staff_user = User.objects.create_user(