diff --git a/src/main/java/org/example/projektarendehantering/application/service/EmployeeMapper.java b/src/main/java/org/example/projektarendehantering/application/service/EmployeeMapper.java index 1382b6c..5843328 100644 --- a/src/main/java/org/example/projektarendehantering/application/service/EmployeeMapper.java +++ b/src/main/java/org/example/projektarendehantering/application/service/EmployeeMapper.java @@ -3,6 +3,7 @@ import org.example.projektarendehantering.infrastructure.persistence.EmployeeEntity; import org.example.projektarendehantering.presentation.dto.EmployeeCreateDTO; import org.example.projektarendehantering.presentation.dto.EmployeeDTO; +import org.example.projektarendehantering.presentation.dto.EmployeeUpdateDTO; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; @@ -36,5 +37,12 @@ public EmployeeEntity toEntity(EmployeeCreateDTO dto) { .role(dto.getRole()) .build(); } + + public void updateEntity(EmployeeUpdateDTO dto, EmployeeEntity entity) { + if (dto == null || entity == null) return; + entity.setDisplayName(dto.getDisplayName()); + entity.setGithubUsername(dto.getGithubUsername()); + entity.setRole(dto.getRole()); + } } diff --git a/src/main/java/org/example/projektarendehantering/application/service/EmployeeService.java b/src/main/java/org/example/projektarendehantering/application/service/EmployeeService.java index 76f28fb..f48543a 100644 --- a/src/main/java/org/example/projektarendehantering/application/service/EmployeeService.java +++ b/src/main/java/org/example/projektarendehantering/application/service/EmployeeService.java @@ -8,6 +8,7 @@ import org.example.projektarendehantering.infrastructure.persistence.EmployeeRepository; import org.example.projektarendehantering.presentation.dto.EmployeeCreateDTO; import org.example.projektarendehantering.presentation.dto.EmployeeDTO; +import org.example.projektarendehantering.presentation.dto.EmployeeUpdateDTO; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; @@ -41,6 +42,28 @@ public EmployeeDTO createEmployee(Actor actor, EmployeeCreateDTO dto) { return employeeMapper.toDTO(employeeRepository.save(entity)); } + @Transactional + public EmployeeDTO updateEmployee(Actor actor, UUID id, EmployeeUpdateDTO dto) { + requireCanManageEmployees(actor); + EmployeeEntity entity = employeeRepository.findById(id) + .orElseThrow(() -> new BadRequestException("EMPLOYEE_NOT_FOUND", "Employee not found")); + + if (!entity.getGithubUsername().equals(dto.getGithubUsername())) { + throw new BadRequestException("EMPLOYEE_USERNAME_IMMUTABLE", "Github username cannot be changed"); + } + + employeeMapper.updateEntity(dto, entity); + return employeeMapper.toDTO(employeeRepository.save(entity)); + } + + @Transactional + public void deleteEmployee(Actor actor, UUID id) { + requireCanManageEmployees(actor); + EmployeeEntity entity = employeeRepository.findById(id) + .orElseThrow(() -> new BadRequestException("EMPLOYEE_NOT_FOUND", "Employee not found")); + employeeRepository.delete(entity); + } + @Transactional(readOnly = true) public Optional getEmployee(Actor actor, UUID id) { requireCanManageEmployees(actor); diff --git a/src/main/java/org/example/projektarendehantering/application/service/PatientMapper.java b/src/main/java/org/example/projektarendehantering/application/service/PatientMapper.java index 2c035b1..60452a5 100644 --- a/src/main/java/org/example/projektarendehantering/application/service/PatientMapper.java +++ b/src/main/java/org/example/projektarendehantering/application/service/PatientMapper.java @@ -3,6 +3,7 @@ import org.example.projektarendehantering.infrastructure.persistence.PatientEntity; import org.example.projektarendehantering.presentation.dto.PatientCreateDTO; import org.example.projektarendehantering.presentation.dto.PatientDTO; +import org.example.projektarendehantering.presentation.dto.PatientUpdateDTO; import org.springframework.stereotype.Component; @Component @@ -27,4 +28,11 @@ public PatientEntity toEntity(PatientCreateDTO dto) { .personalIdentityNumber(dto.getPersonalIdentityNumber()) .build(); } + + public void updateEntity(PatientUpdateDTO dto, PatientEntity entity) { + if (dto == null || entity == null) return; + entity.setFirstName(dto.getFirstName()); + entity.setLastName(dto.getLastName()); + entity.setPersonalIdentityNumber(dto.getPersonalIdentityNumber()); + } } diff --git a/src/main/java/org/example/projektarendehantering/application/service/PatientService.java b/src/main/java/org/example/projektarendehantering/application/service/PatientService.java index 81e0679..50190ea 100644 --- a/src/main/java/org/example/projektarendehantering/application/service/PatientService.java +++ b/src/main/java/org/example/projektarendehantering/application/service/PatientService.java @@ -1,10 +1,15 @@ package org.example.projektarendehantering.application.service; +import org.example.projektarendehantering.common.Actor; +import org.example.projektarendehantering.common.BadRequestException; import org.example.projektarendehantering.common.ConflictException; +import org.example.projektarendehantering.common.NotAuthorizedException; +import org.example.projektarendehantering.common.Role; import org.example.projektarendehantering.infrastructure.persistence.PatientEntity; import org.example.projektarendehantering.infrastructure.persistence.PatientRepository; import org.example.projektarendehantering.presentation.dto.PatientCreateDTO; import org.example.projektarendehantering.presentation.dto.PatientDTO; +import org.example.projektarendehantering.presentation.dto.PatientUpdateDTO; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; @@ -36,6 +41,33 @@ public PatientDTO createPatient(PatientCreateDTO patientDTO) { return patientMapper.toDTO(patientRepository.save(entity)); } + @Transactional + public PatientDTO updatePatient(Actor actor, UUID id, PatientUpdateDTO dto) { + requireCanManagePatients(actor); + PatientEntity entity = patientRepository.findById(id) + .orElseThrow(() -> new BadRequestException("PATIENT_NOT_FOUND", "Patient not found")); + + String newPin = dto.getPersonalIdentityNumber(); + if (newPin != null && !newPin.isBlank() + && !newPin.equals(entity.getPersonalIdentityNumber())) { + patientRepository.findByPersonalIdentityNumber(newPin) + .ifPresent(existing -> { + throw new ConflictException("Patient with personalIdentityNumber already exists"); + }); + } + + patientMapper.updateEntity(dto, entity); + return patientMapper.toDTO(patientRepository.save(entity)); + } + + @Transactional + public void deletePatient(Actor actor, UUID id) { + requireCanManagePatients(actor); + PatientEntity entity = patientRepository.findById(id) + .orElseThrow(() -> new BadRequestException("PATIENT_NOT_FOUND", "Patient not found")); + patientRepository.delete(entity); + } + @Transactional(readOnly = true) public Optional getPatient(UUID id) { return patientRepository.findById(id).map(patientMapper::toDTO); @@ -47,4 +79,14 @@ public List getAllPatients() { .map(patientMapper::toDTO) .collect(Collectors.toList()); } + + private void requireCanManagePatients(Actor actor) { + if (actor == null) { + throw new NotAuthorizedException("Missing actor"); + } + if (actor.role() == Role.MANAGER) { + return; + } + throw new NotAuthorizedException("Not allowed to manage patients"); + } } diff --git a/src/main/java/org/example/projektarendehantering/presentation/dto/EmployeeUpdateDTO.java b/src/main/java/org/example/projektarendehantering/presentation/dto/EmployeeUpdateDTO.java new file mode 100644 index 0000000..f8cd449 --- /dev/null +++ b/src/main/java/org/example/projektarendehantering/presentation/dto/EmployeeUpdateDTO.java @@ -0,0 +1,26 @@ +package org.example.projektarendehantering.presentation.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.example.projektarendehantering.common.Role; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class EmployeeUpdateDTO { + + @NotBlank + private String displayName; + + @NotBlank + private String githubUsername; + + @NotNull + private Role role; + +} diff --git a/src/main/java/org/example/projektarendehantering/presentation/dto/PatientUpdateDTO.java b/src/main/java/org/example/projektarendehantering/presentation/dto/PatientUpdateDTO.java new file mode 100644 index 0000000..25794f3 --- /dev/null +++ b/src/main/java/org/example/projektarendehantering/presentation/dto/PatientUpdateDTO.java @@ -0,0 +1,26 @@ +package org.example.projektarendehantering.presentation.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PatientUpdateDTO { + + @NotBlank + private String firstName; + + @NotBlank + private String lastName; + + @NotBlank + @Pattern(regexp = "^\\d{8}-\\d{4}$", message = "Use format YYYYMMDD-XXXX") + private String personalIdentityNumber; + +} diff --git a/src/main/java/org/example/projektarendehantering/presentation/rest/EmployeeController.java b/src/main/java/org/example/projektarendehantering/presentation/rest/EmployeeController.java index 7d26cda..e015f4d 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/rest/EmployeeController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/rest/EmployeeController.java @@ -6,6 +6,7 @@ import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.EmployeeCreateDTO; import org.example.projektarendehantering.presentation.dto.EmployeeDTO; +import org.example.projektarendehantering.presentation.dto.EmployeeUpdateDTO; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -25,6 +26,17 @@ public ResponseEntity createEmployee(@RequestBody @Valid EmployeeCr return ResponseEntity.ok(employeeService.createEmployee(securityActorAdapter.currentUser(), dto)); } + @PutMapping("/{id}") + public ResponseEntity updateEmployee(@PathVariable UUID id, @RequestBody @Valid EmployeeUpdateDTO dto) { + return ResponseEntity.ok(employeeService.updateEmployee(securityActorAdapter.currentUser(), id, dto)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteEmployee(@PathVariable UUID id) { + employeeService.deleteEmployee(securityActorAdapter.currentUser(), id); + return ResponseEntity.noContent().build(); + } + @GetMapping("/{id}") public ResponseEntity getEmployee(@PathVariable UUID id) { return employeeService.getEmployee(securityActorAdapter.currentUser(), id) diff --git a/src/main/java/org/example/projektarendehantering/presentation/rest/PatientController.java b/src/main/java/org/example/projektarendehantering/presentation/rest/PatientController.java index 2649a61..71abf9e 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/rest/PatientController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/rest/PatientController.java @@ -8,13 +8,14 @@ import org.example.projektarendehantering.presentation.dto.PatientCreateDTO; import org.example.projektarendehantering.presentation.dto.CaseDTO; import org.example.projektarendehantering.presentation.dto.PatientDTO; +import org.example.projektarendehantering.presentation.dto.PatientUpdateDTO; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.UUID; -// Adding more wierd stuff in the code base to commit things again cuz the rabbit rate limits suck :) @RestController @RequestMapping("/api/patients") @RequiredArgsConstructor @@ -25,10 +26,24 @@ public class PatientController { private final SecurityActorAdapter securityActorAdapter; @PostMapping + @PreAuthorize("hasRole('MANAGER')") public ResponseEntity createPatient(@RequestBody @Valid PatientCreateDTO patientDTO) { return ResponseEntity.ok(patientService.createPatient(patientDTO)); } + @PutMapping("/{id}") + @PreAuthorize("hasRole('MANAGER')") + public ResponseEntity updatePatient(@PathVariable UUID id, @RequestBody @Valid PatientUpdateDTO patientDTO) { + return ResponseEntity.ok(patientService.updatePatient(securityActorAdapter.currentUser(), id, patientDTO)); + } + + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('MANAGER')") + public ResponseEntity deletePatient(@PathVariable UUID id) { + patientService.deletePatient(securityActorAdapter.currentUser(), id); + return ResponseEntity.noContent().build(); + } + @GetMapping("/{id}") public ResponseEntity getPatient(@PathVariable UUID id) { return patientService.getPatient(id) diff --git a/src/main/java/org/example/projektarendehantering/presentation/web/EmployeeUiController.java b/src/main/java/org/example/projektarendehantering/presentation/web/EmployeeUiController.java index 3a68b57..dc9cd77 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/web/EmployeeUiController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/web/EmployeeUiController.java @@ -6,14 +6,19 @@ import org.example.projektarendehantering.common.Role; import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.EmployeeCreateDTO; +import org.example.projektarendehantering.presentation.dto.EmployeeDTO; +import org.example.projektarendehantering.presentation.dto.EmployeeUpdateDTO; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import java.util.UUID; + @Controller public class EmployeeUiController { @@ -53,4 +58,45 @@ public String createEmployee(@Valid @ModelAttribute("employeeCreateDTO") Employe employeeService.createEmployee(actor, dto); return "redirect:/ui/employees"; } + + @GetMapping("/ui/employees/edit/{id}") + @PreAuthorize("hasRole('MANAGER')") + public String editEmployee(@PathVariable UUID id, Model model) { + Actor actor = securityActorAdapter.currentUser(); + EmployeeDTO employee = employeeService.getEmployee(actor, id) + .orElseThrow(() -> new IllegalArgumentException("Invalid employee Id:" + id)); + + EmployeeUpdateDTO updateDto = EmployeeUpdateDTO.builder() + .displayName(employee.getDisplayName()) + .githubUsername(employee.getGithubUsername()) + .role(employee.getRole()) + .build(); + + model.addAttribute("employeeUpdateDTO", updateDto); + model.addAttribute("employeeId", id); + model.addAttribute("roles", Role.values()); + return "employees/edit"; + } + + @PostMapping("/ui/employees/update/{id}") + @PreAuthorize("hasRole('MANAGER')") + public String updateEmployee(@PathVariable UUID id, @Valid @ModelAttribute("employeeUpdateDTO") EmployeeUpdateDTO dto, BindingResult result, Model model) { + Actor actor = securityActorAdapter.currentUser(); + if (result.hasErrors()) { + model.addAttribute("employeeId", id); + model.addAttribute("roles", Role.values()); + return "employees/edit"; + } + + employeeService.updateEmployee(actor, id, dto); + return "redirect:/ui/employees"; + } + + @PostMapping("/ui/employees/delete/{id}") + @PreAuthorize("hasRole('MANAGER')") + public String deleteEmployee(@PathVariable UUID id) { + Actor actor = securityActorAdapter.currentUser(); + employeeService.deleteEmployee(actor, id); + return "redirect:/ui/employees"; + } } diff --git a/src/main/java/org/example/projektarendehantering/presentation/web/PatientUiController.java b/src/main/java/org/example/projektarendehantering/presentation/web/PatientUiController.java index 4db94b8..720aeb3 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/web/PatientUiController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/web/PatientUiController.java @@ -2,24 +2,31 @@ import jakarta.validation.Valid; import org.example.projektarendehantering.application.service.PatientService; +import org.example.projektarendehantering.common.BadRequestException; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.PatientCreateDTO; +import org.example.projektarendehantering.presentation.dto.PatientDTO; +import org.example.projektarendehantering.presentation.dto.PatientUpdateDTO; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -// Something wierd +import java.util.UUID; @Controller public class PatientUiController { private final PatientService patientService; + private final SecurityActorAdapter securityActorAdapter; - public PatientUiController(PatientService patientService) { + public PatientUiController(PatientService patientService, SecurityActorAdapter securityActorAdapter) { this.patientService = patientService; + this.securityActorAdapter = securityActorAdapter; } @GetMapping("/ui/patients") @@ -46,4 +53,40 @@ public String createPatient(@Valid @ModelAttribute("patientCreateDTO") PatientCr patientService.createPatient(dto); return "redirect:/ui/patients"; } + + @GetMapping("/ui/patients/edit/{id}") + @PreAuthorize("hasRole('MANAGER')") + public String editPatient(@PathVariable UUID id, Model model) { + PatientDTO patient = patientService.getPatient(id) + .orElseThrow(() -> new BadRequestException("PATIENT_NOT_FOUND", "Invalid patient Id:" + id)); + + PatientUpdateDTO updateDto = PatientUpdateDTO.builder() + .firstName(patient.getFirstName()) + .lastName(patient.getLastName()) + .personalIdentityNumber(patient.getPersonalIdentityNumber()) + .build(); + + model.addAttribute("patientUpdateDTO", updateDto); + model.addAttribute("patientId", id); + return "patients/edit"; + } + + @PostMapping("/ui/patients/update/{id}") + @PreAuthorize("hasRole('MANAGER')") + public String updatePatient(@PathVariable UUID id, @Valid @ModelAttribute("patientUpdateDTO") PatientUpdateDTO dto, BindingResult result, Model model) { + if (result.hasErrors()) { + model.addAttribute("patientId", id); + return "patients/edit"; + } + + patientService.updatePatient(securityActorAdapter.currentUser(), id, dto); + return "redirect:/ui/patients"; + } + + @PostMapping("/ui/patients/delete/{id}") + @PreAuthorize("hasRole('MANAGER')") + public String deletePatient(@PathVariable UUID id) { + patientService.deletePatient(securityActorAdapter.currentUser(), id); + return "redirect:/ui/patients"; + } } diff --git a/src/main/resources/templates/employees/edit.html b/src/main/resources/templates/employees/edit.html new file mode 100644 index 0000000..c4f4cd3 --- /dev/null +++ b/src/main/resources/templates/employees/edit.html @@ -0,0 +1,44 @@ + + + + + +
+ +
+
+

Edit User Mapping

+ Back to list +
+ +
+ + + +
+ +
+
+
+ +
+ + diff --git a/src/main/resources/templates/employees/list.html b/src/main/resources/templates/employees/list.html index d9f35a8..2a975d5 100644 --- a/src/main/resources/templates/employees/list.html +++ b/src/main/resources/templates/employees/list.html @@ -21,6 +21,7 @@

User Mappings (Employees)

Role ID (UUID) Created At + Actions @@ -30,6 +31,13 @@

User Mappings (Employees)

Role UUID Date + + Edit +
+ +
+ diff --git a/src/main/resources/templates/patients/edit.html b/src/main/resources/templates/patients/edit.html new file mode 100644 index 0000000..3c90955 --- /dev/null +++ b/src/main/resources/templates/patients/edit.html @@ -0,0 +1,40 @@ + + + + +
+ +
+
+

Edit Patient

+ Back to list +
+ +
+ + + +
+ +
+
+
+ +
+ + diff --git a/src/main/resources/templates/patients/list.html b/src/main/resources/templates/patients/list.html index c758143..6f0899e 100644 --- a/src/main/resources/templates/patients/list.html +++ b/src/main/resources/templates/patients/list.html @@ -18,6 +18,7 @@

Registered Patients

Last Name Personal ID Created At + Actions @@ -26,6 +27,13 @@

Registered Patients

Last PIN Date + + Edit +
+ +
+ diff --git a/src/test/java/org/example/projektarendehantering/application/service/EmployeeMapperTest.java b/src/test/java/org/example/projektarendehantering/application/service/EmployeeMapperTest.java new file mode 100644 index 0000000..c94afda --- /dev/null +++ b/src/test/java/org/example/projektarendehantering/application/service/EmployeeMapperTest.java @@ -0,0 +1,76 @@ +package org.example.projektarendehantering.application.service; + +import org.example.projektarendehantering.common.Role; +import org.example.projektarendehantering.infrastructure.persistence.EmployeeEntity; +import org.example.projektarendehantering.presentation.dto.EmployeeCreateDTO; +import org.example.projektarendehantering.presentation.dto.EmployeeDTO; +import org.example.projektarendehantering.presentation.dto.EmployeeUpdateDTO; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +class EmployeeMapperTest { + + private final EmployeeMapper employeeMapper = new EmployeeMapper(); + + @Test + void toDTO_shouldMapAllFields() { + UUID id = UUID.randomUUID(); + Instant now = Instant.now(); + EmployeeEntity entity = EmployeeEntity.builder() + .id(id) + .displayName("Alice") + .githubUsername("alice123") + .role(Role.DOCTOR) + .createdAt(now) + .build(); + + EmployeeDTO dto = employeeMapper.toDTO(entity); + + assertThat(dto.getId()).isEqualTo(id); + assertThat(dto.getDisplayName()).isEqualTo("Alice"); + assertThat(dto.getGithubUsername()).isEqualTo("alice123"); + assertThat(dto.getRole()).isEqualTo(Role.DOCTOR); + assertThat(dto.getCreatedAt()).isEqualTo(now); + } + + @Test + void toEntity_shouldMapAllFields() { + EmployeeCreateDTO dto = EmployeeCreateDTO.builder() + .displayName("Bob") + .githubUsername("bob456") + .role(Role.MANAGER) + .build(); + + EmployeeEntity entity = employeeMapper.toEntity(dto); + + assertThat(entity.getDisplayName()).isEqualTo("Bob"); + assertThat(entity.getGithubUsername()).isEqualTo("bob456"); + assertThat(entity.getRole()).isEqualTo(Role.MANAGER); + assertThat(entity.getId()).isNotNull(); + } + + @Test + void updateEntity_shouldUpdateAllFields() { + EmployeeEntity entity = EmployeeEntity.builder() + .displayName("OldName") + .githubUsername("olduser") + .role(Role.DOCTOR) + .build(); + + EmployeeUpdateDTO dto = EmployeeUpdateDTO.builder() + .displayName("NewName") + .githubUsername("newuser") + .role(Role.MANAGER) + .build(); + + employeeMapper.updateEntity(dto, entity); + + assertThat(entity.getDisplayName()).isEqualTo("NewName"); + assertThat(entity.getGithubUsername()).isEqualTo("newuser"); + assertThat(entity.getRole()).isEqualTo(Role.MANAGER); + } +} diff --git a/src/test/java/org/example/projektarendehantering/application/service/EmployeeServiceTest.java b/src/test/java/org/example/projektarendehantering/application/service/EmployeeServiceTest.java index c851c82..09eca31 100644 --- a/src/test/java/org/example/projektarendehantering/application/service/EmployeeServiceTest.java +++ b/src/test/java/org/example/projektarendehantering/application/service/EmployeeServiceTest.java @@ -1,12 +1,14 @@ package org.example.projektarendehantering.application.service; import org.example.projektarendehantering.common.Actor; +import org.example.projektarendehantering.common.BadRequestException; import org.example.projektarendehantering.common.NotAuthorizedException; import org.example.projektarendehantering.common.Role; import org.example.projektarendehantering.infrastructure.persistence.EmployeeEntity; import org.example.projektarendehantering.infrastructure.persistence.EmployeeRepository; import org.example.projektarendehantering.presentation.dto.EmployeeCreateDTO; import org.example.projektarendehantering.presentation.dto.EmployeeDTO; +import org.example.projektarendehantering.presentation.dto.EmployeeUpdateDTO; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -16,6 +18,7 @@ import java.time.Instant; import java.util.List; +import java.util.Optional; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -84,6 +87,50 @@ void createEmployee_shouldDenyDoctor() { .isInstanceOf(NotAuthorizedException.class); } + @Test + void updateEmployee_shouldUpdateIfManager() { + UUID id = UUID.randomUUID(); + EmployeeUpdateDTO dto = new EmployeeUpdateDTO("New Name", "gh_user", Role.DOCTOR); + EmployeeEntity entity = new EmployeeEntity(); + entity.setGithubUsername("gh_user"); + EmployeeDTO resultDTO = new EmployeeDTO(id, "New Name", "gh_user", Role.DOCTOR, Instant.now()); + + when(employeeRepository.findById(id)).thenReturn(Optional.of(entity)); + when(employeeRepository.save(entity)).thenReturn(entity); + when(employeeMapper.toDTO(entity)).thenReturn(resultDTO); + + EmployeeDTO result = employeeService.updateEmployee(managerActor, id, dto); + + assertThat(result.getDisplayName()).isEqualTo("New Name"); + verify(employeeMapper).updateEntity(dto, entity); + } + + @Test + void updateEmployee_shouldThrowIfUsernameChanged() { + UUID id = UUID.randomUUID(); + EmployeeUpdateDTO dto = new EmployeeUpdateDTO("New Name", "new_gh", Role.DOCTOR); + EmployeeEntity entity = new EmployeeEntity(); + entity.setGithubUsername("old_gh"); + + when(employeeRepository.findById(id)).thenReturn(Optional.of(entity)); + + assertThatThrownBy(() -> employeeService.updateEmployee(managerActor, id, dto)) + .isInstanceOf(BadRequestException.class) + .hasMessageContaining("Github username cannot be changed"); + } + + @Test + void deleteEmployee_shouldDeleteIfManager() { + UUID id = UUID.randomUUID(); + EmployeeEntity entity = new EmployeeEntity(); + + when(employeeRepository.findById(id)).thenReturn(Optional.of(entity)); + + employeeService.deleteEmployee(managerActor, id); + + verify(employeeRepository).delete(entity); + } + @Test void requireCanManageEmployees_shouldThrow_whenActorIsNull() { assertThatThrownBy(() -> employeeService.getAllEmployees(null)) diff --git a/src/test/java/org/example/projektarendehantering/application/service/PatientMapperTest.java b/src/test/java/org/example/projektarendehantering/application/service/PatientMapperTest.java new file mode 100644 index 0000000..6f388b2 --- /dev/null +++ b/src/test/java/org/example/projektarendehantering/application/service/PatientMapperTest.java @@ -0,0 +1,74 @@ +package org.example.projektarendehantering.application.service; + +import org.example.projektarendehantering.infrastructure.persistence.PatientEntity; +import org.example.projektarendehantering.presentation.dto.PatientCreateDTO; +import org.example.projektarendehantering.presentation.dto.PatientDTO; +import org.example.projektarendehantering.presentation.dto.PatientUpdateDTO; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +class PatientMapperTest { + + private final PatientMapper patientMapper = new PatientMapper(); + + @Test + void toDTO_shouldMapAllFields() { + UUID id = UUID.randomUUID(); + Instant now = Instant.now(); + PatientEntity entity = PatientEntity.builder() + .id(id) + .firstName("John") + .lastName("Doe") + .personalIdentityNumber("19900101-1234") + .createdAt(now) + .build(); + + PatientDTO dto = patientMapper.toDTO(entity); + + assertThat(dto.getId()).isEqualTo(id); + assertThat(dto.getFirstName()).isEqualTo("John"); + assertThat(dto.getLastName()).isEqualTo("Doe"); + assertThat(dto.getPersonalIdentityNumber()).isEqualTo("19900101-1234"); + assertThat(dto.getCreatedAt()).isEqualTo(now); + } + + @Test + void toEntity_shouldMapAllFields() { + PatientCreateDTO dto = PatientCreateDTO.builder() + .firstName("Jane") + .lastName("Smith") + .personalIdentityNumber("19910101-5678") + .build(); + + PatientEntity entity = patientMapper.toEntity(dto); + + assertThat(entity.getFirstName()).isEqualTo("Jane"); + assertThat(entity.getLastName()).isEqualTo("Smith"); + assertThat(entity.getPersonalIdentityNumber()).isEqualTo("19910101-5678"); + } + + @Test + void updateEntity_shouldUpdateAllFields() { + PatientEntity entity = PatientEntity.builder() + .firstName("OldFirst") + .lastName("OldLast") + .personalIdentityNumber("19800101-0000") + .build(); + + PatientUpdateDTO dto = PatientUpdateDTO.builder() + .firstName("NewFirst") + .lastName("NewLast") + .personalIdentityNumber("19800101-1111") + .build(); + + patientMapper.updateEntity(dto, entity); + + assertThat(entity.getFirstName()).isEqualTo("NewFirst"); + assertThat(entity.getLastName()).isEqualTo("NewLast"); + assertThat(entity.getPersonalIdentityNumber()).isEqualTo("19800101-1111"); + } +} diff --git a/src/test/java/org/example/projektarendehantering/application/service/PatientServiceTest.java b/src/test/java/org/example/projektarendehantering/application/service/PatientServiceTest.java new file mode 100644 index 0000000..ed12158 --- /dev/null +++ b/src/test/java/org/example/projektarendehantering/application/service/PatientServiceTest.java @@ -0,0 +1,134 @@ +package org.example.projektarendehantering.application.service; + +import org.example.projektarendehantering.common.Actor; +import org.example.projektarendehantering.common.BadRequestException; +import org.example.projektarendehantering.common.ConflictException; +import org.example.projektarendehantering.common.NotAuthorizedException; +import org.example.projektarendehantering.common.Role; +import org.example.projektarendehantering.infrastructure.persistence.PatientEntity; +import org.example.projektarendehantering.infrastructure.persistence.PatientRepository; +import org.example.projektarendehantering.presentation.dto.PatientCreateDTO; +import org.example.projektarendehantering.presentation.dto.PatientDTO; +import org.example.projektarendehantering.presentation.dto.PatientUpdateDTO; +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 java.util.Optional; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class PatientServiceTest { + + @Mock + private PatientRepository patientRepository; + + @Mock + private PatientMapper patientMapper; + + @InjectMocks + private PatientService patientService; + + private Actor managerActor; + private Actor doctorActor; + + @BeforeEach + void setUp() { + managerActor = new Actor(UUID.randomUUID(), Role.MANAGER, "Manager", "mgr"); + doctorActor = new Actor(UUID.randomUUID(), Role.DOCTOR, "Doctor", "doc"); + } + + @Test + void createPatient_shouldSaveAndReturnDTO() { + PatientCreateDTO createDto = new PatientCreateDTO(); + createDto.setPersonalIdentityNumber("19900101-1234"); + PatientEntity entity = new PatientEntity(); + entity.setPersonalIdentityNumber("19900101-1234"); + PatientDTO expectedDto = new PatientDTO(); + + when(patientMapper.toEntity(createDto)).thenReturn(entity); + when(patientRepository.findByPersonalIdentityNumber("19900101-1234")).thenReturn(Optional.empty()); + when(patientRepository.save(any(PatientEntity.class))).thenReturn(entity); + when(patientMapper.toDTO(entity)).thenReturn(expectedDto); + + PatientDTO result = patientService.createPatient(createDto); + + assertThat(result).isEqualTo(expectedDto); + verify(patientRepository).save(entity); + } + + @Test + void createPatient_shouldThrowConflictIfPinExists() { + PatientCreateDTO createDto = new PatientCreateDTO(); + createDto.setPersonalIdentityNumber("19900101-1234"); + PatientEntity entity = new PatientEntity(); + entity.setPersonalIdentityNumber("19900101-1234"); + + when(patientMapper.toEntity(createDto)).thenReturn(entity); + when(patientRepository.findByPersonalIdentityNumber("19900101-1234")).thenReturn(Optional.of(new PatientEntity())); + + assertThatThrownBy(() -> patientService.createPatient(createDto)) + .isInstanceOf(ConflictException.class); + } + + @Test + void updatePatient_shouldUpdateAndReturnDTO() { + UUID id = UUID.randomUUID(); + PatientUpdateDTO updateDto = new PatientUpdateDTO(); + updateDto.setPersonalIdentityNumber("19900101-1234"); + + PatientEntity entity = new PatientEntity(); + entity.setPersonalIdentityNumber("19900101-1111"); + + PatientDTO expectedDto = new PatientDTO(); + + when(patientRepository.findById(id)).thenReturn(Optional.of(entity)); + when(patientRepository.findByPersonalIdentityNumber("19900101-1234")).thenReturn(Optional.empty()); + when(patientRepository.save(entity)).thenReturn(entity); + when(patientMapper.toDTO(entity)).thenReturn(expectedDto); + + PatientDTO result = patientService.updatePatient(managerActor, id, updateDto); + + assertThat(result).isEqualTo(expectedDto); + verify(patientMapper).updateEntity(updateDto, entity); + } + + @Test + void updatePatient_shouldThrowIfForbidden() { + UUID id = UUID.randomUUID(); + PatientUpdateDTO updateDto = new PatientUpdateDTO(); + + assertThatThrownBy(() -> patientService.updatePatient(doctorActor, id, updateDto)) + .isInstanceOf(NotAuthorizedException.class); + } + + @Test + void deletePatient_shouldDeleteIfFound() { + UUID id = UUID.randomUUID(); + PatientEntity entity = new PatientEntity(); + + when(patientRepository.findById(id)).thenReturn(Optional.of(entity)); + + patientService.deletePatient(managerActor, id); + + verify(patientRepository).delete(entity); + } + + @Test + void deletePatient_shouldThrowIfNotFound() { + UUID id = UUID.randomUUID(); + + when(patientRepository.findById(id)).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> patientService.deletePatient(managerActor, id)) + .isInstanceOf(BadRequestException.class); + } +} diff --git a/src/test/java/org/example/projektarendehantering/presentation/rest/EmployeeControllerTest.java b/src/test/java/org/example/projektarendehantering/presentation/rest/EmployeeControllerTest.java index dcd843b..2038d45 100644 --- a/src/test/java/org/example/projektarendehantering/presentation/rest/EmployeeControllerTest.java +++ b/src/test/java/org/example/projektarendehantering/presentation/rest/EmployeeControllerTest.java @@ -7,6 +7,7 @@ import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.EmployeeCreateDTO; import org.example.projektarendehantering.presentation.dto.EmployeeDTO; +import org.example.projektarendehantering.presentation.dto.EmployeeUpdateDTO; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -24,11 +25,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; @@ -104,4 +105,33 @@ void getEmployee_shouldReturnEmployee_whenExists() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(empId.toString())); } + + @Test + @WithMockUser(roles = "MANAGER") + void updateEmployee_shouldReturnOk() throws Exception { + UUID id = UUID.randomUUID(); + EmployeeUpdateDTO input = new EmployeeUpdateDTO("Updated Name", "gh_user", Role.DOCTOR); + EmployeeDTO output = new EmployeeDTO(id, "Updated Name", "gh_user", Role.DOCTOR, Instant.now()); + + when(employeeService.updateEmployee(eq(managerActor), eq(id), any(EmployeeUpdateDTO.class))).thenReturn(output); + + mockMvc.perform(put("/api/employees/{id}", id) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(input))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.displayName").value("Updated Name")); + } + + @Test + @WithMockUser(roles = "MANAGER") + void deleteEmployee_shouldReturnNoContent() throws Exception { + UUID id = UUID.randomUUID(); + + mockMvc.perform(delete("/api/employees/{id}", id) + .with(csrf())) + .andExpect(status().isNoContent()); + + verify(employeeService).deleteEmployee(managerActor, id); + } } diff --git a/src/test/java/org/example/projektarendehantering/presentation/rest/PatientControllerTest.java b/src/test/java/org/example/projektarendehantering/presentation/rest/PatientControllerTest.java new file mode 100644 index 0000000..ec2f1cd --- /dev/null +++ b/src/test/java/org/example/projektarendehantering/presentation/rest/PatientControllerTest.java @@ -0,0 +1,106 @@ +package org.example.projektarendehantering.presentation.rest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.example.projektarendehantering.application.service.PatientService; +import org.example.projektarendehantering.common.Actor; +import org.example.projektarendehantering.common.Role; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; +import org.example.projektarendehantering.presentation.dto.PatientCreateDTO; +import org.example.projektarendehantering.presentation.dto.PatientDTO; +import org.example.projektarendehantering.presentation.dto.PatientUpdateDTO; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.context.WebApplicationContext; + +import java.util.UUID; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; + +@SpringBootTest +class PatientControllerTest { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext context; + + @MockitoBean + private PatientService patientService; + + @MockitoBean + private SecurityActorAdapter securityActorAdapter; + + private ObjectMapper objectMapper = new ObjectMapper(); + private Actor managerActor; + + @BeforeEach + void setUp() { + mockMvc = webAppContextSetup(context) + .apply(springSecurity()) + .build(); + managerActor = new Actor(UUID.randomUUID(), Role.MANAGER, "Manager", "mgr"); + when(securityActorAdapter.currentUser()).thenReturn(managerActor); + } + + @Test + @WithMockUser(roles = "MANAGER") + void createPatient_shouldReturnOk() throws Exception { + PatientCreateDTO input = new PatientCreateDTO("John", "Doe", "19900101-1234"); + PatientDTO output = new PatientDTO(); + output.setFirstName("John"); + + when(patientService.createPatient(any(PatientCreateDTO.class))).thenReturn(output); + + mockMvc.perform(post("/api/patients") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(input))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.firstName").value("John")); + } + + @Test + @WithMockUser(roles = "MANAGER") + void updatePatient_shouldReturnOk() throws Exception { + UUID id = UUID.randomUUID(); + PatientUpdateDTO input = new PatientUpdateDTO("Jane", "Doe", "19900101-1234"); + PatientDTO output = new PatientDTO(); + output.setFirstName("Jane"); + + when(patientService.updatePatient(eq(managerActor), eq(id), any(PatientUpdateDTO.class))).thenReturn(output); + + mockMvc.perform(put("/api/patients/{id}", id) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(input))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.firstName").value("Jane")); + } + + @Test + @WithMockUser(roles = "MANAGER") + void deletePatient_shouldReturnNoContent() throws Exception { + UUID id = UUID.randomUUID(); + + mockMvc.perform(delete("/api/patients/{id}", id) + .with(csrf())) + .andExpect(status().isNoContent()); + + verify(patientService).deletePatient(managerActor, id); + } +} diff --git a/src/test/java/org/example/projektarendehantering/presentation/web/EmployeeUiControllerTest.java b/src/test/java/org/example/projektarendehantering/presentation/web/EmployeeUiControllerTest.java new file mode 100644 index 0000000..008f848 --- /dev/null +++ b/src/test/java/org/example/projektarendehantering/presentation/web/EmployeeUiControllerTest.java @@ -0,0 +1,86 @@ +package org.example.projektarendehantering.presentation.web; + +import org.example.projektarendehantering.application.service.EmployeeService; +import org.example.projektarendehantering.common.Actor; +import org.example.projektarendehantering.common.Role; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; +import org.example.projektarendehantering.presentation.dto.EmployeeDTO; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.context.WebApplicationContext; + +import java.time.Instant; +import java.util.Optional; +import java.util.UUID; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; + +@SpringBootTest +class EmployeeUiControllerTest { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext context; + + @MockitoBean + private EmployeeService employeeService; + + @MockitoBean + private SecurityActorAdapter securityActorAdapter; + + private Actor managerActor; + + @BeforeEach + void setUp() { + mockMvc = webAppContextSetup(context) + .apply(springSecurity()) + .build(); + managerActor = new Actor(UUID.randomUUID(), Role.MANAGER, "Manager", "manager_user"); + when(securityActorAdapter.currentUser()).thenReturn(managerActor); + } + + @Test + @WithMockUser(roles = "MANAGER") + void listEmployees_shouldReturnView() throws Exception { + mockMvc.perform(get("/ui/employees")) + .andExpect(status().isOk()) + .andExpect(view().name("employees/list")) + .andExpect(model().attributeExists("employees")); + } + + @Test + @WithMockUser(roles = "MANAGER") + void editEmployee_shouldReturnView() throws Exception { + UUID id = UUID.randomUUID(); + EmployeeDTO employee = new EmployeeDTO(id, "Name", "gh", Role.DOCTOR, Instant.now()); + when(employeeService.getEmployee(eq(managerActor), eq(id))).thenReturn(Optional.of(employee)); + + mockMvc.perform(get("/ui/employees/edit/{id}", id)) + .andExpect(status().isOk()) + .andExpect(view().name("employees/edit")) + .andExpect(model().attributeExists("employeeUpdateDTO")); + } + + @Test + @WithMockUser(roles = "MANAGER") + void deleteEmployee_shouldRedirect() throws Exception { + UUID id = UUID.randomUUID(); + mockMvc.perform(post("/ui/employees/delete/{id}", id).with(csrf())) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/ui/employees")); + } +} diff --git a/src/test/java/org/example/projektarendehantering/presentation/web/PatientUiControllerTest.java b/src/test/java/org/example/projektarendehantering/presentation/web/PatientUiControllerTest.java new file mode 100644 index 0000000..847e1bf --- /dev/null +++ b/src/test/java/org/example/projektarendehantering/presentation/web/PatientUiControllerTest.java @@ -0,0 +1,74 @@ +package org.example.projektarendehantering.presentation.web; + +import org.example.projektarendehantering.application.service.PatientService; +import org.example.projektarendehantering.presentation.dto.PatientDTO; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.context.WebApplicationContext; + +import java.util.Optional; +import java.util.UUID; + +import static org.mockito.Mockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; + +@SpringBootTest +class PatientUiControllerTest { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext context; + + @MockitoBean + private PatientService patientService; + + @BeforeEach + void setUp() { + mockMvc = webAppContextSetup(context) + .apply(springSecurity()) + .build(); + } + + @Test + @WithMockUser(roles = "MANAGER") + void listPatients_shouldReturnView() throws Exception { + mockMvc.perform(get("/ui/patients")) + .andExpect(status().isOk()) + .andExpect(view().name("patients/list")) + .andExpect(model().attributeExists("patients")); + } + + @Test + @WithMockUser(roles = "MANAGER") + void editPatient_shouldReturnView() throws Exception { + UUID id = UUID.randomUUID(); + PatientDTO patient = new PatientDTO(); + patient.setFirstName("John"); + when(patientService.getPatient(id)).thenReturn(Optional.of(patient)); + + mockMvc.perform(get("/ui/patients/edit/{id}", id)) + .andExpect(status().isOk()) + .andExpect(view().name("patients/edit")) + .andExpect(model().attributeExists("patientUpdateDTO")); + } + + @Test + @WithMockUser(roles = "MANAGER") + void deletePatient_shouldRedirect() throws Exception { + UUID id = UUID.randomUUID(); + mockMvc.perform(post("/ui/patients/delete/{id}", id).with(csrf())) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/ui/patients")); + } +}