Coverage for test\test_exception_handlers.py: 99%

116 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-21 20:31 +0100

1import pytest 

2from nexios import get_application, NexiosApp 

3from nexios.exceptions import HTTPException, NotFoundException 

4from nexios.http import Request, Response 

5from nexios.testing import Client 

6from typing import Tuple 

7 

8 

9@pytest.fixture 

10async def async_client(): 

11 app = get_application() # Fresh app instance for each test 

12 async with Client(app, log_requests=True) as c: 

13 yield c, app 

14 

15 

16async def test_default_404_handler(async_client: Tuple[Client, NexiosApp]): 

17 client, app = async_client 

18 

19 @app.get("/existing-route") 

20 async def existing_route(req: Request, res: Response): 

21 return res.text("OK") 

22 

23 # Test non-existent route 

24 response = await client.get("/non-existent-route") 

25 assert response.status_code == 404 

26 assert "Not Found" in response.text 

27 

28 

29async def test_custom_404_handler(async_client: Tuple[Client, NexiosApp]): 

30 client, app = async_client 

31 

32 async def custom_404_handler(req: Request, res: Response, exc: NotFoundException): 

33 return res.json({"error": "Custom not found"}, status_code=404) 

34 

35 app.add_exception_handler(NotFoundException, custom_404_handler) 

36 

37 response = await client.get("/non-existent-route") 

38 assert response.status_code == 404 

39 assert response.json() == {"error": "Custom not found"} 

40 

41 

42async def test_http_exception_handling(async_client: Tuple[Client, NexiosApp]): 

43 client, app = async_client 

44 

45 @app.get("/test-http-exception") 

46 async def test_route(req: Request, res: Response): 

47 raise HTTPException(status_code=403, detail="Access denied") 

48 

49 response = await client.get("/test-http-exception") 

50 assert response.status_code == 403 

51 assert response.json() == "Access denied" 

52 

53 

54async def test_custom_exception_handler(async_client: Tuple[Client, NexiosApp]): 

55 client, app = async_client 

56 

57 class CustomException(Exception): 

58 pass 

59 

60 @app.get("/test-custom-exception") 

61 async def test_route(req: Request, res: Response): 

62 raise CustomException("Something went wrong") 

63 

64 async def handle_custom_exception( 

65 req: Request, res: Response, exc: CustomException 

66 ): 

67 return res.json({"error": str(exc)}, status_code=400) 

68 

69 app.add_exception_handler(CustomException, handle_custom_exception) 

70 

71 response = await client.get("/test-custom-exception") 

72 assert response.status_code == 400 

73 assert response.json() == {"error": "Something went wrong"} 

74 

75 

76async def test_status_code_exception_handler(async_client: Tuple[Client, NexiosApp]): 

77 client, app = async_client 

78 

79 @app.get("/test-status-code") 

80 async def test_route(req: Request, res: Response): 

81 raise HTTPException(status_code=418, detail="I'm a teapot") 

82 

83 async def handle_teapot(req: Request, res: Response, exc: HTTPException): 

84 return res.json({"message": "This is a teapot"}, status_code=418) 

85 

86 app.add_exception_handler(418, handle_teapot) 

87 

88 response = await client.get("/test-status-code") 

89 assert response.status_code == 418 

90 assert response.json() == {"message": "This is a teapot"} 

91 

92 

93async def test_exception_handler_ordering(async_client: Tuple[Client, NexiosApp]): 

94 client, app = async_client 

95 

96 class SpecificException(HTTPException): 

97 pass 

98 

99 class GeneralException(HTTPException): 

100 pass 

101 

102 @app.get("/test-specific") 

103 async def test_specific(req: Request, res: Response): 

104 raise SpecificException(status_code=400, detail="Specific error") 

105 

106 @app.get("/test-general") 

107 async def test_general(req: Request, res: Response): 

108 raise GeneralException(status_code=400, detail="General error") 

109 

110 # Register general handler first 

111 async def general_handler(req: Request, res: Response, exc: HTTPException): 

112 return res.text("General handler") 

113 

114 # Register specific handler second 

115 async def specific_handler(req: Request, res: Response, exc: SpecificException): 

116 return res.text("Specific handler") 

117 

118 app.add_exception_handler(HTTPException, general_handler) 

119 app.add_exception_handler(SpecificException, specific_handler) 

120 

121 # Specific exception should use specific handler 

122 response = await client.get("/test-specific") 

123 assert response.text == "Specific handler" 

124 

125 # General exception should use general handler 

126 response = await client.get("/test-general") 

127 assert response.text == "General handler" 

128 

129 

130async def test_exception_with_headers(async_client: Tuple[Client, NexiosApp]): 

131 client, app = async_client 

132 

133 @app.get("/test-headers") 

134 async def test_route(req: Request, res: Response): 

135 raise HTTPException( 

136 status_code=403, detail="Forbidden", headers={"X-Custom-Header": "value"} 

137 ) 

138 

139 response = await client.get("/test-headers") 

140 assert response.status_code == 403 

141 assert response.headers["x-custom-header"] == "value" 

142 

143 

144async def test_middleware_exception_handling(async_client: Tuple[Client, NexiosApp]): 

145 client, app = async_client 

146 

147 async def error_middleware(req: Request, res: Response, call_next): 

148 try: 

149 return await call_next() 

150 except Exception as exc: 

151 return res.json({"middleware_error": str(exc)}, status_code=500) 

152 

153 app.add_middleware(error_middleware) 

154 

155 @app.get("/test-middleware-error") 

156 async def test_route(req: Request, res: Response): 

157 raise ValueError("Error in route") 

158 

159 response = await client.get("/test-middleware-error") 

160 assert response.status_code == 500 

161 assert response.json() == {"middleware_error": "Error in route"} 

162 

163 

164async def test_combined_exception_handling(async_client: Tuple[Client, NexiosApp]): 

165 client, app = async_client 

166 

167 class CustomError(Exception): 

168 pass 

169 

170 async def custom_handler(req: Request, res: Response, exc: CustomError): 

171 return res.json({"custom": True}, status_code=400) 

172 

173 app.add_exception_handler(CustomError, custom_handler) 

174 

175 @app.get("/test-combined") 

176 async def test_route(req: Request, res: Response): 

177 raise CustomError("Combined test") 

178 

179 response = await client.get("/test-combined") 

180 assert response.status_code == 400 

181 assert response.json() == {"custom": True}