Close chat #169
@@ -11,7 +11,7 @@ final class RuntimeClasspathValidator {
|
||||
if (!resourceExists("application.yml")) {
|
||||
throw new IllegalStateException("Backend resources are missing from the runtime classpath. Reimport the Maven project in IntelliJ and run the shared Maven run configuration.");
|
||||
}
|
||||
if (!resourceExists("db/migration/V1__baseline_schema.sql")) {
|
||||
if (!resourceExists("db/migration/V1__target_baseline.sql")) {
|
||||
throw new IllegalStateException("Flyway migration files are missing from the runtime classpath. Reimport the Maven project in IntelliJ and run the shared Maven run configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.petshop.backend.dto.appointment;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class AppointmentRequest {
|
||||
@@ -25,7 +24,7 @@ public class AppointmentRequest {
|
||||
@NotNull(message = "Appointment status is required")
|
||||
private String appointmentStatus;
|
||||
|
||||
private List<Long> petIds;
|
||||
private Long petId;
|
||||
|
||||
private Long employeeId;
|
||||
|
||||
@@ -77,12 +76,12 @@ public class AppointmentRequest {
|
||||
this.appointmentStatus = appointmentStatus;
|
||||
}
|
||||
|
||||
public List<Long> getPetIds() {
|
||||
return petIds;
|
||||
public Long getPetId() {
|
||||
return petId;
|
||||
}
|
||||
|
||||
public void setPetIds(List<Long> petIds) {
|
||||
this.petIds = petIds;
|
||||
public void setPetId(Long petId) {
|
||||
this.petId = petId;
|
||||
}
|
||||
|
||||
public Long getEmployeeId() {
|
||||
@@ -104,13 +103,13 @@ public class AppointmentRequest {
|
||||
Objects.equals(appointmentDate, that.appointmentDate) &&
|
||||
Objects.equals(appointmentTime, that.appointmentTime) &&
|
||||
Objects.equals(appointmentStatus, that.appointmentStatus) &&
|
||||
Objects.equals(petIds, that.petIds) &&
|
||||
Objects.equals(petId, that.petId) &&
|
||||
Objects.equals(employeeId, that.employeeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(customerId, storeId, serviceId, appointmentDate, appointmentTime, appointmentStatus, petIds, employeeId);
|
||||
return Objects.hash(customerId, storeId, serviceId, appointmentDate, appointmentTime, appointmentStatus, petId, employeeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -122,7 +121,7 @@ public class AppointmentRequest {
|
||||
", appointmentDate=" + appointmentDate +
|
||||
", appointmentTime=" + appointmentTime +
|
||||
", appointmentStatus='" + appointmentStatus + '\'' +
|
||||
", petIds=" + petIds +
|
||||
", petId=" + petId +
|
||||
", employeeId=" + employeeId +
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.petshop.backend.dto.appointment;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class AppointmentResponse {
|
||||
@@ -19,15 +18,15 @@ public class AppointmentResponse {
|
||||
private String appointmentStatus;
|
||||
private Long employeeId;
|
||||
private String employeeName;
|
||||
private List<String> petNames;
|
||||
private List<Long> petIds;
|
||||
private String petName;
|
||||
private Long petId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public AppointmentResponse() {
|
||||
}
|
||||
|
||||
public AppointmentResponse(Long appointmentId, Long customerId, String customerName, Long storeId, String storeName, Long serviceId, String serviceName, LocalDate appointmentDate, LocalTime appointmentTime, String appointmentStatus, List<String> petNames, List<Long> petIds, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
public AppointmentResponse(Long appointmentId, Long customerId, String customerName, Long storeId, String storeName, Long serviceId, String serviceName, LocalDate appointmentDate, LocalTime appointmentTime, String appointmentStatus, String petName, Long petId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.appointmentId = appointmentId;
|
||||
this.customerId = customerId;
|
||||
this.customerName = customerName;
|
||||
@@ -38,8 +37,8 @@ public class AppointmentResponse {
|
||||
this.appointmentDate = appointmentDate;
|
||||
this.appointmentTime = appointmentTime;
|
||||
this.appointmentStatus = appointmentStatus;
|
||||
this.petNames = petNames;
|
||||
this.petIds = petIds;
|
||||
this.petName = petName;
|
||||
this.petId = petId;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
@@ -140,20 +139,20 @@ public class AppointmentResponse {
|
||||
this.employeeName = employeeName;
|
||||
}
|
||||
|
||||
public List<String> getPetNames() {
|
||||
return petNames;
|
||||
public String getPetName() {
|
||||
return petName;
|
||||
}
|
||||
|
||||
public void setPetNames(List<String> petNames) {
|
||||
this.petNames = petNames;
|
||||
public void setPetName(String petName) {
|
||||
this.petName = petName;
|
||||
}
|
||||
|
||||
public List<Long> getPetIds() {
|
||||
return petIds;
|
||||
public Long getPetId() {
|
||||
return petId;
|
||||
}
|
||||
|
||||
public void setPetIds(List<Long> petIds) {
|
||||
this.petIds = petIds;
|
||||
public void setPetId(Long petId) {
|
||||
this.petId = petId;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
@@ -177,12 +176,12 @@ public class AppointmentResponse {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
AppointmentResponse that = (AppointmentResponse) o;
|
||||
return Objects.equals(appointmentId, that.appointmentId) && Objects.equals(customerId, that.customerId) && Objects.equals(customerName, that.customerName) && Objects.equals(storeId, that.storeId) && Objects.equals(storeName, that.storeName) && Objects.equals(serviceId, that.serviceId) && Objects.equals(serviceName, that.serviceName) && Objects.equals(appointmentDate, that.appointmentDate) && Objects.equals(appointmentTime, that.appointmentTime) && Objects.equals(appointmentStatus, that.appointmentStatus) && Objects.equals(petNames, that.petNames) && Objects.equals(petIds, that.petIds) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
|
||||
return Objects.equals(appointmentId, that.appointmentId) && Objects.equals(customerId, that.customerId) && Objects.equals(customerName, that.customerName) && Objects.equals(storeId, that.storeId) && Objects.equals(storeName, that.storeName) && Objects.equals(serviceId, that.serviceId) && Objects.equals(serviceName, that.serviceName) && Objects.equals(appointmentDate, that.appointmentDate) && Objects.equals(appointmentTime, that.appointmentTime) && Objects.equals(appointmentStatus, that.appointmentStatus) && Objects.equals(petName, that.petName) && Objects.equals(petId, that.petId) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(appointmentId, customerId, customerName, storeId, storeName, serviceId, serviceName, appointmentDate, appointmentTime, appointmentStatus, petNames, petIds, createdAt, updatedAt);
|
||||
return Objects.hash(appointmentId, customerId, customerName, storeId, storeName, serviceId, serviceName, appointmentDate, appointmentTime, appointmentStatus, petName, petId, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -198,8 +197,8 @@ public class AppointmentResponse {
|
||||
", appointmentDate=" + appointmentDate +
|
||||
", appointmentTime=" + appointmentTime +
|
||||
", appointmentStatus='" + appointmentStatus + '\'' +
|
||||
", petNames=" + petNames +
|
||||
", petIds=" + petIds +
|
||||
", petName='" + petName + '\'' +
|
||||
", petId=" + petId +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
|
||||
@@ -7,9 +7,7 @@ import org.hibernate.annotations.UpdateTimestamp;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "appointment")
|
||||
@@ -44,13 +42,9 @@ public class Appointment {
|
||||
@Column(nullable = false, length = 20)
|
||||
private String appointmentStatus;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "appointmentPet",
|
||||
joinColumns = @JoinColumn(name = "appointmentId"),
|
||||
inverseJoinColumns = @JoinColumn(name = "petId")
|
||||
)
|
||||
private Set<Pet> pets = new HashSet<>();
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "petId")
|
||||
private Pet pet;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "created_at", updatable = false)
|
||||
@@ -127,12 +121,12 @@ public class Appointment {
|
||||
this.appointmentStatus = appointmentStatus;
|
||||
}
|
||||
|
||||
public Set<Pet> getPets() {
|
||||
return pets;
|
||||
public Pet getPet() {
|
||||
return pet;
|
||||
}
|
||||
|
||||
public void setPets(Set<Pet> pets) {
|
||||
this.pets = pets;
|
||||
public void setPet(Pet pet) {
|
||||
this.pet = pet;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
@@ -175,7 +169,7 @@ public class Appointment {
|
||||
", appointmentDate=" + appointmentDate +
|
||||
", appointmentTime=" + appointmentTime +
|
||||
", appointmentStatus='" + appointmentStatus + '\'' +
|
||||
", pets=" + pets +
|
||||
", pet=" + pet +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
|
||||
@@ -20,7 +20,7 @@ spring:
|
||||
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
ddl-auto: validate
|
||||
naming:
|
||||
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
|
||||
show-sql: ${JPA_SHOW_SQL:false}
|
||||
@@ -32,7 +32,7 @@ spring:
|
||||
flyway:
|
||||
enabled: true
|
||||
baseline-on-migrate: true
|
||||
baseline-version: 0
|
||||
baseline-version: 1
|
||||
|
||||
server:
|
||||
port: ${SERVER_PORT:8080}
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
package com.petshop.backend.controller;
|
||||
|
||||
import com.petshop.backend.entity.Employee;
|
||||
import com.petshop.backend.entity.EmployeeStore;
|
||||
import com.petshop.backend.entity.Customer;
|
||||
import com.petshop.backend.entity.StoreLocation;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.CategoryRepository;
|
||||
import com.petshop.backend.repository.CustomerPetRepository;
|
||||
import com.petshop.backend.repository.CustomerRepository;
|
||||
import com.petshop.backend.repository.EmployeeStoreRepository;
|
||||
import com.petshop.backend.repository.PetRepository;
|
||||
import com.petshop.backend.repository.ProductRepository;
|
||||
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;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class DropdownControllerTest {
|
||||
|
||||
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;
|
||||
|
||||
@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,
|
||||
serviceRepository,
|
||||
productRepository,
|
||||
categoryRepository,
|
||||
storeRepository,
|
||||
supplierRepository,
|
||||
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 getStoreEmployeesReturnsOnlyStaffLinkedEmployees() {
|
||||
StoreLocation store = new StoreLocation();
|
||||
store.setStoreId(1L);
|
||||
|
||||
Employee staffEmployee = new Employee();
|
||||
staffEmployee.setEmployeeId(7L);
|
||||
staffEmployee.setUserId(7L);
|
||||
staffEmployee.setFirstName("Alex");
|
||||
staffEmployee.setLastName("Jones");
|
||||
staffEmployee.setIsActive(true);
|
||||
|
||||
Employee adminEmployee = new Employee();
|
||||
adminEmployee.setEmployeeId(8L);
|
||||
adminEmployee.setUserId(8L);
|
||||
adminEmployee.setFirstName("Admin");
|
||||
adminEmployee.setLastName("Helper");
|
||||
adminEmployee.setIsActive(true);
|
||||
|
||||
User staffUser = new User();
|
||||
staffUser.setId(7L);
|
||||
staffUser.setRole(User.Role.STAFF);
|
||||
staffUser.setActive(true);
|
||||
|
||||
User adminUser = new User();
|
||||
adminUser.setId(8L);
|
||||
adminUser.setRole(User.Role.ADMIN);
|
||||
adminUser.setActive(true);
|
||||
|
||||
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||
.thenReturn(List.of(new EmployeeStore(staffEmployee, store), new EmployeeStore(adminEmployee, store)));
|
||||
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
||||
when(userRepository.findById(8L)).thenReturn(Optional.of(adminUser));
|
||||
|
||||
var response = controller.getStoreEmployees(1L);
|
||||
|
||||
assertEquals(1, response.getBody().size());
|
||||
assertEquals(Long.valueOf(7L), response.getBody().get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getStoreEmployeesReturnsAllStaffWhenStoreIdIsNull() {
|
||||
StoreLocation store = new StoreLocation();
|
||||
store.setStoreId(1L);
|
||||
|
||||
Employee staffEmployee = new Employee();
|
||||
staffEmployee.setEmployeeId(7L);
|
||||
staffEmployee.setUserId(7L);
|
||||
staffEmployee.setFirstName("Alex");
|
||||
staffEmployee.setLastName("Jones");
|
||||
staffEmployee.setIsActive(true);
|
||||
|
||||
User staffUser = new User();
|
||||
staffUser.setId(7L);
|
||||
staffUser.setRole(User.Role.STAFF);
|
||||
staffUser.setActive(true);
|
||||
|
||||
when(employeeStoreRepository.findActiveAllOrderByEmployeeEmployeeIdAsc())
|
||||
.thenReturn(List.of(new EmployeeStore(staffEmployee, store)));
|
||||
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
||||
|
||||
var response = controller.getStoreEmployees(null);
|
||||
|
||||
assertEquals(1, response.getBody().size());
|
||||
assertEquals(Long.valueOf(7L), response.getBody().get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getStoreEmployeesExcludesInactiveStaffUsers() {
|
||||
StoreLocation store = new StoreLocation();
|
||||
store.setStoreId(1L);
|
||||
|
||||
Employee inactiveStaffEmployee = new Employee();
|
||||
inactiveStaffEmployee.setEmployeeId(7L);
|
||||
inactiveStaffEmployee.setUserId(7L);
|
||||
inactiveStaffEmployee.setFirstName("Alex");
|
||||
inactiveStaffEmployee.setLastName("Jones");
|
||||
inactiveStaffEmployee.setIsActive(true);
|
||||
|
||||
User inactiveStaffUser = new User();
|
||||
inactiveStaffUser.setId(7L);
|
||||
inactiveStaffUser.setRole(User.Role.STAFF);
|
||||
inactiveStaffUser.setActive(false);
|
||||
|
||||
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||
.thenReturn(List.of(new EmployeeStore(inactiveStaffEmployee, store)));
|
||||
when(userRepository.findById(7L)).thenReturn(Optional.of(inactiveStaffUser));
|
||||
|
||||
var response = controller.getStoreEmployees(1L);
|
||||
|
||||
assertEquals(0, response.getBody().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import com.petshop.backend.dto.adoption.AdoptionRequest;
|
||||
import com.petshop.backend.entity.Adoption;
|
||||
import com.petshop.backend.entity.Customer;
|
||||
import com.petshop.backend.entity.Employee;
|
||||
import com.petshop.backend.entity.Pet;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.AdoptionRepository;
|
||||
import com.petshop.backend.repository.CustomerRepository;
|
||||
import com.petshop.backend.repository.EmployeeRepository;
|
||||
import com.petshop.backend.repository.PetRepository;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class AdoptionServiceTest {
|
||||
|
||||
@Mock private AdoptionRepository adoptionRepository;
|
||||
@Mock private PetRepository petRepository;
|
||||
@Mock private CustomerRepository customerRepository;
|
||||
@Mock private EmployeeRepository employeeRepository;
|
||||
@Mock private UserRepository userRepository;
|
||||
|
||||
@InjectMocks
|
||||
private AdoptionService adoptionService;
|
||||
|
||||
private Pet pet;
|
||||
private Customer customer;
|
||||
private Employee staffEmployee;
|
||||
private Employee adminEmployee;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
pet = new Pet();
|
||||
pet.setPetId(1L);
|
||||
pet.setPetName("Buddy");
|
||||
pet.setPetStatus("Available");
|
||||
|
||||
customer = new Customer();
|
||||
customer.setCustomerId(1L);
|
||||
customer.setFirstName("Pat");
|
||||
customer.setLastName("Owner");
|
||||
|
||||
staffEmployee = new Employee();
|
||||
staffEmployee.setEmployeeId(7L);
|
||||
staffEmployee.setUserId(7L);
|
||||
staffEmployee.setFirstName("Alex");
|
||||
staffEmployee.setLastName("Jones");
|
||||
staffEmployee.setIsActive(true);
|
||||
|
||||
adminEmployee = new Employee();
|
||||
adminEmployee.setEmployeeId(8L);
|
||||
adminEmployee.setUserId(8L);
|
||||
adminEmployee.setFirstName("Admin");
|
||||
adminEmployee.setLastName("Helper");
|
||||
adminEmployee.setIsActive(true);
|
||||
|
||||
User staffUser = new User();
|
||||
staffUser.setId(7L);
|
||||
staffUser.setRole(User.Role.STAFF);
|
||||
staffUser.setActive(true);
|
||||
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
||||
|
||||
User adminUser = new User();
|
||||
adminUser.setId(8L);
|
||||
adminUser.setRole(User.Role.ADMIN);
|
||||
adminUser.setActive(true);
|
||||
when(userRepository.findById(8L)).thenReturn(Optional.of(adminUser));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAdoptionAutoAssignsFirstStaffEmployee() {
|
||||
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
|
||||
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||
|
||||
when(employeeRepository.findAllByIsActiveTrueOrderByEmployeeIdAsc()).thenReturn(List.of(adminEmployee, staffEmployee));
|
||||
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);
|
||||
request.setCustomerId(1L);
|
||||
request.setAdoptionDate(LocalDate.now());
|
||||
request.setAdoptionStatus("Pending");
|
||||
|
||||
var response = adoptionService.createAdoption(request);
|
||||
|
||||
assertEquals(7L, response.getEmployeeId());
|
||||
assertEquals("Alex Jones", response.getEmployeeName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAdoptionRejectsAdminEmployeeSelection() {
|
||||
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
|
||||
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||
when(employeeRepository.findById(8L)).thenReturn(Optional.of(adminEmployee));
|
||||
|
||||
AdoptionRequest request = new AdoptionRequest();
|
||||
request.setPetId(1L);
|
||||
request.setCustomerId(1L);
|
||||
request.setEmployeeId(8L);
|
||||
request.setAdoptionDate(LocalDate.now());
|
||||
request.setAdoptionStatus("Pending");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> adoptionService.createAdoption(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAdoptionRejectsInactiveStaffUserSelection() {
|
||||
User inactiveStaffUser = new User();
|
||||
inactiveStaffUser.setId(7L);
|
||||
inactiveStaffUser.setRole(User.Role.STAFF);
|
||||
inactiveStaffUser.setActive(false);
|
||||
when(userRepository.findById(7L)).thenReturn(Optional.of(inactiveStaffUser));
|
||||
|
||||
when(petRepository.findById(1L)).thenReturn(Optional.of(pet));
|
||||
when(customerRepository.findById(1L)).thenReturn(Optional.of(customer));
|
||||
when(employeeRepository.findById(7L)).thenReturn(Optional.of(staffEmployee));
|
||||
|
||||
AdoptionRequest request = new AdoptionRequest();
|
||||
request.setPetId(1L);
|
||||
request.setCustomerId(1L);
|
||||
request.setEmployeeId(7L);
|
||||
request.setAdoptionDate(LocalDate.now());
|
||||
request.setAdoptionStatus("Pending");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> adoptionService.createAdoption(request));
|
||||
}
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import com.petshop.backend.entity.Appointment;
|
||||
import com.petshop.backend.entity.Customer;
|
||||
import com.petshop.backend.entity.CustomerPet;
|
||||
import com.petshop.backend.entity.Employee;
|
||||
import com.petshop.backend.entity.EmployeeStore;
|
||||
import com.petshop.backend.entity.Pet;
|
||||
import com.petshop.backend.entity.Service;
|
||||
import com.petshop.backend.entity.StoreLocation;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.AppointmentRepository;
|
||||
import com.petshop.backend.repository.CustomerPetRepository;
|
||||
import com.petshop.backend.repository.CustomerRepository;
|
||||
import com.petshop.backend.repository.EmployeeRepository;
|
||||
import com.petshop.backend.repository.EmployeeStoreRepository;
|
||||
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;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
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.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 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;
|
||||
|
||||
private Customer customer;
|
||||
private StoreLocation store;
|
||||
private Service grooming;
|
||||
private Service nailTrim;
|
||||
private Pet pet;
|
||||
private CustomerPet customerPet;
|
||||
private Employee employee;
|
||||
private LocalDate date;
|
||||
|
||||
@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");
|
||||
customer.setLastName("Owner");
|
||||
|
||||
store = new StoreLocation();
|
||||
store.setStoreId(1L);
|
||||
store.setStoreName("Main Store");
|
||||
|
||||
grooming = new Service();
|
||||
grooming.setServiceId(1L);
|
||||
grooming.setServiceName("Grooming");
|
||||
grooming.setServiceDuration(30);
|
||||
|
||||
nailTrim = new Service();
|
||||
nailTrim.setServiceId(2L);
|
||||
nailTrim.setServiceName("Nail Trim");
|
||||
nailTrim.setServiceDuration(30);
|
||||
|
||||
pet = new Pet();
|
||||
pet.setPetId(1L);
|
||||
pet.setPetName("Milo");
|
||||
|
||||
customerPet = new CustomerPet();
|
||||
customerPet.setCustomerPetId(11L);
|
||||
customerPet.setPetName("Milo Jr");
|
||||
customerPet.setCustomer(customer);
|
||||
|
||||
employee = new Employee();
|
||||
employee.setEmployeeId(7L);
|
||||
employee.setUserId(7L);
|
||||
employee.setFirstName("Alex");
|
||||
employee.setLastName("Jones");
|
||||
employee.setIsActive(true);
|
||||
|
||||
User staffUser = new User();
|
||||
staffUser.setId(7L);
|
||||
staffUser.setRole(User.Role.STAFF);
|
||||
staffUser.setActive(true);
|
||||
when(userRepository.findById(7L)).thenReturn(Optional.of(staffUser));
|
||||
|
||||
date = LocalDate.now().plusDays(1);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkAvailabilityAllowsConcurrentAppointmentsIfAnotherEmployeeFree() {
|
||||
Employee employee2 = new Employee();
|
||||
employee2.setEmployeeId(8L);
|
||||
employee2.setUserId(8L);
|
||||
employee2.setFirstName("Bob");
|
||||
employee2.setIsActive(true);
|
||||
|
||||
User staffUser2 = new User();
|
||||
staffUser2.setId(8L);
|
||||
staffUser2.setRole(User.Role.STAFF);
|
||||
staffUser2.setActive(true);
|
||||
when(userRepository.findById(8L)).thenReturn(Optional.of(staffUser2));
|
||||
|
||||
Appointment existing = appointment(1L, date, LocalTime.of(10, 0), grooming, store);
|
||||
when(storeRepository.findById(1L)).thenReturn(Optional.of(store));
|
||||
when(serviceRepository.findById(2L)).thenReturn(Optional.of(nailTrim));
|
||||
|
||||
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||
.thenReturn(List.of(new EmployeeStore(employee, store), new EmployeeStore(employee2, store)));
|
||||
|
||||
when(appointmentRepository.findByEmployeeEmployeeIdAndAppointmentDate(7L, date)).thenReturn(List.of(existing));
|
||||
when(appointmentRepository.findByEmployeeEmployeeIdAndAppointmentDate(8L, date)).thenReturn(List.of());
|
||||
|
||||
List<String> slots = appointmentService.checkAvailability(1L, 2L, date);
|
||||
|
||||
assertTrue(slots.contains("10:00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
|
||||
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.findByEmployeeEmployeeIdAndAppointmentDate(7L, date)).thenReturn(List.of());
|
||||
when(customerPetRepository.findById(22L)).thenReturn(Optional.of(otherCustomerPet));
|
||||
|
||||
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));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> appointmentService.createAppointment(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAppointmentRejectsCustomerPetOwnedByDifferentCustomer() {
|
||||
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.findByEmployeeEmployeeIdAndAppointmentDate(7L, date)).thenReturn(List.of());
|
||||
when(customerPetRepository.findById(22L)).thenReturn(Optional.of(otherCustomerPet));
|
||||
|
||||
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));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> appointmentService.createAppointment(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAppointmentRejectsAdminEmployeeSelection() {
|
||||
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));
|
||||
|
||||
Employee adminEmployee = new Employee();
|
||||
adminEmployee.setEmployeeId(8L);
|
||||
adminEmployee.setUserId(8L);
|
||||
adminEmployee.setFirstName("Admin");
|
||||
adminEmployee.setLastName("Helper");
|
||||
adminEmployee.setIsActive(true);
|
||||
|
||||
User adminLinkedUser = new User();
|
||||
adminLinkedUser.setId(8L);
|
||||
adminLinkedUser.setRole(User.Role.ADMIN);
|
||||
adminLinkedUser.setActive(true);
|
||||
|
||||
when(userRepository.findById(8L)).thenReturn(Optional.of(adminLinkedUser));
|
||||
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(employeeRepository.findById(8L)).thenReturn(Optional.of(adminEmployee));
|
||||
when(appointmentRepository.findByEmployeeEmployeeIdAndAppointmentDate(7L, date)).thenReturn(List.of());
|
||||
when(customerPetRepository.findById(11L)).thenReturn(Optional.of(customerPet));
|
||||
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||
.thenReturn(List.of(new EmployeeStore(adminEmployee, store), new EmployeeStore(employee, store)));
|
||||
|
||||
var request = new com.petshop.backend.dto.appointment.AppointmentRequest();
|
||||
request.setCustomerId(1L);
|
||||
request.setStoreId(1L);
|
||||
request.setServiceId(1L);
|
||||
request.setEmployeeId(8L);
|
||||
request.setAppointmentDate(date);
|
||||
request.setAppointmentTime(LocalTime.of(10, 0));
|
||||
request.setAppointmentStatus("Booked");
|
||||
request.setCustomerPetIds(List.of(11L));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> appointmentService.createAppointment(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAppointmentRejectsInactiveStaffUserSelection() {
|
||||
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));
|
||||
|
||||
User inactiveStaffUser = new User();
|
||||
inactiveStaffUser.setId(7L);
|
||||
inactiveStaffUser.setRole(User.Role.STAFF);
|
||||
inactiveStaffUser.setActive(false);
|
||||
when(userRepository.findById(7L)).thenReturn(Optional.of(inactiveStaffUser));
|
||||
|
||||
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(employeeRepository.findById(7L)).thenReturn(Optional.of(employee));
|
||||
when(appointmentRepository.findByEmployeeEmployeeIdAndAppointmentDate(7L, date)).thenReturn(List.of());
|
||||
when(customerPetRepository.findById(11L)).thenReturn(Optional.of(customerPet));
|
||||
when(employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(1L))
|
||||
.thenReturn(List.of(new EmployeeStore(employee, store)));
|
||||
|
||||
var request = new com.petshop.backend.dto.appointment.AppointmentRequest();
|
||||
request.setCustomerId(1L);
|
||||
request.setStoreId(1L);
|
||||
request.setServiceId(1L);
|
||||
request.setEmployeeId(7L);
|
||||
request.setAppointmentDate(date);
|
||||
request.setAppointmentTime(LocalTime.of(10, 0));
|
||||
request.setAppointmentStatus("Booked");
|
||||
request.setCustomerPetIds(List.of(11L));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> appointmentService.createAppointment(request));
|
||||
}
|
||||
|
||||
private Appointment appointment(Long id, LocalDate date, LocalTime time, Service service, StoreLocation storeLocation) {
|
||||
Appointment appointment = new Appointment();
|
||||
appointment.setAppointmentId(id);
|
||||
appointment.setAppointmentDate(date);
|
||||
appointment.setAppointmentTime(time);
|
||||
appointment.setAppointmentStatus("Booked");
|
||||
appointment.setService(service);
|
||||
appointment.setStore(storeLocation);
|
||||
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 AppPrincipal(userId, "user", role, 0),
|
||||
"n/a",
|
||||
List.of(new SimpleGrantedAuthority("ROLE_" + role.name()))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import com.petshop.backend.dto.chat.MessageRequest;
|
||||
import com.petshop.backend.dto.chat.UpdateConversationRequest;
|
||||
import com.petshop.backend.entity.Conversation;
|
||||
import com.petshop.backend.entity.Customer;
|
||||
import com.petshop.backend.entity.Message;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.ConversationRepository;
|
||||
import com.petshop.backend.repository.CustomerRepository;
|
||||
import com.petshop.backend.repository.MessageRepository;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ChatServiceTest {
|
||||
|
||||
@Mock
|
||||
private ConversationRepository conversationRepository;
|
||||
|
||||
@Mock
|
||||
private MessageRepository messageRepository;
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Mock
|
||||
private CustomerRepository customerRepository;
|
||||
|
||||
@InjectMocks
|
||||
private ChatService chatService;
|
||||
|
||||
private Customer customer;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
customer = new Customer();
|
||||
customer.setCustomerId(1L);
|
||||
customer.setUserId(10L);
|
||||
customer.setFirstName("Pat");
|
||||
customer.setLastName("Owner");
|
||||
customer.setEmail("pat@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateConversationMarksConversationClosed() {
|
||||
Conversation conversation = conversation(99L, 1L, null, Conversation.ConversationStatus.OPEN);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
when(customerRepository.findByUserId(10L)).thenReturn(Optional.of(customer));
|
||||
when(conversationRepository.save(any(Conversation.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(messageRepository.findByConversationIdOrderByTimestampAsc(99L))
|
||||
.thenReturn(List.of(message("hello")));
|
||||
|
||||
var response = chatService.updateConversation(99L, 10L, User.Role.CUSTOMER, new UpdateConversationRequest("CLOSED"));
|
||||
|
||||
assertEquals("CLOSED", response.getStatus());
|
||||
assertEquals("hello", response.getLastMessage());
|
||||
verify(conversationRepository).save(conversation);
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateConversationRejectsOtherCustomer() {
|
||||
Conversation conversation = conversation(99L, 2L, null, Conversation.ConversationStatus.OPEN);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
when(customerRepository.findByUserId(10L)).thenReturn(Optional.of(customer));
|
||||
|
||||
assertThrows(AccessDeniedException.class,
|
||||
() -> chatService.updateConversation(99L, 10L, User.Role.CUSTOMER, new UpdateConversationRequest("CLOSED")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateConversationIsIdempotent() {
|
||||
Conversation conversation = conversation(99L, 1L, null, Conversation.ConversationStatus.CLOSED);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
when(customerRepository.findByUserId(10L)).thenReturn(Optional.of(customer));
|
||||
when(conversationRepository.save(any(Conversation.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(messageRepository.findByConversationIdOrderByTimestampAsc(99L)).thenReturn(List.of());
|
||||
|
||||
var response = chatService.updateConversation(99L, 10L, User.Role.CUSTOMER, new UpdateConversationRequest("CLOSED"));
|
||||
|
||||
assertEquals("CLOSED", response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void staffCanCloseAssignedConversation() {
|
||||
Conversation conversation = conversation(99L, 1L, 77L, Conversation.ConversationStatus.OPEN);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
when(conversationRepository.save(any(Conversation.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(messageRepository.findByConversationIdOrderByTimestampAsc(99L)).thenReturn(List.of());
|
||||
|
||||
var response = chatService.updateConversation(99L, 77L, User.Role.STAFF, new UpdateConversationRequest("CLOSED"));
|
||||
|
||||
assertEquals("CLOSED", response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void staffCanCloseUnassignedConversation() {
|
||||
Conversation conversation = conversation(99L, 1L, null, Conversation.ConversationStatus.OPEN);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
when(conversationRepository.save(any(Conversation.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(messageRepository.findByConversationIdOrderByTimestampAsc(99L)).thenReturn(List.of());
|
||||
|
||||
var response = chatService.updateConversation(99L, 77L, User.Role.STAFF, new UpdateConversationRequest("CLOSED"));
|
||||
|
||||
assertEquals("CLOSED", response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminCanCloseAnyConversation() {
|
||||
Conversation conversation = conversation(99L, 2L, 88L, Conversation.ConversationStatus.OPEN);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
when(conversationRepository.save(any(Conversation.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(messageRepository.findByConversationIdOrderByTimestampAsc(99L)).thenReturn(List.of());
|
||||
|
||||
var response = chatService.updateConversation(99L, 1L, User.Role.ADMIN, new UpdateConversationRequest("CLOSED"));
|
||||
|
||||
assertEquals("CLOSED", response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateConversationCanReopenClosedConversation() {
|
||||
Conversation conversation = conversation(99L, 1L, null, Conversation.ConversationStatus.CLOSED);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
when(customerRepository.findByUserId(10L)).thenReturn(Optional.of(customer));
|
||||
when(conversationRepository.save(any(Conversation.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(messageRepository.findByConversationIdOrderByTimestampAsc(99L)).thenReturn(List.of());
|
||||
|
||||
var response = chatService.updateConversation(99L, 10L, User.Role.CUSTOMER, new UpdateConversationRequest("OPEN"));
|
||||
|
||||
assertEquals("OPEN", response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateConversationRejectsInvalidStatus() {
|
||||
Conversation conversation = conversation(99L, 1L, null, Conversation.ConversationStatus.OPEN);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
when(customerRepository.findByUserId(10L)).thenReturn(Optional.of(customer));
|
||||
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> chatService.updateConversation(99L, 10L, User.Role.CUSTOMER, new UpdateConversationRequest("INVALID")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendMessageRejectsClosedConversation() {
|
||||
Conversation conversation = conversation(99L, 1L, null, Conversation.ConversationStatus.CLOSED);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
|
||||
assertThrows(AccessDeniedException.class,
|
||||
() -> chatService.sendMessage(99L, 10L, User.Role.CUSTOMER, new MessageRequest("hello")));
|
||||
|
||||
verify(messageRepository, never()).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestHumanTakeoverRejectsClosedConversation() {
|
||||
Conversation conversation = conversation(99L, 1L, null, Conversation.ConversationStatus.CLOSED);
|
||||
when(conversationRepository.findById(99L)).thenReturn(Optional.of(conversation));
|
||||
|
||||
assertThrows(AccessDeniedException.class,
|
||||
() -> chatService.requestHumanTakeover(99L, 10L, User.Role.CUSTOMER));
|
||||
}
|
||||
|
||||
private Conversation conversation(Long id, Long customerId, Long staffId, Conversation.ConversationStatus status) {
|
||||
Conversation conversation = new Conversation();
|
||||
conversation.setId(id);
|
||||
conversation.setCustomerId(customerId);
|
||||
conversation.setStaffId(staffId);
|
||||
conversation.setStatus(status);
|
||||
conversation.setMode(Conversation.ConversationMode.AUTOMATED);
|
||||
conversation.setHumanRequestedAt(LocalDateTime.now());
|
||||
return conversation;
|
||||
}
|
||||
|
||||
private Message message(String content) {
|
||||
Message message = new Message();
|
||||
message.setConversationId(99L);
|
||||
message.setSenderId(10L);
|
||||
message.setContent(content);
|
||||
message.setIsRead(false);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import com.petshop.backend.entity.Adoption;
|
||||
import com.petshop.backend.entity.Customer;
|
||||
import com.petshop.backend.entity.Pet;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.repository.AdoptionRepository;
|
||||
import com.petshop.backend.repository.CustomerRepository;
|
||||
import com.petshop.backend.repository.PetRepository;
|
||||
import com.petshop.backend.repository.StoreRepository;
|
||||
import com.petshop.backend.security.AppPrincipal;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PetServiceTest {
|
||||
|
||||
@Mock
|
||||
private PetRepository petRepository;
|
||||
|
||||
@Mock
|
||||
private AdoptionRepository adoptionRepository;
|
||||
|
||||
@Mock
|
||||
private CustomerRepository customerRepository;
|
||||
|
||||
@Mock
|
||||
private StoreRepository storeRepository;
|
||||
|
||||
@Mock
|
||||
private CatalogImageStorageService catalogImageStorageService;
|
||||
|
||||
@InjectMocks
|
||||
private PetService petService;
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllPetsAnonymousReturnsOnlyPublicPets() {
|
||||
Pageable pageable = PageRequest.of(0, 10);
|
||||
Pet availablePet = pet(1L, "Buddy", "Available");
|
||||
when(petRepository.searchPublicPets(null, null, null, pageable)).thenReturn(new PageImpl<>(List.of(availablePet), pageable, 1));
|
||||
|
||||
var result = petService.getAllPets(null, null, null, null, pageable);
|
||||
|
||||
assertEquals(1, result.getTotalElements());
|
||||
assertEquals("Buddy", result.getContent().get(0).getPetName());
|
||||
verify(petRepository).searchPublicPets(null, null, null, pageable);
|
||||
verify(petRepository, never()).searchPets(null, null, null, null, pageable);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllPetsAnonymousWithAdoptedStatusReturnsEmptyPage() {
|
||||
Pageable pageable = PageRequest.of(0, 10);
|
||||
|
||||
var result = petService.getAllPets(null, null, "Adopted", null, pageable);
|
||||
|
||||
assertEquals(0, result.getTotalElements());
|
||||
verify(petRepository, never()).searchPublicPets(null, null, null, pageable);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllPetsCustomerReturnsVisiblePetsOnly() {
|
||||
Pageable pageable = PageRequest.of(0, 10);
|
||||
setAuthentication(25L, User.Role.CUSTOMER);
|
||||
Pet availablePet = pet(1L, "Buddy", "Available");
|
||||
Pet adoptedPet = pet(2L, "Luna", "Adopted");
|
||||
when(petRepository.searchCustomerVisiblePets(25L, null, null, null, pageable))
|
||||
.thenReturn(new PageImpl<>(List.of(availablePet, adoptedPet), pageable, 2));
|
||||
|
||||
var result = petService.getAllPets(null, null, null, null, pageable);
|
||||
|
||||
assertEquals(2, result.getTotalElements());
|
||||
verify(petRepository).searchCustomerVisiblePets(25L, null, null, null, pageable);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllPetsAdminReturnsAllPets() {
|
||||
Pageable pageable = PageRequest.of(0, 10);
|
||||
setAuthentication(99L, User.Role.ADMIN);
|
||||
Pet availablePet = pet(1L, "Buddy", "Available");
|
||||
Pet adoptedPet = pet(2L, "Luna", "Adopted");
|
||||
when(petRepository.searchPets(null, null, null, null, pageable))
|
||||
.thenReturn(new PageImpl<>(List.of(availablePet, adoptedPet), pageable, 2));
|
||||
|
||||
var result = petService.getAllPets(null, null, null, null, pageable);
|
||||
|
||||
assertEquals(2, result.getTotalElements());
|
||||
verify(petRepository).searchPets(null, null, null, null, pageable);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPetByIdHidesAdoptedPetFromUnrelatedCustomer() {
|
||||
setAuthentication(50L, User.Role.CUSTOMER);
|
||||
Pet adoptedPet = pet(2L, "Luna", "Adopted");
|
||||
when(petRepository.findById(2L)).thenReturn(Optional.of(adoptedPet));
|
||||
when(adoptionRepository.findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(2L, "Completed"))
|
||||
.thenReturn(Optional.of(adoption(2L, 25L)));
|
||||
|
||||
assertThrows(ResourceNotFoundException.class, () -> petService.getPetById(2L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPetByIdAllowsOwnerToSeeAdoptedPet() {
|
||||
setAuthentication(25L, User.Role.CUSTOMER);
|
||||
Pet adoptedPet = pet(2L, "Luna", "Adopted");
|
||||
when(petRepository.findById(2L)).thenReturn(Optional.of(adoptedPet));
|
||||
when(adoptionRepository.findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(2L, "Completed"))
|
||||
.thenReturn(Optional.of(adoption(2L, 25L)));
|
||||
|
||||
var result = petService.getPetById(2L);
|
||||
|
||||
assertEquals(2L, result.getPetId());
|
||||
}
|
||||
|
||||
private void setAuthentication(Long userId, User.Role role) {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(
|
||||
new AppPrincipal(userId, "user", role, 0),
|
||||
"n/a",
|
||||
List.of(new SimpleGrantedAuthority("ROLE_" + role.name()))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private Pet pet(Long id, String name, String status) {
|
||||
Pet pet = new Pet();
|
||||
pet.setPetId(id);
|
||||
pet.setPetName(name);
|
||||
pet.setPetSpecies("Cat");
|
||||
pet.setPetBreed("Mixed");
|
||||
pet.setPetAge(2);
|
||||
pet.setPetStatus(status);
|
||||
pet.setPetPrice(java.math.BigDecimal.TEN);
|
||||
return pet;
|
||||
}
|
||||
|
||||
private Adoption adoption(Long petId, Long userId) {
|
||||
Adoption adoption = new Adoption();
|
||||
Pet pet = new Pet();
|
||||
pet.setPetId(petId);
|
||||
adoption.setPet(pet);
|
||||
Customer customer = new Customer();
|
||||
customer.setCustomerId(1L);
|
||||
customer.setUserId(userId);
|
||||
adoption.setCustomer(customer);
|
||||
adoption.setAdoptionStatus("Completed");
|
||||
return adoption;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user