This commit is contained in:
@@ -1,328 +0,0 @@
|
||||
package ru.soune.nocopy.controller.demo;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import ru.soune.nocopy.dto.demo.*;
|
||||
import ru.soune.nocopy.service.demo.DemoSessionService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("DemoController Tests")
|
||||
class DemoControllerTest {
|
||||
|
||||
@Mock
|
||||
private DemoSessionService demoSessionService;
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest mockRequest;
|
||||
|
||||
@InjectMocks
|
||||
private DemoController demoController;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(mockRequest.getRemoteAddr()).thenReturn("192.168.1.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should upload file successfully")
|
||||
void testUploadFileSuccess() throws IOException {
|
||||
// Arrange
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "test.jpg", "image/jpeg", "test content".getBytes()
|
||||
);
|
||||
|
||||
DemoUploadResponse mockResponse = DemoUploadResponse.builder()
|
||||
.sessionId("session-123")
|
||||
.fileName("test.jpg")
|
||||
.fileType("image")
|
||||
.fileSize(12L)
|
||||
.status("COMPLETED")
|
||||
.message("File uploaded successfully")
|
||||
.uploadedBytes(12L)
|
||||
.progressPercentage(100)
|
||||
.build();
|
||||
|
||||
when(demoSessionService.uploadFile(any(), anyString())).thenReturn(mockResponse);
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.uploadFile(file);
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.getBody() instanceof DemoUploadResponse);
|
||||
DemoUploadResponse body = (DemoUploadResponse) response.getBody();
|
||||
assertEquals("session-123", body.getSessionId());
|
||||
assertEquals("test.jpg", body.getFileName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should reject empty file")
|
||||
void testUploadEmptyFile() {
|
||||
// Arrange
|
||||
MockMultipartFile emptyFile = new MockMultipartFile(
|
||||
"file", "test.jpg", "image/jpeg", new byte[0]
|
||||
);
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.uploadFile(emptyFile);
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
|
||||
assertTrue(response.getBody() instanceof DemoUploadResponse);
|
||||
DemoUploadResponse body = (DemoUploadResponse) response.getBody();
|
||||
assertEquals("FAILED", body.getStatus());
|
||||
assertTrue(body.getMessage().contains("empty"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle file size validation error")
|
||||
void testUploadFileSizeError() throws IOException {
|
||||
// Arrange
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "test.jpg", "image/jpeg", "test content".getBytes()
|
||||
);
|
||||
|
||||
when(demoSessionService.uploadFile(any(), anyString()))
|
||||
.thenThrow(new RuntimeException("File too large"));
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.uploadFile(file);
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
|
||||
assertTrue(response.getBody() instanceof DemoUploadResponse);
|
||||
DemoUploadResponse body = (DemoUploadResponse) response.getBody();
|
||||
assertEquals("FAILED", body.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle upload IO error")
|
||||
void testUploadIOError() throws IOException {
|
||||
// Arrange
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "test.jpg", "image/jpeg", "test content".getBytes()
|
||||
);
|
||||
|
||||
when(demoSessionService.uploadFile(any(), anyString()))
|
||||
.thenThrow(new RuntimeException("Upload failed"));
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.uploadFile(file);
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should get file info successfully")
|
||||
void testGetFileInfoSuccess() {
|
||||
// Arrange
|
||||
DemoSessionInfo mockInfo = DemoSessionInfo.builder()
|
||||
.sessionId("session-123")
|
||||
.fileName("test.jpg")
|
||||
.fileType("image")
|
||||
.fileSize(1024L)
|
||||
.status("COMPLETED")
|
||||
.createdAt(LocalDateTime.now())
|
||||
.expiresAt(LocalDateTime.now().plusHours(24))
|
||||
.searchResultsCount(5)
|
||||
.mimeType("image/jpeg")
|
||||
.build();
|
||||
|
||||
when(demoSessionService.getSessionInfo("session-123", "192.168.1.1"))
|
||||
.thenReturn(mockInfo);
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.getFileInfo("session-123");
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.getBody() instanceof DemoSessionInfo);
|
||||
DemoSessionInfo body = (DemoSessionInfo) response.getBody();
|
||||
assertEquals("test.jpg", body.getFileName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return 404 for non-existent session")
|
||||
void testGetFileInfoNotFound() {
|
||||
// Arrange
|
||||
when(demoSessionService.getSessionInfo("non-existent", "192.168.1.1"))
|
||||
.thenThrow(new RuntimeException("Session not found"));
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.getFileInfo("non-existent");
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should start search successfully")
|
||||
void testStartSearchSuccess() {
|
||||
// Arrange
|
||||
DemoSearchResponse mockResponse = DemoSearchResponse.builder()
|
||||
.sessionId("session-123")
|
||||
.status("COMPLETED")
|
||||
.resultsCount(3)
|
||||
.matchPercentage(92.5)
|
||||
.processingTimeMs(5000L)
|
||||
.message("Search completed successfully")
|
||||
.build();
|
||||
|
||||
when(demoSessionService.startSearch("session-123", "192.168.1.1"))
|
||||
.thenReturn(mockResponse);
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.startSearch("session-123");
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.getBody() instanceof DemoSearchResponse);
|
||||
DemoSearchResponse body = (DemoSearchResponse) response.getBody();
|
||||
assertEquals("COMPLETED", body.getStatus());
|
||||
assertEquals(3, body.getResultsCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle search error")
|
||||
void testStartSearchError() {
|
||||
// Arrange
|
||||
when(demoSessionService.startSearch("session-123", "192.168.1.1"))
|
||||
.thenThrow(new RuntimeException("File not ready"));
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.startSearch("session-123");
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
|
||||
assertTrue(response.getBody() instanceof DemoSearchResponse);
|
||||
DemoSearchResponse body = (DemoSearchResponse) response.getBody();
|
||||
assertEquals("FAILED", body.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should cleanup session successfully")
|
||||
void testCleanupSuccess() {
|
||||
// Arrange
|
||||
doNothing().when(demoSessionService).deleteSession("session-123", "192.168.1.1");
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.cleanup("session-123");
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.getBody() instanceof DemoUploadResponse);
|
||||
DemoUploadResponse body = (DemoUploadResponse) response.getBody();
|
||||
assertEquals("CLEANED", body.getStatus());
|
||||
verify(demoSessionService).deleteSession("session-123", "192.168.1.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle cleanup error")
|
||||
void testCleanupError() {
|
||||
// Arrange
|
||||
doThrow(new RuntimeException("Session not found"))
|
||||
.when(demoSessionService).deleteSession("non-existent", "192.168.1.1");
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.cleanup("non-existent");
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
|
||||
assertTrue(response.getBody() instanceof DemoUploadResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return health status")
|
||||
void testHealthCheck() {
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.health();
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.getBody() instanceof DemoUploadResponse);
|
||||
DemoUploadResponse body = (DemoUploadResponse) response.getBody();
|
||||
assertEquals("HEALTHY", body.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should extract client IP correctly from X-Forwarded-For")
|
||||
void testGetClientIpFromXForwardedFor() throws IOException {
|
||||
// Arrange
|
||||
when(mockRequest.getHeader("X-Forwarded-For")).thenReturn("203.0.113.1, 198.51.100.2");
|
||||
when(mockRequest.getHeader("X-Real-IP")).thenReturn(null);
|
||||
when(mockRequest.getRemoteAddr()).thenReturn("192.168.1.1");
|
||||
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "test.jpg", "image/jpeg", "test content".getBytes()
|
||||
);
|
||||
|
||||
DemoUploadResponse mockResponse = DemoUploadResponse.builder()
|
||||
.sessionId("session-123")
|
||||
.fileName("test.jpg")
|
||||
.fileType("image")
|
||||
.fileSize(12L)
|
||||
.status("COMPLETED")
|
||||
.progressPercentage(100)
|
||||
.build();
|
||||
|
||||
when(demoSessionService.uploadFile(any(), eq("203.0.113.1")))
|
||||
.thenReturn(mockResponse);
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.uploadFile(file);
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
verify(demoSessionService).uploadFile(any(), eq("203.0.113.1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should extract client IP from X-Real-IP")
|
||||
void testGetClientIpFromXRealIp() throws IOException {
|
||||
// Arrange
|
||||
when(mockRequest.getHeader("X-Forwarded-For")).thenReturn(null);
|
||||
when(mockRequest.getHeader("X-Real-IP")).thenReturn("192.0.2.1");
|
||||
when(mockRequest.getRemoteAddr()).thenReturn("192.168.1.1");
|
||||
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "test.jpg", "image/jpeg", "test content".getBytes()
|
||||
);
|
||||
|
||||
DemoUploadResponse mockResponse = DemoUploadResponse.builder()
|
||||
.sessionId("session-123")
|
||||
.fileName("test.jpg")
|
||||
.fileType("image")
|
||||
.fileSize(12L)
|
||||
.status("COMPLETED")
|
||||
.progressPercentage(100)
|
||||
.build();
|
||||
|
||||
when(demoSessionService.uploadFile(any(), eq("192.0.2.1")))
|
||||
.thenReturn(mockResponse);
|
||||
|
||||
// Act
|
||||
ResponseEntity<?> response = demoController.uploadFile(file);
|
||||
|
||||
// Assert
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
verify(demoSessionService).uploadFile(any(), eq("192.0.2.1"));
|
||||
}
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
package ru.soune.nocopy.service.demo;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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 ru.soune.nocopy.entity.demo.DemoSession;
|
||||
import ru.soune.nocopy.entity.demo.DemoSessionStatus;
|
||||
import ru.soune.nocopy.repository.DemoSessionRepository;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("DemoCleanupService Tests")
|
||||
class DemoCleanupServiceTest {
|
||||
|
||||
@Mock
|
||||
private DemoSessionRepository demoSessionRepository;
|
||||
|
||||
@Mock
|
||||
private CloudStorageService cloudStorageService;
|
||||
|
||||
@InjectMocks
|
||||
private DemoCleanupService demoCleanupService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// Настройка тестовых данных
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should cleanup expired sessions")
|
||||
void testCleanupExpiredSessions() {
|
||||
// Arrange
|
||||
List<DemoSession> expiredSessions = createExpiredSessions(3);
|
||||
when(demoSessionRepository.findExpiredSessions(any())).thenReturn(expiredSessions);
|
||||
when(demoSessionRepository.save(any(DemoSession.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// Act
|
||||
demoCleanupService.cleanupExpiredSessions();
|
||||
|
||||
// Assert
|
||||
verify(cloudStorageService, times(3)).deleteFileFromStorage(anyString());
|
||||
verify(demoSessionRepository, times(3)).save(any(DemoSession.class));
|
||||
|
||||
// Проверяем, что сессии помечены как CLEANED
|
||||
expiredSessions.forEach(session ->
|
||||
assertEquals(DemoSessionStatus.CLEANED, session.getStatus())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle cleanup with no expired sessions")
|
||||
void testCleanupNoExpiredSessions() {
|
||||
// Arrange
|
||||
when(demoSessionRepository.findExpiredSessions(any())).thenReturn(new ArrayList<>());
|
||||
|
||||
// Act & Assert - должно завершиться без ошибок
|
||||
assertDoesNotThrow(() -> demoCleanupService.cleanupExpiredSessions());
|
||||
|
||||
// Ничего не должно быть удалено
|
||||
verify(cloudStorageService, never()).deleteFileFromStorage(anyString());
|
||||
verify(demoSessionRepository, never()).save(any(DemoSession.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle S3 deletion failures gracefully")
|
||||
void testCleanupHandlesS3Failures() {
|
||||
// Arrange
|
||||
List<DemoSession> expiredSessions = createExpiredSessions(2);
|
||||
when(demoSessionRepository.findExpiredSessions(any())).thenReturn(expiredSessions);
|
||||
when(demoSessionRepository.save(any(DemoSession.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
doThrow(new RuntimeException("S3 error")).when(cloudStorageService).deleteFileFromStorage(anyString());
|
||||
|
||||
// Act & Assert - должно не бросать исключение
|
||||
assertDoesNotThrow(() -> demoCleanupService.cleanupExpiredSessions());
|
||||
|
||||
// Сессии должны быть помечены как CLEANED несмотря на ошибку S3
|
||||
verify(demoSessionRepository, times(2)).save(any(DemoSession.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should cleanup failed sessions")
|
||||
void testCleanupFailedSessions() {
|
||||
// Arrange
|
||||
List<DemoSession> failedSessions = createFailedSessions(2);
|
||||
when(demoSessionRepository.findByStatus(DemoSessionStatus.FAILED.name())).thenReturn(failedSessions);
|
||||
when(demoSessionRepository.save(any(DemoSession.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// Act
|
||||
demoCleanupService.cleanupFailedSessions();
|
||||
|
||||
// Assert
|
||||
verify(cloudStorageService, times(2)).deleteFileFromStorage(anyString());
|
||||
verify(demoSessionRepository, times(2)).save(any(DemoSession.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should skip failed sessions newer than 1 hour")
|
||||
void testCleanupSkipsRecentFailedSessions() {
|
||||
// Arrange
|
||||
DemoSession recentFailed = createFailedSessionAt(LocalDateTime.now().minusMinutes(30));
|
||||
DemoSession oldFailed = createFailedSessionAt(LocalDateTime.now().minusHours(2));
|
||||
List<DemoSession> failedSessions = List.of(recentFailed, oldFailed);
|
||||
|
||||
when(demoSessionRepository.findByStatus(DemoSessionStatus.FAILED.name())).thenReturn(failedSessions);
|
||||
when(demoSessionRepository.save(any(DemoSession.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// Act
|
||||
demoCleanupService.cleanupFailedSessions();
|
||||
|
||||
// Assert - только старая сессия должна быть очищена
|
||||
verify(cloudStorageService, times(1)).deleteFileFromStorage(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should cleanup session manually")
|
||||
void testCleanupSessionManually() {
|
||||
// Arrange
|
||||
DemoSession session = createTestSession();
|
||||
when(demoSessionRepository.findBySessionId("session-123")).thenReturn(Optional.of(session));
|
||||
when(demoSessionRepository.save(any(DemoSession.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// Act
|
||||
demoCleanupService.cleanupSessionManually("session-123");
|
||||
|
||||
// Assert
|
||||
verify(cloudStorageService).deleteFileFromStorage(session.getS3Path());
|
||||
verify(demoSessionRepository).save(any(DemoSession.class));
|
||||
assertEquals(DemoSessionStatus.CLEANED, session.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should throw exception for non-existent session during manual cleanup")
|
||||
void testCleanupSessionManuallyNotFound() {
|
||||
// Arrange
|
||||
when(demoSessionRepository.findBySessionId("non-existent")).thenReturn(Optional.empty());
|
||||
|
||||
// Act & Assert
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () ->
|
||||
demoCleanupService.cleanupSessionManually("non-existent")
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("not found"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should calculate active demo sessions count correctly")
|
||||
void testGetActiveDemoSessionsCount() {
|
||||
// Arrange
|
||||
List<DemoSession> activeSessions = new ArrayList<>();
|
||||
activeSessions.add(createTestSessionWithStatus(DemoSessionStatus.CREATED));
|
||||
activeSessions.add(createTestSessionWithStatus(DemoSessionStatus.PROCESSING));
|
||||
activeSessions.add(createTestSessionWithStatus(DemoSessionStatus.COMPLETED));
|
||||
activeSessions.add(createTestSessionWithStatus(DemoSessionStatus.CLEANED)); // Это не должно считаться
|
||||
|
||||
when(demoSessionRepository.findExpiredSessions(any())).thenReturn(activeSessions);
|
||||
|
||||
// Act
|
||||
long count = demoCleanupService.getActiveDemoSessionsCount();
|
||||
|
||||
// Assert
|
||||
assertEquals(3, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should calculate total demo data size correctly")
|
||||
void testGetActiveDemoDataSize() {
|
||||
// Arrange
|
||||
List<DemoSession> sessions = new ArrayList<>();
|
||||
sessions.add(createSessionWithSize(1000L, DemoSessionStatus.COMPLETED));
|
||||
sessions.add(createSessionWithSize(2000L, DemoSessionStatus.COMPLETED));
|
||||
sessions.add(createSessionWithSize(3000L, DemoSessionStatus.CLEANED));
|
||||
|
||||
when(demoSessionRepository.findExpiredSessions(any())).thenReturn(sessions);
|
||||
|
||||
// Act
|
||||
long totalSize = demoCleanupService.getActiveDemoDataSize();
|
||||
|
||||
// Assert
|
||||
assertEquals(3000L, totalSize); // Только активные сессии
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return zero when no active sessions")
|
||||
void testGetActiveDemoDataSizeNoActiveSessions() {
|
||||
// Arrange
|
||||
when(demoSessionRepository.findExpiredSessions(any())).thenReturn(new ArrayList<>());
|
||||
|
||||
// Act
|
||||
long totalSize = demoCleanupService.getActiveDemoDataSize();
|
||||
|
||||
// Assert
|
||||
assertEquals(0L, totalSize);
|
||||
}
|
||||
|
||||
// Помощные методы
|
||||
|
||||
private List<DemoSession> createExpiredSessions(int count) {
|
||||
List<DemoSession> sessions = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
sessions.add(DemoSession.builder()
|
||||
.sessionId("expired-session-" + i)
|
||||
.ipAddress("192.168.1." + i)
|
||||
.fileName("file-" + i + ".jpg")
|
||||
.fileType("image")
|
||||
.mimeType("image/jpeg")
|
||||
.fileSize(1024L * (i + 1))
|
||||
.s3Path("demo/file-" + i + ".jpg")
|
||||
.status(DemoSessionStatus.COMPLETED)
|
||||
.createdAt(LocalDateTime.now().minusHours(25))
|
||||
.updatedAt(LocalDateTime.now().minusHours(25))
|
||||
.expiresAt(LocalDateTime.now().minusHours(1))
|
||||
.build());
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
private List<DemoSession> createFailedSessions(int count) {
|
||||
List<DemoSession> sessions = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
sessions.add(DemoSession.builder()
|
||||
.sessionId("failed-session-" + i)
|
||||
.ipAddress("192.168.1." + i)
|
||||
.fileName("file-" + i + ".jpg")
|
||||
.fileType("image")
|
||||
.mimeType("image/jpeg")
|
||||
.fileSize(1024L)
|
||||
.s3Path("demo/file-" + i + ".jpg")
|
||||
.status(DemoSessionStatus.FAILED)
|
||||
.createdAt(LocalDateTime.now().minusHours(2))
|
||||
.updatedAt(LocalDateTime.now().minusHours(2))
|
||||
.expiresAt(LocalDateTime.now().plusHours(22))
|
||||
.build());
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
private DemoSession createFailedSessionAt(LocalDateTime createdAt) {
|
||||
return DemoSession.builder()
|
||||
.sessionId("failed-" + System.nanoTime())
|
||||
.ipAddress("192.168.1.1")
|
||||
.fileName("file.jpg")
|
||||
.fileType("image")
|
||||
.mimeType("image/jpeg")
|
||||
.fileSize(1024L)
|
||||
.s3Path("demo/file.jpg")
|
||||
.status(DemoSessionStatus.FAILED)
|
||||
.createdAt(createdAt)
|
||||
.updatedAt(createdAt)
|
||||
.expiresAt(createdAt.plusHours(24))
|
||||
.build();
|
||||
}
|
||||
|
||||
private DemoSession createTestSession() {
|
||||
return DemoSession.builder()
|
||||
.sessionId("session-123")
|
||||
.ipAddress("192.168.1.1")
|
||||
.fileName("test-image.jpg")
|
||||
.fileType("image")
|
||||
.mimeType("image/jpeg")
|
||||
.fileSize(1024L)
|
||||
.s3Path("demo/test-file.jpg")
|
||||
.status(DemoSessionStatus.COMPLETED)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.updatedAt(LocalDateTime.now())
|
||||
.expiresAt(LocalDateTime.now().plusHours(24))
|
||||
.build();
|
||||
}
|
||||
|
||||
private DemoSession createTestSessionWithStatus(DemoSessionStatus status) {
|
||||
return DemoSession.builder()
|
||||
.sessionId("session-" + System.nanoTime())
|
||||
.ipAddress("192.168.1.1")
|
||||
.fileName("file.jpg")
|
||||
.fileType("image")
|
||||
.mimeType("image/jpeg")
|
||||
.fileSize(1024L)
|
||||
.s3Path("demo/file.jpg")
|
||||
.status(status)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.updatedAt(LocalDateTime.now())
|
||||
.expiresAt(LocalDateTime.now().plusHours(24))
|
||||
.build();
|
||||
}
|
||||
|
||||
private DemoSession createSessionWithSize(Long fileSize, DemoSessionStatus status) {
|
||||
return DemoSession.builder()
|
||||
.sessionId("session-" + System.nanoTime())
|
||||
.ipAddress("192.168.1.1")
|
||||
.fileName("file.jpg")
|
||||
.fileType("image")
|
||||
.mimeType("image/jpeg")
|
||||
.fileSize(fileSize)
|
||||
.s3Path("demo/file.jpg")
|
||||
.status(status)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.updatedAt(LocalDateTime.now())
|
||||
.expiresAt(LocalDateTime.now().plusHours(24))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
package ru.soune.nocopy.service.demo;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import ru.soune.nocopy.dto.demo.DemoSearchResponse;
|
||||
import ru.soune.nocopy.dto.demo.DemoSessionInfo;
|
||||
import ru.soune.nocopy.dto.demo.DemoUploadResponse;
|
||||
import ru.soune.nocopy.entity.demo.DemoSession;
|
||||
import ru.soune.nocopy.entity.demo.DemoSessionStatus;
|
||||
import ru.soune.nocopy.repository.DemoSessionRepository;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("DemoSessionService Tests")
|
||||
class DemoSessionServiceTest {
|
||||
|
||||
@Mock
|
||||
private DemoSessionRepository demoSessionRepository;
|
||||
|
||||
@Mock
|
||||
private CloudStorageService cloudStorageService;
|
||||
|
||||
@InjectMocks
|
||||
private DemoSessionService demoSessionService;
|
||||
|
||||
private String testIpAddress;
|
||||
private MockMultipartFile testFile;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testIpAddress = "192.168.1.1";
|
||||
testFile = new MockMultipartFile(
|
||||
"file",
|
||||
"test-image.jpg",
|
||||
"image/jpeg",
|
||||
"test content".getBytes()
|
||||
);
|
||||
|
||||
// Устанавливаем значения через reflection для тестирования
|
||||
ReflectionTestUtils.setField(demoSessionService, "maxFileSize", 52428800L); // 50 MB
|
||||
ReflectionTestUtils.setField(demoSessionService, "maxFilesPerDay", 10);
|
||||
ReflectionTestUtils.setField(demoSessionService, "demoS3Path", "demo/");
|
||||
ReflectionTestUtils.setField(demoSessionService, "sessionTtlHours", 24);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should successfully upload file")
|
||||
void testUploadFileSuccess() throws IOException {
|
||||
// Arrange
|
||||
when(demoSessionRepository.countSessionsByIpAndTime(anyString(), any())).thenReturn(0);
|
||||
when(demoSessionRepository.save(any(DemoSession.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// Act
|
||||
DemoUploadResponse response = demoSessionService.uploadFile(testFile, testIpAddress);
|
||||
|
||||
// Assert
|
||||
assertNotNull(response);
|
||||
assertNotNull(response.getSessionId());
|
||||
assertEquals("test-image.jpg", response.getFileName());
|
||||
assertEquals("image", response.getFileType());
|
||||
assertEquals(100, response.getProgressPercentage());
|
||||
verify(cloudStorageService).saveFilesToStorage(any(), anyString());
|
||||
verify(demoSessionRepository).save(any(DemoSession.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should reject file that exceeds max size")
|
||||
void testUploadFileExceedsMaxSize() {
|
||||
// Arrange
|
||||
MockMultipartFile largeFile = new MockMultipartFile(
|
||||
"file",
|
||||
"large-file.jpg",
|
||||
"image/jpeg",
|
||||
new byte[52428801] // 50 MB + 1 byte
|
||||
);
|
||||
ReflectionTestUtils.setField(demoSessionService, "maxFileSize", 52428800L);
|
||||
|
||||
// Act & Assert
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () ->
|
||||
demoSessionService.uploadFile(largeFile, testIpAddress)
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("File too large"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should reject file with unsupported type")
|
||||
void testUploadFileUnsupportedType() {
|
||||
// Arrange
|
||||
MockMultipartFile unsupportedFile = new MockMultipartFile(
|
||||
"file",
|
||||
"test-file.exe",
|
||||
"application/x-msdownload",
|
||||
"test content".getBytes()
|
||||
);
|
||||
|
||||
// Act & Assert
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () ->
|
||||
demoSessionService.uploadFile(unsupportedFile, testIpAddress)
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("File type not supported"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should enforce daily file limit")
|
||||
void testUploadFileExceedsDailyLimit() {
|
||||
// Arrange
|
||||
ReflectionTestUtils.setField(demoSessionService, "maxFilesPerDay", 2);
|
||||
when(demoSessionRepository.countSessionsByIpAndTime(anyString(), any())).thenReturn(2);
|
||||
|
||||
// Act & Assert
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () ->
|
||||
demoSessionService.uploadFile(testFile, testIpAddress)
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("Daily limit reached"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should retrieve session info successfully")
|
||||
void testGetSessionInfoSuccess() {
|
||||
// Arrange
|
||||
DemoSession session = createTestSession(testIpAddress);
|
||||
when(demoSessionRepository.findBySessionId("session-123")).thenReturn(Optional.of(session));
|
||||
|
||||
// Act
|
||||
DemoSessionInfo info = demoSessionService.getSessionInfo("session-123", testIpAddress);
|
||||
|
||||
// Assert
|
||||
assertNotNull(info);
|
||||
assertEquals("session-123", info.getSessionId());
|
||||
assertEquals("test-image.jpg", info.getFileName());
|
||||
assertEquals(testIpAddress, session.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should reject session info access from different IP")
|
||||
void testGetSessionInfoUnauthorizedIp() {
|
||||
// Arrange
|
||||
DemoSession session = createTestSession(testIpAddress);
|
||||
when(demoSessionRepository.findBySessionId("session-123")).thenReturn(Optional.of(session));
|
||||
|
||||
// Act & Assert
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () ->
|
||||
demoSessionService.getSessionInfo("session-123", "192.168.1.2")
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("Unauthorized"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should throw exception for non-existent session")
|
||||
void testGetSessionInfoNotFound() {
|
||||
// Arrange
|
||||
when(demoSessionRepository.findBySessionId("non-existent")).thenReturn(Optional.empty());
|
||||
|
||||
// Act & Assert
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () ->
|
||||
demoSessionService.getSessionInfo("non-existent", testIpAddress)
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("not found"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should start search successfully")
|
||||
void testStartSearchSuccess() {
|
||||
// Arrange
|
||||
DemoSession session = createTestSession(testIpAddress);
|
||||
session.setStatus(DemoSessionStatus.COMPLETED);
|
||||
when(demoSessionRepository.findBySessionId("session-123")).thenReturn(Optional.of(session));
|
||||
when(demoSessionRepository.save(any(DemoSession.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// Act
|
||||
DemoSearchResponse response = demoSessionService.startSearch("session-123", testIpAddress);
|
||||
|
||||
// Assert
|
||||
assertNotNull(response);
|
||||
assertEquals("COMPLETED", response.getStatus());
|
||||
assertTrue(response.getResultsCount() >= 0);
|
||||
assertTrue(response.getMatchPercentage() >= 0);
|
||||
assertNotNull(response.getSimilarFiles());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should reject search if file not ready")
|
||||
void testStartSearchFileNotReady() {
|
||||
// Arrange
|
||||
DemoSession session = createTestSession(testIpAddress);
|
||||
session.setStatus(DemoSessionStatus.CREATED);
|
||||
when(demoSessionRepository.findBySessionId("session-123")).thenReturn(Optional.of(session));
|
||||
|
||||
// Act & Assert
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () ->
|
||||
demoSessionService.startSearch("session-123", testIpAddress)
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("not ready"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should delete session and file from S3")
|
||||
void testDeleteSessionSuccess() {
|
||||
// Arrange
|
||||
DemoSession session = createTestSession(testIpAddress);
|
||||
when(demoSessionRepository.findBySessionId("session-123")).thenReturn(Optional.of(session));
|
||||
when(demoSessionRepository.save(any(DemoSession.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// Act
|
||||
demoSessionService.deleteSession("session-123", testIpAddress);
|
||||
|
||||
// Assert
|
||||
verify(cloudStorageService).deleteFileFromStorage(session.getS3Path());
|
||||
verify(demoSessionRepository).save(any(DemoSession.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should reject deletion from different IP")
|
||||
void testDeleteSessionUnauthorizedIp() {
|
||||
// Arrange
|
||||
DemoSession session = createTestSession(testIpAddress);
|
||||
when(demoSessionRepository.findBySessionId("session-123")).thenReturn(Optional.of(session));
|
||||
|
||||
// Act & Assert
|
||||
RuntimeException exception = assertThrows(RuntimeException.class, () ->
|
||||
demoSessionService.deleteSession("session-123", "192.168.1.2")
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("Unauthorized"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle S3 deletion failure gracefully")
|
||||
void testDeleteSessionS3FailureHandling() {
|
||||
// Arrange
|
||||
DemoSession session = createTestSession(testIpAddress);
|
||||
when(demoSessionRepository.findBySessionId("session-123")).thenReturn(Optional.of(session));
|
||||
when(demoSessionRepository.save(any(DemoSession.class))).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
doThrow(new RuntimeException("S3 error")).when(cloudStorageService).deleteFileFromStorage(anyString());
|
||||
|
||||
// Act & Assert - должно не бросать исключение
|
||||
assertDoesNotThrow(() -> demoSessionService.deleteSession("session-123", testIpAddress));
|
||||
|
||||
// Сессия должна быть помечена как очищенная несмотря на ошибку S3
|
||||
verify(demoSessionRepository).save(any(DemoSession.class));
|
||||
}
|
||||
|
||||
// Помощные методы
|
||||
|
||||
private DemoSession createTestSession(String ipAddress) {
|
||||
return DemoSession.builder()
|
||||
.sessionId("session-123")
|
||||
.ipAddress(ipAddress)
|
||||
.fileName("test-image.jpg")
|
||||
.fileType("image")
|
||||
.mimeType("image/jpeg")
|
||||
.fileSize(1024L)
|
||||
.s3Path("demo/test-file.jpg")
|
||||
.status(DemoSessionStatus.COMPLETED)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.updatedAt(LocalDateTime.now())
|
||||
.expiresAt(LocalDateTime.now().plusHours(24))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user