package com.reportdist.service; import com.reportdist.dto.ProjectRequest; import com.reportdist.dto.ProjectResponse; import com.reportdist.entity.Project; import com.reportdist.repository.ProjectRepository; import com.reportdist.repository.ReportRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.time.LocalDateTime; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class ProjectServiceTest { @Mock private ProjectRepository projectRepository; @Mock private ReportRepository reportRepository; private ProjectService projectService; @BeforeEach void setUp() { projectService = new ProjectService(projectRepository, reportRepository); } // ==================== createProject Tests ==================== @Test void createProject_shouldCreateAndReturnProject() { // Given ProjectRequest request = new ProjectRequest("Test Project", "Test Description"); Project savedProject = new Project(1L, "Test Project", "Test Description", LocalDateTime.now()); when(projectRepository.save(any(Project.class))).thenReturn(savedProject); // When ProjectResponse response = projectService.createProject(request); // Then assertNotNull(response); assertEquals(1L, response.getId()); assertEquals("Test Project", response.getName()); assertEquals("Test Description", response.getDescription()); verify(projectRepository, times(1)).save(any(Project.class)); } @Test void createProject_shouldHandleDuplicateName() { // Given - Note: The service doesn't explicitly handle duplicates, // but we test the normal flow still works ProjectRequest request = new ProjectRequest("Duplicate Project", "Description"); Project savedProject = new Project(2L, "Duplicate Project", "Description", LocalDateTime.now()); when(projectRepository.save(any(Project.class))).thenReturn(savedProject); // When ProjectResponse response = projectService.createProject(request); // Then assertNotNull(response); assertEquals("Duplicate Project", response.getName()); verify(projectRepository, times(1)).save(any(Project.class)); } // ==================== getAllProjects Tests ==================== @Test void getAllProjects_shouldReturnEmptyList() { // Given when(projectRepository.findAll()).thenReturn(Collections.emptyList()); // When List result = projectService.getAllProjects(); // Then assertNotNull(result); assertTrue(result.isEmpty()); } @Test void getAllProjects_shouldReturnSingleProject() { // Given Project project = new Project(1L, "Single Project", "Description", LocalDateTime.now()); when(projectRepository.findAll()).thenReturn(Collections.singletonList(project)); // When List result = projectService.getAllProjects(); // Then assertNotNull(result); assertEquals(1, result.size()); assertEquals("Single Project", result.get(0).getName()); } @Test void getAllProjects_shouldReturnMultipleProjects() { // Given Project project1 = new Project(1L, "Project One", "Description 1", LocalDateTime.now()); Project project2 = new Project(2L, "Project Two", "Description 2", LocalDateTime.now()); Project project3 = new Project(3L, "Project Three", "Description 3", LocalDateTime.now()); when(projectRepository.findAll()).thenReturn(Arrays.asList(project1, project2, project3)); // When List result = projectService.getAllProjects(); // Then assertNotNull(result); assertEquals(3, result.size()); assertEquals("Project One", result.get(0).getName()); assertEquals("Project Two", result.get(1).getName()); assertEquals("Project Three", result.get(2).getName()); } // ==================== updateProject Tests ==================== @Test void updateProject_shouldUpdateAndReturnProject() { // Given Long projectId = 1L; ProjectRequest request = new ProjectRequest("Updated Name", "Updated Description"); Project existingProject = new Project(1L, "Original Name", "Original Description", LocalDateTime.now()); Project updatedProject = new Project(1L, "Updated Name", "Updated Description", existingProject.getCreatedAt()); when(projectRepository.findById(projectId)).thenReturn(java.util.Optional.of(existingProject)); when(projectRepository.save(any(Project.class))).thenReturn(updatedProject); // When ProjectResponse response = projectService.updateProject(projectId, request); // Then assertNotNull(response); assertEquals("Updated Name", response.getName()); assertEquals("Updated Description", response.getDescription()); verify(projectRepository, times(1)).findById(projectId); verify(projectRepository, times(1)).save(any(Project.class)); } @Test void updateProject_shouldThrowExceptionWhenNotFound() { // Given Long projectId = 999L; ProjectRequest request = new ProjectRequest("Updated Name", "Updated Description"); when(projectRepository.findById(projectId)).thenReturn(java.util.Optional.empty()); // When & Then RuntimeException exception = assertThrows(RuntimeException.class, () -> { projectService.updateProject(projectId, request); }); assertTrue(exception.getMessage().contains("Project not found")); verify(projectRepository, times(1)).findById(projectId); verify(projectRepository, never()).save(any(Project.class)); } @Test void getAllProjects_shouldIncludeReportCount() { // Given Project project1 = new Project(1L, "Project One", "Description 1", LocalDateTime.now()); Project project2 = new Project(2L, "Project Two", "Description 2", LocalDateTime.now()); when(projectRepository.findAll()).thenReturn(Arrays.asList(project1, project2)); when(reportRepository.countByProjectId(1L)).thenReturn(5L); when(reportRepository.countByProjectId(2L)).thenReturn(3L); // When List result = projectService.getAllProjects(); // Then assertNotNull(result); assertEquals(2, result.size()); assertEquals(5L, result.get(0).getReportCount()); assertEquals(3L, result.get(1).getReportCount()); verify(reportRepository, times(1)).countByProjectId(1L); verify(reportRepository, times(1)).countByProjectId(2L); } @Test void getProjectById_shouldIncludeReportCount() { // Given Long projectId = 1L; Project project = new Project(1L, "Test Project", "Description", LocalDateTime.now()); when(projectRepository.findById(projectId)).thenReturn(java.util.Optional.of(project)); when(reportRepository.countByProjectId(projectId)).thenReturn(10L); // When ProjectResponse response = projectService.getProjectById(projectId); // Then assertNotNull(response); assertEquals(10L, response.getReportCount()); verify(reportRepository, times(1)).countByProjectId(projectId); } @Test void getAllProjects_withNoReports_shouldHaveZeroReportCount() { // Given Project project = new Project(1L, "Empty Project", "No reports", LocalDateTime.now()); when(projectRepository.findAll()).thenReturn(Collections.singletonList(project)); when(reportRepository.countByProjectId(1L)).thenReturn(0L); // When List result = projectService.getAllProjects(); // Then assertNotNull(result); assertEquals(1, result.size()); assertEquals(0L, result.get(0).getReportCount()); } @Test void updateProject_withMultipartCoverImage_shouldReturnUpdatedProjectWithReportCount() { // Given Long projectId = 1L; Project existingProject = new Project(1L, "Original Name", "Description", LocalDateTime.now()); Project updatedProject = new Project(1L, "Updated Name", "Description", existingProject.getCreatedAt()); updatedProject.setCoverImage("/uploads/covers/1/test.png"); when(projectRepository.findById(projectId)).thenReturn(java.util.Optional.of(existingProject)); when(projectRepository.save(any(Project.class))).thenReturn(updatedProject); when(reportRepository.countByProjectId(projectId)).thenReturn(5L); // When ProjectResponse response = projectService.updateProject(projectId, "Updated Name", null, null); // Then assertNotNull(response); assertEquals(5L, response.getReportCount()); verify(reportRepository, times(1)).countByProjectId(projectId); } // ==================== deleteProject Tests ==================== @Test void deleteProject_shouldDeleteSuccessfully() { // Given Long projectId = 1L; when(projectRepository.existsById(projectId)).thenReturn(true); doNothing().when(projectRepository).deleteById(projectId); // When projectService.deleteProject(projectId); // Then verify(projectRepository, times(1)).existsById(projectId); verify(projectRepository, times(1)).deleteById(projectId); } @Test void deleteProject_shouldThrowExceptionWhenNotFound() { // Given Long projectId = 999L; when(projectRepository.existsById(projectId)).thenReturn(false); // When & Then RuntimeException exception = assertThrows(RuntimeException.class, () -> { projectService.deleteProject(projectId); }); assertTrue(exception.getMessage().contains("Project not found")); verify(projectRepository, times(1)).existsById(projectId); verify(projectRepository, never()).deleteById(any()); } }