diff --git a/src/main/java/org/unilab/uniplan/student/StudentController.java b/src/main/java/org/unilab/uniplan/student/StudentController.java index b21f45bb..2f757c45 100644 --- a/src/main/java/org/unilab/uniplan/student/StudentController.java +++ b/src/main/java/org/unilab/uniplan/student/StudentController.java @@ -5,7 +5,6 @@ import jakarta.validation.constraints.NotNull; import java.util.List; import java.util.UUID; -import jakarta.validation.constraints.Size; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -16,10 +15,7 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.unilab.uniplan.student.dto.StudentCourseMajorDto; -import org.unilab.uniplan.student.dto.StudentDto; import org.unilab.uniplan.student.dto.StudentRequestDto; import org.unilab.uniplan.student.dto.StudentResponseDto; @@ -29,54 +25,35 @@ @Tag(name = "Students", description = "Manage students, including faculty numbers and enrollment in course") public class StudentController { - private final StudentService studentService; - private final StudentMapper studentMapper; + private final StudentWebFacade studentWebFacade; @PostMapping - public ResponseEntity createStudent(@RequestBody @NotNull - @Valid final StudentRequestDto studentRequestDTO) { - final StudentDto studentDTO = studentMapper.toInternalDto(studentRequestDTO); - studentService.createStudent(studentDTO); - return ResponseEntity.status(HttpStatus.CREATED) - .body(studentMapper.toResponseDto(studentDTO)); + public ResponseEntity createStudent(@RequestBody @NotNull @Valid final StudentRequestDto studentRequestDTO) { + studentWebFacade.createStudent(studentRequestDTO); + return ResponseEntity.status(HttpStatus.CREATED).build(); } @GetMapping("/{id}") - public ResponseEntity getStudent(@PathVariable - @NotNull final UUID id) { - final StudentResponseDto studentResponseDTO = studentMapper.toResponseDto(studentService.findStudentById( - id)); - - return ResponseEntity.ok(studentResponseDTO); + public ResponseEntity getStudentById(@PathVariable final UUID id) { + return ResponseEntity.ok(studentWebFacade.getStudentById(id)); } @GetMapping - public List getAllStudents() { - return studentMapper.toResponseDtoList(studentService.findAll()); - } - - @GetMapping("/student-course-major/getStudentCourseMajorInfo") - public List getStudentCourseMajorInfo(@RequestParam(required = false) @Size(max = 100) final String firstName, - @RequestParam(required = false) @Size(max = 100) final String lastName, - @RequestParam(required = false) final String facultyNumber, - @RequestParam(required = false) @Size(max = 200) final String majorName){ - return studentService.findStudentCourseMajorInfo(firstName, lastName, facultyNumber, majorName); + public ResponseEntity> getAllStudents() { + return ResponseEntity.ok(studentWebFacade.getAllStudents()); } @PutMapping("/{id}") - public ResponseEntity updateStudent(@PathVariable - @NotNull final UUID id, + public ResponseEntity updateStudent(@PathVariable final UUID id, @RequestBody @NotNull @Valid final StudentRequestDto studentRequestDTO) { - final StudentDto studentDTO = studentMapper.toInternalDto(studentRequestDTO); - studentService.updateStudent(id, studentDTO); - return ResponseEntity.ok(studentMapper.toResponseDto(studentDTO)); + studentWebFacade.updateStudent(id, studentRequestDTO); + return ResponseEntity.noContent().build(); } @DeleteMapping("/{id}") - public ResponseEntity deleteStudent(@PathVariable - @NotNull final UUID id) { - studentService.deleteStudent(id); + public ResponseEntity deleteStudent(@PathVariable final UUID id) { + studentWebFacade.deleteStudent(id); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/org/unilab/uniplan/student/StudentMapper.java b/src/main/java/org/unilab/uniplan/student/StudentMapper.java index 440e5043..a3890b48 100644 --- a/src/main/java/org/unilab/uniplan/student/StudentMapper.java +++ b/src/main/java/org/unilab/uniplan/student/StudentMapper.java @@ -4,7 +4,6 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; -import org.unilab.uniplan.student.dto.StudentDto; import org.unilab.uniplan.student.dto.StudentRequestDto; import org.unilab.uniplan.student.dto.StudentResponseDto; @@ -12,21 +11,16 @@ public interface StudentMapper { @Mapping(source = "courseId", target = "course.id") - Student toEntity(StudentDto studentDto); - - @Mapping(source = "course.id", target = "courseId") - StudentDto toDto(Student student); - @Mapping(target = "id", ignore = true) - @Mapping(source = "courseId", target = "course.id") - void updateEntityFromDto(StudentDto studentDto, @MappingTarget Student student); + Student toEntity(StudentRequestDto requestDto); @Mapping(target = "id", ignore = true) - StudentDto toInternalDto(StudentRequestDto student); + @Mapping(source = "courseId", target = "course.id") + void updateEntity(StudentRequestDto requestDto, @MappingTarget Student student); - @Mapping(source = "courseId", target = "courseId") - StudentResponseDto toResponseDto(StudentDto studentDto); + @Mapping(source = "course.id", target = "courseId") + StudentResponseDto toResponseDto(Student student); - List toResponseDtoList(List students); + List toResponseDtoList(List students); } \ No newline at end of file diff --git a/src/main/java/org/unilab/uniplan/student/StudentRepository.java b/src/main/java/org/unilab/uniplan/student/StudentRepository.java index 85904938..d373973d 100644 --- a/src/main/java/org/unilab/uniplan/student/StudentRepository.java +++ b/src/main/java/org/unilab/uniplan/student/StudentRepository.java @@ -1,33 +1,9 @@ package org.unilab.uniplan.student; -import java.util.List; import java.util.UUID; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import org.unilab.uniplan.student.dto.StudentCourseMajorDto; @Repository public interface StudentRepository extends JpaRepository { - @Query(""" - select new org.unilab.uniplan.student.dto.StudentCourseMajorDto( - s.id, s.firstName, s.lastName, s.facultyNumber, - c.id, c.courseType, c.courseSubtype, c.courseYear, - m.id, m.majorName - ) - from Student s - join s.course c - join c.major m - where (:firstName is null or lower(s.firstName) like lower(concat('%', :firstName, '%'))) - and (:lastName is null or lower(s.lastName) like lower(concat('%', :lastName, '%'))) - and (:facultyNumber is null or s.facultyNumber like concat('%', :facultyNumber, '%')) - and (:majorName is null or lower(m.majorName) like lower(concat('%', :majorName, '%'))) - """) - List searchStudents( - @Param("firstName") String firstName, - @Param("lastName") String lastName, - @Param("facultyNumber") String facultyNumber, - @Param("majorName") String majorName - ); } diff --git a/src/main/java/org/unilab/uniplan/student/StudentService.java b/src/main/java/org/unilab/uniplan/student/StudentService.java index c616dbd3..cd3dc4e6 100644 --- a/src/main/java/org/unilab/uniplan/student/StudentService.java +++ b/src/main/java/org/unilab/uniplan/student/StudentService.java @@ -1,72 +1,35 @@ package org.unilab.uniplan.student; -import static org.unilab.uniplan.utils.ErrorConstants.STUDENT_NOT_FOUND; - -import jakarta.transaction.Transactional; import java.util.List; +import java.util.Optional; import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.unilab.uniplan.exception.ResourceNotFoundException; -import org.unilab.uniplan.student.dto.StudentCourseMajorDto; -import org.unilab.uniplan.student.dto.StudentDto; +import org.unilab.uniplan.common.model.BaseService; @Service @RequiredArgsConstructor -public class StudentService { +public class StudentService implements BaseService { private final StudentRepository studentRepository; - private final StudentMapper studentMapper; - - @Transactional - public StudentDto createStudent(final StudentDto studentDTO) { - final Student student = studentMapper.toEntity(studentDTO); - return saveEntityAndConvertToDto(student); - } - - public StudentDto findStudentById(final UUID id) { - return studentRepository.findById(id) - .map(studentMapper::toDto) - .orElseThrow(() -> new ResourceNotFoundException(STUDENT_NOT_FOUND.getMessage( - String.valueOf(id)))); - } - - public List findAll() { - return studentRepository.findAll() - .stream().map(studentMapper::toDto).toList(); - } - - @Transactional - public StudentDto updateStudent(final UUID id, final StudentDto studentDTO) { - return studentRepository.findById(id) - .map(existingStudent -> updateEntityAndConvertToDto( - studentDTO, - existingStudent)) - .orElseThrow(() -> new ResourceNotFoundException(STUDENT_NOT_FOUND.getMessage( - String.valueOf(id)))); - } - @Transactional - public void deleteStudent(final UUID id) { - final Student student = studentRepository.findById(id) - .orElseThrow(() -> new ResourceNotFoundException( - STUDENT_NOT_FOUND.getMessage(String.valueOf(id)))); - studentRepository.delete(student); + @Override + public void save(final Student entity) { + studentRepository.save(entity); } - public List findStudentCourseMajorInfo(final String firstName, final String lastName, - final String facultyNumber, final String majorName){ - return studentRepository.searchStudents(firstName, lastName, facultyNumber, majorName); + @Override + public List getAll() { + return studentRepository.findAll(); } - private StudentDto updateEntityAndConvertToDto(final StudentDto dto, - final Student entity) { - studentMapper.updateEntityFromDto(dto, entity); - return saveEntityAndConvertToDto(entity); + @Override + public Optional getById(final UUID id) { + return studentRepository.findById(id); } - private StudentDto saveEntityAndConvertToDto(final Student entity) { - final Student savedEntity = studentRepository.save(entity); - return studentMapper.toDto(savedEntity); + @Override + public void delete(final Student entity) { + studentRepository.delete(entity); } } diff --git a/src/main/java/org/unilab/uniplan/student/StudentValidator.java b/src/main/java/org/unilab/uniplan/student/StudentValidator.java new file mode 100644 index 00000000..5a2fcbe2 --- /dev/null +++ b/src/main/java/org/unilab/uniplan/student/StudentValidator.java @@ -0,0 +1,24 @@ +package org.unilab.uniplan.student; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.unilab.uniplan.course.CourseRepository; +import org.unilab.uniplan.exception.ResourceNotFoundException; +import org.unilab.uniplan.student.dto.StudentRequestDto; + +import java.util.UUID; + +import static org.unilab.uniplan.utils.ErrorConstants.COURSE_NOT_FOUND; + +@Component +@RequiredArgsConstructor +public class StudentValidator{ + private final CourseRepository courseRepository; + + public void validate(final StudentRequestDto request) { + UUID id = request.courseId(); + if (!courseRepository.existsById(id)){ + throw new ResourceNotFoundException(COURSE_NOT_FOUND.getMessage(id.toString())); + } + } +} diff --git a/src/main/java/org/unilab/uniplan/student/StudentWebFacade.java b/src/main/java/org/unilab/uniplan/student/StudentWebFacade.java new file mode 100644 index 00000000..3f41e0f7 --- /dev/null +++ b/src/main/java/org/unilab/uniplan/student/StudentWebFacade.java @@ -0,0 +1,66 @@ +package org.unilab.uniplan.student; + +import org.springframework.transaction.annotation.Transactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.unilab.uniplan.exception.ResourceNotFoundException; +import org.unilab.uniplan.student.dto.StudentRequestDto; +import org.unilab.uniplan.student.dto.StudentResponseDto; +import java.util.List; +import java.util.UUID; + +import static org.unilab.uniplan.utils.ErrorConstants.STUDENT_NOT_FOUND; + +@Component +@Slf4j +@RequiredArgsConstructor +public class StudentWebFacade { + + private final StudentMapper studentMapper; + private final StudentService studentService; + private final StudentValidator studentValidator; + + @Transactional + public void createStudent(final StudentRequestDto request){ + studentValidator.validate(request); + final Student student = studentMapper.toEntity(request); + studentService.save(student); + log.info("created student with ID: {}", student.getId()); + } + + @Transactional(readOnly = true) + public List getAllStudents(){ + return studentMapper.toResponseDtoList(studentService.getAll()); + } + + @Transactional(readOnly = true) + public StudentResponseDto getStudentById(final UUID id){ + final Student student = getStudentOrThrow(id); + return studentMapper.toResponseDto(student); + } + + @Transactional + public void updateStudent(final UUID id, + final StudentRequestDto request){ + studentValidator.validate(request); + final Student student = getStudentOrThrow(id); + studentMapper.updateEntity(request, student); + studentService.save(student); + log.info("updated student with ID: {}", student.getId()); + } + + @Transactional + public void deleteStudent(final UUID id){ + final Student student = getStudentOrThrow(id); + studentService.delete(student); + log.info("deleted student with ID: {}", id); + } + + private Student getStudentOrThrow(final UUID id){ + return studentService.getById(id) + .orElseThrow(() -> new ResourceNotFoundException( + STUDENT_NOT_FOUND.getMessage(String.valueOf(id)) + )); + } +} diff --git a/src/main/java/org/unilab/uniplan/student/dto/StudentCourseMajorDto.java b/src/main/java/org/unilab/uniplan/student/dto/StudentCourseMajorDto.java deleted file mode 100644 index 9dc5a51d..00000000 --- a/src/main/java/org/unilab/uniplan/student/dto/StudentCourseMajorDto.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.unilab.uniplan.student.dto; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import java.util.UUID; - -public record StudentCourseMajorDto( - UUID studentId, - @NotBlank - @Size(max = 100) - String firstName, - @NotBlank - @Size(max = 100) - String lastName, - @Size(max = 40) - String facultyNumber, - @NotNull - UUID courseId, - @NotNull - String courseType, - @NotNull - String courseSubType, - @NotNull - Byte courseYear, - @NotNull - UUID majorId, - @NotBlank - @Size(max = 200) - String majorName -) { - -} diff --git a/src/main/java/org/unilab/uniplan/student/dto/StudentDto.java b/src/main/java/org/unilab/uniplan/student/dto/StudentDto.java deleted file mode 100644 index 0329a527..00000000 --- a/src/main/java/org/unilab/uniplan/student/dto/StudentDto.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.unilab.uniplan.student.dto; - -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import java.util.UUID; - -public record StudentDto( - UUID id, - @NotNull - @Size(max = 100) - String firstName, - @NotNull - @Size(max = 100) - String lastName, - @Size(max = 40) - String facultyNumber, - @NotNull - UUID courseId -) { - -} diff --git a/src/main/java/org/unilab/uniplan/student/dto/StudentResponseDto.java b/src/main/java/org/unilab/uniplan/student/dto/StudentResponseDto.java index 8ed48fec..9cba5090 100644 --- a/src/main/java/org/unilab/uniplan/student/dto/StudentResponseDto.java +++ b/src/main/java/org/unilab/uniplan/student/dto/StudentResponseDto.java @@ -1,16 +1,10 @@ package org.unilab.uniplan.student.dto; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; import java.util.UUID; public record StudentResponseDto( UUID id, - @NotNull - @Size(max = 100) String firstName, - @NotNull - @Size(max = 100) String lastName, String facultyNumber, UUID courseId diff --git a/src/test/java/org/unilab/uniplan/student/StudentMapperTest.java b/src/test/java/org/unilab/uniplan/student/StudentMapperTest.java new file mode 100644 index 00000000..7a6a5e8e --- /dev/null +++ b/src/test/java/org/unilab/uniplan/student/StudentMapperTest.java @@ -0,0 +1,110 @@ +package org.unilab.uniplan.student; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mapstruct.factory.Mappers; +import org.mockito.junit.jupiter.MockitoExtension; +import org.unilab.uniplan.course.Course; +import org.unilab.uniplan.student.dto.StudentRequestDto; +import org.unilab.uniplan.student.dto.StudentResponseDto; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.AssertionsKt.assertNull; + +@ExtendWith(MockitoExtension.class) +class StudentMapperTest { + + private final StudentMapper studentMapper = Mappers.getMapper(StudentMapper.class); + + private UUID studentId; + private UUID courseId; + private Student student; + private StudentRequestDto requestDto; + + @BeforeEach + void setUp() { + studentId = UUID.randomUUID(); + courseId = UUID.randomUUID(); + + requestDto = new StudentRequestDto("Petar", "Petrov", "2301261005", courseId); + + Course course = new Course(); + course.setId(courseId); + + student = new Student(); + student.setId(studentId); + student.setFirstName("Petar"); + student.setLastName("Petrov"); + student.setFacultyNumber("2301261005"); + student.setCourse(course); + } + + @Test + void toEntity_ShouldMapFieldsCorrectly() { + Student result = studentMapper.toEntity(requestDto); + + assertNull(result.getId()); + assertEquals("Petar", result.getFirstName()); + assertEquals("Petrov", result.getLastName()); + assertEquals("2301261005", result.getFacultyNumber()); + assertEquals(courseId, result.getCourse().getId()); + } + + @Test + void toEntity_ShouldIgnoreId() { + Student result = studentMapper.toEntity(requestDto); + + assertNull(result.getId()); + } + + @Test + void updateEntity_ShouldUpdateFieldsCorrectly() { + UUID newCourseId = UUID.randomUUID(); + StudentRequestDto updateDto = new StudentRequestDto("Ivan", "Ivanov", "1234567890", newCourseId); + + studentMapper.updateEntity(updateDto, student); + + assertEquals("Ivan", student.getFirstName()); + assertEquals("Ivanov", student.getLastName()); + assertEquals("1234567890", student.getFacultyNumber()); + assertEquals(newCourseId, student.getCourse().getId()); + } + + @Test + void updateEntity_ShouldNotAlterStudentId() { + studentMapper.updateEntity(requestDto, student); + + assertEquals(studentId, student.getId()); + } + + @Test + void toResponseDto_ShouldMapFieldsCorrectly() { + StudentResponseDto result = studentMapper.toResponseDto(student); + + assertEquals(studentId, result.id()); + assertEquals("Petar", result.firstName()); + assertEquals("Petrov", result.lastName()); + assertEquals("2301261005", result.facultyNumber()); + assertEquals(courseId, result.courseId()); + } + + @Test + void toResponseDtoList_ShouldMapAllElements() { + List result = studentMapper.toResponseDtoList(List.of(student)); + + assertEquals(1, result.size()); + assertEquals(studentId, result.getFirst().id()); + } + + @Test + void toResponseDtoList_ShouldReturnEmptyForEmptyInput() { + List result = studentMapper.toResponseDtoList(List.of()); + + assertTrue(result.isEmpty()); + } +} diff --git a/src/test/java/org/unilab/uniplan/student/StudentServiceTest.java b/src/test/java/org/unilab/uniplan/student/StudentServiceTest.java index f20e52c6..ca1bc5e4 100644 --- a/src/test/java/org/unilab/uniplan/student/StudentServiceTest.java +++ b/src/test/java/org/unilab/uniplan/student/StudentServiceTest.java @@ -1,11 +1,7 @@ package org.unilab.uniplan.student; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -18,8 +14,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.unilab.uniplan.exception.ResourceNotFoundException; -import org.unilab.uniplan.student.dto.StudentDto; @ExtendWith(MockitoExtension.class) class StudentServiceTest { @@ -27,108 +21,51 @@ class StudentServiceTest { @Mock private StudentRepository studentRepository; - @Mock - private StudentMapper studentMapper; - @InjectMocks private StudentService studentService; - private UUID studentId; - private StudentDto studentDTO; private Student student; @BeforeEach - void beforeAll() { - studentId = UUID.randomUUID(); - UUID courseId = UUID.randomUUID(); - studentDTO = new StudentDto(studentId, "Petar", "Petrov", "2301261005", courseId); + void setUp() { student = new Student(); } @Test - void createStudentShouldReturnSaveAndReturnStudentDTO() { - when(studentMapper.toEntity(studentDTO)).thenReturn(student); - when(studentRepository.save(student)).thenReturn(student); - when(studentMapper.toDto(student)).thenReturn(studentDTO); - - StudentDto result = studentService.createStudent(studentDTO); - - assertEquals(studentDTO, result); + void save_ShouldDelegateToRepository() { + studentService.save(student); verify(studentRepository).save(student); } @Test - void findStudentByIdShouldReturnStudentDTOIfExists() { - when(studentRepository.findById(studentId)).thenReturn(Optional.of(student)); - when(studentMapper.toDto(student)).thenReturn(studentDTO); - - StudentDto result = studentService.findStudentById(studentId); - - assertEquals(studentDTO, result); - } - - @Test - void findStudentByIdShouldReturnEmptyIfNotExists() { - when(studentRepository.findById(studentId)).thenReturn(Optional.empty()); - - ResourceNotFoundException exception = assertThrows(ResourceNotFoundException.class, () -> studentService.findStudentById(studentId)); - - assertTrue(exception.getMessage().contains(String.valueOf(studentId))); - } - - @Test - void findAllShouldReturnMappedStudentDTOList() { - List students = List.of(student); - when(studentRepository.findAll()).thenReturn(students); - when(studentMapper.toDto(any(Student.class))).thenReturn(studentDTO); + void getById_ShouldReturnStudent_IfExists() { + UUID id = UUID.randomUUID(); + when(studentRepository.findById(id)).thenReturn(Optional.of(student)); - List result = studentService.findAll(); + Optional result = studentService.getById(id); - assertEquals(1, result.size()); - assertEquals(studentDTO, result.getFirst()); - verify(studentRepository).findAll(); + assertTrue(result.isPresent()); + assertEquals(student, result.get()); } @Test - void updateStudentShouldReturnUpdatedStudentDTOIfExists() { - when(studentRepository.findById(studentId)).thenReturn(Optional.of(student)); - doNothing().when(studentMapper).updateEntityFromDto(studentDTO, student); - when(studentRepository.save(student)).thenReturn(student); - when(studentMapper.toDto(student)).thenReturn(studentDTO); - - StudentDto result = studentService.updateStudent(studentId, studentDTO); + void getById_ShouldReturnEmpty_IfNotExists() { + UUID id = UUID.randomUUID(); + when(studentRepository.findById(id)).thenReturn(Optional.empty()); - assertEquals(studentDTO, result); - verify(studentRepository).save(student); + assertTrue(studentService.getById(id).isEmpty()); } @Test - void updateStudentShouldReturnEmptyIfNotFound() { - when(studentRepository.findById(studentId)).thenReturn(Optional.empty()); - - ResourceNotFoundException exception = assertThrows(ResourceNotFoundException.class, () -> studentService.updateStudent(studentId, studentDTO)); + void getAll_ShouldReturnAllStudents() { + when(studentRepository.findAll()).thenReturn(List.of(student)); - assertTrue(exception.getMessage().contains(String.valueOf(studentId))); - verify(studentRepository, never()).save(any()); + assertEquals(1, studentService.getAll().size()); } @Test - void deleteStudentShouldRemoveStudentDTOIfExists() { - when(studentRepository.findById(studentId)).thenReturn(Optional.of(student)); - - studentService.deleteStudent(studentId); - + void delete_ShouldDelegateToRepository() { + studentService.delete(student); verify(studentRepository).delete(student); } - - @Test - void deleteStudentShouldThrowIfNotFound() { - when(studentRepository.findById(studentId)).thenReturn(Optional.empty()); - - ResourceNotFoundException exception = assertThrows(ResourceNotFoundException.class, - () -> studentService.deleteStudent(studentId)); - - assertTrue(exception.getMessage().contains(String.valueOf(studentId))); - verify(studentRepository, never()).delete(any()); - } } diff --git a/src/test/java/org/unilab/uniplan/student/StudentValidatorTest.java b/src/test/java/org/unilab/uniplan/student/StudentValidatorTest.java new file mode 100644 index 00000000..1dd07d23 --- /dev/null +++ b/src/test/java/org/unilab/uniplan/student/StudentValidatorTest.java @@ -0,0 +1,53 @@ +package org.unilab.uniplan.student; + +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.unilab.uniplan.course.CourseRepository; +import org.unilab.uniplan.exception.ResourceNotFoundException; +import org.unilab.uniplan.student.dto.StudentRequestDto; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class StudentValidatorTest { + + @Mock + private CourseRepository courseRepository; + + @InjectMocks + private StudentValidator studentValidator; + + private UUID courseId; + private StudentRequestDto requestDto; + + @BeforeEach + void setUp() { + courseId = UUID.randomUUID(); + requestDto = new StudentRequestDto("Petar", "Petrov", "2301261005", courseId); + } + + @Test + void validate_ShouldPass_WhenCourseExists() { + when(courseRepository.existsById(courseId)).thenReturn(true); + + assertDoesNotThrow(() -> studentValidator.validate(requestDto)); + } + + @Test + void validate_ShouldThrow_WhenCourseNotFound() { + when(courseRepository.existsById(courseId)).thenReturn(false); + + ResourceNotFoundException exception = assertThrows(ResourceNotFoundException.class, + () -> studentValidator.validate(requestDto)); + + assertTrue(exception.getMessage().contains(courseId.toString())); + } +} diff --git a/src/test/java/org/unilab/uniplan/student/StudentWebFacadeTest.java b/src/test/java/org/unilab/uniplan/student/StudentWebFacadeTest.java new file mode 100644 index 00000000..cc459844 --- /dev/null +++ b/src/test/java/org/unilab/uniplan/student/StudentWebFacadeTest.java @@ -0,0 +1,122 @@ +package org.unilab.uniplan.student; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InOrder; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.unilab.uniplan.exception.ResourceNotFoundException; +import org.unilab.uniplan.student.dto.StudentRequestDto; +import org.unilab.uniplan.student.dto.StudentResponseDto; +import java.util.Optional; +import java.util.UUID; + +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.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class StudentWebFacadeTest { + + @Mock + private StudentMapper studentMapper; + + @Mock + private StudentService studentService; + + @Mock + private StudentValidator studentValidator; + + @InjectMocks + private StudentWebFacade studentWebFacade; + + private UUID studentId; + private UUID courseId; + private Student student; + private StudentRequestDto requestDto; + private StudentResponseDto responseDto; + + @BeforeEach + void setUp() { + studentId = UUID.randomUUID(); + courseId = UUID.randomUUID(); + student = new Student(); + requestDto = new StudentRequestDto("Petar", "Petrov", "2301261005", courseId); + responseDto = new StudentResponseDto(studentId, "Petar", "Petrov", "2301261005", courseId); + } + + @Test + void createStudent_ShouldMapValidateAndSave() { + when(studentMapper.toEntity(requestDto)).thenReturn(student); + + studentWebFacade.createStudent(requestDto); + + InOrder order = inOrder(studentValidator, studentMapper, studentService); + order.verify(studentValidator).validate(requestDto); + order.verify(studentMapper).toEntity(requestDto); + order.verify(studentService).save(student); + } + + @Test + void updateStudent_ShouldThrow_IfNotFound() { + when(studentService.getById(studentId)).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, + () -> studentWebFacade.updateStudent(studentId, requestDto)); + + verify(studentService, never()).save(any()); + } + + @Test + void updateStudent_ShouldValidateAndSave() { + when(studentService.getById(studentId)).thenReturn(Optional.of(student)); + + studentWebFacade.updateStudent(studentId, requestDto); + + InOrder order = inOrder(studentValidator, studentMapper, studentService); + order.verify(studentValidator).validate(requestDto); + order.verify(studentMapper).updateEntity(requestDto, student); + order.verify(studentService).save(student); + } + + @Test + void getStudentById_ShouldReturnMappedDto() { + when(studentService.getById(studentId)).thenReturn(Optional.of(student)); + when(studentMapper.toResponseDto(student)).thenReturn(responseDto); + + assertEquals(responseDto, studentWebFacade.getStudentById(studentId)); + } + + @Test + void getStudentById_ShouldThrow_IfNotFound() { + when(studentService.getById(studentId)).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, + () -> studentWebFacade.getStudentById(studentId)); + } + + @Test + void deleteStudent_ShouldDelegateIfExists() { + when(studentService.getById(studentId)).thenReturn(Optional.of(student)); + + studentWebFacade.deleteStudent(studentId); + + verify(studentService).delete(student); + } + + @Test + void deleteStudent_ShouldThrow_IfNotFound() { + when(studentService.getById(studentId)).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, + () -> studentWebFacade.deleteStudent(studentId)); + + verify(studentService, never()).delete(any()); + } +}