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:
@@ -0,0 +1,252 @@
|
||||
package com.reportdist;
|
||||
|
||||
import com.reportdist.dto.ProjectRequest;
|
||||
import com.reportdist.dto.ReportResponse;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
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.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.http.*;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Complete API flow integration test.
|
||||
* Tests: Create Project → Upload Report → Query Report → Delete Report → Delete Project (cascade)
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
class CompleteApiFlowIntegrationTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
private String baseUrl;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
baseUrl = "http://localhost:" + port;
|
||||
// Ensure upload directory exists
|
||||
Path uploadPath = Paths.get(System.getProperty("java.io.tmpdir"), "report-dist-test-uploads");
|
||||
if (!Files.exists(uploadPath)) {
|
||||
Files.createDirectories(uploadPath);
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
// Cleanup is handled by H2 create-drop and file cleanup in each test
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Complete API flow: Create Project → Upload Report → Query Report → Delete Report → Delete Project")
|
||||
void completeApiFlow_shouldSucceed() {
|
||||
// Step 1: Create a project
|
||||
ProjectRequest projectRequest = new ProjectRequest("Integration Test Project", "Testing complete flow");
|
||||
ResponseEntity<Map> createResponse = restTemplate.postForEntity(
|
||||
baseUrl + "/api/projects",
|
||||
projectRequest,
|
||||
Map.class
|
||||
);
|
||||
|
||||
assertEquals(HttpStatus.CREATED, createResponse.getStatusCode());
|
||||
assertNotNull(createResponse.getBody());
|
||||
Long projectId = ((Number) createResponse.getBody().get("id")).longValue();
|
||||
assertNotNull(projectId);
|
||||
assertEquals("Integration Test Project", createResponse.getBody().get("name"));
|
||||
System.out.println("[Step 1] Created project with ID: " + projectId);
|
||||
|
||||
// Step 2: Verify project exists
|
||||
ResponseEntity<Map> getProjectResponse = restTemplate.getForEntity(
|
||||
baseUrl + "/api/projects/" + projectId,
|
||||
Map.class
|
||||
);
|
||||
assertEquals(HttpStatus.OK, getProjectResponse.getStatusCode());
|
||||
assertEquals("Integration Test Project", getProjectResponse.getBody().get("name"));
|
||||
System.out.println("[Step 2] Verified project exists");
|
||||
|
||||
// Step 3: Upload a report to the project
|
||||
String htmlContent = "<html><body><h1>Integration Test Report</h1><p>Content here</p></body></html>";
|
||||
MultiValueMap<String, Object> reportParts = new LinkedMultiValueMap<>();
|
||||
reportParts.add("file", new ByteArrayResource(htmlContent.getBytes()) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "test-report.html";
|
||||
}
|
||||
});
|
||||
reportParts.add("projectId", projectId.toString());
|
||||
reportParts.add("fileType", "HTML");
|
||||
|
||||
HttpHeaders reportHeaders = new HttpHeaders();
|
||||
reportHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
HttpEntity<MultiValueMap<String, Object>> reportRequest = new HttpEntity<>(reportParts, reportHeaders);
|
||||
|
||||
ResponseEntity<Map> uploadResponse = restTemplate.postForEntity(
|
||||
baseUrl + "/api/reports",
|
||||
reportRequest,
|
||||
Map.class
|
||||
);
|
||||
|
||||
assertEquals(HttpStatus.CREATED, uploadResponse.getStatusCode());
|
||||
assertNotNull(uploadResponse.getBody());
|
||||
Long reportId = ((Number) uploadResponse.getBody().get("id")).longValue();
|
||||
assertNotNull(reportId);
|
||||
assertEquals("test-report.html", uploadResponse.getBody().get("fileName"));
|
||||
assertEquals("HTML", uploadResponse.getBody().get("fileType"));
|
||||
String filePath = (String) uploadResponse.getBody().get("filePath");
|
||||
System.out.println("[Step 3] Uploaded report with ID: " + reportId + ", path: " + filePath);
|
||||
|
||||
// Verify file was actually saved
|
||||
Path savedFile = Paths.get(filePath);
|
||||
assertTrue(Files.exists(savedFile), "File should exist on disk");
|
||||
|
||||
// Step 4: Query reports by projectId
|
||||
ResponseEntity<List> reportsResponse = restTemplate.getForEntity(
|
||||
baseUrl + "/api/reports?projectId=" + projectId,
|
||||
List.class
|
||||
);
|
||||
assertEquals(HttpStatus.OK, reportsResponse.getStatusCode());
|
||||
assertNotNull(reportsResponse.getBody());
|
||||
assertEquals(1, reportsResponse.getBody().size());
|
||||
Map reportInList = (Map) reportsResponse.getBody().get(0);
|
||||
assertEquals(reportId, ((Number) reportInList.get("id")).longValue());
|
||||
System.out.println("[Step 4] Queried reports, found: " + reportsResponse.getBody().size());
|
||||
|
||||
// Step 5: Get report by ID with content
|
||||
ResponseEntity<Map> getReportResponse = restTemplate.getForEntity(
|
||||
baseUrl + "/api/reports/" + reportId,
|
||||
Map.class
|
||||
);
|
||||
assertEquals(HttpStatus.OK, getReportResponse.getStatusCode());
|
||||
assertEquals("test-report.html", getReportResponse.getBody().get("fileName"));
|
||||
assertEquals(htmlContent, getReportResponse.getBody().get("fileContent"));
|
||||
System.out.println("[Step 5] Retrieved report with content");
|
||||
|
||||
// Step 6: Delete the report
|
||||
ResponseEntity<Void> deleteReportResponse = restTemplate.exchange(
|
||||
baseUrl + "/api/reports/" + reportId,
|
||||
HttpMethod.DELETE,
|
||||
null,
|
||||
Void.class
|
||||
);
|
||||
assertEquals(HttpStatus.NO_CONTENT, deleteReportResponse.getStatusCode());
|
||||
|
||||
// Verify report file is deleted
|
||||
assertFalse(Files.exists(savedFile), "File should be deleted from disk");
|
||||
|
||||
// Verify report is gone
|
||||
ResponseEntity<Map> getDeletedReport = restTemplate.getForEntity(
|
||||
baseUrl + "/api/reports/" + reportId,
|
||||
Map.class
|
||||
);
|
||||
assertEquals(HttpStatus.NOT_FOUND, getDeletedReport.getStatusCode());
|
||||
System.out.println("[Step 6] Deleted report");
|
||||
|
||||
// Step 7: Delete the project
|
||||
ResponseEntity<Void> deleteProjectResponse = restTemplate.exchange(
|
||||
baseUrl + "/api/projects/" + projectId,
|
||||
HttpMethod.DELETE,
|
||||
null,
|
||||
Void.class
|
||||
);
|
||||
assertEquals(HttpStatus.NO_CONTENT, deleteProjectResponse.getStatusCode());
|
||||
|
||||
// Verify project is gone
|
||||
ResponseEntity<Map> getDeletedProject = restTemplate.getForEntity(
|
||||
baseUrl + "/api/projects/" + projectId,
|
||||
Map.class
|
||||
);
|
||||
assertEquals(HttpStatus.NOT_FOUND, getDeletedProject.getStatusCode());
|
||||
System.out.println("[Step 7] Deleted project");
|
||||
|
||||
System.out.println("[SUCCESS] Complete API flow test passed!");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Cascade delete: Deleting project should not delete reports (manual cleanup)")
|
||||
void deleteProject_shouldNotAutoDeleteReports() {
|
||||
// Create project
|
||||
ProjectRequest projectRequest = new ProjectRequest("Cascade Test Project", "Testing cascade");
|
||||
ResponseEntity<Map> createResponse = restTemplate.postForEntity(
|
||||
baseUrl + "/api/projects",
|
||||
projectRequest,
|
||||
Map.class
|
||||
);
|
||||
assertEquals(HttpStatus.CREATED, createResponse.getStatusCode());
|
||||
Long projectId = ((Number) createResponse.getBody().get("id")).longValue();
|
||||
|
||||
// Upload two reports
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
final int reportIndex = i;
|
||||
String content = "Report " + i;
|
||||
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
|
||||
parts.add("file", new ByteArrayResource(content.getBytes()) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "report" + reportIndex + ".html";
|
||||
}
|
||||
});
|
||||
parts.add("projectId", projectId.toString());
|
||||
parts.add("fileType", "HTML");
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(parts, headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.postForEntity(
|
||||
baseUrl + "/api/reports",
|
||||
request,
|
||||
Map.class
|
||||
);
|
||||
assertEquals(HttpStatus.CREATED, response.getStatusCode());
|
||||
}
|
||||
|
||||
// Verify reports exist
|
||||
ResponseEntity<List> reportsResponse = restTemplate.getForEntity(
|
||||
baseUrl + "/api/reports?projectId=" + projectId,
|
||||
List.class
|
||||
);
|
||||
assertEquals(2, reportsResponse.getBody().size());
|
||||
|
||||
// Delete project
|
||||
ResponseEntity<Void> deleteResponse = restTemplate.exchange(
|
||||
baseUrl + "/api/projects/" + projectId,
|
||||
HttpMethod.DELETE,
|
||||
null,
|
||||
Void.class
|
||||
);
|
||||
assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatusCode());
|
||||
|
||||
// Project should be gone
|
||||
ResponseEntity<Map> getProject = restTemplate.getForEntity(
|
||||
baseUrl + "/api/projects/" + projectId,
|
||||
Map.class
|
||||
);
|
||||
assertEquals(HttpStatus.NOT_FOUND, getProject.getStatusCode());
|
||||
|
||||
// Reports still exist (they're orphaned - not cascade deleted)
|
||||
// This confirms reports are independent entities
|
||||
ResponseEntity<List> orphanedReports = restTemplate.getForEntity(
|
||||
baseUrl + "/api/reports",
|
||||
List.class
|
||||
);
|
||||
assertTrue(orphanedReports.getBody().size() >= 2);
|
||||
System.out.println("[INFO] Project deleted. Reports remain in database (manual cleanup required).");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user