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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.xpeho.spring_boot_java_random_user.domain.exceptions.InvalidPaginationException;
import com.xpeho.spring_boot_java_random_user.domain.exceptions.UserNotFoundException;
import jakarta.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
Expand All @@ -14,6 +15,16 @@
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ErrorResponse> handleConstraintViolationException(ConstraintViolationException ex) {
String message = ex.getConstraintViolations().stream()
.map(v -> v.getPropertyPath() + ": " + v.getMessage())
.findFirst()
.orElse(ex.getMessage());
logger.warn("Constraint violation: {}", message);
return buildErrorResponse("INVALID_PAGINATION", message, HttpStatus.BAD_REQUEST);
}
Comment thread
profotoce59 marked this conversation as resolved.

@ExceptionHandler(InvalidPaginationException.class)
public ResponseEntity<ErrorResponse> handleInvalidPaginationException(InvalidPaginationException ex) {
logger.warn("Invalid pagination request: {}", ex.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.xpeho.spring_boot_java_random_user.domain.entities.UserRequest;
import com.xpeho.spring_boot_java_random_user.domain.enums.Gender;
import com.xpeho.spring_boot_java_random_user.domain.enums.UserSource;
import com.xpeho.spring_boot_java_random_user.domain.exceptions.InvalidPaginationException;
import com.xpeho.spring_boot_java_random_user.domain.exceptions.UserNotFoundException;
import com.xpeho.spring_boot_java_random_user.domain.usecases.*;
import com.xpeho.spring_boot_java_random_user.presentation.controllers.UserController;
Expand All @@ -15,13 +14,15 @@
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.List;


@Validated
@RestController
public class UserHandler implements UserController {

Comment thread
profotoce59 marked this conversation as resolved.
Expand Down Expand Up @@ -54,12 +55,6 @@ public UserHandler(

@Override
public ResponseEntity<UserResponseDTO> getRandomUsers(int page, int size, UserSource source) {
if (page < 1) {
throw new InvalidPaginationException("Page must be greater than or equal to 1. Requested: " + page);
}
if (size < 1 || size > 30) {
throw new InvalidPaginationException("Page size must be between 1 and 30. Requested: " + size);
}
try {
PaginatedUsers result = fetchAndSaveRandomUsersUseCase.execute(page, size, source);
UserResponseDTO response = new UserResponseDTO(
Expand All @@ -70,7 +65,7 @@ public ResponseEntity<UserResponseDTO> getRandomUsers(int page, int size, UserSo
);
return ResponseEntity.ok(response);
} catch (IOException e) {
logger.error("Error fetching random users: {}", e.getMessage(), e);
logger.error("Error fetching random users", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
Expand Down Expand Up @@ -124,6 +119,6 @@ public void deleteUserById(int id) {
}

private void logUserNotFound(UserNotFoundException e) {
logger.warn(USER_NOT_FOUND_LOG, e.getMessage(), e);
logger.warn(USER_NOT_FOUND_LOG, e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@
import com.xpeho.spring_boot_java_random_user.domain.entities.UserRequest;
import com.xpeho.spring_boot_java_random_user.domain.enums.Gender;
import com.xpeho.spring_boot_java_random_user.domain.enums.UserSource;
import com.xpeho.spring_boot_java_random_user.domain.exceptions.InvalidPaginationException;
import com.xpeho.spring_boot_java_random_user.domain.exceptions.UserNotFoundException;
import com.xpeho.spring_boot_java_random_user.domain.usecases.*;
import com.xpeho.spring_boot_java_random_user.presentation.handlers.UserHandler;
import com.xpeho.spring_boot_java_random_user.presentation.dto.UserResponseDTO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

Expand Down Expand Up @@ -82,19 +79,6 @@ void shouldReturnInternalServerErrorWhenGetRandomUsersFails() throws IOException
verify(fetchAndSaveRandomUsersUseCase, times(1)).execute(page, size, UserSource.RANDOM_USER);
}

@ParameterizedTest
@CsvSource({
"1, 31",
"0, 10",
"1, 0"
})
@DisplayName("Should throw InvalidPaginationException for invalid pagination inputs")
void shouldThrowInvalidPaginationExceptionForInvalidPaginationInputs(int page, int size) throws IOException {
assertThrows(InvalidPaginationException.class, () -> userHandler.getRandomUsers(page, size, UserSource.DUMMY));
verify(fetchAndSaveRandomUsersUseCase, never()).execute(page, size, UserSource.DUMMY);
}


@Test
@DisplayName("Should return 200 and user when getUserById succeeds")
void shouldReturnOkWhenGetUserByIdSucceeds() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,42 @@
import com.xpeho.spring_boot_java_random_user.domain.exceptions.InvalidPaginationException;
import com.xpeho.spring_boot_java_random_user.domain.exceptions.UserNotFoundException;
import com.xpeho.spring_boot_java_random_user.domain.enums.UserSource;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class GlobalExceptionHandlerTest {

private final GlobalExceptionHandler handler = new GlobalExceptionHandler();

@Test
@DisplayName("Should return 400 BAD_REQUEST when ConstraintViolationException is thrown")
void shouldReturnBadRequestWhenConstraintViolationException() {
ConstraintViolation<?> violation = mock(ConstraintViolation.class);
when(violation.getPropertyPath()).thenReturn(mock(jakarta.validation.Path.class));
when(violation.getPropertyPath().toString()).thenReturn("size");
when(violation.getMessage()).thenReturn("must be less than or equal to 30");

ConstraintViolationException ex = new ConstraintViolationException(Set.of(violation));
ResponseEntity<ErrorResponse> response = handler.handleConstraintViolationException(ex);

assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertNotNull(response.getBody());
assertEquals("INVALID_PAGINATION", response.getBody().error());
assertEquals(400, response.getBody().status());
assertTrue(response.getBody().message().contains("must be less than or equal to 30"));
}

@Test
@DisplayName("Should return 400 BAD_REQUEST when InvalidPaginationException is thrown")
void shouldReturnBadRequestWhenInvalidPaginationException() {
Expand Down
24 changes: 24 additions & 0 deletions src/test/java/feature/StepDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,28 @@ public void theClientCallToGetTheCreatedUser() {
assertNotNull(createdUserId, "No user was created before this step");
executeGet("/random-users/" + createdUserId);
}

@When("the client call to GET \\/random-users")
public void theClientCallToGetRandomUsers() {
executeGet("/random-users");
}

@When("the client call to GET \\/random-users with page {int} and size {int}")
public void theClientCallToGetRandomUsersWithPageAndSize(int page, int size) {
executeGet("/random-users?page=" + page + "&size=" + size);
}

@And("the response contains a list of users")
public void theResponseContainsAListOfUsers() throws Exception {
JsonNode body = objectMapper.readTree(latestResponse.getBody());
assertNotNull(body.get("data"));
assertTrue(body.get("data").isArray());
}

@And("the response contains {int} users")
public void theResponseContainsUsers(int expectedSize) throws Exception {
JsonNode body = objectMapper.readTree(latestResponse.getBody());
assertNotNull(body.get("data"));
assertEquals(expectedSize, body.get("data").size());
}
}
15 changes: 15 additions & 0 deletions src/test/resources/features/get_random_users.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Feature: GET /random-users endpoint

Scenario: Fetch random users with default parameters
When the client call to GET /random-users
Then the response status should be 200
And the response contains a list of users

Scenario: Fetch random users with custom page and size
When the client call to GET /random-users with page 1 and size 5
Then the response status should be 200
And the response contains 5 users

Scenario: Fetch random users with size above maximum
When the client call to GET /random-users with page 1 and size 31
Then the response status should be 400
Comment thread
profotoce59 marked this conversation as resolved.
Loading