fix: FilePreview fileType case + Tailwind v4 gradient transparent bug

- FilePreview.vue: add normalizedFileType computed to handle backend
  returning uppercase HTML/MD/PPTX (fixes preview/download buttons)
- FilePreview.vue: bg-gradient-to-r from-orange-500 -> bg-orange-500
  (Tailwind v4 gradient + CSS variable = transparent)
- ReportCard.vue: bg-gradient-to-r -> bg-orange-600 for selected state
- Add .opencode/, node_modules/, dist/ to .gitignore
- Initial git setup for publish project
This commit is contained in:
2026-05-24 20:09:42 +08:00
commit b9137204a0
78 changed files with 12950 additions and 0 deletions
@@ -0,0 +1,276 @@
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<ProjectResponse> 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<ProjectResponse> 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<ProjectResponse> 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<ProjectResponse> 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<ProjectResponse> 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());
}
}