diff --git a/src/test/java/de/hmmh/pmt/IntegrationTest.java b/src/test/java/de/hmmh/pmt/IntegrationTest.java index eb59826..91852a4 100644 --- a/src/test/java/de/hmmh/pmt/IntegrationTest.java +++ b/src/test/java/de/hmmh/pmt/IntegrationTest.java @@ -1,6 +1,8 @@ package de.hmmh.pmt; import com.fasterxml.jackson.databind.ObjectMapper; +import de.hmmh.pmt.db.Allocation; +import de.hmmh.pmt.db.AllocationRepository; import de.hmmh.pmt.db.Project; import de.hmmh.pmt.db.ProjectRepository; import de.hmmh.pmt.employee.api.EmployeeControllerApi; @@ -24,13 +26,19 @@ import java.util.Map; public abstract class IntegrationTest { protected final static String baseUri = "/api/v1"; + protected final static long TEST_EMPLOYEE_A_ID = 1L; + protected final static long TEST_QUALIFICATION_A_ID = 10L; + protected final static long TEST_QUALIFICATION_B_ID = 11L; @Autowired protected MockMvc mvc; @Autowired protected ObjectMapper objectMapper; + @Autowired protected ProjectRepository projectRepository; + @Autowired + protected AllocationRepository allocationRepository; @MockBean protected EmployeeControllerApi mockEmployeeApi; @@ -39,11 +47,13 @@ public abstract class IntegrationTest { @BeforeEach void setUp() { + allocationRepository.deleteAll(); projectRepository.deleteAll(); } @AfterEach void cleanUp() { + allocationRepository.deleteAll(); projectRepository.deleteAll(); } @@ -54,7 +64,7 @@ public abstract class IntegrationTest { researchLabProject.setName("Underwater Research Lab"); researchLabProject.setGoal( "Discover new marine species!"); researchLabProject.setCustomerId(73L); - researchLabProject.setAdministratorId(202L); + researchLabProject.setAdministratorId(TEST_EMPLOYEE_A_ID); researchLabProject.setStart(LocalDateTime.of(2025, 5, 22, 8, 45)); researchLabProject.setPlannedEnd(LocalDateTime.of(2026, 5, 22, 8, 45)); projects.put("research-lab", researchLabProject); @@ -63,7 +73,7 @@ public abstract class IntegrationTest { spaceStationProject.setName("International Space Station Expansion"); spaceStationProject.setGoal("Build new modules for extended research."); spaceStationProject.setCustomerId(85L); - spaceStationProject.setAdministratorId(150L); + spaceStationProject.setAdministratorId(TEST_EMPLOYEE_A_ID); spaceStationProject.setStart(LocalDateTime.of(2024, 10, 15, 10, 0)); spaceStationProject.setPlannedEnd(LocalDateTime.of(2025, 12, 15, 10, 0)); projects.put("space-station", spaceStationProject); @@ -72,7 +82,7 @@ public abstract class IntegrationTest { aiResearchProject.setName("AI Human Interaction Study"); aiResearchProject.setGoal("Study AI interactions in healthcare."); aiResearchProject.setCustomerId(63L); - aiResearchProject.setAdministratorId(180L); + aiResearchProject.setAdministratorId(TEST_EMPLOYEE_A_ID); aiResearchProject.setStart(LocalDateTime.of(2023, 11, 3, 9, 30)); aiResearchProject.setPlannedEnd(LocalDateTime.of(2024, 6, 3, 9, 30)); projects.put("ai-research", aiResearchProject); @@ -81,7 +91,7 @@ public abstract class IntegrationTest { renewableEnergyProject.setName("Renewable Energy Storage System"); renewableEnergyProject.setGoal("Develop new battery tech for solar power."); renewableEnergyProject.setCustomerId(90L); - renewableEnergyProject.setAdministratorId(175L); + renewableEnergyProject.setAdministratorId(TEST_EMPLOYEE_A_ID); renewableEnergyProject.setStart(LocalDateTime.of(2024, 1, 10, 11, 0)); renewableEnergyProject.setPlannedEnd(LocalDateTime.of(2025, 1, 10, 11, 0)); projects.put("renewable-energy", renewableEnergyProject); @@ -90,22 +100,72 @@ public abstract class IntegrationTest { climateChangeProject.setName("Climate Change Impact Analysis"); climateChangeProject.setGoal("Study global warming effects on ecosystems."); climateChangeProject.setCustomerId(72L); - climateChangeProject.setAdministratorId(190L); + climateChangeProject.setAdministratorId(TEST_EMPLOYEE_A_ID); climateChangeProject.setStart(LocalDateTime.of(2025, 3, 12, 8, 0)); climateChangeProject.setPlannedEnd(LocalDateTime.of(2026, 3, 12, 8, 0)); projects.put("climate change", climateChangeProject); - Project medicalResearchProject = new Project(); medicalResearchProject.setName("Cancer Treatment Innovation"); medicalResearchProject.setGoal("Develop new gene therapy techniques."); medicalResearchProject.setCustomerId(95L); - medicalResearchProject.setAdministratorId(155L); + medicalResearchProject.setAdministratorId(TEST_EMPLOYEE_A_ID); medicalResearchProject.setStart(LocalDateTime.of(2024, 8, 20, 9, 15)); medicalResearchProject.setPlannedEnd(LocalDateTime.of(2026, 8, 20, 9, 15)); projects.put("medical-research", medicalResearchProject); + Project overlappingProjectA = new Project(); + overlappingProjectA.setName("Overlap A"); + overlappingProjectA.setGoal("A Project That Overlaps with another one for Testing"); + overlappingProjectA.setCustomerId(19L); + overlappingProjectA.setAdministratorId(TEST_EMPLOYEE_A_ID); + overlappingProjectA.setStart(LocalDateTime.of(1970, 6, 20, 9, 15)); + overlappingProjectA.setPlannedEnd(LocalDateTime.of(2025, 8, 20, 9, 15)); + projects.put("overlap-a", overlappingProjectA); + + Project overlappingProjectB = new Project(); + overlappingProjectB.setName("Overlap B"); + overlappingProjectB.setGoal("B Project That Overlaps with another one for Testing"); + overlappingProjectB.setCustomerId(23L); + overlappingProjectB.setAdministratorId(TEST_EMPLOYEE_A_ID); + overlappingProjectB.setStart(LocalDateTime.of(2024, 7, 20, 9, 15)); + overlappingProjectB.setPlannedEnd(LocalDateTime.of(2026, 12, 20, 9, 15)); + projects.put("overlap-b", overlappingProjectB); + + Project overlappingProjectC = new Project(); + overlappingProjectC.setName("Overlap C"); + overlappingProjectC.setGoal("C Project That Overlaps with another one for Testing"); + overlappingProjectC.setCustomerId(19L); + overlappingProjectC.setAdministratorId(TEST_EMPLOYEE_A_ID); + overlappingProjectC.setStart(LocalDateTime.of(2024, 6, 20, 9, 15)); + overlappingProjectC.setPlannedEnd(LocalDateTime.of(2026, 8, 20, 9, 15)); + projects.put("overlap-c", overlappingProjectC); + + + Project nonOverlappingProject = new Project(); + nonOverlappingProject.setName("Non Overlapping to Overlap a-c"); + nonOverlappingProject.setGoal("Project That doesnt overlap with another one of the Overlap ones for Testing"); + nonOverlappingProject.setCustomerId(19L); + nonOverlappingProject.setAdministratorId(TEST_EMPLOYEE_A_ID); + nonOverlappingProject.setStart(LocalDateTime.of(1970, 1, 20, 9, 15)); + nonOverlappingProject.setPlannedEnd(LocalDateTime.of(1970, 2, 20, 9, 15)); + projects.put("non-overlap", nonOverlappingProject); + projectRepository.saveAllAndFlush(projects.values()); return projects; } + + + protected Map createTestAllocationData(Map allProjects) { + Map allocations = new HashMap<>(); + + Allocation allocation1ToOverlapA = new Allocation(); + allocation1ToOverlapA.setProject(allProjects.get("overlap-a")); + allocation1ToOverlapA.setEmployeeId(TEST_EMPLOYEE_A_ID); + allocation1ToOverlapA.setRole(TEST_QUALIFICATION_A_ID); + allocations.put("1>overlap-a", allocation1ToOverlapA); + + allocationRepository.saveAllAndFlush(allocations.values()); + return allocations; + } } diff --git a/src/test/java/de/hmmh/pmt/project/AddEmployeeTest.java b/src/test/java/de/hmmh/pmt/project/AddEmployeeTest.java index d9d9011..071af38 100644 --- a/src/test/java/de/hmmh/pmt/project/AddEmployeeTest.java +++ b/src/test/java/de/hmmh/pmt/project/AddEmployeeTest.java @@ -1,52 +1,163 @@ package de.hmmh.pmt.project; import de.hmmh.pmt.IntegrationTest; +import de.hmmh.pmt.db.Allocation; import de.hmmh.pmt.db.Project; import de.hmmh.pmt.dtos.AddEmployeeDTO; import de.hmmh.pmt.employee.dtos.EmployeeResponseDTO; import de.hmmh.pmt.employee.dtos.QualificationGetDTO; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestClientException; import java.util.List; import java.util.Map; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class AddEmployeeTest extends IntegrationTest { @Test void addValidEmployee() throws Exception { - EmployeeResponseDTO employee= new EmployeeResponseDTO(); - employee.setId(2L); - employee.setSkillSet(List.of(newQualification(1L))); when(mockEmployeeApi.findById(anyLong())) - .thenReturn(employee); + .thenReturn(getEmployee()); Map allProjects = createTestProjectData(); - AddEmployeeDTO addEmployeeDTO = new AddEmployeeDTO(); - addEmployeeDTO.setEmployeeId(1L); - addEmployeeDTO.setQualificationId(1L); - RequestBuilder request = MockMvcRequestBuilders - .post(baseUri + "/project/" + allProjects.get("research-lab").getId()) - .contentType(MediaType.APPLICATION_JSON) - .content(this.objectMapper.writeValueAsString(addEmployeeDTO)); this.mvc - .perform(request) + .perform(getRequest(allProjects.get("research-lab").getId(), getAddEmployeeDTO())) .andExpect(status().isNoContent()); } + @Test + void shouldNotAddEmployeeWhenProjectIsNotFound() throws Exception { + this.mvc + .perform(getRequest(0L, getAddEmployeeDTO())) + .andExpect(status().isNotFound()); + } - private static QualificationGetDTO newQualification(Long id){ + @Test + void shouldNotAddEmployeeWhenEmployeeDoesNotExist() throws Exception { + when(this.mockEmployeeApi.findById(Mockito.anyLong())) + .thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)); + + Map allProjects = createTestProjectData(); + + this.mvc + .perform(getRequest(allProjects.get("research-lab").getId(), getAddEmployeeDTO())) + .andExpect(status().isNotFound()); + } + + @Test + void shouldReturnUnavailableWhenEmployeeApiIsDown() throws Exception { + when(this.mockEmployeeApi.findById(Mockito.anyLong())) + .thenThrow(new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR)); + + Map allProjects = createTestProjectData(); + + this.mvc + .perform(getRequest(allProjects.get("research-lab").getId(), getAddEmployeeDTO())) + .andExpect(status().isServiceUnavailable()); + } + + @Test + void shouldReturnInternalServerErrorOnApiClientCrash() throws Exception { + when(this.mockEmployeeApi.findById(Mockito.anyLong())) + .thenThrow(new RestClientException("Api Client crash")); + + Map allProjects = createTestProjectData(); + + this.mvc + .perform(getRequest(allProjects.get("research-lab").getId(), getAddEmployeeDTO())) + .andExpect(status().isInternalServerError()); + } + + @Test + void shouldReturnUnprocessableWhenEmployeeDoesNotHaveTheQualification() throws Exception { + when(mockEmployeeApi.findById(anyLong())) + .thenReturn(getEmployee()); + + Map allProjects = createTestProjectData(); + + AddEmployeeDTO addEmployeeDTO = getAddEmployeeDTO(); + addEmployeeDTO.setQualificationId(TEST_QUALIFICATION_B_ID); + + this.mvc + .perform(getRequest(allProjects.get("research-lab").getId(), addEmployeeDTO)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + void shouldReturnUnprocessableWhenEmployeesProjectTimeRangesOverlapsBackwards() throws Exception { + when(mockEmployeeApi.findById(anyLong())) + .thenReturn(getEmployee()); + + Map allProjects = createTestProjectData(); + Map allAllocations = createTestAllocationData(allProjects); + + this.mvc + .perform(getRequest(allProjects.get("overlap-b").getId(), getAddEmployeeDTO())) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + void shouldReturnUnprocessableWhenEmployeesProjectTimeRangesOverlapsForwards() throws Exception { + when(mockEmployeeApi.findById(anyLong())) + .thenReturn(getEmployee()); + + Map allProjects = createTestProjectData(); + Map allAllocations = createTestAllocationData(allProjects); + + this.mvc + .perform(getRequest(allProjects.get("overlap-c").getId(), getAddEmployeeDTO())) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + void shouldNotReturnUnprocessableWhenAllocationsDontOverlap() throws Exception { + when(mockEmployeeApi.findById(anyLong())) + .thenReturn(getEmployee()); + + Map allProjects = createTestProjectData(); + Map allAllocations = createTestAllocationData(allProjects); + + this.mvc + .perform(getRequest(allProjects.get("non-overlap").getId(), getAddEmployeeDTO())) + .andExpect(status().isNoContent()); + } + + private QualificationGetDTO newQualification(Long id) { QualificationGetDTO qualificationGetDTO = new QualificationGetDTO(); qualificationGetDTO.setId(id); return qualificationGetDTO; } + private EmployeeResponseDTO getEmployee() { + EmployeeResponseDTO employee = new EmployeeResponseDTO(); + employee.setId(TEST_EMPLOYEE_A_ID); + employee.setSkillSet(List.of(newQualification(TEST_QUALIFICATION_A_ID))); + return employee; + } + + private AddEmployeeDTO getAddEmployeeDTO() { + AddEmployeeDTO addEmployeeDTO = new AddEmployeeDTO(); + addEmployeeDTO.setEmployeeId(TEST_EMPLOYEE_A_ID); + addEmployeeDTO.setQualificationId(TEST_QUALIFICATION_A_ID); + return addEmployeeDTO; + } + + private RequestBuilder getRequest(Long projectId, AddEmployeeDTO addEmployeeDTO) throws Exception { + return MockMvcRequestBuilders + .post(baseUri + "/project/" + projectId) + .content(this.objectMapper.writeValueAsString(addEmployeeDTO)) + .contentType(MediaType.APPLICATION_JSON); + } }