From fd49c949acca8cf7941f89c2c2b38a792e791a5f Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Mon, 23 Feb 2026 09:00:05 +0100 Subject: [PATCH 1/7] copy modification Signed-off-by: SOUISSI Maissa (Externe) --- .../service/RootNetworkNodeInfoService.java | 79 +++++++++++++++++++ .../study/server/service/StudyService.java | 11 +-- .../ModificationToExcludeTest.java | 76 +++++++++++++++++- 3 files changed, 160 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java b/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java index 43d192701..2daa42b8d 100644 --- a/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java +++ b/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java @@ -117,6 +117,52 @@ public void createNodeLinks(@NonNull StudyEntity studyEntity, @NonNull NetworkMo }); } + // get the root networks that are common between source and destination studies + private List getCommonRootNetworks(@NonNull UUID sourceStudyUuid, @NonNull UUID destinationStudyUuid) { + Set sourceTags = rootNetworkNodeInfoRepository + .findAllByRootNetworkStudyId(sourceStudyUuid).stream() + .map(RootNetworkNodeInfoEntity::getRootNetwork) + .map(RootNetworkEntity::getTag) + .collect(Collectors.toSet()); + + return rootNetworkNodeInfoRepository + .findAllByRootNetworkStudyId(destinationStudyUuid).stream() + .map(RootNetworkNodeInfoEntity::getRootNetwork) + .filter(rn -> rn.getTag() != null && sourceTags.contains(rn.getTag())) + .toList(); + } + + // create links for root networks with common tags + public void createNodeLinksWithCommonTag( + @NonNull NetworkModificationNodeInfoEntity targetNodeInfoEntity, + @NonNull NetworkModificationNodeInfoEntity sourceNodeInfoEntity, + @NonNull Map originToDuplicateModificationUuidMap, + @NonNull Map targetRootNetworksByTag) { + + // Map source node links by root network tag + Map sourceNodeLinksByTag = sourceNodeInfoEntity.getRootNetworkNodeInfos().stream() + .filter(rn -> rn.getRootNetwork().getTag() != null) + .collect(Collectors.toMap(rn -> rn.getRootNetwork().getTag(), rn -> rn)); + + targetRootNetworksByTag.forEach((tag, targetRN) -> { + RootNetworkNodeInfoEntity sourceNodeLink = sourceNodeLinksByTag.get(tag); + Set modificationsToExclude = Collections.emptySet(); + + if (sourceNodeLink != null) { + // Map modifications to the duplicated UUIDs + modificationsToExclude = sourceNodeLink.getModificationsUuidsToExclude().stream() + .map(originToDuplicateModificationUuidMap::get) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + // Create a new RootNetworkNodeInfoEntity for the target + RootNetworkNodeInfoEntity newRNNodeInfo = createDefaultEntity(targetNodeInfoEntity.getId(), modificationsToExclude); + + addLink(targetNodeInfoEntity, targetRN, newRNNodeInfo); + }); + } + public void duplicateNodeLinks(List sourceNodeLinks, @NonNull NetworkModificationNodeInfoEntity destinationNodeInfoEntity, Map originToDuplicateModificationUuidMap, Map originToDuplicateRootNetworkMap) { // For each root network create a link with the node sourceNodeLinks.forEach(nodeLink -> { @@ -534,6 +580,39 @@ public void copyModificationsToExclude(UUID originNodeUuid, UUID targetNodeUuid, })); } + /** + * Copies modification applicability (via exclusions) from origin study to target study + * according to root network tags + */ + public void copyModificationsToExcludeByCommonRootNetworkTag( + UUID originStudyUuid, UUID targetStudyUuid, UUID originNodeUuid, UUID targetNodeUuid, Map originToDuplicateModificationsUuids) { + List targetRootNetworksWithCommonTags = getCommonRootNetworks(originStudyUuid, targetStudyUuid); + + Map targetRootByTag = targetRootNetworksWithCommonTags.stream() + .filter(rn -> rn.getTag() != null) + .collect(Collectors.toMap(RootNetworkEntity::getTag, Function.identity(), (a, b) -> a)); + + rootNetworkNodeInfoRepository.findAllByNodeInfoId(originNodeUuid).forEach(originNodeInfo -> { + RootNetworkEntity targetRoot = targetRootByTag.get(originNodeInfo.getRootNetwork().getTag()); + if (targetRoot == null) { + return; + } + + getRootNetworkNodeInfo(targetNodeUuid, targetRoot.getId()) + .ifPresent(targetNodeInfo -> { + // Collect mapped exclusions into a Set + Set duplicatedExcludedModifications = originNodeInfo.getModificationsUuidsToExclude().stream() + .map(originToDuplicateModificationsUuids::get) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + if (!duplicatedExcludedModifications.isEmpty()) { + targetNodeInfo.addModificationsToExclude(duplicatedExcludedModifications); + } + }); + }); + } + @Transactional public void updateRootNetworkNode(UUID nodeUuid, UUID rootNetworkUuid, RootNetworkNodeInfo rootNetworkNodeInfo) { RootNetworkNodeInfoEntity rootNetworkNodeInfoEntity = rootNetworkNodeInfoRepository.findByNodeInfoIdAndRootNetworkId(nodeUuid, rootNetworkUuid).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); 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 a31716c63..5ff637878 100644 --- a/src/main/java/org/gridsuite/study/server/service/StudyService.java +++ b/src/main/java/org/gridsuite/study/server/service/StudyService.java @@ -2547,15 +2547,16 @@ public void duplicateOrInsertNetworkModifications( .toList(); NetworkModificationsResult networkModificationResults = networkModificationService.duplicateOrInsertModifications(groupUuid, action, Pair.of(modifications, modificationApplicationContexts)); + Map originToDuplicateModificationsUuids = new HashMap<>(); + for (int i = 0; i < modifications.size(); i++) { + originToDuplicateModificationsUuids.put(modifications.get(i).getUuid(), networkModificationResults.modificationUuids().get(i)); + } if (targetStudyUuid.equals(originStudyUuid)) { - Map originToDuplicateModificationsUuids = new HashMap<>(); - for (int i = 0; i < modifications.size(); i++) { - originToDuplicateModificationsUuids.put(modifications.get(i).getUuid(), networkModificationResults.modificationUuids().get(i)); - } rootNetworkNodeInfoService.copyModificationsToExclude(originNodeUuid, targetNodeUuid, originToDuplicateModificationsUuids); + } else { + rootNetworkNodeInfoService.copyModificationsToExcludeByCommonRootNetworkTag(originStudyUuid, targetStudyUuid, originNodeUuid, targetNodeUuid, originToDuplicateModificationsUuids); } - if (networkModificationResults != null) { int index = 0; // for each NetworkModificationResult, send an impact notification - studyRootNetworkEntities are ordered in the same way as networkModificationResults diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index a07bbf0e1..b3d4057db 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -165,6 +165,76 @@ void testExcludeModifications() throws Exception { assertEquals(0, rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNode.getId(), rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow().getModificationsUuidsToExclude().size()); } + @Test + void testDuplicateModificationsBetweenStudiesWithCommonRootNetworkTags() { + // -------- Study 1 -------- + StudyEntity study1 = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); + createDummyRootNetwork(study1, "rnA", "ok"); // common root network + createDummyRootNetwork(study1, "rnB", "no1"); + studyRepository.save(study1); + + List rootNetworksStudy1 = studyService.getExistingBasicRootNetworkInfos(study1.getId()); + + NodeEntity rootNode1 = networkModificationTreeService.createRoot(study1); + NetworkModificationNode node1 = networkModificationTreeService.createNode( + study1, rootNode1.getIdNode(), createModificationNodeInfo(NODE_1_NAME), InsertMode.AFTER, null + ); + + // set modifications to exclude only for common root network + BasicRootNetworkInfos commonRnStudy1 = rootNetworksStudy1.stream() + .filter(rn -> "ok".equals(rn.tag())) + .findFirst() + .orElseThrow(); + RootNetworkNodeInfoEntity commonRnNodeInfoStudy1 = rootNetworkNodeInfoRepository + .findByNodeInfoIdAndRootNetworkId(node1.getId(), commonRnStudy1.rootNetworkUuid()) + .orElseThrow(); + commonRnNodeInfoStudy1.setModificationsUuidsToExclude(MODIFICATIONS_TO_EXCLUDE_RN_1); + rootNetworkNodeInfoRepository.save(commonRnNodeInfoStudy1); + + // -------- Study 2 -------- + StudyEntity study2 = TestUtils.createDummyStudy(NETWORK_UUID2, CASE_UUID2, CASE_NAME2, CASE_FORMAT2, REPORT_UUID2); + createDummyRootNetwork(study2, "rnX", "no2"); // not common + createDummyRootNetwork(study2, "rnA", "ok"); // common root network + studyRepository.save(study2); + + List rootNetworksStudy2 = studyService.getExistingBasicRootNetworkInfos(study2.getId()); + NodeEntity rootNode2 = networkModificationTreeService.createRoot(study2); + + Mockito.doReturn(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP) + .when(networkModificationService) + .duplicateModificationsGroup(any(), any()); + + UUID duplicatedNodeUuid = networkModificationTreeService.duplicateStudyNode( + node1.getId(), rootNode2.getIdNode(), InsertMode.AFTER + ); + + // -------- Assertions -------- + // For common root network -> modifications should be copied + BasicRootNetworkInfos commonRnStudy2 = rootNetworksStudy2.stream() + .filter(rn -> "ok".equals(rn.tag())) + .findFirst() + .orElseThrow(); + RootNetworkNodeInfoEntity commonRnNodeInfoStudy2 = rootNetworkNodeInfoRepository + .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(duplicatedNodeUuid, commonRnStudy2.rootNetworkUuid()) + .orElseThrow(); + Set expectedExcluded = MODIFICATIONS_TO_EXCLUDE_RN_1.stream() + .map(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP::get) + .collect(Collectors.toSet()); + assertEquals(expectedExcluded, commonRnNodeInfoStudy2.getModificationsUuidsToExclude(), + "Modifications to exclude should be copied for common root network"); + + // unique root network -> modifications should be empty + BasicRootNetworkInfos uniqueRnStudy2 = rootNetworksStudy2.stream() + .filter(rn -> "no2".equals(rn.tag())) + .findFirst() + .orElseThrow(); + RootNetworkNodeInfoEntity uniqueRnNodeInfoStudy2 = rootNetworkNodeInfoRepository + .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(duplicatedNodeUuid, uniqueRnStudy2.rootNetworkUuid()) + .orElseThrow(); + assertTrue(uniqueRnNodeInfoStudy2.getModificationsUuidsToExclude().isEmpty(), + "Modifications to exclude should be empty for non-common root network"); + } + @Test void testDuplicateNodeWithModificationsToExclude() { // create study with two root networks @@ -555,7 +625,7 @@ void testBuildNodeWithExcludedModifications() { assertThat(buildInfosCaptor.getValue().getModificationUuidsToExclude().get(secondNode.getModificationGroupUuid())).usingRecursiveComparison().ignoringCollectionOrder().isEqualTo(MODIFICATIONS_TO_EXCLUDE_RN_2); } - private void createDummyRootNetwork(StudyEntity studyEntity, String name) { + private void createDummyRootNetwork(StudyEntity studyEntity, String name, String tag) { RootNetworkEntity rootNetworkEntity = RootNetworkInfos.builder() .id(UUID.randomUUID()) .name(name) @@ -566,4 +636,8 @@ private void createDummyRootNetwork(StudyEntity studyEntity, String name) { .build().toEntity(); studyEntity.addRootNetwork(rootNetworkEntity); } + + private void createDummyRootNetwork(StudyEntity studyEntity, String name) { + createDummyRootNetwork(studyEntity, name, UUID.randomUUID().toString().substring(0, 4)); + } } From 499f10e41f51e986811006eb2e264165e98e28df Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Mon, 23 Feb 2026 09:04:10 +0100 Subject: [PATCH 2/7] copy modification and applicability for common tag Signed-off-by: SOUISSI Maissa (Externe) --- .../study/server/rootnetworks/ModificationToExcludeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index b3d4057db..fd78dfa9d 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -629,7 +629,7 @@ private void createDummyRootNetwork(StudyEntity studyEntity, String name, String RootNetworkEntity rootNetworkEntity = RootNetworkInfos.builder() .id(UUID.randomUUID()) .name(name) - .tag(UUID.randomUUID().toString().substring(0, 4)) + .tag(tag) .caseInfos(new CaseInfos(UUID.randomUUID(), UUID.randomUUID(), "caseName", "caseFormat")) .networkInfos(new NetworkInfos(UUID.randomUUID(), UUID.randomUUID().toString())) .reportUuid(UUID.randomUUID()) From 44f49aeadbdcd43336e7ba2840467e58c46b3b7d Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Mon, 23 Feb 2026 09:26:34 +0100 Subject: [PATCH 3/7] test copy modifs between studies and copy applicabilities for common root network tag Signed-off-by: SOUISSI Maissa (Externe) --- .../ModificationToExcludeTest.java | 193 +++++++++++------- 1 file changed, 123 insertions(+), 70 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index fd78dfa9d..7bcbc6b95 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -165,76 +165,6 @@ void testExcludeModifications() throws Exception { assertEquals(0, rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNode.getId(), rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow().getModificationsUuidsToExclude().size()); } - @Test - void testDuplicateModificationsBetweenStudiesWithCommonRootNetworkTags() { - // -------- Study 1 -------- - StudyEntity study1 = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); - createDummyRootNetwork(study1, "rnA", "ok"); // common root network - createDummyRootNetwork(study1, "rnB", "no1"); - studyRepository.save(study1); - - List rootNetworksStudy1 = studyService.getExistingBasicRootNetworkInfos(study1.getId()); - - NodeEntity rootNode1 = networkModificationTreeService.createRoot(study1); - NetworkModificationNode node1 = networkModificationTreeService.createNode( - study1, rootNode1.getIdNode(), createModificationNodeInfo(NODE_1_NAME), InsertMode.AFTER, null - ); - - // set modifications to exclude only for common root network - BasicRootNetworkInfos commonRnStudy1 = rootNetworksStudy1.stream() - .filter(rn -> "ok".equals(rn.tag())) - .findFirst() - .orElseThrow(); - RootNetworkNodeInfoEntity commonRnNodeInfoStudy1 = rootNetworkNodeInfoRepository - .findByNodeInfoIdAndRootNetworkId(node1.getId(), commonRnStudy1.rootNetworkUuid()) - .orElseThrow(); - commonRnNodeInfoStudy1.setModificationsUuidsToExclude(MODIFICATIONS_TO_EXCLUDE_RN_1); - rootNetworkNodeInfoRepository.save(commonRnNodeInfoStudy1); - - // -------- Study 2 -------- - StudyEntity study2 = TestUtils.createDummyStudy(NETWORK_UUID2, CASE_UUID2, CASE_NAME2, CASE_FORMAT2, REPORT_UUID2); - createDummyRootNetwork(study2, "rnX", "no2"); // not common - createDummyRootNetwork(study2, "rnA", "ok"); // common root network - studyRepository.save(study2); - - List rootNetworksStudy2 = studyService.getExistingBasicRootNetworkInfos(study2.getId()); - NodeEntity rootNode2 = networkModificationTreeService.createRoot(study2); - - Mockito.doReturn(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP) - .when(networkModificationService) - .duplicateModificationsGroup(any(), any()); - - UUID duplicatedNodeUuid = networkModificationTreeService.duplicateStudyNode( - node1.getId(), rootNode2.getIdNode(), InsertMode.AFTER - ); - - // -------- Assertions -------- - // For common root network -> modifications should be copied - BasicRootNetworkInfos commonRnStudy2 = rootNetworksStudy2.stream() - .filter(rn -> "ok".equals(rn.tag())) - .findFirst() - .orElseThrow(); - RootNetworkNodeInfoEntity commonRnNodeInfoStudy2 = rootNetworkNodeInfoRepository - .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(duplicatedNodeUuid, commonRnStudy2.rootNetworkUuid()) - .orElseThrow(); - Set expectedExcluded = MODIFICATIONS_TO_EXCLUDE_RN_1.stream() - .map(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP::get) - .collect(Collectors.toSet()); - assertEquals(expectedExcluded, commonRnNodeInfoStudy2.getModificationsUuidsToExclude(), - "Modifications to exclude should be copied for common root network"); - - // unique root network -> modifications should be empty - BasicRootNetworkInfos uniqueRnStudy2 = rootNetworksStudy2.stream() - .filter(rn -> "no2".equals(rn.tag())) - .findFirst() - .orElseThrow(); - RootNetworkNodeInfoEntity uniqueRnNodeInfoStudy2 = rootNetworkNodeInfoRepository - .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(duplicatedNodeUuid, uniqueRnStudy2.rootNetworkUuid()) - .orElseThrow(); - assertTrue(uniqueRnNodeInfoStudy2.getModificationsUuidsToExclude().isEmpty(), - "Modifications to exclude should be empty for non-common root network"); - } - @Test void testDuplicateNodeWithModificationsToExclude() { // create study with two root networks @@ -354,6 +284,129 @@ void testGetModificationsToExclude() throws Exception { assertEquals(excludedNetworkModifications.get(1).modificationUuidsToExclude(), rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(firstNode.getId(), rootNetworkBasicInfos.get(1).rootNetworkUuid()).orElseThrow().getModificationsUuidsToExclude()); } + @Test + void testDuplicateModificationBetweenStudiesWithCommonRootNetworkTag() { + // -------- STUDY 1 -------- + StudyEntity study1 = TestUtils.createDummyStudy( + NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID + ); + + createDummyRootNetwork(study1, "rnCommon", "COM"); + createDummyRootNetwork(study1, "rnOnly1", "O1"); + + studyRepository.save(study1); + + List rnStudy1 = + studyService.getExistingBasicRootNetworkInfos(study1.getId()); + + NodeEntity root1 = networkModificationTreeService.createRoot(study1); + NetworkModificationNode node1 = + networkModificationTreeService.createNode( + study1, root1.getIdNode(), + createModificationNodeInfo(NODE_1_NAME), + InsertMode.AFTER, null + ); + + // Put exclusions ONLY on COMMON root network + UUID commonRn1 = rnStudy1.stream() + .filter(r -> "COM".equals(r.tag())) + .findFirst().orElseThrow() + .rootNetworkUuid(); + + RootNetworkNodeInfoEntity rnInfo1 = + rootNetworkNodeInfoRepository + .findByNodeInfoIdAndRootNetworkId(node1.getId(), commonRn1) + .orElseThrow(); + + rnInfo1.setModificationsUuidsToExclude(MODIFICATIONS_TO_EXCLUDE_RN_1); + rootNetworkNodeInfoRepository.save(rnInfo1); + + // -------- STUDY 2 -------- + + StudyEntity study2 = TestUtils.createDummyStudy( + NETWORK_UUID2, CASE_UUID2, CASE_NAME2, CASE_FORMAT2, REPORT_UUID2 + ); + + createDummyRootNetwork(study2, "rnOther", "OTH"); + createDummyRootNetwork(study2, "rnCommon", "COM"); + + studyRepository.save(study2); + + List rnStudy2 = + studyService.getExistingBasicRootNetworkInfos(study2.getId()); + NodeEntity root2 = networkModificationTreeService.createRoot(study2); + NetworkModificationNode node2 = + networkModificationTreeService.createNode( + study2, root2.getIdNode(), + createModificationNodeInfo(NODE_2_NAME), + InsertMode.AFTER, null + ); + + // -------- MOCK DUPLICATION RESULT -------- + List modifications = List.of( + ModificationsToCopyInfos.builder().uuid(MODIFICATION_TO_EXCLUDE_1).build(), + ModificationsToCopyInfos.builder().uuid(MODIFICATION_TO_EXCLUDE_2).build() + ); + + Mockito.doReturn( + new NetworkModificationsResult( + modifications.stream() + .map(m -> ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP.get(m.getUuid())) + .toList(), + List.of() + ) + ).when(networkModificationService) + .duplicateOrInsertModifications(any(), any(), any()); + // -------- EXECUTE -------- + studyService.duplicateOrInsertNetworkModifications( + study2.getId(), + node2.getId(), + study1.getId(), + node1.getId(), + modifications, + USER_ID, + StudyConstants.ModificationsActionType.COPY + ); + // -------- ASSERT COMMON ROOT NETWORK -------- + UUID commonRn2 = rnStudy2.stream() + .filter(r -> "COM".equals(r.tag())) + .findFirst().orElseThrow() + .rootNetworkUuid(); + + RootNetworkNodeInfoEntity resultCommon = + rootNetworkNodeInfoRepository + .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( + node2.getId(), commonRn2 + ).orElseThrow(); + + Set duplicatedUuids = modifications.stream() + .map(ModificationsToCopyInfos::getUuid) + .map(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP::get) + .collect(Collectors.toSet()); + + Set expected = MODIFICATIONS_TO_EXCLUDE_RN_1.stream() + .map(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP::get) + .filter(duplicatedUuids::contains) + .collect(Collectors.toSet()); + + assertEquals(expected, resultCommon.getModificationsUuidsToExclude()); + + // -------- ASSERT NON-COMMON ROOT NETWORK -------- + + UUID otherRn2 = rnStudy2.stream() + .filter(r -> "OTH".equals(r.tag())) + .findFirst().orElseThrow() + .rootNetworkUuid(); + + RootNetworkNodeInfoEntity resultOther = + rootNetworkNodeInfoRepository + .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( + node2.getId(), otherRn2 + ).orElseThrow(); + + assertTrue(resultOther.getModificationsUuidsToExclude().isEmpty()); + } + @Test void testDuplicateModificationWithModificationsToExclude() { // create study with two root networks From bb598b017f5074a435521719b3d38670263282b1 Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Mon, 23 Feb 2026 09:36:12 +0100 Subject: [PATCH 4/7] clean Signed-off-by: SOUISSI Maissa (Externe) --- .../service/RootNetworkNodeInfoService.java | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java b/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java index 2daa42b8d..edd43c242 100644 --- a/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java +++ b/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java @@ -132,37 +132,6 @@ private List getCommonRootNetworks(@NonNull UUID sourceStudyU .toList(); } - // create links for root networks with common tags - public void createNodeLinksWithCommonTag( - @NonNull NetworkModificationNodeInfoEntity targetNodeInfoEntity, - @NonNull NetworkModificationNodeInfoEntity sourceNodeInfoEntity, - @NonNull Map originToDuplicateModificationUuidMap, - @NonNull Map targetRootNetworksByTag) { - - // Map source node links by root network tag - Map sourceNodeLinksByTag = sourceNodeInfoEntity.getRootNetworkNodeInfos().stream() - .filter(rn -> rn.getRootNetwork().getTag() != null) - .collect(Collectors.toMap(rn -> rn.getRootNetwork().getTag(), rn -> rn)); - - targetRootNetworksByTag.forEach((tag, targetRN) -> { - RootNetworkNodeInfoEntity sourceNodeLink = sourceNodeLinksByTag.get(tag); - Set modificationsToExclude = Collections.emptySet(); - - if (sourceNodeLink != null) { - // Map modifications to the duplicated UUIDs - modificationsToExclude = sourceNodeLink.getModificationsUuidsToExclude().stream() - .map(originToDuplicateModificationUuidMap::get) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - } - - // Create a new RootNetworkNodeInfoEntity for the target - RootNetworkNodeInfoEntity newRNNodeInfo = createDefaultEntity(targetNodeInfoEntity.getId(), modificationsToExclude); - - addLink(targetNodeInfoEntity, targetRN, newRNNodeInfo); - }); - } - public void duplicateNodeLinks(List sourceNodeLinks, @NonNull NetworkModificationNodeInfoEntity destinationNodeInfoEntity, Map originToDuplicateModificationUuidMap, Map originToDuplicateRootNetworkMap) { // For each root network create a link with the node sourceNodeLinks.forEach(nodeLink -> { From fb8571ca6e3b6840e873ab21bc44ebc9cb51d6d9 Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Mon, 23 Feb 2026 11:51:59 +0100 Subject: [PATCH 5/7] copy node with wapplicability modifications for common root network tag Signed-off-by: SOUISSI Maissa (Externe) --- .../NetworkModificationTreeService.java | 13 +- .../service/RootNetworkNodeInfoService.java | 40 +++ .../ModificationToExcludeTest.java | 250 +++++++++++++++--- 3 files changed, 264 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java b/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java index 61fa775b0..04c23f56b 100644 --- a/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java +++ b/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java @@ -155,15 +155,16 @@ private NetworkModificationNode duplicateNode(@NonNull StudyEntity study, @NonNu NetworkModificationNodeInfoEntity newNodeInfoEntity = networkModificationNodeInfoRepository.getReferenceById(newNode.getId()); NetworkModificationNodeInfoEntity originNodeInfoEntity = networkModificationNodeInfoRepository.getReferenceById(originNodeUuid); if (!isDuplicatingStudy && study.getId() != sourceStudy.getId()) { - rootNetworkNodeInfoService.createNodeLinks(study, newNodeInfoEntity); + rootNetworkNodeInfoService.createNodeLinksFromSourceForCommonTags( + sourceStudy, + study, originNodeInfoEntity.getRootNetworkNodeInfos(), + newNodeInfoEntity, + originToDuplicateModificationUuidMap + ); } else { - // when duplicating node within the same study, we need to retrieve excluded modifications from source node - // when duplicating study, we need to retrieve excluded modifications from source node as well, but we also need to have a correspondence between source root networks and duplicated ones - // since they are fetched in order, we ensure the duplicate is made accurately Map originToDuplicateRootNetworkMap = new HashMap<>(); for (int i = 0; i < sourceStudy.getRootNetworks().size(); i++) { - // when study.getId() == sourceStudy.getId(), this mapping makes root networks target themselves, but it makes the code more concise with study duplication - originToDuplicateRootNetworkMap.put(sourceStudy.getRootNetworks().get(i), study.getRootNetworks().get(i)); + originToDuplicateRootNetworkMap.put(sourceStudy.getRootNetworks().get(i), study.getRootNetworks().get(i)); } rootNetworkNodeInfoService.duplicateNodeLinks(originNodeInfoEntity.getRootNetworkNodeInfos(), newNodeInfoEntity, originToDuplicateModificationUuidMap, originToDuplicateRootNetworkMap); } diff --git a/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java b/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java index edd43c242..9f4aa0814 100644 --- a/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java +++ b/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java @@ -145,6 +145,46 @@ public void duplicateNodeLinks(List sourceNodeLinks, }); } + public void createNodeLinksFromSourceForCommonTags( + @NonNull StudyEntity sourceStudy, + @NonNull StudyEntity destinationStudy, + @NonNull List sourceNodeLinks, + @NonNull NetworkModificationNodeInfoEntity destinationNodeInfoEntity, + @NonNull Map originToDuplicateModificationUuidMap) { + + // Map tag → link source + Map sourceLinkByTag = + sourceNodeLinks.stream() + .filter(l -> l.getRootNetwork().getTag() != null) + .collect(Collectors.toMap( + l -> l.getRootNetwork().getTag(), + Function.identity(), + (a, b) -> a + )); + + // Pour chaque root network destination + destinationStudy.getRootNetworks().forEach(destRoot -> { + String tag = destRoot.getTag(); + + // Si tag commun, copier les exclusions + Set exclusions; + if (tag != null && sourceLinkByTag.containsKey(tag)) { + // root network commun → copier exclusions + exclusions = sourceLinkByTag.get(tag).getModificationsUuidsToExclude() + .stream() + .map(originToDuplicateModificationUuidMap::get) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } else { + // root network unique → toutes modifications actives + exclusions = Collections.emptySet(); + } + + RootNetworkNodeInfoEntity entity = createDefaultEntity(destinationNodeInfoEntity.getId(), exclusions); + addLink(destinationNodeInfoEntity, destRoot, entity); + }); + } + private static RootNetworkNodeInfoEntity createDefaultEntity(UUID nodeUuid) { return createDefaultEntity(nodeUuid, new HashSet<>()); } diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index 7bcbc6b95..e0435d2ef 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -202,48 +202,232 @@ void testDuplicateNodeWithModificationsToExclude() { } @Test - void testDuplicateNodeBetweenStudiesWithModificationsToExclude() { - // create study with two root networks - StudyEntity studyEntity = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); - createDummyRootNetwork(studyEntity, "secondRootNetwork"); - studyRepository.save(studyEntity); - List rootNetworkBasicInfos = studyService.getExistingBasicRootNetworkInfos(studyEntity.getId()); + void testDuplicateNodeWithCommonAndUniqueRootNetworksBetweenStudies() { + // 1️⃣ Créer étude source S1 + StudyEntity studyS1 = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); + createDummyRootNetwork(studyS1, "S1RN1", "COM"); // root network commun + createDummyRootNetwork(studyS1, "S1RN2", "RNS"); // unique source + studyRepository.save(studyS1); + + List s1RootInfos = studyService.getExistingBasicRootNetworkInfos(studyS1.getId()); +// Supposons que les modifications du node soient A, B, C + UUID modA = UUID.randomUUID(); + UUID modB = UUID.randomUUID(); + UUID modC = UUID.randomUUID(); + +// Pour le root network COM, on veut que B et C soient exclus + Set modificationsToExcludeCom = Set.of(modB, modC); + +// Pour le root network RNS, toutes les modifications sont exclues + Set modificationsToExcludeRnq = Set.of(modA, modB, modC); + +// Optionnel : mapping mock pour duplication + Map originToDuplicateModificationUuidMap = Map.of( + modA, UUID.randomUUID(), + modB, UUID.randomUUID(), + modC, UUID.randomUUID() + ); + // 2️⃣ Créer node source N1 avec modifications A, B, C + NodeEntity rootNodeS1 = networkModificationTreeService.createRoot(studyS1); + NetworkModificationNode nodeN1 = networkModificationTreeService.createNode( + studyS1, rootNodeS1.getIdNode(), createModificationNodeInfo("N1"), InsertMode.AFTER, null); + + // Définir exclusions sur root networks source + RootNetworkNodeInfoEntity rnComS1 = rootNetworkNodeInfoRepository + .findByNodeInfoIdAndRootNetworkId(nodeN1.getId(), s1RootInfos.get(1).rootNetworkUuid()) + .orElseThrow(); + rnComS1.setModificationsUuidsToExclude(modificationsToExcludeCom); // ex: B, C + rootNetworkNodeInfoRepository.save(rnComS1); + + RootNetworkNodeInfoEntity rnRnsS1 = rootNetworkNodeInfoRepository + .findByNodeInfoIdAndRootNetworkId(nodeN1.getId(), s1RootInfos.get(2).rootNetworkUuid()) + .orElseThrow(); + rnRnsS1.setModificationsUuidsToExclude(modificationsToExcludeRnq); // ex: A, B, C + rootNetworkNodeInfoRepository.save(rnRnsS1); + + // 3️⃣ Créer étude cible S2 + StudyEntity studyS2 = TestUtils.createDummyStudy(NETWORK_UUID2, CASE_UUID2, CASE_NAME2, CASE_FORMAT2, REPORT_UUID2); + createDummyRootNetwork(studyS2, "S2RN1", "COM"); // commun + createDummyRootNetwork(studyS2, "S2RN2", "RNT"); // unique cible + studyRepository.save(studyS2); + + List s2RootInfos = studyService.getExistingBasicRootNetworkInfos(studyS2.getId()); + NodeEntity rootNodeS2 = networkModificationTreeService.createRoot(studyS2); + + // 4️⃣ Mock duplications des UUID + Mockito.doReturn(originToDuplicateModificationUuidMap) + .when(networkModificationService) + .duplicateModificationsGroup(any(), any()); + + // 5️⃣ Dupliquer node N1 de S1 vers S2 + UUID duplicateNodeUuid = networkModificationTreeService.duplicateStudyNode( + nodeN1.getId(), rootNodeS2.getIdNode(), InsertMode.AFTER); + + // 6️⃣ Vérifier root network commun (COM) → exclusions copiées + RootNetworkNodeInfoEntity rnComS2 = rootNetworkNodeInfoRepository + .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( + duplicateNodeUuid, s2RootInfos.stream().filter(rn -> "COM".equals(rn.tag())).toList().getFirst().rootNetworkUuid()) + .orElseThrow(); + assertEquals( + modificationsToExcludeCom.stream() + .map(originToDuplicateModificationUuidMap::get) + .collect(Collectors.toSet()), + rnComS2.getModificationsUuidsToExclude(), + "Exclusions doivent être copiées pour le root network commun" + ); - // create root node and a network modification node - it will create a RootNetworkNodeInfoEntity for each root network - NodeEntity rootNode = networkModificationTreeService.createRoot(studyEntity); - NetworkModificationNode firstNode = networkModificationTreeService.createNode(studyEntity, rootNode.getIdNode(), createModificationNodeInfo(NODE_1_NAME), InsertMode.AFTER, null); + // 7️⃣ Vérifier root network unique cible (RNT) → toutes les modifs actives + RootNetworkNodeInfoEntity rnRntS2 = rootNetworkNodeInfoRepository + .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( + duplicateNodeUuid, s2RootInfos.stream().filter(rn -> "RNT".equals(rn.tag())).toList().getFirst().rootNetworkUuid()) + .orElseThrow(); + assertTrue(rnRntS2.getModificationsUuidsToExclude().isEmpty(), + "Root network unique cible (RNT) ne devrait pas avoir des modifications to exclude"); + } - // for each RootNetworkNodeInfoEntity, set some modifications to exclude - RootNetworkNodeInfoEntity rootNetworkNodeInfoEntity1 = rootNetworkNodeInfoRepository.findByNodeInfoIdAndRootNetworkId(firstNode.getId(), rootNetworkBasicInfos.getFirst().rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); - rootNetworkNodeInfoEntity1.setModificationsUuidsToExclude(MODIFICATIONS_TO_EXCLUDE_RN_1); - rootNetworkNodeInfoRepository.save(rootNetworkNodeInfoEntity1); + @Test + void testDuplicateNodeBetweenStudiesWithModificationsToExcludeForCommonTag() { + // -------- Study 1 -------- + StudyEntity study1 = TestUtils.createDummyStudy( + NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID + ); + createDummyRootNetwork(study1, "rootNetwork1", "X"); // RN1 with tag X + createDummyRootNetwork(study1, "rootNetwork2", "Y"); // RN2 with tag Y + studyRepository.save(study1); - RootNetworkNodeInfoEntity rootNetworkNodeInfoEntity2 = rootNetworkNodeInfoRepository.findByNodeInfoIdAndRootNetworkId(firstNode.getId(), rootNetworkBasicInfos.get(1).rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); - rootNetworkNodeInfoEntity2.setModificationsUuidsToExclude(MODIFICATIONS_TO_EXCLUDE_RN_2); - rootNetworkNodeInfoRepository.save(rootNetworkNodeInfoEntity2); + List rootNetworksStudy1 = studyService.getExistingBasicRootNetworkInfos(study1.getId()); - // create another study, the target of the node duplication - StudyEntity studyEntity2 = TestUtils.createDummyStudy(NETWORK_UUID2, CASE_UUID2, CASE_NAME2, CASE_FORMAT2, REPORT_UUID2); - createDummyRootNetwork(studyEntity2, "secondRootNetwork2"); - studyRepository.save(studyEntity2); - List rootNetworkBasicInfos2 = studyService.getExistingBasicRootNetworkInfos(studyEntity2.getId()); + NodeEntity rootNodeS1 = networkModificationTreeService.createRoot(study1); + NetworkModificationNode nodeS1 = networkModificationTreeService.createNode(study1, rootNodeS1.getIdNode(), createModificationNodeInfo(NODE_1_NAME), InsertMode.AFTER, null); - // create root node for this new study - NodeEntity rootNode2 = networkModificationTreeService.createRoot(studyEntity2); + UUID modificationOrigineUuid = MODIFICATION_TO_EXCLUDE_1; - // mock duplicateModificationsGroup to return a mapping between origin modification uuid and their duplicate uuid - Mockito.doReturn(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP).when(networkModificationService).duplicateModificationsGroup(any(), any()); + // Exclude Modifications for RN2 (tag Y) + BasicRootNetworkInfos rnYStudy1 = rootNetworksStudy1.stream() + .filter(rn -> "Y".equals(rn.tag())) + .findFirst() + .orElseThrow(); - // run duplication - UUID duplicateNodeUuid = networkModificationTreeService.duplicateStudyNode(firstNode.getId(), rootNode2.getIdNode(), InsertMode.AFTER); + RootNetworkNodeInfoEntity rnInfoStudy1 = rootNetworkNodeInfoRepository + .findByNodeInfoIdAndRootNetworkId(nodeS1.getId(), rnYStudy1.rootNetworkUuid()) + .orElseThrow(); + + rnInfoStudy1.setModificationsUuidsToExclude(Set.of(modificationOrigineUuid)); + rootNetworkNodeInfoRepository.save(rnInfoStudy1); + + // -------- Study 2 -------- + StudyEntity study2 = TestUtils.createDummyStudy( + NETWORK_UUID2, CASE_UUID2, CASE_NAME2, CASE_FORMAT2, REPORT_UUID2 + ); + createDummyRootNetwork(study2, "rootNetwork1_2", "X"); // RN1 with tag X + createDummyRootNetwork(study2, "rootNetwork2_2", "Y"); // RN2 with tag Y + studyRepository.save(study2); + + List rootNetworksStudy2 = + studyService.getExistingBasicRootNetworkInfos(study2.getId()); + + NodeEntity rootNodeS2 = networkModificationTreeService.createRoot(study2); + + Mockito.doReturn(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP) + .when(networkModificationService) + .duplicateModificationsGroup(any(), any()); + + // -------- Duplicate node -------- + UUID duplicatedNodeUuid = networkModificationTreeService.duplicateStudyNode(nodeS1.getId(), rootNodeS2.getIdNode(), InsertMode.AFTER); + + UUID modificationDupliqueeUuid = ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP.get(modificationOrigineUuid); + + // -------- Assertions -------- + + // RN2 tag Y (common) -> modification EXCLUDED + BasicRootNetworkInfos rnYStudy2 = rootNetworksStudy2.stream() + .filter(rn -> "Y".equals(rn.tag())) + .findFirst() + .orElseThrow(); + + RootNetworkNodeInfoEntity rnYInfoStudy2 = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(duplicatedNodeUuid, rnYStudy2.rootNetworkUuid()).orElseThrow(); + + assertTrue(rnYInfoStudy2.getModificationsUuidsToExclude().contains(modificationDupliqueeUuid), + "Duplicated Modification should be excluded on RN2 (tag Y)"); + + // RN1 tag X -> modification active (not excluded) + BasicRootNetworkInfos rnXStudy2 = rootNetworksStudy2.stream() + .filter(rn -> "X".equals(rn.tag())) + .findFirst() + .orElseThrow(); + + RootNetworkNodeInfoEntity rnXInfoStudy2 = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(duplicatedNodeUuid, rnXStudy2.rootNetworkUuid()).orElseThrow(); + + assertTrue(rnXInfoStudy2.getModificationsUuidsToExclude().isEmpty(), "Modification is active on RN1 (tag X)"); + } + + + @Test + void testDuplicateNodeBetweenStudies() { + // 1️⃣ Créer étude source avec deux root networks + StudyEntity sourceStudy = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); + createDummyRootNetwork(sourceStudy, "RN_COMMON"); + createDummyRootNetwork(sourceStudy, "RN_ONLY_SOURCE"); + studyRepository.save(sourceStudy); + + List sourceRootInfos = studyService.getExistingBasicRootNetworkInfos(sourceStudy.getId()); + + // 2️⃣ Créer node source + NodeEntity rootNode = networkModificationTreeService.createRoot(sourceStudy); + NetworkModificationNode sourceNode = networkModificationTreeService.createNode( + sourceStudy, rootNode.getIdNode(), createModificationNodeInfo(NODE_1_NAME), InsertMode.AFTER, null + ); + + // définir exclusions sur source + RootNetworkNodeInfoEntity rnCommonSource = rootNetworkNodeInfoRepository + .findByNodeInfoIdAndRootNetworkId(sourceNode.getId(), sourceRootInfos.getFirst().rootNetworkUuid()) + .orElseThrow(); + rnCommonSource.setModificationsUuidsToExclude(MODIFICATIONS_TO_EXCLUDE_RN_1); + rootNetworkNodeInfoRepository.save(rnCommonSource); + + RootNetworkNodeInfoEntity rnOnlySource = rootNetworkNodeInfoRepository + .findByNodeInfoIdAndRootNetworkId(sourceNode.getId(), sourceRootInfos.get(1).rootNetworkUuid()) + .orElseThrow(); + rnOnlySource.setModificationsUuidsToExclude(MODIFICATIONS_TO_EXCLUDE_RN_2); + rootNetworkNodeInfoRepository.save(rnOnlySource); + + // 3️⃣ Créer étude cible avec un root network commun + un root network uniquement cible + StudyEntity targetStudy = TestUtils.createDummyStudy(NETWORK_UUID2, CASE_UUID2, CASE_NAME2, CASE_FORMAT2, REPORT_UUID2); + createDummyRootNetwork(targetStudy, "RN_COMMON"); + createDummyRootNetwork(targetStudy, "RN_ONLY_TARGET"); + studyRepository.save(targetStudy); + + List targetRootInfos = studyService.getExistingBasicRootNetworkInfos(targetStudy.getId()); + NodeEntity targetRootNode = networkModificationTreeService.createRoot(targetStudy); + + // 4️⃣ Mock duplications des UUID + Mockito.doReturn(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP) + .when(networkModificationService) + .duplicateModificationsGroup(any(), any()); + + // 5️⃣ Dupliquer le nœud source vers targetStudy + UUID duplicateNodeUuid = networkModificationTreeService.duplicateStudyNode( + sourceNode.getId(), targetRootNode.getIdNode(), InsertMode.AFTER + ); + + // 6️⃣ Vérifier root networks communs + RootNetworkNodeInfoEntity rnCommonTarget = rootNetworkNodeInfoRepository + .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( + duplicateNodeUuid, targetRootInfos.get(0).rootNetworkUuid() + ).orElseThrow(); + + assertEquals( + MODIFICATIONS_TO_EXCLUDE_RN_1.stream().map(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP::get).collect(Collectors.toSet()), + rnCommonTarget.getModificationsUuidsToExclude() + ); - // get RootNetworkNodeInfoEntity of newly duplicated node - RootNetworkNodeInfoEntity duplicateRootNetworkNodeInfoEntity1 = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(duplicateNodeUuid, rootNetworkBasicInfos2.get(0).rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); - RootNetworkNodeInfoEntity duplicateRootNetworkNodeInfoEntity2 = rootNetworkNodeInfoRepository.findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId(duplicateNodeUuid, rootNetworkBasicInfos2.get(1).rootNetworkUuid()).orElseThrow(() -> new StudyException(NOT_FOUND, "Root network not found")); + // 7️⃣ Vérifier root networks uniquement dans cible → toutes actives + RootNetworkNodeInfoEntity rnOnlyTarget = rootNetworkNodeInfoRepository + .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( + duplicateNodeUuid, targetRootInfos.get(1).rootNetworkUuid() + ).orElseThrow(); - // assert those values are empty, duplicating node between studies should empty modifications to exclude - assertTrue(duplicateRootNetworkNodeInfoEntity1.getModificationsUuidsToExclude().isEmpty()); - assertTrue(duplicateRootNetworkNodeInfoEntity2.getModificationsUuidsToExclude().isEmpty()); + assertTrue(rnOnlyTarget.getModificationsUuidsToExclude().isEmpty()); } @Test From 543471b2bfd6ad384e6c1e14a2a6a5fa48056dde Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Mon, 23 Feb 2026 12:03:08 +0100 Subject: [PATCH 6/7] checkstyles Signed-off-by: SOUISSI Maissa (Externe) --- .../service/NetworkModificationTreeService.java | 15 +++++---------- .../service/RootNetworkNodeInfoService.java | 15 ++------------- .../rootnetworks/ModificationToExcludeTest.java | 17 ++++++++--------- 3 files changed, 15 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java b/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java index 04c23f56b..7b6eb4749 100644 --- a/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java +++ b/src/main/java/org/gridsuite/study/server/service/NetworkModificationTreeService.java @@ -148,23 +148,18 @@ public NetworkModificationNode createNode(@NonNull StudyEntity study, @NonNull U return newNode; } - private NetworkModificationNode duplicateNode(@NonNull StudyEntity study, @NonNull StudyEntity sourceStudy, @NonNull UUID referenceNodeId, @NonNull NetworkModificationNode newNodeInfo, @NonNull UUID originNodeUuid, @NonNull InsertMode insertMode, Map originToDuplicateModificationUuidMap, boolean isDuplicatingStudy) { + private NetworkModificationNode duplicateNode(@NonNull StudyEntity targetStudy, @NonNull StudyEntity sourceStudy, @NonNull UUID referenceNodeId, @NonNull NetworkModificationNode newNodeInfo, @NonNull UUID originNodeUuid, @NonNull InsertMode insertMode, Map originToDuplicateModificationUuidMap, boolean isDuplicatingStudy) { // create new node - NetworkModificationNode newNode = createAndInsertNode(study, referenceNodeId, newNodeInfo, insertMode, null); + NetworkModificationNode newNode = createAndInsertNode(targetStudy, referenceNodeId, newNodeInfo, insertMode, null); NetworkModificationNodeInfoEntity newNodeInfoEntity = networkModificationNodeInfoRepository.getReferenceById(newNode.getId()); NetworkModificationNodeInfoEntity originNodeInfoEntity = networkModificationNodeInfoRepository.getReferenceById(originNodeUuid); - if (!isDuplicatingStudy && study.getId() != sourceStudy.getId()) { - rootNetworkNodeInfoService.createNodeLinksFromSourceForCommonTags( - sourceStudy, - study, originNodeInfoEntity.getRootNetworkNodeInfos(), - newNodeInfoEntity, - originToDuplicateModificationUuidMap - ); + if (!isDuplicatingStudy && targetStudy.getId() != sourceStudy.getId()) { + rootNetworkNodeInfoService.createNodeLinksFromSourceForCommonTags(targetStudy, originNodeInfoEntity.getRootNetworkNodeInfos(), newNodeInfoEntity, originToDuplicateModificationUuidMap); } else { Map originToDuplicateRootNetworkMap = new HashMap<>(); for (int i = 0; i < sourceStudy.getRootNetworks().size(); i++) { - originToDuplicateRootNetworkMap.put(sourceStudy.getRootNetworks().get(i), study.getRootNetworks().get(i)); + originToDuplicateRootNetworkMap.put(sourceStudy.getRootNetworks().get(i), targetStudy.getRootNetworks().get(i)); } rootNetworkNodeInfoService.duplicateNodeLinks(originNodeInfoEntity.getRootNetworkNodeInfos(), newNodeInfoEntity, originToDuplicateModificationUuidMap, originToDuplicateRootNetworkMap); } diff --git a/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java b/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java index 9f4aa0814..1451bb722 100644 --- a/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java +++ b/src/main/java/org/gridsuite/study/server/service/RootNetworkNodeInfoService.java @@ -145,13 +145,7 @@ public void duplicateNodeLinks(List sourceNodeLinks, }); } - public void createNodeLinksFromSourceForCommonTags( - @NonNull StudyEntity sourceStudy, - @NonNull StudyEntity destinationStudy, - @NonNull List sourceNodeLinks, - @NonNull NetworkModificationNodeInfoEntity destinationNodeInfoEntity, - @NonNull Map originToDuplicateModificationUuidMap) { - + public void createNodeLinksFromSourceForCommonTags(@NonNull StudyEntity destinationStudy, @NonNull List sourceNodeLinks, @NonNull NetworkModificationNodeInfoEntity destinationNodeInfoEntity, @NonNull Map originToDuplicateModificationUuidMap) { // Map tag → link source Map sourceLinkByTag = sourceNodeLinks.stream() @@ -162,24 +156,19 @@ public void createNodeLinksFromSourceForCommonTags( (a, b) -> a )); - // Pour chaque root network destination destinationStudy.getRootNetworks().forEach(destRoot -> { String tag = destRoot.getTag(); - - // Si tag commun, copier les exclusions + // copy exclusions only for common root networks tag Set exclusions; if (tag != null && sourceLinkByTag.containsKey(tag)) { - // root network commun → copier exclusions exclusions = sourceLinkByTag.get(tag).getModificationsUuidsToExclude() .stream() .map(originToDuplicateModificationUuidMap::get) .filter(Objects::nonNull) .collect(Collectors.toSet()); } else { - // root network unique → toutes modifications actives exclusions = Collections.emptySet(); } - RootNetworkNodeInfoEntity entity = createDefaultEntity(destinationNodeInfoEntity.getId(), exclusions); addLink(destinationNodeInfoEntity, destRoot, entity); }); diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index e0435d2ef..ed40f21b4 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -361,10 +361,9 @@ void testDuplicateNodeBetweenStudiesWithModificationsToExcludeForCommonTag() { assertTrue(rnXInfoStudy2.getModificationsUuidsToExclude().isEmpty(), "Modification is active on RN1 (tag X)"); } - @Test void testDuplicateNodeBetweenStudies() { - // 1️⃣ Créer étude source avec deux root networks + // create Study 1 StudyEntity sourceStudy = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); createDummyRootNetwork(sourceStudy, "RN_COMMON"); createDummyRootNetwork(sourceStudy, "RN_ONLY_SOURCE"); @@ -372,13 +371,13 @@ void testDuplicateNodeBetweenStudies() { List sourceRootInfos = studyService.getExistingBasicRootNetworkInfos(sourceStudy.getId()); - // 2️⃣ Créer node source + // Create nodes on study 1 NodeEntity rootNode = networkModificationTreeService.createRoot(sourceStudy); NetworkModificationNode sourceNode = networkModificationTreeService.createNode( sourceStudy, rootNode.getIdNode(), createModificationNodeInfo(NODE_1_NAME), InsertMode.AFTER, null ); - // définir exclusions sur source + // excluded modifications RootNetworkNodeInfoEntity rnCommonSource = rootNetworkNodeInfoRepository .findByNodeInfoIdAndRootNetworkId(sourceNode.getId(), sourceRootInfos.getFirst().rootNetworkUuid()) .orElseThrow(); @@ -391,7 +390,7 @@ void testDuplicateNodeBetweenStudies() { rnOnlySource.setModificationsUuidsToExclude(MODIFICATIONS_TO_EXCLUDE_RN_2); rootNetworkNodeInfoRepository.save(rnOnlySource); - // 3️⃣ Créer étude cible avec un root network commun + un root network uniquement cible + // Study 2 StudyEntity targetStudy = TestUtils.createDummyStudy(NETWORK_UUID2, CASE_UUID2, CASE_NAME2, CASE_FORMAT2, REPORT_UUID2); createDummyRootNetwork(targetStudy, "RN_COMMON"); createDummyRootNetwork(targetStudy, "RN_ONLY_TARGET"); @@ -400,17 +399,17 @@ void testDuplicateNodeBetweenStudies() { List targetRootInfos = studyService.getExistingBasicRootNetworkInfos(targetStudy.getId()); NodeEntity targetRootNode = networkModificationTreeService.createRoot(targetStudy); - // 4️⃣ Mock duplications des UUID + // 4️⃣ Mock UUID duplications Mockito.doReturn(ORIGIN_TO_DUPLICATE_MODIFICATION_UUID_MAP) .when(networkModificationService) .duplicateModificationsGroup(any(), any()); - // 5️⃣ Dupliquer le nœud source vers targetStudy + // duplicate node from source to target study UUID duplicateNodeUuid = networkModificationTreeService.duplicateStudyNode( sourceNode.getId(), targetRootNode.getIdNode(), InsertMode.AFTER ); - // 6️⃣ Vérifier root networks communs + // get root networks with common tag RootNetworkNodeInfoEntity rnCommonTarget = rootNetworkNodeInfoRepository .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( duplicateNodeUuid, targetRootInfos.get(0).rootNetworkUuid() @@ -421,7 +420,7 @@ void testDuplicateNodeBetweenStudies() { rnCommonTarget.getModificationsUuidsToExclude() ); - // 7️⃣ Vérifier root networks uniquement dans cible → toutes actives + // check that exclusions is ignored for non common root networks RootNetworkNodeInfoEntity rnOnlyTarget = rootNetworkNodeInfoRepository .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( duplicateNodeUuid, targetRootInfos.get(1).rootNetworkUuid() From 9c8106f7b6760416c5b1761629ee6c5ee34e14b5 Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Mon, 23 Feb 2026 12:32:13 +0100 Subject: [PATCH 7/7] fix Signed-off-by: SOUISSI Maissa (Externe) --- .../ModificationToExcludeTest.java | 43 ++++++++----------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java index ed40f21b4..12d0e062b 100644 --- a/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java +++ b/src/test/java/org/gridsuite/study/server/rootnetworks/ModificationToExcludeTest.java @@ -203,36 +203,29 @@ void testDuplicateNodeWithModificationsToExclude() { @Test void testDuplicateNodeWithCommonAndUniqueRootNetworksBetweenStudies() { - // 1️⃣ Créer étude source S1 - StudyEntity studyS1 = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); - createDummyRootNetwork(studyS1, "S1RN1", "COM"); // root network commun - createDummyRootNetwork(studyS1, "S1RN2", "RNS"); // unique source - studyRepository.save(studyS1); - - List s1RootInfos = studyService.getExistingBasicRootNetworkInfos(studyS1.getId()); -// Supposons que les modifications du node soient A, B, C UUID modA = UUID.randomUUID(); UUID modB = UUID.randomUUID(); UUID modC = UUID.randomUUID(); - -// Pour le root network COM, on veut que B et C soient exclus Set modificationsToExcludeCom = Set.of(modB, modC); - -// Pour le root network RNS, toutes les modifications sont exclues Set modificationsToExcludeRnq = Set.of(modA, modB, modC); - -// Optionnel : mapping mock pour duplication Map originToDuplicateModificationUuidMap = Map.of( modA, UUID.randomUUID(), modB, UUID.randomUUID(), modC, UUID.randomUUID() ); - // 2️⃣ Créer node source N1 avec modifications A, B, C + // Create study S1 + StudyEntity studyS1 = TestUtils.createDummyStudy(NETWORK_UUID, CASE_UUID, CASE_NAME, CASE_FORMAT, REPORT_UUID); + createDummyRootNetwork(studyS1, "S1RN1", "COM"); + createDummyRootNetwork(studyS1, "S1RN2", "RNS"); + studyRepository.save(studyS1); + List s1RootInfos = studyService.getExistingBasicRootNetworkInfos(studyS1.getId()); + + // Create node N1 with modifications A, B, C NodeEntity rootNodeS1 = networkModificationTreeService.createRoot(studyS1); NetworkModificationNode nodeN1 = networkModificationTreeService.createNode( studyS1, rootNodeS1.getIdNode(), createModificationNodeInfo("N1"), InsertMode.AFTER, null); - // Définir exclusions sur root networks source + // Modifications A and B are excluded for the root network with tag "COM" in study S1 RootNetworkNodeInfoEntity rnComS1 = rootNetworkNodeInfoRepository .findByNodeInfoIdAndRootNetworkId(nodeN1.getId(), s1RootInfos.get(1).rootNetworkUuid()) .orElseThrow(); @@ -245,25 +238,25 @@ void testDuplicateNodeWithCommonAndUniqueRootNetworksBetweenStudies() { rnRnsS1.setModificationsUuidsToExclude(modificationsToExcludeRnq); // ex: A, B, C rootNetworkNodeInfoRepository.save(rnRnsS1); - // 3️⃣ Créer étude cible S2 + // Create Study 2 StudyEntity studyS2 = TestUtils.createDummyStudy(NETWORK_UUID2, CASE_UUID2, CASE_NAME2, CASE_FORMAT2, REPORT_UUID2); - createDummyRootNetwork(studyS2, "S2RN1", "COM"); // commun - createDummyRootNetwork(studyS2, "S2RN2", "RNT"); // unique cible + createDummyRootNetwork(studyS2, "S2RN1", "COM"); + createDummyRootNetwork(studyS2, "S2RN2", "RNT"); studyRepository.save(studyS2); List s2RootInfos = studyService.getExistingBasicRootNetworkInfos(studyS2.getId()); NodeEntity rootNodeS2 = networkModificationTreeService.createRoot(studyS2); - // 4️⃣ Mock duplications des UUID + // Mock duplicated modifications Mockito.doReturn(originToDuplicateModificationUuidMap) .when(networkModificationService) .duplicateModificationsGroup(any(), any()); - // 5️⃣ Dupliquer node N1 de S1 vers S2 + // Duplicate Node N1 from S1 to S2 UUID duplicateNodeUuid = networkModificationTreeService.duplicateStudyNode( nodeN1.getId(), rootNodeS2.getIdNode(), InsertMode.AFTER); - // 6️⃣ Vérifier root network commun (COM) → exclusions copiées + // Check that the common root network (COM) on S2 has the same excluded modifications copied from S1 RootNetworkNodeInfoEntity rnComS2 = rootNetworkNodeInfoRepository .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( duplicateNodeUuid, s2RootInfos.stream().filter(rn -> "COM".equals(rn.tag())).toList().getFirst().rootNetworkUuid()) @@ -273,16 +266,16 @@ void testDuplicateNodeWithCommonAndUniqueRootNetworksBetweenStudies() { .map(originToDuplicateModificationUuidMap::get) .collect(Collectors.toSet()), rnComS2.getModificationsUuidsToExclude(), - "Exclusions doivent être copiées pour le root network commun" + "Excluded modifications must be copied for the common root network" ); - // 7️⃣ Vérifier root network unique cible (RNT) → toutes les modifs actives + // Check the target unique root network (RNT) → all active modifications RootNetworkNodeInfoEntity rnRntS2 = rootNetworkNodeInfoRepository .findWithModificationsToExcludeByNodeInfoIdAndRootNetworkId( duplicateNodeUuid, s2RootInfos.stream().filter(rn -> "RNT".equals(rn.tag())).toList().getFirst().rootNetworkUuid()) .orElseThrow(); assertTrue(rnRntS2.getModificationsUuidsToExclude().isEmpty(), - "Root network unique cible (RNT) ne devrait pas avoir des modifications to exclude"); + "Target unique root network (RNT) should not have modifications to exclude"); } @Test