Skip to content

Comments

Fix #436: Add cause message to RestClientException#437

Open
banterCZ wants to merge 2 commits intodevelopfrom
issues/436-cause-message
Open

Fix #436: Add cause message to RestClientException#437
banterCZ wants to merge 2 commits intodevelopfrom
issues/436-cause-message

Conversation

@banterCZ
Copy link
Member

No description provided.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #436 by enriching RestClientException messages thrown by DefaultRestClient so they include the underlying exception message, and it adds/adjusts tests to validate the new message behavior.

Changes:

  • Append the caught exception message to RestClientException messages for GET/POST/PUT/DELETE/PATCH/HEAD failures.
  • Add new tests asserting the updated failure message includes DNS resolution details.
  • Refactor an existing error-response test to use assertThrows and add an assertion for the exception message.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
rest-client-base/src/main/java/com/wultra/core/rest/client/base/DefaultRestClient.java Updates thrown RestClientException messages to include the underlying exception message.
rest-client-base/src/test/java/com/wultra/core/rest/client/base/DefaultRestClientTest.java Adds message assertions for request-failure scenarios and improves one error-response test structure.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

throw (RestClientException) ex.getCause();
}
throw new RestClientException("HTTP GET request failed", ex);
throw new RestClientException("HTTP GET request failed; " + ex.getMessage(), ex);
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new exception message concatenates ex.getMessage() unconditionally. For some exception types getMessage() can be null, resulting in messages like HTTP GET request failed; null, which is not helpful. Consider appending the cause message only when present/non-blank (or falling back to ex.getClass().getSimpleName()/ex.toString()), and apply the same pattern across the other HTTP methods updated in this PR.

Copilot uses AI. Check for mistakes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO there should be always some message for exception and we should just put it into RestClientException, no matter it is null, empty string ... So the implementation in this PR is correct

Comment on lines 159 to 166
void testGet_causeMessage() throws Exception {
final DefaultRestClient localRestClient = new DefaultRestClient("http://nonexistent.invalid");
final RestClientException exception = assertThrows(RestClientException.class, () ->
localRestClient.get("/", new ParameterizedTypeReference<>() {}));

assertTrue(
exception.getMessage().startsWith("HTTP GET request failed; Failed to resolve 'nonexistent.invalid'"),
() -> "Unexpected message: " + exception.getMessage());
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests make real network calls and assert a very specific DNS-resolution error message. This is likely to be flaky/slow in CI (network may be blocked) and the message text varies across OS/JDK/Reactor Netty versions. To make the test stable, avoid external DNS and instead inject/mock the WebClient/connector to throw a deterministic exception, or assert more loosely (e.g., that the message starts with HTTP <METHOD> request failed; and that the cause message is non-empty/contains the hostname).

Copilot uses AI. Check for mistakes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that mocking webClient would be better. On the other hand it would require quite a lot of stubbing and these tests would be hard to read. So usage real localRestClient is acceptable for this use-case.

Comment on lines 159 to 211
void testGet_causeMessage() throws Exception {
final DefaultRestClient localRestClient = new DefaultRestClient("http://nonexistent.invalid");
final RestClientException exception = assertThrows(RestClientException.class, () ->
localRestClient.get("/", new ParameterizedTypeReference<>() {}));

assertTrue(
exception.getMessage().startsWith("HTTP GET request failed; Failed to resolve 'nonexistent.invalid'"),
() -> "Unexpected message: " + exception.getMessage());
}

@Test
void testHead_causeMessage() throws Exception {
final DefaultRestClient localRestClient = new DefaultRestClient("http://nonexistent.invalid");
final RestClientException exception = assertThrows(RestClientException.class, () ->
localRestClient.head("/", new ParameterizedTypeReference<>() {}));

assertTrue(
exception.getMessage().startsWith("HTTP HEAD request failed; Failed to resolve 'nonexistent.invalid'"),
() -> "Unexpected message: " + exception.getMessage());
}

@Test
void testDelete_causeMessage() throws Exception {
final DefaultRestClient localRestClient = new DefaultRestClient("http://nonexistent.invalid");
final RestClientException exception = assertThrows(RestClientException.class, () ->
localRestClient.delete("/", new ParameterizedTypeReference<>() {}));

assertTrue(
exception.getMessage().startsWith("HTTP DELETE request failed; Failed to resolve 'nonexistent.invalid'"),
() -> "Unexpected message: " + exception.getMessage());
}

@Test
void testPut_causeMessage() throws Exception {
final DefaultRestClient localRestClient = new DefaultRestClient("http://nonexistent.invalid");
final RestClientException exception = assertThrows(RestClientException.class, () ->
localRestClient.put("/", null, new ParameterizedTypeReference<>() {}));

assertTrue(
exception.getMessage().startsWith("HTTP PUT request failed; Failed to resolve 'nonexistent.invalid'"),
() -> "Unexpected message: " + exception.getMessage());
}

@Test
void testPost_causeMessage() throws Exception {
final DefaultRestClient localRestClient = new DefaultRestClient("http://nonexistent.invalid");
final RestClientException exception = assertThrows(RestClientException.class, () ->
localRestClient.post("/", null, new ParameterizedTypeReference<>() {}));

assertTrue(
exception.getMessage().startsWith("HTTP POST request failed; Failed to resolve 'nonexistent.invalid'"),
() -> "Unexpected message: " + exception.getMessage());
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The five *_causeMessage tests are nearly identical aside from the HTTP method. Consider converting them to a single parameterized test (e.g., method name + lambda) to reduce duplication and make future message changes easier to update consistently.

Copilot uses AI. Check for mistakes.
Copy link

@michal-rozehnal-w michal-rozehnal-w left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice PR, it will definitely help us in any future investigations

throw (RestClientException) ex.getCause();
}
throw new RestClientException("HTTP GET request failed", ex);
throw new RestClientException("HTTP GET request failed; " + ex.getMessage(), ex);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO there should be always some message for exception and we should just put it into RestClientException, no matter it is null, empty string ... So the implementation in this PR is correct

Comment on lines 159 to 166
void testGet_causeMessage() throws Exception {
final DefaultRestClient localRestClient = new DefaultRestClient("http://nonexistent.invalid");
final RestClientException exception = assertThrows(RestClientException.class, () ->
localRestClient.get("/", new ParameterizedTypeReference<>() {}));

assertTrue(
exception.getMessage().startsWith("HTTP GET request failed; Failed to resolve 'nonexistent.invalid'"),
() -> "Unexpected message: " + exception.getMessage());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that mocking webClient would be better. On the other hand it would require quite a lot of stubbing and these tests would be hard to read. So usage real localRestClient is acceptable for this use-case.

Copy link
Member

@romanstrobl romanstrobl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks OK, I just don't like the ; in the error message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants