diff --git a/src/main/java/com/endava/cats/fuzzer/special/CustomFuzzerUtil.java b/src/main/java/com/endava/cats/fuzzer/special/CustomFuzzerUtil.java index f16f989b2..9d6b1edb4 100644 --- a/src/main/java/com/endava/cats/fuzzer/special/CustomFuzzerUtil.java +++ b/src/main/java/com/endava/cats/fuzzer/special/CustomFuzzerUtil.java @@ -38,7 +38,15 @@ import java.util.stream.Collectors; import static com.endava.cats.json.JsonUtils.NOT_SET; -import static com.endava.cats.util.CatsDSLWords.*; +import static com.endava.cats.util.CatsDSLWords.CATS_HEADERS; +import static com.endava.cats.util.CatsDSLWords.CHECKS; +import static com.endava.cats.util.CatsDSLWords.DESCRIPTION; +import static com.endava.cats.util.CatsDSLWords.EXPECTED_RESPONSE_CODE; +import static com.endava.cats.util.CatsDSLWords.HTTP_METHOD; +import static com.endava.cats.util.CatsDSLWords.ONE_OF_SELECTION; +import static com.endava.cats.util.CatsDSLWords.OUTPUT; +import static com.endava.cats.util.CatsDSLWords.RESERVED_WORDS; +import static com.endava.cats.util.CatsDSLWords.VERIFY; @ApplicationScoped public class CustomFuzzerUtil { @@ -209,10 +217,18 @@ private Map matchVariablesWithTheResponse(CatsResponse response, //we make sure that "checkBoolean" is not marked as NOT_SET and set to TRUE so that is matched against the computed expression return result.entrySet() .stream() - .map(entry -> entry.getKey().equalsIgnoreCase(CHECK) ? new AbstractMap.SimpleEntry<>(entry.getKey(), TRUE) : entry) + .map(CustomFuzzerUtil::remapCheckBoolean) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } + private static Map.Entry remapCheckBoolean(Map.Entry entry) { + return CHECKS.entrySet() + .stream() + .filter(checkEntry -> entry.getKey().startsWith(checkEntry.getKey())) + .map(checkEntry -> new AbstractMap.SimpleEntry<>(entry.getKey(), checkEntry.getValue())) + .findFirst() + .orElseGet(() -> new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue())); + } public String getTestScenario(String testName, Map currentPathValues) { String description = WordUtils.nullOrValueOf(currentPathValues.get(DESCRIPTION)); diff --git a/src/main/java/com/endava/cats/io/ServiceCaller.java b/src/main/java/com/endava/cats/io/ServiceCaller.java index 3d845b67c..25155c82b 100755 --- a/src/main/java/com/endava/cats/io/ServiceCaller.java +++ b/src/main/java/com/endava/cats/io/ServiceCaller.java @@ -460,8 +460,8 @@ private String getContentType(HttpMethod method, String defaultContentType) { } private void addIfNotPresent(String headerName, String headerValue, ServiceData data, List> headers) { - boolean notAccept = data.getHeaders().stream().noneMatch(catsHeader -> catsHeader.getName().equalsIgnoreCase(headerName)); - if (notAccept) { + boolean exists = data.getHeaders().stream().noneMatch(catsHeader -> catsHeader.getName().equalsIgnoreCase(headerName)); + if (exists) { headers.add(new KeyValuePair<>(headerName, headerValue)); } } diff --git a/src/main/java/com/endava/cats/util/CatsDSLWords.java b/src/main/java/com/endava/cats/util/CatsDSLWords.java index e3e22c530..19a9da770 100644 --- a/src/main/java/com/endava/cats/util/CatsDSLWords.java +++ b/src/main/java/com/endava/cats/util/CatsDSLWords.java @@ -1,6 +1,7 @@ package com.endava.cats.util; import java.util.List; +import java.util.Map; public abstract class CatsDSLWords { /** @@ -17,10 +18,12 @@ public abstract class CatsDSLWords { */ public static final String CHECK = "checkBoolean"; - /** - * Self-explanatory. - */ - public static final String TRUE = "true"; + public static final String CHECK_TRUE = "checkTrue"; + + public static final String CHECK_FALSE = "checkFalse"; + + public static final Map CHECKS = Map.of(CHECK, "true", CHECK_TRUE, "true", CHECK_FALSE, "false"); + /** * Reserved word used in CATS DSL files to specify the http method to be run. */ diff --git a/src/test/java/com/endava/cats/args/FilterArgumentsTest.java b/src/test/java/com/endava/cats/args/FilterArgumentsTest.java index f05db972e..58698818f 100644 --- a/src/test/java/com/endava/cats/args/FilterArgumentsTest.java +++ b/src/test/java/com/endava/cats/args/FilterArgumentsTest.java @@ -92,7 +92,7 @@ void shouldIncludeAllFuzzers() { List fuzzers = filterArguments.getFirstPhaseFuzzersForPath(); Assertions.assertThat(fuzzers).contains("LeadingControlCharsInHeadersFuzzer", "LeadingWhitespacesInHeadersFuzzer", "LeadingMultiCodePointEmojisInFieldsTrimValidateFuzzer" - , "RemoveFieldsFuzzer", "CheckSecurityHeadersFuzzer").hasSize(118); + , "RemoveFieldsFuzzer", "CheckSecurityHeadersFuzzer").hasSize(124); } @Test @@ -159,7 +159,7 @@ void shouldReturnGetAndDeleteWhenNotHttpMethodSupplied() { @Test void shouldReturnAllRegisteredFuzzers() { - Assertions.assertThat(filterArguments.getAllRegisteredFuzzers()).hasSize(122); + Assertions.assertThat(filterArguments.getAllRegisteredFuzzers()).hasSize(128); } @Test diff --git a/src/test/java/com/endava/cats/fuzzer/http/EmptyJsonArrayBodyFuzzerTest.java b/src/test/java/com/endava/cats/fuzzer/http/EmptyJsonArrayBodyFuzzerTest.java index d5a152fd6..fbe853b50 100644 --- a/src/test/java/com/endava/cats/fuzzer/http/EmptyJsonArrayBodyFuzzerTest.java +++ b/src/test/java/com/endava/cats/fuzzer/http/EmptyJsonArrayBodyFuzzerTest.java @@ -1,7 +1,76 @@ package com.endava.cats.fuzzer.http; +import com.endava.cats.fuzzer.executor.SimpleExecutor; +import com.endava.cats.http.HttpMethod; +import com.endava.cats.http.ResponseCodeFamily; +import com.endava.cats.io.ServiceCaller; +import com.endava.cats.model.CatsResponse; +import com.endava.cats.model.FuzzingData; +import com.endava.cats.report.TestCaseExporter; +import com.endava.cats.report.TestCaseListener; import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.mockito.InjectSpy; +import io.swagger.v3.oas.models.media.StringSchema; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; @QuarkusTest -class EmptyJsonArrayBodyTest { +class EmptyJsonArrayBodyFuzzerTest { + private ServiceCaller serviceCaller; + @InjectSpy + private TestCaseListener testCaseListener; + private EmptyJsonArrayBodyFuzzer emptyJsonArrayBodyFuzzer; + + @BeforeEach + void setup() { + serviceCaller = Mockito.mock(ServiceCaller.class); + SimpleExecutor simpleExecutor = new SimpleExecutor(testCaseListener, serviceCaller); + emptyJsonArrayBodyFuzzer = new EmptyJsonArrayBodyFuzzer(simpleExecutor); + ReflectionTestUtils.setField(testCaseListener, "testCaseExporter", Mockito.mock(TestCaseExporter.class)); + } + + @Test + void shouldNotRunForEmptyPayload() { + emptyJsonArrayBodyFuzzer.fuzz(Mockito.mock(FuzzingData.class)); + + Mockito.verifyNoInteractions(testCaseListener); + } + + @Test + void givenAHttpMethodWithPayload_whenApplyingTheMalformedJsonFuzzer_thenTheResultsAreCorrectlyReported() { + FuzzingData data = FuzzingData.builder().method(HttpMethod.POST).reqSchema(new StringSchema()).requestContentTypes(List.of("application/json")).build(); + ReflectionTestUtils.setField(data, "processedPayload", "{\"id\": 1}"); + + CatsResponse catsResponse = CatsResponse.builder().body("{}").responseCode(400).build(); + Mockito.when(serviceCaller.call(Mockito.any())).thenReturn(catsResponse); + Mockito.doNothing().when(testCaseListener).reportResult(Mockito.any(), Mockito.eq(data), Mockito.any(), Mockito.any(), Mockito.anyBoolean()); + + emptyJsonArrayBodyFuzzer.fuzz(data); + Mockito.verify(testCaseListener, Mockito.times(1)).reportResult(Mockito.any(), Mockito.eq(data), Mockito.eq(catsResponse), Mockito.eq(ResponseCodeFamily.FOURXX), Mockito.anyBoolean()); + } + + @Test + void shouldHaveToString() { + Assertions.assertThat(emptyJsonArrayBodyFuzzer).hasToString(emptyJsonArrayBodyFuzzer.getClass().getSimpleName()); + } + + @Test + void shouldHaveDescription() { + Assertions.assertThat(emptyJsonArrayBodyFuzzer.description()).isNotBlank(); + } + + @Test + void shouldSkipForNonHttpBodyMethods() { + Assertions.assertThat(emptyJsonArrayBodyFuzzer.skipForHttpMethods()).contains(HttpMethod.GET, HttpMethod.DELETE); + } + + @Test + void shouldHaveNullPayload() { + Assertions.assertThat(emptyJsonArrayBodyFuzzer.getPayload(null)).isEqualTo("[]"); + } } diff --git a/src/test/resources/functionalFuzzer-verify.yml b/src/test/resources/functionalFuzzer-verify.yml index 87a9f7bdb..a968a7e63 100644 --- a/src/test/resources/functionalFuzzer-verify.yml +++ b/src/test/resources/functionalFuzzer-verify.yml @@ -14,6 +14,9 @@ id: 25 $#length(): 3 checkBoolean: T(java.time.LocalDate).now().isAfter(T(java.time.LocalDate).parse(expiry.toString())) + checkTrue: T(java.time.LocalDate).now().isAfter(T(java.time.LocalDate).parse(expiry.toString())) + checkFalse: T(java.time.LocalDate).now().isBefore(T(java.time.LocalDate).parse(expiry.toString())) + checkTrue_2: T(java.time.LocalDate).now().isAfter(T(java.time.LocalDate).parse(expiry.toString())) test_2: description: Second Test Case pet: ${resp}