Skip to content

Conversation

@ericfitz
Copy link
Contributor

Summary

Fixes IllegalArgumentException: count is negative: -1 that occurs when CATS generates HTML reports for API responses containing malformed JSON with extra closing brackets.

Root Cause

The SimpleJsonFormatter.formatJsonString() method was calling String.repeat(indentLevel - 1) on line 57 without checking if indentLevel was 0, resulting in a negative repeat count.

This occurred when:

  • API responses contained malformed JSON (e.g., {"key":"value"}})
  • The formatter encountered more closing brackets than opening brackets
  • The indentLevel counter reached 0 before all closing brackets were processed

Changes

  • Fix: Added Math.max(0, indentLevel - 1) guard in SimpleJsonFormatter.java:57
  • Tests: Added 3 test cases for malformed JSON edge cases:
    • shouldHandleMalformedJsonWithExtraClosingBrackets() - tests {...}}
    • shouldHandleMalformedJsonWithExtraClosingSquareBrackets() - tests [...]]
    • shouldHandleNestedStructures() - ensures normal nested JSON still works

Test Results

[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0

All tests pass, including the 3 new edge case tests.

Impact

  • Before: CATS would crash with MustacheException when generating HTML reports for malformed JSON responses
  • After: CATS gracefully handles malformed JSON and continues generating reports

Files Changed

  • src/main/java/com/endava/cats/util/SimpleJsonFormatter.java - 1 line changed
  • src/test/java/com/endava/cats/util/SimpleJsonFormatterTest.java - 23 lines added

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

Fixed IllegalArgumentException when formatting JSON with extra closing brackets.
The formatter was calling String.repeat() with a negative value when indentLevel
reached 0 and tried to calculate (indentLevel - 1).

Changes:
- Added Math.max(0, indentLevel - 1) guard in SimpleJsonFormatter.java:57
- Added 3 test cases for malformed JSON edge cases
- All 8 tests now pass

Fixes error when generating HTML reports for responses with malformed JSON.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ericfitz
Copy link
Contributor Author

Example exception:

com.github.mustachejava.MustacheException: Failed to get value for requestJson @[test-case.mustache:151]
	at com.github.mustachejava.codes.ValueCode.execute(ValueCode.java:74)
	at com.github.mustachejava.codes.DefaultMustache.run(DefaultMustache.java:34)
	at com.github.mustachejava.codes.DefaultCode.run(DefaultCode.java:227)
	at com.github.mustachejava.codes.IterableCode.next(IterableCode.java:143)
	at com.github.mustachejava.reflect.AbstractObjectHandler.iterate(AbstractObjectHandler.java:96)
	at com.github.mustachejava.codes.IterableCode.execute(IterableCode.java:138)
	at com.github.mustachejava.codes.IterableCode.handle(IterableCode.java:54)
	at com.github.mustachejava.codes.IterableCode.execute(IterableCode.java:42)
	at com.github.mustachejava.codes.DefaultMustache.run(DefaultMustache.java:34)
	at com.github.mustachejava.codes.DefaultCode.run(DefaultCode.java:227)
	at com.github.mustachejava.codes.IterableCode.next(IterableCode.java:143)
	at com.github.mustachejava.reflect.AbstractObjectHandler.iterate(AbstractObjectHandler.java:96)
	at com.github.mustachejava.codes.IterableCode.execute(IterableCode.java:138)
	at com.github.mustachejava.codes.IterableCode.handle(IterableCode.java:54)
	at com.github.mustachejava.codes.IterableCode.execute(IterableCode.java:42)
	at com.github.mustachejava.codes.DefaultMustache.run(DefaultMustache.java:34)
	at com.github.mustachejava.codes.DefaultCode.run(DefaultCode.java:227)
	at com.github.mustachejava.codes.IterableCode.next(IterableCode.java:143)
	at com.github.mustachejava.reflect.AbstractObjectHandler.iterate(AbstractObjectHandler.java:96)
	at com.github.mustachejava.codes.IterableCode.execute(IterableCode.java:138)
	at com.github.mustachejava.codes.IterableCode.handle(IterableCode.java:54)
	at com.github.mustachejava.codes.IterableCode.execute(IterableCode.java:42)
	at com.github.mustachejava.codes.DefaultMustache.run(DefaultMustache.java:34)
	at com.github.mustachejava.codes.DefaultCode.execute(DefaultCode.java:175)
	at com.github.mustachejava.codes.DefaultMustache.execute(DefaultMustache.java:57)
	at com.github.mustachejava.Mustache.execute(Mustache.java:38)
	at com.endava.cats.report.TestCaseExporter.writeHtmlTestCase(TestCaseExporter.java:426)
	at com.endava.cats.report.TestCaseExporter.writeTestCase(TestCaseExporter.java:402)
	at com.endava.cats.report.TestCaseExporterHtmlJs_Subclass.writeTestCase$$superforward(Unknown Source)
	at com.endava.cats.report.TestCaseExporterHtmlJs_Subclass$$function$$1.apply(Unknown Source)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:73)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:62)
	at com.endava.cats.aop.DryRunAspect.intercept(DryRunAspect.java:133)
	at com.endava.cats.aop.DryRunAspect_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:42)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:30)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:27)
	at com.endava.cats.report.TestCaseExporterHtmlJs_Subclass.writeTestCase(Unknown Source)
	at com.endava.cats.report.TestReportsGenerator.writeTestCase(TestReportsGenerator.java:52)
	at com.endava.cats.report.TestCaseListener.endTestCase(TestCaseListener.java:314)
	at com.endava.cats.report.TestCaseListener.createAndExecuteTest(TestCaseListener.java:171)
	at com.endava.cats.report.TestCaseListener_Subclass.createAndExecuteTest$$superforward(Unknown Source)
	at com.endava.cats.report.TestCaseListener_Subclass$$function$$19.apply(Unknown Source)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:73)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:62)
	at com.endava.cats.aop.DryRunAspect.intercept(DryRunAspect.java:133)
	at com.endava.cats.aop.DryRunAspect_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:42)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:30)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:27)
	at com.endava.cats.report.TestCaseListener_Subclass.createAndExecuteTest(Unknown Source)
	at com.endava.cats.report.TestCaseListener_ClientProxy.createAndExecuteTest(Unknown Source)
	at com.endava.cats.fuzzer.executor.SimpleExecutor.execute(SimpleExecutor.java:55)
	at com.endava.cats.fuzzer.executor.SimpleExecutor_ClientProxy.execute(Unknown Source)
	at com.endava.cats.fuzzer.http.InsertRandomValuesInBodyFuzzer.fuzz(InsertRandomValuesInBodyFuzzer.java:47)
	at com.endava.cats.command.CatsCommand.runSingleFuzzer(CatsCommand.java:449)
	at com.endava.cats.command.CatsCommand.lambda$runFuzzers$0(CatsCommand.java:438)
	at java.base@25.0.1/java.util.ArrayList.forEach(ArrayList.java:1604)
	at com.endava.cats.command.CatsCommand.runFuzzers(CatsCommand.java:438)
	at com.endava.cats.command.CatsCommand.fuzzPath(CatsCommand.java:429)
	at com.endava.cats.command.CatsCommand.startFuzzing(CatsCommand.java:330)
	at com.endava.cats.command.CatsCommand.doLogic(CatsCommand.java:263)
	at com.endava.cats.command.CatsCommand.run(CatsCommand.java:209)
	at picocli.CommandLine.executeUserObject(CommandLine.java:2045)
	at picocli.CommandLine.access$1500(CommandLine.java:148)
	at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2469)
	at picocli.CommandLine$RunLast.handle(CommandLine.java:2461)
	at picocli.CommandLine$RunLast.handle(CommandLine.java:2423)
	at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2277)
	at picocli.CommandLine$RunLast.execute(CommandLine.java:2425)
	at picocli.CommandLine.execute(CommandLine.java:2174)
	at com.endava.cats.CatsMain.run(CatsMain.java:48)
	at com.endava.cats.CatsMain_ClientProxy.run(Unknown Source)
	at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:141)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:80)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:51)
	at io.quarkus.runner.GeneratedMain.main(Unknown Source)
	at java.base@25.0.1/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: com.github.mustachejava.MustacheException: Failed: public java.lang.String com.endava.cats.model.CatsTestCase.getRequestJson() @[test-case.mustache:151]
	at com.github.mustachejava.reflect.GuardedBinding.get(GuardedBinding.java:80)
	at com.github.mustachejava.codes.DefaultCode.get(DefaultCode.java:158)
	at com.github.mustachejava.codes.ValueCode.execute(ValueCode.java:62)
	... 77 more
