This is an automated email from the ASF dual-hosted git repository.
roryqi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 4ac4ea6002 [#14240] feat(api): add ServiceUnavailableException mapped
to HTTP 503 (#14241)
4ac4ea6002 is described below
commit 4ac4ea60026a3fc1e58d3346a9916feda95304b1
Author: MaSai <[email protected]>
AuthorDate: Thu Oct 29 09:54:14 2026 +0800
[#14240] feat(api): add ServiceUnavailableException mapped to HTTP 503
(#14241)
### What changes were proposed in this pull request?
- Add `org.apache.gravitino.exceptions.ServiceUnavailableException`.
- Add `ErrorConstants.SERVICE_UNAVAILABLE_CODE` (1013),
`ErrorResponse.serviceUnavailable(...)`, and
`Utils.serviceUnavailable(message[, throwable])` returning **HTTP 503**.
- Map the exception in `ExceptionHandlers` (`BaseExceptionHandler`,
catalog, and secret handlers).
- Reconstruct `ServiceUnavailableException` in the Java client
`ErrorHandlers` / `RestErrorHandler`.
### Why are the changes needed?
`ConnectionFailedException` maps to HTTP 502, which fits federated
catalog backends. Temporarily unavailable dependencies (for example a
secret provider) should surface as **HTTP 503** so callers can tell
retryable outages from gateway failures and from 500s.
Fix: #14240
### Does this PR introduce any user-facing change?
Yes:
- New public exception type and REST error code.
- APIs that throw `ServiceUnavailableException` return HTTP 503 with a
structured `ErrorResponse`.
### How was this patch tested?
```
./gradlew :common:test --tests
'org.apache.gravitino.dto.responses.TestResponses' \
:server-common:test --tests 'org.apache.gravitino.server.web.TestUtils' \
:server:test --tests
'org.apache.gravitino.server.web.rest.TestExceptionHandlers' \
:clients:client-java:test --tests
'org.apache.gravitino.client.TestErrorHandlers'
```
---------
Co-authored-by: Cursor <[email protected]>
---
.../exceptions/ServiceUnavailableException.java | 49 ++++++++++++++++++++++
.../org/apache/gravitino/client/ErrorHandlers.java | 4 ++
.../apache/gravitino/client/TestErrorHandlers.java | 28 ++++++++++++-
clients/client-python/gravitino/constants/error.py | 5 +++
clients/client-python/gravitino/exceptions/base.py | 4 ++
.../tests/unittests/test_error_handler.py | 25 +++++++++++
.../gravitino/dto/responses/ErrorConstants.java | 3 ++
.../gravitino/dto/responses/ErrorResponse.java | 26 ++++++++++++
.../gravitino/dto/responses/TestResponses.java | 1 +
.../org/apache/gravitino/server/web/Utils.java | 14 +++++++
.../org/apache/gravitino/server/web/TestUtils.java | 10 +++++
.../server/web/rest/ExceptionHandlers.java | 29 ++++++++++++-
.../server/web/rest/TestExceptionHandlers.java | 18 ++++++++
13 files changed, 213 insertions(+), 3 deletions(-)
diff --git
a/api/src/main/java/org/apache/gravitino/exceptions/ServiceUnavailableException.java
b/api/src/main/java/org/apache/gravitino/exceptions/ServiceUnavailableException.java
new file mode 100644
index 0000000000..40e6b7d2a5
--- /dev/null
+++
b/api/src/main/java/org/apache/gravitino/exceptions/ServiceUnavailableException.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.exceptions;
+
+import com.google.errorprone.annotations.FormatMethod;
+import com.google.errorprone.annotations.FormatString;
+
+/** An exception thrown when a configured dependency is temporarily
unavailable. */
+public class ServiceUnavailableException extends GravitinoRuntimeException {
+ /**
+ * Constructs a new exception with the specified detail message.
+ *
+ * @param cause the cause.
+ * @param errorMessageTemplate the detail message.
+ * @param args the arguments to the message.
+ */
+ @FormatMethod
+ public ServiceUnavailableException(
+ Throwable cause, @FormatString String errorMessageTemplate, Object...
args) {
+ super(cause, errorMessageTemplate, args);
+ }
+
+ /**
+ * Constructs a new exception with the specified detail message.
+ *
+ * @param errorMessageTemplate the detail message.
+ * @param args the arguments to the message.
+ */
+ @FormatMethod
+ public ServiceUnavailableException(@FormatString String
errorMessageTemplate, Object... args) {
+ super(errorMessageTemplate, args);
+ }
+}
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
index 6b35b5e016..35cb952990 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
@@ -83,6 +83,7 @@ import org.apache.gravitino.exceptions.RESTException;
import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
import org.apache.gravitino.exceptions.SchemaAlreadyExistsException;
import org.apache.gravitino.exceptions.SemanticModelAlreadyExistsException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
import org.apache.gravitino.exceptions.TableAlreadyExistsException;
import org.apache.gravitino.exceptions.TagAlreadyAssociatedException;
import org.apache.gravitino.exceptions.TagAlreadyExistsException;
@@ -1439,6 +1440,9 @@ public class ErrorHandlers {
if (errorResponse.getCode() ==
ErrorConstants.OPTIMISTIC_LOCK_CONFLICT_CODE) {
throw new OptimisticLockException("%s",
formatErrorMessage(errorResponse));
}
+ if (errorResponse.getCode() == ErrorConstants.SERVICE_UNAVAILABLE_CODE) {
+ throw new ServiceUnavailableException("%s",
formatErrorMessage(errorResponse));
+ }
if (errorResponse.getCode() == ErrorConstants.CONNECTION_FAILED_CODE) {
throw new ConnectionFailedException("%s",
formatErrorMessage(errorResponse));
}
diff --git
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestErrorHandlers.java
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestErrorHandlers.java
index 0416faca8c..9723345fa5 100644
---
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestErrorHandlers.java
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestErrorHandlers.java
@@ -26,12 +26,38 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.apache.gravitino.dto.responses.ErrorResponse;
import org.apache.gravitino.exceptions.OptimisticLockException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-/** Tests REST error handling for optimistic-lock conflicts. */
+/** Tests REST error handling for cross-cutting error codes. */
public class TestErrorHandlers {
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testServiceUnavailableAcrossHandlers() throws
ReflectiveOperationException {
+ ErrorResponse response =
+ ErrorResponse.serviceUnavailable("secret provider 'vault' is
unavailable");
+ List<Method> factories =
+ Arrays.stream(ErrorHandlers.class.getDeclaredMethods())
+ .filter(method -> Modifier.isPublic(method.getModifiers()))
+ .filter(method -> Modifier.isStatic(method.getModifiers()))
+ .filter(method -> method.getName().endsWith("ErrorHandler"))
+ .filter(method -> method.getParameterCount() == 0)
+ .filter(method ->
Consumer.class.isAssignableFrom(method.getReturnType()))
+ .collect(Collectors.toList());
+ Assertions.assertFalse(factories.isEmpty());
+
+ for (Method factory : factories) {
+ Consumer<ErrorResponse> handler = (Consumer<ErrorResponse>)
factory.invoke(null);
+ ServiceUnavailableException exception =
+ Assertions.assertThrows(
+ ServiceUnavailableException.class, () ->
handler.accept(response), factory.getName());
+ Assertions.assertEquals(
+ "secret provider 'vault' is unavailable", exception.getMessage(),
factory.getName());
+ }
+ }
+
@Test
@SuppressWarnings("unchecked")
public void testOptimisticLockConflictAcrossHandlers() throws
ReflectiveOperationException {
diff --git a/clients/client-python/gravitino/constants/error.py
b/clients/client-python/gravitino/constants/error.py
index c63b8a9d09..6c31185220 100644
--- a/clients/client-python/gravitino/constants/error.py
+++ b/clients/client-python/gravitino/constants/error.py
@@ -30,6 +30,7 @@ from gravitino.exceptions.base import (
NotInUseException,
InUseException,
OptimisticLockException,
+ ServiceUnavailableException,
UnauthorizedException,
)
@@ -76,6 +77,9 @@ class ErrorConstants(IntEnum):
# Error codes for optimistic-lock conflicts.
OPTIMISTIC_LOCK_CONFLICT_CODE = 1012
+ # Error codes for service unavailable.
+ SERVICE_UNAVAILABLE_CODE = 1013
+
# Error codes for invalid state.
UNKNOWN_ERROR_CODE = 1100
@@ -93,6 +97,7 @@ EXCEPTION_MAPPING = {
NotInUseException: ErrorConstants.NOT_IN_USE_CODE,
InUseException: ErrorConstants.IN_USE_CODE,
OptimisticLockException: ErrorConstants.OPTIMISTIC_LOCK_CONFLICT_CODE,
+ ServiceUnavailableException: ErrorConstants.SERVICE_UNAVAILABLE_CODE,
UnauthorizedException: ErrorConstants.UNAUTHORIZED_CODE,
}
diff --git a/clients/client-python/gravitino/exceptions/base.py
b/clients/client-python/gravitino/exceptions/base.py
index d632747d83..db9061ad65 100644
--- a/clients/client-python/gravitino/exceptions/base.py
+++ b/clients/client-python/gravitino/exceptions/base.py
@@ -157,6 +157,10 @@ class ConnectionFailedException(GravitinoRuntimeException):
"""An exception thrown when connect to catalog failed."""
+class ServiceUnavailableException(GravitinoRuntimeException):
+ """An exception thrown when a configured dependency is temporarily
unavailable."""
+
+
class OptimisticLockException(GravitinoRuntimeException):
"""Raised when another update changes an entity before a write
completes."""
diff --git a/clients/client-python/tests/unittests/test_error_handler.py
b/clients/client-python/tests/unittests/test_error_handler.py
index 9ccc2e370a..f2bf283627 100644
--- a/clients/client-python/tests/unittests/test_error_handler.py
+++ b/clients/client-python/tests/unittests/test_error_handler.py
@@ -59,6 +59,7 @@ from gravitino.exceptions.base import (
RoleAlreadyExistsException,
SchemaAlreadyExistsException,
SemanticModelAlreadyExistsException,
+ ServiceUnavailableException,
TableAlreadyExistsException,
UnauthorizedException,
UnsupportedOperationException,
@@ -203,6 +204,30 @@ class TestErrorHandler(unittest.TestCase):
):
handler.handle(response)
+ def test_service_unavailable(self):
+ response = ErrorResponse.from_json(
+ '{"code":1013,"type":"ServiceUnavailableException",'
+ '"message":"Service unavailable","stack":null}'
+ )
+ generated = ErrorResponse.generate_error_response(
+ ServiceUnavailableException, "Service unavailable"
+ )
+ self.assertEqual(1013, generated.code())
+
+ for handler in (
+ REST_ERROR_HANDLER,
+ TABLE_ERROR_HANDLER,
+ VIEW_ERROR_HANDLER,
+ PARTITION_ERROR_HANDLER,
+ STATISTICS_ERROR_HANDLER,
+ CATALOG_ERROR_HANDLER,
+ ):
+ with self.subTest(handler=type(handler).__name__):
+ with self.assertRaisesRegex(
+ ServiceUnavailableException, "Service unavailable"
+ ):
+ handler.handle(response)
+
def test_rest_error_handler(self):
with self.assertRaises(RESTException):
REST_ERROR_HANDLER.handle(
diff --git
a/common/src/main/java/org/apache/gravitino/dto/responses/ErrorConstants.java
b/common/src/main/java/org/apache/gravitino/dto/responses/ErrorConstants.java
index 8b94b0df3e..8a241fb211 100644
---
a/common/src/main/java/org/apache/gravitino/dto/responses/ErrorConstants.java
+++
b/common/src/main/java/org/apache/gravitino/dto/responses/ErrorConstants.java
@@ -60,6 +60,9 @@ public class ErrorConstants {
/** Error codes for optimistic-lock conflicts. */
public static final int OPTIMISTIC_LOCK_CONFLICT_CODE = 1012;
+ /** Error codes for service unavailable. */
+ public static final int SERVICE_UNAVAILABLE_CODE = 1013;
+
/** Error codes for invalid state. */
public static final int UNKNOWN_ERROR_CODE = 1100;
diff --git
a/common/src/main/java/org/apache/gravitino/dto/responses/ErrorResponse.java
b/common/src/main/java/org/apache/gravitino/dto/responses/ErrorResponse.java
index f537ccf6ee..59be69c809 100644
--- a/common/src/main/java/org/apache/gravitino/dto/responses/ErrorResponse.java
+++ b/common/src/main/java/org/apache/gravitino/dto/responses/ErrorResponse.java
@@ -30,6 +30,7 @@ import lombok.Getter;
import org.apache.gravitino.exceptions.ConnectionFailedException;
import org.apache.gravitino.exceptions.ForbiddenException;
import org.apache.gravitino.exceptions.RESTException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
/** Represents an error response. */
@Getter
@@ -161,6 +162,31 @@ public class ErrorResponse extends BaseResponse {
getStackTrace(throwable));
}
+ /**
+ * Create a new service unavailable error instance of {@link ErrorResponse}.
+ *
+ * @param message The message of the error.
+ * @return The new instance.
+ */
+ public static ErrorResponse serviceUnavailable(String message) {
+ return serviceUnavailable(message, null);
+ }
+
+ /**
+ * Create a new service unavailable error instance of {@link ErrorResponse}.
+ *
+ * @param message The message of the error.
+ * @param throwable The throwable that caused the error.
+ * @return The new instance.
+ */
+ public static ErrorResponse serviceUnavailable(String message, Throwable
throwable) {
+ return new ErrorResponse(
+ ErrorConstants.SERVICE_UNAVAILABLE_CODE,
+ ServiceUnavailableException.class.getSimpleName(),
+ message,
+ getStackTrace(throwable));
+ }
+
/**
* Create a new not found error instance of {@link ErrorResponse}.
*
diff --git
a/common/src/test/java/org/apache/gravitino/dto/responses/TestResponses.java
b/common/src/test/java/org/apache/gravitino/dto/responses/TestResponses.java
index ec58f95593..235181c25f 100644
--- a/common/src/test/java/org/apache/gravitino/dto/responses/TestResponses.java
+++ b/common/src/test/java/org/apache/gravitino/dto/responses/TestResponses.java
@@ -243,6 +243,7 @@ public class TestResponses {
ErrorResponse[] responses = {
ErrorResponse.illegalArguments(message, throwable),
ErrorResponse.connectionFailed(message, throwable),
+ ErrorResponse.serviceUnavailable(message, throwable),
ErrorResponse.notFound("error type", message, throwable),
ErrorResponse.internalError(message, throwable),
ErrorResponse.alreadyExists("error type", message, throwable),
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
b/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
index eadcae8d05..b90e355de6 100644
--- a/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
+++ b/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
@@ -130,6 +130,20 @@ public class Utils {
.build();
}
+ public static Response serviceUnavailable(String message) {
+ return Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity(ErrorResponse.serviceUnavailable(message))
+ .type(MediaType.APPLICATION_JSON)
+ .build();
+ }
+
+ public static Response serviceUnavailable(String message, Throwable
throwable) {
+ return Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity(ErrorResponse.serviceUnavailable(message, throwable))
+ .type(MediaType.APPLICATION_JSON)
+ .build();
+ }
+
public static Response internalError(String message) {
return internalError(message, null);
}
diff --git
a/server-common/src/test/java/org/apache/gravitino/server/web/TestUtils.java
b/server-common/src/test/java/org/apache/gravitino/server/web/TestUtils.java
index 52618dfcd8..9a6d44270c 100644
--- a/server-common/src/test/java/org/apache/gravitino/server/web/TestUtils.java
+++ b/server-common/src/test/java/org/apache/gravitino/server/web/TestUtils.java
@@ -116,6 +116,16 @@ public class TestUtils {
assertEquals("Connection failed", errorResponse.getMessage());
}
+ @Test
+ public void testServiceUnavailable() {
+ Response response = Utils.serviceUnavailable("Service unavailable");
+ assertNotNull(response);
+ assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(),
response.getStatus());
+ assertEquals(MediaType.APPLICATION_JSON,
response.getMediaType().toString());
+ ErrorResponse errorResponse = (ErrorResponse) response.getEntity();
+ assertEquals("Service unavailable", errorResponse.getMessage());
+ }
+
@Test
public void testInternalError() {
Response response = Utils.internalError("Internal error");
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
index a9eeaad49f..80ee553cd5 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
@@ -50,6 +50,7 @@ import
org.apache.gravitino.exceptions.PolicyAlreadyExistsException;
import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
import org.apache.gravitino.exceptions.SchemaAlreadyExistsException;
import org.apache.gravitino.exceptions.SemanticModelAlreadyExistsException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
import org.apache.gravitino.exceptions.TableAlreadyExistsException;
import org.apache.gravitino.exceptions.TagAlreadyAssociatedException;
import org.apache.gravitino.exceptions.TagAlreadyExistsException;
@@ -227,6 +228,9 @@ public class ExceptionHandlers {
if (e instanceof IllegalArgumentException) {
response = ErrorResponse.illegalArguments(e.getMessage(), throwable);
+ } else if (e instanceof ServiceUnavailableException) {
+ response = ErrorResponse.serviceUnavailable(e.getMessage(), throwable);
+
} else if (e instanceof ConnectionFailedException) {
response = ErrorResponse.connectionFailed(e.getMessage(), throwable);
@@ -435,13 +439,19 @@ public class ExceptionHandlers {
public Response handle(OperationType op, String catalog, String metalake,
Exception e) {
String formatted = StringUtil.isBlank(catalog) ? "" : " [" + catalog +
"]";
// Keep schema-check context even when the backend message contains
"Exception: ".
- String reason = e instanceof ConnectionFailedException ? e.getMessage()
: getErrorMsg(e);
+ String reason =
+ e instanceof ConnectionFailedException || e instanceof
ServiceUnavailableException
+ ? e.getMessage()
+ : getErrorMsg(e);
String errorMsg = getCatalogErrorMsg(formatted, op.name(), metalake,
reason);
LOG.warn(errorMsg, e);
if (e instanceof IllegalArgumentException) {
return Utils.illegalArguments(errorMsg, e);
+ } else if (e instanceof ServiceUnavailableException) {
+ return Utils.serviceUnavailable(errorMsg, e);
+
} else if (e instanceof ConnectionFailedException) {
return Utils.connectionFailed(errorMsg, e);
@@ -805,12 +815,16 @@ public class ExceptionHandlers {
@Override
public Response handle(OperationType op, String secret, String parent,
Exception e) {
- String errorMsg = getSecretErrorMsg(parent, getErrorMsg(e));
+ String reason = e instanceof ServiceUnavailableException ?
e.getMessage() : getErrorMsg(e);
+ String errorMsg = getSecretErrorMsg(parent, reason);
LOG.warn(errorMsg, e);
if (e instanceof IllegalArgumentException) {
return Utils.illegalArguments(errorMsg, e);
+ } else if (e instanceof ServiceUnavailableException) {
+ return Utils.serviceUnavailable(errorMsg, e);
+
} else if (e instanceof NotFoundException) {
return Utils.notFound(errorMsg, e);
@@ -1042,6 +1056,9 @@ public class ExceptionHandlers {
} else if (e instanceof UnsupportedOperationException) {
return Utils.unsupportedOperation(errorMsg, e);
+ } else if (e instanceof ServiceUnavailableException) {
+ return Utils.serviceUnavailable(errorMsg, e);
+
} else if (e instanceof ConnectionFailedException) {
return Utils.connectionFailed(errorMsg, e);
@@ -1229,6 +1246,14 @@ public class ExceptionHandlers {
String errorMsg =
getBaseErrorMsg(formattedObject, op.name(), formattedParent,
getErrorMsg(e));
+ // A configured dependency that is temporarily unavailable is not an
internal Gravitino error:
+ // surface it as 503 Service Unavailable so callers can tell an outage
from a server bug.
+ if (e instanceof ServiceUnavailableException) {
+ // WARN, not ERROR: a dependency outage is not a Gravitino bug, but
still trace it here.
+ LOG.warn(errorMsg, e);
+ return Utils.serviceUnavailable(errorMsg, e);
+ }
+
// A backend a catalog federates to being unreachable is a
downstream-dependency failure, not
// an internal Gravitino error: surface it as 502 Bad Gateway so callers
can tell a dependency
// outage from a server bug.
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestExceptionHandlers.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestExceptionHandlers.java
index afbc556a9f..44fec2564f 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestExceptionHandlers.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestExceptionHandlers.java
@@ -26,6 +26,7 @@ import org.apache.gravitino.exceptions.InUseException;
import org.apache.gravitino.exceptions.NoSuchJobTemplateException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.OptimisticLockException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
import org.apache.gravitino.exceptions.UnmodifiableStatisticException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -132,6 +133,23 @@ public class TestExceptionHandlers {
Assertions.assertEquals(OptimisticLockException.class.getSimpleName(),
errorResponse.getType());
}
+ @Test
+ public void testServiceUnavailableReturnsServiceUnavailable() {
+ Response response =
+ ExceptionHandlers.handleTableException(
+ OperationType.LOAD,
+ "table",
+ "schema",
+ new ServiceUnavailableException("secret provider 'vault' is
unavailable"));
+
+ Assertions.assertEquals(
+ Response.Status.SERVICE_UNAVAILABLE.getStatusCode(),
response.getStatus());
+ ErrorResponse errorResponse = (ErrorResponse) response.getEntity();
+ Assertions.assertEquals(ErrorConstants.SERVICE_UNAVAILABLE_CODE,
errorResponse.getCode());
+ Assertions.assertEquals(
+ ServiceUnavailableException.class.getSimpleName(),
errorResponse.getType());
+ }
+
@Test
void testUnsupportedOperationReturnsNotImplemented() {
UnsupportedOperationException exception =