Coverage for tests/test_custom_middleware_exception.py: 100%

51 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-12-04 08:29 +0000

1from pathlib import Path 1opqrstu

2from typing import Optional 1opqrstu

3 

4from fastapi import APIRouter, FastAPI, File, UploadFile 1opqrstu

5from fastapi.exceptions import HTTPException 1opqrstu

6from fastapi.testclient import TestClient 1opqrstu

7 

8app = FastAPI() 1opqrstu

9 

10router = APIRouter() 1opqrstu

11 

12 

13class ContentSizeLimitMiddleware: 1opqrstu

14 """Content size limiting middleware for ASGI applications 

15 Args: 

16 app (ASGI application): ASGI application 

17 max_content_size (optional): the maximum content size allowed in bytes, None for no limit 

18 """ 

19 

20 def __init__(self, app: APIRouter, max_content_size: Optional[int] = None): 1opqrstu

21 self.app = app 1abcdefg

22 self.max_content_size = max_content_size 1abcdefg

23 

24 def receive_wrapper(self, receive): 1opqrstu

25 received = 0 1ahbicjdkelfmgn

26 

27 async def inner(): 1ahbicjdkelfmgn

28 nonlocal received 

29 message = await receive() 1ahbicjdkelfmgn

30 if message["type"] != "http.request": 1ahbicjdkelfmgn

31 return message # pragma: no cover 

32 

33 body_len = len(message.get("body", b"")) 1ahbicjdkelfmgn

34 received += body_len 1ahbicjdkelfmgn

35 if received > self.max_content_size: 1ahbicjdkelfmgn

36 raise HTTPException( 1abcdefg

37 422, 

38 detail={ 

39 "name": "ContentSizeLimitExceeded", 

40 "code": 999, 

41 "message": "File limit exceeded", 

42 }, 

43 ) 

44 return message 1hijklmn

45 

46 return inner 1ahbicjdkelfmgn

47 

48 async def __call__(self, scope, receive, send): 1opqrstu

49 if scope["type"] != "http" or self.max_content_size is None: 1ahbicjdkelfmgn

50 await self.app(scope, receive, send) 1ahbicjdkelfmgn

51 return 1ahbicjdkelfmgn

52 

53 wrapper = self.receive_wrapper(receive) 1ahbicjdkelfmgn

54 await self.app(scope, wrapper, send) 1ahbicjdkelfmgn

55 

56 

57@router.post("/middleware") 1opqrstu

58def run_middleware(file: UploadFile = File(..., description="Big File")): 1opqrstu

59 return {"message": "OK"} 1hijklmn

60 

61 

62app.include_router(router) 1opqrstu

63app.add_middleware(ContentSizeLimitMiddleware, max_content_size=2**8) 1opqrstu

64 

65 

66client = TestClient(app) 1opqrstu

67 

68 

69def test_custom_middleware_exception(tmp_path: Path): 1opqrstu

70 default_pydantic_max_size = 2**16 1abcdefg

71 path = tmp_path / "test.txt" 1abcdefg

72 path.write_bytes(b"x" * (default_pydantic_max_size + 1)) 1abcdefg

73 

74 with client: 1abcdefg

75 with open(path, "rb") as file: 1abcdefg

76 response = client.post("/middleware", files={"file": file}) 1abcdefg

77 assert response.status_code == 422, response.text 1abcdefg

78 assert response.json() == { 1abcdefg

79 "detail": { 

80 "name": "ContentSizeLimitExceeded", 

81 "code": 999, 

82 "message": "File limit exceeded", 

83 } 

84 } 

85 

86 

87def test_custom_middleware_exception_not_raised(tmp_path: Path): 1opqrstu

88 path = tmp_path / "test.txt" 1hijklmn

89 path.write_bytes(b"<file content>") 1hijklmn

90 

91 with client: 1hijklmn

92 with open(path, "rb") as file: 1hijklmn

93 response = client.post("/middleware", files={"file": file}) 1hijklmn

94 assert response.status_code == 200, response.text 1hijklmn

95 assert response.json() == {"message": "OK"} 1hijklmn