Coverage for test\test_exception_handlers.py: 99%

1 import pytest

2 from nexios import get_application, NexiosApp

3 from nexios.exceptions import HTTPException, NotFoundException

4 from nexios.http import Request, Response

5 from nexios.testing import Client

6 from typing import Tuple

7

8

9@pytest.fixture

10 async 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

16 async 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

29 async 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

42 async 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

54 async 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

76 async 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

93 async 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

130 async 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

144 async 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

164 async 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}