Caused by: com.github.mustachejava.MustacheException: Error invoking method com.endava.cats.model.CatsTestCase.getRequestJson() on com.endava.cats.model.CatsTestCase@445338686
	at com.github.mustachejava.reflect.ReflectionWrapper.call(ReflectionWrapper.java:68)
	at com.github.mustachejava.reflect.GuardedBinding.get(GuardedBinding.java:76)
	... 79 more
Caused by: java.lang.IllegalArgumentException: count is negative: -1
	at java.base@25.0.1/java.lang.String.repeat(String.java:4733)
	at com.endava.cats.util.SimpleJsonFormatter.formatJsonString(SimpleJsonFormatter.java:57)
	at com.endava.cats.util.SimpleJsonFormatter.formatJson(SimpleJsonFormatter.java:95)
	at com.endava.cats.model.CatsTestCase.getRequestJson(CatsTestCase.java:144)
	at java.base@25.0.1/java.lang.reflect.Method.invoke(Method.java:565)
	at com.github.mustachejava.reflect.ReflectionWrapper.call(ReflectionWrapper.java:62)
	... 80 more

@en-milie en-milie merged commit df9046b into Endava:master Dec 4, 2025
@ericfitz ericfitz deleted the fix/json-formatter-negative-indent branch December 5, 2025 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants