fastapi-errors-plus 0.5.0__tar.gz → 0.6.1__tar.gz

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.
Files changed (19) hide show
  1. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/LICENSE +1 -0
  2. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/PKG-INFO +72 -5
  3. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/README.md +71 -4
  4. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus/__init__.py +1 -1
  5. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus/base.py +1 -0
  6. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus/protocol.py +1 -0
  7. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus.egg-info/PKG-INFO +72 -5
  8. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/pyproject.toml +27 -1
  9. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/tests/test_app.py +118 -0
  10. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/tests/test_base.py +1 -0
  11. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/tests/test_integration.py +95 -0
  12. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus/errors.py +0 -0
  13. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus/py.typed +0 -0
  14. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus.egg-info/SOURCES.txt +0 -0
  15. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus.egg-info/dependency_links.txt +0 -0
  16. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus.egg-info/requires.txt +0 -0
  17. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/fastapi_errors_plus.egg-info/top_level.txt +0 -0
  18. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/setup.cfg +0 -0
  19. {fastapi_errors_plus-0.5.0 → fastapi_errors_plus-0.6.1}/tests/test_errors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-errors-plus
3
- Version: 0.5.0
3
+ Version: 0.6.1
4
4
  Summary: Universal library for documenting errors in FastAPI endpoints
5
5
  Author-email: Igor Selivanov <seligoroff@gmail.com>
6
6
  License: MIT
@@ -246,10 +246,10 @@ def delete_item(id: int):
246
246
  ```
247
247
 
248
248
  **Benefits:**
249
- - No need to write ErrorDTO classes from scratch
250
- - Correct implementation out of the box
251
- - Reusable across all endpoints
252
- - Supports inheritance for custom logic
249
+ - No need to write ErrorDTO classes from scratch
250
+ - Correct implementation out of the box
251
+ - Reusable across all endpoints
252
+ - Supports inheritance for custom logic
253
253
 
254
254
  ### 5. Mixed Usage
255
255
 
@@ -319,6 +319,8 @@ class ErrorDTO(Protocol):
319
319
 
320
320
  Any class implementing this protocol (through structural typing) can be used with `Errors()`.
321
321
 
322
+ **Best Practice:** For maximum clarity, consider making your domain exceptions implement the ErrorDTO protocol directly. See [Best Practice: Connecting Exceptions and ErrorDTO](#best-practice-connecting-exceptions-and-errordto) for details.
323
+
322
324
  ### When to Use Protocol vs BaseErrorDTO
323
325
 
324
326
  **Use Protocol (structural typing)** when:
@@ -333,6 +335,71 @@ Any class implementing this protocol (through structural typing) can be used wit
333
335
 
334
336
  Both approaches work together — you can mix them in the same `Errors()` call!
335
337
 
338
+ ## Best Practice: Connecting Exceptions and ErrorDTO
339
+
340
+ ### Problem
341
+
342
+ It's not always clear which exception corresponds to which ErrorDTO:
343
+
344
+ ```python
345
+ # Not clear which exception this documents
346
+ responses=Errors(notification_not_found_error)
347
+ ```
348
+
349
+ ### Solution: Domain Exception as ErrorDTO
350
+
351
+ **Recommended approach** — make your exceptions implement ErrorDTO protocol:
352
+
353
+ ```python
354
+ # domain/exceptions.py
355
+ from typing import Dict, Any
356
+
357
+ class DomainException(Exception):
358
+ """Base exception implementing ErrorDTO protocol."""
359
+ status_code: int
360
+ message: str
361
+
362
+ def to_example(self) -> Dict[str, Any]:
363
+ return {self.message: {"value": {"detail": self.message}}}
364
+
365
+ @classmethod
366
+ def for_openapi(cls):
367
+ """Returns instance for OpenAPI documentation."""
368
+ return cls()
369
+
370
+ class NotificationNotFoundError(DomainException):
371
+ status_code = 404
372
+ message = "Notification not found"
373
+
374
+ def __init__(self, notification_id: str = ""):
375
+ self.notification_id = notification_id
376
+ super().__init__(self.message)
377
+
378
+ @classmethod
379
+ def for_openapi(cls):
380
+ return cls(notification_id="example_id")
381
+
382
+ # In endpoint
383
+ @router.delete(
384
+ "/{notificationId}",
385
+ responses=Errors(
386
+ NotificationNotFoundError.for_openapi(), # Clear connection!
387
+ ),
388
+ )
389
+ async def delete_notification(notification_id: str):
390
+ if not notification:
391
+ raise NotificationNotFoundError(notification_id) # Same exception!
392
+ ```
393
+
394
+ **Benefits:**
395
+ - Exception and ErrorDTO are one class
396
+ - Clear connection visible in endpoint
397
+ - No duplication
398
+ - Type-safe
399
+ - Works with any project architecture
400
+
401
+ See [examples/domain_exceptions.py](examples/domain_exceptions.py) for complete example.
402
+
336
403
  ## Compatibility with Existing Projects
337
404
 
338
405
  If your project already has error DTOs (like `ApiErrorDTO`), they can work with `fastapi-errors-plus` if they implement the `ErrorDTO` protocol:
@@ -220,10 +220,10 @@ def delete_item(id: int):
220
220
  ```
