Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ springBoot {
}

dependencies {
implementation("uk.gov.hmcts.cp:api-hmcts-crime-template:2.0.0")
implementation("uk.gov.hmcts.cp:api-hmcts-crime-template:2.0.1")

// --- Observability / Actuator / OTEL / Prometheus ---
implementation 'org.springframework.boot:spring-boot-starter-actuator'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
import org.springframework.web.server.ResponseStatusException;
import uk.gov.hmcts.cp.openapi.model.ErrorResponse;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.Instant;
import java.util.Objects;

@RestControllerAdvice
Expand All @@ -21,16 +20,20 @@ public GlobalExceptionHandler(final Tracer tracer) {
}

@ExceptionHandler(ResponseStatusException.class)
public ResponseEntity<ErrorResponse> handleResponseStatusException(final ResponseStatusException responseStatusException) {
public ResponseEntity<ErrorResponse> handleResponseStatusException(
final ResponseStatusException responseStatusException) {

final ErrorResponse error = ErrorResponse.builder()
.error(String.valueOf(responseStatusException.getStatusCode().value()))
.message(responseStatusException.getReason() != null ? responseStatusException.getReason() : responseStatusException.getMessage())
.timestamp(OffsetDateTime.now(ZoneOffset.UTC))
.message(responseStatusException.getReason() != null
? responseStatusException.getReason()
: responseStatusException.getMessage())
.timestamp(Instant.now())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good idea to test the time.
A better solution that Samir and I discussed is to use a clock server to allow us to inject a fixed time in Tests thus we can assert what the time response is exactly
Example here https://github.com/hmcts/service-hmcts-springboot-demo/tree/main/clock-demo

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ClockService using offsetdate time

.traceId(Objects.requireNonNull(tracer.currentSpan()).context().traceId())
.build();

return ResponseEntity
.status(responseStatusException.getStatusCode())
.body(error);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,25 @@

import io.micrometer.tracing.Span;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.TraceContext;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.server.ResponseStatusException;

import uk.gov.hmcts.cp.openapi.model.ErrorResponse;
import io.micrometer.tracing.TraceContext;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.time.Instant;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class GlobalExceptionHandlerTest {

@Test
void handleResponseStatusExceptionShouldReturnErrorResponseWithCorrectFields() {
void handle_response_status_exception_should_return_error_response_with_correct_fields() {
// Arrange
final Tracer tracer = mock(Tracer.class);
final Span span = mock(Span.class);
Expand All @@ -29,18 +33,32 @@ void handleResponseStatusExceptionShouldReturnErrorResponseWithCorrectFields() {
final GlobalExceptionHandler handler = new GlobalExceptionHandler(tracer);

final String reason = "Test error";
final ResponseStatusException exception = new ResponseStatusException(HttpStatus.NOT_FOUND, reason);
final ResponseStatusException exception =
new ResponseStatusException(HttpStatus.NOT_FOUND, reason);

final Instant beforeCall = Instant.now();

// Act
final ResponseEntity<ErrorResponse> response = handler.handleResponseStatusException(exception);
final ResponseEntity<ErrorResponse> response =
handler.handleResponseStatusException(exception);

final Instant afterCall = Instant.now();

// Assert
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());

final ErrorResponse error = response.getBody();
assertNotNull(error);

assertEquals("404", error.getError());
assertEquals(reason, error.getMessage());
assertNotNull(error.getTimestamp());
assertEquals("test-trace-id", error.getTraceId());

assertNotNull(error.getTimestamp());
assertTrue(
!error.getTimestamp().isBefore(beforeCall)
&& !error.getTimestamp().isAfter(afterCall),
"Timestamp should be between beforeCall and afterCall"
);
}
}
}
Loading