diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMTableExport.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMTableExport.kt index c6d7ec86a..5b87b678d 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMTableExport.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMTableExport.kt @@ -2,8 +2,6 @@ package fr.gouv.dgampa.rapportnav.domain.entities.aem import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionType -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.NavActionEntity import org.slf4j.LoggerFactory import java.time.Instant @@ -50,5 +48,19 @@ data class AEMTableExport( tableExport.sovereignProtect?.nbrOfRecognizedVessel = mission.generalInfo?.nbrOfRecognizedVessel?.toDouble(); return tableExport } + + fun fromMissionList(missions: List): List { + val tableExports = mutableListOf() + + for (mission in missions) { + if (mission.actions != null) { + val tableExport = fromMissionAction(mission.actions ?: listOf(), mission.endDateTimeUtc) + tableExport.sovereignProtect?.nbrOfRecognizedVessel = mission.generalInfo?.nbrOfRecognizedVessel?.toDouble() + tableExports.add(tableExport) + } + } + + return tableExports + } } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportZipMissionsAEM.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportZipMissionsAEM.kt new file mode 100644 index 000000000..0d2246de1 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportZipMissionsAEM.kt @@ -0,0 +1,87 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export + +import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.aem.AEMTableExport +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionAEMExportEntity +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMission +import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow +import fr.gouv.dgampa.rapportnav.infrastructure.utils.Base64Converter +import fr.gouv.dgampa.rapportnav.infrastructure.utils.FileUtils +import fr.gouv.dgampa.rapportnav.infrastructure.utils.office.ExportExcelFile +import fr.gouv.dgampa.rapportnav.infrastructure.utils.office.OfficeConverter +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Value +import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardCopyOption + +@UseCase +class ExportZipMissionsAEM( + private val getMissionById: GetMission, + @Value("\${rapportnav.aem.template.path}") private val aemTemplatePath: String, + @Value("\${rapportnav.aem.tmp_xlsx.path}") private val aemTmpXLSXPath: String, + private val fillAEMExcelRow: FillAEMExcelRow +) { + + private val logger: Logger = LoggerFactory.getLogger(ExportZipMissionsAEM::class.java) + + fun execute(missionIds: List): MissionAEMExportEntity { + var missions = mutableListOf() + + for (missionId in missionIds) { + val mission = getMissionById.execute(missionId) + if (mission != null) { + missions.add(mission) + } + } + + if (missions.size > 0) { + + val inputStream = javaClass.getResourceAsStream(aemTemplatePath) + ?: throw IllegalArgumentException("Template file not found: $aemTemplatePath") + + val tmpPath = Path.of(aemTmpXLSXPath) + Files.copy(inputStream, tmpPath, StandardCopyOption.REPLACE_EXISTING) + inputStream.close() + + val excelFile = ExportExcelFile(tmpPath.toString()) + + val filesToZip = mutableListOf(); + + for (mission in missions) { + val tableExport = AEMTableExport.fromMission(mission) + fillAEMExcelRow.fill(tableExport, excelFile, "Synthese", 3) + excelFile.save() + + logger.info("Excel file processed and saved") + + val odsFilePath = OfficeConverter().convert(tmpPath.toString(), "Mission-${mission.id}.ods") + filesToZip.add(File(odsFilePath)) + } + + val zipFile = File("tmp_output.zip") + + val fileUtils = FileUtils(); + + val outputZipFile = fileUtils.zip(zipFile, filesToZip) + + val base64Content = Base64Converter().convertToBase64(outputZipFile.absolutePath) + + for (file in filesToZip) { + file.delete() // remove file from project + } + + outputZipFile.delete() // remove zip from project + + return MissionAEMExportEntity( + fileName = "tableaux_aem.zip", + fileContent = base64Content + ) + + } + throw RuntimeException("Error : Mission list is empty") + } +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/v2/ExportMissionsAEM.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/v2/ExportMissionsAEM.kt new file mode 100644 index 000000000..3b0ab5b0d --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/v2/ExportMissionsAEM.kt @@ -0,0 +1,82 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.v2 + +import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.aem.AEMTableExport +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionAEMExportEntity +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMission +import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow +import fr.gouv.dgampa.rapportnav.infrastructure.utils.Base64Converter +import fr.gouv.dgampa.rapportnav.infrastructure.utils.office.ExportExcelFile +import fr.gouv.dgampa.rapportnav.infrastructure.utils.office.OfficeConverter +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Value +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardCopyOption + +@UseCase +class ExportMissionsAEM( + @Value("\${rapportnav.aem.template.path}") private val aemTemplatePath: String, + @Value("\${rapportnav.aem.tmp_xlsx.path}") private val aemTmpXLSXPath: String, + @Value("\${rapportnav.aem.tmp_ods.path}") private val aemTmpODSPath: String, + private val fillAEMExcelRow: FillAEMExcelRow, + private val getMissionById: GetMission, + + ) { + private val logger: Logger = LoggerFactory.getLogger(ExportMissionsAEM::class.java) + + fun execute(missionIds: List): MissionAEMExportEntity? + { + + var missions = mutableListOf() + + for (missionId in missionIds) { + val mission = getMissionById.execute(missionId) + if (mission != null) { + missions.add(mission) + } + } + + return try { + val inputStream = javaClass.getResourceAsStream(aemTemplatePath) + ?: throw IllegalArgumentException("Template file not found: $aemTemplatePath") + + val tmpPath = Path.of(aemTmpXLSXPath) + Files.copy(inputStream, tmpPath, StandardCopyOption.REPLACE_EXISTING) + inputStream.close() + + logger.info("Template file copied to temporary path: $tmpPath") + + val excelFile = ExportExcelFile(tmpPath.toString()) + val tableExportList = AEMTableExport.fromMissionList(missions) + + if (tableExportList.isNotEmpty()) { + var rowStart = 3 + + for (tableExport in tableExportList) { + fillAEMExcelRow.fill(tableExport, excelFile, "Synthese", rowStart) + rowStart++ + } + excelFile.save() + + logger.info("Excel file processed and saved for export on missions list") + + val odsFile = OfficeConverter().convert(tmpPath.toString(), aemTmpODSPath) + val base64Content = Base64Converter().convertToBase64(odsFile) + + return MissionAEMExportEntity( + fileName = "Rapport_AEM.ods", + fileContent = base64Content + ) + } + + logger.error("Actions in missions list are null") + null + } catch (e: Exception) { + logger.error("An error occurred during mission processing", e) + null + } + } +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/MissionController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/MissionController.kt index 8502f131e..7fef74dd4 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/MissionController.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/MissionController.kt @@ -7,11 +7,13 @@ import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExpor import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.* import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMissionRapportPatrouille import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMissionAEM +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportZipMissionsAEM import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.AddOrUpdateMissionGeneralInfo import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.user.GetControlUnitsForUser import fr.gouv.dgampa.rapportnav.domain.use_cases.user.GetUserFromToken import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.MissionEnvInput +import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.export.MissionAEMExportInput import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.generalInfo.MissionGeneralInfoInput import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.generalInfo.MissionServiceInput import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.Mission @@ -38,7 +40,8 @@ class MissionController( private val exportMissionRapportPatrouille: ExportMissionRapportPatrouille, private val updateMissionService: UpdateMissionService, private val patchEnvMission: PatchEnvMission, - private val exportExcelFile: ExportMissionAEM + private val exportMissionAEM: ExportMissionAEM, + private val exportZipMissionsAEM: ExportZipMissionsAEM ) { private val logger = LoggerFactory.getLogger(MissionController::class.java) @@ -216,6 +219,10 @@ class MissionController( @QueryMapping fun missionAEMExport(@Argument missionId: Int): MissionAEMExportEntity? { - return exportExcelFile.execute(missionId) + return exportMissionAEM.execute(missionId) + } + @QueryMapping + fun missionAEMExportZip(@Argument exportZip: MissionAEMExportInput): MissionAEMExportEntity? { + return exportZipMissionsAEM.execute(exportZip.ids) } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/adapters/export/MissionAEMExportInput.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/adapters/export/MissionAEMExportInput.kt new file mode 100644 index 000000000..ed934310a --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/adapters/export/MissionAEMExportInput.kt @@ -0,0 +1,6 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.export + +data class MissionAEMExportInput( + var ids: List +) { +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/v2/MissionExportController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/v2/MissionExportController.kt new file mode 100644 index 000000000..b2bde2d3e --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/v2/MissionExportController.kt @@ -0,0 +1,19 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.api.bff.v2 + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionAEMExportEntity +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.v2.ExportMissionsAEM +import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.export.MissionAEMExportInput +import org.springframework.graphql.data.method.annotation.Argument +import org.springframework.graphql.data.method.annotation.QueryMapping +import org.springframework.stereotype.Controller + +@Controller +class MissionExportController( + private val exportMissionsAEM: ExportMissionsAEM +) +{ + @QueryMapping + fun missionAEMExportV2(@Argument missionExportAEMExportInput: MissionAEMExportInput): MissionAEMExportEntity? { + return exportMissionsAEM.execute(missionExportAEMExportInput.ids) + } +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/utils/FileUtils.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/utils/FileUtils.kt new file mode 100644 index 000000000..63a4c4617 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/utils/FileUtils.kt @@ -0,0 +1,25 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.utils + +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +class FileUtils( +) { + + fun zip(outputZipFile: File, files: List): File { + ZipOutputStream(FileOutputStream(outputZipFile)).use { zipOut -> + files.forEach { file -> + FileInputStream(file).use { fis -> + val zipEntry = ZipEntry(file.name) + zipOut.putNextEntry(zipEntry) + fis.copyTo(zipOut) + zipOut.closeEntry() + } + } + } + return outputZipFile + } +} diff --git a/backend/src/main/resources/graphql/mission.graphqls b/backend/src/main/resources/graphql/mission.graphqls index 9e502f093..8e80c4c7d 100644 --- a/backend/src/main/resources/graphql/mission.graphqls +++ b/backend/src/main/resources/graphql/mission.graphqls @@ -4,6 +4,8 @@ type Query { mission(missionId: ID): Mission missionGeneralInfo(missionId: ID): MissionGeneralInfo missionAEMExport(missionId: ID): MissionExport + missionAEMExportZip(exportZip: MissionAEMExportInput): MissionExport + missionAEMExportV2(missionIds: MissionAEMExportInput): MissionExport } type Mutation { @@ -86,3 +88,7 @@ input MissionEnvInput { startDateTimeUtc: Instant endDateTimeUtc: Instant } + +input MissionAEMExportInput { + ids: [Int] +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportMissionAEMTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportMissionAEMTests.kt index 0d1d277f6..9a81afdd4 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportMissionAEMTests.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportMissionAEMTests.kt @@ -1,8 +1,12 @@ package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.export +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionAEMExportEntity import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMission import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMissionAEM import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow +import fr.gouv.gmampa.rapportnav.mocks.mission.MissionEntityMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionControlMock import org.assertj.core.api.Assertions import org.junit.jupiter.api.Test import org.mockito.Mockito @@ -33,14 +37,12 @@ class ExportMissionAEMTests { Assertions.assertThat(result).isNull() } - /* + @Test fun `execute AEM export return a MissionAEMExportEntity when mission and action exist`() { - val action = NavActionControlMock.createActionControlEntity().toNavActionEntity() - + val action = NavActionControlMock.create().toNavActionEntity() val missionAction = MissionActionEntity.NavAction(action) - val missionId = 1 val mission = MissionEntityMock.create(actions = listOf(missionAction)) Mockito.`when`(getMissionById.execute(missionId)).thenReturn(mission) @@ -50,5 +52,5 @@ class ExportMissionAEMTests { Assertions.assertThat(result).isNotNull() Assertions.assertThat(result).isInstanceOf(MissionAEMExportEntity::class.java) - }*/ + } } diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportZipMissionsAEMTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportZipMissionsAEMTests.kt new file mode 100644 index 000000000..93e809ed9 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportZipMissionsAEMTests.kt @@ -0,0 +1,44 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.export + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionAEMExportEntity +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMission +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportZipMissionsAEM +import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow +import fr.gouv.gmampa.rapportnav.mocks.mission.MissionEntityMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionControlMock +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test +import org.mockito.Mockito +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean + +@SpringBootTest(classes = [ExportZipMissionsAEM::class]) +class ExportZipMissionsAEMTests { + + @MockBean + private lateinit var getMissionById: GetMission + + @MockBean + private lateinit var fillAEMExcelRow: FillAEMExcelRow + + @Autowired + private lateinit var exportZipMissionsAEM: ExportZipMissionsAEM + + + @Test + fun `should return a MissionExportAEMEntity`() { + val action = NavActionControlMock.create().toNavActionEntity() + val missionAction = MissionActionEntity.NavAction(action) + val mission = MissionEntityMock.create(actions = listOf(missionAction)) + val mission2 = MissionEntityMock.create(actions = listOf(missionAction), id = 2) + + Mockito.`when`(getMissionById.execute(1)).thenReturn(mission) + Mockito.`when`(getMissionById.execute(2)).thenReturn(mission2) + + val missionAEMExport = exportZipMissionsAEM.execute(listOf(1, 2)) + + Assertions.assertThat(missionAEMExport).isInstanceOf(MissionAEMExportEntity::class.java) + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/v2/ExportMissionsAEMTest.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/v2/ExportMissionsAEMTest.kt new file mode 100644 index 000000000..4541dcbe8 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/v2/ExportMissionsAEMTest.kt @@ -0,0 +1,44 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.export.v2 + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionAEMExportEntity +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMission +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.v2.ExportMissionsAEM +import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow +import fr.gouv.gmampa.rapportnav.mocks.mission.MissionEntityMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionControlMock +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test +import org.mockito.Mockito +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean + +@SpringBootTest(classes = [ExportMissionsAEM::class]) +class ExportMissionsAEMTest { + + @Autowired + private lateinit var exportMissionListAEM: ExportMissionsAEM + + @MockBean + private lateinit var fillAEMExcelRow: FillAEMExcelRow + + @MockBean + private lateinit var getMissionById: GetMission + + @Test + fun `execute AEM mission list export return a MissionAEMExportEntity when mission list has actions`() { + val action = NavActionControlMock.create().toNavActionEntity() + val missionAction = MissionActionEntity.NavAction(action) + + val mission = MissionEntityMock.create(actions = listOf(missionAction)) + + Mockito.`when`(getMissionById.execute(1)).thenReturn(mission) + + val result = exportMissionListAEM.execute(listOf(1)) + + Assertions.assertThat(result).isNotNull() + Assertions.assertThat(result).isInstanceOf(MissionAEMExportEntity::class.java) + + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/infrastructure/utils/ExportZipTest.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/infrastructure/utils/ExportZipTest.kt new file mode 100644 index 000000000..a08cf1be5 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/infrastructure/utils/ExportZipTest.kt @@ -0,0 +1,55 @@ +package fr.gouv.gmampa.rapportnav.infrastructure.utils + +import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow +import fr.gouv.dgampa.rapportnav.infrastructure.utils.FileUtils +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +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.mock.mockito.MockBean +import java.io.File +import java.io.FileInputStream +import java.util.zip.ZipInputStream + +@SpringBootTest(classes = [FileUtils::class]) +class ExportZipTest { + + @Autowired + private lateinit var fileUtils: FileUtils + + @MockBean + private lateinit var fillAEMExcelRow: FillAEMExcelRow + + @Test + fun `should zip a list of files`() { + + val filesToZip = listOf(File("file1.txt").apply { writeText("Hello, World!") }, + File("file2.txt").apply { writeText("Kotlin ZIP Test") }) + + val zipFile = File("test_output.zip") + + val outputFile = fileUtils.zip(zipFile, filesToZip) + + val filesInZip = mutableListOf() + ZipInputStream(FileInputStream(zipFile)).use { zis -> + var entry = zis.nextEntry + while (entry != null) { + filesInZip.add(entry.name) + entry = zis.nextEntry + } + } + + assertTrue(outputFile.exists()) + assertTrue(outputFile.length() > 0) + assertEquals(2, filesInZip.size) + assertTrue(filesInZip.contains("file1.txt")) + assertTrue(filesInZip.contains("file2.txt")) + + zipFile.delete() + for (file in filesToZip) { + file.delete() + } + + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/infrastructure/utils/FileUtilsTest.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/infrastructure/utils/FileUtilsTest.kt new file mode 100644 index 000000000..363b23e23 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/infrastructure/utils/FileUtilsTest.kt @@ -0,0 +1,63 @@ +package fr.gouv.gmampa.rapportnav.infrastructure.utils + +import fr.gouv.dgampa.rapportnav.infrastructure.utils.FileUtils +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import java.io.File +import java.io.FileInputStream +import java.nio.file.Path +import java.util.zip.ZipInputStream + +class FileUtilsTest { + + private lateinit var fileUtils: FileUtils + + @BeforeEach + fun setUp() { + fileUtils = FileUtils() + } + + @Test + fun `should zip files correctly`(@TempDir tempDir: Path) { + // Arrange + val file1 = createTempFileWithContent(tempDir, "file1.txt", "Hello, world!") + val file2 = createTempFileWithContent(tempDir, "file2.txt", "Kotlin is great!") + + val outputZipFile = tempDir.resolve("output.zip").toFile() + + // Act + fileUtils.zip(outputZipFile, listOf(file1, file2)) + + // Assert + assertTrue(outputZipFile.exists(), "The output zip file should exist") + + val zippedFiles = getZippedFiles(outputZipFile) + assertEquals(2, zippedFiles.size, "There should be 2 files in the zip") + + assertEquals("Hello, world!", zippedFiles["file1.txt"], "Content of file1.txt should match") + assertEquals("Kotlin is great!", zippedFiles["file2.txt"], "Content of file2.txt should match") + } + + private fun createTempFileWithContent(tempDir: Path, filename: String, content: String): File { + val file = tempDir.resolve(filename).toFile() + file.writeText(content) + return file + } + + private fun getZippedFiles(zipFile: File): Map { + val contentMap = mutableMapOf() + ZipInputStream(FileInputStream(zipFile)).use { zipIn -> + var entry = zipIn.nextEntry + while (entry != null) { + val content = zipIn.readBytes().toString(Charsets.UTF_8) + contentMap[entry.name] = content + zipIn.closeEntry() + entry = zipIn.nextEntry + } + } + return contentMap + } +}