From 96a0ce15bf065fc586117cf4c219f966cc89ea12 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 10 Jun 2026 11:26:29 +0200 Subject: [PATCH 1/4] Fix invalidated multiroot studies blocked nodes --- .../study/server/service/StudyService.java | 15 +++++---------- .../study/server/service/SupervisionService.java | 11 ++++++++--- 2 files changed, 13 insertions(+), 13 deletions(-) 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 324a97716..b5c6d1f6c 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -3703,16 +3703,11 @@ private List getCurrentLimitViolations(UUID nodeUuid public void invalidateStudyRootNetwork(UUID studyUuid, UUID rootNetworkUuid, String userId) { rootNetworkService.assertIsRootNetworkInStudy(studyUuid, rootNetworkUuid); var rootNodeUuid = networkModificationTreeService.getStudyRootNodeUuid(studyUuid); - try { - // First we unbuild all nodes - doUnbuildNodeTree(studyUuid, rootNodeUuid, true, true, userId); - // Then we erase data linked to root node on all root networks - rootNetworkService.invalidateRootNetworkRemoteInfos(List.of(rootNetworkService.getRootNetworkInfos(rootNetworkUuid)), true, false); - rootNetworkService.updateRootNetworkIndexationStatus(studyUuid, rootNetworkUuid, RootNetworkIndexationStatus.NOT_INDEXED); - } finally { - networkModificationTreeService.unblockNodeTree(rootNetworkUuid, rootNodeUuid); - } - + // First we unbuild all nodes + doUnbuildNodeTree(studyUuid, rootNodeUuid, true, true, userId); + // Then we erase data linked to root node on all root networks + rootNetworkService.invalidateRootNetworkRemoteInfos(List.of(rootNetworkService.getRootNetworkInfos(rootNetworkUuid)), true, false); + rootNetworkService.updateRootNetworkIndexationStatus(studyUuid, rootNetworkUuid, RootNetworkIndexationStatus.NOT_INDEXED); notificationService.emitRootNetworksUpdated(studyUuid); } } diff --git a/src/main/java/org/gridsuite/study/server/service/SupervisionService.java b/src/main/java/org/gridsuite/study/server/service/SupervisionService.java index 8b442a594..6a01f6bd1 100644 --- a/src/main/java/org/gridsuite/study/server/service/SupervisionService.java +++ b/src/main/java/org/gridsuite/study/server/service/SupervisionService.java @@ -373,9 +373,14 @@ public void unbuildAllNodes(UUID studyUuid) { public void invalidateStudy(UUID studyUuid) { AtomicReference startTime = new AtomicReference<>(); startTime.set(System.nanoTime()); - rootNetworkService.getStudyRootNetworkIds(studyUuid).forEach(rnId -> - studyService.invalidateStudyRootNetwork(studyUuid, rnId, SUPERVISION_USER) - ); + try { + rootNetworkService.getStudyRootNetworkIds(studyUuid).forEach(rnId -> + studyService.invalidateStudyRootNetwork(studyUuid, rnId, SUPERVISION_USER) + ); + } finally { + var rootNodeUuid = networkModificationTreeService.getStudyRootNodeUuid(studyUuid); + studyService.unblockNodeTree(studyUuid, rootNodeUuid); + } notificationService.emitElementUpdated(studyUuid, SUPERVISION_USER); LOGGER.trace("Study {} nodes builds deleted and root node invalidated in : {} milliseconds", studyUuid, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get())); } From 25131b1856036ce3babc43e5f45eacd522a03656 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 10 Jun 2026 14:11:28 +0200 Subject: [PATCH 2/4] Make test more robust --- .../server/SupervisionControllerTest.java | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java index 0766cc7b5..1ba02d79b 100644 --- a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java +++ b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java @@ -18,10 +18,7 @@ import com.powsybl.network.store.client.NetworkStoreService; import com.powsybl.network.store.iidm.impl.NetworkFactoryImpl; import org.elasticsearch.client.RestClient; -import org.gridsuite.study.server.dto.BasicRootNetworkInfos; -import org.gridsuite.study.server.dto.CreatedStudyBasicInfos; -import org.gridsuite.study.server.dto.RootNetworkIndexationStatus; -import org.gridsuite.study.server.dto.VoltageLevelInfos; +import org.gridsuite.study.server.dto.*; import org.gridsuite.study.server.dto.elasticsearch.EquipmentInfos; import org.gridsuite.study.server.dto.elasticsearch.TombstonedEquipmentInfos; import org.gridsuite.study.server.dto.supervision.SupervisionStudyInfos; @@ -29,6 +26,7 @@ import org.gridsuite.study.server.elasticsearch.StudyInfosService; import org.gridsuite.study.server.repository.StudyEntity; import org.gridsuite.study.server.repository.StudyRepository; +import org.gridsuite.study.server.repository.rootnetwork.RootNetworkNodeInfoRepository; import org.gridsuite.study.server.service.*; import org.gridsuite.study.server.utils.TestUtils; import org.junit.jupiter.api.AfterEach; @@ -49,6 +47,7 @@ import java.util.Set; import java.util.UUID; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -78,6 +77,9 @@ class SupervisionControllerTest { private static final UUID NETWORK_UUID = UUID.randomUUID(); private static final UUID STUDY_UUID = UUID.randomUUID(); + private static final UUID SECOND_NETWORK_UUID = UUID.randomUUID(); + private static final UUID SECOND_CASE_UUID = UUID.randomUUID(); + @Autowired private NetworkModificationTreeService networkModificationTreeService; @@ -114,6 +116,9 @@ class SupervisionControllerTest { @Autowired private StudyInfosService studyInfosService; + @Autowired + private RootNetworkNodeInfoRepository rootNetworkNodeInfoRepository; + private static EquipmentInfos toEquipmentInfos(Identifiable i) { return EquipmentInfos.builder() .networkUuid(SupervisionControllerTest.NETWORK_UUID) @@ -159,6 +164,24 @@ private StudyEntity initStudy() throws Exception { return study; } + private void addSecondRootNetwork(UUID rootNetworkUuid, UUID networkUuid) { + rootNetworkService.insertCreationRequest(STUDY_UUID, + RootNetworkInfos.builder() + .id(rootNetworkUuid) + .name("second") + .tag("SND") + .caseInfos(new CaseInfos(UUID.randomUUID(), null, "secondCase", "XIIDM")) + .networkInfos(new NetworkInfos(networkUuid, "secondNetworkId")) + .build(), + "userId"); + studyService.createRootNetwork(STUDY_UUID, + RootNetworkInfos.builder() + .id(rootNetworkUuid) + .networkInfos(new NetworkInfos(networkUuid, "secondNetworkId")) + .caseInfos(new CaseInfos(UUID.randomUUID(), null, "secondCase", "XIIDM")) + .build()); + } + private void assertIndexationCount(long expectedEquipmentsIndexationCount, long expectedTombstonedEquipmentsIndexationCount) throws Exception { MvcResult mvcResult; // Test get indexed equipments and tombstoned equipments counts @@ -315,18 +338,28 @@ void testSupervisionStudiesBasicData() throws Exception { @Test void testInvalidateStudy() throws Exception { initStudy(); + UUID secondRootNetworkUuid = UUID.randomUUID(); + addSecondRootNetwork(secondRootNetworkUuid, SECOND_NETWORK_UUID); + when(rootNetworkService.getNetworkUuid(secondRootNetworkUuid)).thenReturn(SECOND_NETWORK_UUID); + Mockito.doNothing().when(networkStoreService).deleteNetwork(NETWORK_UUID); + Mockito.doNothing().when(networkStoreService).deleteNetwork(SECOND_NETWORK_UUID); mockMvc.perform(delete("/v1/supervision/studies/{studyUuid}/invalidate", STUDY_UUID)) .andExpect(status().isOk()); - // Remote root-network data was deleted - Mockito.verify(rootNetworkService, Mockito.times(1)) + Mockito.verify(rootNetworkService, Mockito.times(2)) .invalidateRootNetworkRemoteInfos(any(), eq(true), eq(false)); Mockito.verify(networkStoreService, Mockito.times(1)).deleteNetwork(NETWORK_UUID); + Mockito.verify(networkStoreService, Mockito.times(1)).deleteNetwork(SECOND_NETWORK_UUID); - // Indexation flipped to NOT_INDEXED so the auto-detect path will reimport on reopen assertIndexationStatus(STUDY_UUID, RootNetworkIndexationStatus.NOT_INDEXED.name()); assertIndexationCount(0, 0); + + List allRootNetworkUuids = rootNetworkService.getStudyRootNetworkIds(STUDY_UUID); + assertThat(allRootNetworkUuids).hasSize(2); + allRootNetworkUuids.forEach(rootNetworkUuid -> + rootNetworkNodeInfoRepository.findAllByRootNetworkId(rootNetworkUuid) + .forEach(info -> assertThat(info.getBlockedNode()).isFalse())); } } From 339471b3a750094478b421cb6417372d0c5e23b2 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 10 Jun 2026 14:16:21 +0200 Subject: [PATCH 3/4] Add comments --- .../org/gridsuite/study/server/SupervisionControllerTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java index 1ba02d79b..333e0cb68 100644 --- a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java +++ b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java @@ -348,14 +348,18 @@ void testInvalidateStudy() throws Exception { mockMvc.perform(delete("/v1/supervision/studies/{studyUuid}/invalidate", STUDY_UUID)) .andExpect(status().isOk()); + // Check that both root network underlying networks have been erased Mockito.verify(rootNetworkService, Mockito.times(2)) .invalidateRootNetworkRemoteInfos(any(), eq(true), eq(false)); Mockito.verify(networkStoreService, Mockito.times(1)).deleteNetwork(NETWORK_UUID); Mockito.verify(networkStoreService, Mockito.times(1)).deleteNetwork(SECOND_NETWORK_UUID); + // Check that the study index data has been erased assertIndexationStatus(STUDY_UUID, RootNetworkIndexationStatus.NOT_INDEXED.name()); assertIndexationCount(0, 0); + // Check that all nodes aren't blocked + Mockito.verify(studyService, Mockito.times(1)).unblockNodeTree(eq(STUDY_UUID), any()); List allRootNetworkUuids = rootNetworkService.getStudyRootNetworkIds(STUDY_UUID); assertThat(allRootNetworkUuids).hasSize(2); allRootNetworkUuids.forEach(rootNetworkUuid -> From 1b7a40b9cdfad3526b40f754d41886711c94f613 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 10 Jun 2026 14:24:07 +0200 Subject: [PATCH 4/4] Coderabbit suggestion --- .../org/gridsuite/study/server/SupervisionControllerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java index 333e0cb68..789f8b0a1 100644 --- a/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java +++ b/src/test/java/org/gridsuite/study/server/SupervisionControllerTest.java @@ -170,7 +170,7 @@ private void addSecondRootNetwork(UUID rootNetworkUuid, UUID networkUuid) { .id(rootNetworkUuid) .name("second") .tag("SND") - .caseInfos(new CaseInfos(UUID.randomUUID(), null, "secondCase", "XIIDM")) + .caseInfos(new CaseInfos(SECOND_CASE_UUID, null, "secondCase", "XIIDM")) .networkInfos(new NetworkInfos(networkUuid, "secondNetworkId")) .build(), "userId"); @@ -178,7 +178,7 @@ private void addSecondRootNetwork(UUID rootNetworkUuid, UUID networkUuid) { RootNetworkInfos.builder() .id(rootNetworkUuid) .networkInfos(new NetworkInfos(networkUuid, "secondNetworkId")) - .caseInfos(new CaseInfos(UUID.randomUUID(), null, "secondCase", "XIIDM")) + .caseInfos(new CaseInfos(SECOND_CASE_UUID, null, "secondCase", "XIIDM")) .build()); }