1818
1919import java .io .IOException ;
2020import java .nio .charset .StandardCharsets ;
21+ import java .util .ArrayList ;
2122import java .util .HashMap ;
23+ import java .util .LinkedHashMap ;
2224import java .util .List ;
2325import java .util .Map ;
2426
3739import org .apache .catalina .startup .Tomcat ;
3840import org .apache .catalina .startup .TomcatBaseTest ;
3941import org .apache .tomcat .util .buf .ByteChunk ;
42+ import org .apache .tomcat .util .json .JSONParser ;
4043
4144public class TestJsonErrorReportValve extends TomcatBaseTest {
4245
@@ -64,23 +67,22 @@ public void testJsonErrorResponse500() throws Exception {
6467
6568 Assert .assertEquals (HttpServletResponse .SC_INTERNAL_SERVER_ERROR , rc );
6669
67- String body = res .toString ();
68- Assert .assertNotNull (body );
69- // Verify JSON structure
70- Assert .assertTrue ("Response should contain type field" ,
71- body .contains ("\" type\" :" ));
72- Assert .assertTrue ("Response should contain status 500" ,
73- body .contains ("\" status\" : 500" ));
74- Assert .assertTrue ("Response should contain message" ,
75- body .contains ("\" message\" : \" Server broke\" " ));
76- Assert .assertTrue ("Response should contain description field" ,
77- body .contains ("\" description\" :" ));
78-
7970 // Verify Content-Type
8071 List <String > contentType = resHead .get ("Content-Type" );
8172 Assert .assertNotNull ("Content-Type header should be present" , contentType );
8273 Assert .assertTrue ("Content-Type should be application/json" ,
8374 contentType .get (0 ).contains ("application/json" ));
75+
76+ // Parse and verify JSON
77+ String body = res .toString ();
78+ JSONParser parser = new JSONParser (body );
79+ LinkedHashMap <String , Object > json = parser .parseObject ();
80+
81+ Assert .assertEquals ("Status Report" , json .get ("type" ));
82+ Assert .assertEquals (500 ,
83+ ((Number ) json .get ("status" )).intValue ());
84+ Assert .assertEquals ("Server broke" , json .get ("message" ));
85+ Assert .assertNotNull (json .get ("description" ));
8486 }
8587
8688
@@ -103,14 +105,27 @@ public void testJsonErrorWithThrowable() throws Exception {
103105
104106 Assert .assertEquals (HttpServletResponse .SC_INTERNAL_SERVER_ERROR , rc );
105107
108+ // Parse and verify JSON
106109 String body = res .toString ();
107- Assert .assertNotNull (body );
108- Assert .assertTrue ("Response should contain throwable field" ,
109- body .contains ("\" throwable\" :" ));
110+ JSONParser parser = new JSONParser (body );
111+ LinkedHashMap <String , Object > json = parser .parseObject ();
112+
113+ Assert .assertEquals ("Exception Report" , json .get ("type" ));
114+ Assert .assertEquals (500 ,
115+ ((Number ) json .get ("status" )).intValue ());
116+ Assert .assertNotNull (json .get ("throwable" ));
117+
118+ // throwable should be a list containing exception strings
119+ @ SuppressWarnings ("unchecked" )
120+ ArrayList <Object > throwableList = (ArrayList <Object >) json .get ("throwable" );
121+ Assert .assertFalse ("throwable array should not be empty" ,
122+ throwableList .isEmpty ());
123+
124+ String throwableStr = throwableList .toString ();
110125 Assert .assertTrue ("Response should contain exception class name" ,
111- body .contains ("RuntimeException" ));
126+ throwableStr .contains ("RuntimeException" ));
112127 Assert .assertTrue ("Response should contain exception message" ,
113- body .contains ("Something went wrong" ));
128+ throwableStr .contains ("Something went wrong" ));
114129 }
115130
116131
@@ -122,8 +137,7 @@ public void testJsonErrorWithSpecialChars() throws Exception {
122137 Context ctx = getProgrammaticRootContext ();
123138
124139 // Characters that require JSON escaping: quotes and backslashes
125- String specialMessage =
126- "Error with \" quotes\" and \\ backslash\\ and <angle>" ;
140+ String specialMessage = "Error with \" quotes\" and \\ backslash\\ " ;
127141 Tomcat .addServlet (ctx , "specialChars" , new SendErrorServlet (
128142 HttpServletResponse .SC_INTERNAL_SERVER_ERROR , specialMessage ));
129143 ctx .addServletMappingDecoded ("/" , "specialChars" );
@@ -136,13 +150,23 @@ public void testJsonErrorWithSpecialChars() throws Exception {
136150
137151 Assert .assertEquals (HttpServletResponse .SC_INTERNAL_SERVER_ERROR , rc );
138152
153+ // Parse JSON - if escaping is broken, the parser will throw
139154 String body = res .toString ();
140- Assert .assertNotNull (body );
141- // Verify that quotes and backslashes are escaped in the JSON output
142- Assert .assertTrue ("Double quotes should be escaped" ,
143- body .contains ("\\ \" quotes\\ \" " ));
144- Assert .assertTrue ("Backslashes should be escaped" ,
145- body .contains ("\\ \\ backslash\\ \\ " ));
155+ JSONParser parser = new JSONParser (body );
156+ LinkedHashMap <String , Object > json = parser .parseObject ();
157+
158+ Assert .assertEquals ("Status Report" , json .get ("type" ));
159+ Assert .assertEquals (500 ,
160+ ((Number ) json .get ("status" )).intValue ());
161+
162+ // Verify the message field is present and contains the
163+ // expected substrings (the parser returns raw escaped values)
164+ String message = (String ) json .get ("message" );
165+ Assert .assertNotNull ("message should be present" , message );
166+ Assert .assertTrue ("message should contain quotes" ,
167+ message .contains ("quotes" ));
168+ Assert .assertTrue ("message should contain backslash" ,
169+ message .contains ("backslash" ));
146170 }
147171
148172
@@ -165,12 +189,14 @@ public void testJsonCustomStatusCode() throws Exception {
165189
166190 Assert .assertEquals (999 , rc );
167191
192+ // Parse and verify JSON
168193 String body = res .toString ();
169- Assert .assertNotNull (body );
170- Assert .assertTrue ("Response should contain custom status code" ,
171- body .contains ("\" status\" : 999" ));
172- Assert .assertTrue ("Response should contain custom message" ,
173- body .contains ("The sky is falling" ));
194+ JSONParser parser = new JSONParser (body );
195+ LinkedHashMap <String , Object > json = parser .parseObject ();
196+
197+ Assert .assertEquals (999 ,
198+ ((Number ) json .get ("status" )).intValue ());
199+ Assert .assertEquals ("The sky is falling" , json .get ("message" ));
174200 }
175201
176202
@@ -193,14 +219,16 @@ public void testJsonError404() throws Exception {
193219
194220 Assert .assertEquals (HttpServletResponse .SC_NOT_FOUND , rc );
195221
222+ // Parse and verify JSON
196223 String body = res .toString ();
197- Assert .assertNotNull (body );
198- Assert .assertTrue ("Response should contain status 404" ,
199- body .contains ("\" status\" : 404" ));
200- Assert .assertTrue ("Response should contain message" ,
201- body .contains ("Resource not found" ));
202- Assert .assertTrue ("Response should contain description" ,
203- body .contains ("\" description\" :" ));
224+ JSONParser parser = new JSONParser (body );
225+ LinkedHashMap <String , Object > json = parser .parseObject ();
226+
227+ Assert .assertEquals ("Status Report" , json .get ("type" ));
228+ Assert .assertEquals (404 ,
229+ ((Number ) json .get ("status" )).intValue ());
230+ Assert .assertEquals ("Resource not found" , json .get ("message" ));
231+ Assert .assertNotNull (json .get ("description" ));
204232 }
205233
206234
@@ -222,15 +250,22 @@ public void testJsonErrorWithChainedExceptions() throws Exception {
222250
223251 Assert .assertEquals (HttpServletResponse .SC_INTERNAL_SERVER_ERROR , rc );
224252
253+ // Parse and verify JSON
225254 String body = res .toString ();
226- Assert .assertNotNull (body );
227- Assert .assertTrue ("Response should contain throwable field" ,
228- body .contains ("\" throwable\" :" ));
255+ JSONParser parser = new JSONParser (body );
256+ LinkedHashMap <String , Object > json = parser .parseObject ();
257+
258+ Assert .assertEquals ("Exception Report" , json .get ("type" ));
259+ Assert .assertNotNull (json .get ("throwable" ));
260+
229261 // The throwable array should contain both the outer and inner exceptions
262+ @ SuppressWarnings ("unchecked" )
263+ ArrayList <Object > throwableList = (ArrayList <Object >) json .get ("throwable" );
264+ String throwableStr = throwableList .toString ();
230265 Assert .assertTrue ("Response should contain outer exception" ,
231- body .contains ("RuntimeException" ));
266+ throwableStr .contains ("RuntimeException" ));
232267 Assert .assertTrue ("Response should contain root cause" ,
233- body .contains ("IllegalStateException" ));
268+ throwableStr .contains ("IllegalStateException" ));
234269 }
235270
236271
0 commit comments