221
221
 
222
222
  **Benefits:**
223
- - No need to write ErrorDTO classes from scratch
224
- - Correct implementation out of the box
225
- - Reusable across all endpoints
226
- - Supports inheritance for custom logic
223
+ - No need to write ErrorDTO classes from scratch
224
+ - Correct implementation out of the box
225
+ - Reusable across all endpoints
226
+ - Supports inheritance for custom logic
227
227
 
228
228
  ### 5. Mixed Usage
229
229
 
@@ -293,6 +293,8 @@ class ErrorDTO(Protocol):
293
293
 
294
294
  Any class implementing this protocol (through structural typing) can be used with `Errors()`.
295
295
 
296
+ **Best Practice:** For maximum clarity, consider making your domain exceptions implement the ErrorDTO protocol directly. See [Best Practice: Connecting Exceptions and ErrorDTO](#best-practice-connecting-exceptions-and-errordto) for details.
297
+
296
298
  ### When to Use Protocol vs BaseErrorDTO
297
299
 
298
300
  **Use Protocol (structural typing)** when:
@@ -307,6 +309,71 @@ Any class implementing this protocol (through structural typing) can be used wit
307
309
 
308
310
  Both approaches work together — you can mix them in the same `Errors()` call!
309
311
 
312
+ ## Best Practice: Connecting Exceptions and ErrorDTO
313
+
314
+ ### Problem
315
+
316
+ It's not always clear which exception corresponds to which ErrorDTO:
317
+
318
+ ```python
319
+ # Not clear which exception this documents
320
+ responses=Errors(notification_not_found_error)
321
+ ```
322
+
323
+ ### Solution: Domain Exception as ErrorDTO
324
+
325
+ **Recommended approach** — make your exceptions implement ErrorDTO protocol:
326
+
327
+ ```python
328
+ # domain/exceptions.py
329
+ from typing import Dict, Any
330
+
331
+ class DomainException(Exception):
332
+ """Base exception implementing ErrorDTO protocol."""
333
+ status_code: int
334
+ message: str
335
+
336
+ def to_example(self) -> Dict[str, Any]:
337
+ return {self.message: {"value": {"detail": self.message}}}
338
+
339
+ @classmethod
340
+ def for_openapi(cls):
341
+ """Returns instance for OpenAPI documentation."""
342
+ return cls()
343
+
344
+ class NotificationNotFoundError(DomainException):
345
+ status_code = 404
346
+ message = "Notification not found"
347
+
348
+ def __init__(self, notification_id: str = ""):
349
+ self.notification_id = notification_id
350
+ super().__init__(self.message)
351
+
352
+ @classmethod
353
+ def for_openapi(cls):
354
+ return cls(notification_id="example_id")
355
+
356
+ # In endpoint
357
+ @router.delete(
358
+ "/{notificationId}",
359
+ responses=Errors(
360
+ NotificationNotFoundError.for_openapi(), # Clear connection!
361
+ ),
362
+ )
363
+ async def delete_notification(notification_id: str):
364
+ if not notification:
365
+ raise NotificationNotFoundError(notification_id) # Same exception!
366
+ ```
367
+
368
+ **Benefits:**
369
+ - Exception and ErrorDTO are one class
370
+ - Clear connection visible in endpoint
371
+ - No duplication
372
+ - Type-safe
373
+ - Works with any project architecture
374
+
375
+ See [examples/domain_exceptions.py](examples/domain_exceptions.py) for complete example.
376
+
310
377
  ## Compatibility with Existing Projects
311
378
 
312
379
  If your project already has error DTOs (like `ApiErrorDTO`), they can work with `fastapi-errors-plus` if they implement the `ErrorDTO` protocol:
@@ -10,5 +10,5 @@ __all__ = [
10
10
  "BaseErrorDTO", # Base implementation (optional)
11
11
  "StandardErrorDTO", # Extended implementation (optional)
12
12
  ]
13
- __version__ = "0.5.0"
13
+ __version__ = "0.6.1"
14
14
 
@@ -121,3 +121,4 @@ class StandardErrorDTO(BaseErrorDTO):
121
121
  }
