django-bolt 0.1.0__cp310-abi3-win_amd64.whl → 0.1.2__cp310-abi3-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of django-bolt might be problematic. Click here for more details.

Files changed (56) hide show
  1. django_bolt/__init__.py +2 -2
  2. django_bolt/_core.pyd +0 -0
  3. django_bolt/_json.py +169 -0
  4. django_bolt/admin/static_routes.py +15 -21
  5. django_bolt/api.py +181 -61
  6. django_bolt/auth/__init__.py +2 -2
  7. django_bolt/decorators.py +15 -3
  8. django_bolt/dependencies.py +30 -24
  9. django_bolt/error_handlers.py +2 -1
  10. django_bolt/openapi/plugins.py +3 -2
  11. django_bolt/openapi/schema_generator.py +65 -20
  12. django_bolt/pagination.py +2 -1
  13. django_bolt/responses.py +3 -2
  14. django_bolt/serialization.py +5 -4
  15. {django_bolt-0.1.0.dist-info → django_bolt-0.1.2.dist-info}/METADATA +181 -201
  16. {django_bolt-0.1.0.dist-info → django_bolt-0.1.2.dist-info}/RECORD +18 -55
  17. django_bolt/auth/README.md +0 -464
  18. django_bolt/auth/REVOCATION_EXAMPLE.md +0 -391
  19. django_bolt/tests/__init__.py +0 -0
  20. django_bolt/tests/admin_tests/__init__.py +0 -1
  21. django_bolt/tests/admin_tests/conftest.py +0 -6
  22. django_bolt/tests/admin_tests/test_admin_with_django.py +0 -278
  23. django_bolt/tests/admin_tests/urls.py +0 -9
  24. django_bolt/tests/cbv/__init__.py +0 -0
  25. django_bolt/tests/cbv/test_class_views.py +0 -570
  26. django_bolt/tests/cbv/test_class_views_django_orm.py +0 -703
  27. django_bolt/tests/cbv/test_class_views_features.py +0 -1173
  28. django_bolt/tests/cbv/test_class_views_with_client.py +0 -622
  29. django_bolt/tests/conftest.py +0 -165
  30. django_bolt/tests/test_action_decorator.py +0 -399
  31. django_bolt/tests/test_auth_secret_key.py +0 -83
  32. django_bolt/tests/test_decorator_syntax.py +0 -159
  33. django_bolt/tests/test_error_handling.py +0 -481
  34. django_bolt/tests/test_file_response.py +0 -192
  35. django_bolt/tests/test_global_cors.py +0 -172
  36. django_bolt/tests/test_guards_auth.py +0 -441
  37. django_bolt/tests/test_guards_integration.py +0 -303
  38. django_bolt/tests/test_health.py +0 -283
  39. django_bolt/tests/test_integration_validation.py +0 -400
  40. django_bolt/tests/test_json_validation.py +0 -536
  41. django_bolt/tests/test_jwt_auth.py +0 -327
  42. django_bolt/tests/test_jwt_token.py +0 -458
  43. django_bolt/tests/test_logging.py +0 -837
  44. django_bolt/tests/test_logging_merge.py +0 -419
  45. django_bolt/tests/test_middleware.py +0 -492
  46. django_bolt/tests/test_middleware_server.py +0 -230
  47. django_bolt/tests/test_model_viewset.py +0 -323
  48. django_bolt/tests/test_models.py +0 -24
  49. django_bolt/tests/test_pagination.py +0 -1258
  50. django_bolt/tests/test_parameter_validation.py +0 -178
  51. django_bolt/tests/test_syntax.py +0 -626
  52. django_bolt/tests/test_testing_utilities.py +0 -163
  53. django_bolt/tests/test_testing_utilities_simple.py +0 -123
  54. django_bolt/tests/test_viewset_unified.py +0 -346
  55. {django_bolt-0.1.0.dist-info → django_bolt-0.1.2.dist-info}/WHEEL +0 -0
  56. {django_bolt-0.1.0.dist-info → django_bolt-0.1.2.dist-info}/entry_points.txt +0 -0
