diff --git a/README.md b/README.md
index cd6173d..96e6545 100644
--- a/README.md
+++ b/README.md
@@ -88,6 +88,7 @@ dummy.api.base-url=https://dummyjson.com/
| `POST` | `/random-users` | Create a new user | ✅ |
| `PUT` | `/random-users/{id}` | Update user | ✅ |
| `DELETE` | `/random-users/{id}` | Delete user | ✅ |
+| `GET` | `/random-users/filter` | Filter users by criteria | ✅ |
### Example Request
@@ -115,6 +116,12 @@ curl -X POST "http://localhost:8080/random-users" \
"nat": "FR"
}'
+# Filter users by gender and nationality
+curl -X GET "http://localhost:8080/random-users/filter?gender=MALE&nat=FR"
+
+# Filter users by firstname (partial match, case-insensitive)
+curl -X GET "http://localhost:8080/random-users/filter?firstname=john"
+
# Update user
curl -X PUT "http://localhost:8080/random-users/1" \
-H "Content-Type: application/json" \
diff --git a/pom.xml b/pom.xml
index d8d2ec5..802600f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,7 +50,7 @@
org.springframework.boot
- spring-boot-starter-data-jdbc
+ spring-boot-starter-data-jpa
org.springframework.boot
@@ -78,11 +78,6 @@
org.springframework.boot
spring-boot-starter-actuator-test
test
-
-
- org.springframework.boot
- spring-boot-starter-data-jdbc-test
- test
org.springframework.boot
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/data/models/database/User.java b/src/main/java/com/xpeho/spring_boot_java_random_user/data/models/database/User.java
index 501cc58..bdd3a8a 100644
--- a/src/main/java/com/xpeho/spring_boot_java_random_user/data/models/database/User.java
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/data/models/database/User.java
@@ -1,34 +1,39 @@
package com.xpeho.spring_boot_java_random_user.data.models.database;
-import org.springframework.data.annotation.Id;
-import org.springframework.data.relational.core.mapping.Column;
-import org.springframework.data.relational.core.mapping.Table;
-
-@Table("users")
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "users")
public class User {
@Id
- @Column("id")
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id")
private Long id;
- @Column("gender")
+ @Column(name = "gender")
private String gender;
- @Column("firstname")
+ @Column(name = "firstname")
private String firstname;
- @Column("lastname")
+ @Column(name = "lastname")
private String lastname;
- @Column("civility")
+ @Column(name = "civility")
private String civility;
- @Column("email")
+ @Column(name = "email")
private String email;
- @Column("phone")
+ @Column(name = "phone")
private String phone;
- @Column("picture")
+ @Column(name = "picture")
private String picture;
- @Column("nationality")
+ @Column(name = "nationality")
private String nationality;
- // Required by Spring Data JDBC to instantiate the entity via reflection
+ // Required by JPA
public User() {
- // No initialization needed; fields are populated by Spring Data JDBC after instantiation
+ // No initialization needed
}
public Long getId() {
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/data/services/UserServiceImpl.java b/src/main/java/com/xpeho/spring_boot_java_random_user/data/services/UserServiceImpl.java
index 0ccb6e3..76271af 100644
--- a/src/main/java/com/xpeho/spring_boot_java_random_user/data/services/UserServiceImpl.java
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/data/services/UserServiceImpl.java
@@ -3,7 +3,9 @@
import com.xpeho.spring_boot_java_random_user.data.converters.UserConverter;
import com.xpeho.spring_boot_java_random_user.data.models.database.User;
import com.xpeho.spring_boot_java_random_user.data.sources.database.UserRepository;
+import com.xpeho.spring_boot_java_random_user.data.sources.database.UserSpecifications;
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
import com.xpeho.spring_boot_java_random_user.domain.services.LocalUserService;
import org.springframework.stereotype.Service;
@@ -47,4 +49,11 @@ public UserEntity save(UserEntity user) {
public void deleteById(long id) {
userRepository.deleteById(id);
}
+
+ @Override
+ public List filterUsers(UserFilter filter) {
+ return userRepository.findAll(UserSpecifications.byFilter(filter)).stream()
+ .map(userConverter::toDomain)
+ .toList();
+ }
}
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserRepository.java b/src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserRepository.java
index 9fc516e..b23c856 100644
--- a/src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserRepository.java
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserRepository.java
@@ -1,7 +1,8 @@
package com.xpeho.spring_boot_java_random_user.data.sources.database;
import com.xpeho.spring_boot_java_random_user.data.models.database.User;
-import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-public interface UserRepository extends CrudRepository {
+public interface UserRepository extends JpaRepository, JpaSpecificationExecutor {
}
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserSpecifications.java b/src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserSpecifications.java
new file mode 100644
index 0000000..f4be154
--- /dev/null
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserSpecifications.java
@@ -0,0 +1,55 @@
+package com.xpeho.spring_boot_java_random_user.data.sources.database;
+
+import com.xpeho.spring_boot_java_random_user.data.models.database.User;
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.Path;
+import jakarta.persistence.criteria.Predicate;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class UserSpecifications {
+ private UserSpecifications() {
+ }
+
+ public static Specification byFilter(UserFilter filter) {
+ return (user, query, criteriaBuilder) -> {
+ List predicates = new ArrayList<>();
+
+ if (filter.gender() != null) {
+ predicates.add(criteriaBuilder.equal(
+ criteriaBuilder.lower(user.get("gender")),
+ filter.gender().name().toLowerCase()
+ ));
+ }
+
+ addContainsPredicate(predicates, criteriaBuilder, user.get("firstname"), filter.firstname());
+ addContainsPredicate(predicates, criteriaBuilder, user.get("lastname"), filter.lastname());
+ addContainsPredicate(predicates, criteriaBuilder, user.get("civility"), filter.civility());
+ addContainsPredicate(predicates, criteriaBuilder, user.get("email"), filter.email());
+ addContainsPredicate(predicates, criteriaBuilder, user.get("phone"), filter.phone());
+ addContainsPredicate(predicates, criteriaBuilder, user.get("nationality"), filter.nat());
+
+ return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
+ };
+ }
+
+ private static void addContainsPredicate(
+ List predicates,
+ CriteriaBuilder criteriaBuilder,
+ Path field,
+ String value
+ ) {
+ if (!StringUtils.hasText(value)) {
+ return;
+ }
+
+ predicates.add(criteriaBuilder.like(
+ criteriaBuilder.lower(field),
+ "%" + value.toLowerCase() + "%"
+ ));
+ }
+}
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/domain/entities/UserFilter.java b/src/main/java/com/xpeho/spring_boot_java_random_user/domain/entities/UserFilter.java
new file mode 100644
index 0000000..7b1a63f
--- /dev/null
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/domain/entities/UserFilter.java
@@ -0,0 +1,14 @@
+package com.xpeho.spring_boot_java_random_user.domain.entities;
+
+import com.xpeho.spring_boot_java_random_user.domain.enums.Gender;
+
+public record UserFilter(
+ Gender gender,
+ String firstname,
+ String lastname,
+ String civility,
+ String email,
+ String phone,
+ String nat
+) {
+}
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/domain/enums/Gender.java b/src/main/java/com/xpeho/spring_boot_java_random_user/domain/enums/Gender.java
new file mode 100644
index 0000000..1904a32
--- /dev/null
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/domain/enums/Gender.java
@@ -0,0 +1,6 @@
+package com.xpeho.spring_boot_java_random_user.domain.enums;
+
+public enum Gender {
+ MALE,
+ FEMALE
+}
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/domain/services/LocalUserService.java b/src/main/java/com/xpeho/spring_boot_java_random_user/domain/services/LocalUserService.java
index 80104a2..8bb472e 100644
--- a/src/main/java/com/xpeho/spring_boot_java_random_user/domain/services/LocalUserService.java
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/domain/services/LocalUserService.java
@@ -1,6 +1,7 @@
package com.xpeho.spring_boot_java_random_user.domain.services;
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
import java.util.List;
import java.util.Optional;
@@ -13,5 +14,7 @@ public interface LocalUserService {
UserEntity save(UserEntity user);
void deleteById(long id);
+
+ List filterUsers(UserFilter filter);
}
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/domain/usecases/FilterUsersUseCase.java b/src/main/java/com/xpeho/spring_boot_java_random_user/domain/usecases/FilterUsersUseCase.java
new file mode 100644
index 0000000..a28a71d
--- /dev/null
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/domain/usecases/FilterUsersUseCase.java
@@ -0,0 +1,21 @@
+package com.xpeho.spring_boot_java_random_user.domain.usecases;
+
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
+import com.xpeho.spring_boot_java_random_user.domain.services.LocalUserService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class FilterUsersUseCase {
+ private final LocalUserService userService;
+
+ public FilterUsersUseCase(LocalUserService userService) {
+ this.userService = userService;
+ }
+
+ public List execute(UserFilter filter) {
+ return userService.filterUsers(filter);
+ }
+}
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/controllers/UserController.java b/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/controllers/UserController.java
index e029f3b..abe1376 100644
--- a/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/controllers/UserController.java
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/controllers/UserController.java
@@ -2,6 +2,7 @@
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
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.presentation.dto.UserResponseDTO;
import io.swagger.v3.oas.annotations.Operation;
@@ -13,6 +14,8 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
+import java.util.List;
+
@RequestMapping("/random-users")
@@ -92,6 +95,32 @@ ResponseEntity updateRandomUser(
ResponseEntity createUser(@RequestBody UserRequest user);
+ @GetMapping("/filter")
+ @Operation(
+ summary = "Filter users",
+ description = "Search users by optional filters on gender, firstname, lastname, civility, email, phone and nationality. All filters are case-insensitive and support partial matching.",
+ parameters = {
+ @Parameter(name = "gender", description = "Filter by gender (MALE or FEMALE)"),
+ @Parameter(name = "firstname", description = "Filter by firstname"),
+ @Parameter(name = "lastname", description = "Filter by lastname"),
+ @Parameter(name = "civility", description = "Filter by civility"),
+ @Parameter(name = "email", description = "Filter by email"),
+ @Parameter(name = "phone", description = "Filter by phone"),
+ @Parameter(name = "nat", description = "Filter by nationality")
+ }
+ )
+ @ApiResponse(responseCode = "200", description = "Filtered list of users")
+ @ApiResponse(responseCode = "500", description = "Internal server error")
+ ResponseEntity> filterUsers(
+ @RequestParam(required = false) Gender gender,
+ @RequestParam(required = false) String firstname,
+ @RequestParam(required = false) String lastname,
+ @RequestParam(required = false) String civility,
+ @RequestParam(required = false) String email,
+ @RequestParam(required = false) String phone,
+ @RequestParam(required = false) String nat
+ );
+
@DeleteMapping("/{id}")
@Operation(
summary = "Delete user by id",
diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/handlers/UserHandler.java b/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/handlers/UserHandler.java
index 9b0b01a..9872731 100644
--- a/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/handlers/UserHandler.java
+++ b/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/handlers/UserHandler.java
@@ -2,7 +2,9 @@
import com.xpeho.spring_boot_java_random_user.domain.entities.PaginatedUsers;
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
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;
@@ -17,6 +19,7 @@
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
+import java.util.List;
@RestController
@@ -30,20 +33,22 @@ public class UserHandler implements UserController {
private final GetUserByIdUseCase getUserByIdUseCase;
private final CreateUserUseCase createUserUseCase;
private final DeleteUserByIdUseCase deleteUserUseCase;
+ private final FilterUsersUseCase filterUsersUseCase;
public UserHandler(
FetchAndSaveRandomUsersUseCase fetchAndSaveRandomUsersUseCase,
UpdateRandomUserUseCase updateRandomUserUseCase,
GetUserByIdUseCase getUserByIdUseCase,
CreateUserUseCase createUserUseCase,
- DeleteUserByIdUseCase deleteUserUseCase
+ DeleteUserByIdUseCase deleteUserUseCase,
+ FilterUsersUseCase filterUsersUseCase
) {
this.fetchAndSaveRandomUsersUseCase = fetchAndSaveRandomUsersUseCase;
this.updateRandomUserUseCase = updateRandomUserUseCase;
this.getUserByIdUseCase = getUserByIdUseCase;
this.createUserUseCase = createUserUseCase;
this.deleteUserUseCase = deleteUserUseCase;
-
+ this.filterUsersUseCase = filterUsersUseCase;
}
@@ -99,6 +104,16 @@ public ResponseEntity createUser(@RequestBody UserRequest user) {
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
+ @Override
+ public ResponseEntity> filterUsers(
+ Gender gender, String firstname, String lastname,
+ String civility, String email, String phone, String nat
+ ) {
+ UserFilter filter = new UserFilter(gender, firstname, lastname, civility, email, phone, nat);
+ List users = filterUsersUseCase.execute(filter);
+ return ResponseEntity.ok(users);
+ }
+
@Override
public void deleteUserById(int id) {
try {
diff --git a/src/test/java/com/xpeho/spring_boot_java_random_user/data/services/UserServiceImplTest.java b/src/test/java/com/xpeho/spring_boot_java_random_user/data/services/UserServiceImplTest.java
index 85adecb..942d99e 100644
--- a/src/test/java/com/xpeho/spring_boot_java_random_user/data/services/UserServiceImplTest.java
+++ b/src/test/java/com/xpeho/spring_boot_java_random_user/data/services/UserServiceImplTest.java
@@ -4,10 +4,15 @@
import com.xpeho.spring_boot_java_random_user.data.models.database.User;
import com.xpeho.spring_boot_java_random_user.data.sources.database.UserRepository;
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
+import com.xpeho.spring_boot_java_random_user.domain.enums.Gender;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
+import org.springframework.data.jpa.domain.Specification;
+import java.util.Collections;
+import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -84,4 +89,63 @@ void shouldSaveMappedUserAndReturnMappedDomainEntity() {
verify(userRepository).save(daoToSave);
verify(userConverter).toDomain(savedDao);
}
+
+ @Test
+ @DisplayName("Should build a specification and call repository for filtered users")
+ void shouldFilterUsersWithGender() {
+ UserFilter filter = new UserFilter(Gender.MALE, "John", null, null, null, null, null);
+
+ User dao = new User();
+ dao.setId(1L);
+ dao.setFirstname("John");
+
+ UserEntity expected = new UserEntity(1L, "male", "John", "Doe", "Mr", "john@doe.com", "1234", "pic.jpg", "FR");
+
+ when(userRepository.findAll(org.mockito.ArgumentMatchers.>any()))
+ .thenReturn(List.of(dao));
+ when(userConverter.toDomain(dao)).thenReturn(expected);
+
+ List result = userService.filterUsers(filter);
+
+ assertEquals(1, result.size());
+ assertEquals(expected, result.get(0));
+ verify(userRepository).findAll(org.mockito.ArgumentMatchers.>any());
+ verify(userConverter).toDomain(dao);
+ }
+
+ @Test
+ @DisplayName("Should call repository when gender filter is null")
+ void shouldFilterUsersWithNullGender() {
+ UserFilter filter = new UserFilter(null, null, "Smith", null, null, null, null);
+
+ User dao = new User();
+ dao.setId(2L);
+ dao.setLastname("Smith");
+
+ UserEntity expected = new UserEntity(2L, "female", "Alice", "Smith", "Ms", "alice@smith.com", "5678", "pic2.jpg", "US");
+
+ when(userRepository.findAll(org.mockito.ArgumentMatchers.>any()))
+ .thenReturn(List.of(dao));
+ when(userConverter.toDomain(dao)).thenReturn(expected);
+
+ List result = userService.filterUsers(filter);
+
+ assertEquals(1, result.size());
+ assertEquals(expected, result.get(0));
+ verify(userRepository).findAll(org.mockito.ArgumentMatchers.>any());
+ }
+
+ @Test
+ @DisplayName("Should return empty list when no users match filter")
+ void shouldReturnEmptyListWhenNoUsersMatchFilter() {
+ UserFilter filter = new UserFilter(Gender.FEMALE, "Unknown", null, null, null, null, null);
+
+ when(userRepository.findAll(org.mockito.ArgumentMatchers.>any()))
+ .thenReturn(Collections.emptyList());
+
+ List result = userService.filterUsers(filter);
+
+ assertTrue(result.isEmpty());
+ verify(userRepository).findAll(org.mockito.ArgumentMatchers.>any());
+ }
}
diff --git a/src/test/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserSpecificationsTest.java b/src/test/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserSpecificationsTest.java
new file mode 100644
index 0000000..cbe3095
--- /dev/null
+++ b/src/test/java/com/xpeho/spring_boot_java_random_user/data/sources/database/UserSpecificationsTest.java
@@ -0,0 +1,59 @@
+package com.xpeho.spring_boot_java_random_user.data.sources.database;
+
+import com.xpeho.spring_boot_java_random_user.data.models.database.User;
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
+import jakarta.persistence.criteria.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.jpa.domain.Specification;
+
+import static org.mockito.Mockito.*;
+
+class UserSpecificationsTest {
+ private Root user;
+ private CriteriaQuery> query;
+ private CriteriaBuilder cb;
+ private Expression lowerExpr;
+ private Predicate predicate;
+
+ @BeforeEach
+ @SuppressWarnings("unchecked")
+ void setUp() {
+ user = mock(Root.class);
+ query = mock(CriteriaQuery.class);
+ cb = mock(CriteriaBuilder.class);
+ lowerExpr = mock(Expression.class);
+ predicate = mock(Predicate.class);
+ when(cb.lower(any())).thenReturn(lowerExpr);
+ when(cb.equal(any(), anyString())).thenReturn(predicate);
+ when(cb.like(any(Expression.class), anyString())).thenReturn(predicate);
+ when(cb.and(any(Predicate[].class))).thenReturn(predicate);
+ }
+
+
+
+ @Test
+ @DisplayName("Should add like predicates for all text fields")
+ void shouldAddLikePredicatesForAllTextFields() {
+ UserFilter filter = new UserFilter(null, "John", "Doe", "Mr", "john@doe.com", "1234", "FR");
+
+ Specification spec = UserSpecifications.byFilter(filter);
+ spec.toPredicate(user, query, cb);
+
+ verify(user).get("firstname");
+ verify(user).get("lastname");
+ verify(user).get("civility");
+ verify(user).get("email");
+ verify(user).get("phone");
+ verify(user).get("nationality");
+ verify(cb).like(lowerExpr, "%john%");
+ verify(cb).like(lowerExpr, "%doe%");
+ verify(cb).like(lowerExpr, "%mr%");
+ verify(cb).like(lowerExpr, "%john@doe.com%");
+ verify(cb).like(lowerExpr, "%1234%");
+ verify(cb).like(lowerExpr, "%fr%");
+ }
+
+
+}
diff --git a/src/test/java/com/xpeho/spring_boot_java_random_user/domain/usecases/FilterUsersUseCaseTest.java b/src/test/java/com/xpeho/spring_boot_java_random_user/domain/usecases/FilterUsersUseCaseTest.java
new file mode 100644
index 0000000..368f69f
--- /dev/null
+++ b/src/test/java/com/xpeho/spring_boot_java_random_user/domain/usecases/FilterUsersUseCaseTest.java
@@ -0,0 +1,69 @@
+package com.xpeho.spring_boot_java_random_user.domain.usecases;
+
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
+import com.xpeho.spring_boot_java_random_user.domain.enums.Gender;
+import com.xpeho.spring_boot_java_random_user.domain.services.LocalUserService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+class FilterUsersUseCaseTest {
+ private LocalUserService userService;
+ private FilterUsersUseCase useCase;
+
+ @BeforeEach
+ void setUp() {
+ userService = mock(LocalUserService.class);
+ useCase = new FilterUsersUseCase(userService);
+ }
+
+ @Test
+ @DisplayName("Should return filtered users matching the filter")
+ void shouldReturnFilteredUsers() {
+ UserFilter filter = new UserFilter(Gender.MALE, "John", null, null, null, null, null);
+ List expected = List.of(
+ new UserEntity(1L, "male", "John", "Doe", "Mr", "john@example.com", "0600000000", "http://pic.jpg", "FR")
+ );
+ when(userService.filterUsers(filter)).thenReturn(expected);
+
+ List result = useCase.execute(filter);
+
+ assertEquals(expected, result);
+ verify(userService).filterUsers(filter);
+ }
+
+ @Test
+ @DisplayName("Should return empty list when no users match the filter")
+ void shouldReturnEmptyListWhenNoMatch() {
+ UserFilter filter = new UserFilter(Gender.FEMALE, "Unknown", null, null, null, null, null);
+ when(userService.filterUsers(filter)).thenReturn(Collections.emptyList());
+
+ List result = useCase.execute(filter);
+
+ assertTrue(result.isEmpty());
+ verify(userService).filterUsers(filter);
+ }
+
+ @Test
+ @DisplayName("Should pass filter with all fields to the service")
+ void shouldPassFilterWithAllFields() {
+ UserFilter filter = new UserFilter(Gender.FEMALE, "Alice", "Smith", "Ms", "alice@example.com", "0611111111", "US");
+ List expected = List.of(
+ new UserEntity(5L, "female", "Alice", "Smith", "Ms", "alice@example.com", "0611111111", "http://pic2.jpg", "US")
+ );
+ when(userService.filterUsers(filter)).thenReturn(expected);
+
+ List result = useCase.execute(filter);
+
+ assertEquals(expected, result);
+ verify(userService, times(1)).filterUsers(filter);
+ verifyNoMoreInteractions(userService);
+ }
+}
diff --git a/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/UserHandlerTest.java b/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/UserHandlerTest.java
index 25b2a8b..9dcd49d 100644
--- a/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/UserHandlerTest.java
+++ b/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/UserHandlerTest.java
@@ -2,7 +2,9 @@
import com.xpeho.spring_boot_java_random_user.domain.entities.PaginatedUsers;
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
+import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
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;
@@ -30,6 +32,7 @@ class UserHandlerTest {
private GetUserByIdUseCase getUserByIdUseCase;
private CreateUserUseCase createUserUseCase;
private DeleteUserByIdUseCase deleteUserUseCase;
+ private FilterUsersUseCase filterUsersUseCase;
private UserHandler userHandler;
@BeforeEach
@@ -39,7 +42,8 @@ void setUp() {
getUserByIdUseCase = mock(GetUserByIdUseCase.class);
createUserUseCase = mock(CreateUserUseCase.class);
deleteUserUseCase = mock(DeleteUserByIdUseCase.class);
- userHandler = new UserHandler(fetchAndSaveRandomUsersUseCase, updateRandomUserUseCase, getUserByIdUseCase, createUserUseCase, deleteUserUseCase);
+ filterUsersUseCase = mock(FilterUsersUseCase.class);
+ userHandler = new UserHandler(fetchAndSaveRandomUsersUseCase, updateRandomUserUseCase, getUserByIdUseCase, createUserUseCase, deleteUserUseCase, filterUsersUseCase);
}
@Test
@@ -177,4 +181,36 @@ void shouldLogWarningWhenDeleteUserByIdFails() {
verify(deleteUserUseCase, times(1)).execute(userId);
}
+
+ @Test
+ @DisplayName("Should return 200 and filtered users when filterUsers succeeds")
+ void shouldReturnOkWhenFilterUsersSucceeds() {
+ UserFilter filter = new UserFilter(Gender.MALE, null, null, null, null, null, "FR");
+ List users = List.of(
+ new UserEntity(1L, "male", "John", "Doe", "Mr", "john@example.com", "0600000000", "pic.jpg", "FR")
+ );
+ when(filterUsersUseCase.execute(filter)).thenReturn(users);
+
+ ResponseEntity> response = userHandler.filterUsers(Gender.MALE, null, null, null, null, null, "FR");
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertNotNull(response.getBody());
+ assertEquals(1, response.getBody().size());
+ assertEquals(users, response.getBody());
+ verify(filterUsersUseCase, times(1)).execute(filter);
+ }
+
+ @Test
+ @DisplayName("Should return 200 and empty list when no users match filter")
+ void shouldReturnOkWithEmptyListWhenNoUsersMatchFilter() {
+ UserFilter filter = new UserFilter(null, "NonExistent", null, null, null, null, null);
+ when(filterUsersUseCase.execute(filter)).thenReturn(List.of());
+
+ ResponseEntity> response = userHandler.filterUsers(null, "NonExistent", null, null, null, null, null);
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertNotNull(response.getBody());
+ assertTrue(response.getBody().isEmpty());
+ verify(filterUsersUseCase, times(1)).execute(filter);
+ }
}