Skip to content

Commit daeed6b

Browse files
committed
feat: Add new linter to check to check relevant response codes have response bodies
1 parent 1f936a9 commit daeed6b

File tree

6 files changed

+133
-4
lines changed

6 files changed

+133
-4
lines changed

src/main/java/com/endava/cats/args/ProcessingArguments.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public class ProcessingArguments {
7777
description = "Generate anyOf/oneOf combinations also for response schemas. By default it creates one response payload with all possibilities. Default: @|bold,underline ${DEFAULT-VALUE}|@")
7878
private boolean generateAllXxxCombinationsForResponses;
7979

80+
@Setter
8081
@CommandLine.Option(names = {"--filterXxxFromRequestPayloads"}, negatable = true, defaultValue = "true", fallbackValue = "true",
8182
description = "In extremely rare cases when CATS fails to generate anyOf/oneOf combinations some requests may still contain ONE_OF/ANY_OF markers. They are filtered out by default. " +
8283
"Setting this to false will send them as requests which will probably fail. It's mostly for debug purposes. Default: @|bold,underline ${DEFAULT-VALUE}|@")

src/main/java/com/endava/cats/command/LintCommand.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public void run() {
6464
catsCommand.filterArguments.customFilter("Linter");
6565
catsCommand.filterArguments.getSkipFuzzers().addAll(Optional.ofNullable(skipFuzzers).orElse(Collections.emptyList()));
6666
catsCommand.filterArguments.getCheckArguments().setIncludeContract(true);
67+
catsCommand.processingArguments.setFilterXxxFromRequestPayloads(false);
6768
catsCommand.run();
6869
}
6970

src/main/java/com/endava/cats/fuzzer/contract/RecommendedHttpCodesLinterFuzzer.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,4 @@ protected String runKey(FuzzingData data) {
5656
public String description() {
5757
return "verifies that the current path contains all recommended HTTP response codes for all operations";
5858
}
59-
6059
}
61-
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.endava.cats.fuzzer.contract;
2+
3+
import com.endava.cats.annotations.LinterFuzzer;
4+
import com.endava.cats.http.HttpMethod;
5+
import com.endava.cats.model.FuzzingData;
6+
import com.endava.cats.report.TestCaseListener;
7+
import io.github.ludovicianul.prettylogger.PrettyLogger;
8+
import io.github.ludovicianul.prettylogger.PrettyLoggerFactory;
9+
import jakarta.inject.Singleton;
10+
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
/**
15+
* Checks if the http response codes have a response body.
16+
*/
17+
@Singleton
18+
@LinterFuzzer
19+
public class ResponsesWithBodiesLinterFuzzer extends BaseLinterFuzzer {
20+
private final PrettyLogger log = PrettyLoggerFactory.getLogger(this.getClass());
21+
22+
/**
23+
* Creates a new ResponsesWithBodiesLinterFuzzer instance.
24+
*
25+
* @param tcl the test case listener
26+
*/
27+
public ResponsesWithBodiesLinterFuzzer(TestCaseListener tcl) {
28+
super(tcl);
29+
}
30+
31+
@Override
32+
public void process(FuzzingData data) {
33+
testCaseListener.addScenario(log, "Check if all http response codes for HTTP method {} have a response body", data.getMethod());
34+
testCaseListener.addExpectedResult(log, "All HTTP response codes (except for 204 and 304) have a response body");
35+
36+
List<String> httpResponseCodesMissingBody = data.getResponses()
37+
.entrySet()
38+
.stream()
39+
.filter(entry -> entry.getValue().isEmpty())
40+
.map(Map.Entry::getKey)
41+
.toList();
42+
43+
if (httpResponseCodesMissingBody.isEmpty()) {
44+
testCaseListener.reportResultInfo(log, data, "All HTTP response codes have a response body");
45+
} else {
46+
testCaseListener.reportResultError(log, data, "Missing response body for some HTTP response codes", "The following HTTP response codes are missing a response body: {}", httpResponseCodesMissingBody);
47+
}
48+
}
49+
50+
@Override
51+
public List<HttpMethod> skipForHttpMethods() {
52+
return List.of(HttpMethod.HEAD);
53+
}
54+
55+
@Override
56+
protected String runKey(FuzzingData data) {
57+
return data.getPath() + data.getMethod();
58+
}
59+
60+
@Override
61+
public String description() {
62+
return "verifies that HTTP response codes (except for 204 and 304) have a response body";
63+
}
64+
}