@@ -1,165 +0,0 @@
1
- """
2
- Pytest configuration for Django-Bolt tests.
3
-
4
- Ensures Django settings are properly reset between tests.
5
- Provides utilities for subprocess-based testing.
6
- """
7
- import os
8
- import pathlib
9
- import signal
10
- import socket
11
- import subprocess
12
- import sys
13
- import time
14
- import logging
15
- import pytest
16
-
17
- # Suppress httpx INFO logs during tests
18
- logging.getLogger("httpx").setLevel(logging.WARNING)
19
-
20
-
21
- def pytest_configure(config):
22
- """Configure Django settings for pytest-django."""
23
- import django
24
- from django.conf import settings
25
-
26
- # Skip configuration if DJANGO_SETTINGS_MODULE is already set
27
- # This allows specific test modules to use their own Django settings
28
- if os.getenv("DJANGO_SETTINGS_MODULE"):
29
- return
30
-
31
- if not settings.configured:
32
- # Configure with all apps including admin to support all tests
33
- # The admin apps don't significantly impact non-admin tests
34
- settings.configure(
35
- DEBUG=True,
36
- SECRET_KEY='test-secret-key-global',
37
- ALLOWED_HOSTS=['*'],
38
- INSTALLED_APPS=[
39
- 'django.contrib.admin',
40
- 'django.contrib.auth',
41
- 'django.contrib.contenttypes',
42
- 'django.contrib.sessions',
43
- 'django.contrib.messages',
44
- 'django.contrib.staticfiles',
45
- 'django_bolt',
46
- ],
47
- MIDDLEWARE=[
48
- 'django.middleware.security.SecurityMiddleware',
49
- 'django.contrib.sessions.middleware.SessionMiddleware',
50
- 'django.middleware.common.CommonMiddleware',
51
- 'django.middleware.csrf.CsrfViewMiddleware',
52
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
53
- 'django.contrib.messages.middleware.MessageMiddleware',
54
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
55
- ],
56
- ROOT_URLCONF='django_bolt.tests.admin_tests.urls',
57
- TEMPLATES=[
58
- {
59
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
60
- 'DIRS': [],
61
- 'APP_DIRS': True,
62
- 'OPTIONS': {
63
- 'context_processors': [
64
- 'django.template.context_processors.debug',
65
- 'django.template.context_processors.request',
66
- 'django.contrib.auth.context_processors.auth',
67
- 'django.contrib.messages.context_processors.messages',
68
- ],
69
- },
70
- },
71
- ],
72
- DATABASES={
73
- 'default': {
74
- 'ENGINE': 'django.db.backends.sqlite3',
75
- 'NAME': ':memory:',
76
- }
77
- },
78
- USE_TZ=True,
79
- LANGUAGE_CODE='en-us',
80
- TIME_ZONE='UTC',
81
- USE_I18N=True,
82
- STATIC_URL='/static/',
83
- DEFAULT_AUTO_FIELD='django.db.models.BigAutoField',
84
- )
85
- # Setup Django apps so ExceptionReporter works
86
- django.setup()
87
-
88
-
89
- @pytest.fixture(scope='session')
90
- def django_db_setup(django_db_setup, django_db_blocker):
91
- """
92
- Ensure database migrations are run before any tests that use the database.
93
- This creates the auth_user table and other Django core tables.
94
- Also creates test model tables (Article, etc.).
95
- """
96
- from django.core.management import call_command
97
- from django.db import connection
98
-
99
- with django_db_blocker.unblock():
100
- # Run migrations to create all necessary tables
101
- call_command('migrate', '--run-syncdb', verbosity=0)
102
-
103
- # Create test model tables manually since they're not in migrations
104
- with connection.schema_editor() as schema_editor:
105
- from django_bolt.tests.test_models import Article
106
- schema_editor.create_model(Article)
107
-
108
-
109
- def spawn_process(command):
110
- """Spawn a subprocess in a new process group"""
111
- import platform
112
- if platform.system() == "Windows":
113
- process = subprocess.Popen(
114
- command,
115
- shell=True,
116
- creationflags=subprocess.CREATE_NEW_PROCESS_GROUP,
117
- stdout=subprocess.PIPE,
118
- stderr=subprocess.PIPE
119
- )
120
- else:
121
- process = subprocess.Popen(
122
- command,
123
- preexec_fn=os.setsid,
124
- stdout=subprocess.PIPE,
125
- stderr=subprocess.PIPE
126
- )
127
- return process
128
-
129
-
130
- def kill_process(process):
131
- """Kill a subprocess and its process group"""
132
- import platform
133
- if platform.system() == "Windows":
134
- try:
135
- process.send_signal(signal.CTRL_BREAK_EVENT)
136
- except:
137
- pass
138
- try:
139
- process.kill()
140
- except:
141
- pass
142
- else:
143
- try:
144
- os.killpg(os.getpgid(process.pid), signal.SIGKILL)
145
- except ProcessLookupError:
146
- pass
147
- except:
148
- pass
149
-
150
-
151
- def wait_for_server(host, port, timeout=15):
152
- """Wait for server to be reachable"""
153
- start_time = time.time()
154
- while time.time() - start_time < timeout:
155
- try:
156
- sock = socket.create_connection((host, port), timeout=2)
157
- sock.close()
158
- return True
159
- except Exception:
160
- time.sleep(0.5)
161
- return False
162
-
163
-
164
- # Django configuration is now handled by pytest-django
165
- # via pytest_configure above
@@ -1,399 +0,0 @@
1
- """
2
- Tests for @action decorator with ViewSets.
3
-
4
- This test suite verifies that the @action decorator works correctly:
5
- - Instance-level actions (detail=True)
6
- - Collection-level actions (detail=False)
7
- - Multiple HTTP methods on single action
8
- - Custom path parameter
9
- - Auth/guards inheritance from class-level
10
- """
11
- import pytest
12
- import msgspec
13
- from django_bolt import BoltAPI, ViewSet, action
14
- from django_bolt.testing import TestClient
15
- from .test_models import Article
16
-
17
-
18
- # --- Fixtures ---
19
-
20
- @pytest.fixture
21
- def api():
22
- """Create a fresh BoltAPI instance for each test."""
23
- return BoltAPI()
24
-
25
-
26
- # --- Schemas ---
27
-
28
- class ArticleSchema(msgspec.Struct):
29
- """Article schema."""
30
- id: int
31
- title: str
32
- content: str
33
-
34
-
35
- class ArticleCreateSchema(msgspec.Struct):
36
- """Schema for creating articles."""
37
- title: str
38
- content: str
39
- author: str
40
-
41
-
42
- # --- Tests ---
43
-
44
- @pytest.mark.django_db(transaction=True)
45
- def test_action_decorator_detail_true(api):
46
- """Test @action with detail=True (instance-level action)."""
47
-
48
- @api.viewset("/articles")
49
- class ArticleViewSet(ViewSet):
50
- queryset = Article.objects.all()
51
- serializer_class = ArticleSchema
52
-
53
- async def list(self, request):
54
- """List articles."""
55
- articles = []
56
- async for article in await self.get_queryset():
57
- articles.append(ArticleSchema(
58
- id=article.id,
59
- title=article.title,
60
- content=article.content
61
- ))
62
- return articles
63
-
64
- async def retrieve(self, request, pk: int):
65
- """Retrieve single article."""
66
- article = await self.get_object(pk)
67
- return ArticleSchema(
68
- id=article.id,
69
- title=article.title,
70
- content=article.content
71
- )
72
-
73
- @action(methods=["POST"], detail=True)
74
- async def publish(self, request, pk: int):
75
- """Publish an article. POST /articles/{pk}/publish"""
76
- article = await self.get_object(pk)
77
- article.is_published = True
78
- await article.asave()
79
- return {"published": True, "article_id": pk}
80
-
81
- # Create test article
82
- article = Article.objects.create(
83
- title="Test Article",
84
- content="Test content",
85
- author="Test Author",
86
- is_published=False
87
- )
88
-
89
- client = TestClient(api)
90
-
91
- # Test the custom action
92
- response = client.post(f"/articles/{article.id}/publish")
93
- assert response.status_code == 200
94
- data = response.json()
95
- assert data["published"] is True
96
- assert data["article_id"] == article.id
97
-
98
- # Verify article was published
99
- article.refresh_from_db()
100
- assert article.is_published is True
101
-
102
-
103
- @pytest.mark.django_db(transaction=True)
104
- def test_action_decorator_detail_false(api):
105
- """Test @action with detail=False (collection-level action)."""
106
-
107
- @api.viewset("/articles")
108
- class ArticleViewSet(ViewSet):
109
- queryset = Article.objects.all()
110
- serializer_class = ArticleSchema
111
-
112
- async def list(self, request):
113
- """List articles."""
114
- articles = []
115
- async for article in await self.get_queryset():
116
- articles.append(ArticleSchema(
117
- id=article.id,
118
- title=article.title,
119
- content=article.content
120
- ))
121
- return articles
122
-
123
- @action(methods=["GET"], detail=False)
124
- async def published(self, request):
125
- """Get published articles. GET /articles/published"""
126
- articles = []
127
- async for article in Article.objects.filter(is_published=True):
128
- articles.append(ArticleSchema(
129
- id=article.id,
130
- title=article.title,
131
- content=article.content
132
- ))
133
- return articles
134
-
135
- # Create test articles
136
- Article.objects.create(
137
- title="Published 1",
138
- content="Content 1",
139
- author="Author 1",
140
- is_published=True
141
- )
142
- Article.objects.create(
143
- title="Draft",
144
- content="Content 2",
145
- author="Author 2",
146
- is_published=False
147
- )
148
- Article.objects.create(
149
- title="Published 2",
150
- content="Content 3",
151
- author="Author 3",
152
- is_published=True
153
- )
154
-
155
- client = TestClient(api)
156
-
157
- # Test the custom action
158
- response = client.get("/articles/published")
159
- assert response.status_code == 200
160
- data = response.json()
161
- assert len(data) == 2
162
- assert all(article["title"].startswith("Published") for article in data)
163
-
164
-
165
- @pytest.mark.django_db(transaction=True)
166
- def test_action_decorator_multiple_methods(api):
167
- """Test @action with multiple HTTP methods."""
168
-
169
- class StatusUpdate(msgspec.Struct):
170
- """Schema for status update."""
171
- is_published: bool
172
-
173
- @api.viewset("/articles")
174
- class ArticleViewSet(ViewSet):
175
- queryset = Article.objects.all()
176
- serializer_class = ArticleSchema
177
-
178
- async def list(self, request):
179
- """List articles."""
180
- return []
181
-
182
- @action(methods=["GET"], detail=True, path="status")
183
- async def get_status(self, request, pk: int):
184
- """GET /articles/{pk}/status - Get article status"""
185
- article = await self.get_object(pk)
186
- return {"is_published": article.is_published}
187
-
188
- @action(methods=["POST"], detail=True, path="status")
189
- async def update_status(self, request, pk: int, data: StatusUpdate):
190
- """POST /articles/{pk}/status - Update article status"""
191
- article = await self.get_object(pk)
192
- article.is_published = data.is_published
193
- await article.asave()
194
- return {"updated": True, "is_published": article.is_published}
195
-
196
- # Create test article
197
- article = Article.objects.create(
198
- title="Test Article",
199
- content="Test content",
200
- author="Test Author",
201
- is_published=False
202
- )
203
-
204
- client = TestClient(api)
205
-
206
- # Test GET
207
- response = client.get(f"/articles/{article.id}/status")
208
- assert response.status_code == 200
209
- data = response.json()
210
- assert data["is_published"] is False
211
-
212
- # Test POST
213
- response = client.post(f"/articles/{article.id}/status", json={"is_published": True})
214
- assert response.status_code == 200
215
- data = response.json()
216
- assert data["updated"] is True
217
- assert data["is_published"] is True
218
-
219
-
220
- @pytest.mark.django_db(transaction=True)
221
- def test_action_decorator_custom_path(api):
222
- """Test @action with custom path parameter."""
223
-
224
- @api.viewset("/articles")
225
- class ArticleViewSet(ViewSet):
226
- queryset = Article.objects.all()
227
- serializer_class = ArticleSchema
228
-
229
- async def list(self, request):
230
- """List articles."""
231
- return []
232
-
233
- @action(methods=["POST"], detail=True, path="custom-action-name")
234
- async def some_method_name(self, request, pk: int):
235
- """POST /articles/{pk}/custom-action-name"""
236
- return {"action": "custom-action-name", "article_id": pk}
237
-
238
- client = TestClient(api)
239
-
240
- # Create test article
241
- article = Article.objects.create(
242
- title="Test Article",
243
- content="Test content",
244
- author="Test Author"
245
- )
246
-
247
- # Test custom path (not method name)
248
- response = client.post(f"/articles/{article.id}/custom-action-name")
249
- assert response.status_code == 200
250
- data = response.json()
251
- assert data["action"] == "custom-action-name"
252
- assert data["article_id"] == article.id
253
-
254
-
255
- @pytest.mark.django_db(transaction=True)
256
- def test_action_decorator_with_api_view_raises_error(api):
257
- """Test that @action raises error when used with api.view() instead of api.viewset()."""
258
-
259
- # This should raise an error because api.view() doesn't support @action
260
- with pytest.raises(ValueError, match="uses @action decorator.*api.viewset"):
261
- @api.view("/articles", methods=["GET"])
262
- class ArticleViewSet(ViewSet):
263
- async def get(self, request):
264
- return []
265
-
266
- @action(methods=["POST"], detail=False)
267
- async def custom_action(self, request):
268
- return {"ok": True}
269
-
270
-
271
- @pytest.mark.django_db(transaction=True)
272
- def test_action_decorator_defaults_to_function_name(api):
273
- """Test that @action uses function name as default path."""
274
-
275
- @api.viewset("/articles")
276
- class ArticleViewSet(ViewSet):
277
- queryset = Article.objects.all()
278
- serializer_class = ArticleSchema
279
-
280
- async def list(self, request):
281
- """List articles."""
282
- return []
283
-
284
- @action(methods=["POST"], detail=True)
285
- async def archive(self, request, pk: int):
286
- """POST /articles/{pk}/archive"""
287
- return {"archived": True, "article_id": pk}
288
-
289
- client = TestClient(api)
290
-
291
- # Create test article
292
- article = Article.objects.create(
293
- title="Test Article",
294
- content="Test content",
295
- author="Test Author"
296
- )
297
-
298
- # Test action at /articles/{pk}/archive (function name)
299
- response = client.post(f"/articles/{article.id}/archive")
300
- assert response.status_code == 200
301
- data = response.json()
302
- assert data["archived"] is True
303
- assert data["article_id"] == article.id
304
-
305
-
306
- @pytest.mark.django_db(transaction=True)
307
- def test_action_decorator_with_query_params(api):
308
- """Test @action with query parameters."""
309
-
310
- @api.viewset("/articles")
311
- class ArticleViewSet(ViewSet):
312
- queryset = Article.objects.all()
313
- serializer_class = ArticleSchema
314
-
315
- async def list(self, request):
316
- """List articles."""
317
- return []
318
-
319
- @action(methods=["GET"], detail=False)
320
- async def search(self, request, query: str, limit: int = 10):
321
- """GET /articles/search?query=xxx&limit=5"""
322
- articles = []
323
- async for article in Article.objects.filter(title__icontains=query)[:limit]:
324
- articles.append(ArticleSchema(
325
- id=article.id,
326
- title=article.title,
327
- content=article.content
328
- ))
329
- return {"query": query, "limit": limit, "results": articles}
330
-
331
- # Create test articles
332
- Article.objects.create(title="Python Guide", content="Content", author="Author")
333
- Article.objects.create(title="Django Tutorial", content="Content", author="Author")
334
- Article.objects.create(title="Python Basics", content="Content", author="Author")
335
-
336
- client = TestClient(api)
337
-
338
- # Test with query params
339
- response = client.get("/articles/search?query=Python&limit=5")
340
- assert response.status_code == 200
341
- data = response.json()
342
- assert data["query"] == "Python"
343
- assert data["limit"] == 5
344
- assert len(data["results"]) == 2
345
-
346
-
347
- @pytest.mark.django_db(transaction=True)
348
- def test_action_decorator_invalid_method(api):
349
- """Test that @action raises error for invalid HTTP methods."""
350
-
351
- with pytest.raises(ValueError, match="Invalid HTTP method"):
352
- @action(methods=["INVALID"], detail=True)
353
- async def some_action(self, request, pk: int):
354
- pass
355
-
356
-
357
- @pytest.mark.django_db(transaction=True)
358
- def test_action_decorator_with_different_lookup_fields(api):
359
- """Test @action respects custom lookup_field."""
360
-
361
- @api.viewset("/articles")
362
- class ArticleViewSet(ViewSet):
363
- queryset = Article.objects.all()
364
- serializer_class = ArticleSchema
365
- lookup_field = 'id' # Explicitly set to 'id' instead of default 'pk'
366
-
367
- async def list(self, request):
368
- """List articles."""
369
- return []
370
-
371
- async def retrieve(self, request, id: int):
372
- """Retrieve article by id."""
373
- article = await self.get_object(id=id)
374
- return ArticleSchema(
375
- id=article.id,
376
- title=article.title,
377
- content=article.content
378
- )
379
-
380
- @action(methods=["POST"], detail=True)
381
- async def feature(self, request, id: int):
382
- """POST /articles/{id}/feature"""
383
- return {"featured": True, "article_id": id}
384
-
385
- client = TestClient(api)
386
-
387
- # Create test article
388
- article = Article.objects.create(
389
- title="Test Article",
390
- content="Test content",
391
- author="Test Author"
392
- )
393
-
394
- # Test action with custom lookup field
395
- response = client.post(f"/articles/{article.id}/feature")
396
- assert response.status_code == 200
397
- data = response.json()
398
- assert data["featured"] is True
399
- assert data["article_id"] == article.id
@@ -1,83 +0,0 @@
1
- """
2
- Test that JWT authentication uses Django SECRET_KEY when not specified
3
- """
4
- import pytest
5
- from django_bolt import BoltAPI
6
- from django_bolt.auth import JWTAuthentication
7
- from django_bolt.auth import IsAuthenticated
8
-
9
-
10
- def test_jwt_auth_uses_django_secret_key():
11
- """Test that JWTAuthentication uses Django SECRET_KEY when secret not provided"""
12
- # Django is already configured by pytest-django with SECRET_KEY='test-secret-key-global'
13
- from django.conf import settings
14
-
15
- # Create JWT auth without explicit secret
16
- auth = JWTAuthentication() # No secret specified
17
-
18
- # Should use Django's SECRET_KEY
19
- assert auth.secret == settings.SECRET_KEY
20
- print("✓ JWTAuthentication uses Django SECRET_KEY when not specified")
21
-
22
-
23
- def test_jwt_auth_explicit_secret_overrides():
24
- """Test that explicit secret overrides Django SECRET_KEY"""
25
- from django.conf import settings
26
-
27
- # Create with explicit secret
28
- auth = JWTAuthentication(secret="custom-secret")
29
-
30
- # Should use the explicit secret, not Django's
31
- assert auth.secret == 'custom-secret'
32
- assert auth.secret != settings.SECRET_KEY
33
- print("✓ Explicit secret overrides Django SECRET_KEY")
34
-
35
-
36
- def test_route_with_django_secret():
37
- """Test that route-level auth uses Django SECRET_KEY"""
38
- from django.conf import settings
39
-
40
- api = BoltAPI()
41
-
42
- @api.get(
43
- "/protected",
44
- auth=[JWTAuthentication()], # No secret - should use Django's
45
- guards=[IsAuthenticated()]
46
- )
47
- async def protected_endpoint():
48
- return {"message": "Protected"}
49
-
50
- # Check that metadata has Django SECRET_KEY
51
- handler_id = 0
52
- if handler_id in api._handler_middleware:
53
- metadata = api._handler_middleware[handler_id]
54
- auth_backends = metadata.get('auth_backends', [])
55
- assert len(auth_backends) > 0
56
- assert auth_backends[0]['secret'] == settings.SECRET_KEY
57
- print("✓ Route-level JWT auth uses Django SECRET_KEY")
58
-
59
-
60
- def test_global_auth_with_django_secret():
61
- """Test global auth configuration with Django SECRET_KEY"""
62
- from django.conf import settings
63
-
64
- # Set auth classes (settings already configured by pytest-django)
65
- settings.BOLT_AUTHENTICATION_CLASSES = [
66
- JWTAuthentication() # No secret - should use Django's
67
- ]
68
-
69
- from django_bolt.auth import get_default_authentication_classes
70
-
71
- auth_classes = get_default_authentication_classes()
72
- assert len(auth_classes) > 0
73
- assert auth_classes[0].secret == settings.SECRET_KEY
74
- print("✓ Global auth configuration uses Django SECRET_KEY")
75
-
76
-
77
- if __name__ == "__main__":
78
- test_jwt_auth_uses_django_secret_key()
79
- test_jwt_auth_explicit_secret_overrides()
80
- test_route_with_django_secret()
81
- test_global_auth_with_django_secret()
82
-
83
- print("\n✅ All Django SECRET_KEY integration tests passed!")