Allow admin ownership bypass

This commit is contained in:
2026-04-05 16:01:46 -06:00
parent 30b5041ae5
commit c84d817810
6 changed files with 199 additions and 190 deletions

View File

@@ -80,8 +80,15 @@ public class DropdownController {
@GetMapping("/appointment-customers")
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
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(
customerRepository.findAllWithPets().stream()
customers.stream()
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName()))
.collect(Collectors.toList())
);
@@ -194,7 +201,7 @@ public class DropdownController {
return false;
}
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()))
.isPresent();
}

View File

@@ -173,7 +173,7 @@ public class AdoptionService {
return false;
}
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()))
.isPresent();
}

View File

@@ -99,6 +99,8 @@ public class AppointmentService {
public AppointmentResponse createAppointment(AppointmentRequest request) {
validateAppointmentRequest(request);
User authenticatedUser = AuthenticationHelper.getAuthenticatedUser(userRepository);
Customer customer = customerRepository.findById(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())
.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);
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<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());
Appointment appointment = new Appointment();
@@ -142,6 +144,8 @@ public class AppointmentService {
public AppointmentResponse updateAppointment(Long id, AppointmentRequest request) {
validateAppointmentRequest(request);
User authenticatedUser = AuthenticationHelper.getAuthenticatedUser(userRepository);
Appointment appointment = appointmentRepository.findById(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())
.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);
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<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());
appointment.setCustomer(customer);
@@ -251,12 +255,12 @@ public class AppointmentService {
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<>();
for (Long customerPetId : customerPetIds) {
CustomerPet customerPet = customerPetRepository.findById(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");
}
customerPets.add(customerPet);
@@ -333,7 +337,7 @@ public class AppointmentService {
return false;
}
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()))
.isPresent();
}
@@ -368,8 +372,7 @@ public class AppointmentService {
return true;
}
private void validateStoreAccess(Long requestedStoreId) {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
private void validateStoreAccess(Long requestedStoreId, User user) {
if (user.getRole() != User.Role.STAFF) {
return;
}

View File

@@ -15,7 +15,12 @@ import com.petshop.backend.repository.ServiceRepository;
import com.petshop.backend.repository.StoreRepository;
import com.petshop.backend.repository.SupplierRepository;
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.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.List;
import java.util.Optional;
@@ -26,20 +31,32 @@ import static org.mockito.Mockito.when;
class DropdownControllerTest {
@Test
void getStoreEmployeesReturnsOnlyStaffLinkedEmployees() {
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);
private PetRepository petRepository;
private CustomerRepository customerRepository;
private CustomerPetRepository customerPetRepository;
private ServiceRepository serviceRepository;
private ProductRepository productRepository;
private CategoryRepository categoryRepository;
private StoreRepository storeRepository;
private SupplierRepository supplierRepository;
private EmployeeStoreRepository employeeStoreRepository;
private UserRepository userRepository;
private DropdownController controller;
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,
customerRepository,
customerPetRepository,
@@ -51,7 +68,25 @@ class DropdownControllerTest {
employeeStoreRepository,
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();
store.setStoreId(1L);
@@ -86,37 +121,13 @@ class DropdownControllerTest {
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("Alex Jones", response.getBody().get(0).getLabel());
assertEquals(Long.valueOf(8L), response.getBody().get(1).getId());
}
@Test
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();
store.setStoreId(1L);
@@ -142,30 +153,33 @@ class DropdownControllerTest {
}
@Test
void getAppointmentCustomersReturnsOnlyCustomersWithPets() {
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);
void getAppointmentCustomersReturnsOnlyCustomersWithPetsForStaff() {
User staffUser = new User();
staffUser.setId(99L);
staffUser.setRole(User.Role.STAFF);
when(userRepository.findById(99L)).thenReturn(Optional.of(staffUser));
setAuthentication(99L, User.Role.STAFF);
DropdownController controller = new DropdownController(
petRepository,
customerRepository,
customerPetRepository,
serviceRepository,
productRepository,
categoryRepository,
storeRepository,
supplierRepository,
employeeStoreRepository,
userRepository
);
Customer one = new Customer();
one.setCustomerId(1L);
one.setFirstName("Alex");
one.setLastName("Brown");
when(customerRepository.findAllWithPets()).thenReturn(List.of(one));
var response = controller.getAppointmentCustomers();
assertEquals(1, response.getBody().size());
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();
one.setCustomerId(1L);
@@ -177,12 +191,12 @@ class DropdownControllerTest {
two.setFirstName("Emily");
two.setLastName("Clark");
when(customerRepository.findAllWithPets()).thenReturn(List.of(one, two));
when(customerRepository.findAll()).thenReturn(List.of(one, two));
var response = controller.getAppointmentCustomers();
assertEquals(2, response.getBody().size());
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());
}
}

View File

@@ -87,10 +87,11 @@ class AdoptionServiceTest {
}
@Test
void createAdoptionAutoAssignsFirstStaffEmployee() {
void createAdoptionAutoAssignsFirstAssignableEmployee() {
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
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 -> {
Adoption adoption = invocation.getArgument(0);
adoption.setAdoptionId(10L);
@@ -110,10 +111,15 @@ class AdoptionServiceTest {
}
@Test
void createAdoptionRejectsAdminEmployeeSelection() {
void createAdoptionAllowsAdminEmployeeSelection() {
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
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();
request.setPetId(1L);
@@ -122,7 +128,9 @@ class AdoptionServiceTest {
request.setAdoptionDate(LocalDate.now());
request.setAdoptionStatus("Pending");
assertThrows(IllegalArgumentException.class, () -> adoptionService.createAdoption(request));
var response = adoptionService.createAdoption(request);
assertEquals(8L, response.getEmployeeId());
}
@Test

View File

@@ -18,6 +18,7 @@ import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.ServiceRepository;
import com.petshop.backend.repository.StoreRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.security.AppPrincipal;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
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.assertFalse;
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;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class AppointmentServiceTest {
@Mock
private AppointmentRepository appointmentRepository;
@Mock
private CustomerRepository customerRepository;
@Mock
private CustomerPetRepository customerPetRepository;
@Mock
private PetRepository petRepository;
@Mock
private ServiceRepository serviceRepository;
@Mock
private StoreRepository storeRepository;
@Mock
private UserRepository userRepository;
@Mock
private EmployeeRepository employeeRepository;
@Mock
private EmployeeStoreRepository employeeStoreRepository;
@Mock private AppointmentRepository appointmentRepository;
@Mock private CustomerRepository customerRepository;
@Mock private CustomerPetRepository customerPetRepository;
@Mock private ServiceRepository serviceRepository;
@Mock private PetRepository petRepository;
@Mock private StoreRepository storeRepository;
@Mock private UserRepository userRepository;
@Mock private EmployeeRepository employeeRepository;
@Mock private EmployeeStoreRepository employeeStoreRepository;
@InjectMocks
private AppointmentService appointmentService;
@@ -89,6 +73,13 @@ class AppointmentServiceTest {
@BeforeEach
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.setCustomerId(1L);
customer.setFirstName("Pat");
@@ -131,7 +122,6 @@ class AppointmentServiceTest {
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
date = LocalDate.now().plusDays(1);
}
@AfterEach
@@ -164,62 +154,10 @@ class AppointmentServiceTest {
}
@Test
void cancelledAppointmentsDoNotBlockAvailability() {
when(storeRepository.findById(1L)).thenReturn(Optional.of(store));
when(serviceRepository.findById(1L)).thenReturn(Optional.of(grooming));
when(appointmentRepository.findByStoreAndDate(1L, date)).thenReturn(List.of());
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);
void createAppointmentRejectsCustomerPetOwnedByDifferentCustomerForStaff() {
setAuthentication(7L, User.Role.STAFF);
when(employeeRepository.findByUserId(7L)).thenReturn(Optional.of(employee));
when(employeeStoreRepository.findByEmployeeEmployeeId(7L)).thenReturn(Optional.of(new EmployeeStore(employee, store)));
Customer otherCustomer = new Customer();
otherCustomer.setCustomerId(2L);
@@ -250,26 +188,63 @@ class AppointmentServiceTest {
}
@Test
void createAppointmentAllowsCustomerOwnedCustomerPet() {
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));
void createAppointmentAllowsCustomerPetOwnedByDifferentCustomerForAdmin() {
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(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(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 -> {
Appointment appointment = invocation.getArgument(0);
appointment.setAppointmentId(99L);
appointment.setAppointmentId(101L);
return appointment;
});
@@ -280,24 +255,19 @@ class AppointmentServiceTest {
request.setAppointmentDate(date);
request.setAppointmentTime(LocalTime.of(10, 0));
request.setAppointmentStatus("Booked");
request.setCustomerPetIds(List.of(11L));
request.setCustomerPetIds(List.of(22L));
var response = appointmentService.createAppointment(request);
assertEquals(99L, response.getAppointmentId());
assertEquals(101L, response.getAppointmentId());
assertEquals(1L, response.getCustomerId());
assertEquals(7L, response.getEmployeeId());
}
@Test
void createAppointmentRejectsAdminEmployeeSelection() {
User adminUser = new User();
adminUser.setId(99L);
adminUser.setUsername("admin");
adminUser.setRole(User.Role.ADMIN);
adminUser.setTokenVersion(0);
when(userRepository.findById(99L)).thenReturn(Optional.of(adminUser));
setAuthentication(99L, User.Role.ADMIN);
void createAppointmentAllowsAdminEmployeeSelection() {
setAuthentication(7L, User.Role.STAFF);
when(employeeRepository.findByUserId(7L)).thenReturn(Optional.of(employee));
when(employeeStoreRepository.findByEmployeeEmployeeId(7L)).thenReturn(Optional.of(new EmployeeStore(employee, store)));
Employee adminEmployee = new Employee();
adminEmployee.setEmployeeId(8L);
@@ -320,6 +290,11 @@ class AppointmentServiceTest {
when(customerPetRepository.findById(11L)).thenReturn(Optional.of(customerPet));
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
.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();
request.setCustomerId(1L);
@@ -331,19 +306,20 @@ class AppointmentServiceTest {
request.setAppointmentStatus("Booked");
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
void createAppointmentRejectsInactiveStaffUserSelection() {
setAuthentication(99L, User.Role.ADMIN);
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);
User inactiveStaffUser = new User();
inactiveStaffUser.setId(7L);
@@ -384,13 +360,14 @@ class AppointmentServiceTest {
appointment.setEmployee(employee);
appointment.setCustomer(customer);
appointment.setPets(Set.of());
appointment.setCustomerPets(Set.of());
return appointment;
}
private void setAuthentication(Long userId, User.Role role) {
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(
new com.petshop.backend.security.AppPrincipal(userId, "user", role, 0),
new AppPrincipal(userId, "user", role, 0),
"n/a",
List.of(new SimpleGrantedAuthority("ROLE_" + role.name()))
)