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,159 +0,0 @@
1
- """
2
- Tests for decorator syntax (@api.view and @api.viewset).
3
-
4
- This test suite verifies that the decorator pattern works correctly.
5
- """
6
- import pytest
7
- import msgspec
8
- from django_bolt import BoltAPI, ViewSet, action
9
- from django_bolt.views import APIView
10
- from django_bolt.testing import TestClient
11
- from .test_models import Article
12
-
13
-
14
- # --- Fixtures ---
15
-
16
- @pytest.fixture
17
- def api():
18
- """Create a fresh BoltAPI instance for each test."""
19
- return BoltAPI()
20
-
21
-
22
- # --- Tests ---
23
-
24
- def test_view_decorator_syntax(api):
25
- """Test @api.view() decorator syntax."""
26
-
27
- @api.view("/health")
28
- class HealthView(APIView):
29
- async def get(self, request):
30
- return {"status": "healthy"}
31
-
32
- client = TestClient(api)
33
- response = client.get("/health")
34
- assert response.status_code == 200
35
- assert response.json()["status"] == "healthy"
36
-
37
-
38
- def test_view_decorator_with_multiple_methods(api):
39
- """Test @api.view() with multiple HTTP methods."""
40
-
41
- class ItemData(msgspec.Struct):
42
- name: str
43
-
44
- @api.view("/items")
45
- class ItemView(APIView):
46
- async def get(self, request):
47
- return {"items": ["item1", "item2"]}
48
-
49
- async def post(self, request, data: ItemData):
50
- return {"created": True, "item": data.name}
51
-
52
- client = TestClient(api)
53
-
54
- response = client.get("/items")
55
- assert response.status_code == 200
56
- assert "items" in response.json()
57
-
58
- response = client.post("/items", json={"name": "test"})
59
- assert response.status_code == 200
60
- assert response.json()["created"] is True
61
-
62
-
63
- @pytest.mark.django_db(transaction=True)
64
- def test_viewset_decorator_syntax(api):
65
- """Test @api.viewset() decorator syntax."""
66
-
67
- class ArticleSchema(msgspec.Struct):
68
- id: int
69
- title: str
70
-
71
- @api.viewset("/articles")
72
- class ArticleViewSet(ViewSet):
73
- queryset = Article.objects.all()
74
- serializer_class = ArticleSchema
75
-
76
- async def list(self, request):
77
- return []
78
-
79
- async def retrieve(self, request, pk: int):
80
- article = await self.get_object(pk)
81
- return ArticleSchema(id=article.id, title=article.title)
82
-
83
- # Create test article
84
- article = Article.objects.create(
85
- title="Test Article",
86
- content="Test content",
87
- author="Test Author"
88
- )
89
-
90
- client = TestClient(api)
91
-
92
- # Test list
93
- response = client.get("/articles")
94
- assert response.status_code == 200
95
-
96
- # Test retrieve
97
- response = client.get(f"/articles/{article.id}")
98
- assert response.status_code == 200
99
- data = response.json()
100
- assert data["title"] == "Test Article"
101
-
102
-
103
- @pytest.mark.django_db(transaction=True)
104
- def test_viewset_decorator_with_custom_actions(api):
105
- """Test @api.viewset() decorator with @action decorator."""
106
-
107
- class ArticleSchema(msgspec.Struct):
108
- id: int
109
- title: str
110
-
111
- @api.viewset("/articles")
112
- class ArticleViewSet(ViewSet):
113
- queryset = Article.objects.all()
114
-
115
- async def list(self, request):
116
- return []
117
-
118
- @action(methods=["POST"], detail=True)
119
- async def publish(self, request, pk: int):
120
- article = await self.get_object(pk)
121
- article.is_published = True
122
- await article.asave()
123
- return {"published": True, "article_id": pk}
124
-
125
- @action(methods=["GET"], detail=False)
126
- async def published(self, request):
127
- articles = []
128
- async for article in Article.objects.filter(is_published=True):
129
- articles.append(ArticleSchema(id=article.id, title=article.title))
130
- return articles
131
-
132
- # Create test articles
133
- article1 = Article.objects.create(
134
- title="Published",
135
- content="Content",
136
- author="Author",
137
- is_published=True
138
- )
139
- article2 = Article.objects.create(
140
- title="Draft",
141
- content="Content",
142
- author="Author",
143
- is_published=False
144
- )
145
-
146
- client = TestClient(api)
147
-
148
- # Test custom action: publish
149
- response = client.post(f"/articles/{article2.id}/publish")
150
- assert response.status_code == 200
151
- assert response.json()["published"] is True
152
-
153
- # Test custom action: published
154
- response = client.get("/articles/published")
155
- assert response.status_code == 200
156
- data = response.json()
157
- assert len(data) == 2 # Both should be published now
158
-
159
-
@@ -1,481 +0,0 @@
1
- """Tests for Django-Bolt error handling system."""
2
-
3
- import pytest
4
- import msgspec
5
- from django_bolt.exceptions import (
6
- HTTPException,
7
- BadRequest,
8
- Unauthorized,
9
- Forbidden,
10
- NotFound,
11
- UnprocessableEntity,
12
- TooManyRequests,
13
- InternalServerError,
14
- ServiceUnavailable,
15
- RequestValidationError,
16
- ResponseValidationError,
17
- )
18
- from django_bolt.error_handlers import (
19
- format_error_response,
20
- http_exception_handler,
21
- request_validation_error_handler,
22
- response_validation_error_handler,
23
- msgspec_validation_error_to_dict,
24
- generic_exception_handler,
25
- handle_exception,
26
- )
27
-
28
-
29
- class TestExceptions:
30
- """Test exception classes."""
31
-
32
- def test_http_exception_basic(self):
33
- """Test basic HTTPException."""
34
- exc = HTTPException(status_code=400, detail="Bad request")
35
- assert exc.status_code == 400
36
- assert exc.detail == "Bad request"
37
- assert exc.headers == {}
38
- assert exc.extra is None
39
-
40
- def test_http_exception_with_headers(self):
41
- """Test HTTPException with custom headers."""
42
- exc = HTTPException(
43
- status_code=401,
44
- detail="Unauthorized",
45
- headers={"WWW-Authenticate": "Bearer"}
46
- )
47
- assert exc.status_code == 401
48
- assert exc.headers == {"WWW-Authenticate": "Bearer"}
49
-
50
- def test_http_exception_with_extra(self):
51
- """Test HTTPException with extra data."""
52
- exc = HTTPException(
53
- status_code=422,
54
- detail="Validation failed",
55
- extra={"errors": ["field1", "field2"]}
56
- )
57
- assert exc.extra == {"errors": ["field1", "field2"]}
58
-
59
- def test_http_exception_default_message(self):
60
- """Test HTTPException uses HTTP status phrase as default."""
61
- exc = HTTPException(status_code=404)
62
- assert exc.detail == "Not Found"
63
-
64
- def test_specialized_exceptions(self):
65
- """Test specialized exception classes."""
66
- assert BadRequest().status_code == 400
67
- assert Unauthorized().status_code == 401
68
- assert Forbidden().status_code == 403
69
- assert NotFound().status_code == 404
70
- assert UnprocessableEntity().status_code == 422
71
- assert TooManyRequests().status_code == 429
72
- assert InternalServerError().status_code == 500
73
- assert ServiceUnavailable().status_code == 503
74
-
75
- def test_exception_repr(self):
76
- """Test exception __repr__."""
77
- exc = NotFound(detail="User not found")
78
- assert "404" in repr(exc)
79
- assert "NotFound" in repr(exc)
80
- assert "User not found" in repr(exc)
81
-
82
- def test_validation_exception(self):
83
- """Test ValidationException."""
84
- errors = [
85
- {"loc": ["body", "name"], "msg": "Field required", "type": "missing"}
86
- ]
87
- exc = RequestValidationError(errors)
88
- assert exc.errors() == errors
89
-
90
- def test_request_validation_error_with_body(self):
91
- """Test RequestValidationError stores body."""
92
- errors = [{"loc": ["body"], "msg": "Invalid", "type": "value_error"}]
93
- body = {"test": "data"}
94
- exc = RequestValidationError(errors, body=body)
95
- assert exc.body == body
96
-
97
-
98
- class TestErrorHandlers:
99
- """Test error handler functions."""
100
-
101
- def test_format_error_response_simple(self):
102
- """Test simple error response formatting."""
103
- status, headers, body = format_error_response(
104
- status_code=404,
105
- detail="Not found"
106
- )
107
- assert status == 404
108
- assert ("content-type", "application/json") in headers
109
-
110
- # Decode and check JSON
111
- import json
112
- data = json.loads(body)
113
- assert data["detail"] == "Not found"
114
-
115
- def test_format_error_response_with_extra(self):
116
- """Test error response with extra data."""
117
- status, headers, body = format_error_response(
118
- status_code=422,
119
- detail="Validation failed",
120
- extra={"errors": ["field1"]}
121
- )
122
-
123
- import json
124
- data = json.loads(body)
125
- assert data["detail"] == "Validation failed"
126
- assert data["extra"] == {"errors": ["field1"]}
127
-
128
- def test_http_exception_handler(self):
129
- """Test HTTPException handler."""
130
- exc = NotFound(detail="User not found")
131
- status, headers, body = http_exception_handler(exc)
132
-
133
- assert status == 404
134
- import json
135
- data = json.loads(body)
136
- assert data["detail"] == "User not found"
137
-
138
- def test_http_exception_handler_with_headers(self):
139
- """Test HTTPException handler preserves custom headers."""
140
- exc = Unauthorized(
141
- detail="Auth required",
142
- headers={"WWW-Authenticate": "Bearer"}
143
- )
144
- status, headers, body = http_exception_handler(exc)
145
-
146
- assert status == 401
147
- assert ("WWW-Authenticate", "Bearer") in headers
148
-
149
- def test_request_validation_error_handler(self):
150
- """Test request validation error handler."""
151
- errors = [
152
- {
153
- "loc": ["body", "email"],
154
- "msg": "Invalid email",
155
- "type": "value_error"
156
- }
157
- ]
158
- exc = RequestValidationError(errors)
159
- status, headers, body = request_validation_error_handler(exc)
160
-
161
- assert status == 422
162
- import json
163
- data = json.loads(body)
164
- assert isinstance(data["detail"], list)
165
- assert len(data["detail"]) == 1
166
- assert data["detail"][0]["loc"] == ["body", "email"]
167
-
168
- def test_response_validation_error_handler(self):
169
- """Test response validation error handler."""
170
- errors = [
171
- {
172
- "loc": ["response", "id"],
173
- "msg": "Field required",
174
- "type": "missing"
175
- }
176
- ]
177
- exc = ResponseValidationError(errors)
178
- status, headers, body = response_validation_error_handler(exc)
179
-
180
- assert status == 500
181
- import json
182
- data = json.loads(body)
183
- assert data["detail"] == "Response validation error"
184
- assert "validation_errors" in data["extra"]
185
-
186
- def test_generic_exception_handler_production(self):
187
- """Test generic exception handler in production mode."""
188
- exc = ValueError("Something went wrong")
189
- status, headers, body = generic_exception_handler(exc, debug=False)
190
-
191
- assert status == 500
192
- import json
193
- data = json.loads(body)
194
- assert data["detail"] == "Internal Server Error"
195
- # Should not expose details in production
196
- assert "extra" not in data
197
-
198
- def test_generic_exception_handler_debug(self):
199
- """Test generic exception handler in debug mode returns HTML."""
200
- # Configure Django settings for ExceptionReporter
201
- import django
202
- from django.conf import settings
203
- if not settings.configured:
204
- settings.configure(
205
- DEBUG=True,
206
- SECRET_KEY='test-secret-key',
207
- INSTALLED_APPS=[],
208
- ROOT_URLCONF='',
209
- )
210
- django.setup()
211
-
212
- exc = ValueError("Something went wrong")
213
-
214
- # Create a mock request dict
215
- request_dict = {
216
- "method": "GET",
217
- "path": "/test",
218
- "headers": {"user-agent": "test"},
219
- "query_params": {}
220
- }
221
-
222
- status, headers, body = generic_exception_handler(exc, debug=True, request=request_dict)
223
-
224
- assert status == 500, "Debug exception must return 500 status"
225
- # Should return HTML in debug mode
226
- headers_dict = dict(headers)
227
- assert headers_dict.get("content-type") == "text/html; charset=utf-8", \
228
- "Debug mode must return HTML content type"
229
- # Verify it's HTML content
230
- html_content = body.decode("utf-8")
231
- assert "<!DOCTYPE html>" in html_content or "<html>" in html_content, \
232
- "Debug mode must return valid HTML document"
233
- assert "ValueError" in html_content, \
234
- "HTML must contain exception type"
235
- assert "Something went wrong" in html_content, \
236
- "HTML must contain exception message"
237
-
238
- def test_generic_exception_handler_debug_without_request(self):
239
- """Test generic exception handler in debug mode works without request."""
240
- import django
241
- from django.conf import settings
242
- if not settings.configured:
243
- settings.configure(
244
- DEBUG=True,
245
- SECRET_KEY='test-secret-key',
246
- INSTALLED_APPS=[],
247
- ROOT_URLCONF='',
248
- )
249
- django.setup()
250
-
251
- exc = RuntimeError("Error without request context")
252
-
253
- # Call without request parameter
254
- status, headers, body = generic_exception_handler(exc, debug=True, request=None)
255
-
256
- assert status == 500
257
- headers_dict = dict(headers)
258
- assert headers_dict.get("content-type") == "text/html; charset=utf-8"
259
- html_content = body.decode("utf-8")
260
- assert "RuntimeError" in html_content
261
- assert "Error without request context" in html_content
262
-
263
- def test_generic_exception_handler_debug_fallback_to_json(self):
264
- """Test generic exception handler falls back to JSON if HTML generation fails."""
265
- # Mock ExceptionReporter to raise an exception
266
- from unittest.mock import patch
267
-
268
- exc = ValueError("Test exception")
269
-
270
- # Mock the import inside generic_exception_handler
271
- with patch('django.views.debug.ExceptionReporter', side_effect=Exception("HTML failed")):
272
- status, headers, body = generic_exception_handler(exc, debug=True, request=None)
273
-
274
- assert status == 500, "Fallback must return 500 status"
275
- headers_dict = dict(headers)
276
- assert headers_dict.get("content-type") == "application/json", \
277
- "Fallback must return JSON content type"
278
-
279
- # Should fall back to JSON with traceback
280
- import json
281
- data = json.loads(body)
282
- assert "ValueError" in data["detail"], \
283
- "Fallback JSON must contain exception type in detail"
284
- assert "extra" in data, \
285
- "Fallback JSON must include extra field"
286
- assert "traceback" in data["extra"], \
287
- "Fallback JSON must include traceback"
288
- assert "exception_type" in data["extra"], \
289
- "Fallback JSON must include exception_type"
290
- assert data["extra"]["exception_type"] == "ValueError"
291
-
292
- def test_generic_exception_handler_preserves_traceback(self):
293
- """Test that exception traceback is properly preserved and formatted."""
294
- def inner_function():
295
- raise ValueError("Inner error")
296
-
297
- def outer_function():
298
- inner_function()
299
-
300
- try:
301
- outer_function()
302
- except ValueError as exc:
303
- status, headers, body = generic_exception_handler(exc, debug=True, request=None)
304
-
305
- # Try HTML first (primary path)
306
- headers_dict = dict(headers)
307
- if headers_dict.get("content-type") == "text/html; charset=utf-8":
308
- html_content = body.decode("utf-8")
309
- # HTML should contain traceback info
310
- assert "inner_function" in html_content or "outer_function" in html_content, \
311
- "HTML traceback must show function names"
312
- else:
313
- # Fallback JSON path
314
- import json
315
- data = json.loads(body)
316
- traceback_lines = data["extra"]["traceback"]
317
- traceback_str = "".join(traceback_lines)
318
- assert "inner_function" in traceback_str, \
319
- "JSON traceback must contain inner_function"
320
- assert "outer_function" in traceback_str, \
321
- "JSON traceback must contain outer_function"
322
-
323
- def test_handle_exception_http_exception(self):
324
- """Test main exception handler with HTTPException."""
325
- exc = NotFound(detail="Resource not found")
326
- status, headers, body = handle_exception(exc)
327
-
328
- assert status == 404
329
- import json
330
- data = json.loads(body)
331
- assert data["detail"] == "Resource not found"
332
-
333
- def test_handle_exception_validation_error(self):
334
- """Test main exception handler with validation error."""
335
- errors = [{"loc": ["body"], "msg": "Invalid", "type": "value_error"}]
336
- exc = RequestValidationError(errors)
337
- status, headers, body = handle_exception(exc)
338
-
339
- assert status == 422
340
- import json
341
- data = json.loads(body)
342
- assert isinstance(data["detail"], list)
343
-
344
- def test_handle_exception_generic(self):
345
- """Test main exception handler with generic exception."""
346
- exc = RuntimeError("Unexpected error")
347
- status, headers, body = handle_exception(exc, debug=False)
348
-
349
- assert status == 500
350
- import json
351
- data = json.loads(body)
352
- assert data["detail"] == "Internal Server Error"
353
-
354
- def test_handle_exception_with_request_parameter(self):
355
- """Test that handle_exception properly passes request to generic_exception_handler."""
356
- import django
357
- from django.conf import settings
358
- if not settings.configured:
359
- settings.configure(
360
- DEBUG=True,
361
- SECRET_KEY='test-secret-key',
362
- INSTALLED_APPS=[],
363
- ROOT_URLCONF='',
364
- )
365
- django.setup()
366
-
367
- exc = ValueError("Test with request")
368
- request_dict = {
369
- "method": "POST",
370
- "path": "/api/users",
371
- "headers": {"content-type": "application/json"},
372
- }
373
-
374
- status, headers, body = handle_exception(exc, debug=True, request=request_dict)
375
-
376
- assert status == 500, "Exception with request must return 500"
377
- # Should return HTML in debug mode
378
- headers_dict = dict(headers)
379
- assert headers_dict.get("content-type") == "text/html; charset=utf-8", \
380
- "Debug mode with request must return HTML"
381
- html_content = body.decode("utf-8")
382
- assert "ValueError" in html_content
383
- assert "Test with request" in html_content
384
-
385
- def test_handle_exception_respects_django_debug_setting(self):
386
- """Test that handle_exception uses Django DEBUG setting when debug param is not provided."""
387
- from django.conf import settings
388
-
389
- # Store original DEBUG setting
390
- original_debug = settings.DEBUG
391
-
392
- # Temporarily set DEBUG to True for this test
393
- settings.DEBUG = True
394
-
395
- try:
396
- exc = ValueError("Should use Django DEBUG")
397
-
398
- # Call without debug parameter (should check Django settings)
399
- status, headers, _ = handle_exception(exc)
400
-
401
- # Since Django DEBUG=True, should return HTML
402
- headers_dict = dict(headers)
403
- assert headers_dict.get("content-type") == "text/html; charset=utf-8", \
404
- "Should use Django DEBUG=True setting when debug param is not provided"
405
- assert status == 500
406
- finally:
407
- # Restore original setting
408
- settings.DEBUG = original_debug
409
-
410
- def test_handle_exception_debug_overrides_django_setting(self):
411
- """Test that explicit debug=True overrides Django DEBUG=False."""
412
- exc = ValueError("Explicit debug override")
413
-
414
- # Explicitly pass debug=True (should ignore Django settings)
415
- status, _, _ = handle_exception(exc, debug=True)
416
-
417
- # Should return 500 because we have an exception
418
- assert status == 500, "Exception must return 500 status"
419
-
420
- def test_msgspec_validation_error_conversion(self):
421
- """Test msgspec ValidationError to dict conversion."""
422
- # Create a msgspec validation error
423
- class TestStruct(msgspec.Struct):
424
- name: str
425
- age: int
426
-
427
- try:
428
- msgspec.json.decode(b'{"age": "invalid"}', type=TestStruct)
429
- except msgspec.ValidationError as e:
430
- errors = msgspec_validation_error_to_dict(e)
431
- assert isinstance(errors, list)
432
- assert len(errors) > 0
433
- assert "loc" in errors[0]
434
- assert "msg" in errors[0]
435
- assert "type" in errors[0]
436
-
437
-
438
- class TestExceptionIntegration:
439
- """Integration tests for exception handling."""
440
-
441
- def test_exception_chain(self):
442
- """Test that exceptions can be chained properly."""
443
- try:
444
- raise ValueError("Original error")
445
- except ValueError as e:
446
- exc = InternalServerError(detail=str(e))
447
- assert exc.status_code == 500
448
- assert "Original error" in exc.detail
449
-
450
- def test_exception_context_preservation(self):
451
- """Test that exception context is preserved."""
452
- exc = BadRequest(
453
- detail="Invalid input",
454
- extra={"field": "email", "value": "invalid@"}
455
- )
456
-
457
- status, headers, body = http_exception_handler(exc)
458
- import json
459
- data = json.loads(body)
460
-
461
- assert data["detail"] == "Invalid input"
462
- assert data["extra"]["field"] == "email"
463
- assert data["extra"]["value"] == "invalid@"
464
-
465
- def test_multiple_validation_errors(self):
466
- """Test handling multiple validation errors."""
467
- errors = [
468
- {"loc": ["body", "name"], "msg": "Field required", "type": "missing"},
469
- {"loc": ["body", "email"], "msg": "Invalid format", "type": "value_error"},
470
- {"loc": ["body", "age"], "msg": "Must be positive", "type": "value_error"},
471
- ]
472
- exc = RequestValidationError(errors)
473
- status, headers, body = request_validation_error_handler(exc)
474
-
475
- import json
476
- data = json.loads(body)
477
- assert len(data["detail"]) == 3
478
-
479
-
480
- if __name__ == "__main__":
481
- pytest.main([__file__, "-v"])