diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/crew/MissionCrewEntity.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/crew/MissionCrewEntity.kt index 65e9b5a8f..befc74450 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/crew/MissionCrewEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/crew/MissionCrewEntity.kt @@ -40,5 +40,15 @@ data class MissionCrewEntity( updatedAt = crew.updatedAt ) } + + fun fromAgentServiceEntity(agentService: AgentServiceEntity, missionId: Int? = null, missionIdUUID: UUID? = null): MissionCrewEntity { + return MissionCrewEntity( + missionId = missionId, + missionIdUUID = missionIdUUID, + comment = null, + agent = agentService.agent, + role = agentService.role, + ) + } } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/apikey/CreateApiKey.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/apikey/CreateApiKey.kt index 7a176d641..05dd38121 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/apikey/CreateApiKey.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/apikey/CreateApiKey.kt @@ -61,7 +61,7 @@ class CreateApiKey( val saved = repo.save(ApiKeyModel.fromApiKeyEntity(apiKey)) - logger.info("✅ Created new API key for owner='{}', publicId='{}'", owner, publicId) + logger.info("Created new API key for owner='{}', publicId='{}'", owner, publicId) return saved?.toApiKeyEntity() to rawKey } catch (ex: Exception) { diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionId.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionId.kt index 2fa2d4ec2..e857199bf 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionId.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionId.kt @@ -6,7 +6,7 @@ import fr.gouv.dgampa.rapportnav.domain.repositories.mission.crew.IMissionCrewRe import java.util.* @UseCase -class GetAgentsCrewByMissionId(private val agentCrewRepository: IMissionCrewRepository) { +class GetAgentsCrewByMissionId(private val missionCrewRepository: IMissionCrewRepository) { val rolePriority = listOf( "Commandant", "Second capitaine", @@ -25,13 +25,13 @@ class GetAgentsCrewByMissionId(private val agentCrewRepository: IMissionCrewRepo ) fun execute(missionId: Int, commentDefaultsToString: Boolean? = false): List { - return agentCrewRepository.findByMissionId(missionId = missionId) + return missionCrewRepository.findByMissionId(missionId = missionId) .map { MissionCrewEntity.fromMissionCrewModel(it) } .sortedBy { rolePriority.indexOf(it.role?.title) } //TODO replace by it.role.prority } fun execute(missionIdUUID: UUID, commentDefaultsToString: Boolean? = false): List { - return agentCrewRepository.findByMissionIdUUID(missionIdUUID = missionIdUUID) + return missionCrewRepository.findByMissionIdUUID(missionIdUUID = missionIdUUID) .map { MissionCrewEntity.fromMissionCrewModel(it) } .sortedBy { rolePriority.indexOf(it.role?.title) } //TODO replace by it.role.prority } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/CreateGeneralInfos.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/CreateGeneralInfos.kt index a0a401230..f166208d9 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/CreateGeneralInfos.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/CreateGeneralInfos.kt @@ -1,34 +1,65 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.v2 import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.v2.MissionGeneralInfoEntity2 import fr.gouv.dgampa.rapportnav.domain.repositories.mission.generalInfo.IMissionGeneralInfoRepository import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.v2.generalInfo.MissionGeneralInfo2 +import fr.gouv.dgampa.rapportnav.infrastructure.database.repositories.mission.crew.JPAMissionCrewRepository +import org.slf4j.LoggerFactory import java.util.* @UseCase class CreateGeneralInfos( - private val generalInfosRepository: IMissionGeneralInfoRepository + private val generalInfoRepository: IMissionGeneralInfoRepository, + private val crewRepository: JPAMissionCrewRepository ) { + + private val logger = LoggerFactory.getLogger(CreateGeneralInfos::class.java) + fun execute( missionId: Int? = null, missionIdUUID: UUID? = null, generalInfo2: MissionGeneralInfo2 - ): MissionGeneralInfoEntity2 { - val generalInfoModel = generalInfosRepository.save( - generalInfo2.toMissionGeneralInfoEntity( - missionId = missionId, - missionIdUUID = missionIdUUID + ): MissionGeneralInfoEntity2? { + return try { + // Try saving main general info + val savedGeneralInfo = generalInfoRepository.save( + generalInfo2.toMissionGeneralInfoEntity( + missionId = missionId, + missionIdUUID = missionIdUUID + ) ) - ) - return MissionGeneralInfoEntity2( - data = MissionGeneralInfoEntity( - id = generalInfoModel.id, - missionId = generalInfoModel.missionId, - missionIdUUID = generalInfoModel.missionIdUUID, - missionReportType = generalInfo2.missionReportType + + // Save related crew safely (if any) + val savedCrew = generalInfo2.crew?.mapNotNull { crew -> + try { + val crewEntity = crew.toMissionCrewEntity(missionId = missionId, missionIdUUID = missionIdUUID) + crewRepository.save(crewEntity) + } catch (e: Exception) { + // Log and skip crew member if saving fails + logger.info("Failed to save crew member: '{}'", e.message) + null + } + } + + // Return fully composed entity + MissionGeneralInfoEntity2( + data = MissionGeneralInfoEntity( + id = savedGeneralInfo.id, + missionId = savedGeneralInfo.missionId, + missionIdUUID = savedGeneralInfo.missionIdUUID, + missionReportType = generalInfo2.missionReportType + ), + crew = savedCrew?.map { MissionCrewEntity.fromMissionCrewModel(it) } ) - ) + + } catch (e: Exception) { + // Log top-level failure + logger.info("Failed to create general info: '{}'", e.message) + null + } } } + diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/CreateMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/CreateMission.kt index baf6c97e5..20a823d15 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/CreateMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/CreateMission.kt @@ -38,7 +38,11 @@ class CreateMission( generalInfo2 = generalInfo2, serviceId = serviceId ) ?: return null - val generalInfosEntity = createGeneralInfos.execute(missionIdUUID = missionNav.id, generalInfo2 = generalInfo2) + val generalInfosEntity = createGeneralInfos.execute( + missionId = null, + missionIdUUID = missionNav.id, + generalInfo2 = generalInfo2, + ) return MissionEntity2( idUUID = missionNav.id, diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/GetGeneralInfo2.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/GetGeneralInfo2.kt index dba0e3ca1..3981c389a 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/GetGeneralInfo2.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/v2/GetGeneralInfo2.kt @@ -3,12 +3,15 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.v2 import fr.gouv.dgampa.rapportnav.config.UseCase import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.controlResources.LegacyControlUnitEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.ServiceEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.AgentServiceEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.v2.MissionGeneralInfoEntity2 +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsByServiceId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetServiceByControlUnit import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId +import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.crew.MissionCrew import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.v2.generalInfo.MissionGeneralInfo2 import org.slf4j.LoggerFactory import java.util.* @@ -18,7 +21,8 @@ class GetGeneralInfo2( private val createGeneralInfos: CreateGeneralInfos, private val getServiceByControlUnit: GetServiceByControlUnit, private val getAgentsCrewByMissionId: GetAgentsCrewByMissionId, - private val getMissionGeneralInfoByMissionId: GetMissionGeneralInfoByMissionId + private val getMissionGeneralInfoByMissionId: GetMissionGeneralInfoByMissionId, + private val getAgentsByServiceId: GetAgentsByServiceId, ) { private val logger = LoggerFactory.getLogger(GetGeneralInfo2::class.java) @@ -30,7 +34,7 @@ class GetGeneralInfo2( return MissionGeneralInfoEntity2( services = services, crew = fetchCrew(missionId), - data = fetchGeneralInfo(missionId = missionId, serviceId = services.first().id) + data = fetchGeneralInfo(missionId = missionId, serviceId = services.firstOrNull()?.id) ) } @@ -48,10 +52,13 @@ class GetGeneralInfo2( private fun fetchGeneralInfo(missionId: Int, serviceId: Int? = null): MissionGeneralInfoEntity? { return try { - getMissionGeneralInfoByMissionId.execute(missionId = missionId) ?: createGeneralInfos( - missionId = missionId, - serviceId = serviceId - ) + val entity: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId = missionId) + entity + ?: createGeneralInfosRow( + missionId = missionId, + serviceId = serviceId + ) + } catch (e: Exception) { logger.error("Error fetching Nav general info for missionId: {}", missionId, e) throw e @@ -60,8 +67,12 @@ class GetGeneralInfo2( private fun fetchGeneralInfoUUID(missionIdUUID: UUID, serviceId: Int? = null): MissionGeneralInfoEntity? { return try { - getMissionGeneralInfoByMissionId.execute(missionIdUUID = missionIdUUID) - ?: createGeneralInfos(missionIdUUID = missionIdUUID, serviceId = serviceId) + val entity: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionIdUUID = missionIdUUID) + entity + ?: createGeneralInfosRow( + missionIdUUID = missionIdUUID, + serviceId = serviceId + ) } catch (e: Exception) { logger.error("Error fetching Nav general info for missionId: {}", missionIdUUID, e) throw e @@ -95,7 +106,7 @@ class GetGeneralInfo2( } } - private fun createGeneralInfos( + private fun createGeneralInfosRow( missionId: Int? = null, missionIdUUID: UUID? = null, serviceId: Int? = null @@ -103,7 +114,28 @@ class GetGeneralInfo2( return createGeneralInfos.execute( missionId = missionId, missionIdUUID = missionIdUUID, - generalInfo2 = MissionGeneralInfo2(serviceId = serviceId) - ).data + generalInfo2 = MissionGeneralInfo2( + serviceId = serviceId, + crew = createMissionCrew( + missionId = missionId, + missionIdUUID = missionIdUUID, + serviceId = serviceId + )?.map { MissionCrew.fromMissionCrewEntity(it) } + ), + )?.data + } + + private fun createMissionCrew(missionId: Int? = null, missionIdUUID: UUID? = null, serviceId: Int? = null): List? { + return serviceId?.let { serviceId -> + val serviceAgents: List = getAgentsByServiceId.execute(serviceId) + val crew: List? = serviceAgents.map { + MissionCrewEntity.fromAgentServiceEntity( + agentService = it, + missionId = missionId, + missionIdUUID = missionIdUUID + ) + } + crew + } } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/v2/AgentRestController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/v2/AgentRestController.kt index 32bc01023..79ce98872 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/v2/AgentRestController.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/bff/v2/AgentRestController.kt @@ -1,6 +1,5 @@ package fr.gouv.dgampa.rapportnav.infrastructure.api.bff.v2 -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgents import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsByServiceId import fr.gouv.dgampa.rapportnav.domain.use_cases.user.GetUserFromToken import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.crew.Agent @@ -18,7 +17,6 @@ import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/api/v2/agents") class AgentRestController( - private val getAgents: GetAgents, private val getUserFromToken: GetUserFromToken, private val getAgentsByServiceId: GetAgentsByServiceId, ) { diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/v2/CreateGeneralInfosTest.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/v2/CreateGeneralInfosTest.kt new file mode 100644 index 000000000..0cff29d13 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/v2/CreateGeneralInfosTest.kt @@ -0,0 +1,147 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.v2 + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.AgentEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.generalInfo.IMissionGeneralInfoRepository +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.v2.CreateGeneralInfos +import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.crew.MissionCrew +import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.v2.generalInfo.MissionGeneralInfo2 +import fr.gouv.dgampa.rapportnav.infrastructure.database.repositories.mission.crew.JPAMissionCrewRepository +import fr.gouv.gmampa.rapportnav.mocks.mission.MissionGeneralInfo2Mock +import fr.gouv.gmampa.rapportnav.mocks.mission.crew.MissionCrewEntityMock +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.mock +import org.mockito.kotlin.any +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.bean.override.mockito.MockitoBean +import java.util.UUID + +@SpringBootTest(classes = [CreateGeneralInfos::class]) +class CreateGeneralInfosTest { + + @Autowired + private lateinit var createGeneralInfos: CreateGeneralInfos + + @MockitoBean + private lateinit var generalInfoRepository: IMissionGeneralInfoRepository + + @MockitoBean + private lateinit var crewRepository: JPAMissionCrewRepository + + @BeforeEach + fun setUp() { + generalInfoRepository = mock() + crewRepository = mock() + createGeneralInfos = CreateGeneralInfos(generalInfoRepository, crewRepository) + } + + @Test + fun `should save general info and crew`() { + // Given + val missionUuid = UUID.randomUUID() + + val crewEntity = MissionCrewEntityMock.create(agent = AgentEntity( + firstName = "Alice", + lastName = "", + )) + val crewModel = crewEntity.toMissionCrewModel() + + val generalInfoInput = MissionGeneralInfo2Mock.create(crew = listOf(MissionCrew.fromMissionCrewEntity(crewEntity))) + + val savedGeneralInfoModel = MissionGeneralInfoEntity( + id = 100, missionId = 42, missionIdUUID = missionUuid + ) + + // Mock behavior + whenever(generalInfoRepository.save(any())).thenReturn(savedGeneralInfoModel.toMissionGeneralInfoModel()) + whenever(crewRepository.save(any())).thenReturn(crewModel) + + // When + val result = createGeneralInfos.execute( + missionId = 42, + missionIdUUID = missionUuid, + generalInfo2 = generalInfoInput + ) + + // Then + assertNotNull(result) + assertEquals(100, result?.data?.id) + assertEquals(missionUuid, result?.data?.missionIdUUID) + assertEquals(1, result?.crew?.size) + assertEquals("Alice", result?.crew?.first()?.agent?.firstName) + + verify(generalInfoRepository, times(1)).save(any()) + verify(crewRepository, times(1)).save(any()) + } + + @Test + fun `should save general info even if crew is null`() { + val missionId = 1234 + val generalInfoInput = MissionGeneralInfo2(crew = null) + val savedGeneralInfoModel = MissionGeneralInfoEntity(id = missionId) + + whenever(generalInfoRepository.save(any())).thenReturn(savedGeneralInfoModel.toMissionGeneralInfoModel()) + + val result = createGeneralInfos.execute(missionId = missionId, generalInfo2 = generalInfoInput) + + assertEquals(missionId, result?.data?.id) + assertEquals(null, result?.crew) + + verify(generalInfoRepository, times(1)).save(any()) + verify(crewRepository, never()).save(any()) + } + + @Test + fun `should return null when general info fails to save`() { + val generalInfoInput = MissionGeneralInfo2Mock.create() + whenever(generalInfoRepository.save(any())).thenReturn(null) + + val result = createGeneralInfos.execute(generalInfo2 = generalInfoInput) + + assertNull(result) + verify(generalInfoRepository, times(1)).save(any()) + verify(crewRepository, never()).save(any()) + } + + @Test + fun `should skip crew member if saving crew fails`() { + val missionUuid = UUID.randomUUID() + val crewEntity = MissionCrewEntityMock.create(agent = AgentEntity(firstName = "Bob", lastName = "")) + val generalInfoInput = MissionGeneralInfo2Mock.create(crew = listOf(MissionCrew.fromMissionCrewEntity(crewEntity))) + val savedGeneralInfoModel = MissionGeneralInfoEntity(id = 200, missionId = 99, missionIdUUID = missionUuid) + + whenever(generalInfoRepository.save(any())).thenReturn(savedGeneralInfoModel.toMissionGeneralInfoModel()) + whenever(crewRepository.save(any())).thenThrow(RuntimeException("DB error")) + + val result = createGeneralInfos.execute(missionId = 99, missionIdUUID = missionUuid, generalInfo2 = generalInfoInput) + + assertNotNull(result) + assertEquals(200, result?.data?.id) + assertTrue(result?.crew?.isEmpty() == true) + + verify(generalInfoRepository, times(1)).save(any()) + verify(crewRepository, times(1)).save(any()) + } + + @Test + fun `should return null when top-level exception is thrown`() { + val generalInfoInput = MissionGeneralInfo2Mock.create() + whenever(generalInfoRepository.save(any())).thenThrow(RuntimeException("Unexpected failure")) + + val result = createGeneralInfos.execute(generalInfo2 = generalInfoInput) + + assertNull(result) + verify(generalInfoRepository, times(1)).save(any()) + verify(crewRepository, never()).save(any()) + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/v2/GetGeneralInfos2Test.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/v2/GetGeneralInfos2Test.kt new file mode 100644 index 000000000..d60936481 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/v2/GetGeneralInfos2Test.kt @@ -0,0 +1,129 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.v2 + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.controlResources.LegacyControlUnitEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.ServiceEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.v2.MissionGeneralInfoEntity2 +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsByServiceId +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetServiceByControlUnit +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.v2.CreateGeneralInfos +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.v2.GetGeneralInfo2 +import fr.gouv.gmampa.rapportnav.mocks.mission.crew.MissionCrewEntityMock +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.mockito.Mockito.mock +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.bean.override.mockito.MockitoBean + +@SpringBootTest(classes = [GetGeneralInfo2::class]) +class GetGeneralInfos2Test { + + @Autowired + private lateinit var getGeneralInfo2: GetGeneralInfo2 + + @MockitoBean + private lateinit var createGeneralInfos: CreateGeneralInfos + @MockitoBean + private lateinit var getServiceByControlUnit: GetServiceByControlUnit + @MockitoBean + private lateinit var getAgentsCrewByMissionId: GetAgentsCrewByMissionId + @MockitoBean + private lateinit var getMissionGeneralInfoByMissionId: GetMissionGeneralInfoByMissionId + @MockitoBean + private lateinit var getAgentsByServiceId: GetAgentsByServiceId + + @BeforeEach + fun setUp() { + createGeneralInfos = mock() + getServiceByControlUnit = mock() + getAgentsCrewByMissionId = mock() + getMissionGeneralInfoByMissionId = mock() + getAgentsByServiceId = mock() + getGeneralInfo2 = GetGeneralInfo2( + createGeneralInfos, + getServiceByControlUnit, + getAgentsCrewByMissionId, + getMissionGeneralInfoByMissionId, + getAgentsByServiceId + ) + + } + + @Test + fun `should return existing general info for missionId`() { + val missionId = 10 + val controlUnits = listOf(LegacyControlUnitEntity(id = 1)) + val services = listOf(ServiceEntity(id = 123, name = "Customs")) + val generalInfo = MissionGeneralInfoEntity(id = 50, missionId = missionId) + val crew = listOf(MissionCrewEntityMock.create()) + + whenever(getServiceByControlUnit.execute(controlUnits)).thenReturn(services) + whenever(getMissionGeneralInfoByMissionId.execute(missionId)).thenReturn(generalInfo) + whenever(getAgentsCrewByMissionId.execute(missionId)).thenReturn(crew) + + val result = getGeneralInfo2.execute(missionId, controlUnits) + + assertEquals(services, result.services) + assertEquals(crew, result.crew) + assertEquals(generalInfo, result.data) + verify(createGeneralInfos, never()).execute(any(), any(), any()) + } + + @Test + fun `should create general info when not found`() { + val missionId = 42 + val controlUnits = listOf(LegacyControlUnitEntity(id = 1)) + val services = listOf(ServiceEntity(id = 99, name = "Navy")) + val created = MissionGeneralInfoEntity(id = 77, missionId = missionId) + val crew = listOf(MissionCrewEntityMock.create()) + + whenever(getServiceByControlUnit.execute(controlUnits)).thenReturn(services) + whenever(getMissionGeneralInfoByMissionId.execute(missionId)).thenReturn(null) + whenever(createGeneralInfos.execute(anyOrNull(), anyOrNull(), any())).thenReturn(MissionGeneralInfoEntity2(data = created)) + whenever(getAgentsCrewByMissionId.execute(missionId)).thenReturn(crew) + + val result = getGeneralInfo2.execute(missionId, controlUnits) + + assertEquals(services.first().id, result.services?.first()?.id) + verify(createGeneralInfos, times(1)).execute(anyOrNull(), anyOrNull(), any()) + } + + @Test + fun `should return empty crew when crew fetching fails`() { + val missionId = 5 + val services = listOf(ServiceEntity(id = 1, name = "Navy")) + val generalInfo = MissionGeneralInfoEntity(id = 99, missionId = missionId) + + whenever(getServiceByControlUnit.execute(null)).thenReturn(services) + whenever(getMissionGeneralInfoByMissionId.execute(missionId)).thenReturn(generalInfo) + whenever(getAgentsCrewByMissionId.execute(missionId)).thenThrow(RuntimeException("DB error")) + + val result = getGeneralInfo2.execute(missionId) + + assertTrue(result.crew!!.isEmpty()) + verify(getAgentsCrewByMissionId).execute(missionId) + } + + @Test + fun `should return empty services when fetch fails`() { + val missionId = 123 + whenever(getServiceByControlUnit.execute(null)).thenThrow(RuntimeException("network")) + whenever(getMissionGeneralInfoByMissionId.execute(missionId)).thenReturn(MissionGeneralInfoEntity(id = 1)) + + val result = getGeneralInfo2.execute(missionId) + + assertTrue(result.services?.isEmpty() == true) + } + +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/MissionGeneralInfo2Mock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/MissionGeneralInfo2Mock.kt index 7db6eafd6..3845346f6 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/MissionGeneralInfo2Mock.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/MissionGeneralInfo2Mock.kt @@ -3,6 +3,7 @@ package fr.gouv.gmampa.rapportnav.mocks.mission import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.MissionTypeEnum import fr.gouv.dgampa.rapportnav.domain.entities.mission.v2.MissionReinforcementTypeEnum import fr.gouv.dgampa.rapportnav.domain.entities.mission.v2.MissionReportTypeEnum +import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.crew.MissionCrew import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.v2.generalInfo.MissionGeneralInfo2 import java.time.Instant @@ -13,7 +14,8 @@ object MissionGeneralInfo2Mock { missionReportType: MissionReportTypeEnum = MissionReportTypeEnum.FIELD_REPORT, missionTypes: List = listOf(MissionTypeEnum.SEA), reinforcementType: MissionReinforcementTypeEnum? = null, - nbHourAtSea: Int? = null + nbHourAtSea: Int? = null, + crew: List? = null, ): MissionGeneralInfo2 { return MissionGeneralInfo2( startDateTimeUtc = startDateTimeUtc, @@ -22,7 +24,8 @@ object MissionGeneralInfo2Mock { missionTypes = missionTypes, reinforcementType = reinforcementType, nbHourAtSea = nbHourAtSea, - missionId = 0 + missionId = 0, + crew = crew ) } }