diff --git a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java index 5ee6e33a..683dcc9d 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/SecurityAnalysisController.java @@ -7,6 +7,7 @@ package org.gridsuite.securityanalysis.server; import com.powsybl.iidm.network.ThreeSides; +import com.powsybl.loadflow.LoadFlowResult; import com.powsybl.security.LimitViolationType; import com.powsybl.security.SecurityAnalysisResult; import io.swagger.v3.oas.annotations.Operation; @@ -158,12 +159,12 @@ public ResponseEntity getNResultZippedCsv(@Parameter(description = "Resu @Operation(summary = "Get a security analysis result from the database - NMK contingencies result") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis result"), @ApiResponse(responseCode = "404", description = "Security analysis result has not been found")}) - public ResponseEntity> getNmKContingenciesResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, - @Parameter(description = "network Uuid") @RequestParam(name = "networkUuid", required = false) UUID networkUuid, - @Parameter(description = "variant Id") @RequestParam(name = "variantId", required = false) String variantId, - @Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters, - @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, - @Parameter(description = "Pagination parameters") Pageable pageable) { + public ResponseEntity> getPagedNmKContingenciesResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, + @Parameter(description = "network Uuid") @RequestParam(name = "networkUuid", required = false) UUID networkUuid, + @Parameter(description = "variant Id") @RequestParam(name = "variantId", required = false) String variantId, + @Parameter(description = "Filters") @RequestParam(name = "filters", required = false) String stringFilters, + @Parameter(description = "Global Filters") @RequestParam(name = "globalFilters", required = false) String globalFilters, + @Parameter(description = "Pagination parameters") Pageable pageable) { String decodedStringFilters = stringFilters != null ? URLDecoder.decode(stringFilters, StandardCharsets.UTF_8) : null; String decodedStringGlobalFilters = globalFilters != null ? URLDecoder.decode(globalFilters, StandardCharsets.UTF_8) : null; Page result = securityAnalysisResultService.findNmKContingenciesPaged(resultUuid, networkUuid, variantId, decodedStringFilters, decodedStringGlobalFilters, pageable); @@ -173,6 +174,18 @@ public ResponseEntity> getNmKContingenciesResult(@Par : ResponseEntity.notFound().build(); } + @GetMapping(value = "/results/{resultUuid}/nmk-contingencies-result", produces = APPLICATION_JSON_VALUE) + @Operation(summary = "Get a security analysis result from the database - NMK contingencies result") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis result"), + @ApiResponse(responseCode = "404", description = "Security analysis result has not been found")}) + public ResponseEntity> getNmKContingenciesResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { + List result = securityAnalysisResultService.findNmKContingenciesResult(resultUuid); + + return result != null + ? ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result) + : ResponseEntity.notFound().build(); + } + @PostMapping(value = "/results/{resultUuid}/nmk-contingencies-result/csv", produces = APPLICATION_OCTET_STREAM_VALUE, consumes = APPLICATION_JSON_VALUE) @Operation(summary = "Get a security analysis result from the database - NMK contingencies result - CSV export") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The security analysis result csv export"), @@ -247,6 +260,22 @@ public ResponseEntity stop(@Parameter(description = "Result UUID") @PathVa return ResponseEntity.ok().build(); } + @PostMapping(value = "/results/{resultUuid}", produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE) + @Operation(summary = "Save security analysis results") + @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "The security analysis results have been saved to database")}) + public ResponseEntity saveResult(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid, + @RequestBody SecurityAnalysisResult result) { + securityAnalysisResultService.insert( + null, + resultUuid, + result, + result.getPreContingencyResult().getStatus() == LoadFlowResult.ComponentResult.Status.CONVERGED + ? SecurityAnalysisStatus.CONVERGED + : SecurityAnalysisStatus.DIVERGED + ); + return ResponseEntity.ok().build(); + } + @GetMapping(value = "/providers", produces = APPLICATION_JSON_VALUE) @Operation(summary = "Get all security analysis providers") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Security analysis providers have been found")}) diff --git a/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyEntity.java b/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyEntity.java index 21e7a562..2c8f5656 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyEntity.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyEntity.java @@ -13,6 +13,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.FieldNameConstants; +import org.springframework.lang.Nullable; import java.util.List; import java.util.Map; @@ -64,7 +65,7 @@ private void setContingencyLimitViolations(List } } - public static ContingencyEntity toEntity(Network network, PostContingencyResult postContingencyResult, Map subjectLimitViolationsBySubjectId) { + public static ContingencyEntity toEntity(@Nullable Network network, PostContingencyResult postContingencyResult, Map subjectLimitViolationsBySubjectId) { List contingencyElements = postContingencyResult.getContingency().getElements().stream().map(contingencyElement -> ContingencyElementEmbeddable.toEntity(contingencyElement)).collect(Collectors.toList()); List contingencyLimitViolations = postContingencyResult.getLimitViolationsResult().getLimitViolations().stream() diff --git a/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyLimitViolationEntity.java b/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyLimitViolationEntity.java index 85e4dbb5..426316bb 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyLimitViolationEntity.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/entities/ContingencyLimitViolationEntity.java @@ -13,6 +13,7 @@ import lombok.*; import lombok.experimental.FieldNameConstants; import lombok.experimental.SuperBuilder; +import org.springframework.lang.Nullable; /** * @author Kevin Le Saulnier @@ -30,10 +31,8 @@ public class ContingencyLimitViolationEntity extends AbstractLimitViolationEntit @Setter private ContingencyEntity contingency; - public static ContingencyLimitViolationEntity toEntity(Network network, LimitViolation limitViolation, SubjectLimitViolationEntity subjectLimitViolation) { - Double patlLimit = getPatlLimit(limitViolation, network); - - ContingencyLimitViolationEntity contingencyLimitViolationEntity = ContingencyLimitViolationEntity.builder() + public static ContingencyLimitViolationEntity toEntity(@Nullable Network network, LimitViolation limitViolation, SubjectLimitViolationEntity subjectLimitViolation) { + ContingencyLimitViolationEntityBuilder contingencyLimitViolationEntityBuilder = ContingencyLimitViolationEntity.builder() .limit(limitViolation.getLimit()) .limitName(limitViolation.getLimitName()) .limitType(limitViolation.getLimitType()) @@ -41,17 +40,27 @@ public static ContingencyLimitViolationEntity toEntity(Network network, LimitVio .value(limitViolation.getValue()) .side(limitViolation.getSide()) .loading(computeLoading(limitViolation, limitViolation.getLimit())) - .locationId(ComputationResultUtils.getViolationLocationId(limitViolation, network)) .subjectLimitViolation(subjectLimitViolation) - .patlLimit(patlLimit) - .patlLoading(computeLoading(limitViolation, patlLimit)) - .nextLimitName(getNextLimitName(limitViolation, network)) - .acceptableDuration(calculateActualOverloadDuration(limitViolation, network)) - .upcomingAcceptableDuration(calculateUpcomingOverloadDuration(limitViolation)) - .build(); + .upcomingAcceptableDuration(calculateUpcomingOverloadDuration(limitViolation)); + + if (network != null) { + enrichBuilderWithNetworkData(contingencyLimitViolationEntityBuilder, network, limitViolation); + } + + ContingencyLimitViolationEntity contingencyLimitViolationEntity = contingencyLimitViolationEntityBuilder.build(); subjectLimitViolation.addContingencyLimitViolation(contingencyLimitViolationEntity); return contingencyLimitViolationEntity; } + + private static void enrichBuilderWithNetworkData(ContingencyLimitViolationEntityBuilder contingencyLimitViolationEntityBuilder, Network network, LimitViolation limitViolation) { + Double patlLimit = getPatlLimit(limitViolation, network); + contingencyLimitViolationEntityBuilder + .patlLimit(patlLimit) + .patlLoading(computeLoading(limitViolation, patlLimit)) + .nextLimitName(getNextLimitName(limitViolation, network)) + .locationId(ComputationResultUtils.getViolationLocationId(limitViolation, network)) + .acceptableDuration(calculateActualOverloadDuration(limitViolation, network)); + } } diff --git a/src/main/java/org/gridsuite/securityanalysis/server/entities/PreContingencyLimitViolationEntity.java b/src/main/java/org/gridsuite/securityanalysis/server/entities/PreContingencyLimitViolationEntity.java index 061ddf0d..f860c3d7 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/entities/PreContingencyLimitViolationEntity.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/entities/PreContingencyLimitViolationEntity.java @@ -14,6 +14,7 @@ import jakarta.persistence.*; import lombok.experimental.FieldNameConstants; import lombok.experimental.SuperBuilder; +import org.springframework.lang.Nullable; import java.util.List; import java.util.Map; @@ -35,27 +36,37 @@ public class PreContingencyLimitViolationEntity extends AbstractLimitViolationEn @Setter SecurityAnalysisResultEntity result; - public static List toEntityList(Network network, PreContingencyResult preContingencyResult, Map subjectLimitViolationsBySubjectId) { + public static List toEntityList(@Nullable Network network, PreContingencyResult preContingencyResult, Map subjectLimitViolationsBySubjectId) { return preContingencyResult.getLimitViolationsResult().getLimitViolations().stream().map(limitViolation -> toEntity(network, limitViolation, subjectLimitViolationsBySubjectId.get(limitViolation.getSubjectId()))).collect(Collectors.toList()); } - public static PreContingencyLimitViolationEntity toEntity(Network network, LimitViolation limitViolation, SubjectLimitViolationEntity subjectLimitViolation) { - Double patlLimit = getPatlLimit(limitViolation, network); - return PreContingencyLimitViolationEntity.builder() + public static PreContingencyLimitViolationEntity toEntity(@Nullable Network network, LimitViolation limitViolation, SubjectLimitViolationEntity subjectLimitViolation) { + PreContingencyLimitViolationEntityBuilder preContingencyLimitViolationEntityBuilder = PreContingencyLimitViolationEntity.builder() .subjectLimitViolation(subjectLimitViolation) .limit(limitViolation.getLimit()) .limitName(limitViolation.getLimitName()) .limitType(limitViolation.getLimitType()) - .acceptableDuration(calculateActualOverloadDuration(limitViolation, network)) .upcomingAcceptableDuration(calculateUpcomingOverloadDuration(limitViolation)) .limitReduction(limitViolation.getLimitReduction()) .value(limitViolation.getValue()) .side(limitViolation.getSide()) + .loading(computeLoading(limitViolation, limitViolation.getLimit())); + + if (network != null) { + enrichBuilderWithNetworkData(preContingencyLimitViolationEntityBuilder, network, limitViolation); + } + + return preContingencyLimitViolationEntityBuilder.build(); + } + + private static void enrichBuilderWithNetworkData(PreContingencyLimitViolationEntity.PreContingencyLimitViolationEntityBuilder preContingencyLimitViolationEntityBuilder, Network network, LimitViolation limitViolation) { + Double patlLimit = getPatlLimit(limitViolation, network); + + preContingencyLimitViolationEntityBuilder + .acceptableDuration(calculateActualOverloadDuration(limitViolation, network)) .patlLimit(patlLimit) - .loading(computeLoading(limitViolation, limitViolation.getLimit())) .patlLoading(computeLoading(limitViolation, patlLimit)) .locationId(ComputationResultUtils.getViolationLocationId(limitViolation, network)) - .nextLimitName(getNextLimitName(limitViolation, network)) - .build(); + .nextLimitName(getNextLimitName(limitViolation, network)); } } diff --git a/src/main/java/org/gridsuite/securityanalysis/server/entities/SecurityAnalysisResultEntity.java b/src/main/java/org/gridsuite/securityanalysis/server/entities/SecurityAnalysisResultEntity.java index 6d192143..470fe5fe 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/entities/SecurityAnalysisResultEntity.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/entities/SecurityAnalysisResultEntity.java @@ -13,6 +13,7 @@ import lombok.experimental.FieldNameConstants; import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisStatus; import org.jgrapht.alg.util.Pair; +import org.springframework.lang.Nullable; import java.util.List; import java.util.Map; @@ -53,7 +54,7 @@ public SecurityAnalysisResultEntity(UUID id) { this.id = id; } - public static SecurityAnalysisResultEntity toEntity(Network network, UUID resultUuid, SecurityAnalysisResult securityAnalysisResult, SecurityAnalysisStatus securityAnalysisStatus) { + public static SecurityAnalysisResultEntity toEntity(@Nullable Network network, UUID resultUuid, SecurityAnalysisResult securityAnalysisResult, SecurityAnalysisStatus securityAnalysisStatus) { Map subjectLimitViolationsBySubjectId = getUniqueSubjectLimitViolationsFromResult(securityAnalysisResult) .stream().collect(Collectors.toMap( SubjectLimitViolationEntity::getSubjectId, diff --git a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java index cfdbdd60..fd020cc8 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultService.java @@ -29,6 +29,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.data.domain.*; import org.springframework.data.jpa.domain.Specification; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -238,7 +239,7 @@ public void assertResultExists(UUID resultUuid) { } @Transactional - public void insert(Network network, UUID resultUuid, SecurityAnalysisResult result, SecurityAnalysisStatus status) { + public void insert(@Nullable Network network, UUID resultUuid, SecurityAnalysisResult result, SecurityAnalysisStatus status) { Objects.requireNonNull(resultUuid); Objects.requireNonNull(result); diff --git a/src/main/resources/db/changelog/changesets/changelog_20260116T110615Z.xml b/src/main/resources/db/changelog/changesets/changelog_20260116T110615Z.xml new file mode 100644 index 00000000..a2b0bab9 --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20260116T110615Z.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index e1801e26..2dfde1d1 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -51,3 +51,6 @@ databaseChangeLog: - include: file: changesets/changelog_20250908T143920Z.xml relativeToChangelogFile: true + - include: + file: changesets/changelog_20260116T110615Z.xml + relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java index 82cb02ae..a507adf1 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java @@ -26,6 +26,7 @@ import org.assertj.core.api.Assertions; import org.gridsuite.computation.dto.GlobalFilter; import org.gridsuite.computation.dto.ResourceFilterDTO; +import org.gridsuite.computation.error.ComputationException; import org.gridsuite.computation.service.AbstractFilterService; import org.gridsuite.computation.service.ReportService; import org.gridsuite.computation.service.UuidGeneratorService; @@ -36,6 +37,7 @@ import org.gridsuite.securityanalysis.server.repositories.SubjectLimitViolationRepository; import org.gridsuite.securityanalysis.server.service.ActionsService; import org.gridsuite.securityanalysis.server.service.LoadFlowService; +import org.gridsuite.securityanalysis.server.service.SecurityAnalysisResultService; import org.gridsuite.securityanalysis.server.service.SecurityAnalysisWorkerService; import org.gridsuite.securityanalysis.server.util.ContextConfigurationWithTestChannel; import org.gridsuite.securityanalysis.server.util.CsvExportUtils; @@ -43,6 +45,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.mockito.MockitoAnnotations; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,6 +57,7 @@ import org.springframework.http.MediaType; import org.springframework.messaging.Message; import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -72,6 +76,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static com.powsybl.network.store.model.NetworkStoreApi.VERSION; +import static org.gridsuite.computation.error.ComputationBusinessErrorCode.RESULT_NOT_FOUND; import static org.gridsuite.computation.service.NotificationService.*; import static org.gridsuite.securityanalysis.server.SecurityAnalysisProviderMock.*; import static org.gridsuite.securityanalysis.server.service.SecurityAnalysisService.COMPUTATION_TYPE; @@ -143,6 +148,9 @@ class SecurityAnalysisControllerTest { @Autowired private SubjectLimitViolationRepository subjectLimitViolationRepository; + @MockitoSpyBean + private SecurityAnalysisResultService securityAnalysisResultService; + private static final Map ENUM_TRANSLATIONS_EN = Map.of( "ONE", "Side 1", "TWO", "Side 2", @@ -838,6 +846,58 @@ void getDefaultProviderTest() throws Exception { ); } + @Test + void saveResultTest() throws Exception { + UUID resultUuid = UUID.randomUUID(); + SecurityAnalysisResult securityAnalysisResult = SecurityAnalysisProviderMock.RESULT; + doNothing().when(securityAnalysisResultService).insert(null, resultUuid, securityAnalysisResult, SecurityAnalysisStatus.CONVERGED); + + mockMvc.perform(post("/" + VERSION + "/results/" + resultUuid) + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsString(securityAnalysisResult))) + .andExpect(status().isOk()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SecurityAnalysisResult.class); + verify(securityAnalysisResultService, times(1)).insert(eq(null), eq(resultUuid), captor.capture(), eq(SecurityAnalysisStatus.CONVERGED)); + Assertions.assertThat(captor.getValue()) + .usingRecursiveComparison() + // this field is not well serialized/deserialized - since it's deprecated / not used, we ignore it here + .ignoringFieldsMatchingRegexes(".*\\.limitViolationsResult\\.computationOk") + .isEqualTo(securityAnalysisResult); + } + + @Test + void getNmKContingenciesResult() throws Exception { + UUID resultUuid = UUID.randomUUID(); + + List serviceResult = SecurityAnalysisProviderMock.RESULT_CONTINGENCIES; + + doReturn(serviceResult).when(securityAnalysisResultService).findNmKContingenciesResult(resultUuid); + + mockMvc.perform(get("/" + VERSION + "/results/" + resultUuid + "/nmk-contingencies-result") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json(mapper.writeValueAsString(serviceResult))); + + verify(securityAnalysisResultService, times(1)) + .findNmKContingenciesResult(resultUuid); + } + + @Test + void getNmKContingenciesResultNotFound() throws Exception { + UUID resultUuid = UUID.randomUUID(); + + doReturn(null).when(securityAnalysisResultService).findNmKContingenciesResult(resultUuid); + + mockMvc.perform(get("/" + VERSION + "/results/" + resultUuid + "/nmk-contingencies-result") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()); + + verify(securityAnalysisResultService, times(1)) + .findNmKContingenciesResult(resultUuid); + } + @Test void getZippedCsvResults() throws Exception { // running computation to create some results diff --git a/src/test/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultServiceTest.java b/src/test/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultServiceTest.java index c2ea216b..0ebb1b22 100644 --- a/src/test/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultServiceTest.java +++ b/src/test/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisResultServiceTest.java @@ -10,15 +10,21 @@ import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.network.store.iidm.impl.NetworkFactoryImpl; import com.vladmihalcea.sql.SQLStatementCountValidator; +import org.gridsuite.securityanalysis.server.dto.ContingencyResultDTO; import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisStatus; +import org.gridsuite.securityanalysis.server.dto.SubjectLimitViolationDTO; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import java.util.List; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; import static org.gridsuite.securityanalysis.server.SecurityAnalysisProviderMock.*; import static org.gridsuite.securityanalysis.server.util.DatabaseQueryUtils.assertRequestsCount; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author Florent MILLOT @@ -41,4 +47,35 @@ void deleteResultPerfTest() { // 1 manual select to get the contingencyUuids, and 4 select at the end for the last delete when applying the cascade assertRequestsCount(5, 0, 0, 6); } + + @Test + void insertResultWithoutNetworkTest() { + UUID resultUuid = UUID.randomUUID(); + securityAnalysisResultService.insert(null, resultUuid, RESULT, SecurityAnalysisStatus.CONVERGED); + securityAnalysisResultService.assertResultExists(resultUuid); + + List contingencyResults = securityAnalysisResultService.findNmKContingenciesResult(resultUuid); + assertEquals(RESULT.getPostContingencyResults().size(), contingencyResults.size()); + // check fields based on network are actually nullish + contingencyResults.forEach(this::checkFieldBasedOnNetworkAreNullish); + } + + @SuppressWarnings("java:S5841") // some limitViolation could be empty, which will make allSatisfy pass automatically - this behaviour is intended + private void checkFieldBasedOnNetworkAreNullish(ContingencyResultDTO contingencyResult) { + assertThat(contingencyResult.getSubjectLimitViolations()) + .extracting(SubjectLimitViolationDTO::getLimitViolation) + .allSatisfy(lv -> + assertThat(lv) + .satisfies(v -> assertThat(v.getPatlLimit()).isNull()) + .satisfies(v -> assertThat(v.getPatlLoading()).isNull()) + .satisfies(v -> assertThat(v.getNextLimitName()).isNull()) + .satisfies(v -> assertThat(v.getLocationId()).isNull()) + .satisfies(v -> assertThat(v.getAcceptableDuration()).isNull()) + ); + } + + @AfterEach + void tearDown() { + securityAnalysisResultService.deleteAll(); + } }