diff --git a/src/main/java/org/gridsuite/study/server/service/StudyService.java b/src/main/java/org/gridsuite/study/server/service/StudyService.java index c863fca0a..c8dba0254 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -546,23 +546,22 @@ private Optional doDeleteStudyIfNotCreationInProgress(UUID stu Optional studyCreationRequestEntity = studyCreationRequestRepository.findById(studyUuid); Optional studyEntity = studyRepository.findById(studyUuid); DeleteStudyInfos deleteStudyInfos = null; - if (studyCreationRequestEntity.isEmpty()) { + if (studyCreationRequestEntity.isEmpty() && studyEntity.isPresent()) { List rootNetworkInfos = getStudyRootNetworksInfos(studyUuid); // get all modification groups related to the study List modificationGroupUuids = networkModificationTreeService.getAllStudyNetworkModificationNodeInfo(studyUuid).stream().map(NetworkModificationNodeInfoEntity::getModificationGroupUuid).toList(); - studyEntity.ifPresent(s -> { - networkModificationTreeService.doDeleteTree(studyUuid); - studyRepository.deleteById(studyUuid); - studyInfosService.deleteByUuid(studyUuid); - computationParametersService.deleteComputationsParameters(s); - removeNetworkVisualizationParameters(s.getNetworkVisualizationParametersUuid()); - removeSpreadsheetConfigCollection(s.getSpreadsheetConfigCollectionUuid()); - removeWorkspacesConfig(s.getWorkspacesConfigUuid()); - removeNadConfigs(s.getNadConfigsUuids().stream().toList()); - }); + StudyEntity s = studyEntity.get(); + networkModificationTreeService.doDeleteTree(studyUuid); + studyRepository.deleteById(studyUuid); + studyInfosService.deleteByUuid(studyUuid); + computationParametersService.deleteComputationsParameters(s); + removeNetworkVisualizationParameters(s.getNetworkVisualizationParametersUuid()); + removeSpreadsheetConfigCollection(s.getSpreadsheetConfigCollectionUuid()); + removeWorkspacesConfig(s.getWorkspacesConfigUuid()); + removeNadConfigs(s.getNadConfigsUuids().stream().toList()); deleteStudyInfos = new DeleteStudyInfos(rootNetworkInfos, modificationGroupUuids); } else { - studyCreationRequestRepository.deleteById(studyCreationRequestEntity.get().getId()); + studyCreationRequestEntity.ifPresent(creationRequestEntity -> studyCreationRequestRepository.deleteById(creationRequestEntity.getId())); } if (deleteStudyInfos == null) { diff --git a/src/test/java/org/gridsuite/study/server/loadflow/LoadFlowTest.java b/src/test/java/org/gridsuite/study/server/loadflow/LoadFlowTest.java index 3a1b9b0db..f1cc956e6 100644 --- a/src/test/java/org/gridsuite/study/server/loadflow/LoadFlowTest.java +++ b/src/test/java/org/gridsuite/study/server/loadflow/LoadFlowTest.java @@ -70,13 +70,20 @@ import static org.gridsuite.study.server.StudyConstants.HEADER_USER_ID; import static org.gridsuite.study.server.dto.ComputationType.LOAD_FLOW; import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NOT_FOUND; -import static org.gridsuite.study.server.notification.NotificationService.*; +import static org.gridsuite.study.server.notification.NotificationService.HEADER_UPDATE_TYPE; +import static org.gridsuite.study.server.notification.NotificationService.NODE_BUILD_STATUS_UPDATED; +import static org.gridsuite.study.server.notification.NotificationService.UPDATE_TYPE_ALL_COMPUTATION_STATUS; +import static org.gridsuite.study.server.notification.NotificationService.UPDATE_TYPE_COMPUTATION_PARAMETERS; import static org.gridsuite.study.server.utils.TestUtils.USER_DEFAULT_PROFILE_JSON; +import static org.gridsuite.study.server.utils.TestUtils.synchronizeStudyServerExecutionService; import static org.gridsuite.study.server.utils.wiremock.WireMockUtilsCriteria.removeRequestMatching; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -177,6 +184,8 @@ class LoadFlowTest { private TestUtils studyTestUtils; @Autowired private ConsumerService consumerService; + @MockitoSpyBean + private StudyServerExecutionService studyServerExecutionService; private static WireMockServer wireMockServer; private WireMockStubs wireMockStubs; @@ -507,7 +516,7 @@ void testDeleteLoadFlowResults() throws Exception { //Test result count testResultCount(); - //Delete Voltage init results + //Delete loadflow results testDeleteResults(studyNameUserIdUuid, 1); } @@ -569,6 +578,10 @@ private void testDeleteResults(UUID studyUuid, int expectedInitialResultCount) t List rootNetworkNodeInfoEntities = rootNetworkNodeInfoRepository.findAllByLoadFlowResultUuidNotNull(); RootNetworkNodeInfoEntity rootNetworkNodeInfoEntity = rootNetworkNodeInfoEntities.getFirst(); + // Run runAsync tasks inline so fire-and-forget cleanup (e.g. blocking=false remote deletions) + // completes before the test verifies it, removes the race condition. + synchronizeStudyServerExecutionService(studyServerExecutionService); + assertEquals(expectedInitialResultCount, rootNetworkNodeInfoEntities.size()); wireMockStubs.loadflowServer.stubDeleteLoadflowResults(LOADFLOW_RESULT_UUID); wireMockStubs.reportServer.stubDeleteReport(); @@ -576,6 +589,7 @@ private void testDeleteResults(UUID studyUuid, int expectedInitialResultCount) t .queryParam("type", LOAD_FLOW.toString()) .queryParam("dryRun", "false")) .andExpect(status().isOk()); + wireMockStubs.loadflowServer.verifyDeleteLoadflowResults(); wireMockStubs.reportServer.verifyDeleteReport(); diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/RootNetworkTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/RootNetworkTest.java index 8af66baea..4a2a77bee 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/RootNetworkTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/RootNetworkTest.java @@ -14,11 +14,11 @@ import com.powsybl.network.store.client.NetworkStoreService; import com.powsybl.ws.commons.error.PowsyblWsProblemDetail; import org.gridsuite.study.server.ContextConfigurationWithTestChannel; -import org.gridsuite.study.server.error.StudyException; import org.gridsuite.study.server.dto.*; import org.gridsuite.study.server.dto.caseimport.CaseImportAction; import org.gridsuite.study.server.dto.caseimport.CaseImportReceiver; import org.gridsuite.study.server.elasticsearch.EquipmentInfosService; +import org.gridsuite.study.server.error.StudyException; import org.gridsuite.study.server.networkmodificationtree.dto.InsertMode; import org.gridsuite.study.server.networkmodificationtree.dto.NetworkModificationNode; import org.gridsuite.study.server.networkmodificationtree.entities.NodeEntity; @@ -57,12 +57,21 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.gridsuite.study.server.StudyConstants.HEADER_ERROR; +import static org.gridsuite.study.server.StudyConstants.HEADER_IMPORT_PARAMETERS; +import static org.gridsuite.study.server.StudyConstants.HEADER_RECEIVER; +import static org.gridsuite.study.server.StudyConstants.HEADER_USER_ID; import static org.gridsuite.study.server.error.StudyBusinessErrorCode.MAXIMUM_ROOT_NETWORK_BY_STUDY_REACHED; import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NOT_FOUND; -import static org.gridsuite.study.server.StudyConstants.*; import static org.gridsuite.study.server.utils.TestUtils.createModificationNodeInfo; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; +import static org.gridsuite.study.server.utils.TestUtils.synchronizeStudyServerExecutionService; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.springframework.http.MediaType.APPLICATION_JSON; @@ -144,6 +153,8 @@ class RootNetworkTest { @MockitoSpyBean private StudyService studyService; + @MockitoSpyBean + private StudyServerExecutionService studyServerExecutionService; @MockitoBean private ReportService reportService; @@ -460,6 +471,10 @@ void testCreateRootNetworkConsumerWithoutRequest() throws Exception { StudyEntity studyEntity = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); studyRepository.save(studyEntity); + // Run runAsync tasks inline so fire-and-forget cleanup (e.g. blocking=false remote deletions) + // completes before the test verifies it, removes the race condition. + synchronizeStudyServerExecutionService(studyServerExecutionService); + // DO NOT insert creation request - it means root network won't be created and remote resources will be deleted RootNetworkInfos rootNetworkInfos = RootNetworkInfos.builder().id(UUID.randomUUID()).name("newRootNetworkName").tag("newT") .caseInfos(new CaseInfos(CASE_UUID2, CASE_UUID, CASE_NAME2, CASE_FORMAT2)).networkInfos(new NetworkInfos(NETWORK_UUID2, NETWORK_ID2)) @@ -530,6 +545,10 @@ void testDeleteRootNetwork() throws Exception { // before deletion, check we have 2 root networks for study assertEquals(2, studyService.getExistingBasicRootNetworkInfos(studyEntity.getId()).size()); + // Run runAsync tasks inline so fire-and-forget cleanup (e.g. blocking=false remote deletions) + // completes before the test verifies it, removes the race condition. + synchronizeStudyServerExecutionService(studyServerExecutionService); + mockMvc.perform(delete("/v1/studies/{studyUuid}/root-networks", studyEntity.getId()) .contentType(APPLICATION_JSON) .content(objectMapper.writeValueAsString(List.of(rootNetworkEntityToDeleteUuid))) diff --git a/src/test/java/org/gridsuite/study/server/studycontroller/StudyTest.java b/src/test/java/org/gridsuite/study/server/studycontroller/StudyTest.java index d8e80caf0..1f8f75d54 100644 --- a/src/test/java/org/gridsuite/study/server/studycontroller/StudyTest.java +++ b/src/test/java/org/gridsuite/study/server/studycontroller/StudyTest.java @@ -483,6 +483,12 @@ void testCreateStudyWithErrorDuringCaseImport() throws Exception { assertEquals(userId, headers.get(HEADER_USER_ID)); assertEquals(NotificationService.UPDATE_TYPE_STUDY_CREATION_STARTED, headers.get(HEADER_UPDATE_TYPE)); + message = output.receive(TIMEOUT, "study.update"); + + headers = message.getHeaders(); + assertEquals(userId, headers.get(HEADER_USER_ID)); + assertEquals(NotificationService.UPDATE_TYPE_STUDY_CREATION_FINISHED, headers.get(HEADER_UPDATE_TYPE)); + MvcResult mvcResult = mockMvc.perform(get("/v1/study_creation_requests").header(USER_ID_HEADER, "userId")).andExpectAll( status().isOk(), content().contentType(MediaType.APPLICATION_JSON))