This is an automated email from the ASF dual-hosted git repository.
apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 8f4cc43563 IGNITE-23195 AssertionError is not handled in REST API
(#4383)
8f4cc43563 is described below
commit 8f4cc435631221a116270a1c45f54612687e0cde
Author: Vadim Pakhnushev <[email protected]>
AuthorDate: Mon Sep 16 17:38:57 2024 +0300
IGNITE-23195 AssertionError is not handled in REST API (#4383)
---
.../handler/IgniteCliApiExceptionHandler.java | 1 +
modules/rest-api/build.gradle | 5 +
.../internal/rest/api/GeneralErrorsController.java | 2 +-
.../exception/handler/JavaExceptionHandler.java | 8 +-
.../rest/exception/handler/ErrorHandlingTest.java | 256 ++++++++-------------
.../rest/matcher/MicronautHttpResponseMatcher.java | 61 ++++-
.../internal/rest/matcher/ProblemMatcher.java | 4 +-
.../internal/rest/matcher/RestJobStateMatcher.java | 0
modules/rest/build.gradle | 8 +-
...terStateHttpServerFilterNotInitializedTest.java | 27 +--
.../rest/compute/ItComputeControllerTest.java | 29 +--
11 files changed, 191 insertions(+), 210 deletions(-)
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
index 8fd9befcec..f400f50a65 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
@@ -115,6 +115,7 @@ public class IgniteCliApiExceptionHandler implements
ExceptionHandler<IgniteCliA
try {
return objectMapper.readValue(responseBody, Problem.class);
} catch (JsonProcessingException ex) {
+ LOG.error("Failed to extract problem from body {}", ex,
responseBody);
throw new RuntimeException(ex);
}
}
diff --git a/modules/rest-api/build.gradle b/modules/rest-api/build.gradle
index ac99e8020b..d3b12fa70d 100644
--- a/modules/rest-api/build.gradle
+++ b/modules/rest-api/build.gradle
@@ -18,6 +18,7 @@
apply from: "$rootDir/buildscripts/java-core.gradle"
apply from: "$rootDir/buildscripts/publishing.gradle"
apply from: "$rootDir/buildscripts/java-junit5.gradle"
+apply from: "$rootDir/buildscripts/java-test-fixtures.gradle"
description = 'ignite-rest-api'
@@ -50,6 +51,10 @@ dependencies {
testImplementation libs.micronaut.http.server.netty
testImplementation libs.hamcrest.core
testImplementation libs.hamcrest.optional
+
+ testFixturesImplementation testFixtures(project(":ignite-core"))
+ testFixturesImplementation libs.hamcrest.core
+ testFixturesImplementation libs.micronaut.http.core
}
ext {
diff --git
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/GeneralErrorsController.java
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/GeneralErrorsController.java
index 56c5a1e457..f03e735a25 100644
---
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/GeneralErrorsController.java
+++
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/GeneralErrorsController.java
@@ -60,7 +60,7 @@ public class GeneralErrorsController {
public HttpResponse<? extends Problem> unsupportedMediaType(HttpRequest<?>
request) {
return HttpProblemResponse.from(
Problem.fromHttpCode(HttpCode.UNSUPPORTED_MEDIA_TYPE)
- .detail("Unsupported media type: " +
request.getContentType().map(MediaType::getType).orElse(null))
+ .detail("Unsupported media type: " +
request.getContentType().map(MediaType::getName).orElse(null))
);
}
diff --git
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/exception/handler/JavaExceptionHandler.java
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/exception/handler/JavaExceptionHandler.java
index e60ee1858b..563524d09e 100644
---
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/exception/handler/JavaExceptionHandler.java
+++
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/exception/handler/JavaExceptionHandler.java
@@ -29,16 +29,16 @@ import org.apache.ignite.internal.rest.constants.HttpCode;
import org.apache.ignite.internal.rest.problem.HttpProblemResponse;
/**
- * Handles {@link Exception} and represents it as an application/problem+json
response. This will catch all unhandled exceptions since all
+ * Handles {@link Throwable} and represents it as an application/problem+json
response. This will catch all unhandled exceptions since all
* REST endpoints are marked as producing problem json.
*/
@Singleton
-@Requires(classes = {Exception.class, ExceptionHandler.class})
-public class JavaExceptionHandler implements ExceptionHandler<Exception,
HttpResponse<? extends Problem>> {
+@Requires(classes = {Throwable.class, ExceptionHandler.class})
+public class JavaExceptionHandler implements ExceptionHandler<Throwable,
HttpResponse<? extends Problem>> {
private static final IgniteLogger LOG =
Loggers.forClass(JavaExceptionHandler.class);
@Override
- public HttpResponse<? extends Problem> handle(HttpRequest request,
Exception exception) {
+ public HttpResponse<? extends Problem> handle(HttpRequest request,
Throwable exception) {
LOG.error("Unhandled exception", exception);
return HttpProblemResponse.from(
Problem.fromHttpCode(HttpCode.INTERNAL_SERVER_ERROR)
diff --git
a/modules/rest-api/src/test/java/org/apache/ignite/internal/rest/exception/handler/ErrorHandlingTest.java
b/modules/rest-api/src/test/java/org/apache/ignite/internal/rest/exception/handler/ErrorHandlingTest.java
index cec54b7532..730dc1ee79 100644
---
a/modules/rest-api/src/test/java/org/apache/ignite/internal/rest/exception/handler/ErrorHandlingTest.java
+++
b/modules/rest-api/src/test/java/org/apache/ignite/internal/rest/exception/handler/ErrorHandlingTest.java
@@ -17,16 +17,20 @@
package org.apache.ignite.internal.rest.exception.handler;
-import static org.apache.ignite.internal.rest.constants.HttpCode.BAD_REQUEST;
-import static
org.apache.ignite.internal.rest.constants.HttpCode.METHOD_NOT_ALLOWED;
-import static org.apache.ignite.internal.rest.constants.HttpCode.NOT_FOUND;
-import static
org.apache.ignite.internal.rest.constants.HttpCode.UNSUPPORTED_MEDIA_TYPE;
-import static
org.apache.ignite.internal.rest.problem.ProblemJsonMediaType.APPLICATION_JSON_PROBLEM_TYPE;
+import static io.micronaut.http.HttpStatus.BAD_REQUEST;
+import static io.micronaut.http.HttpStatus.INTERNAL_SERVER_ERROR;
+import static io.micronaut.http.HttpStatus.METHOD_NOT_ALLOWED;
+import static io.micronaut.http.HttpStatus.NOT_FOUND;
+import static io.micronaut.http.HttpStatus.UNAUTHORIZED;
+import static io.micronaut.http.HttpStatus.UNSUPPORTED_MEDIA_TYPE;
+import static
org.apache.ignite.internal.rest.matcher.MicronautHttpResponseMatcher.isProblemResponse;
+import static org.apache.ignite.internal.rest.matcher.ProblemMatcher.isProblem;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
@@ -34,7 +38,7 @@ import io.micronaut.context.annotation.Property;
import io.micronaut.core.bind.exceptions.UnsatisfiedArgumentException;
import io.micronaut.core.type.Argument;
import io.micronaut.http.HttpRequest;
-import io.micronaut.http.HttpResponse;
+import io.micronaut.http.HttpStatus;
import io.micronaut.http.MutableHttpRequest;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.annotation.Client;
@@ -52,9 +56,11 @@ import
org.apache.ignite.internal.lang.IgniteInternalException;
import org.apache.ignite.internal.rest.api.InvalidParam;
import org.apache.ignite.internal.rest.api.Problem;
import org.apache.ignite.internal.rest.constants.MediaType;
+import org.apache.ignite.internal.rest.matcher.ProblemMatcher;
import org.apache.ignite.lang.IgniteException;
-import org.junit.jupiter.api.Assertions;
+import org.hamcrest.Matcher;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@@ -70,143 +76,99 @@ public class ErrorHandlingTest {
@Client("/test")
HttpClient client;
- private final AtomicReference<Throwable> throwable = new
AtomicReference<>(new RuntimeException());
+ private static final AtomicReference<Throwable> throwable = new
AtomicReference<>(new RuntimeException());
private static Stream<Arguments> testExceptions() {
return Stream.of(
// couldn't find a case when exception is thrown
- Arguments.of(new
UnsatisfiedArgumentException(Argument.DOUBLE)),
+ arguments(new UnsatisfiedArgumentException(Argument.DOUBLE),
BAD_REQUEST,
+ "Bad Request", "Required argument [double] not
specified"),
// thrown when request uri is invalid, but it's not possible
to create such request with HttpClient (it validates uri)
- Arguments.of(new URISyntaxException("uri", "reason")),
- Arguments.of(new
AuthenticationException("authentication-exception")),
- Arguments.of(new AuthorizationException(null)),
- Arguments.of(new IgniteException("ignite-exception")),
- Arguments.of(new
IgniteInternalCheckedException("ignite-internal-exception")),
- Arguments.of(new
IgniteInternalException("ignite-internal-exception")),
- Arguments.of(new RuntimeException("runtime-exception")),
- Arguments.of(new Exception("exception"))
+ arguments(new URISyntaxException("uri", "reason"),
BAD_REQUEST, "Malformed URI", "reason: uri"),
+ arguments(new
AuthenticationException("authentication-exception"), UNAUTHORIZED,
+ "Unauthorized", "authentication-exception"),
+ arguments(new AuthorizationException(null), UNAUTHORIZED,
"Unauthorized", null),
+ arguments(new IgniteException("ignite-exception"),
INTERNAL_SERVER_ERROR, "Internal Server Error", "ignite-exception"),
+ arguments(new
IgniteInternalCheckedException("ignite-internal-exception"),
INTERNAL_SERVER_ERROR,
+ "Internal Server Error", "ignite-internal-exception"),
+ arguments(new
IgniteInternalException("ignite-internal-exception"), INTERNAL_SERVER_ERROR,
+ "Internal Server Error", "ignite-internal-exception"),
+ arguments(new RuntimeException("runtime-exception"),
INTERNAL_SERVER_ERROR, "Internal Server Error", "runtime-exception"),
+ arguments(new Exception("exception"), INTERNAL_SERVER_ERROR,
"Internal Server Error", "exception"),
+ // Can't test for the AssertionError because TC will fail if
this text is logged. We log unhandled exception in the
+ // JavaExceptionHandler
+ // arguments(new AssertionError("assert"),
INTERNAL_SERVER_ERROR, "Internal Server Error", "assert"),
+ arguments(new Throwable("assert"), INTERNAL_SERVER_ERROR,
"Internal Server Error", "assert")
);
}
@ParameterizedTest
@MethodSource("testExceptions")
- public void testExceptions(Throwable throwable) {
- this.throwable.set(throwable);
+ public void testExceptions(Throwable throwable, HttpStatus status, String
title, String detail) {
+ ErrorHandlingTest.throwable.set(throwable);
- // Invoke endpoint with not allowed method
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () -> client.toBlocking().exchange("/test/throw-exception")
- );
-
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
-
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
- assertEquals(response.code(), problem.status());
- assertNotNull(problem.title());
+ assertThrowsProblem(() ->
client.toBlocking().exchange("/throw-exception"), status, title, detail);
}
@Test
public void endpoint404() {
// Invoke non-existing endpoint
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () -> client.toBlocking().exchange("/endpoint404")
+ assertThrowsProblem(
+ () -> client.toBlocking().exchange("/endpoint404"),
+ NOT_FOUND,
+ "Not Found",
+ "Requested resource not found: /test/endpoint404"
);
-
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
-
- assertEquals(NOT_FOUND.code(), response.status().getCode());
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
-
- assertEquals(NOT_FOUND.code(), problem.status());
- assertEquals("Not Found", problem.title());
- assertEquals("Requested resource not found: /test/endpoint404",
problem.detail());
}
@Test
public void invalidDataTypePathVariable() {
// Invoke endpoint with wrong path variable data type
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () -> client.toBlocking().exchange("/list/abc")
+ assertThrowsProblem(
+ () -> client.toBlocking().exchange("/list/abc"),
+ BAD_REQUEST,
+ "Invalid parameter",
+ "Failed to convert argument [id] for value [abc] due to: For
input string: \"abc\""
);
-
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
-
- assertEquals(BAD_REQUEST.code(), response.status().getCode());
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
-
- assertEquals(BAD_REQUEST.code(), problem.status());
- assertEquals("Invalid parameter", problem.title());
- assertEquals("Failed to convert argument [id] for value [abc] due to:
For input string: \"abc\"", problem.detail());
}
@Test
public void requiredQueryValueNotSpecified() {
// Invoke endpoint without required query value
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () -> client.toBlocking().exchange("/list")
+ assertThrowsProblem(
+ () -> client.toBlocking().exchange("/list"),
+ BAD_REQUEST,
+ "Bad Request",
+ "Required QueryValue [greatThan] not specified"
);
-
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
-
- assertEquals(BAD_REQUEST.code(), response.status().getCode());
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
-
- assertEquals(BAD_REQUEST.code(), problem.status());
- assertEquals("Bad Request", problem.title());
- assertEquals("Required QueryValue [greatThan] not specified",
problem.detail());
}
@Test
public void invalidTypeQueryValue() {
// Invoke endpoint with wrong data type of request argument
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () -> client.toBlocking().exchange("/list?greatThan=abc")
+ assertThrowsProblem(
+ () -> client.toBlocking().exchange("/list?greatThan=abc"),
+ BAD_REQUEST,
+ "Invalid parameter",
+ "Failed to convert argument [greatThan] for value [abc] due
to: For input string: \"abc\""
);
-
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
-
- assertEquals(BAD_REQUEST.code(), response.status().getCode());
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
-
- assertEquals(BAD_REQUEST.code(), problem.status());
- assertEquals("Invalid parameter", problem.title());
- assertEquals("Failed to convert argument [greatThan] for value [abc]
due to: For input string: \"abc\"", problem.detail());
}
@Test
public void invalidTypeQueryValue1() {
// Invoke endpoint with wrong request argument values
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () ->
client.toBlocking().exchange("/list?greatThan=-1&lessThan=11")
+ assertThrowsProblem(
+ () ->
client.toBlocking().exchange("/list?greatThan=-1&lessThan=11"),
+ BAD_REQUEST,
+ isProblem()
+ .withStatus(BAD_REQUEST.getCode())
+ .withTitle("Bad Request")
+ .withDetail("Validation failed")
+ .withInvalidParams(containsInAnyOrder(
+ new InvalidParam("list.greatThan", "greatThan:
must be greater than or equal to 0"),
+ new InvalidParam("list.lessThan", "lessThan:
must be less than or equal to 10")
+ ))
);
-
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
-
- assertEquals(BAD_REQUEST.code(), response.status().getCode());
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
-
- assertEquals(BAD_REQUEST.code(), problem.status());
- assertEquals("Bad Request", problem.title());
- assertEquals("Validation failed", problem.detail());
-
- assertEquals(2, problem.invalidParams().size());
-
- assertThat(problem.invalidParams(), containsInAnyOrder(
- new InvalidParam("list.greatThan", "greatThan: must be greater
than or equal to 0"),
- new InvalidParam("list.lessThan", "lessThan: must be less than
or equal to 10")
- ));
}
@Test
@@ -216,20 +178,12 @@ public class ErrorHandlingTest {
.contentType(MediaType.TEXT_PLAIN)
.body("text='qwe'");
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () -> client.toBlocking().exchange(request,
Argument.of(EchoMessage.class), Argument.of(Problem.class))
+ assertThrowsProblem(
+ () -> client.toBlocking().exchange(request,
Argument.of(EchoMessage.class), Argument.of(Problem.class)),
+ UNSUPPORTED_MEDIA_TYPE,
+ "Unsupported Media Type",
+ "Unsupported media type: text/plain"
);
-
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
-
- assertEquals(UNSUPPORTED_MEDIA_TYPE.code(),
response.status().getCode());
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
-
- assertEquals(UNSUPPORTED_MEDIA_TYPE.code(), problem.status());
- assertEquals("Unsupported Media Type", problem.title());
- assertEquals("Unsupported media type: text", problem.detail());
}
@Test
@@ -239,20 +193,12 @@ public class ErrorHandlingTest {
.contentType(MediaType.APPLICATION_JSON)
.body("{text='qwe'");
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () -> client.toBlocking().exchange(request,
Argument.of(EchoMessage.class), Argument.of(Problem.class))
+ assertThrowsProblem(
+ () -> client.toBlocking().exchange(request,
Argument.of(EchoMessage.class), Argument.of(Problem.class)),
+ BAD_REQUEST,
+ "Invalid JSON",
+ containsString("Unexpected character")
);
-
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
-
- assertEquals(BAD_REQUEST.code(), response.status().getCode());
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
-
- assertEquals(BAD_REQUEST.code(), problem.status());
- assertEquals("Invalid JSON", problem.title());
- assertThat(problem.detail(), containsString("Unexpected character"));
}
@Test
@@ -262,20 +208,12 @@ public class ErrorHandlingTest {
.contentType(MediaType.APPLICATION_JSON)
.body("");
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () -> client.toBlocking().exchange(request,
Argument.of(EchoMessage.class), Argument.of(Problem.class))
+ assertThrowsProblem(
+ () -> client.toBlocking().exchange(request,
Argument.of(EchoMessage.class), Argument.of(Problem.class)),
+ BAD_REQUEST,
+ "Bad Request",
+ containsString("Required Body [dto] not specified")
);
-
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
-
- assertEquals(BAD_REQUEST.code(), response.status().getCode());
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
-
- assertEquals(BAD_REQUEST.code(), problem.status());
- assertEquals("Bad Request", problem.title());
- assertThat(problem.detail(), containsString("Required Body [dto] not
specified"));
}
@Test
@@ -283,20 +221,30 @@ public class ErrorHandlingTest {
// Invoke endpoint with not allowed method
MutableHttpRequest<String> request =
HttpRequest.GET(UriBuilder.of("/echo").build());
- HttpClientResponseException thrown = Assertions.assertThrows(
- HttpClientResponseException.class,
- () -> client.toBlocking().exchange(request,
Argument.of(EchoMessage.class), Argument.of(Problem.class))
+ assertThrowsProblem(
+ () -> client.toBlocking().exchange(request,
Argument.of(EchoMessage.class), Argument.of(Problem.class)),
+ METHOD_NOT_ALLOWED,
+ "Method Not Allowed",
+ "Method not allowed: GET"
);
+ }
- HttpResponse<?> response = thrown.getResponse();
- Problem problem = response.getBody(Problem.class).get();
+ private static void assertThrowsProblem(Executable executable, HttpStatus
status, String title, String detail) {
+ assertThrowsProblem(executable, status, title, equalTo(detail));
+ }
+
+ private static void assertThrowsProblem(Executable executable, HttpStatus
status, String title, Matcher<String> detailMatcher) {
+ assertThrowsProblem(executable, status, isProblem()
+ .withStatus(status.getCode())
+ .withTitle(title)
+ .withDetail(detailMatcher)
+ );
+ }
- assertEquals(METHOD_NOT_ALLOWED.code(), response.status().getCode());
- assertEquals(APPLICATION_JSON_PROBLEM_TYPE.getType(),
response.getContentType().get().getType());
+ private static void assertThrowsProblem(Executable executable, HttpStatus
status, ProblemMatcher problemMatcher) {
+ HttpClientResponseException thrown =
assertThrows(HttpClientResponseException.class, executable);
- assertEquals(METHOD_NOT_ALLOWED.code(), problem.status());
- assertEquals("Method Not Allowed", problem.title());
- assertEquals("Method not allowed: GET", problem.detail());
+ assertThat(thrown.getResponse(), isProblemResponse(status,
problemMatcher));
}
@Bean
diff --git
a/modules/rest/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/MicronautHttpResponseMatcher.java
b/modules/rest-api/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/MicronautHttpResponseMatcher.java
similarity index 62%
rename from
modules/rest/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/MicronautHttpResponseMatcher.java
rename to
modules/rest-api/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/MicronautHttpResponseMatcher.java
index d1163b55b7..e6825e4140 100644
---
a/modules/rest/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/MicronautHttpResponseMatcher.java
+++
b/modules/rest-api/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/MicronautHttpResponseMatcher.java
@@ -17,11 +17,15 @@
package org.apache.ignite.internal.rest.matcher;
+import static
org.apache.ignite.internal.rest.problem.ProblemJsonMediaType.APPLICATION_JSON_PROBLEM_TYPE;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
+import io.micronaut.http.MediaType;
+import java.util.Optional;
+import org.apache.ignite.internal.rest.api.Problem;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
@@ -34,7 +38,9 @@ public class MicronautHttpResponseMatcher<T> extends
TypeSafeMatcher<HttpRespons
private Matcher<T> bodyMatcher;
- private Class<T> body;
+ private Class<T> bodyClass;
+
+ private Matcher<String> mediaTypeMatcher;
private MicronautHttpResponseMatcher(Matcher<Integer> statusCodeMatcher) {
this.statusCodeMatcher = statusCodeMatcher;
@@ -60,6 +66,18 @@ public class MicronautHttpResponseMatcher<T> extends
TypeSafeMatcher<HttpRespons
return new MicronautHttpResponseMatcher<>(is(statusCode));
}
+ /**
+ * Creates a matcher that matches when the examined {@link HttpResponse}
is a problem json that matches the specified matcher.
+ *
+ * @param problemMatcher Expected problem.
+ * @return Matcher.
+ */
+ public static MicronautHttpResponseMatcher<Problem>
isProblemResponse(HttpStatus status, ProblemMatcher problemMatcher) {
+ return MicronautHttpResponseMatcher.<Problem>hasStatus(status)
+ .withMediaType(APPLICATION_JSON_PROBLEM_TYPE)
+ .withBody(problemMatcher.withStatus(status.getCode()),
Problem.class);
+ }
+
/**
* Sets the expected body.
*
@@ -68,7 +86,7 @@ public class MicronautHttpResponseMatcher<T> extends
TypeSafeMatcher<HttpRespons
*/
public MicronautHttpResponseMatcher<T> withBody(T body) {
this.bodyMatcher = equalTo(body);
- this.body = (Class<T>) body.getClass();
+ this.bodyClass = (Class<T>) body.getClass();
return this;
}
@@ -76,12 +94,23 @@ public class MicronautHttpResponseMatcher<T> extends
TypeSafeMatcher<HttpRespons
* Sets the body matcher.
*
* @param bodyMatcher Body matcher.
- * @param body Body class.
+ * @param bodyClass Body class.
* @return Matcher.
*/
- public MicronautHttpResponseMatcher<T> withBody(Matcher<T> bodyMatcher,
Class<T> body) {
+ public MicronautHttpResponseMatcher<T> withBody(Matcher<T> bodyMatcher,
Class<T> bodyClass) {
this.bodyMatcher = bodyMatcher;
- this.body = body;
+ this.bodyClass = bodyClass;
+ return this;
+ }
+
+ /**
+ * Sets the media type.
+ *
+ * @param mediaType Media type.
+ * @return Matcher.
+ */
+ public MicronautHttpResponseMatcher<T> withMediaType(MediaType mediaType) {
+ this.mediaTypeMatcher = equalTo(mediaType.getName());
return this;
}
@@ -91,8 +120,18 @@ public class MicronautHttpResponseMatcher<T> extends
TypeSafeMatcher<HttpRespons
return false;
}
- if (bodyMatcher != null &&
!bodyMatcher.matches(httpResponse.getBody(body).get())) {
- return false;
+ if (bodyMatcher != null) {
+ Optional<T> body = httpResponse.getBody(bodyClass);
+ if (body.isEmpty() || !bodyMatcher.matches(body.get())) {
+ return false;
+ }
+ }
+
+ if (mediaTypeMatcher != null) {
+ Optional<MediaType> contentType = httpResponse.getContentType();
+ if (contentType.isEmpty() ||
!mediaTypeMatcher.matches(contentType.get().getName())) {
+ return false;
+ }
}
return true;
@@ -107,6 +146,10 @@ public class MicronautHttpResponseMatcher<T> extends
TypeSafeMatcher<HttpRespons
if (bodyMatcher != null) {
description.appendText(" and body
").appendDescriptionOf(bodyMatcher);
}
+
+ if (mediaTypeMatcher != null) {
+ description.appendText(" and content type
").appendDescriptionOf(mediaTypeMatcher);
+ }
}
@Override
@@ -114,6 +157,8 @@ public class MicronautHttpResponseMatcher<T> extends
TypeSafeMatcher<HttpRespons
mismatchDescription.appendText("status code was ")
.appendValue(item.code())
.appendText(" and body was ")
- .appendValue(item.getBody(String.class));
+ .appendValue(item.getBody(String.class))
+ .appendText(" and content type was ")
+
.appendValue(item.getContentType().map(MediaType::getName).orElse(null));
}
}
diff --git
a/modules/rest/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/ProblemMatcher.java
b/modules/rest-api/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/ProblemMatcher.java
similarity index 96%
rename from
modules/rest/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/ProblemMatcher.java
rename to
modules/rest-api/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/ProblemMatcher.java
index 4faf8040db..9be1f99f20 100644
---
a/modules/rest/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/ProblemMatcher.java
+++
b/modules/rest-api/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/ProblemMatcher.java
@@ -46,7 +46,7 @@ public class ProblemMatcher extends TypeSafeMatcher<Problem> {
private Matcher<UUID> traceIdMatcher = AnythingMatcher.anything();
- private Matcher<Collection<InvalidParam>> invalidParamsMatcher =
AnythingMatcher.anything();
+ private Matcher<Iterable<? extends InvalidParam>> invalidParamsMatcher =
AnythingMatcher.anything();
/**
* Creates a matcher for {@link Problem}.
@@ -124,7 +124,7 @@ public class ProblemMatcher extends
TypeSafeMatcher<Problem> {
return withInvalidParams(equalTo(invalidParams));
}
- public ProblemMatcher withInvalidParams(Matcher<Collection<InvalidParam>>
matcher) {
+ public ProblemMatcher withInvalidParams(Matcher<Iterable<? extends
InvalidParam>> matcher) {
this.invalidParamsMatcher = matcher;
return this;
}
diff --git
a/modules/rest/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/RestJobStateMatcher.java
b/modules/rest-api/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/RestJobStateMatcher.java
similarity index 100%
rename from
modules/rest/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/RestJobStateMatcher.java
rename to
modules/rest-api/src/testFixtures/java/org/apache/ignite/internal/rest/matcher/RestJobStateMatcher.java
diff --git a/modules/rest/build.gradle b/modules/rest/build.gradle
index 626317f38a..e8f8f1f9cd 100644
--- a/modules/rest/build.gradle
+++ b/modules/rest/build.gradle
@@ -18,7 +18,6 @@
apply from: "$rootDir/buildscripts/java-core.gradle"
apply from: "$rootDir/buildscripts/publishing.gradle"
apply from: "$rootDir/buildscripts/java-junit5.gradle"
-apply from: "$rootDir/buildscripts/java-test-fixtures.gradle"
apply from: "$rootDir/buildscripts/java-integration-test.gradle"
description = 'ignite-rest'
@@ -85,7 +84,7 @@ dependencies {
integrationTestImplementation
testFixtures(project(':ignite-cluster-management'))
integrationTestImplementation
testFixtures(project(':ignite-configuration'))
integrationTestImplementation testFixtures(project(":ignite-api"))
- integrationTestImplementation testFixtures(project(":ignite-rest"))
+ integrationTestImplementation testFixtures(project(":ignite-rest-api"))
integrationTestImplementation libs.awaitility
integrationTestImplementation libs.micronaut.junit5
integrationTestImplementation libs.micronaut.test
@@ -98,9 +97,4 @@ dependencies {
//So, exclude asm-core transitive dependency to protect of jar-hell.
exclude group: 'org.ow2.asm', module: 'asm'
}
-
- testFixturesImplementation(project(":ignite-rest-api"))
- testFixturesImplementation testFixtures(project(":ignite-core"))
- testFixturesImplementation libs.hamcrest.core
- testFixturesImplementation libs.micronaut.http.core
}
diff --git
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
index e95d02d88b..1d8c5d54fe 100644
---
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
+++
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
@@ -20,13 +20,12 @@ package org.apache.ignite.internal.rest;
import static io.micronaut.http.HttpRequest.GET;
import static io.micronaut.http.HttpRequest.PATCH;
import static io.micronaut.http.HttpStatus.CONFLICT;
-import static
org.apache.ignite.internal.rest.problem.ProblemJsonMediaType.APPLICATION_JSON_PROBLEM_TYPE;
+import static
org.apache.ignite.internal.rest.matcher.MicronautHttpResponseMatcher.isProblemResponse;
+import static org.apache.ignite.internal.rest.matcher.ProblemMatcher.isProblem;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.client.HttpClient;
@@ -37,7 +36,6 @@ import jakarta.inject.Inject;
import java.util.stream.Stream;
import org.apache.ignite.internal.Cluster;
import org.apache.ignite.internal.ClusterPerClassIntegrationTest;
-import org.apache.ignite.internal.rest.api.Problem;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.params.ParameterizedTest;
@@ -92,24 +90,19 @@ public class
ItClusterStateHttpServerFilterNotInitializedTest extends ClusterPer
@ParameterizedTest
@MethodSource("disabledEndpoints")
- void clusterEndpointsDisabledWhenNotInitialized(HttpRequest<String>
request) throws JsonProcessingException {
+ void clusterEndpointsDisabledWhenNotInitialized(HttpRequest<String>
request) {
HttpClientResponseException ex = assertThrows(
HttpClientResponseException.class,
() -> client.toBlocking().exchange(request)
);
- assertThat(ex.getStatus(), is(CONFLICT));
-
- assertThat(ex.getResponse().getContentType().orElseThrow().getName(),
is(APPLICATION_JSON_PROBLEM_TYPE.getName()));
- Problem problem = readProblem(ex);
-
- assertThat(problem.status(), is(CONFLICT.getCode()));
- assertThat(problem.title(), is("Cluster is not initialized"));
- assertThat(problem.detail(), is("Cluster is not initialized. Call
/management/v1/cluster/init in order to initialize cluster."));
- }
-
- private Problem readProblem(HttpClientResponseException ex) throws
JsonProcessingException {
- return mapper.readValue(ex.getResponse().getBody(String.class).get(),
Problem.class);
+ assertThat(
+ ex.getResponse(),
+ isProblemResponse(CONFLICT, isProblem()
+ .withTitle("Cluster is not initialized")
+ .withDetail("Cluster is not initialized. Call
/management/v1/cluster/init in order to initialize cluster.")
+ )
+ );
}
@ParameterizedTest
diff --git
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/compute/ItComputeControllerTest.java
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/compute/ItComputeControllerTest.java
index 5f63384c40..e00dab44f7 100644
---
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/compute/ItComputeControllerTest.java
+++
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/compute/ItComputeControllerTest.java
@@ -19,7 +19,10 @@ package org.apache.ignite.internal.rest.compute;
import static io.micronaut.http.HttpRequest.DELETE;
import static io.micronaut.http.HttpRequest.PUT;
+import static io.micronaut.http.HttpStatus.CONFLICT;
+import static io.micronaut.http.HttpStatus.NOT_FOUND;
import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl;
+import static
org.apache.ignite.internal.rest.matcher.MicronautHttpResponseMatcher.isProblemResponse;
import static org.apache.ignite.internal.rest.matcher.ProblemMatcher.isProblem;
import static
org.apache.ignite.internal.rest.matcher.RestJobStateMatcher.canceled;
import static
org.apache.ignite.internal.rest.matcher.RestJobStateMatcher.completed;
@@ -50,10 +53,8 @@ import org.apache.ignite.compute.JobExecution;
import org.apache.ignite.compute.JobExecutionContext;
import org.apache.ignite.compute.JobTarget;
import org.apache.ignite.internal.ClusterPerClassIntegrationTest;
-import org.apache.ignite.internal.rest.api.Problem;
import org.apache.ignite.internal.rest.api.compute.JobState;
import org.apache.ignite.internal.rest.api.compute.UpdateJobPriorityBody;
-import org.apache.ignite.internal.rest.matcher.MicronautHttpResponseMatcher;
import org.apache.ignite.network.ClusterNode;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@@ -180,8 +181,7 @@ public class ItComputeControllerTest extends
ClusterPerClassIntegrationTest {
assertThat(
httpClientResponseException.getResponse(),
- MicronautHttpResponseMatcher.<Problem>hasStatusCode(404)
-
.withBody(isProblem().withStatus(404).withDetail("Compute job not found
[jobId=" + jobId + "]"), Problem.class)
+ isProblemResponse(NOT_FOUND, isProblem().withDetail("Compute
job not found [jobId=" + jobId + "]"))
);
}
@@ -226,8 +226,7 @@ public class ItComputeControllerTest extends
ClusterPerClassIntegrationTest {
assertThat(
httpClientResponseException.getResponse(),
- MicronautHttpResponseMatcher.<Problem>hasStatusCode(404)
-
.withBody(isProblem().withStatus(404).withDetail("Compute job not found
[jobId=" + jobId + "]"), Problem.class)
+ isProblemResponse(NOT_FOUND, isProblem().withDetail("Compute
job not found [jobId=" + jobId + "]"))
);
}
@@ -252,9 +251,8 @@ public class ItComputeControllerTest extends
ClusterPerClassIntegrationTest {
assertThat(
httpClientResponseException.getResponse(),
- MicronautHttpResponseMatcher.<Problem>hasStatusCode(409)
- .withBody(isProblem().withStatus(409)
- .withDetail("Compute job has an illegal status
[jobId=" + jobId + ", status=COMPLETED]"), Problem.class)
+ isProblemResponse(CONFLICT, isProblem()
+ .withDetail("Compute job has an illegal status
[jobId=" + jobId + ", status=COMPLETED]"))
);
}
@@ -311,8 +309,7 @@ public class ItComputeControllerTest extends
ClusterPerClassIntegrationTest {
assertThat(
httpClientResponseException.getResponse(),
- MicronautHttpResponseMatcher.<Problem>hasStatusCode(404)
-
.withBody(isProblem().withStatus(404).withDetail("Compute job not found
[jobId=" + jobId + "]"), Problem.class)
+ isProblemResponse(NOT_FOUND, isProblem().withDetail("Compute
job not found [jobId=" + jobId + "]"))
);
}
@@ -335,9 +332,8 @@ public class ItComputeControllerTest extends
ClusterPerClassIntegrationTest {
assertThat(
httpClientResponseException.getResponse(),
- MicronautHttpResponseMatcher.<Problem>hasStatusCode(409)
- .withBody(isProblem().withStatus(409)
- .withDetail("Compute job has an illegal status
[jobId=" + jobId + ", status=EXECUTING]"), Problem.class)
+ isProblemResponse(CONFLICT, isProblem()
+ .withDetail("Compute job has an illegal status
[jobId=" + jobId + ", status=EXECUTING]"))
);
}
@@ -364,9 +360,8 @@ public class ItComputeControllerTest extends
ClusterPerClassIntegrationTest {
assertThat(
httpClientResponseException.getResponse(),
- MicronautHttpResponseMatcher.<Problem>hasStatusCode(409)
- .withBody(isProblem().withStatus(409)
- .withDetail("Compute job has an illegal status
[jobId=" + jobId + ", status=COMPLETED]"), Problem.class)
+ isProblemResponse(CONFLICT, isProblem()
+ .withDetail("Compute job has an illegal status
[jobId=" + jobId + ", status=COMPLETED]"))
);
}