From d6c5f62a43af3704a99b6726cd9193f3040eca47 Mon Sep 17 00:00:00 2001 From: mscarsel Date: Tue, 14 Jan 2025 12:14:18 +0100 Subject: [PATCH] [P4ADEV-1829] Moved file save logics to FileStorerService --- helm/values.yaml | 3 + .../fileshare/config/FoldersPathsConfig.java | 29 ++++ .../pu/fileshare/service/FileService.java | 63 +------ .../fileshare/service/FileStorerService.java | 78 +++++++++ .../IngestionFlowFileServiceImpl.java | 16 +- src/main/resources/application.yml | 9 +- .../config/FoldersPathsConfigTest.java | 47 ++++++ .../pu/fileshare/service/FileServiceTest.java | 129 +------------- .../service/FileStorerServiceTest.java | 159 ++++++++++++++++++ .../IngestionFlowFileServiceImplTest.java | 14 +- 10 files changed, 348 insertions(+), 199 deletions(-) create mode 100644 src/main/java/it/gov/pagopa/pu/fileshare/config/FoldersPathsConfig.java create mode 100644 src/main/java/it/gov/pagopa/pu/fileshare/service/FileStorerService.java create mode 100644 src/test/java/it/gov/pagopa/pu/fileshare/config/FoldersPathsConfigTest.java create mode 100644 src/test/java/it/gov/pagopa/pu/fileshare/service/FileStorerServiceTest.java diff --git a/helm/values.yaml b/helm/values.yaml index f08ddc1..1d27d0d 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -76,9 +76,12 @@ microservice-chart: SHARED_FOLDER_ROOT: "/shared" + ORGANIZATION_BASE_URL: "http://p4pa-organization-microservice-chart:8080" + AUTH_SERVER_BASE_URL: "http://p4pa-auth-microservice-chart:8080/payhub" envSecret: APPLICATIONINSIGHTS_CONNECTION_STRING: appinsights-connection-string + FILE_ENCRYPT_PASSWORD: file-encrypt-password # nodeSelector: {} diff --git a/src/main/java/it/gov/pagopa/pu/fileshare/config/FoldersPathsConfig.java b/src/main/java/it/gov/pagopa/pu/fileshare/config/FoldersPathsConfig.java new file mode 100644 index 0000000..97dba72 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/fileshare/config/FoldersPathsConfig.java @@ -0,0 +1,29 @@ +package it.gov.pagopa.pu.fileshare.config; + +import it.gov.pagopa.pu.fileshare.dto.generated.IngestionFlowFileType; +import java.util.Map; +import java.util.Optional; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Slf4j +@Getter +@Setter +@Configuration +@ConfigurationProperties(prefix = "folders") +public class FoldersPathsConfig { + private String shared; + private Map ingestionFlowFileTypePaths; + + public String getIngestionFlowFilePath(IngestionFlowFileType ingestionFlowFileType) { + return Optional.ofNullable( + ingestionFlowFileTypePaths.get(ingestionFlowFileType)) + .orElseThrow(()-> { + log.debug("No path configured for ingestionFlowFileType {}",ingestionFlowFileType); + return new UnsupportedOperationException(); + }); + } +} diff --git a/src/main/java/it/gov/pagopa/pu/fileshare/service/FileService.java b/src/main/java/it/gov/pagopa/pu/fileshare/service/FileService.java index e11544a..8dee37b 100644 --- a/src/main/java/it/gov/pagopa/pu/fileshare/service/FileService.java +++ b/src/main/java/it/gov/pagopa/pu/fileshare/service/FileService.java @@ -1,32 +1,15 @@ package it.gov.pagopa.pu.fileshare.service; -import it.gov.pagopa.pu.fileshare.exception.custom.FileUploadException; import it.gov.pagopa.pu.fileshare.exception.custom.InvalidFileException; -import it.gov.pagopa.pu.fileshare.util.AESUtils; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @Slf4j @Service public class FileService { - private final String sharedFolderRootPath; - private final String fileEncryptPassword; - - public FileService(@Value("${folders.shared}") String sharedFolderRootPath, - @Value(("${app.fileEncryptPassword}")) String fileEncryptPassword) { - this.sharedFolderRootPath = sharedFolderRootPath; - this.fileEncryptPassword = fileEncryptPassword; - } public void validateFile(MultipartFile ingestionFlowFile, String validFileExt) { if( ingestionFlowFile == null){ @@ -38,7 +21,7 @@ public void validateFile(MultipartFile ingestionFlowFile, String validFileExt) { validateFilename(filename); } - private static void validateFilename(String filename) { + public static void validateFilename(String filename) { if(Stream.of("..", "\\", "/").anyMatch(filename::contains)){ log.debug("Invalid ingestion flow filename"); throw new InvalidFileException("Invalid filename"); @@ -51,48 +34,4 @@ private static void validateFileExtension(String validFileExt, String filename) throw new InvalidFileException("Invalid file extension"); } } - - private Path getFilePath(String relativePath, String filename) { - String basePath = sharedFolderRootPath+relativePath; - Path fileLocation = Paths.get(basePath,filename).normalize(); - if(!fileLocation.startsWith(basePath)){ - log.debug("Invalid file path"); - throw new InvalidFileException("Invalid file path"); - } - return fileLocation; - } - - public void saveToSharedFolder(MultipartFile file, String relativePath){ - if(file==null){ - log.debug("File is mandatory"); - throw new FileUploadException("File is mandatory"); - } - - String filename = org.springframework.util.StringUtils.cleanPath(StringUtils.defaultString(file.getOriginalFilename())); - validateFilename(filename); - Path fileLocation = getFilePath(relativePath, filename); - //create missing parent folder, if any - try { - if (!fileLocation.toAbsolutePath().getParent().toFile().exists()) - Files.createDirectories(fileLocation.toAbsolutePath().getParent()); - encryptAndSaveFile(file, fileLocation); - }catch (Exception e) { - log.debug( - "Error uploading file to folder %s%s".formatted(sharedFolderRootPath, - relativePath), e); - throw new FileUploadException( - "Error uploading file to folder %s%s [%s]".formatted( - sharedFolderRootPath, relativePath, e.getMessage())); - } - log.debug("File upload to folder %s%s completed".formatted(sharedFolderRootPath, - relativePath)); - } - - private void encryptAndSaveFile(MultipartFile file, Path fileLocation) - throws IOException { - try(InputStream is = file.getInputStream(); - InputStream cipherIs = AESUtils.encrypt(fileEncryptPassword, is)){ - Files.copy(cipherIs, fileLocation, StandardCopyOption.REPLACE_EXISTING); - } - } } diff --git a/src/main/java/it/gov/pagopa/pu/fileshare/service/FileStorerService.java b/src/main/java/it/gov/pagopa/pu/fileshare/service/FileStorerService.java new file mode 100644 index 0000000..35795b2 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/fileshare/service/FileStorerService.java @@ -0,0 +1,78 @@ +package it.gov.pagopa.pu.fileshare.service; + +import it.gov.pagopa.pu.fileshare.config.FoldersPathsConfig; +import it.gov.pagopa.pu.fileshare.exception.custom.FileUploadException; +import it.gov.pagopa.pu.fileshare.exception.custom.InvalidFileException; +import it.gov.pagopa.pu.fileshare.util.AESUtils; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +@Slf4j +@Service +public class FileStorerService { + private final FoldersPathsConfig foldersPathsConfig; + private final String fileEncryptPassword; + + public FileStorerService(FoldersPathsConfig foldersPathsConfig, + @Value(("${app.fileEncryptPassword}")) String fileEncryptPassword) { + this.foldersPathsConfig = foldersPathsConfig; + this.fileEncryptPassword = fileEncryptPassword; + } + + private Path getFilePath(String relativePath, String filename) { + String basePath = foldersPathsConfig.getShared()+relativePath; + Path fileLocation = Paths.get(basePath,filename).normalize(); + if(!fileLocation.startsWith(basePath)){ + log.debug("Invalid file path"); + throw new InvalidFileException("Invalid file path"); + } + return fileLocation; + } + + public String saveToSharedFolder(MultipartFile file, String relativePath){ + if(file==null){ + log.debug("File is mandatory"); + throw new FileUploadException("File is mandatory"); + } + + String sharedFolderRootPath = foldersPathsConfig.getShared(); + String filename = org.springframework.util.StringUtils.cleanPath( + StringUtils.defaultString(file.getOriginalFilename())); + FileService.validateFilename(filename); + Path fileLocation = getFilePath(relativePath, filename); + //create missing parent folder, if any + try { + if (!Files.exists(fileLocation.getParent())) + Files.createDirectories(fileLocation.getParent()); + encryptAndSaveFile(file, fileLocation); + }catch (Exception e) { + String errorMessage = "Error uploading file to folder %s%s".formatted( + sharedFolderRootPath, + relativePath); + log.debug( + errorMessage, e); + throw new FileUploadException( + errorMessage); + } + log.debug("File upload to folder %s%s completed".formatted(sharedFolderRootPath, + relativePath)); + return Paths.get(relativePath,filename).toString(); + } + + private void encryptAndSaveFile(MultipartFile file, Path fileLocation) + throws IOException { + try(InputStream is = file.getInputStream(); + InputStream cipherIs = AESUtils.encrypt(fileEncryptPassword, is)){ + Files.copy(cipherIs, fileLocation, StandardCopyOption.REPLACE_EXISTING); + } + } +} diff --git a/src/main/java/it/gov/pagopa/pu/fileshare/service/ingestion/IngestionFlowFileServiceImpl.java b/src/main/java/it/gov/pagopa/pu/fileshare/service/ingestion/IngestionFlowFileServiceImpl.java index 9c88898..e1df49a 100644 --- a/src/main/java/it/gov/pagopa/pu/fileshare/service/ingestion/IngestionFlowFileServiceImpl.java +++ b/src/main/java/it/gov/pagopa/pu/fileshare/service/ingestion/IngestionFlowFileServiceImpl.java @@ -1,7 +1,9 @@ package it.gov.pagopa.pu.fileshare.service.ingestion; +import it.gov.pagopa.pu.fileshare.config.FoldersPathsConfig; import it.gov.pagopa.pu.fileshare.dto.generated.IngestionFlowFileType; import it.gov.pagopa.pu.fileshare.service.FileService; +import it.gov.pagopa.pu.fileshare.service.FileStorerService; import it.gov.pagopa.pu.fileshare.service.UserAuthorizationService; import it.gov.pagopa.pu.p4paauth.dto.generated.UserInfo; import lombok.extern.slf4j.Slf4j; @@ -14,24 +16,28 @@ public class IngestionFlowFileServiceImpl implements IngestionFlowFileService { private final UserAuthorizationService userAuthorizationService; private final FileService fileService; + private final FileStorerService fileStorerService; + private final FoldersPathsConfig foldersPathsConfig; private final String validIngestionFlowFileExt; - private final String ingestionFlowFilePath; public IngestionFlowFileServiceImpl( UserAuthorizationService userAuthorizationService, FileService fileService, - @Value("${uploads.ingestion-flow-file.valid-extension}") String validIngestionFlowFileExt, - @Value("${folders.ingestion-flow-file.path}") String ingestionFlowFilePath + FileStorerService fileStorerService, + FoldersPathsConfig foldersPathsConfig, + @Value("${uploads.ingestion-flow-file.valid-extension}") String validIngestionFlowFileExt ) { this.userAuthorizationService = userAuthorizationService; this.fileService = fileService; + this.fileStorerService = fileStorerService; + this.foldersPathsConfig = foldersPathsConfig; this.validIngestionFlowFileExt = validIngestionFlowFileExt; - this.ingestionFlowFilePath = ingestionFlowFilePath; } @Override public void uploadIngestionFlowFile(Long organizationId, IngestionFlowFileType ingestionFlowFileType, MultipartFile ingestionFlowFile, UserInfo user, String accessToken) { userAuthorizationService.checkUserAuthorization(organizationId, user, accessToken); fileService.validateFile(ingestionFlowFile, validIngestionFlowFileExt); - fileService.saveToSharedFolder(ingestionFlowFile,ingestionFlowFilePath); + String savedFilePath = fileStorerService.saveToSharedFolder(ingestionFlowFile, + foldersPathsConfig.getIngestionFlowFilePath(ingestionFlowFileType)); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 68b8998..a26487e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -51,8 +51,13 @@ folders: process-target-sub-folders: archive: "\${PROCESS_TARGET_SUB_FOLDER_ARCHIVE:archive}" errors: "\${PROCESS_TARGET_SUB_FOLDER_ERRORS:errors}" - ingestion-flow-file: - path: "\${INGESTION_FLOW_FILE_PATH:/ingestion_flow_file}" + ingestion-flow-file-type-paths: + RECEIPT: "\${INGESTION_FLOW_FILE_RECEIPT_PATH:/receipt}" + PAYMENTS_REPORTING: "\${INGESTION_FLOW_FILE_PAYMENTS_REPORTING_PATH:/payments_reporting}" + OPI: "\${INGESTION_FLOW_FILE_OPI_PATH:/opi}" + TREASURY_CSV: "\${INGESTION_FLOW_FILE_TREASURY_CSV_PATH:/treasury_csv}" + TREASURY_XLS: "\${INGESTION_FLOW_FILE_TREASURY_XLS_PATH:/treasury_xls}" + TREASURY_POSTE: "\${INGESTION_FLOW_FILE_TREASURY_POSTE_PATH:/treasury_poste}" rest: default-timeout: diff --git a/src/test/java/it/gov/pagopa/pu/fileshare/config/FoldersPathsConfigTest.java b/src/test/java/it/gov/pagopa/pu/fileshare/config/FoldersPathsConfigTest.java new file mode 100644 index 0000000..596113d --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/fileshare/config/FoldersPathsConfigTest.java @@ -0,0 +1,47 @@ +package it.gov.pagopa.pu.fileshare.config; + +import it.gov.pagopa.pu.fileshare.dto.generated.IngestionFlowFileType; +import java.util.EnumMap; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class FoldersPathsConfigTest { + private FoldersPathsConfig foldersPathsConfig; + + @BeforeEach + void setUp() { + foldersPathsConfig = new FoldersPathsConfig(); + + } + + @Test + void givenPopulatedPathWhenGetIngestionFlowFilePathThenOK(){ + String expected = "/receipt"; + Map paths = new EnumMap<>( + IngestionFlowFileType.class); + paths.put(IngestionFlowFileType.RECEIPT, "/receipt"); + foldersPathsConfig.setIngestionFlowFileTypePaths(paths); + + String result = foldersPathsConfig.getIngestionFlowFilePath( + IngestionFlowFileType.RECEIPT); + + Assertions.assertEquals(expected,result); + } + + @Test + void givenNoPathWhenGetIngestionFlowFilePathThenUnsupportedOperation(){ + foldersPathsConfig.setIngestionFlowFileTypePaths(new EnumMap<>(IngestionFlowFileType.class)); + try { + foldersPathsConfig.getIngestionFlowFilePath( + IngestionFlowFileType.RECEIPT); + Assertions.fail("Expected UnsupportedOperationException"); + }catch (UnsupportedOperationException e){ + //do nothing + } + } +} diff --git a/src/test/java/it/gov/pagopa/pu/fileshare/service/FileServiceTest.java b/src/test/java/it/gov/pagopa/pu/fileshare/service/FileServiceTest.java index 9af6b00..856b99c 100644 --- a/src/test/java/it/gov/pagopa/pu/fileshare/service/FileServiceTest.java +++ b/src/test/java/it/gov/pagopa/pu/fileshare/service/FileServiceTest.java @@ -1,17 +1,10 @@ package it.gov.pagopa.pu.fileshare.service; -import it.gov.pagopa.pu.fileshare.exception.custom.FileUploadException; import it.gov.pagopa.pu.fileshare.exception.custom.InvalidFileException; -import it.gov.pagopa.pu.fileshare.util.AESUtils; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Paths; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.MockedStatic; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; @@ -20,12 +13,10 @@ class FileServiceTest { private FileService fileService; private static final String VALID_FILE_EXTENSION = ".zip"; - private static final String SHARED_FOLDER_ROOT_PATH = "/shared"; - private static final String FILE_ENCRYPT_PASSWORD = "testPassword"; @BeforeEach void setUp() { - fileService = new FileService(SHARED_FOLDER_ROOT_PATH,FILE_ENCRYPT_PASSWORD); + fileService = new FileService(); } @Test @@ -83,122 +74,4 @@ void givenInvalidFilenameWhenValidateFileThenInvalidFileException(){ //do nothing } } - - @Test - void givenInvalidFileWhenSaveToSharedFolderThenFileUploadException() { - try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( - AESUtils.class); - MockedStatic filesMockedStatic = Mockito.mockStatic( - Files.class)) { - try { - fileService.saveToSharedFolder(null, ""); - Assertions.fail("Expected FileUploadException"); - } catch (FileUploadException e) { - aesUtilsMockedStatic.verifyNoInteractions(); - filesMockedStatic.verifyNoInteractions(); - } - } - } - - @Test - void givenInvalidFilenameWhenSaveToSharedFolderThenInvalidFileException() { - MockMultipartFile file = new MockMultipartFile( - "ingestionFlowFile", - "../test.txt", - MediaType.TEXT_PLAIN_VALUE, - "this is a test file".getBytes() - ); - - try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( - AESUtils.class); - MockedStatic filesMockedStatic = Mockito.mockStatic( - Files.class)) { - try { - fileService.saveToSharedFolder(file, ""); - Assertions.fail("Expected InvalidFileException"); - } catch (InvalidFileException e) { - aesUtilsMockedStatic.verifyNoInteractions(); - filesMockedStatic.verifyNoInteractions(); - } - } - } - - @Test - void givenErrorWhenSaveToSharedFolderThenFileUploadException() { - MockMultipartFile file = new MockMultipartFile( - "ingestionFlowFile", - "test.txt", - MediaType.TEXT_PLAIN_VALUE, - "this is a test file".getBytes() - ); - - try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( - AESUtils.class); - MockedStatic filesMockedStatic = Mockito.mockStatic( - Files.class)) { - Mockito.when(AESUtils.encrypt(Mockito.eq(FILE_ENCRYPT_PASSWORD), (InputStream) Mockito.any())) - .thenThrow(new RuntimeException()); - - try { - fileService.saveToSharedFolder(file, ""); - Assertions.fail("Expected FileUploadException"); - } catch (FileUploadException e) { - aesUtilsMockedStatic.verify(() -> AESUtils.encrypt(Mockito.anyString(),(InputStream) Mockito.any())); - aesUtilsMockedStatic.verifyNoMoreInteractions(); - filesMockedStatic.verify(() -> Files.createDirectories(Mockito.any())); - filesMockedStatic.verifyNoMoreInteractions(); - } - } - } - - @Test - void givenInvalidPathWhenSaveToSharedFolderThenInvalidFileException() { - MockMultipartFile file = new MockMultipartFile( - "ingestionFlowFile", - "test.txt", - MediaType.TEXT_PLAIN_VALUE, - "this is a test file".getBytes() - ); - - try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( - AESUtils.class); - MockedStatic filesMockedStatic = Mockito.mockStatic( - Files.class)) { - - try { - fileService.saveToSharedFolder(file, "/../relative"); - Assertions.fail("Expected InvalidFileException"); - } catch (InvalidFileException e) { - aesUtilsMockedStatic.verifyNoInteractions(); - filesMockedStatic.verifyNoInteractions(); - } - } - } - - @Test - void givenValidFileWhenSaveToSharedFolderThenOK() { - String filename = "test.txt"; - MockMultipartFile file = new MockMultipartFile( - "ingestionFlowFile", - filename, - MediaType.TEXT_PLAIN_VALUE, - "this is a test file".getBytes() - ); - - try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( - AESUtils.class); - MockedStatic filesMockedStatic = Mockito.mockStatic( - Files.class) - ) { - String relativePath = "/relative"; - fileService.saveToSharedFolder(file, relativePath); - - aesUtilsMockedStatic.verify(() -> AESUtils.encrypt(Mockito.anyString(),(InputStream) Mockito.any())); - aesUtilsMockedStatic.verifyNoMoreInteractions(); - filesMockedStatic.verify(() -> Files.createDirectories(Mockito.eq( - Paths.get(SHARED_FOLDER_ROOT_PATH,relativePath)))); - filesMockedStatic.verify(() -> Files.copy((InputStream) Mockito.any(),Mockito.eq(Paths.get(SHARED_FOLDER_ROOT_PATH,relativePath,filename)), Mockito.any())); - filesMockedStatic.verifyNoMoreInteractions(); - } - } } diff --git a/src/test/java/it/gov/pagopa/pu/fileshare/service/FileStorerServiceTest.java b/src/test/java/it/gov/pagopa/pu/fileshare/service/FileStorerServiceTest.java new file mode 100644 index 0000000..a9be115 --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/fileshare/service/FileStorerServiceTest.java @@ -0,0 +1,159 @@ +package it.gov.pagopa.pu.fileshare.service; + +import it.gov.pagopa.pu.fileshare.config.FoldersPathsConfig; +import it.gov.pagopa.pu.fileshare.exception.custom.FileUploadException; +import it.gov.pagopa.pu.fileshare.exception.custom.InvalidFileException; +import it.gov.pagopa.pu.fileshare.util.AESUtils; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.junit.jupiter.api.Assertions; +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.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; + +@ExtendWith(MockitoExtension.class) +class FileStorerServiceTest { + private FileStorerService fileStorerService; + @Mock + private FoldersPathsConfig foldersPathsConfig; + private static final String FILE_ENCRYPT_PASSWORD = "testPassword"; + + @BeforeEach + void setUp() { + fileStorerService = new FileStorerService(foldersPathsConfig,FILE_ENCRYPT_PASSWORD); + } + + @Test + void givenInvalidFileWhenSaveToSharedFolderThenFileUploadException() { + try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( + AESUtils.class); + MockedStatic filesMockedStatic = Mockito.mockStatic( + Files.class)) { + try { + fileStorerService.saveToSharedFolder(null, ""); + Assertions.fail("Expected FileUploadException"); + } catch (FileUploadException e) { + aesUtilsMockedStatic.verifyNoInteractions(); + filesMockedStatic.verifyNoInteractions(); + } + } + } + + @Test + void givenInvalidFilenameWhenSaveToSharedFolderThenInvalidFileException() { + MockMultipartFile file = new MockMultipartFile( + "ingestionFlowFile", + "../test.txt", + MediaType.TEXT_PLAIN_VALUE, + "this is a test file".getBytes() + ); + + try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( + AESUtils.class); + MockedStatic filesMockedStatic = Mockito.mockStatic( + Files.class)) { + try { + fileStorerService.saveToSharedFolder(file, ""); + Assertions.fail("Expected InvalidFileException"); + } catch (InvalidFileException e) { + aesUtilsMockedStatic.verifyNoInteractions(); + filesMockedStatic.verifyNoInteractions(); + } + } + } + + @Test + void givenErrorWhenSaveToSharedFolderThenFileUploadException() { + MockMultipartFile file = new MockMultipartFile( + "ingestionFlowFile", + "test.txt", + MediaType.TEXT_PLAIN_VALUE, + "this is a test file".getBytes() + ); + + try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( + AESUtils.class); + MockedStatic filesMockedStatic = Mockito.mockStatic( + Files.class)) { + Mockito.when(AESUtils.encrypt(Mockito.eq(FILE_ENCRYPT_PASSWORD), (InputStream) Mockito.any())) + .thenThrow(new RuntimeException()); + + try { + fileStorerService.saveToSharedFolder(file, ""); + Assertions.fail("Expected FileUploadException"); + } catch (FileUploadException e) { + aesUtilsMockedStatic.verify(() -> AESUtils.encrypt(Mockito.anyString(),(InputStream) Mockito.any())); + aesUtilsMockedStatic.verifyNoMoreInteractions(); + filesMockedStatic.verify(() -> Files.exists(Mockito.any())); + filesMockedStatic.verify(() -> Files.createDirectories(Mockito.any())); + filesMockedStatic.verifyNoMoreInteractions(); + } + } + } + + @Test + void givenInvalidPathWhenSaveToSharedFolderThenInvalidFileException() { + MockMultipartFile file = new MockMultipartFile( + "ingestionFlowFile", + "test.txt", + MediaType.TEXT_PLAIN_VALUE, + "this is a test file".getBytes() + ); + + try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( + AESUtils.class); + MockedStatic filesMockedStatic = Mockito.mockStatic( + Files.class)) { + String sharedFolderPath = "/shared"; + Mockito.when(foldersPathsConfig.getShared()).thenReturn(sharedFolderPath); + + try { + fileStorerService.saveToSharedFolder(file, "/relative/../../test"); + Assertions.fail("Expected InvalidFileException"); + } catch (InvalidFileException e) { + aesUtilsMockedStatic.verifyNoInteractions(); + filesMockedStatic.verifyNoInteractions(); + } + } + } + + @Test + void givenValidFileWhenSaveToSharedFolderThenOK() { + String filename = "test.txt"; + MockMultipartFile file = new MockMultipartFile( + "ingestionFlowFile", + filename, + MediaType.TEXT_PLAIN_VALUE, + "this is a test file".getBytes() + ); + + try (MockedStatic aesUtilsMockedStatic = Mockito.mockStatic( + AESUtils.class); + MockedStatic filesMockedStatic = Mockito.mockStatic( + Files.class) + ) { + String sharedFolderPath = "/shared"; + Mockito.when(foldersPathsConfig.getShared()).thenReturn(sharedFolderPath); + String relativePath = "/relative"; + + String result = fileStorerService.saveToSharedFolder(file, relativePath); + + Assertions.assertEquals(Paths.get(relativePath,filename).toString(),result); + aesUtilsMockedStatic.verify(() -> AESUtils.encrypt(Mockito.anyString(),(InputStream) Mockito.any())); + aesUtilsMockedStatic.verifyNoMoreInteractions(); + filesMockedStatic.verify(() -> Files.exists(Mockito.eq( + Paths.get(sharedFolderPath,relativePath)))); + filesMockedStatic.verify(() -> Files.createDirectories(Mockito.eq( + Paths.get(sharedFolderPath,relativePath)))); + filesMockedStatic.verify(() -> Files.copy((InputStream) Mockito.any(),Mockito.eq(Paths.get(sharedFolderPath,relativePath,filename)), Mockito.any())); + filesMockedStatic.verifyNoMoreInteractions(); + } + } +} diff --git a/src/test/java/it/gov/pagopa/pu/fileshare/service/ingestion/IngestionFlowFileServiceImplTest.java b/src/test/java/it/gov/pagopa/pu/fileshare/service/ingestion/IngestionFlowFileServiceImplTest.java index e060c83..5b3df94 100644 --- a/src/test/java/it/gov/pagopa/pu/fileshare/service/ingestion/IngestionFlowFileServiceImplTest.java +++ b/src/test/java/it/gov/pagopa/pu/fileshare/service/ingestion/IngestionFlowFileServiceImplTest.java @@ -1,7 +1,9 @@ package it.gov.pagopa.pu.fileshare.service.ingestion; +import it.gov.pagopa.pu.fileshare.config.FoldersPathsConfig; import it.gov.pagopa.pu.fileshare.dto.generated.IngestionFlowFileType; import it.gov.pagopa.pu.fileshare.service.FileService; +import it.gov.pagopa.pu.fileshare.service.FileStorerService; import it.gov.pagopa.pu.fileshare.service.UserAuthorizationService; import it.gov.pagopa.pu.fileshare.util.TestUtils; import org.junit.jupiter.api.BeforeEach; @@ -20,6 +22,10 @@ class IngestionFlowFileServiceImplTest { private UserAuthorizationService userAuthorizationServiceMock; @Mock private FileService fileServiceMock; + @Mock + private FileStorerService fileStorerServiceMock; + @Mock + private FoldersPathsConfig foldersPathsConfigMock; private IngestionFlowFileServiceImpl ingestionFlowFileService; private final String accessToken = "TOKEN"; private final long organizationId = 1L; @@ -28,7 +34,8 @@ class IngestionFlowFileServiceImplTest { @BeforeEach void setUp() { - ingestionFlowFileService = new IngestionFlowFileServiceImpl(userAuthorizationServiceMock, fileServiceMock,VALID_FILE_EXTENSION,INGESTION_FLOW_FILE_PATH); + ingestionFlowFileService = new IngestionFlowFileServiceImpl(userAuthorizationServiceMock,fileServiceMock,fileStorerServiceMock, + foldersPathsConfigMock,VALID_FILE_EXTENSION); } @Test @@ -39,13 +46,16 @@ void givenAuthorizedUserWhenUploadIngestionFlowFileThenOk(){ MediaType.TEXT_PLAIN_VALUE, "this is a test file".getBytes() ); + Mockito.when(foldersPathsConfigMock.getIngestionFlowFilePath(IngestionFlowFileType.RECEIPT)) + .thenReturn(INGESTION_FLOW_FILE_PATH); ingestionFlowFileService.uploadIngestionFlowFile(organizationId, IngestionFlowFileType.RECEIPT, file, TestUtils.getSampleUser(),accessToken); Mockito.verify(userAuthorizationServiceMock).checkUserAuthorization(organizationId,TestUtils.getSampleUser(),accessToken); Mockito.verify(fileServiceMock).validateFile(file,VALID_FILE_EXTENSION); - Mockito.verify(fileServiceMock).saveToSharedFolder(file,INGESTION_FLOW_FILE_PATH); + Mockito.verify(foldersPathsConfigMock).getIngestionFlowFilePath(Mockito.any()); + Mockito.verify(fileStorerServiceMock).saveToSharedFolder(file,INGESTION_FLOW_FILE_PATH); } }