@@ -245,6 +245,143 @@ public void normalRequest_commitsRpcServerResponse() throws Exception {
245245 assertArrayEquals (rpcResp , resp .getContentAsByteArray ());
246246 }
247247
248+ // --- Content-Type header: must be application/json-rpc (no charset suffix) ---
249+
250+ @ Test
251+ public void errorResponse_contentTypeIsApplicationJsonRpc () throws Exception {
252+ MockHttpServletResponse resp = doPost ("not valid json" );
253+ assertEquals ("application/json-rpc" , resp .getContentType ());
254+ }
255+
256+ @ Test
257+ public void batchResponse_contentTypeIsApplicationJsonRpc () throws Exception {
258+ byte [] singleResp = "{\" jsonrpc\" :\" 2.0\" ,\" result\" :\" ok\" ,\" id\" :1}"
259+ .getBytes (StandardCharsets .UTF_8 );
260+ doAnswer (inv -> {
261+ OutputStream out = inv .getArgument (1 );
262+ out .write (singleResp );
263+ return 0 ;
264+ }).when (mockRpcServer ).handleRequest (any (InputStream .class ), any (OutputStream .class ));
265+
266+ MockHttpServletResponse resp = doPost ("[{\" id\" :1}]" );
267+ assertEquals ("application/json-rpc" , resp .getContentType ());
268+ }
269+
270+ // --- Primitive root node → Invalid Request (-32600), id must be JSON null ---
271+
272+ @ Test
273+ public void primitiveRootNull_returnsInvalidRequestWithJsonNullId () throws Exception {
274+ MockHttpServletResponse resp = doPost ("null" );
275+ assertEquals (200 , resp .getStatus ());
276+ JsonNode body = MAPPER .readTree (resp .getContentAsString ());
277+ assertFalse (body .isArray ());
278+ assertEquals ("2.0" , body .get ("jsonrpc" ).asText ());
279+ assertEquals (-32600 , body .get ("error" ).get ("code" ).asInt ());
280+ assertTrue ("id must be JSON null, not the string \" null\" " , body .get ("id" ).isNull ());
281+ assertFalse ("id must not be a string" , body .get ("id" ).isTextual ());
282+ }
283+
284+ @ Test
285+ public void primitiveRootBoolean_returnsInvalidRequest () throws Exception {
286+ MockHttpServletResponse resp = doPost ("true" );
287+ assertEquals (200 , resp .getStatus ());
288+ assertEquals (-32600 , MAPPER .readTree (resp .getContentAsString ()).get ("error" ).get ("code" ).asInt ());
289+ }
290+
291+ @ Test
292+ public void primitiveRootNumber_returnsInvalidRequest () throws Exception {
293+ MockHttpServletResponse resp = doPost ("123" );
294+ assertEquals (200 , resp .getStatus ());
295+ assertEquals (-32600 , MAPPER .readTree (resp .getContentAsString ()).get ("error" ).get ("code" ).asInt ());
296+ }
297+
298+ @ Test
299+ public void primitiveRootString_returnsInvalidRequest () throws Exception {
300+ MockHttpServletResponse resp = doPost ("\" hello\" " );
301+ assertEquals (200 , resp .getStatus ());
302+ assertEquals (-32600 , MAPPER .readTree (resp .getContentAsString ()).get ("error" ).get ("code" ).asInt ());
303+ }
304+
305+ // --- Non-object element inside a batch → Invalid Request per element ---
306+
307+ @ Test
308+ public void batchWithNestedArray_returnsInvalidRequestArray () throws Exception {
309+ MockHttpServletResponse resp = doPost ("[[]]" );
310+ assertEquals (200 , resp .getStatus ());
311+ JsonNode body = MAPPER .readTree (resp .getContentAsString ());
312+ assertTrue ("response must be a JSON array" , body .isArray ());
313+ assertEquals (1 , body .size ());
314+ assertEquals (-32600 , body .get (0 ).get ("error" ).get ("code" ).asInt ());
315+ assertTrue ("id in batch error must be JSON null" , body .get (0 ).get ("id" ).isNull ());
316+ }
317+
318+ @ Test
319+ public void batchWithMixedObjectAndArray_objectProcessedArrayRejected () throws Exception {
320+ byte [] singleResp = "{\" jsonrpc\" :\" 2.0\" ,\" result\" :\" ok\" ,\" id\" :1}"
321+ .getBytes (StandardCharsets .UTF_8 );
322+ doAnswer (inv -> {
323+ OutputStream out = inv .getArgument (1 );
324+ out .write (singleResp );
325+ return 0 ;
326+ }).when (mockRpcServer ).handleRequest (any (InputStream .class ), any (OutputStream .class ));
327+
328+ MockHttpServletResponse resp = doPost ("[{\" id\" :1}, []]" );
329+ assertEquals (200 , resp .getStatus ());
330+ JsonNode body = MAPPER .readTree (resp .getContentAsString ());
331+ assertTrue ("response must be a JSON array" , body .isArray ());
332+ assertEquals (2 , body .size ());
333+ assertEquals ("ok" , body .get (0 ).get ("result" ).asText ());
334+ assertEquals (-32600 , body .get (1 ).get ("error" ).get ("code" ).asInt ());
335+ }
336+
337+ @ Test
338+ public void batchWithNumericAndStringElements_allGetInvalidRequest () throws Exception {
339+ MockHttpServletResponse resp = doPost ("[42, \" foo\" , true]" );
340+ assertEquals (200 , resp .getStatus ());
341+ JsonNode body = MAPPER .readTree (resp .getContentAsString ());
342+ assertTrue ("response must be a JSON array" , body .isArray ());
343+ assertEquals (3 , body .size ());
344+ for (int i = 0 ; i < 3 ; i ++) {
345+ assertEquals (-32600 , body .get (i ).get ("error" ).get ("code" ).asInt ());
346+ }
347+ }
348+
349+ // --- StreamReadConstraints: maxNestingDepth and maxTokenCount must be enforced ---
350+
351+ @ Test
352+ public void excessivelyNestedRequest_returnsParseError () throws Exception {
353+ int limit = CommonParameter .getInstance ().getMaxNestingDepth ();
354+ StringBuilder sb = new StringBuilder ();
355+ for (int i = 0 ; i <= limit ; i ++) {
356+ sb .append ('[' );
357+ }
358+ sb .append ('0' );
359+ for (int i = 0 ; i <= limit ; i ++) {
360+ sb .append (']' );
361+ }
362+
363+ MockHttpServletResponse resp = doPost (sb .toString ());
364+ assertEquals (200 , resp .getStatus ());
365+ assertEquals (-32700 , MAPPER .readTree (resp .getContentAsString ()).get ("error" ).get ("code" ).asInt ());
366+ }
367+
368+ @ Test
369+ public void tooManyTokens_returnsParseError () throws Exception {
370+ int limit = CommonParameter .getInstance ().getMaxTokenCount ();
371+ StringBuilder sb = new StringBuilder ("[" );
372+ for (int i = 0 ; i < limit ; i ++) {
373+ if (i > 0 ) {
374+ sb .append (',' );
375+ }
376+ sb .append ('0' );
377+ }
378+ sb .append (']' );
379+
380+ MockHttpServletResponse resp = doPost (sb .toString ());
381+ assertEquals (200 , resp .getStatus ());
382+ assertEquals (-32700 , MAPPER .readTree (resp .getContentAsString ()).get ("error" ).get ("code" ).asInt ());
383+ }
384+
248385 // --- helpers ---
249386
250387 private MockHttpServletResponse doPost (String body ) throws Exception {
0 commit comments