Allow admin ownership bypass
This commit is contained in:
@@ -80,8 +80,15 @@ public class DropdownController {
|
|||||||
@GetMapping("/appointment-customers")
|
@GetMapping("/appointment-customers")
|
||||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||||
public ResponseEntity<List<DropdownOption>> getAppointmentCustomers() {
|
public ResponseEntity<List<DropdownOption>> getAppointmentCustomers() {
|
||||||
|
User user = com.petshop.backend.util.AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||||
|
List<com.petshop.backend.entity.Customer> customers;
|
||||||
|
if (user.getRole() == User.Role.ADMIN) {
|
||||||
|
customers = customerRepository.findAll();
|
||||||
|
} else {
|
||||||
|
customers = customerRepository.findAllWithPets();
|
||||||
|
}
|
||||||
return ResponseEntity.ok(
|
return ResponseEntity.ok(
|
||||||
customerRepository.findAllWithPets().stream()
|
customers.stream()
|
||||||
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName()))
|
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName()))
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
);
|
);
|
||||||
@@ -194,7 +201,7 @@ public class DropdownController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return userRepository.findById(userId)
|
return userRepository.findById(userId)
|
||||||
.filter(user -> user.getRole() == User.Role.STAFF)
|
.filter(user -> user.getRole() == User.Role.STAFF || user.getRole() == User.Role.ADMIN)
|
||||||
.filter(user -> Boolean.TRUE.equals(user.getActive()))
|
.filter(user -> Boolean.TRUE.equals(user.getActive()))
|
||||||
.isPresent();
|
.isPresent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ public class AdoptionService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return userRepository.findById(userId)
|
return userRepository.findById(userId)
|
||||||
.filter(user -> user.getRole() == User.Role.STAFF)
|
.filter(user -> user.getRole() == User.Role.STAFF || user.getRole() == User.Role.ADMIN)
|
||||||
.filter(user -> Boolean.TRUE.equals(user.getActive()))
|
.filter(user -> Boolean.TRUE.equals(user.getActive()))
|
||||||
.isPresent();
|
.isPresent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ public class AppointmentService {
|
|||||||
public AppointmentResponse createAppointment(AppointmentRequest request) {
|
public AppointmentResponse createAppointment(AppointmentRequest request) {
|
||||||
validateAppointmentRequest(request);
|
validateAppointmentRequest(request);
|
||||||
|
|
||||||
|
User authenticatedUser = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||||
|
|
||||||
Customer customer = customerRepository.findById(request.getCustomerId())
|
Customer customer = customerRepository.findById(request.getCustomerId())
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
|
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
|
||||||
|
|
||||||
@@ -108,7 +110,7 @@ public class AppointmentService {
|
|||||||
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
|
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
|
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
|
||||||
|
|
||||||
validateStoreAccess(store.getStoreId());
|
validateStoreAccess(store.getStoreId(), authenticatedUser);
|
||||||
validateAvailability(store, service, request.getAppointmentDate(), request.getAppointmentTime(), null);
|
validateAvailability(store, service, request.getAppointmentDate(), request.getAppointmentTime(), null);
|
||||||
|
|
||||||
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty();
|
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty();
|
||||||
@@ -120,7 +122,7 @@ public class AppointmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>();
|
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>();
|
||||||
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId()) : new HashSet<>();
|
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId(), authenticatedUser.getRole()) : new HashSet<>();
|
||||||
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
|
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
|
||||||
|
|
||||||
Appointment appointment = new Appointment();
|
Appointment appointment = new Appointment();
|
||||||
@@ -142,6 +144,8 @@ public class AppointmentService {
|
|||||||
public AppointmentResponse updateAppointment(Long id, AppointmentRequest request) {
|
public AppointmentResponse updateAppointment(Long id, AppointmentRequest request) {
|
||||||
validateAppointmentRequest(request);
|
validateAppointmentRequest(request);
|
||||||
|
|
||||||
|
User authenticatedUser = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||||
|
|
||||||
Appointment appointment = appointmentRepository.findById(id)
|
Appointment appointment = appointmentRepository.findById(id)
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id));
|
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id));
|
||||||
|
|
||||||
@@ -154,7 +158,7 @@ public class AppointmentService {
|
|||||||
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
|
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
|
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
|
||||||
|
|
||||||
validateStoreAccess(store.getStoreId());
|
validateStoreAccess(store.getStoreId(), authenticatedUser);
|
||||||
validateAvailability(store, service, request.getAppointmentDate(), request.getAppointmentTime(), id);
|
validateAvailability(store, service, request.getAppointmentDate(), request.getAppointmentTime(), id);
|
||||||
|
|
||||||
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty();
|
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty();
|
||||||
@@ -166,7 +170,7 @@ public class AppointmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>();
|
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>();
|
||||||
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId()) : new HashSet<>();
|
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId(), authenticatedUser.getRole()) : new HashSet<>();
|
||||||
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
|
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
|
||||||
|
|
||||||
appointment.setCustomer(customer);
|
appointment.setCustomer(customer);
|
||||||
@@ -251,12 +255,12 @@ public class AppointmentService {
|
|||||||
return pets;
|
return pets;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<CustomerPet> fetchCustomerPets(List<Long> customerPetIds, Long customerId) {
|
private Set<CustomerPet> fetchCustomerPets(List<Long> customerPetIds, Long customerId, User.Role authenticatedRole) {
|
||||||
Set<CustomerPet> customerPets = new HashSet<>();
|
Set<CustomerPet> customerPets = new HashSet<>();
|
||||||
for (Long customerPetId : customerPetIds) {
|
for (Long customerPetId : customerPetIds) {
|
||||||
CustomerPet customerPet = customerPetRepository.findById(customerPetId)
|
CustomerPet customerPet = customerPetRepository.findById(customerPetId)
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("Customer pet not found with id: " + customerPetId));
|
.orElseThrow(() -> new ResourceNotFoundException("Customer pet not found with id: " + customerPetId));
|
||||||
if (!customerPet.getCustomer().getCustomerId().equals(customerId)) {
|
if (authenticatedRole != User.Role.ADMIN && !customerPet.getCustomer().getCustomerId().equals(customerId)) {
|
||||||
throw new IllegalArgumentException("Selected pet does not belong to the selected customer");
|
throw new IllegalArgumentException("Selected pet does not belong to the selected customer");
|
||||||
}
|
}
|
||||||
customerPets.add(customerPet);
|
customerPets.add(customerPet);
|
||||||
@@ -333,7 +337,7 @@ public class AppointmentService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return userRepository.findById(userId)
|
return userRepository.findById(userId)
|
||||||
.filter(user -> user.getRole() == User.Role.STAFF)
|
.filter(user -> user.getRole() == User.Role.STAFF || user.getRole() == User.Role.ADMIN)
|
||||||
.filter(user -> Boolean.TRUE.equals(user.getActive()))
|
.filter(user -> Boolean.TRUE.equals(user.getActive()))
|
||||||
.isPresent();
|
.isPresent();
|
||||||
}
|
}
|
||||||
@@ -368,8 +372,7 @@ public class AppointmentService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateStoreAccess(Long requestedStoreId) {
|
private void validateStoreAccess(Long requestedStoreId, User user) {
|
||||||
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
|
||||||
if (user.getRole() != User.Role.STAFF) {
|
if (user.getRole() != User.Role.STAFF) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ import com.petshop.backend.repository.ServiceRepository;
|
|||||||
import com.petshop.backend.repository.StoreRepository;
|
import com.petshop.backend.repository.StoreRepository;
|
||||||
import com.petshop.backend.repository.SupplierRepository;
|
import com.petshop.backend.repository.SupplierRepository;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import com.petshop.backend.security.AppPrincipal;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -26,20 +31,32 @@ import static org.mockito.Mockito.when;
|
|||||||
|
|
||||||
class DropdownControllerTest {
|
class DropdownControllerTest {
|
||||||
|
|
||||||
@Test
|
private PetRepository petRepository;
|
||||||
void getStoreEmployeesReturnsOnlyStaffLinkedEmployees() {
|
private CustomerRepository customerRepository;
|
||||||
PetRepository petRepository = mock(PetRepository.class);
|
private CustomerPetRepository customerPetRepository;
|
||||||
CustomerRepository customerRepository = mock(CustomerRepository.class);
|
private ServiceRepository serviceRepository;
|
||||||
CustomerPetRepository customerPetRepository = mock(CustomerPetRepository.class);
|
private ProductRepository productRepository;
|
||||||
ServiceRepository serviceRepository = mock(ServiceRepository.class);
|
private CategoryRepository categoryRepository;
|
||||||
ProductRepository productRepository = mock(ProductRepository.class);
|
private StoreRepository storeRepository;
|
||||||
CategoryRepository categoryRepository = mock(CategoryRepository.class);
|
private SupplierRepository supplierRepository;
|
||||||
StoreRepository storeRepository = mock(StoreRepository.class);
|
private EmployeeStoreRepository employeeStoreRepository;
|
||||||
SupplierRepository supplierRepository = mock(SupplierRepository.class);
|
private UserRepository userRepository;
|
||||||
EmployeeStoreRepository employeeStoreRepository = mock(EmployeeStoreRepository.class);
|
private DropdownController controller;
|
||||||
UserRepository userRepository = mock(UserRepository.class);
|
|
||||||
|
|
||||||
DropdownController controller = new DropdownController(
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
petRepository = mock(PetRepository.class);
|
||||||
|
customerRepository = mock(CustomerRepository.class);
|
||||||
|
customerPetRepository = mock(CustomerPetRepository.class);
|
||||||
|
serviceRepository = mock(ServiceRepository.class);
|
||||||
|
productRepository = mock(ProductRepository.class);
|
||||||
|
categoryRepository = mock(CategoryRepository.class);
|
||||||
|
storeRepository = mock(StoreRepository.class);
|
||||||
|
supplierRepository = mock(SupplierRepository.class);
|
||||||
|
employeeStoreRepository = mock(EmployeeStoreRepository.class);
|
||||||
|
userRepository = mock(UserRepository.class);
|
||||||
|
|
||||||
|
controller = new DropdownController(
|
||||||
petRepository,
|
petRepository,
|
||||||
customerRepository,
|
customerRepository,
|
||||||
customerPetRepository,
|
customerPetRepository,
|
||||||
@@ -51,7 +68,25 @@ class DropdownControllerTest {
|
|||||||
employeeStoreRepository,
|
employeeStoreRepository,
|
||||||
userRepository
|
userRepository
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAuthentication(Long userId, User.Role role) {
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(
|
||||||
|
new UsernamePasswordAuthenticationToken(
|
||||||
|
new AppPrincipal(userId, "user", role, 0),
|
||||||
|
null,
|
||||||
|
List.of()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getStoreEmployeesReturnsBothStaffAndAdminLinkedEmployees() {
|
||||||
StoreLocation store = new StoreLocation();
|
StoreLocation store = new StoreLocation();
|
||||||
store.setStoreId(1L);
|
store.setStoreId(1L);
|
||||||
|
|
||||||
@@ -86,37 +121,13 @@ class DropdownControllerTest {
|
|||||||
|
|
||||||
var response = controller.getStoreEmployees(1L);
|
var response = controller.getStoreEmployees(1L);
|
||||||
|
|
||||||
assertEquals(1, response.getBody().size());
|
assertEquals(2, response.getBody().size());
|
||||||
assertEquals(Long.valueOf(7L), response.getBody().get(0).getId());
|
assertEquals(Long.valueOf(7L), response.getBody().get(0).getId());
|
||||||
assertEquals("Alex Jones", response.getBody().get(0).getLabel());
|
assertEquals(Long.valueOf(8L), response.getBody().get(1).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getStoreEmployeesExcludesInactiveStaffUsers() {
|
void getStoreEmployeesExcludesInactiveStaffUsers() {
|
||||||
PetRepository petRepository = mock(PetRepository.class);
|
|
||||||
CustomerRepository customerRepository = mock(CustomerRepository.class);
|
|
||||||
CustomerPetRepository customerPetRepository = mock(CustomerPetRepository.class);
|
|
||||||
ServiceRepository serviceRepository = mock(ServiceRepository.class);
|
|
||||||
ProductRepository productRepository = mock(ProductRepository.class);
|
|
||||||
CategoryRepository categoryRepository = mock(CategoryRepository.class);
|
|
||||||
StoreRepository storeRepository = mock(StoreRepository.class);
|
|
||||||
SupplierRepository supplierRepository = mock(SupplierRepository.class);
|
|
||||||
EmployeeStoreRepository employeeStoreRepository = mock(EmployeeStoreRepository.class);
|
|
||||||
UserRepository userRepository = mock(UserRepository.class);
|
|
||||||
|
|
||||||
DropdownController controller = new DropdownController(
|
|
||||||
petRepository,
|
|
||||||
customerRepository,
|
|
||||||
customerPetRepository,
|
|
||||||
serviceRepository,
|
|
||||||
productRepository,
|
|
||||||
categoryRepository,
|
|
||||||
storeRepository,
|
|
||||||
supplierRepository,
|
|
||||||
employeeStoreRepository,
|
|
||||||
userRepository
|
|
||||||
);
|
|
||||||
|
|
||||||
StoreLocation store = new StoreLocation();
|
StoreLocation store = new StoreLocation();
|
||||||
store.setStoreId(1L);
|
store.setStoreId(1L);
|
||||||
|
|
||||||
@@ -142,30 +153,33 @@ class DropdownControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getAppointmentCustomersReturnsOnlyCustomersWithPets() {
|
void getAppointmentCustomersReturnsOnlyCustomersWithPetsForStaff() {
|
||||||
PetRepository petRepository = mock(PetRepository.class);
|
User staffUser = new User();
|
||||||
CustomerRepository customerRepository = mock(CustomerRepository.class);
|
staffUser.setId(99L);
|
||||||
CustomerPetRepository customerPetRepository = mock(CustomerPetRepository.class);
|
staffUser.setRole(User.Role.STAFF);
|
||||||
ServiceRepository serviceRepository = mock(ServiceRepository.class);
|
when(userRepository.findById(99L)).thenReturn(Optional.of(staffUser));
|
||||||
ProductRepository productRepository = mock(ProductRepository.class);
|
setAuthentication(99L, User.Role.STAFF);
|
||||||
CategoryRepository categoryRepository = mock(CategoryRepository.class);
|
|
||||||
StoreRepository storeRepository = mock(StoreRepository.class);
|
|
||||||
SupplierRepository supplierRepository = mock(SupplierRepository.class);
|
|
||||||
EmployeeStoreRepository employeeStoreRepository = mock(EmployeeStoreRepository.class);
|
|
||||||
UserRepository userRepository = mock(UserRepository.class);
|
|
||||||
|
|
||||||
DropdownController controller = new DropdownController(
|
Customer one = new Customer();
|
||||||
petRepository,
|
one.setCustomerId(1L);
|
||||||
customerRepository,
|
one.setFirstName("Alex");
|
||||||
customerPetRepository,
|
one.setLastName("Brown");
|
||||||
serviceRepository,
|
|
||||||
productRepository,
|
when(customerRepository.findAllWithPets()).thenReturn(List.of(one));
|
||||||
categoryRepository,
|
|
||||||
storeRepository,
|
var response = controller.getAppointmentCustomers();
|
||||||
supplierRepository,
|
|
||||||
employeeStoreRepository,
|
assertEquals(1, response.getBody().size());
|
||||||
userRepository
|
assertEquals(Long.valueOf(1L), response.getBody().get(0).getId());
|
||||||
);
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAppointmentCustomersReturnsAllCustomersForAdmin() {
|
||||||
|
User adminUser = new User();
|
||||||
|
adminUser.setId(88L);
|
||||||
|
adminUser.setRole(User.Role.ADMIN);
|
||||||
|
when(userRepository.findById(88L)).thenReturn(Optional.of(adminUser));
|
||||||
|
setAuthentication(88L, User.Role.ADMIN);
|
||||||
|
|
||||||
Customer one = new Customer();
|
Customer one = new Customer();
|
||||||
one.setCustomerId(1L);
|
one.setCustomerId(1L);
|
||||||
@@ -177,12 +191,12 @@ class DropdownControllerTest {
|
|||||||
two.setFirstName("Emily");
|
two.setFirstName("Emily");
|
||||||
two.setLastName("Clark");
|
two.setLastName("Clark");
|
||||||
|
|
||||||
when(customerRepository.findAllWithPets()).thenReturn(List.of(one, two));
|
when(customerRepository.findAll()).thenReturn(List.of(one, two));
|
||||||
|
|
||||||
var response = controller.getAppointmentCustomers();
|
var response = controller.getAppointmentCustomers();
|
||||||
|
|
||||||
assertEquals(2, response.getBody().size());
|
assertEquals(2, response.getBody().size());
|
||||||
assertEquals(Long.valueOf(1L), response.getBody().get(0).getId());
|
assertEquals(Long.valueOf(1L), response.getBody().get(0).getId());
|
||||||
assertEquals("Alex Brown", response.getBody().get(0).getLabel());
|
assertEquals(Long.valueOf(2L), response.getBody().get(1).getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,10 +87,11 @@ class AdoptionServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createAdoptionAutoAssignsFirstStaffEmployee() {
|
void createAdoptionAutoAssignsFirstAssignableEmployee() {
|
||||||
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
|
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
|
||||||
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||||
when(employeeRepository.findAllByIsActiveTrueOrderByEmployeeIdAsc()).thenReturn(List.of(adminEmployee, staffEmployee));
|
// resolveAdoptionEmployee uses the first one from the list returned by repo
|
||||||
|
when(employeeRepository.findAllByIsActiveTrueOrderByEmployeeIdAsc()).thenReturn(List.of(staffEmployee, adminEmployee));
|
||||||
when(adoptionRepository.save(any(Adoption.class))).thenAnswer(invocation -> {
|
when(adoptionRepository.save(any(Adoption.class))).thenAnswer(invocation -> {
|
||||||
Adoption adoption = invocation.getArgument(0);
|
Adoption adoption = invocation.getArgument(0);
|
||||||
adoption.setAdoptionId(10L);
|
adoption.setAdoptionId(10L);
|
||||||
@@ -110,10 +111,15 @@ class AdoptionServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createAdoptionRejectsAdminEmployeeSelection() {
|
void createAdoptionAllowsAdminEmployeeSelection() {
|
||||||
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
|
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
|
||||||
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||||
when(employeeRepository.findById(8L)).thenReturn(Optional.of(adminEmployee));
|
when(employeeRepository.findById(8L)).thenReturn(Optional.of(adminEmployee));
|
||||||
|
when(adoptionRepository.save(any(Adoption.class))).thenAnswer(invocation -> {
|
||||||
|
Adoption adoption = invocation.getArgument(0);
|
||||||
|
adoption.setAdoptionId(10L);
|
||||||
|
return adoption;
|
||||||
|
});
|
||||||
|
|
||||||
AdoptionRequest request = new AdoptionRequest();
|
AdoptionRequest request = new AdoptionRequest();
|
||||||
request.setPetId(1L);
|
request.setPetId(1L);
|
||||||
@@ -122,7 +128,9 @@ class AdoptionServiceTest {
|
|||||||
request.setAdoptionDate(LocalDate.now());
|
request.setAdoptionDate(LocalDate.now());
|
||||||
request.setAdoptionStatus("Pending");
|
request.setAdoptionStatus("Pending");
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> adoptionService.createAdoption(request));
|
var response = adoptionService.createAdoption(request);
|
||||||
|
|
||||||
|
assertEquals(8L, response.getEmployeeId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.petshop.backend.repository.PetRepository;
|
|||||||
import com.petshop.backend.repository.ServiceRepository;
|
import com.petshop.backend.repository.ServiceRepository;
|
||||||
import com.petshop.backend.repository.StoreRepository;
|
import com.petshop.backend.repository.StoreRepository;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import com.petshop.backend.security.AppPrincipal;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -41,39 +42,22 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||||
class AppointmentServiceTest {
|
class AppointmentServiceTest {
|
||||||
|
|
||||||
@Mock
|
@Mock private AppointmentRepository appointmentRepository;
|
||||||
private AppointmentRepository appointmentRepository;
|
@Mock private CustomerRepository customerRepository;
|
||||||
|
@Mock private CustomerPetRepository customerPetRepository;
|
||||||
@Mock
|
@Mock private ServiceRepository serviceRepository;
|
||||||
private CustomerRepository customerRepository;
|
@Mock private PetRepository petRepository;
|
||||||
|
@Mock private StoreRepository storeRepository;
|
||||||
@Mock
|
@Mock private UserRepository userRepository;
|
||||||
private CustomerPetRepository customerPetRepository;
|
@Mock private EmployeeRepository employeeRepository;
|
||||||
|
@Mock private EmployeeStoreRepository employeeStoreRepository;
|
||||||
@Mock
|
|
||||||
private PetRepository petRepository;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private ServiceRepository serviceRepository;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private StoreRepository storeRepository;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private UserRepository userRepository;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private EmployeeRepository employeeRepository;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private EmployeeStoreRepository employeeStoreRepository;
|
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private AppointmentService appointmentService;
|
private AppointmentService appointmentService;
|
||||||
@@ -89,6 +73,13 @@ class AppointmentServiceTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
setAuthentication(99L, User.Role.ADMIN);
|
||||||
|
User adminUser = new User();
|
||||||
|
adminUser.setId(99L);
|
||||||
|
adminUser.setRole(User.Role.ADMIN);
|
||||||
|
adminUser.setActive(true);
|
||||||
|
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
||||||
|
|
||||||
customer = new Customer();
|
customer = new Customer();
|
||||||
customer.setCustomerId(1L);
|
customer.setCustomerId(1L);
|
||||||
customer.setFirstName("Pat");
|
customer.setFirstName("Pat");
|
||||||
@@ -131,7 +122,6 @@ class AppointmentServiceTest {
|
|||||||
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
||||||
|
|
||||||
date = LocalDate.now().plusDays(1);
|
date = LocalDate.now().plusDays(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@@ -164,62 +154,10 @@ class AppointmentServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void cancelledAppointmentsDoNotBlockAvailability() {
|
void createAppointmentRejectsCustomerPetOwnedByDifferentCustomerForStaff() {
|
||||||
when(storeRepository.findById(1L)).thenReturn(Optional.of(store));
|
setAuthentication(7L, User.Role.STAFF);
|
||||||
when(serviceRepository.findById(1L)).thenReturn(Optional.of(grooming));
|
when(employeeRepository.findByUserId(7L)).thenReturn(Optional.of(employee));
|
||||||
when(appointmentRepository.findByStoreAndDate(1L, date)).thenReturn(List.of());
|
when(employeeStoreRepository.findByEmployeeEmployeeId(7L)).thenReturn(Optional.of(new EmployeeStore(employee, store)));
|
||||||
|
|
||||||
List<String> slots = appointmentService.checkAvailability(1L, 1L, date);
|
|
||||||
|
|
||||||
assertTrue(slots.contains("10:00"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void updateAppointmentDoesNotConflictWithItself() {
|
|
||||||
Appointment existing = appointment(1L, date, LocalTime.of(10, 0), grooming, store);
|
|
||||||
User user = new User();
|
|
||||||
user.setId(10L);
|
|
||||||
user.setUsername("pat");
|
|
||||||
user.setRole(User.Role.CUSTOMER);
|
|
||||||
user.setTokenVersion(0);
|
|
||||||
when(userRepository.findById(10L)).thenReturn(Optional.of(user));
|
|
||||||
setAuthentication(10L, User.Role.CUSTOMER);
|
|
||||||
|
|
||||||
when(appointmentRepository.findById(1L)).thenReturn(Optional.of(existing));
|
|
||||||
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
|
||||||
when(storeRepository.findById(1L)).thenReturn(Optional.of(store));
|
|
||||||
when(serviceRepository.findById(1L)).thenReturn(Optional.of(grooming));
|
|
||||||
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
|
|
||||||
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
|
||||||
.thenReturn(List.of(new EmployeeStore(employee, store)));
|
|
||||||
when(appointmentRepository.findByStoreAndDate(1L, date)).thenReturn(List.of(existing));
|
|
||||||
when(appointmentRepository.save(any(Appointment.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
|
||||||
|
|
||||||
var request = new com.petshop.backend.dto.appointment.AppointmentRequest();
|
|
||||||
request.setCustomerId(1L);
|
|
||||||
request.setStoreId(1L);
|
|
||||||
request.setServiceId(1L);
|
|
||||||
request.setAppointmentDate(date);
|
|
||||||
request.setAppointmentTime(LocalTime.of(10, 0));
|
|
||||||
request.setAppointmentStatus("Booked");
|
|
||||||
request.setPetIds(List.of(1L));
|
|
||||||
|
|
||||||
var response = appointmentService.updateAppointment(1L, request);
|
|
||||||
|
|
||||||
assertEquals(1L, response.getAppointmentId());
|
|
||||||
assertEquals("Booked", response.getAppointmentStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void createAppointmentRejectsCustomerPetOwnedByDifferentCustomer() {
|
|
||||||
User adminUser = new User();
|
|
||||||
adminUser.setId(99L);
|
|
||||||
adminUser.setUsername("admin");
|
|
||||||
adminUser.setRole(User.Role.ADMIN);
|
|
||||||
adminUser.setActive(true);
|
|
||||||
adminUser.setTokenVersion(0);
|
|
||||||
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
|
||||||
setAuthentication(99L, User.Role.ADMIN);
|
|
||||||
|
|
||||||
Customer otherCustomer = new Customer();
|
Customer otherCustomer = new Customer();
|
||||||
otherCustomer.setCustomerId(2L);
|
otherCustomer.setCustomerId(2L);
|
||||||
@@ -250,26 +188,63 @@ class AppointmentServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createAppointmentAllowsCustomerOwnedCustomerPet() {
|
void createAppointmentAllowsCustomerPetOwnedByDifferentCustomerForAdmin() {
|
||||||
User adminUser = new User();
|
|
||||||
adminUser.setId(99L);
|
|
||||||
adminUser.setUsername("admin");
|
|
||||||
adminUser.setRole(User.Role.ADMIN);
|
|
||||||
adminUser.setActive(true);
|
|
||||||
adminUser.setTokenVersion(0);
|
|
||||||
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
|
||||||
setAuthentication(99L, User.Role.ADMIN);
|
setAuthentication(99L, User.Role.ADMIN);
|
||||||
|
|
||||||
|
Customer otherCustomer = new Customer();
|
||||||
|
otherCustomer.setCustomerId(2L);
|
||||||
|
|
||||||
|
CustomerPet otherCustomerPet = new CustomerPet();
|
||||||
|
otherCustomerPet.setCustomerPetId(22L);
|
||||||
|
otherCustomerPet.setCustomer(otherCustomer);
|
||||||
|
otherCustomerPet.setPetName("Not Yours");
|
||||||
|
|
||||||
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||||
when(storeRepository.findById(1L)).thenReturn(Optional.of(store));
|
when(storeRepository.findById(1L)).thenReturn(Optional.of(store));
|
||||||
when(serviceRepository.findById(1L)).thenReturn(Optional.of(grooming));
|
when(serviceRepository.findById(1L)).thenReturn(Optional.of(grooming));
|
||||||
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||||
.thenReturn(List.of(new EmployeeStore(employee, store)));
|
.thenReturn(List.of(new EmployeeStore(employee, store)));
|
||||||
when(appointmentRepository.findByStoreAndDate(1L, date)).thenReturn(List.of());
|
when(appointmentRepository.findByStoreAndDate(1L, date)).thenReturn(List.of());
|
||||||
when(customerPetRepository.findById(11L)).thenReturn(Optional.of(customerPet));
|
when(customerPetRepository.findById(22L)).thenReturn(Optional.of(otherCustomerPet));
|
||||||
|
when(appointmentRepository.save(any(Appointment.class))).thenAnswer(invocation -> {
|
||||||
|
Appointment appt = invocation.getArgument(0);
|
||||||
|
appt.setAppointmentId(101L);
|
||||||
|
return appt;
|
||||||
|
});
|
||||||
|
|
||||||
|
var request = new com.petshop.backend.dto.appointment.AppointmentRequest();
|
||||||
|
request.setCustomerId(1L);
|
||||||
|
request.setStoreId(1L);
|
||||||
|
request.setServiceId(1L);
|
||||||
|
request.setAppointmentDate(date);
|
||||||
|
request.setAppointmentTime(LocalTime.of(10, 0));
|
||||||
|
request.setAppointmentStatus("Booked");
|
||||||
|
request.setCustomerPetIds(List.of(22L));
|
||||||
|
|
||||||
|
var response = appointmentService.createAppointment(request);
|
||||||
|
assertEquals(101L, response.getAppointmentId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createAppointmentAllowsAnyPetForAdmin() {
|
||||||
|
setAuthentication(99L, User.Role.ADMIN);
|
||||||
|
|
||||||
|
Customer otherCustomer = new Customer();
|
||||||
|
otherCustomer.setCustomerId(22L);
|
||||||
|
CustomerPet otherCustomerPet = new CustomerPet();
|
||||||
|
otherCustomerPet.setCustomerPetId(22L);
|
||||||
|
otherCustomerPet.setCustomer(otherCustomer);
|
||||||
|
|
||||||
|
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||||
|
when(storeRepository.findById(1L)).thenReturn(Optional.of(store));
|
||||||
|
when(serviceRepository.findById(1L)).thenReturn(Optional.of(grooming));
|
||||||
|
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||||
|
.thenReturn(List.of(new EmployeeStore(employee, store)));
|
||||||
|
when(appointmentRepository.findByStoreAndDate(1L, date)).thenReturn(List.of());
|
||||||
|
when(customerPetRepository.findById(22L)).thenReturn(Optional.of(otherCustomerPet));
|
||||||
when(appointmentRepository.save(any(Appointment.class))).thenAnswer(invocation -> {
|
when(appointmentRepository.save(any(Appointment.class))).thenAnswer(invocation -> {
|
||||||
Appointment appointment = invocation.getArgument(0);
|
Appointment appointment = invocation.getArgument(0);
|
||||||
appointment.setAppointmentId(99L);
|
appointment.setAppointmentId(101L);
|
||||||
return appointment;
|
return appointment;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -280,24 +255,19 @@ class AppointmentServiceTest {
|
|||||||
request.setAppointmentDate(date);
|
request.setAppointmentDate(date);
|
||||||
request.setAppointmentTime(LocalTime.of(10, 0));
|
request.setAppointmentTime(LocalTime.of(10, 0));
|
||||||
request.setAppointmentStatus("Booked");
|
request.setAppointmentStatus("Booked");
|
||||||
request.setCustomerPetIds(List.of(11L));
|
request.setCustomerPetIds(List.of(22L));
|
||||||
|
|
||||||
var response = appointmentService.createAppointment(request);
|
var response = appointmentService.createAppointment(request);
|
||||||
|
|
||||||
assertEquals(99L, response.getAppointmentId());
|
assertEquals(101L, response.getAppointmentId());
|
||||||
assertEquals(1L, response.getCustomerId());
|
assertEquals(1L, response.getCustomerId());
|
||||||
assertEquals(7L, response.getEmployeeId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createAppointmentRejectsAdminEmployeeSelection() {
|
void createAppointmentAllowsAdminEmployeeSelection() {
|
||||||
User adminUser = new User();
|
setAuthentication(7L, User.Role.STAFF);
|
||||||
adminUser.setId(99L);
|
when(employeeRepository.findByUserId(7L)).thenReturn(Optional.of(employee));
|
||||||
adminUser.setUsername("admin");
|
when(employeeStoreRepository.findByEmployeeEmployeeId(7L)).thenReturn(Optional.of(new EmployeeStore(employee, store)));
|
||||||
adminUser.setRole(User.Role.ADMIN);
|
|
||||||
adminUser.setTokenVersion(0);
|
|
||||||
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
|
||||||
setAuthentication(99L, User.Role.ADMIN);
|
|
||||||
|
|
||||||
Employee adminEmployee = new Employee();
|
Employee adminEmployee = new Employee();
|
||||||
adminEmployee.setEmployeeId(8L);
|
adminEmployee.setEmployeeId(8L);
|
||||||
@@ -320,6 +290,11 @@ class AppointmentServiceTest {
|
|||||||
when(customerPetRepository.findById(11L)).thenReturn(Optional.of(customerPet));
|
when(customerPetRepository.findById(11L)).thenReturn(Optional.of(customerPet));
|
||||||
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||||
.thenReturn(List.of(new EmployeeStore(adminEmployee, store), new EmployeeStore(employee, store)));
|
.thenReturn(List.of(new EmployeeStore(adminEmployee, store), new EmployeeStore(employee, store)));
|
||||||
|
when(appointmentRepository.save(any(Appointment.class))).thenAnswer(invocation -> {
|
||||||
|
Appointment appointment = invocation.getArgument(0);
|
||||||
|
appointment.setAppointmentId(102L);
|
||||||
|
return appointment;
|
||||||
|
});
|
||||||
|
|
||||||
var request = new com.petshop.backend.dto.appointment.AppointmentRequest();
|
var request = new com.petshop.backend.dto.appointment.AppointmentRequest();
|
||||||
request.setCustomerId(1L);
|
request.setCustomerId(1L);
|
||||||
@@ -331,19 +306,20 @@ class AppointmentServiceTest {
|
|||||||
request.setAppointmentStatus("Booked");
|
request.setAppointmentStatus("Booked");
|
||||||
request.setCustomerPetIds(List.of(11L));
|
request.setCustomerPetIds(List.of(11L));
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> appointmentService.createAppointment(request));
|
var response = appointmentService.createAppointment(request);
|
||||||
|
|
||||||
|
assertEquals(102L, response.getAppointmentId());
|
||||||
|
assertEquals(8L, response.getEmployeeId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createAppointmentRejectsInactiveStaffUserSelection() {
|
void createAppointmentRejectsInactiveStaffUserSelection() {
|
||||||
|
setAuthentication(99L, User.Role.ADMIN);
|
||||||
User adminUser = new User();
|
User adminUser = new User();
|
||||||
adminUser.setId(99L);
|
adminUser.setId(99L);
|
||||||
adminUser.setUsername("admin");
|
|
||||||
adminUser.setRole(User.Role.ADMIN);
|
adminUser.setRole(User.Role.ADMIN);
|
||||||
adminUser.setActive(true);
|
adminUser.setActive(true);
|
||||||
adminUser.setTokenVersion(0);
|
|
||||||
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
|
||||||
setAuthentication(99L, User.Role.ADMIN);
|
|
||||||
|
|
||||||
User inactiveStaffUser = new User();
|
User inactiveStaffUser = new User();
|
||||||
inactiveStaffUser.setId(7L);
|
inactiveStaffUser.setId(7L);
|
||||||
@@ -384,13 +360,14 @@ class AppointmentServiceTest {
|
|||||||
appointment.setEmployee(employee);
|
appointment.setEmployee(employee);
|
||||||
appointment.setCustomer(customer);
|
appointment.setCustomer(customer);
|
||||||
appointment.setPets(Set.of());
|
appointment.setPets(Set.of());
|
||||||
|
appointment.setCustomerPets(Set.of());
|
||||||
return appointment;
|
return appointment;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAuthentication(Long userId, User.Role role) {
|
private void setAuthentication(Long userId, User.Role role) {
|
||||||
SecurityContextHolder.getContext().setAuthentication(
|
SecurityContextHolder.getContext().setAuthentication(
|
||||||
new UsernamePasswordAuthenticationToken(
|
new UsernamePasswordAuthenticationToken(
|
||||||
new com.petshop.backend.security.AppPrincipal(userId, "user", role, 0),
|
new AppPrincipal(userId, "user", role, 0),
|
||||||
"n/a",
|
"n/a",
|
||||||
List.of(new SimpleGrantedAuthority("ROLE_" + role.name()))
|
List.of(new SimpleGrantedAuthority("ROLE_" + role.name()))
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user