122
122
 
123
123
 
124
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-errors-plus
3
- Version: 0.5.0
3
+ Version: 0.6.1
4
4
  Summary: Universal library for documenting errors in FastAPI endpoints
5
5
  Author-email: Igor Selivanov <seligoroff@gmail.com>
6
6
  License: MIT
@@ -246,10 +246,10 @@ def delete_item(id: int):
246
246
  ```
247
247
 
248
248
  **Benefits:**
249
- - No need to write ErrorDTO classes from scratch
250
- - Correct implementation out of the box
251
- - Reusable across all endpoints
252
- - Supports inheritance for custom logic
249
+ - No need to write ErrorDTO classes from scratch
250
+ - Correct implementation out of the box
251
+ - Reusable across all endpoints
252
+ - Supports inheritance for custom logic
253
253
 
254
254
  ### 5. Mixed Usage
255
255
 
@@ -319,6 +319,8 @@ class ErrorDTO(Protocol):
319
319
 
320
320
  Any class implementing this protocol (through structural typing) can be used with `Errors()`.
321
321
 
322
+ **Best Practice:** For maximum clarity, consider making your domain exceptions implement the ErrorDTO protocol directly. See [Best Practice: Connecting Exceptions and ErrorDTO](#best-practice-connecting-exceptions-and-errordto) for details.
323
+
322
324
  ### When to Use Protocol vs BaseErrorDTO
323
325
 
324
326
  **Use Protocol (structural typing)** when:
@@ -333,6 +335,71 @@ Any class implementing this protocol (through structural typing) can be used wit
333
335
 
334
336
  Both approaches work together — you can mix them in the same `Errors()` call!
335
337
 
338
+ ## Best Practice: Connecting Exceptions and ErrorDTO
339
+
340
+ ### Problem
341
+
342
+ It's not always clear which exception corresponds to which ErrorDTO:
343
+
344
+ ```python
345
+ # Not clear which exception this documents
346
+ responses=Errors(notification_not_found_error)
347
+ ```
348
+
349
+ ### Solution: Domain Exception as ErrorDTO
350
+
351
+ **Recommended approach** — make your exceptions implement ErrorDTO protocol:
352
+
353
+ ```python
354
+ # domain/exceptions.py
355
+ from typing import Dict, Any
356
+
357
+ class DomainException(Exception):
358
+ """Base exception implementing ErrorDTO protocol."""
359
+ status_code: int
360
+ message: str
361
+
362
+ def to_example(self) -> Dict[str, Any]:
363
+ return {self.message: {"value": {"detail": self.message}}}
364
+
365
+ @classmethod
366
+ def for_openapi(cls):
367
+ """Returns instance for OpenAPI documentation."""
368
+ return cls()
369
+
370
+ class NotificationNotFoundError(DomainException):
371
+ status_code = 404
372
+ message = "Notification not found"
373
+
374
+ def __init__(self, notification_id: str = ""):
375
+ self.notification_id = notification_id
376
+ super().__init__(self.message)
377
+
378
+ @classmethod
379
+ def for_openapi(cls):
380
+ return cls(notification_id="example_id")
381
+
382
+ # In endpoint
383
+ @router.delete(
384
+ "/{notificationId}",
385
+ responses=Errors(
386
+ NotificationNotFoundError.for_openapi(), # Clear connection!
387
+ ),
388
+ )
389
+ async def delete_notification(notification_id: str):
390
+ if not notification:
391
+ raise NotificationNotFoundError(notification_id) # Same exception!
392
+ ```
393
+
394
+ **Benefits:**
395
+ - Exception and ErrorDTO are one class
396
+ - Clear connection visible in endpoint
397
+ - No duplication
398
+ - Type-safe
399
+ - Works with any project architecture
400
+
401
+ See [examples/domain_exceptions.py](examples/domain_exceptions.py) for complete example.
402
+
336
403
  ## Compatibility with Existing Projects
337
404
 
338
405
  If your project already has error DTOs (like `ApiErrorDTO`), they can work with `fastapi-errors-plus` if they implement the `ErrorDTO` protocol:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fastapi-errors-plus"
7
- version = "0.5.0"
7
+ version = "0.6.1"
8
8
  description = "Universal library for documenting errors in FastAPI endpoints"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -46,3 +46,29 @@ python_files = "test_*.py"
46
46
  python_classes = "Test*"
47
47
  python_functions = "test_*"
48
48
 
49
+ [tool.coverage.run]
50
+ source = ["fastapi_errors_plus"]
51
+ omit = [
52
+ "*/tests/*",
53
+ "*/__pycache__/*",
54
+ "*/venv/*",
55
+ "*/.venv/*",
56
+ ]
57
+
58
+ [tool.coverage.report]
59
+ exclude_lines = [
60
+ "pragma: no cover",
61
+ "def __repr__",
62
+ "raise AssertionError",
63
+ "raise NotImplementedError",
64
+ "if __name__ == .__main__.:",
65
+ "if TYPE_CHECKING:",
66
+ "@abstractmethod",
67
+ ]
68
+ precision = 2
69
+ show_missing = true
70
+ skip_covered = false
71
+
72
+ [tool.coverage.html]
73
+ directory = "tests/htmlcov"
74
+
@@ -1,5 +1,7 @@
1
1
  """Test FastAPI application for integration testing."""
2
2
 
3
+ from typing import Dict, Any
4
+
3
5
  from fastapi import APIRouter, FastAPI, status
4
6
  from fastapi_errors_plus import BaseErrorDTO, Errors, StandardErrorDTO
5
7
  from tests.conftest import SimpleErrorDTO
@@ -11,6 +13,57 @@ app = FastAPI(title="Test API for fastapi-errors-plus")
11
13
  router = APIRouter()
12
14
 
13
15
 
16
+ # Domain Exception pattern for testing (Best Practice example)
17
+ class DomainException(Exception):
18
+ """Base exception implementing ErrorDTO protocol."""
19
+ status_code: int
20
+ message: str
21
+
22
+ def to_example(self) -> Dict[str, Any]:
23
+ """Generate example for OpenAPI."""
24
+ return {
25
+ self.message: {
26
+ "value": {"detail": self.message},
27
+ },
28
+ }
29
+
30
+ @classmethod
31
+ def for_openapi(cls):
32
+ """Returns instance for OpenAPI documentation."""
33
+ return cls()
34
+
35
+
36
+ class TestItemNotFoundError(DomainException):
37
+ """Test item not found error."""
38
+ status_code = status.HTTP_404_NOT_FOUND
39
+ message = "Test item not found"
40
+
41
+ def __init__(self, item_id: str = ""):
42
+ self.item_id = item_id
43
+ super().__init__(self.message)
44
+
45
+ @classmethod
46
+ def for_openapi(cls):
47
+ """Returns instance for OpenAPI documentation."""
48
+ return cls(item_id="test_id")
49
+
50
+
51
+ class TestItemAccessDeniedError(DomainException):
52
+ """Test item access denied error."""
53
+ status_code = status.HTTP_403_FORBIDDEN
54
+ message = "Test item access denied"
55
+
56
+ def __init__(self, item_id: str = "", user_id: str = ""):
57
+ self.item_id = item_id
58
+ self.user_id = user_id
59
+ super().__init__(self.message)
60
+
61
+ @classmethod
62
+ def for_openapi(cls):
63
+ """Returns instance for OpenAPI documentation."""
64
+ return cls(item_id="test_id", user_id="test_user")
65
+
66
+
14
67
  # Example 1: Standard flags only
15
68
  @router.get(
16
69
  "/standard-flags",
@@ -220,6 +273,71 @@ def create_item_mixed_base_dto(item_id: int):
220
273
  return {"message": f"Item {item_id} created"}
221
274
 
222
275
 
276
+ # Domain Exception pattern for testing (Best Practice example)
277
+ class DomainException(Exception):
278
+ """Base exception implementing ErrorDTO protocol."""
279
+ status_code: int
280
+ message: str
281
+
282
+ def to_example(self) -> Dict[str, Any]:
283
+ """Generate example for OpenAPI."""
284
+ return {
285
+ self.message: {
286
+ "value": {"detail": self.message},
287
+ },
288
+ }
289
+
290
+ @classmethod
291
+ def for_openapi(cls):
292
+ """Returns instance for OpenAPI documentation."""
293
+ return cls()
294
+
295
+
296
+ class TestItemNotFoundError(DomainException):
297
+ """Test item not found error."""
298
+ status_code = status.HTTP_404_NOT_FOUND
299
+ message = "Test item not found"
300
+
301
+ def __init__(self, item_id: str = ""):
302
+ self.item_id = item_id
303
+ super().__init__(self.message)
304
+
305
+ @classmethod
306
+ def for_openapi(cls):
307
+ """Returns instance for OpenAPI documentation."""
308
+ return cls(item_id="test_id")
309
+
310
+
311
+ class TestItemAccessDeniedError(DomainException):
312
+ """Test item access denied error."""
313
+ status_code = status.HTTP_403_FORBIDDEN
314
+ message = "Test item access denied"
315
+
316
+ def __init__(self, item_id: str = "", user_id: str = ""):
317
+ self.item_id = item_id
318
+ self.user_id = user_id
319
+ super().__init__(self.message)
320
+
321
+ @classmethod
322
+ def for_openapi(cls):
323
+ """Returns instance for OpenAPI documentation."""
324
+ return cls(item_id="test_id", user_id="test_user")
325
+
326
+
327
+ # Example 11: Domain Exception as ErrorDTO (Best Practice pattern)
328
+ @router.delete(
329
+ "/domain-exception/{item_id}",
330
+ responses=Errors(
331
+ TestItemNotFoundError.for_openapi(), # Using for_openapi() pattern
332
+ TestItemAccessDeniedError.for_openapi(), # Using for_openapi() pattern
333
+ unauthorized_401=True, # Standard flag
334
+ ),
335
+ )
336
+ def delete_item_domain_exception(item_id: str):
337
+ """Endpoint demonstrating Domain Exception as ErrorDTO pattern."""
338
+ return {"message": f"Item {item_id} deleted"}
339
+
340
+
223
341
  # Register router
224
342
  app.include_router(router, prefix="/api/v1", tags=["test"])
225
343
 
@@ -303,3 +303,4 @@ class TestBaseErrorDTOCompatibility:
303
303
  assert status.HTTP_500_INTERNAL_SERVER_ERROR in responses
304
304
 
305
305
 
306
+
@@ -319,6 +319,93 @@ class TestMixedBaseDTOEndpoint:
319
319
  assert "500" in responses # From flag
320
320
 
321
321
 
322
+ class TestDomainExceptionEndpoint:
323
+ """Tests for Domain Exception as ErrorDTO endpoint (Best Practice pattern)."""
324
+
325
+ def test_responses_in_openapi(self):
326
+ """Test that Domain Exception responses are in OpenAPI."""
327
+ client = TestClient(app)
328
+ response = client.get("/openapi.json")
329
+ schema = response.json()
330
+
331
+ endpoint = schema["paths"]["/api/v1/domain-exception/{item_id}"]["delete"]
332
+ responses = endpoint["responses"]
333
+
334
+ assert "401" in responses # From flag
335
+ assert "403" in responses # From TestItemAccessDeniedError.for_openapi()
336
+ assert "404" in responses # From TestItemNotFoundError.for_openapi()
337
+
338
+ def test_404_response_structure_from_domain_exception(self):
339
+ """Test 404 response structure from Domain Exception."""
340
+ client = TestClient(app)
341
+ response = client.get("/openapi.json")
342
+ schema = response.json()
343
+
344
+ endpoint = schema["paths"]["/api/v1/domain-exception/{item_id}"]["delete"]
345
+ error_404 = endpoint["responses"]["404"]
346
+
347
+ assert error_404["description"] == "Test item not found"
348
+ assert "content" in error_404
349
+ assert "application/json" in error_404["content"]
350
+ assert "examples" in error_404["content"]["application/json"]
351
+ examples = error_404["content"]["application/json"]["examples"]
352
+ assert "Test item not found" in examples
353
+
354
+ def test_403_response_structure_from_domain_exception(self):
355
+ """Test 403 response structure from Domain Exception."""
356
+ client = TestClient(app)
357
+ response = client.get("/openapi.json")
358
+ schema = response.json()
359
+
360
+ endpoint = schema["paths"]["/api/v1/domain-exception/{item_id}"]["delete"]
361
+ error_403 = endpoint["responses"]["403"]
362
+
363
+ assert error_403["description"] == "Test item access denied"
364
+ assert "content" in error_403
365
+ assert "application/json" in error_403["content"]
366
+ assert "examples" in error_403["content"]["application/json"]
367
+ examples = error_403["content"]["application/json"]["examples"]
368
+ assert "Test item access denied" in examples
369
+
370
+ def test_for_openapi_method_returns_error_dto(self):
371
+ """Test that for_openapi() method returns instance implementing ErrorDTO."""
372
+ from tests.test_app import TestItemNotFoundError
373
+
374
+ error_instance = TestItemNotFoundError.for_openapi()
375
+
376
+ # Check that it implements ErrorDTO protocol
377
+ assert hasattr(error_instance, "status_code")
378
+ assert hasattr(error_instance, "message")
379
+ assert hasattr(error_instance, "to_example")
380
+ assert callable(error_instance.to_example)
381
+
382
+ # Check values
383
+ assert error_instance.status_code == 404
384
+ assert error_instance.message == "Test item not found"
385
+ assert error_instance.item_id == "test_id"
386
+
387
+ # Check to_example() works
388
+ example = error_instance.to_example()
389
+ assert isinstance(example, dict)
390
+ assert "Test item not found" in example
391
+
392
+ def test_domain_exception_works_with_errors_class(self):
393
+ """Test that Domain Exception instances work with Errors class."""
394
+ from fastapi_errors_plus import Errors
395
+ from tests.test_app import TestItemNotFoundError, TestItemAccessDeniedError
396
+
397
+ errors = Errors(
398
+ TestItemNotFoundError.for_openapi(),
399
+ TestItemAccessDeniedError.for_openapi(),
400
+ )
401
+ responses = errors
402
+
403
+ assert 404 in responses
404
+ assert 403 in responses
405
+ assert responses[404]["description"] == "Test item not found"
406
+ assert responses[403]["description"] == "Test item access denied"
407
+
408
+
322
409
  class TestRealEndpoints:
323
410
  """Tests for actual endpoint responses."""
324
411
 
@@ -361,5 +448,13 @@ class TestRealEndpoints:
361
448
 
362
449
  assert response.status_code == 200
363
450
  assert response.json() == {"message": "Item 1 deleted"}
451
+
452
+ def test_domain_exception_endpoint_works(self):
453
+ """Test that Domain Exception endpoint returns 200."""
454
+ client = TestClient(app)
455
+ response = client.delete("/api/v1/domain-exception/1")
456
+
457
+ assert response.status_code == 200
458
+ assert response.json() == {"message": "Item 1 deleted"}
364
459
 
365
460