Coverage for tests / test_validation_error_context.py: 100%

107 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-12 18:15 +0000

1from fastapi import FastAPI, Request, WebSocket 1abcd

2from fastapi.exceptions import ( 1abcd

3 RequestValidationError, 

4 ResponseValidationError, 

5 WebSocketRequestValidationError, 

6) 

7from fastapi.testclient import TestClient 1abcd

8from pydantic import BaseModel 1abcd

9 

10 

11class Item(BaseModel): 1abcd

12 id: int 1abcd

13 name: str 1abcd

14 

15 

16class ExceptionCapture: 1abcd

17 def __init__(self): 1abcd

18 self.exception = None 1abcd

19 

20 def capture(self, exc): 1abcd

21 self.exception = exc 1keflmnghopqijrs

22 return exc 1keflmnghopqijrs

23 

24 

25app = FastAPI() 1abcd

26sub_app = FastAPI() 1abcd

27captured_exception = ExceptionCapture() 1abcd

28 

29app.mount(path="/sub", app=sub_app) 1abcd

30 

31 

32@app.exception_handler(RequestValidationError) 1abcd

33@sub_app.exception_handler(RequestValidationError) 1abcd

34async def request_validation_handler(request: Request, exc: RequestValidationError): 1abcd

35 captured_exception.capture(exc) 1knq

36 raise exc 1knq

37 

38 

39@app.exception_handler(ResponseValidationError) 1abcd

40@sub_app.exception_handler(ResponseValidationError) 1abcd

41async def response_validation_handler(_: Request, exc: ResponseValidationError): 1abcd

42 captured_exception.capture(exc) 1efghij

43 raise exc 1efghij

44 

45 

46@app.exception_handler(WebSocketRequestValidationError) 1abcd

47@sub_app.exception_handler(WebSocketRequestValidationError) 1abcd

48async def websocket_validation_handler( 1abcd

49 websocket: WebSocket, exc: WebSocketRequestValidationError 

50): 

51 captured_exception.capture(exc) 1lmoprs

52 raise exc 1lmoprs

53 

54 

55@app.get("/users/{user_id}") 1abcd

56def get_user(user_id: int): 1abcd

57 return {"user_id": user_id} # pragma: no cover 

58 

59 

60@app.get("/items/", response_model=Item) 1abcd

61def get_item(): 1abcd

62 return {"name": "Widget"} 1egi

63 

64 

65@sub_app.get("/items/", response_model=Item) 1abcd

66def get_sub_item(): 1abcd

67 return {"name": "Widget"} # pragma: no cover 1fhj

68 

69 

70@app.websocket("/ws/{item_id}") 1abcd

71async def websocket_endpoint(websocket: WebSocket, item_id: int): 1abcd

72 await websocket.accept() # pragma: no cover 

73 await websocket.send_text(f"Item: {item_id}") # pragma: no cover 

74 await websocket.close() # pragma: no cover 

75 

76 

77@sub_app.websocket("/ws/{item_id}") 1abcd

78async def subapp_websocket_endpoint(websocket: WebSocket, item_id: int): 1abcd

79 await websocket.accept() # pragma: no cover 

80 await websocket.send_text(f"Item: {item_id}") # pragma: no cover 

81 await websocket.close() # pragma: no cover 

82 

83 

84client = TestClient(app) 1abcd

85 

86 

87def test_request_validation_error_includes_endpoint_context(): 1abcd

88 captured_exception.exception = None 1knq

89 try: 1knq

90 client.get("/users/invalid") 1knq

91 except Exception: 1knq

92 pass 1knq

93 

94 assert captured_exception.exception is not None 1knq

95 error_str = str(captured_exception.exception) 1knq

96 assert "get_user" in error_str 1knq

97 assert "/users/" in error_str 1knq

98 

99 

100def test_response_validation_error_includes_endpoint_context(): 1abcd

101 captured_exception.exception = None 1egi

102 try: 1egi

103 client.get("/items/") 1egi

104 except Exception: 1egi

105 pass 1egi

106 

107 assert captured_exception.exception is not None 1egi

108 error_str = str(captured_exception.exception) 1egi

109 assert "get_item" in error_str 1egi

110 assert "/items/" in error_str 1egi

111 

112 

113def test_websocket_validation_error_includes_endpoint_context(): 1abcd

114 captured_exception.exception = None 1mps

115 try: 1mps

116 with client.websocket_connect("/ws/invalid"): 1mps

117 pass # pragma: no cover 

118 except Exception: 1mps

119 pass 1mps

120 

121 assert captured_exception.exception is not None 1mps

122 error_str = str(captured_exception.exception) 1mps

123 assert "websocket_endpoint" in error_str 1mps

124 assert "/ws/" in error_str 1mps

125 

126 

127def test_subapp_request_validation_error_includes_endpoint_context(): 1abcd

128 captured_exception.exception = None 1fhj

129 try: 1fhj

130 client.get("/sub/items/") 1fhj

131 except Exception: 1fhj

132 pass 1fhj

133 

134 assert captured_exception.exception is not None 1fhj

135 error_str = str(captured_exception.exception) 1fhj

136 assert "get_sub_item" in error_str 1fhj

137 assert "/sub/items/" in error_str 1fhj

138 

139 

140def test_subapp_websocket_validation_error_includes_endpoint_context(): 1abcd

141 captured_exception.exception = None 1lor

142 try: 1lor

143 with client.websocket_connect("/sub/ws/invalid"): 1lor

144 pass # pragma: no cover 

145 except Exception: 1lor

146 pass 1lor

147 

148 assert captured_exception.exception is not None 1lor

149 error_str = str(captured_exception.exception) 1lor

150 assert "subapp_websocket_endpoint" in error_str 1lor

151 assert "/sub/ws/" in error_str 1lor

152 

153 

154def test_validation_error_with_only_path(): 1abcd

155 errors = [{"type": "missing", "loc": ("body", "name"), "msg": "Field required"}] 1wxy

156 exc = RequestValidationError(errors, endpoint_ctx={"path": "GET /api/test"}) 1wxy

157 error_str = str(exc) 1wxy

158 assert "Endpoint: GET /api/test" in error_str 1wxy

159 assert 'File "' not in error_str 1wxy

160 

161 

162def test_validation_error_with_no_context(): 1abcd

163 errors = [{"type": "missing", "loc": ("body", "name"), "msg": "Field required"}] 1tuv

164 exc = RequestValidationError(errors, endpoint_ctx={}) 1tuv

165 error_str = str(exc) 1tuv

166 assert "1 validation error:" in error_str 1tuv

167 assert "Endpoint" not in error_str 1tuv

168 assert 'File "' not in error_str 1tuv