diff --git a/graphql-spring-boot-test-autoconfigure/src/main/java/com/graphql/spring/boot/test/GraphQLTestAutoConfiguration.java b/graphql-spring-boot-test-autoconfigure/src/main/java/com/graphql/spring/boot/test/GraphQLTestAutoConfiguration.java index 5920b5e2..fa7c530a 100644 --- a/graphql-spring-boot-test-autoconfigure/src/main/java/com/graphql/spring/boot/test/GraphQLTestAutoConfiguration.java +++ b/graphql-spring-boot-test-autoconfigure/src/main/java/com/graphql/spring/boot/test/GraphQLTestAutoConfiguration.java @@ -2,12 +2,15 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; +import org.springframework.core.io.ResourceLoader; @Configuration @ConditionalOnWebApplication @@ -16,8 +19,14 @@ public class GraphQLTestAutoConfiguration { @Bean @ConditionalOnMissingBean - public GraphQLTestTemplate graphQLTestUtils() { - return new GraphQLTestTemplate(); + public GraphQLTestTemplate graphQLTestUtils( + final ResourceLoader resourceLoader, + final TestRestTemplate restTemplate, + @Value("${graphql.servlet.mapping:/graphql}") + final String graphqlMapping, + final ObjectMapper objectMapper + ) { + return new GraphQLTestTemplate(resourceLoader, restTemplate, graphqlMapping, objectMapper); } @Bean diff --git a/graphql-spring-boot-test-autoconfigure/src/test/java/com/graphql/spring/boot/test/GraphQLTestAutoConfigurationTestBase.java b/graphql-spring-boot-test-autoconfigure/src/test/java/com/graphql/spring/boot/test/GraphQLTestAutoConfigurationTestBase.java index f5770423..d47ded3e 100644 --- a/graphql-spring-boot-test-autoconfigure/src/test/java/com/graphql/spring/boot/test/GraphQLTestAutoConfigurationTestBase.java +++ b/graphql-spring-boot-test-autoconfigure/src/test/java/com/graphql/spring/boot/test/GraphQLTestAutoConfigurationTestBase.java @@ -18,23 +18,20 @@ public class GraphQLTestAutoConfigurationTestBase { void assertThatTestSubscriptionWorksCorrectly() { // GIVEN - final GraphQLTestSubscription graphQLTestSubscription - = applicationContext.getBean(GraphQLTestSubscription.class); - // WHEN - final GraphQLResponse graphQLResponse - = graphQLTestSubscription.start("test-subscription.graphql").awaitAndGetNextResponse(1000); - // THEN - assertThat(graphQLResponse.get("$.data.testSubscription")).isEqualTo(FOO); + final GraphQLTestSubscription testSubscription = applicationContext.getBean(GraphQLTestSubscription.class); + // WHEN - THEN + testSubscription.start("test-subscription.graphql") + .awaitAndGetNextResponse(1000) + .assertThatNoErrorsArePresent() + .assertThatField("$.data.testSubscription").asString().isEqualTo(FOO); } void assertThatTestTemplateAutoConfigurationWorksCorrectly() throws IOException { // GIVEN - final GraphQLTestTemplate graphQLTestTemplate - = applicationContext.getBean(GraphQLTestTemplate.class); - // WHEN - final GraphQLResponse graphQLResponse - = graphQLTestTemplate.postForResource("test-query.graphql"); - // THEN - assertThat(graphQLResponse.get("$.data.testQuery")).isEqualTo(FOO); + final GraphQLTestTemplate testTemplate = applicationContext.getBean(GraphQLTestTemplate.class); + // WHEN - THEN + testTemplate.postForResource("test-query.graphql") + .assertThatNoErrorsArePresent() + .assertThatField("$.data.testQuery").asString().isEqualTo(FOO); } } diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLResponse.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLResponse.java index 1ae82fb8..18f390e9 100644 --- a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLResponse.java +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLResponse.java @@ -1,9 +1,15 @@ package com.graphql.spring.boot.test; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.graphql.spring.boot.test.assertions.GraphQLErrorListAssertion; +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.NumberOfErrorsAssertion; +import com.graphql.spring.boot.test.helper.GraphQLTestConstantsHelper; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.ReadContext; +import org.springframework.boot.test.json.JsonContentAssert; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -30,17 +36,24 @@ public JsonNode readTree() throws IOException { return mapper.readTree(responseEntity.getBody()); } + public Object getRaw(String path) { + return get(path, Object.class); + } + public String get(String path) { return get(path, String.class); } public T get(String path, Class type) { - return mapper.convertValue(context.read(path, Object.class), type); + return mapper.convertValue(context.read(path), type); + } + + public T get(String path, JavaType type) { + return mapper.convertValue(context.read(path), type); } public List getList(String path, Class type) { - final List raw = context.read(path, List.class); - return mapper.convertValue(raw, mapper.getTypeFactory().constructCollectionType(List.class, type)); + return get(path, mapper.getTypeFactory().constructCollectionType(List.class, type)); } public ReadContext context() { @@ -58,4 +71,71 @@ public HttpStatus getStatusCode() { public ResponseEntity getRawResponse() { return responseEntity; } + + /** + * Asserts that no errors are present in the response. An empty or null "errors" array also passes this test. + * @return this object + */ + public GraphQLResponse assertThatNoErrorsArePresent() { + return assertThatListOfErrors().hasNoErrors().and(); + } + + /** + * Returns an assertion for the number of errors in the response. + * @return the assertion for the number of errors. + */ + public NumberOfErrorsAssertion assertThatNumberOfErrors() { + return new NumberOfErrorsAssertion(this); + } + + /** + * Returns an assertion for the list of errors in the response. + * @return the assertion for the list of errors. + */ + public GraphQLErrorListAssertion assertThatListOfErrors() { + return new GraphQLErrorListAssertion(this); + } + + /** + * Returns an assertion for the given field(s). + * @param jsonPath the JSON path of the field(s) to assert. + * @return the assertion for the given field. + */ + public GraphQLFieldAssert assertThatField(final String jsonPath) { + return new GraphQLFieldAssert(this, jsonPath); + } + + /** + * Returns an assertion for the root "data" field. + * @return the assertion for the $.data field. + */ + public GraphQLFieldAssert assertThatDataField() { + return assertThatField(GraphQLTestConstantsHelper.DATA_PATH); + } + + /** + * Returns an assertion for the root "extensions" field. + * @return the assertion for the $.extensions field. + */ + public GraphQLFieldAssert assertThatExtensionsField() { + return assertThatField(GraphQLTestConstantsHelper.EXTENSIONS_PATH); + } + + /** + * Returns an assertion for the root "errors" field. + * @return the assertion for the $.errors field. + */ + public GraphQLFieldAssert assertThatErrorsField() { + return assertThatField(GraphQLTestConstantsHelper.ERRORS_PATH); + } + + /** + * Returns an assertion for the JSON content of the response. Since the Spring Boot Framework does not provide + * an abstract version of {@link JsonContentAssert} (as core AssertJ assertions do), it is not possible + * chain other assertions after this one. + * @return a {@link JsonContentAssert} instance for the content of the response. + */ + public JsonContentAssert assertThatJsonContent() { + return new JsonContentAssert(null, responseEntity.getBody()); + } } diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLTestError.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLTestError.java new file mode 100644 index 00000000..59fdbf38 --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLTestError.java @@ -0,0 +1,72 @@ +package com.graphql.spring.boot.test; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import graphql.ErrorClassification; +import graphql.ErrorType; +import graphql.GraphQLError; +import graphql.language.SourceLocation; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.util.NumberUtils; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static java.util.Objects.nonNull; + +/** + * An implementation of the {@link GraphQLError} interface for testing purposes. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GraphQLTestError implements GraphQLError { + private String message; + @JsonTypeInfo(defaultImpl = JacksonFriendlySourceLocation.class, use = JsonTypeInfo.Id.CLASS) + private List locations; + @JsonTypeInfo(defaultImpl = ErrorType.class, use = JsonTypeInfo.Id.CLASS) + private ErrorClassification errorType; + private List path; + private Map extensions; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append(Optional.ofNullable(errorType).map(ErrorClassification::toString).orElse("")); + sb.append(": "); + sb.append(Optional.ofNullable(message).orElse("")); + if (nonNull(locations) && !locations.isEmpty()) { + sb.append(" at line "); + locations.forEach( + location -> sb + .append(location.getLine()) + .append(", column ") + .append(location.getColumn()).append(" in ") + .append(Optional.ofNullable(location.getSourceName()).orElse("unnamed/unspecified source")) + ); + } + if (nonNull(path) && !path.isEmpty()) { + sb.append(". Selection path: "); + sb.append(path.stream() + .map(Object::toString) + .map(this::toNumericIndexIfPossible) + .collect(Collectors.joining("/")) + .replaceAll("/\\[", "[") + ); + } + return sb.toString(); + } + + private String toNumericIndexIfPossible(final String s) { + try { + return "[" + NumberUtils.parseNumber(s, Long.class) + "]"; + } catch (IllegalArgumentException e) { + return s; + } + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLTestTemplate.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLTestTemplate.java index 3b618c30..5ec0e030 100644 --- a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLTestTemplate.java +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLTestTemplate.java @@ -3,7 +3,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.Getter; +import lombok.NonNull; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.core.io.Resource; @@ -12,31 +13,53 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; +import org.springframework.util.MultiValueMap; import org.springframework.util.StreamUtils; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import static java.util.Objects.nonNull; + +/** + * Helper class to test GraphQL queries and mutations. + */ public class GraphQLTestTemplate { - @Autowired - private ResourceLoader resourceLoader; - @Autowired(required = false) - private TestRestTemplate restTemplate; - @Value("${graphql.servlet.mapping:/graphql}") - private String graphqlMapping; - @Autowired - private ObjectMapper objectMapper; + private final ResourceLoader resourceLoader; + private final TestRestTemplate restTemplate; + private final String graphqlMapping; + private final ObjectMapper objectMapper; + @Getter + private final HttpHeaders headers = new HttpHeaders(); - private HttpHeaders headers = new HttpHeaders(); + public GraphQLTestTemplate( + final ResourceLoader resourceLoader, + final TestRestTemplate restTemplate, + @Value("${graphql.servlet.mapping:/graphql}") + final String graphqlMapping, + final ObjectMapper objectMapper + ) { + this.resourceLoader = resourceLoader; + this.restTemplate = restTemplate; + this.graphqlMapping = graphqlMapping; + this.objectMapper = objectMapper; + } - private String createJsonQuery(String graphql, ObjectNode variables) + private String createJsonQuery(String graphql, String operation, ObjectNode variables) throws JsonProcessingException { ObjectNode wrapper = objectMapper.createObjectNode(); wrapper.put("query", graphql); + if (nonNull(operation)) { + wrapper.put("operationName", operation); + } wrapper.set("variables", variables); return objectMapper.writeValueAsString(wrapper); } @@ -53,87 +76,243 @@ private String loadResource(Resource resource) throws IOException { } /** + * @deprecated use {{@link #withAdditionalHeader(String, String...)}} instead * Add an HTTP header that will be sent with each request this sends. * * @param name Name (key) of HTTP header to add. * @param value Value of HTTP header to add. */ - public void addHeader(String name, String value) { - headers.add(name, value); + @Deprecated + public void addHeader(final String name, final String value) { + withAdditionalHeader(name, value); + } + + /** + * Add an HTTP header that will be sent with each request this sends. + * + * @param name Name (key) of HTTP header to add. + * @param value Value(s) of HTTP header to add. + * @return self + */ + public GraphQLTestTemplate withAdditionalHeader(final String name, final String... value) { + headers.addAll(name, Arrays.asList(value)); + return this; } /** + * Add multiple HTTP header that will be sent with each request this sends. + * + * @param additionalHeaders additional headers to add + * @return self + */ + public GraphQLTestTemplate withAdditionalHeaders(final MultiValueMap additionalHeaders) { + headers.addAll(additionalHeaders); + return this; + } + + /** + * Adds a bearer token to the authorization header. + * @param token the bearer token + * @return self + */ + public GraphQLTestTemplate withBearerAuth(@NonNull final String token) { + headers.setBearerAuth(token); + return this; + } + + /** + * Adds basic authentication to the authorization header. + * @param username the username + * @param password the password + * @param charset the charset used by the credentials + * @return self + */ + public GraphQLTestTemplate withBasicAuth( + @NonNull final String username, + @NonNull final String password, + @Nullable final Charset charset + ) { + headers.setBasicAuth(username, password, charset); + return this; + } + + /** + * Adds basic authentication to the authorization header. + * @param username the username + * @param password the password + * @return self + */ + public GraphQLTestTemplate withBasicAuth(@NonNull final String username, @NonNull final String password) { + headers.setBasicAuth(username, password, null); + return this; + } + + /** + * Adds basic authentication to the authorization header. + * @param encodedCredentials the encoded credentials + * @return self + */ + public GraphQLTestTemplate withBasicAuth(@NonNull final String encodedCredentials) { + headers.setBasicAuth(encodedCredentials); + return this; + } + + /** + * @deprecated use {{@link #withHeaders(HttpHeaders)}} instead. * Replace any associated HTTP headers with the provided headers. * * @param newHeaders Headers to use. */ + @Deprecated public void setHeaders(HttpHeaders newHeaders) { - headers = newHeaders; + withHeaders(newHeaders); } /** + * Replace any associated HTTP headers with the provided headers. + * + * @param newHeaders Headers to use. + * @return self + */ + public GraphQLTestTemplate withHeaders(final HttpHeaders newHeaders) { + return withClearHeaders().withAdditionalHeaders(newHeaders); + } + + /** + * @deprecated use {{@link #withClearHeaders()}} instead * Clear all associated HTTP headers. */ + @Deprecated public void clearHeaders() { - setHeaders(new HttpHeaders()); + withClearHeaders(); + } + + /** + * Clear all associated HTTP headers. + * @return self + */ + public GraphQLTestTemplate withClearHeaders() { + headers.clear(); + return this; } /** + * Loads a GraphQL query or mutation from the given classpath resource and sends it to the GraphQL server. + * * @deprecated Use {@link #postForResource(String)} instead * * @param graphqlResource path to the classpath resource containing the GraphQL query - * @return GraphQLResponse containing the result of query execution + * @return {@link GraphQLResponse} containing the result of query execution * @throws IOException if the resource cannot be loaded from the classpath */ + @Deprecated public GraphQLResponse perform(String graphqlResource) throws IOException { return postForResource(graphqlResource); } + /** + * Loads a GraphQL query or mutation from the given classpath resource and sends it to the GraphQL server. + * + * @param graphqlResource path to the classpath resource containing the GraphQL query + * @param variables the input variables for the GraphQL query + * @return {@link GraphQLResponse} containing the result of query execution + * @throws IOException if the resource cannot be loaded from the classpath + */ public GraphQLResponse perform(String graphqlResource, ObjectNode variables) throws IOException { - String graphql = loadQuery(graphqlResource); - String payload = createJsonQuery(graphql, variables); - return post(payload); + return perform(graphqlResource, null, variables, Collections.emptyList()); + } + + /** + * Loads a GraphQL query or mutation from the given classpath resource and sends it to the GraphQL server. + * + * @param graphqlResource path to the classpath resource containing the GraphQL query + * @param operationName the name of the GraphQL operation to be executed + * @return {@link GraphQLResponse} containing the result of query execution + * @throws IOException if the resource cannot be loaded from the classpath + */ + public GraphQLResponse perform(String graphqlResource, String operationName) throws IOException { + return perform(graphqlResource, operationName, null, Collections.emptyList()); } + /** + * Loads a GraphQL query or mutation from the given classpath resource and sends it to the GraphQL server. + * + * @param graphqlResource path to the classpath resource containing the GraphQL query + * @param operation the name of the GraphQL operation to be executed + * @param variables the input variables for the GraphQL query + * @return {@link GraphQLResponse} containing the result of query execution + * @throws IOException if the resource cannot be loaded from the classpath + */ + public GraphQLResponse perform(String graphqlResource, String operation, ObjectNode variables) throws IOException { + return perform(graphqlResource, operation, variables, Collections.emptyList()); + } + + /** + * Loads a GraphQL query or mutation from the given classpath resource and sends it to the GraphQL server. + * + * @param graphqlResource path to the classpath resource containing the GraphQL query + * @param variables the input variables for the GraphQL query + * @param fragmentResources an ordered list of classpath resources containing GraphQL fragment definitions. + * @return {@link GraphQLResponse} containing the result of query execution + * @throws IOException if the resource cannot be loaded from the classpath + */ public GraphQLResponse perform(String graphqlResource, ObjectNode variables, List fragmentResources) throws IOException { + return perform(graphqlResource, null, variables, fragmentResources); + } + + /** + * Loads a GraphQL query or mutation from the given classpath resource and sends it to the GraphQL server. + * + * @param graphqlResource path to the classpath resource containing the GraphQL query + * @param variables the input variables for the GraphQL query + * @param fragmentResources an ordered list of classpath resources containing GraphQL fragment definitions. + * @return {@link GraphQLResponse} containing the result of query execution + * @throws IOException if the resource cannot be loaded from the classpath + */ + public GraphQLResponse perform(String graphqlResource, String operationName, ObjectNode variables, List fragmentResources) throws IOException { StringBuilder sb = new StringBuilder(); for (String fragmentResource : fragmentResources) { sb.append(loadQuery(fragmentResource)); } String graphql = sb.append(loadQuery(graphqlResource)).toString(); - String payload = createJsonQuery(graphql, variables); + String payload = createJsonQuery(graphql, operationName, variables); return post(payload); } /** - * Loads a GraphQL query from the given classpath resource and sends it to the GraphQL server. + * Loads a GraphQL query or mutation from the given classpath resource and sends it to the GraphQL server. * * @param graphqlResource path to the classpath resource containing the GraphQL query - * @return GraphQLResponse containing the result of query execution + * @return {@link GraphQLResponse} containing the result of query execution * @throws IOException if the resource cannot be loaded from the classpath */ public GraphQLResponse postForResource(String graphqlResource) throws IOException { - return perform(graphqlResource, null); + return perform(graphqlResource, null, null, Collections.emptyList()); } /** - * Loads a GraphQL query from the given classpath resource, appending any graphql fragment + * Loads a GraphQL query or mutation from the given classpath resource, appending any graphql fragment * resources provided and sends it to the GraphQL server. * * @param graphqlResource path to the classpath resource containing the GraphQL query - * @param fragmentResources an ordered list of classpaths containing GraphQL fragment definitions. - * @return GraphQLResponse containing the result of query execution + * @param fragmentResources an ordered list of classpath resources containing GraphQL fragment definitions. + * @return {@link GraphQLResponse} containing the result of query execution * @throws IOException if the resource cannot be loaded from the classpath */ public GraphQLResponse postForResource(String graphqlResource, List fragmentResources) throws IOException { - return perform(graphqlResource, null, fragmentResources); + return perform(graphqlResource, null, null, fragmentResources); } public GraphQLResponse postMultipart(String query, String variables) { return postRequest(RequestFactory.forMultipart(query, variables, headers)); } - private GraphQLResponse post(String payload) { + /** + * Performs a GraphQL request with the provided payload. + * @param payload the GraphQL payload + * @return @return {@link GraphQLResponse} containing the result of query execution + */ + public GraphQLResponse post(String payload) { return postRequest(RequestFactory.forJson(payload, headers)); } diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/JacksonFriendlySourceLocation.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/JacksonFriendlySourceLocation.java new file mode 100644 index 00000000..7953e199 --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/JacksonFriendlySourceLocation.java @@ -0,0 +1,21 @@ +package com.graphql.spring.boot.test; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import graphql.language.SourceLocation; +import org.springframework.lang.Nullable; + +/** + * Allow deserialization of {@link graphql.language.SourceLocation}. + */ +public class JacksonFriendlySourceLocation extends SourceLocation { + + @JsonCreator + public JacksonFriendlySourceLocation( + final @JsonProperty("line") int line, + final @JsonProperty("column") int column, + final @Nullable @JsonProperty("sourceName") String sourceName + ) { + super(line, column, sourceName); + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLBigDecimalAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLBigDecimalAssert.java new file mode 100644 index 00000000..774217e4 --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLBigDecimalAssert.java @@ -0,0 +1,22 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.AbstractBigDecimalAssert; + +import java.math.BigDecimal; + +public class GraphQLBigDecimalAssert extends AbstractBigDecimalAssert + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQlResponse; + + public GraphQLBigDecimalAssert(final GraphQLResponse graphQLResponse, final BigDecimal actual) { + super(actual, GraphQLBigDecimalAssert.class); + this.graphQlResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQlResponse; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLBigIntegerAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLBigIntegerAssert.java new file mode 100644 index 00000000..6b2bfa9a --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLBigIntegerAssert.java @@ -0,0 +1,22 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.AbstractBigIntegerAssert; + +import java.math.BigInteger; + +public class GraphQLBigIntegerAssert extends AbstractBigIntegerAssert + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQlResponse; + + public GraphQLBigIntegerAssert(final GraphQLResponse graphQLResponse, final BigInteger actual) { + super(actual, GraphQLBigIntegerAssert.class); + this.graphQlResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQlResponse; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLBooleanAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLBooleanAssert.java new file mode 100644 index 00000000..201d3986 --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLBooleanAssert.java @@ -0,0 +1,20 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.AbstractBooleanAssert; + +public class GraphQLBooleanAssert extends AbstractBooleanAssert + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQlResponse; + + public GraphQLBooleanAssert(final GraphQLResponse graphQLResponse, final Boolean actual) { + super(actual, GraphQLBooleanAssert.class); + this.graphQlResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQlResponse; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLByteAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLByteAssert.java new file mode 100644 index 00000000..d6d72563 --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLByteAssert.java @@ -0,0 +1,20 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.AbstractByteAssert; + +public class GraphQLByteAssert extends AbstractByteAssert + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQlResponse; + + public GraphQLByteAssert(final GraphQLResponse graphQLResponse, final Byte actual) { + super(actual, GraphQLByteAssert.class); + this.graphQlResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQlResponse; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLErrorListAssertion.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLErrorListAssertion.java new file mode 100644 index 00000000..d8daf323 --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLErrorListAssertion.java @@ -0,0 +1,58 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import com.graphql.spring.boot.test.GraphQLTestError; +import com.jayway.jsonpath.PathNotFoundException; +import graphql.GraphQLError; +import org.assertj.core.api.FactoryBasedNavigableListAssert; +import org.assertj.core.api.ObjectAssert; +import org.assertj.core.api.ObjectAssertFactory; + +import java.util.List; +import java.util.stream.Collectors; + +import static com.graphql.spring.boot.test.helper.GraphQLTestConstantsHelper.ERRORS_PATH; +import static java.util.Objects.nonNull; +import static org.assertj.core.api.Assertions.fail; + +public class GraphQLErrorListAssertion extends FactoryBasedNavigableListAssert< + GraphQLErrorListAssertion, + List, + GraphQLError, + ObjectAssert + > + implements GraphQLResponseAssertion +{ + private final GraphQLResponse graphQLResponse; + + public GraphQLErrorListAssertion(final GraphQLResponse graphQLResponse) { + super(getGraphQLErrors(graphQLResponse), GraphQLErrorListAssertion.class, new ObjectAssertFactory<>()); + this.graphQLResponse = graphQLResponse; + } + + public GraphQLResponseAssertion hasNoErrors() { + final List graphQLErrors = getGraphQLErrors(graphQLResponse); + if (nonNull(graphQLErrors) && !graphQLErrors.isEmpty()) { + final String combinedMessage = graphQLErrors.stream() + .map(GraphQLError::toString) + .collect(Collectors.joining(System.lineSeparator())); + fail(String.format("Expected no GraphQL errors, but got %s: %s", graphQLErrors.size(), combinedMessage)); + } + return this; + } + + @Override + public GraphQLResponse and() { + return graphQLResponse; + } + + private static List getGraphQLErrors(final GraphQLResponse graphQLResponse) { + List errorList = null; + try { + errorList = graphQLResponse.getList(ERRORS_PATH, GraphQLTestError.class); + } catch (PathNotFoundException e) { + // do nothing, error list is still null + } + return errorList; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLFieldAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLFieldAssert.java new file mode 100644 index 00000000..9ef1b80e --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLFieldAssert.java @@ -0,0 +1,244 @@ +package com.graphql.spring.boot.test.assertions; + +import com.fasterxml.jackson.databind.JavaType; +import com.graphql.spring.boot.test.GraphQLResponse; +import com.jayway.jsonpath.PathNotFoundException; +import lombok.RequiredArgsConstructor; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; +import java.util.Objects; + +import static java.util.Objects.nonNull; +import static org.assertj.core.api.Assertions.fail; + +/** + * Provides fluent assertions for a field (specified by a json path) of the GraphQL response. + */ +@RequiredArgsConstructor +public class GraphQLFieldAssert implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQLResponse; + private final String jsonPath; + + /** + * Asserts that the field specified by the provided JSON path is not present in the response. + * @throws AssertionError if the field is present in the response + * @return self + */ + public GraphQLFieldAssert isNotPresent() { + try { + graphQLResponse.getRaw(jsonPath); + fail("Expected that field %s is not present.", jsonPath); + } catch (PathNotFoundException e) { + // test passed + } + return this; + } + + /** + * Asserts that the field specified by the provided JSON path is not present in the response or its value is null. + * @throws AssertionError if the field is present in the response and its value is not null. + * @return self + */ + public GraphQLFieldAssert isNotPresentOrNull() { + try { + if (nonNull(graphQLResponse.getRaw(jsonPath))) { + fail("Expected field %s to be null or not present.", jsonPath); + } + } catch (PathNotFoundException e) { + // test passed + } + return this; + } + + /** + * Asserts that the field specified by the provided JSON path is null. + * @throws AssertionError if the field is not present in the response or its value is not null. + * @return self + */ + public GraphQLFieldAssert isNull() { + try { + if(nonNull(graphQLResponse.getRaw(jsonPath))) { + fail("Expected field %s to be null.", jsonPath); + } + } catch (PathNotFoundException e) { + fail(String.format("Expected field %s to be present.", jsonPath), e); + } + return this; + } + + /** + * Asserts that the field specified by the provided JSON path is not null. + * @throws AssertionError if the field is not present in the response or its value is null. + * @return self + */ + public GraphQLFieldAssert isNotNull() { + try { + if (Objects.isNull(graphQLResponse.getRaw(jsonPath))) { + fail("Expected field %s to be non-null.", jsonPath); + } + } catch (PathNotFoundException e) { + fail(String.format("Expected field %s to be present.", jsonPath), e); + } + return this; + } + + /** + * Returns an assertion for the content of the field as {@link BigDecimal}. + * @return a {@link GraphQLBigDecimalAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to {@link BigDecimal} + */ + public GraphQLBigDecimalAssert asBigDecimal() { + return new GraphQLBigDecimalAssert(graphQLResponse, getFieldAs(BigDecimal.class)); + } + + /** + * Returns an assertion for the content of the field as {@link BigInteger}. + * @return a {@link GraphQLBigIntegerAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to {@link BigInteger} + */ + public GraphQLBigIntegerAssert asBigInteger() { + return new GraphQLBigIntegerAssert(graphQLResponse, getFieldAs(BigInteger.class)); + } + + /** + * Returns an assertion for the content of the field as {@link Long}. + * @return a {@link GraphQLLongAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to {@link Long} + */ + public GraphQLLongAssert asLong() { + return new GraphQLLongAssert(graphQLResponse, getFieldAs(Long.class)); + } + + /** + * Returns an assertion for the content of the field as {@link Integer}. + * @return a {@link GraphQLIntegerAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to {@link Integer} + */ + public GraphQLIntegerAssert asInteger() { + return new GraphQLIntegerAssert(graphQLResponse, getFieldAs(Integer.class)); + } + + /** + * Returns an assertion for the content of the field as {@link Short}. + * @return a {@link GraphQLShortAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to {@link Short} + */ + public GraphQLShortAssert asShort() { + return new GraphQLShortAssert(graphQLResponse, getFieldAs(Short.class)); + } + + /** + * Returns an assertion for the content of the field as {@link Byte}. + * @return a {@link GraphQLByteAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to {@link Byte} + */ + public GraphQLByteAssert asByte() { + return new GraphQLByteAssert(graphQLResponse, getFieldAs(Byte.class)); + } + + /** + * Returns an assertion for the content of the field as {@link Boolean}. + * @return a {@link GraphQLBooleanAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to {@link Boolean} + */ + public GraphQLBooleanAssert asBoolean() { + return new GraphQLBooleanAssert(graphQLResponse, getFieldAs(Boolean.class)); + } + + /** + * Returns an assertion for the content of the field as {@link String}. + * @return a {@link GraphQLStringAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to {@link String} + */ + public GraphQLStringAssert asString() { + return new GraphQLStringAssert(graphQLResponse, getFieldAs(String.class)); + } + + /** + * Returns an assertion for the content of the field as an instance of the specified class. + * @param clazz The class of the object. to assert + * @return a {@link GraphQLGenericObjectAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to the specified class + */ + public GraphQLGenericObjectAssert as(final Class clazz) { + return new GraphQLGenericObjectAssert<>(graphQLResponse, getFieldAs(clazz)); + } + + /** + * Returns an assertion for the content of the field as an instance of the specified type. + * @param javaType The java type definition. + * @return a {@link GraphQLGenericObjectAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to the specified class + */ + public GraphQLGenericObjectAssert as(final JavaType javaType) { + return new GraphQLGenericObjectAssert<>(graphQLResponse, getFieldAs(javaType)); + } + + /** + * Returns an assertion for the content of the field as an instance of the specified list type. + * @param javaListType The java type definition. Expected to define a list type. + * @return a {@link GraphQLListAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to the specified class + * or if the provided type is not a list type. + */ + public GraphQLListAssert asList(final JavaType javaListType) { + return new GraphQLListAssert<>(graphQLResponse, getFieldAs(javaListType)); + } + + /** + * Returns an assertion for the content of the field as list of objects. + * @param elementClass the type of objects in the list + * @return a {@link GraphQLGenericObjectAssert} instance + * @throws AssertionError if the path does not exist or the content could not be converted to the specified class + */ + public GraphQLListAssert asListOf(final Class elementClass) { + return new GraphQLListAssert<>(graphQLResponse, getFieldAsList(elementClass)); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphQLResponse and() { + return graphQLResponse; + } + + private T getFieldAs(final Class targetClass) { + try { + return graphQLResponse.get(jsonPath, targetClass); + } catch (PathNotFoundException e) { + fail(String.format("Expected field %s to be present.", jsonPath), e); + return null; + } catch (IllegalArgumentException e) { + fail(String.format("Expected that content of field %s can be converted to %s.", jsonPath, targetClass), e); + return null; + } + } + + private T getFieldAs(final JavaType javaType) { + try { + return graphQLResponse.get(jsonPath, javaType); + } catch (PathNotFoundException e) { + fail(String.format("Expected field %s to be present.", jsonPath), e); + return null; + } catch (IllegalArgumentException e) { + fail(String.format("Expected that content of field %s can be converted to %s.", jsonPath, javaType), e); + return null; + } + } + + private List getFieldAsList(final Class targetClass) { + try { + return graphQLResponse.getList(jsonPath, targetClass); + } catch (PathNotFoundException e) { + fail(String.format("Expected field %s to be present.", jsonPath), e); + return null; + } catch (IllegalArgumentException e) { + fail(String.format("Expected that content of field %s can be converted to %s.", jsonPath, targetClass), e); + return null; + } + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLGenericObjectAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLGenericObjectAssert.java new file mode 100644 index 00000000..a3ac4d26 --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLGenericObjectAssert.java @@ -0,0 +1,21 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.AbstractObjectAssert; + +public class GraphQLGenericObjectAssert + extends AbstractObjectAssert, T> + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQLResponse; + + public GraphQLGenericObjectAssert(final GraphQLResponse graphQLResponse, final T actual) { + super(actual, GraphQLGenericObjectAssert.class); + this.graphQLResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQLResponse; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLIntegerAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLIntegerAssert.java new file mode 100644 index 00000000..66f31bee --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLIntegerAssert.java @@ -0,0 +1,21 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.AbstractIntegerAssert; +import org.assertj.core.api.AbstractLongAssert; + +public class GraphQLIntegerAssert extends AbstractIntegerAssert + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQlResponse; + + public GraphQLIntegerAssert(final GraphQLResponse graphQLResponse, final Integer actual) { + super(actual, GraphQLIntegerAssert.class); + this.graphQlResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQlResponse; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLListAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLListAssert.java new file mode 100644 index 00000000..3d27388d --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLListAssert.java @@ -0,0 +1,29 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.FactoryBasedNavigableListAssert; +import org.assertj.core.api.ObjectAssert; +import org.assertj.core.api.ObjectAssertFactory; + +import java.util.List; + +public class GraphQLListAssert extends FactoryBasedNavigableListAssert< + GraphQLListAssert, + List, + T, + ObjectAssert + > + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQlResponse; + + public GraphQLListAssert(final GraphQLResponse graphQLResponse, final List actual) { + super(actual, GraphQLListAssert.class, new ObjectAssertFactory<>()); + this.graphQlResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQlResponse; + } +} \ No newline at end of file diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLLongAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLLongAssert.java new file mode 100644 index 00000000..183f945e --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLLongAssert.java @@ -0,0 +1,20 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.AbstractLongAssert; + +public class GraphQLLongAssert extends AbstractLongAssert + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQlResponse; + + public GraphQLLongAssert(final GraphQLResponse graphQLResponse, final Long actual) { + super(actual, GraphQLLongAssert.class); + this.graphQlResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQlResponse; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLResponseAssertion.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLResponseAssertion.java new file mode 100644 index 00000000..24dd59ad --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLResponseAssertion.java @@ -0,0 +1,16 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; + +/** + * Common interface for GraphQL assertions. The main purpose of this interface is to allow chaining fluent assertions + * for a {@link GraphQLResponse}. + */ +public interface GraphQLResponseAssertion { + + /** + * @return the instance of {@link GraphQLResponse} for which this assertion was created. Allows chaining fluent + * assertions. + */ + GraphQLResponse and(); +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLShortAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLShortAssert.java new file mode 100644 index 00000000..9200be20 --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLShortAssert.java @@ -0,0 +1,22 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.AbstractIntegerAssert; +import org.assertj.core.api.AbstractShortArrayAssert; +import org.assertj.core.api.AbstractShortAssert; + +public class GraphQLShortAssert extends AbstractShortAssert + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQlResponse; + + public GraphQLShortAssert(final GraphQLResponse graphQLResponse, final Short actual) { + super(actual, GraphQLShortAssert.class); + this.graphQlResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQlResponse; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLStringAssert.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLStringAssert.java new file mode 100644 index 00000000..e08bcade --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/GraphQLStringAssert.java @@ -0,0 +1,20 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import org.assertj.core.api.AbstractStringAssert; + +public class GraphQLStringAssert extends AbstractStringAssert + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQlResponse; + + public GraphQLStringAssert(final GraphQLResponse graphQLResponse, final String actual) { + super(actual, GraphQLStringAssert.class); + this.graphQlResponse = graphQLResponse; + } + + @Override + public GraphQLResponse and() { + return graphQlResponse; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/NumberOfErrorsAssertion.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/NumberOfErrorsAssertion.java new file mode 100644 index 00000000..c460ef48 --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/assertions/NumberOfErrorsAssertion.java @@ -0,0 +1,42 @@ +package com.graphql.spring.boot.test.assertions; + +import com.graphql.spring.boot.test.GraphQLResponse; +import com.graphql.spring.boot.test.GraphQLTestError; +import com.jayway.jsonpath.PathNotFoundException; +import lombok.Getter; +import org.assertj.core.api.AbstractIntegerAssert; + +import java.util.List; + +import static com.graphql.spring.boot.test.helper.GraphQLTestConstantsHelper.ERRORS_PATH; +import static java.util.Objects.nonNull; + +public class NumberOfErrorsAssertion + extends AbstractIntegerAssert + implements GraphQLResponseAssertion { + + private final GraphQLResponse graphQLResponse; + + public NumberOfErrorsAssertion(final GraphQLResponse response) { + super(getNumberOfErrors(response), NumberOfErrorsAssertion.class); + graphQLResponse = response; + } + + @Override + public GraphQLResponse and() { + return graphQLResponse; + } + + private static Integer getNumberOfErrors(final GraphQLResponse response) { + int numErrors = 0; + try { + final List errorList = response.getList(ERRORS_PATH, GraphQLTestError.class); + if(nonNull(errorList)) { + numErrors = errorList.size(); + } + } catch (PathNotFoundException e) { + // do nothing, number of errors is zero + } + return numErrors; + } +} diff --git a/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/helper/GraphQLTestConstantsHelper.java b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/helper/GraphQLTestConstantsHelper.java new file mode 100644 index 00000000..245fbcbf --- /dev/null +++ b/graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/helper/GraphQLTestConstantsHelper.java @@ -0,0 +1,12 @@ +package com.graphql.spring.boot.test.helper; + +public final class GraphQLTestConstantsHelper { + + public static final String ERRORS_PATH = "$.errors"; + public static final String DATA_PATH = "$.data"; + public static final String EXTENSIONS_PATH = "$.extensions"; + + private GraphQLTestConstantsHelper() { + + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLAssertIsNotNullTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLAssertIsNotNullTest.java new file mode 100644 index 00000000..3e443ab0 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLAssertIsNotNullTest.java @@ -0,0 +1,51 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLAssertIsNotNullTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should pass if the value at the provided path is not null.") + void shouldPassIfIsNotNull() { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willReturn(NON_NULL_VALUE); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatCode(graphQLFieldAssert::isNotNull).doesNotThrowAnyException(); + assertThat(graphQLFieldAssert.isNotNull().and()).isSameAs(graphQLResponse); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::isNotNull) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path is null.") + void shouldFailIfIsNotNull() { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::isNotNull) + .withMessage("Expected field %s to be non-null.", MOCK_PATH); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLAssertIsNotPresentOrNullTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLAssertIsNotPresentOrNullTest.java new file mode 100644 index 00000000..7c4add91 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLAssertIsNotPresentOrNullTest.java @@ -0,0 +1,49 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLAssertIsNotPresentOrNullTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should pass if the provided path is not present.") + void shouldPassIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatCode(graphQLFieldAssert::isNotPresentOrNull).doesNotThrowAnyException(); + assertThat(graphQLFieldAssert.isNotPresentOrNull().and()).isSameAs(graphQLResponse); + } + + @Test + @DisplayName("Should pass if the value at the provided path is null.") + void shouldPassIfIsNull() { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatCode(graphQLFieldAssert::isNotPresentOrNull).doesNotThrowAnyException(); + assertThat(graphQLFieldAssert.isNotPresentOrNull().and()).isSameAs(graphQLResponse); + } + + @Test + @DisplayName("Should fail if the value at the provided path is not null.") + void shouldFailIfIsNotNull() { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willReturn(NON_NULL_VALUE); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::isNotPresentOrNull) + .withMessage("Expected field %s to be null or not present.", MOCK_PATH); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsBigDecimalTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsBigDecimalTest.java new file mode 100644 index 00000000..d0e78fe7 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsBigDecimalTest.java @@ -0,0 +1,74 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLBigDecimalAssert; +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import java.math.BigDecimal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsBigDecimalTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a big decimal assertion (value at specific path is valid number).") + void shouldReturnBigDecimalAssertIfFieldIsNonNull() { + // GIVEN + final BigDecimal value = new BigDecimal("1.23"); + given(graphQLResponse.get(MOCK_PATH, BigDecimal.class)).willReturn(value); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLBigDecimalAssert actual = graphQLFieldAssert.asBigDecimal(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isEqualByComparingTo(value).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(value); + } + + @Test + @DisplayName("Should return a big decimal assertion (value at specific path is null).") + void shouldReturnBigDecimalAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, BigDecimal.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLBigDecimalAssert actual = graphQLFieldAssert.asBigDecimal(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, BigDecimal.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asBigDecimal) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, BigDecimal.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asBigDecimal) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + BigDecimal.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsBigIntegerTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsBigIntegerTest.java new file mode 100644 index 00000000..d7eca6d2 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsBigIntegerTest.java @@ -0,0 +1,74 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLBigIntegerAssert; +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import java.math.BigInteger; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsBigIntegerTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a big integer assertion (value at specific path is valid number).") + void shouldReturnBigIntegerAssertIfFieldIsNonNull() { + // GIVEN + final BigInteger value = new BigInteger("123"); + given(graphQLResponse.get(MOCK_PATH, BigInteger.class)).willReturn(value); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLBigIntegerAssert actual = graphQLFieldAssert.asBigInteger(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isEqualByComparingTo(value).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(value); + } + + @Test + @DisplayName("Should return a big integer assertion (value at specific path is null).") + void shouldReturnBigIntegerAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, BigInteger.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLBigIntegerAssert actual = graphQLFieldAssert.asBigInteger(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, BigInteger.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asBigInteger) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, BigInteger.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asBigInteger) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + BigInteger.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsBooleanTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsBooleanTest.java new file mode 100644 index 00000000..49fea025 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsBooleanTest.java @@ -0,0 +1,71 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.GraphQLBooleanAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsBooleanTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a Boolean assertion (value at specific path is valid Boolean value).") + void shouldReturnBooleanAssertIfFieldIsNonNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Boolean.class)).willReturn(true); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLBooleanAssert actual = graphQLFieldAssert.asBoolean(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isTrue().and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(true); + } + + @Test + @DisplayName("Should return a Boolean assertion (value at specific path is null).") + void shouldReturnBooleanAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Boolean.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLBooleanAssert actual = graphQLFieldAssert.asBoolean(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Boolean.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asBoolean) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Boolean.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asBoolean) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + Boolean.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsByteTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsByteTest.java new file mode 100644 index 00000000..61a860bb --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsByteTest.java @@ -0,0 +1,72 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.GraphQLByteAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsByteTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a Byte assertion (value at specific path is valid Byte value).") + void shouldReturnByteAssertIfFieldIsNonNull() { + // GIVEN + final Byte value = 123; + given(graphQLResponse.get(MOCK_PATH, Byte.class)).willReturn(value); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLByteAssert actual = graphQLFieldAssert.asByte(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isEqualByComparingTo(value).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(value); + } + + @Test + @DisplayName("Should return a Byte assertion (value at specific path is null).") + void shouldReturnByteAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Byte.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLByteAssert actual = graphQLFieldAssert.asByte(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Byte.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asByte) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Byte.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asByte) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + Byte.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsIntegerTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsIntegerTest.java new file mode 100644 index 00000000..1aa3998a --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsIntegerTest.java @@ -0,0 +1,72 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.GraphQLIntegerAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsIntegerTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a Integer assertion (value at specific path is valid Integer value).") + void shouldReturnIntegerAssertIfFieldIsNonNull() { + // GIVEN + final Integer value = 123; + given(graphQLResponse.get(MOCK_PATH, Integer.class)).willReturn(value); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLIntegerAssert actual = graphQLFieldAssert.asInteger(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isEqualByComparingTo(value).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(value); + } + + @Test + @DisplayName("Should return a Integer assertion (value at specific path is null).") + void shouldReturnIntegerAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Integer.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLIntegerAssert actual = graphQLFieldAssert.asInteger(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Integer.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asInteger) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Integer.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asInteger) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + Integer.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsJavaListTypeTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsJavaListTypeTest.java new file mode 100644 index 00000000..9940c7fb --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsJavaListTypeTest.java @@ -0,0 +1,80 @@ +package com.graphql.spring.boot.test; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.GraphQLListAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsJavaListTypeTest extends GraphQLFieldAssertTestBase { + + private static final JavaType STRING_LIST_TYPE = TypeFactory.defaultInstance() + .constructCollectionLikeType(List.class, String.class); + + @Test + @DisplayName("Should return a String list assertion (value at specific path is valid list).") + void shouldReturnStringListAssertIfFieldIsNonNull() { + // GIVEN + final List values = Arrays.asList("value1", "value2"); + given(graphQLResponse.get(MOCK_PATH, STRING_LIST_TYPE)).willReturn(values); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLListAssert actual = graphQLFieldAssert.asList(STRING_LIST_TYPE); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.containsExactlyElementsOf(values).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(values); + } + + @Test + @DisplayName("Should return a String list assertion (value at specific path is null).") + void shouldReturnStringListAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, STRING_LIST_TYPE)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLListAssert actual = graphQLFieldAssert.asList(STRING_LIST_TYPE); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, STRING_LIST_TYPE)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> graphQLFieldAssert.asList(STRING_LIST_TYPE)) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, STRING_LIST_TYPE)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> graphQLFieldAssert.asList(STRING_LIST_TYPE)) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + STRING_LIST_TYPE) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsJavaTypeTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsJavaTypeTest.java new file mode 100644 index 00000000..96dd5caa --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsJavaTypeTest.java @@ -0,0 +1,77 @@ +package com.graphql.spring.boot.test; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.GraphQLGenericObjectAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsJavaTypeTest extends GraphQLFieldAssertTestBase { + + private static final JavaType FOO_TYPE = TypeFactory.defaultInstance().constructType(Foo.class); + + @Test + @DisplayName("Should return a generic assertion (value at specific path is non-null).") + void shouldReturnGenericObjectAssertIfFieldIsNonNull() { + // GIVEN + final Foo foo = new Foo("fooBar"); + given(graphQLResponse.get(MOCK_PATH, FOO_TYPE)).willReturn(foo); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLGenericObjectAssert + actual = graphQLFieldAssert.as(FOO_TYPE); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isEqualTo(foo).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(foo); + } + + @Test + @DisplayName("Should return a generic assertion (value at specific path is null).") + void shouldReturnGenericObjectAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, FOO_TYPE)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLGenericObjectAssert actual = graphQLFieldAssert.as(FOO_TYPE); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, FOO_TYPE)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> graphQLFieldAssert.as(FOO_TYPE)) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, FOO_TYPE)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> graphQLFieldAssert.as(FOO_TYPE)) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + FOO_TYPE) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsListTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsListTest.java new file mode 100644 index 00000000..952145e8 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsListTest.java @@ -0,0 +1,76 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.GraphQLListAssert; +import com.graphql.spring.boot.test.assertions.GraphQLStringAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsListTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a String list assertion (value at specific path is valid list).") + void shouldReturnStringListAssertIfFieldIsNonNull() { + // GIVEN + final List values = Arrays.asList("value1", "value2"); + given(graphQLResponse.getList(MOCK_PATH, String.class)).willReturn(values); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLListAssert actual = graphQLFieldAssert.asListOf(String.class); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.containsExactlyElementsOf(values).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(values); + } + + @Test + @DisplayName("Should return a String list assertion (value at specific path is null).") + void shouldReturnStringListAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.getList(MOCK_PATH, String.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLListAssert actual = graphQLFieldAssert.asListOf(String.class); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.getList(MOCK_PATH, String.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> graphQLFieldAssert.asListOf(String.class)) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.getList(MOCK_PATH, String.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> graphQLFieldAssert.asListOf(String.class)) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + String.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsLongTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsLongTest.java new file mode 100644 index 00000000..29c2a9da --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsLongTest.java @@ -0,0 +1,72 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLLongAssert; +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsLongTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a long assertion (value at specific path is valid long value).") + void shouldReturnLongAssertIfFieldIsNonNull() { + // GIVEN + final Long value = 123L; + given(graphQLResponse.get(MOCK_PATH, Long.class)).willReturn(value); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLLongAssert actual = graphQLFieldAssert.asLong(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isEqualByComparingTo(value).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(value); + } + + @Test + @DisplayName("Should return a long assertion (value at specific path is null).") + void shouldReturnLongAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Long.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLLongAssert actual = graphQLFieldAssert.asLong(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Long.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asLong) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Long.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asLong) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + Long.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsShortTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsShortTest.java new file mode 100644 index 00000000..23c9dfd1 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsShortTest.java @@ -0,0 +1,72 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.GraphQLShortAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsShortTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a Short assertion (value at specific path is valid Short value).") + void shouldReturnShortAssertIfFieldIsNonNull() { + // GIVEN + final Short value = 123; + given(graphQLResponse.get(MOCK_PATH, Short.class)).willReturn(value); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLShortAssert actual = graphQLFieldAssert.asShort(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isEqualByComparingTo(value).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(value); + } + + @Test + @DisplayName("Should return a Short assertion (value at specific path is null).") + void shouldReturnShortAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Short.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLShortAssert actual = graphQLFieldAssert.asShort(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Short.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asShort) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Short.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asShort) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + Short.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsStringTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsStringTest.java new file mode 100644 index 00000000..5668e4bf --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsStringTest.java @@ -0,0 +1,72 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.GraphQLStringAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsStringTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a String assertion (value at specific path is valid String value).") + void shouldReturnStringAssertIfFieldIsNonNull() { + // GIVEN + final String value = "some value"; + given(graphQLResponse.get(MOCK_PATH, String.class)).willReturn(value); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLStringAssert actual = graphQLFieldAssert.asString(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isNotBlank().and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(value); + } + + @Test + @DisplayName("Should return a String assertion (value at specific path is null).") + void shouldReturnStringAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, String.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLStringAssert actual = graphQLFieldAssert.asString(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, String.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asString) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, String.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::asString) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + String.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsTest.java new file mode 100644 index 00000000..7f51f6aa --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertAsTest.java @@ -0,0 +1,74 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.GraphQLGenericObjectAssert; +import com.jayway.jsonpath.PathNotFoundException; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertAsTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should return a generic assertion (value at specific path is non-null).") + void shouldReturnGenericObjectAssertIfFieldIsNonNull() { + // GIVEN + final Foo foo = new Foo("fooBar"); + given(graphQLResponse.get(MOCK_PATH, Foo.class)).willReturn(foo); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLGenericObjectAssert actual = graphQLFieldAssert.as(Foo.class); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual.isEqualTo(foo).and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isSameAs(foo); + } + + @Test + @DisplayName("Should return a generic assertion (value at specific path is null).") + void shouldReturnGenericObjectAssertIfFieldIsNull() { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Foo.class)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN + final GraphQLGenericObjectAssert actual = graphQLFieldAssert.as(Foo.class); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual.and()).isSameAs(graphQLResponse); + assertThat(actual).extracting("actual").isNull(); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Foo.class)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> graphQLFieldAssert.as(Foo.class)) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path cannot be converted.") + void shouldFailIfCannotBeConverted(final @Mock IllegalArgumentException illegalArgumentException) { + // GIVEN + given(graphQLResponse.get(MOCK_PATH, Foo.class)).willThrow(illegalArgumentException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> graphQLFieldAssert.as(Foo.class)) + .withMessage("Expected that content of field %s can be converted to %s.", MOCK_PATH, + Foo.class) + .withCause(illegalArgumentException); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertIsNotPresentTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertIsNotPresentTest.java new file mode 100644 index 00000000..5c54cd50 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertIsNotPresentTest.java @@ -0,0 +1,46 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertIsNotPresentTest extends GraphQLFieldAssertTestBase { + + private static Stream valuesThatShouldCauseFailure() { + return Stream.of(null, NON_NULL_VALUE); + } + + @ParameterizedTest(name = "value = {0}") + @MethodSource("valuesThatShouldCauseFailure") + @DisplayName("Should fail if the value at the provided path is present.") + void shouldFailIfValueIsPresent(final String value) { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willReturn(value); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::isNotPresent) + .withMessage("Expected that field %s is not present.", MOCK_PATH); + } + + @Test + @DisplayName("Should pass if the path is not present in the response.") + void shouldPassIfNotPresent() { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willThrow(PathNotFoundException.class); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatCode(graphQLFieldAssert::isNotPresent).doesNotThrowAnyException(); + assertThat(graphQLFieldAssert.isNotPresent().and()).isSameAs(graphQLResponse); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertIsNullTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertIsNullTest.java new file mode 100644 index 00000000..b0351601 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertIsNullTest.java @@ -0,0 +1,51 @@ +package com.graphql.spring.boot.test; + +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; + +public class GraphQLFieldAssertIsNullTest extends GraphQLFieldAssertTestBase { + + @Test + @DisplayName("Should pass if the value at the provided path is null.") + void shouldPassIfNull() { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willReturn(null); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatCode(graphQLFieldAssert::isNull).doesNotThrowAnyException(); + assertThat(graphQLFieldAssert.isNull().and()).isSameAs(graphQLResponse); + } + + @Test + @DisplayName("Should fail if the value at the provided path is missing.") + void shouldFailIfPathNotFound(final @Mock PathNotFoundException pathNotFoundException) { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willThrow(pathNotFoundException); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::isNull) + .withMessage("Expected field %s to be present.", MOCK_PATH) + .withCause(pathNotFoundException); + } + + @Test + @DisplayName("Should fail if the value at the provided path is not null.") + void shouldFailIfIsNotNull() { + // GIVEN + given(graphQLResponse.getRaw(MOCK_PATH)).willReturn(NON_NULL_VALUE); + final GraphQLFieldAssert graphQLFieldAssert = new GraphQLFieldAssert(graphQLResponse, MOCK_PATH); + // WHEN - THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLFieldAssert::isNull) + .withMessage("Expected field %s to be null.", MOCK_PATH); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertTestBase.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertTestBase.java new file mode 100644 index 00000000..ad455d36 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLFieldAssertTestBase.java @@ -0,0 +1,23 @@ +package com.graphql.spring.boot.test; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class GraphQLFieldAssertTestBase { + + protected static final String MOCK_PATH = "test.path"; + protected static final String NON_NULL_VALUE = "non-null"; + + @Mock + protected GraphQLResponse graphQLResponse; + + @Data + @AllArgsConstructor + protected static class Foo { + private String bar; + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLResponseTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLResponseTest.java index cc63cf7d..f670381b 100644 --- a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLResponseTest.java +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLResponseTest.java @@ -1,29 +1,46 @@ package com.graphql.spring.boot.test; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; +import com.graphql.spring.boot.test.assertions.GraphQLErrorListAssertion; +import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert; +import com.graphql.spring.boot.test.assertions.NumberOfErrorsAssertion; +import graphql.ErrorType; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.json.JsonContentAssert; +import org.springframework.core.io.ClassPathResource; import org.springframework.http.ResponseEntity; +import org.springframework.util.StreamUtils; +import java.io.IOException; +import java.io.InputStream; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class GraphQLResponseTest { - private static final String DATA_PATH = "$.data.test"; + private static final String DATA_PATH_TEST = "$.data.test"; + private static final String INNER_FOO_LIST_DATA_PATH = "$.data.externalList[*].fooList"; + private static final String NESTED_LIST_RESPONSE_FILE = "response-with-nested-list.json"; + private static final String EMPTY_OBJECT_RESPONSE = "{}"; @Autowired private ObjectMapper objectMapper; @@ -65,17 +82,23 @@ private static Stream testGetListArguments() { ); } + private static String loadClassPathResource(final String path) { + try(InputStream resourceStream = new ClassPathResource(path).getInputStream()) { + return StreamUtils.copyToString(resourceStream, StandardCharsets.UTF_8); + } catch (IOException e) { + fail("Test setup error - failed to load test resource.", e); + return ""; + } + } + @DisplayName("Should get the JSON node's value as a String.") @ParameterizedTest @MethodSource("testGetStringArguments") - public void testGetString( - final String bodyString, - final String expected - ) { + public void testGetString(final String bodyString, final String expected) { //GIVEN - final GraphQLResponse graphQLResponse = new GraphQLResponse(ResponseEntity.ok(bodyString), objectMapper); + final GraphQLResponse graphQLResponse = createResponse(bodyString); //WHEN - final String actual = graphQLResponse.get(DATA_PATH); + final String actual = graphQLResponse.get(DATA_PATH_TEST); //THEN assertThat(actual).isEqualTo(expected); } @@ -83,15 +106,11 @@ public void testGetString( @DisplayName("Should get the JSON node's value as an instance of a specified class.") @ParameterizedTest @MethodSource("testGetArguments") - public void testGet( - final String bodyString, - final Class clazz, - final T expected - ) { + public void testGet(final String bodyString, final Class clazz, final T expected) { //GIVEN - final GraphQLResponse graphQLResponse = new GraphQLResponse(ResponseEntity.ok(bodyString), objectMapper); + final GraphQLResponse graphQLResponse = createResponse(bodyString); //WHEN - final T actual = graphQLResponse.get(DATA_PATH, clazz); + final T actual = graphQLResponse.get(DATA_PATH_TEST, clazz); //THEN assertThat(actual).isInstanceOf(clazz).isEqualTo(expected); } @@ -99,23 +118,181 @@ public void testGet( @DisplayName("Should get the JSON node's value as a List.") @ParameterizedTest @MethodSource("testGetListArguments") - public void testGetList( - final String bodyString, - final Class clazz, - final List expected - ) { + public void testGetList(final String bodyString, final Class clazz, final List expected) { //GIVEN - final GraphQLResponse graphQLResponse = new GraphQLResponse(ResponseEntity.ok(bodyString), objectMapper); + final GraphQLResponse graphQLResponse = createResponse(bodyString); //WHEN - final List actual = graphQLResponse.getList(DATA_PATH, clazz); + final List actual = graphQLResponse.getList(DATA_PATH_TEST, clazz); //THEN assertThat(actual).containsExactlyElementsOf(expected); } + @DisplayName("Should get field as defined by Jackson's JavaType.") + @Test + public void testGetAsJavaType() { + // GIVEN + final List> expected = Arrays.asList(Arrays.asList("foo1", "foo2"), + Collections.singletonList("foo3")); + final JavaType stringList = objectMapper.getTypeFactory().constructCollectionType(List.class, String.class); + final JavaType listOfStringLists = objectMapper.getTypeFactory().constructCollectionType(List.class, + stringList); + // WHEN + final List> actual = createResponse(loadClassPathResource(NESTED_LIST_RESPONSE_FILE)) + .get(INNER_FOO_LIST_DATA_PATH, listOfStringLists); + // THEN + assertThat(actual).containsExactlyElementsOf(expected); + + } + + @DisplayName("Should throw illegal argument exception if type is incompatible") + @Test + public void testGetAsJavaTypeConversionError() { + // GIVEN + final JavaType stringType = objectMapper.getTypeFactory().constructType(String.class); + // WHEN + final GraphQLResponse actual = createResponse(loadClassPathResource(NESTED_LIST_RESPONSE_FILE)); + // THEN + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> actual.get(INNER_FOO_LIST_DATA_PATH, stringType)); + + } + + @ParameterizedTest + @ValueSource(strings = { + "{\"data\": { \"foo\":\"bar\" } }", + "{\"errors\": null, \"data\": { \"foo\":\"bar\"} }", + "{\"errors\": [], \"data\": { \"foo\":\"bar\"} }" + }) + @DisplayName("Should pass the assertion if no errors are present.") + void testExpectNoErrorsPass(final String response) { + // WHEN + final GraphQLResponse graphQLResponse = createResponse(response); + // THEN + assertThat(graphQLResponse.assertThatNoErrorsArePresent()) + .as("Should not throw an exception. Should return GraphQL response instance.") + .isSameAs(graphQLResponse); + } + + @Test + @DisplayName("Should throw assertion error if an error is present in the response.") + void testExpectNoErrorsFail() { + // GIVEN + final String response = "{\"errors\": [{\"message\": \"Test error.\"}], \"data\": { \"foo\":\"bar\"} }"; + // WHEN + final GraphQLResponse graphQLResponse = createResponse(response); + // THEN + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(graphQLResponse::assertThatNoErrorsArePresent) + .withMessage("Expected no GraphQL errors, but got 1: : Test error."); + } + + @Test + @DisplayName("Should return an assertion for the number of errors.") + void testNumberOfErrorsAssertion() { + // GIVEN + final String response = "{\"errors\": [{\"message\": \"Test error.\"}, {\"message\": \"Test error 2.\"}], " + + "\"data\": { \"foo\":\"bar\"} }"; + final GraphQLResponse graphQLResponse = createResponse(response); + // WHEN + final NumberOfErrorsAssertion actual = graphQLResponse.assertThatNumberOfErrors(); + // THEN + assertThat(actual).extracting("actual").isEqualTo(2); + assertThat(actual.and()).isSameAs(graphQLResponse); + } + + @Test + @DisplayName("Should return an assertion for the list of errors.") + void testErrorListAssertion() { + // GIVEN + final String response = "{\"errors\": [{\"message\": \"Test error.\", \"errorType\": \"DataFetchingException\"}], " + + "\"data\": { \"foo\":\"bar\"} }"; + final GraphQLResponse graphQLResponse = createResponse(response); + // WHEN + final GraphQLErrorListAssertion actual = graphQLResponse.assertThatListOfErrors(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual).extracting("actual").isEqualTo(Collections.singletonList(GraphQLTestError.builder() + .message("Test error.").errorType(ErrorType.DataFetchingException).build())); + assertThat(actual.and()).isSameAs(graphQLResponse); + } + + @Test + @DisplayName("Should return an assertion for the json content of the response.") + void testFieldAssertion() { + // GIVEN + final String response = "{ \"data\": { \"foo\":\"bar\"} }"; + final GraphQLResponse graphQLResponse = createResponse(response); + // WHEN + final JsonContentAssert actual = graphQLResponse.assertThatJsonContent(); + // THEN + assertThat(actual).isNotNull(); + assertThat(actual).extracting("actual").isEqualTo(response); + } + + @Test + @DisplayName("Should return an assertion for the data field.") + void testDataFieldAssertion() { + // GIVEN + final GraphQLResponse response = createResponse(EMPTY_OBJECT_RESPONSE); + // WHEN + final GraphQLFieldAssert actual = response.assertThatDataField(); + // THEN + assertThatAssertionIsCorrect(actual, response, "$.data"); + } + + @Test + @DisplayName("Should return an assertion for the errors field.") + void testErrorFieldAssertion() { + // GIVEN + final GraphQLResponse response = createResponse(EMPTY_OBJECT_RESPONSE); + // WHEN + final GraphQLFieldAssert actual = response.assertThatErrorsField(); + // THEN + assertThatAssertionIsCorrect(actual, response, "$.errors"); + } + + @Test + @DisplayName("Should return an assertion for the extensions field.") + void testErrorExtensionsAssertion() { + // GIVEN + final GraphQLResponse response = createResponse(EMPTY_OBJECT_RESPONSE); + // WHEN + final GraphQLFieldAssert actual = response.assertThatExtensionsField(); + // THEN + assertThatAssertionIsCorrect(actual, response, "$.extensions"); + } + + @Test + @DisplayName("Should return a field assertion for the specific field.") + void testJsonAssertion() { + // GIVEN + final String path = "$any.path"; + final GraphQLResponse graphQLResponse = createResponse(EMPTY_OBJECT_RESPONSE); + // WHEN + final GraphQLFieldAssert actual = graphQLResponse.assertThatField(path); + // THEN + assertThatAssertionIsCorrect(actual, graphQLResponse, path); + } + + private GraphQLResponse createResponse(final String s) { + return new GraphQLResponse(ResponseEntity.ok(s), objectMapper); + } + + private void assertThatAssertionIsCorrect( + final GraphQLFieldAssert actual, + final GraphQLResponse expectedResponse, + final Object expectedPath + ) { + assertThat(actual).isNotNull(); + assertThat(actual).extracting("graphQLResponse", "jsonPath") + .containsExactly(expectedResponse, expectedPath); + } + @Data @AllArgsConstructor @NoArgsConstructor private static class FooBar { + private String foo; private BigDecimal bar; } diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestErrorToStringTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestErrorToStringTest.java new file mode 100644 index 00000000..8d86925a --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestErrorToStringTest.java @@ -0,0 +1,78 @@ +package com.graphql.spring.boot.test; + +import graphql.ErrorType; +import graphql.GraphQLError; +import graphql.language.SourceLocation; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.Collections; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class GraphQLTestErrorToStringTest { + + private static final String TEST_MESSAGE = "Test message"; + + private static Stream toStringTestArguments() { + return Stream.of( + Arguments.of( + new GraphQLTestError(null, null, null, null, null), + ": " + ), + Arguments.of( + new GraphQLTestError(TEST_MESSAGE, null, null, null, null), + ": Test message" + ), + Arguments.of( + new GraphQLTestError(TEST_MESSAGE, null, ErrorType.DataFetchingException, null, null), + "DataFetchingException: Test message" + ), + Arguments.of( + new GraphQLTestError(TEST_MESSAGE, Collections.emptyList(), ErrorType.DataFetchingException, null, null), + "DataFetchingException: Test message" + ), + Arguments.of( + new GraphQLTestError(TEST_MESSAGE, Collections.singletonList(new SourceLocation(1, 2)), + ErrorType.DataFetchingException, null, null), + "DataFetchingException: Test message at line 1, column 2 in unnamed/unspecified source" + ), + Arguments.of( + new GraphQLTestError(TEST_MESSAGE, Collections.singletonList(new SourceLocation(1, 2)), + ErrorType.DataFetchingException, Collections.emptyList(), null), + "DataFetchingException: Test message at line 1, column 2 in unnamed/unspecified source" + ), + Arguments.of( + new GraphQLTestError(TEST_MESSAGE, Collections.singletonList(new SourceLocation(1, 2)), + ErrorType.DataFetchingException, Arrays.asList("path", "to", "error"), null), + "DataFetchingException: Test message at line 1, column 2 in unnamed/unspecified source. Selection path: path/to/error" + ), + Arguments.of( + new GraphQLTestError(TEST_MESSAGE, Collections.singletonList(new SourceLocation(1, 2, "test.graphql")), + ErrorType.DataFetchingException, Arrays.asList("path", "to", "error"), null), + "DataFetchingException: Test message at line 1, column 2 in test.graphql. Selection path: path/to/error" + ), + Arguments.of( + new GraphQLTestError(TEST_MESSAGE, Collections.singletonList(new SourceLocation(1, 2)), + ErrorType.DataFetchingException, Arrays.asList("path", 123, "error"), null), + "DataFetchingException: Test message at line 1, column 2 in unnamed/unspecified source. Selection path: path[123]/error" + ), + Arguments.of( + new GraphQLTestError(TEST_MESSAGE, Collections.singletonList(new SourceLocation(1, 2)), + ErrorType.DataFetchingException, Arrays.asList("path", 123, "error"), Collections.singletonMap("please ignore", "this")), + "DataFetchingException: Test message at line 1, column 2 in unnamed/unspecified source. Selection path: path[123]/error" + ) + ); + } + + @DisplayName("toString should work properly") + @ParameterizedTest(name = "{1}") + @MethodSource("toStringTestArguments") + void testGraphQLTestErrorToString(final GraphQLError error, final String expectedToString) { + assertThat(error.toString()).isEqualTo(expectedToString); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestSubscriptionAwaitAndGetResponseTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestSubscriptionAwaitAndGetResponseTest.java index 1a7cb94f..901d33b8 100644 --- a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestSubscriptionAwaitAndGetResponseTest.java +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestSubscriptionAwaitAndGetResponseTest.java @@ -18,11 +18,11 @@ public class GraphQLTestSubscriptionAwaitAndGetResponseTest extends GraphQLTestS @DisplayName("Should await and get single response.") void shouldAwaitAndGetResponse() { // WHEN - final GraphQLResponse graphQLResponse = graphQLTestSubscription + graphQLTestSubscription .start(TIMER_SUBSCRIPTION_RESOURCE) - .awaitAndGetNextResponse(TIMEOUT); + .awaitAndGetNextResponse(TIMEOUT) + .assertThatField(DATA_TIMER_FIELD).asLong().isZero(); // THEN - assertThat(graphQLResponse.get(DATA_TIMER_FIELD, Long.class)).isEqualTo(0); assertThatSubscriptionWasStopped(); } @@ -78,9 +78,9 @@ void shouldWorkWithMultipleAwaitAndGetCalls() { graphQLTestSubscription.start(TIMER_SUBSCRIPTION_RESOURCE); // WHEN - final GraphQLResponse graphQLResponse = graphQLTestSubscription.awaitAndGetNextResponse(TIMEOUT, false); + graphQLTestSubscription.awaitAndGetNextResponse(TIMEOUT, false).assertThatField(DATA_TIMER_FIELD) + .asLong().isZero(); // THEN - assertThat(graphQLResponse.get(DATA_TIMER_FIELD, Long.class)).isEqualTo(0); assertThatSubscriptionWasNotStopped(); // WHEN @@ -106,12 +106,11 @@ void shouldHandleSubscriptionWithParameters() { // GIVEN final String param = String.valueOf(UUID.randomUUID()); final Map startPayload = Collections.singletonMap("param", param); - // WHEN - final GraphQLResponse graphQLResponse = graphQLTestSubscription + // WHEN - THEN + graphQLTestSubscription .start(SUBSCRIPTION_WITH_PARAMETER_RESOURCE, startPayload) - .awaitAndGetNextResponse(TIMEOUT); - // THEN - assertThat(graphQLResponse.get(DATA_SUBSCRIPTION_WITH_PARAMETER_FIELD)).isEqualTo(param); + .awaitAndGetNextResponse(TIMEOUT) + .assertThatField(DATA_SUBSCRIPTION_WITH_PARAMETER_FIELD).asString().isEqualTo(param); } @Test @@ -120,12 +119,11 @@ void shouldSubscriptionWithInitPayload() { // GIVEN final String initParamValue = String.valueOf(UUID.randomUUID()); final Map initPayload = Collections.singletonMap("initParamValue", initParamValue); - // WHEN - final GraphQLResponse graphQLResponse = graphQLTestSubscription + // WHEN - THEN + graphQLTestSubscription .init(initPayload) .start(SUBSCRIPTION_WITH_INIT_PAYLOAD_RESOURCE) - .awaitAndGetNextResponse(TIMEOUT); - // THEN - assertThat(graphQLResponse.get(DATA_SUBSCRIPTION_WITH_INIT_PAYLOAD_FIELD)).isEqualTo(initParamValue); + .awaitAndGetNextResponse(TIMEOUT) + .assertThatField(DATA_SUBSCRIPTION_WITH_INIT_PAYLOAD_FIELD).asString().isEqualTo(initParamValue); } } diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestSubscriptionErrorTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestSubscriptionErrorTest.java index 22964b6d..8738490c 100644 --- a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestSubscriptionErrorTest.java +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestSubscriptionErrorTest.java @@ -11,10 +11,11 @@ public class GraphQLTestSubscriptionErrorTest extends GraphQLTestSubscriptionTes @Test @DisplayName("Should handle error messages.") void shouldHandleErrorMessages() { - // WHEN - final GraphQLResponse graphQLResponse = graphQLTestSubscription.start(SUBSCRIPTION_THAT_THROWS_EXCEPTION) - .awaitAndGetNextResponse(TIMEOUT); - // THEN - assertThat(graphQLResponse.get("$.errors[0].message")).isNotBlank(); + // WHEN - THEN + graphQLTestSubscription.start(SUBSCRIPTION_THAT_THROWS_EXCEPTION) + .awaitAndGetNextResponse(TIMEOUT) + .assertThatDataField().isNotPresent() + .and() + .assertThatListOfErrors().isNotEmpty(); } } diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestTemplateIntegrationTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestTemplateIntegrationTest.java new file mode 100644 index 00000000..6d58ee87 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestTemplateIntegrationTest.java @@ -0,0 +1,184 @@ +package com.graphql.spring.boot.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.graphql.spring.boot.test.beans.FooBar; +import graphql.GraphQLError; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.io.ResourceLoader; +import org.springframework.http.HttpHeaders; + +import java.awt.*; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class GraphQLTestTemplateIntegrationTest { + + private static final String SIMPLE_TEST_QUERY = "simple-test-query.graphql"; + private static final String SIMPLE_TEST_QUERY_WITH_FRAGMENTS = "simple-test-query-with-fragments.graphql"; + private static final String TEST_FRAGMENT_FILE = "foo-bar-fragment.graphql"; + private static final String QUERY_WITH_VARIABLES = "query-with-variables.graphql"; + private static final String COMPLEX_TEST_QUERY = "complex-query.graphql"; + private static final String MULTIPLE_QUERIES = "multiple-queries.graphql"; + private static final String INPUT_STRING_VALUE = "input-value"; + private static final String INPUT_STRING_NAME = "input"; + private static final String INPUT_HEADER_NAME = "headerName"; + private static final String TEST_HEADER_NAME = "x-test"; + private static final String TEST_HEADER_VALUE = String.valueOf(UUID.randomUUID()); + private static final String FOO = "FOO"; + private static final String BAR = "BAR"; + private static final String TEST = "TEST"; + private static final String DATA_FIELD_FOO_BAR = "$.data.fooBar"; + private static final String DATA_FIELD_QUERY_WITH_VARIABLES = "$.data.queryWithVariables"; + private static final String DATA_FIELD_OTHER_QUERY = "$.data.otherQuery"; + private static final String DATA_FIELD_QUERY_WITH_HEADER = "$.data.queryWithHeader"; + private static final String DATA_FIELD_DUMMY = "$.data.dummy"; + private static final String OPERATION_NAME_WITH_VARIABLES = "withVariable"; + private static final String OPERATION_NAME_TEST_QUERY_1 = "testQuery1"; + private static final String OPERATION_NAME_TEST_QUERY_2 = "testQuery2"; + private static final String OPERATION_NAME_COMPLEX_QUERY = "complexQuery"; + private static final String GRAPHQL_ENDPOINT = "/graphql"; + + @Autowired + private ResourceLoader resourceLoader; + + @Autowired + private TestRestTemplate testRestTemplate; + + @Autowired + private ObjectMapper objectMapper; + + private GraphQLTestTemplate graphQLTestTemplate; + + @BeforeEach + void setUp() { + graphQLTestTemplate = new GraphQLTestTemplate(resourceLoader, testRestTemplate, GRAPHQL_ENDPOINT, objectMapper); + } + + @Test + @DisplayName("Test postForResource with only the GraphQL resource provided.") + void testPostForResource() throws IOException { + graphQLTestTemplate.postForResource(SIMPLE_TEST_QUERY) + .assertThatNoErrorsArePresent() + .assertThatField(DATA_FIELD_OTHER_QUERY).asString().isEqualTo(TEST); + } + + @Test + @DisplayName("Test postForResource with fragments.") + void testPostForResourceWithFragments() throws IOException { + graphQLTestTemplate.postForResource(SIMPLE_TEST_QUERY_WITH_FRAGMENTS, + Collections.singletonList(TEST_FRAGMENT_FILE)) + .assertThatNoErrorsArePresent() + .assertThatField(DATA_FIELD_FOO_BAR).as(FooBar.class) + .usingRecursiveComparison() + .ignoringAllOverriddenEquals() + .isEqualTo(FooBar.builder().foo(FOO).bar(BAR).build()); + } + + @Test + @DisplayName("Test perform with variables.") + void testPerformWithVariables() throws IOException { + // GIVEN + final ObjectNode variables = objectMapper.createObjectNode(); + variables.put(INPUT_STRING_NAME, INPUT_STRING_VALUE); + // WHEN - THEN + graphQLTestTemplate.perform(QUERY_WITH_VARIABLES, variables) + .assertThatNoErrorsArePresent() + .assertThatField(DATA_FIELD_QUERY_WITH_VARIABLES).asString().isEqualTo(INPUT_STRING_VALUE); + } + + @Test + @DisplayName("Test perform with variables and operation name") + void testPerformWithOperationAndVariables() throws IOException { + // GIVEN + final ObjectNode variables = objectMapper.createObjectNode(); + variables.put(INPUT_STRING_NAME, INPUT_STRING_VALUE); + // WHEN - THEN + graphQLTestTemplate.perform(MULTIPLE_QUERIES, OPERATION_NAME_WITH_VARIABLES, variables) + .assertThatNoErrorsArePresent() + .assertThatField(DATA_FIELD_QUERY_WITH_VARIABLES).asString().isEqualTo(INPUT_STRING_VALUE); + } + + @Test + @DisplayName("Test perform with variables and fragments") + void testPerformWithVariablesAndFragments() throws IOException { + // GIVEN + final FooBar expected = new FooBar(String.valueOf(UUID.randomUUID()), String.valueOf(UUID.randomUUID())); + final ObjectNode variables = objectMapper.valueToTree(expected); + // WHEN - THEN + graphQLTestTemplate + .perform(SIMPLE_TEST_QUERY_WITH_FRAGMENTS, variables, Collections.singletonList(TEST_FRAGMENT_FILE)) + .assertThatNoErrorsArePresent() + .assertThatField(DATA_FIELD_FOO_BAR) + .as(FooBar.class).usingRecursiveComparison().ignoringAllOverriddenEquals().isEqualTo(expected); + } + + @Test + @DisplayName("Test perform with operation name.") + void testPerformWithOperationName() throws IOException { + // WHEN - THEN + graphQLTestTemplate.perform(MULTIPLE_QUERIES, OPERATION_NAME_TEST_QUERY_1) + .assertThatNoErrorsArePresent() + .assertThatField(DATA_FIELD_DUMMY).asBoolean().isTrue(); + graphQLTestTemplate.perform(MULTIPLE_QUERIES, OPERATION_NAME_TEST_QUERY_2) + .assertThatNoErrorsArePresent() + .assertThatField(DATA_FIELD_OTHER_QUERY).asString().isEqualTo(TEST); + } + + @Test + @DisplayName("Test perform with GraphQL errors.") + void testPerformWithGraphQLError() throws IOException { + graphQLTestTemplate.postForResource(SIMPLE_TEST_QUERY, Collections.singletonList(TEST_FRAGMENT_FILE)) + .assertThatDataField().isNotPresentOrNull() + .and().assertThatNumberOfErrors().isOne() + .and().assertThatListOfErrors().extracting(GraphQLError::getMessage) + .allMatch(message -> message.contains("UnusedFragment")); + } + + @Test + @DisplayName("Test perform with all possible inputs.") + void testPerformWithAllInputs() throws IOException { + // GIVEN + final ObjectNode variables = objectMapper.createObjectNode(); + variables.put(INPUT_STRING_NAME, INPUT_STRING_VALUE); + variables.put(INPUT_HEADER_NAME, TEST_HEADER_NAME); + final HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(TEST_HEADER_NAME, TEST_HEADER_VALUE); + // WHEN - THEN + graphQLTestTemplate + .withHeaders(httpHeaders) + .perform(COMPLEX_TEST_QUERY, OPERATION_NAME_COMPLEX_QUERY, variables, + Collections.singletonList(TEST_FRAGMENT_FILE)) + .assertThatNoErrorsArePresent() + .assertThatField(DATA_FIELD_QUERY_WITH_HEADER).asString().isEqualTo(TEST_HEADER_VALUE) + .and().assertThatField(DATA_FIELD_QUERY_WITH_VARIABLES).asString().isEqualTo(INPUT_STRING_VALUE) + .and().assertThatField(DATA_FIELD_FOO_BAR).as(FooBar.class).isEqualTo(new FooBar(FOO, BAR)); + } + + @Test + @DisplayName("Test post with custom payload.") + void testPost() { + // GIVEN + final HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(TEST_HEADER_NAME, TEST_HEADER_VALUE); + final String payload = "{\"query\":" + + "\"query ($input: String!, $headerName: String!) " + + "{ queryWithVariables(input: $input) queryWithHeader(headerName: $headerName) }\", " + + "\"variables\": {\"input\": \"input-value\", \"headerName\": \"x-test\"}}"; + // WHEN - THEN + graphQLTestTemplate + .withHeaders(httpHeaders) + .post(payload) + .assertThatNoErrorsArePresent() + .assertThatField(DATA_FIELD_QUERY_WITH_VARIABLES).asString().isEqualTo(INPUT_STRING_VALUE) + .and().assertThatField(DATA_FIELD_QUERY_WITH_HEADER).asString().isEqualTo(TEST_HEADER_VALUE); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestTemplateTest.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestTemplateTest.java new file mode 100644 index 00000000..56f5b030 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/GraphQLTestTemplateTest.java @@ -0,0 +1,189 @@ +package com.graphql.spring.boot.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.io.ResourceLoader; +import org.springframework.http.HttpHeaders; + +import java.nio.charset.StandardCharsets; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) +public class GraphQLTestTemplateTest { + + private static final String GRAPHQL_ENDPOINT = "/test-graphql-endpoint"; + private static final String HEADER_NAME_1 = "header-1"; + private static final String HEADER_VALUE_1 = "value-1"; + private static final String HEADER_NAME_2 = "header-2"; + private static final String HEADER_VALUE_2 = "value-2"; + private static final String MOCK_BEARER_TOKEN = "mock token"; + private static final String TEST_USERNAME = "test"; + private static final String TEST_PASSWORD = "value"; + private static final String ENCODED_BASIC_AUTH = "dGVzdDp2YWx1ZQ=="; + private static final String BASIC_AUTH_PREFIX = "Basic "; + private static final String BEARER_AUTH_PREFIX = "Bearer "; + + @Mock + private ResourceLoader resourceLoader; + + @Mock + private TestRestTemplate testRestTemplate; + + private GraphQLTestTemplate graphQLTestTemplate; + + @BeforeEach + void setUp() { + graphQLTestTemplate = new GraphQLTestTemplate(resourceLoader, testRestTemplate, GRAPHQL_ENDPOINT, + new ObjectMapper()); + } + + @Test + void testAddHeader() { + // WHEN + graphQLTestTemplate.addHeader(HEADER_NAME_1, HEADER_VALUE_1); + graphQLTestTemplate.addHeader(HEADER_NAME_2, HEADER_VALUE_2); + // THEN + assertThatContainsAllHeaders(); + } + + @Test + void testWithAdditionalHeader() { + // WHEN + final GraphQLTestTemplate actual = graphQLTestTemplate + .withAdditionalHeader(HEADER_NAME_1, HEADER_VALUE_1) + .withAdditionalHeader(HEADER_NAME_2, HEADER_VALUE_2); + // THEN + assertThat(actual).isSameAs(graphQLTestTemplate); + assertThatContainsAllHeaders(); + } + + @Test + void testWithAdditionalHeaders() { + // GIVEN + graphQLTestTemplate.getHeaders().add(HEADER_NAME_1, HEADER_VALUE_1); + final HttpHeaders additionalHeaders = new HttpHeaders(); + additionalHeaders.add(HEADER_NAME_2, HEADER_VALUE_2); + // WHEN + final GraphQLTestTemplate actualGraphqlTestTemplate = graphQLTestTemplate + .withAdditionalHeaders(additionalHeaders); + // THEN + assertThat(actualGraphqlTestTemplate).isSameAs(graphQLTestTemplate); + assertThatContainsAllHeaders(); + } + + @Test + void testWithHeaders() { + // GIVEN + graphQLTestTemplate.getHeaders().add(HEADER_NAME_1, HEADER_VALUE_1); + final HttpHeaders newHeaders = new HttpHeaders(); + newHeaders.add(HEADER_NAME_2, HEADER_VALUE_2); + // WHEN + final GraphQLTestTemplate actual = graphQLTestTemplate + .withHeaders(newHeaders); + // THEN + assertThat(actual).isSameAs(graphQLTestTemplate); + assertThat(actual.getHeaders()).hasSize(1); + assertThatContainsSecondHeader(); + } + + @Test + void testSetHeaders() { + // GIVEN + graphQLTestTemplate.getHeaders().add(HEADER_NAME_1, HEADER_VALUE_1); + final HttpHeaders newHeaders = new HttpHeaders(); + newHeaders.add(HEADER_NAME_2, HEADER_VALUE_2); + // WHEN + graphQLTestTemplate.setHeaders(newHeaders); + // THEN + assertThat(graphQLTestTemplate.getHeaders()).hasSize(1); + assertThatContainsSecondHeader(); + } + + @Test + void testWithClearHeaders() { + // GIVEN + graphQLTestTemplate.getHeaders().add(HEADER_NAME_1, HEADER_VALUE_1); + // WHEN + final GraphQLTestTemplate actual = graphQLTestTemplate.withClearHeaders(); + // THEN + assertThat(actual).isSameAs(actual); + assertThat(graphQLTestTemplate.getHeaders()).isEmpty(); + } + + @Test + void testClearHeaders() { + // GIVEN + graphQLTestTemplate.getHeaders().add(HEADER_NAME_1, HEADER_VALUE_1); + // WHEN + graphQLTestTemplate.clearHeaders(); + // THEN + assertThat(graphQLTestTemplate.getHeaders()).isEmpty(); + } + + @Test + void testWithBearerAuth() { + // WHEN + final GraphQLTestTemplate actual = graphQLTestTemplate.withBearerAuth(MOCK_BEARER_TOKEN); + // THEN + assertThat(actual).isSameAs(graphQLTestTemplate); + assertAuthHeader(BEARER_AUTH_PREFIX, MOCK_BEARER_TOKEN); + } + + @Test + void testWithEncodedBasicAuth() { + // WHEN + final GraphQLTestTemplate actual = graphQLTestTemplate.withBasicAuth(ENCODED_BASIC_AUTH); + // THEN + assertThat(actual).isSameAs(graphQLTestTemplate); + assertAuthHeader(BASIC_AUTH_PREFIX, ENCODED_BASIC_AUTH); + } + + @Test + void testWithBasicAuth() { + // WHEN + final GraphQLTestTemplate actual = graphQLTestTemplate.withBasicAuth(TEST_USERNAME, TEST_PASSWORD); + // THEN + assertThat(actual).isSameAs(graphQLTestTemplate); + assertAuthHeader(BASIC_AUTH_PREFIX, ENCODED_BASIC_AUTH); + } + + @Test + void testWithBasicAuthCharset() { + // WHEN + final GraphQLTestTemplate actual = graphQLTestTemplate.withBasicAuth(TEST_USERNAME, TEST_PASSWORD, + StandardCharsets.UTF_8); + // THEN + assertThat(actual).isSameAs(graphQLTestTemplate); + assertAuthHeader(BASIC_AUTH_PREFIX, ENCODED_BASIC_AUTH); + } + + private void assertAuthHeader(final String authPrefix, final String authValue) { + assertThatContainsHeader(HttpHeaders.AUTHORIZATION, authPrefix + authValue); + } + + private void assertThatContainsAllHeaders() { + final HttpHeaders actual = graphQLTestTemplate.getHeaders(); + assertThat(actual).hasSize(2); + assertThatContainsFirstHeader(); + assertThatContainsSecondHeader(); + } + + private void assertThatContainsSecondHeader() { + assertThatContainsHeader(HEADER_NAME_2, HEADER_VALUE_2); + } + + private void assertThatContainsFirstHeader() { + assertThatContainsHeader(HEADER_NAME_1, HEADER_VALUE_1); + } + + private void assertThatContainsHeader(final String headerName, final String headerValue) { + assertThat(graphQLTestTemplate.getHeaders()).containsEntry(headerName, Collections.singletonList(headerValue)); + } +} diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/beans/DummyQuery.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/beans/DummyQuery.java index d0265039..74ccb2bf 100644 --- a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/beans/DummyQuery.java +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/beans/DummyQuery.java @@ -1,12 +1,39 @@ package com.graphql.spring.boot.test.beans; +import graphql.kickstart.servlet.context.GraphQLServletContext; import graphql.kickstart.tools.GraphQLQueryResolver; +import graphql.schema.DataFetchingEnvironment; import org.springframework.stereotype.Service; +import java.util.Optional; + @Service public class DummyQuery implements GraphQLQueryResolver { public boolean dummy() { return true; } + + public String otherQuery() { + return "TEST"; + } + + public FooBar fooBar(String foo, String bar) { + return FooBar.builder() + .bar(Optional.ofNullable(bar).orElse("BAR")) + .foo(Optional.ofNullable(foo).orElse("FOO")) + .build(); + } + + public String queryWithVariables(final String input) { + return input; + } + + public String queryWithHeader( + final String headerName, + final DataFetchingEnvironment dataFetchingEnvironment + ) { + return ((GraphQLServletContext) dataFetchingEnvironment.getContext()).getHttpServletRequest() + .getHeader(headerName); + } } diff --git a/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/beans/FooBar.java b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/beans/FooBar.java new file mode 100644 index 00000000..92270d70 --- /dev/null +++ b/graphql-spring-boot-test/src/test/java/com/graphql/spring/boot/test/beans/FooBar.java @@ -0,0 +1,15 @@ +package com.graphql.spring.boot.test.beans; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FooBar { + private String foo; + private String bar; +} diff --git a/graphql-spring-boot-test/src/test/resources/complex-query.graphql b/graphql-spring-boot-test/src/test/resources/complex-query.graphql new file mode 100644 index 00000000..0f649544 --- /dev/null +++ b/graphql-spring-boot-test/src/test/resources/complex-query.graphql @@ -0,0 +1,11 @@ +query simpleUnusedQuery { + dummy +} + +query complexQuery($input: String!, $headerName: String!) { + queryWithHeader(headerName: $headerName) + queryWithVariables(input: $input) + fooBar { + ...FooBarFragment + } +} \ No newline at end of file diff --git a/graphql-spring-boot-test/src/test/resources/foo-bar-fragment.graphql b/graphql-spring-boot-test/src/test/resources/foo-bar-fragment.graphql new file mode 100644 index 00000000..14bb907d --- /dev/null +++ b/graphql-spring-boot-test/src/test/resources/foo-bar-fragment.graphql @@ -0,0 +1,4 @@ +fragment FooBarFragment on FooBar { + foo + bar +} \ No newline at end of file diff --git a/graphql-spring-boot-test/src/test/resources/multiple-queries.graphql b/graphql-spring-boot-test/src/test/resources/multiple-queries.graphql new file mode 100644 index 00000000..084a8f2a --- /dev/null +++ b/graphql-spring-boot-test/src/test/resources/multiple-queries.graphql @@ -0,0 +1,11 @@ +query testQuery1 { + dummy +} + +query testQuery2 { + otherQuery +} + +query withVariable($input: String!) { + queryWithVariables(input: $input) +} \ No newline at end of file diff --git a/graphql-spring-boot-test/src/test/resources/query-with-variables.graphql b/graphql-spring-boot-test/src/test/resources/query-with-variables.graphql new file mode 100644 index 00000000..a9f32b1b --- /dev/null +++ b/graphql-spring-boot-test/src/test/resources/query-with-variables.graphql @@ -0,0 +1,3 @@ +query ($input: String!) { + queryWithVariables(input: $input) +} \ No newline at end of file diff --git a/graphql-spring-boot-test/src/test/resources/response-with-nested-list.json b/graphql-spring-boot-test/src/test/resources/response-with-nested-list.json new file mode 100644 index 00000000..ffac2f16 --- /dev/null +++ b/graphql-spring-boot-test/src/test/resources/response-with-nested-list.json @@ -0,0 +1,16 @@ +{ + "data": { + "externalList": [ + { + "fooList": [ + "foo1", "foo2" + ] + }, + { + "fooList": [ + "foo3" + ] + } + ] + } +} \ No newline at end of file diff --git a/graphql-spring-boot-test/src/test/resources/simple-test-query-with-fragments.graphql b/graphql-spring-boot-test/src/test/resources/simple-test-query-with-fragments.graphql new file mode 100644 index 00000000..1a7ab64a --- /dev/null +++ b/graphql-spring-boot-test/src/test/resources/simple-test-query-with-fragments.graphql @@ -0,0 +1,5 @@ +query($foo: String, $bar: String) { + fooBar(foo: $foo, bar: $bar) { + ...FooBarFragment + } +} \ No newline at end of file diff --git a/graphql-spring-boot-test/src/test/resources/simple-test-query.graphql b/graphql-spring-boot-test/src/test/resources/simple-test-query.graphql new file mode 100644 index 00000000..56f54e9c --- /dev/null +++ b/graphql-spring-boot-test/src/test/resources/simple-test-query.graphql @@ -0,0 +1,3 @@ +query { + otherQuery +} \ No newline at end of file diff --git a/graphql-spring-boot-test/src/test/resources/test-schema.graphqls b/graphql-spring-boot-test/src/test/resources/test-schema.graphqls index 2136b747..d373ef51 100644 --- a/graphql-spring-boot-test/src/test/resources/test-schema.graphqls +++ b/graphql-spring-boot-test/src/test/resources/test-schema.graphqls @@ -1,3 +1,8 @@ +type FooBar { + foo: String! + bar: String! +} + type Subscription { timer: Float! subscriptionWithParameter(param: String!): String! @@ -8,4 +13,8 @@ type Subscription { type Query { dummy: Boolean! + otherQuery: String! + fooBar(foo: String, bar: String): FooBar! + queryWithVariables(input: String!): String! + queryWithHeader(headerName: String!): String } \ No newline at end of file