src/test/java/com/endava/cats/args/FilterArgumentsTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ void shouldIncludeAllFuzzers() {
7777
List<String> fuzzers = filterArguments.getFirstPhaseFuzzersForPath();
7878

7979
Assertions.assertThat(fuzzers).contains("LeadingControlCharsInHeadersFuzzer", "LeadingWhitespacesInHeadersFuzzer", "LeadingMultiCodePointEmojisInFieldsTrimValidateFuzzer"
80-
, "RemoveFieldsFuzzer", "CheckSecurityHeadersFuzzer").hasSize(138);
80+
, "RemoveFieldsFuzzer", "CheckSecurityHeadersFuzzer").hasSize(139);
8181
}
8282

8383
@Test
@@ -144,7 +144,7 @@ void shouldReturnGetAndDeleteWhenNotHttpMethodSupplied() {
144144

145145
@Test
146146
void shouldReturnAllRegisteredFuzzers() {
147-
Assertions.assertThat(filterArguments.getAllRegisteredFuzzers()).hasSize(143);
147+
Assertions.assertThat(filterArguments.getAllRegisteredFuzzers()).hasSize(144);
148148
}
149149

150150
@Test
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.endava.cats.fuzzer.contract;
2+
3+
import com.endava.cats.args.IgnoreArguments;
4+
import com.endava.cats.args.ReportingArguments;
5+
import com.endava.cats.context.CatsGlobalContext;
6+
import com.endava.cats.model.FuzzingData;
7+
import com.endava.cats.report.ExecutionStatisticsListener;
8+
import com.endava.cats.report.TestCaseExporter;
9+
import com.endava.cats.report.TestCaseExporterHtmlJs;
10+
import com.endava.cats.report.TestCaseListener;
11+
import io.quarkus.test.junit.QuarkusTest;
12+
import jakarta.enterprise.inject.Instance;
13+
import org.assertj.core.api.Assertions;
14+
import org.junit.jupiter.api.BeforeEach;
15+
import org.junit.jupiter.api.Test;
16+
import org.mockito.Mockito;
17+
18+
import java.util.List;
19+
import java.util.Map;
20+
import java.util.stream.Stream;
21+
22+
@QuarkusTest
23+
class ResponsesWithBodiesLinterFuzzerTest {
24+
25+
private TestCaseListener testCaseListener;
26+
27+
private ResponsesWithBodiesLinterFuzzer responsesWithBodiesLinterFuzzer;
28+
29+
@BeforeEach
30+
void setup() {
31+
Instance<TestCaseExporter> exporters = Mockito.mock(Instance.class);
32+
TestCaseExporter exporter = Mockito.mock(TestCaseExporterHtmlJs.class);
33+
Mockito.when(exporters.stream()).thenReturn(Stream.of(exporter));
34+
testCaseListener = Mockito.spy(new TestCaseListener(Mockito.mock(CatsGlobalContext.class), Mockito.mock(ExecutionStatisticsListener.class), exporters,
35+
Mockito.mock(IgnoreArguments.class), Mockito.mock(ReportingArguments.class)));
36+
responsesWithBodiesLinterFuzzer = new ResponsesWithBodiesLinterFuzzer(testCaseListener);
37+
}
38+
39+
@Test
40+
void shouldReturnInfo() {
41+
FuzzingData data = FuzzingData.builder().responses(Map.of("200", List.of("1", "2"))).build();
42+
responsesWithBodiesLinterFuzzer.fuzz(data);
43+
44+
Mockito.verify(testCaseListener, Mockito.times(1)).reportResultInfo(Mockito.any(), Mockito.any(), Mockito.eq("All HTTP response codes have a response body"));
45+
}
46+
47+
@Test
48+
void shouldReturnError() {
49+
FuzzingData data = FuzzingData.builder().responses(Map.of("200", List.of())).build();
50+
responsesWithBodiesLinterFuzzer.fuzz(data);
51+
52+
Mockito.verify(testCaseListener, Mockito.times(1)).reportResultError(Mockito.any(), Mockito.any(),
53+
Mockito.eq("Missing response body for some HTTP response codes"), Mockito.eq("The following HTTP response codes are missing a response body: {}"), Mockito.eq(List.of("200")));
54+
}
55+
56+
@Test
57+
void shouldReturnSimpleClassNameForToString() {
58+
Assertions.assertThat(responsesWithBodiesLinterFuzzer).hasToString(responsesWithBodiesLinterFuzzer.getClass().getSimpleName());
59+
}
60+
61+
@Test
62+
void shouldReturnMeaningfulDescription() {
63+
Assertions.assertThat(responsesWithBodiesLinterFuzzer.description()).isEqualTo("verifies that HTTP response codes (except for 204 and 304) have a response body");
64+
}
65+
}

0 commit comments

Comments
 (0)