diff --git a/src/main/java/org/gridsuite/shortcircuit/server/ShortCircuitController.java b/src/main/java/org/gridsuite/shortcircuit/server/ShortCircuitController.java index 0c454ba6..ea03975a 100644 --- a/src/main/java/org/gridsuite/shortcircuit/server/ShortCircuitController.java +++ b/src/main/java/org/gridsuite/shortcircuit/server/ShortCircuitController.java @@ -8,16 +8,12 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.powsybl.security.LimitViolationType; -import com.powsybl.shortcircuit.ShortCircuitParameters; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import org.gridsuite.shortcircuit.server.dto.*; -import org.gridsuite.shortcircuit.server.service.ShortCircuitRunContext; import org.gridsuite.shortcircuit.server.service.ShortCircuitService; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -25,7 +21,9 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.*; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import static com.powsybl.shortcircuit.Fault.FaultType; import static org.gridsuite.shortcircuit.server.computation.service.NotificationService.HEADER_USER_ID; @@ -44,18 +42,9 @@ public ShortCircuitController(ShortCircuitService shortCircuitService) { this.shortCircuitService = shortCircuitService; } - private static ShortCircuitParameters getNonNullParameters(ShortCircuitParameters parameters) { - ShortCircuitParameters shortCircuitParameters = parameters != null ? parameters : new ShortCircuitParameters(); - shortCircuitParameters.setDetailedReport(false); - return shortCircuitParameters; - } - - @PostMapping(value = "/networks/{networkUuid}/run-and-save", produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE) + @PostMapping(value = "/networks/{networkUuid}/run-and-save", produces = APPLICATION_JSON_VALUE) @Operation(summary = "Run a short circuit analysis on a network") - @ApiResponses(value = {@ApiResponse(responseCode = "200", - description = "The short circuit analysis has been performed", - content = {@Content(mediaType = APPLICATION_JSON_VALUE, - schema = @Schema(implementation = ShortCircuitParameters.class))})}) + @ApiResponse(responseCode = "200", description = "The short circuit analysis has been performed") public ResponseEntity runAndSave(@Parameter(description = "Network UUID") @PathVariable("networkUuid") UUID networkUuid, @Parameter(description = "Variant Id") @RequestParam(name = "variantId", required = false) String variantId, @Parameter(description = "Result receiver") @RequestParam(name = "receiver", required = false) String receiver, @@ -63,12 +52,9 @@ public ResponseEntity runAndSave(@Parameter(description = "Network UUID") @Parameter(description = "reporterId") @RequestParam(name = "reporterId", required = false) String reporterId, @Parameter(description = "The type name for the report") @RequestParam(name = "reportType", required = false) String reportType, @Parameter(description = "Bus Id - Used for analysis targeting one bus") @RequestParam(name = "busId", required = false) String busId, - @RequestBody(required = false) ShortCircuitParameters parameters, + @Parameter(description = "ID of parameters to use, fallback on default ones if none") @RequestParam(name = "parametersUuid") Optional parametersUuid, @RequestHeader(HEADER_USER_ID) String userId) { - ShortCircuitParameters nonNullParameters = getNonNullParameters(parameters); - ShortCircuitRunContext runContext = new ShortCircuitRunContext(networkUuid, variantId, receiver, nonNullParameters, reportUuid, reporterId, reportType, userId, null, busId); - UUID resultUuid = shortCircuitService.runAndSaveResult(runContext); - return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(resultUuid); + return ResponseEntity.ok().contentType(APPLICATION_JSON).body(shortCircuitService.runAndSaveResult(networkUuid, variantId, receiver, reportUuid, reporterId, reportType, userId, busId, parametersUuid)); } @GetMapping(value = "/results/{resultUuid}", produces = APPLICATION_JSON_VALUE) @@ -181,5 +167,4 @@ public ResponseEntity> getFaultTypes(@Parameter(description = "R public ResponseEntity> getLimitTypes(@Parameter(description = "Result UUID") @PathVariable("resultUuid") UUID resultUuid) { return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(shortCircuitService.getLimitTypes(resultUuid)); } - } diff --git a/src/main/java/org/gridsuite/shortcircuit/server/ShortCircuitParametersController.java b/src/main/java/org/gridsuite/shortcircuit/server/ShortCircuitParametersController.java new file mode 100644 index 00000000..711062c2 --- /dev/null +++ b/src/main/java/org/gridsuite/shortcircuit/server/ShortCircuitParametersController.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.shortcircuit.server; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.gridsuite.shortcircuit.server.dto.ShortCircuitParametersInfos; +import org.gridsuite.shortcircuit.server.service.ShortCircuitService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@RestController +@RequestMapping(path = "/" + ShortCircuitApi.API_VERSION + "/parameters", produces = APPLICATION_JSON_VALUE) +@Tag(name = "Short circuit server analysis parameters") +public class ShortCircuitParametersController { + public static final String DUPLICATE_FROM = "duplicateFrom"; + + private final ShortCircuitService shortCircuitService; + + public ShortCircuitParametersController(ShortCircuitService shortCircuitService) { + this.shortCircuitService = shortCircuitService; + } + + @GetMapping(path = "/{parametersUuid}") + @Operation(summary = "Get the parameters for an analysis") + @ApiResponse(responseCode = "200", description = "The parameters asked") + @ApiResponse(responseCode = "404", description = "The parameters don't exists") + public ResponseEntity getParameters(@Parameter(description = "UUID of parameters") @PathVariable("parametersUuid") UUID parametersUuid) { + return ResponseEntity.of(shortCircuitService.getParameters(parametersUuid)); + } + + @PostMapping(consumes = APPLICATION_JSON_VALUE) + @Operation(summary = "Create a new set of parameters for an analysis using given parameters") + @ApiResponse(responseCode = "200", description = "The new parameters entity ID") + public ResponseEntity createParameters(@Parameter(description = "Parameters to save") @RequestBody ShortCircuitParametersInfos parameters) { + return ResponseEntity.ok(shortCircuitService.createParameters(parameters)); + } + + @PostMapping(path = "/default") + @Operation(summary = "Create a new set of parameters for an analysis using default parameters") + @ApiResponse(responseCode = "200", description = "The new parameters entity ID") + public ResponseEntity createDefaultParameters() { + return ResponseEntity.ok(shortCircuitService.createParameters(null)); + } + + @PostMapping(params = { DUPLICATE_FROM }) + @Operation(summary = "Duplicate the parameters of an analysis") + @ApiResponse(responseCode = "200", description = "The new parameters ID") + @ApiResponse(responseCode = "404", description = "The parameters don't exist") + public ResponseEntity duplicateParameters(@Parameter(description = "UUID of parameters to duplicate") @RequestParam(name = DUPLICATE_FROM) UUID sourceParametersUuid) { + return ResponseEntity.of(shortCircuitService.duplicateParameters(sourceParametersUuid)); + } + + @DeleteMapping(path = "/{parametersUuid}") + @Operation(summary = "Delete a set of parameters") + @ApiResponse(responseCode = "204", description = "The parameters are successfully deleted") + @ApiResponse(responseCode = "404", description = "The parameters don't exists") + public ResponseEntity deleteParameters(@Parameter(description = "UUID of parameters") @PathVariable("parametersUuid") UUID parametersUuid) { + return (shortCircuitService.deleteParameters(parametersUuid) ? ResponseEntity.noContent() : ResponseEntity.notFound()).build(); + } + + @PutMapping(path = "/{parametersUuid}", consumes = APPLICATION_JSON_VALUE) + @Operation(summary = "Update parameters for an analysis or reset them to default ones") + @ApiResponse(responseCode = "204", description = "The parameters are successfully updated") + @ApiResponse(responseCode = "404", description = "The parameters don't exists") + public ResponseEntity updateOrResetParameters(@Parameter(description = "UUID of parameters") @PathVariable("parametersUuid") UUID parametersUuid, + @Parameter(description = "Parameters to save instead of default ones", schema = @Schema(implementation = ShortCircuitParametersInfos.class)) + @RequestBody(required = false) ShortCircuitParametersInfos parameters) { + return (shortCircuitService.updateOrResetParameters(parametersUuid, parameters) ? ResponseEntity.noContent() : ResponseEntity.notFound()).build(); + } +} diff --git a/src/main/java/org/gridsuite/shortcircuit/server/dto/ShortCircuitParametersInfos.java b/src/main/java/org/gridsuite/shortcircuit/server/dto/ShortCircuitParametersInfos.java new file mode 100644 index 00000000..e4f1c0ca --- /dev/null +++ b/src/main/java/org/gridsuite/shortcircuit/server/dto/ShortCircuitParametersInfos.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.shortcircuit.server.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; +import com.powsybl.shortcircuit.ShortCircuitParameters; +import com.powsybl.shortcircuit.VoltageRange; +import lombok.Builder; +import lombok.extern.jackson.Jacksonized; +import org.gridsuite.shortcircuit.server.service.ShortCircuitService; + +import java.util.List; + +/** + * @since 1.7.0 + */ +@Builder +@Jacksonized +public record ShortCircuitParametersInfos( + ShortCircuitPredefinedConfiguration predefinedParameters, + ShortCircuitParameters parameters +) { + @JsonProperty(access = Access.READ_ONLY) + public List cei909VoltageRanges() { + return ShortCircuitService.CEI909_VOLTAGE_PROFILE; + } +} diff --git a/src/main/java/org/gridsuite/shortcircuit/server/dto/ShortCircuitPredefinedConfiguration.java b/src/main/java/org/gridsuite/shortcircuit/server/dto/ShortCircuitPredefinedConfiguration.java new file mode 100644 index 00000000..b5cd6333 --- /dev/null +++ b/src/main/java/org/gridsuite/shortcircuit/server/dto/ShortCircuitPredefinedConfiguration.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.shortcircuit.server.dto; + +/** + * @since 1.7.0 + */ +public enum ShortCircuitPredefinedConfiguration { + ICC_MAX_WITH_CEI909, + ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP, + ICC_MIN_WITH_NOMINAL_VOLTAGE_MAP +} diff --git a/src/main/java/org/gridsuite/shortcircuit/server/entities/ShortCircuitParametersEntity.java b/src/main/java/org/gridsuite/shortcircuit/server/entities/ShortCircuitParametersEntity.java new file mode 100644 index 00000000..3926d3c9 --- /dev/null +++ b/src/main/java/org/gridsuite/shortcircuit/server/entities/ShortCircuitParametersEntity.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.shortcircuit.server.entities; + +import com.powsybl.shortcircuit.InitialVoltageProfileMode; +import com.powsybl.shortcircuit.StudyType; +import jakarta.persistence.*; +import lombok.*; +import lombok.experimental.Accessors; +import org.gridsuite.shortcircuit.server.dto.ShortCircuitPredefinedConfiguration; + +import java.util.UUID; + +/** + * @since 1.7.0 + */ +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Getter +@Setter +@Entity +@Table(name = "shortcircuit_parameters") +public class ShortCircuitParametersEntity { + + public ShortCircuitParametersEntity(boolean withLimitViolations, boolean withVoltageResult, boolean withFeederResult, StudyType studyType, + double minVoltageDropProportionalThreshold, ShortCircuitPredefinedConfiguration predefinedParameters, + boolean withLoads, boolean withShuntCompensators, boolean withVscConverterStations, boolean withNeutralPosition, + InitialVoltageProfileMode initialVoltageProfileMode) { + this(null, withLimitViolations, withVoltageResult, withFeederResult, studyType, minVoltageDropProportionalThreshold, + predefinedParameters, withLoads, withShuntCompensators, withVscConverterStations, withNeutralPosition, initialVoltageProfileMode); + } + + public ShortCircuitParametersEntity(@NonNull final ShortCircuitParametersEntity sourceToClone) { + this(sourceToClone.isWithLimitViolations(), + sourceToClone.isWithVoltageResult(), + sourceToClone.isWithFeederResult(), + sourceToClone.getStudyType(), + sourceToClone.getMinVoltageDropProportionalThreshold(), + sourceToClone.getPredefinedParameters(), + sourceToClone.isWithLoads(), + sourceToClone.isWithShuntCompensators(), + sourceToClone.isWithVscConverterStations(), + sourceToClone.isWithNeutralPosition(), + sourceToClone.getInitialVoltageProfileMode()); + } + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id") + private UUID id; + + @Builder.Default + @Column(name = "withLimitViolations", columnDefinition = "boolean default true") + private boolean withLimitViolations = true; + + @Builder.Default + @Column(name = "withVoltageResult", columnDefinition = "boolean default false") + private boolean withVoltageResult = false; + + @Builder.Default + @Column(name = "withFeederResult", columnDefinition = "boolean default true") + private boolean withFeederResult = true; + + @Builder.Default + @Column(name = "studyType", columnDefinition = "varchar(255) default 'TRANSIENT'") + @Enumerated(EnumType.STRING) + private StudyType studyType = StudyType.TRANSIENT; + + @Builder.Default + @Column(name = "minVoltageDropProportionalThreshold", columnDefinition = "double precision default 20.0") + private double minVoltageDropProportionalThreshold = 20.0; + + @Builder.Default + @Column(name = "predefinedParameters", columnDefinition = "varchar(255) default 'ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP'") + @Enumerated(EnumType.STRING) + private ShortCircuitPredefinedConfiguration predefinedParameters = ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP; + + @Builder.Default + @Column(name = "withLoads", columnDefinition = "boolean default false") + private boolean withLoads = false; + + @Builder.Default + @Column(name = "withShuntCompensators", columnDefinition = "boolean default false") + private boolean withShuntCompensators = false; + + @Builder.Default + @Column(name = "withVscConverterStations", columnDefinition = "boolean default true") + private boolean withVscConverterStations = true; + + @Builder.Default + @Column(name = "withNeutralPosition", columnDefinition = "boolean default true") + private boolean withNeutralPosition = true; + + @Builder.Default + @Column(name = "initialVoltageProfileMode", columnDefinition = "varchar(255) default 'NOMINAL'") + @Enumerated(EnumType.STRING) + private InitialVoltageProfileMode initialVoltageProfileMode = InitialVoltageProfileMode.NOMINAL; + + public ShortCircuitParametersEntity updateWith(final ShortCircuitParametersEntity source) { + return this.setWithLimitViolations(source.isWithLimitViolations()) + .setWithVoltageResult(source.isWithVoltageResult()) + .setWithFeederResult(source.isWithFeederResult()) + .setStudyType(source.getStudyType()) + .setMinVoltageDropProportionalThreshold(source.getMinVoltageDropProportionalThreshold()) + .setPredefinedParameters(source.getPredefinedParameters()) + .setWithLoads(source.isWithLoads()) + .setWithShuntCompensators(source.isWithShuntCompensators()) + .setWithVscConverterStations(source.isWithVscConverterStations()) + .setWithNeutralPosition(source.isWithNeutralPosition()) + .setInitialVoltageProfileMode(source.getInitialVoltageProfileMode()); + } +} diff --git a/src/main/java/org/gridsuite/shortcircuit/server/repositories/ParametersRepository.java b/src/main/java/org/gridsuite/shortcircuit/server/repositories/ParametersRepository.java new file mode 100644 index 00000000..4cfaa27a --- /dev/null +++ b/src/main/java/org/gridsuite/shortcircuit/server/repositories/ParametersRepository.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.shortcircuit.server.repositories; + +import org.gridsuite.shortcircuit.server.entities.ShortCircuitParametersEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface ParametersRepository extends JpaRepository { +} diff --git a/src/main/java/org/gridsuite/shortcircuit/server/service/ShortCircuitService.java b/src/main/java/org/gridsuite/shortcircuit/server/service/ShortCircuitService.java index 439dbbd8..35c95679 100644 --- a/src/main/java/org/gridsuite/shortcircuit/server/service/ShortCircuitService.java +++ b/src/main/java/org/gridsuite/shortcircuit/server/service/ShortCircuitService.java @@ -8,23 +8,30 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.security.LimitViolationType; +import com.powsybl.shortcircuit.InitialVoltageProfileMode; +import com.powsybl.shortcircuit.ShortCircuitParameters; +import com.powsybl.shortcircuit.VoltageRange; import com.powsybl.ws.commons.LogUtils; import com.univocity.parsers.csv.CsvWriter; import com.univocity.parsers.csv.CsvWriterSettings; +import org.apache.commons.lang3.StringUtils; import org.gridsuite.shortcircuit.server.ShortCircuitException; import org.gridsuite.shortcircuit.server.computation.service.AbstractComputationService; import org.gridsuite.shortcircuit.server.computation.service.NotificationService; import org.gridsuite.shortcircuit.server.computation.service.UuidGeneratorService; import org.gridsuite.shortcircuit.server.dto.*; import org.gridsuite.shortcircuit.server.entities.*; +import org.gridsuite.shortcircuit.server.repositories.ParametersRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.io.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.TimeUnit; @@ -40,17 +47,36 @@ */ @Service public class ShortCircuitService extends AbstractComputationService { - private static final Logger LOGGER = LoggerFactory.getLogger(ShortCircuitService.class); public static final String GET_SHORT_CIRCUIT_RESULTS_MSG = "Get ShortCircuit Results {} in {}ms"; - public ShortCircuitService(NotificationService notificationService, UuidGeneratorService uuidGeneratorService, ShortCircuitAnalysisResultService resultService, ObjectMapper objectMapper) { + public static final List CEI909_VOLTAGE_PROFILE = List.of( + new VoltageRange(10.0, 199.99, 1.1), + new VoltageRange(200.0, 299.99, 1.09), + new VoltageRange(300.0, 500.0, 1.05) + ); + + private final ParametersRepository parametersRepository; + + public ShortCircuitService(final NotificationService notificationService, final UuidGeneratorService uuidGeneratorService, + final ShortCircuitAnalysisResultService resultService, final ParametersRepository parametersRepository, + final ObjectMapper objectMapper) { super(notificationService, resultService, objectMapper, uuidGeneratorService, null); + this.parametersRepository = parametersRepository; } + public UUID runAndSaveResult(UUID networkUuid, String variantId, String receiver, UUID reportUuid, String reporterId, String reportType, + String userId, String busId, final Optional parametersUuid) { + ShortCircuitParameters parameters = fromEntity(parametersUuid.flatMap(parametersRepository::findById).orElseGet(ShortCircuitParametersEntity::new)).parameters(); + parameters.setWithFortescueResult(StringUtils.isNotBlank(busId)); + parameters.setDetailedReport(false); + return runAndSaveResult(new ShortCircuitRunContext(networkUuid, variantId, receiver, parameters, reportUuid, reporterId, reportType, userId, null, busId)); + } + + @Override public UUID runAndSaveResult(ShortCircuitRunContext runContext) { Objects.requireNonNull(runContext); - var resultUuid = uuidGeneratorService.generate(); + final UUID resultUuid = uuidGeneratorService.generate(); // update status to running status setStatus(List.of(resultUuid), ShortCircuitAnalysisStatus.RUNNING); @@ -103,6 +129,43 @@ private static FeederResult fromEntity(FeederResultEntity feederResultEntity) { return new FeederResult(feederResultEntity.getConnectableId(), feederResultEntity.getCurrent(), feederResultEntity.getPositiveMagnitude()); } + private static ShortCircuitParametersEntity toEntity(ShortCircuitParametersInfos parametersInfos) { + final ShortCircuitParameters parameters = parametersInfos.parameters(); + return new ShortCircuitParametersEntity( + parameters.isWithLimitViolations(), + parameters.isWithVoltageResult(), + parameters.isWithFeederResult(), + parameters.getStudyType(), + parameters.getMinVoltageDropProportionalThreshold(), + parametersInfos.predefinedParameters(), + parameters.isWithLoads(), + parameters.isWithShuntCompensators(), + parameters.isWithVSCConverterStations(), + parameters.isWithNeutralPosition(), + parameters.getInitialVoltageProfileMode() + ); + } + + private static ShortCircuitParametersInfos fromEntity(ShortCircuitParametersEntity entity) { + Objects.requireNonNull(entity); + return new ShortCircuitParametersInfos( + entity.getPredefinedParameters(), + new ShortCircuitParameters() + .setStudyType(entity.getStudyType()) + .setMinVoltageDropProportionalThreshold(entity.getMinVoltageDropProportionalThreshold()) + .setWithFeederResult(entity.isWithFeederResult()) + .setWithLimitViolations(entity.isWithLimitViolations()) + .setWithVoltageResult(entity.isWithVoltageResult()) + .setWithLoads(entity.isWithLoads()) + .setWithShuntCompensators(entity.isWithShuntCompensators()) + .setWithVSCConverterStations(entity.isWithVscConverterStations()) + .setWithNeutralPosition(entity.isWithNeutralPosition()) + .setInitialVoltageProfileMode(entity.getInitialVoltageProfileMode()) + // the voltageRanges is not taken into account when initialVoltageProfileMode=NOMINAL + .setVoltageRanges(InitialVoltageProfileMode.CONFIGURED.equals(entity.getInitialVoltageProfileMode()) ? CEI909_VOLTAGE_PROFILE : null) + ); + } + private static ShortCircuitAnalysisResultEntity sortByElementId(ShortCircuitAnalysisResultEntity result) { result.setFaultResults(result.getFaultResults().stream() .sorted(Comparator.comparing(fr -> fr.getFault().getElementId())) @@ -296,4 +359,39 @@ public List getLimitTypes(UUID resultUuid) { public List getFaultTypes(UUID resultUuid) { return resultService.findFaultTypes(resultUuid); } + + public Optional getParameters(final UUID parametersUuid) { + return parametersRepository.findById(parametersUuid).map(ShortCircuitService::fromEntity); + } + + @Transactional + public boolean deleteParameters(final UUID parametersUuid) { + final boolean result = parametersRepository.existsById(parametersUuid); + if (result) { + parametersRepository.deleteById(parametersUuid); + } + return result; + } + + @Transactional + public Optional duplicateParameters(UUID sourceParametersUuid) { + return parametersRepository.findById(sourceParametersUuid) + .map(ShortCircuitParametersEntity::new) + .map(parametersRepository::save) + .map(ShortCircuitParametersEntity::getId); + } + + public UUID createParameters(@Nullable final ShortCircuitParametersInfos parameters) { + return parametersRepository.save(parameters != null ? toEntity(parameters) : new ShortCircuitParametersEntity()).getId(); + } + + @Transactional + public boolean updateOrResetParameters(final UUID parametersUuid, @Nullable final ShortCircuitParametersInfos givenParameters) { + return parametersRepository.findById(parametersUuid) + .map(parameters -> { + parameters.updateWith(givenParameters != null ? toEntity(givenParameters) : new ShortCircuitParametersEntity()); + return true; + }) + .orElse(false); + } } diff --git a/src/main/resources/db/changelog/changesets/changelog_20240515T001122Z.xml b/src/main/resources/db/changelog/changesets/changelog_20240515T001122Z.xml new file mode 100644 index 00000000..6207d19b --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20240515T001122Z.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index b189cb76..858d5f31 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -1,5 +1,4 @@ databaseChangeLog: - - include: file: changesets/changelog_20221003T150015Z.xml relativeToChangelogFile: true @@ -36,4 +35,6 @@ databaseChangeLog: - include: file: changesets/changelog_20240528T161201Z.xml relativeToChangelogFile: true - + - include: + file: changesets/changelog_20240515T001122Z.xml + relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/shortcircuit/server/ShortCircuitParametersControllerTest.java b/src/test/java/org/gridsuite/shortcircuit/server/ShortCircuitParametersControllerTest.java new file mode 100644 index 00000000..ea5bac63 --- /dev/null +++ b/src/test/java/org/gridsuite/shortcircuit/server/ShortCircuitParametersControllerTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.shortcircuit.server; + +import lombok.NonNull; +import org.assertj.core.api.WithAssertions; +import org.gridsuite.shortcircuit.server.dto.ShortCircuitParametersInfos; +import org.gridsuite.shortcircuit.server.service.ShortCircuitService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultMatcher; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith({ MockitoExtension.class }) +@WebMvcTest(controllers = { ShortCircuitParametersController.class }) +class ShortCircuitParametersControllerTest implements WithAssertions { + private final String defaultParametersJson; + + public ShortCircuitParametersControllerTest() throws URISyntaxException, IOException { + this.defaultParametersJson = Files.readString(Paths.get(this.getClass().getResource(this.getClass().getSimpleName() + ".json").toURI())).replaceAll("\\s+", ""); + } + + @Autowired + private MockMvc mockMvc; + + @MockBean + private ShortCircuitService shortCircuitService; + + @AfterEach + void checkMocks() { + Mockito.verifyNoMoreInteractions(shortCircuitService); + } + + @Test + void testGetExistingParameters() throws Exception { + final UUID arg = UUID.randomUUID(); + final Optional returned = Optional.of(new ShortCircuitParametersInfos(null, null)); + when(shortCircuitService.getParameters(any(UUID.class))).thenReturn(returned); + mockMvc.perform(get("/v1/parameters/{pUuid}", arg.toString())) + .andExpectAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), content().string(defaultParametersJson)); + final ArgumentCaptor uuidCaptor = ArgumentCaptor.forClass(UUID.class); + verify(shortCircuitService).getParameters(uuidCaptor.capture()); + assertThat(uuidCaptor.getValue()).isEqualTo(arg); + } + + @Test + void testGetNonExistingParameters() throws Exception { + final UUID arg = UUID.randomUUID(); + when(shortCircuitService.getParameters(any(UUID.class))).thenReturn(Optional.empty()); + mockMvc.perform(get("/v1/parameters/{pUuid}", arg.toString())) + .andExpectAll(status().isNotFound()); + final ArgumentCaptor uuidCaptor = ArgumentCaptor.forClass(UUID.class); + verify(shortCircuitService).getParameters(uuidCaptor.capture()); + assertThat(uuidCaptor.getValue()).isEqualTo(arg); + } + + @Test + void testCreateParameters() throws Exception { + final UUID returned = UUID.randomUUID(); + when(shortCircuitService.createParameters(any(ShortCircuitParametersInfos.class))).thenReturn(returned); + mockMvc.perform(post("/v1/parameters").content(defaultParametersJson).contentType(MediaType.APPLICATION_JSON)) + .andExpectAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), content().string("\"" + returned + "\"")); + final ArgumentCaptor dtoCaptor = ArgumentCaptor.forClass(ShortCircuitParametersInfos.class); + verify(shortCircuitService).createParameters(dtoCaptor.capture()); + assertThat(dtoCaptor.getValue()).isEqualTo(new ShortCircuitParametersInfos(null, null)); + } + + @Test + void testCreateDefaultParameters() throws Exception { + final UUID returned = UUID.randomUUID(); + when(shortCircuitService.createParameters(nullable(ShortCircuitParametersInfos.class))).thenReturn(returned); + mockMvc.perform(post("/v1/parameters/default")) + .andExpectAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), content().string("\"" + returned + "\"")); + verify(shortCircuitService).createParameters(null); + } + + @Test + void testDuplicateExistingParameters() throws Exception { + final UUID arg = UUID.randomUUID(); + final UUID returned = UUID.randomUUID(); + when(shortCircuitService.duplicateParameters(any(UUID.class))).thenReturn(Optional.of(returned)); + mockMvc.perform(post("/v1/parameters").param("duplicateFrom", arg.toString())) + .andExpectAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), content().string("\"" + returned + "\"")); + final ArgumentCaptor uuidCaptor = ArgumentCaptor.forClass(UUID.class); + verify(shortCircuitService).duplicateParameters(uuidCaptor.capture()); + assertThat(uuidCaptor.getValue()).isEqualTo(arg); + } + + @Test + void testDuplicateNonExistingParameters() throws Exception { + final UUID arg = UUID.randomUUID(); + when(shortCircuitService.duplicateParameters(any(UUID.class))).thenReturn(Optional.empty()); + mockMvc.perform(post("/v1/parameters").param(ShortCircuitParametersController.DUPLICATE_FROM, arg.toString())) + .andExpectAll(status().isNotFound()); + final ArgumentCaptor uuidCaptor = ArgumentCaptor.forClass(UUID.class); + verify(shortCircuitService).duplicateParameters(uuidCaptor.capture()); + assertThat(uuidCaptor.getValue()).isEqualTo(arg); + } + + static Stream testParametersArgs() { + return Stream.of( + Arguments.arguments(true, status().isNoContent()), + Arguments.arguments(false, status().isNotFound()) + ); + } + + @MethodSource("testParametersArgs") + @ParameterizedTest + void testDeleteParameters(final boolean existing, @NonNull final ResultMatcher statusMatcher) throws Exception { + final UUID arg = UUID.randomUUID(); + when(shortCircuitService.deleteParameters(any(UUID.class))).thenReturn(existing); + mockMvc.perform(delete("/v1/parameters/{pUuid}", arg.toString())) + .andExpectAll(statusMatcher); + final ArgumentCaptor uuidCaptor = ArgumentCaptor.forClass(UUID.class); + verify(shortCircuitService).deleteParameters(uuidCaptor.capture()); + assertThat(uuidCaptor.getValue()).isEqualTo(arg); + } + + @MethodSource("testParametersArgs") + @ParameterizedTest + void testUpdateParameters(final boolean existing, @NonNull final ResultMatcher statusMatcher) throws Exception { + final UUID arg1 = UUID.randomUUID(); + when(shortCircuitService.updateOrResetParameters(any(UUID.class), any(ShortCircuitParametersInfos.class))).thenReturn(existing); + mockMvc.perform(put("/v1/parameters/{pUuid}", arg1.toString()).content(defaultParametersJson).contentType(MediaType.APPLICATION_JSON)) + .andExpectAll(statusMatcher); + final ArgumentCaptor uuidCaptor = ArgumentCaptor.forClass(UUID.class); + final ArgumentCaptor dtoCaptor = ArgumentCaptor.forClass(ShortCircuitParametersInfos.class); + verify(shortCircuitService).updateOrResetParameters(uuidCaptor.capture(), dtoCaptor.capture()); + assertThat(uuidCaptor.getValue()).isEqualTo(arg1); + assertThat(dtoCaptor.getValue()).isEqualTo(new ShortCircuitParametersInfos(null, null)); + } + + @MethodSource("testParametersArgs") + @ParameterizedTest + void testResetParameters(final boolean existing, @NonNull final ResultMatcher statusMatcher) throws Exception { + final UUID arg1 = UUID.randomUUID(); + when(shortCircuitService.updateOrResetParameters(any(UUID.class), nullable(ShortCircuitParametersInfos.class))).thenReturn(existing); + mockMvc.perform(put("/v1/parameters/{pUuid}", arg1.toString())) + .andExpectAll(statusMatcher); + final ArgumentCaptor uuidCaptor = ArgumentCaptor.forClass(UUID.class); + verify(shortCircuitService).updateOrResetParameters(uuidCaptor.capture(), isNull()); + assertThat(uuidCaptor.getValue()).isEqualTo(arg1); + } +} diff --git a/src/test/java/org/gridsuite/shortcircuit/server/ShortCircuitParametersITest.java b/src/test/java/org/gridsuite/shortcircuit/server/ShortCircuitParametersITest.java new file mode 100644 index 00000000..d26f2fd2 --- /dev/null +++ b/src/test/java/org/gridsuite/shortcircuit/server/ShortCircuitParametersITest.java @@ -0,0 +1,284 @@ +package org.gridsuite.shortcircuit.server; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.powsybl.shortcircuit.InitialVoltageProfileMode; +import com.powsybl.shortcircuit.StudyType; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.assertj.core.api.WithAssertions; +import org.gridsuite.shortcircuit.server.computation.service.NotificationService; +import org.gridsuite.shortcircuit.server.dto.ShortCircuitPredefinedConfiguration; +import org.gridsuite.shortcircuit.server.entities.ShortCircuitParametersEntity; +import org.gridsuite.shortcircuit.server.repositories.ParametersRepository; +import org.gridsuite.shortcircuit.server.service.ShortCircuitService; +import org.json.JSONException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.junit.jupiter.MockitoExtension; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.transaction.annotation.Transactional; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +import static org.gridsuite.shortcircuit.server.ShortCircuitParametersController.DUPLICATE_FROM; +import static org.gridsuite.shortcircuit.server.computation.service.NotificationService.HEADER_USER_ID; +import static org.gridsuite.shortcircuit.server.service.ShortCircuitResultContext.HEADER_BUS_ID; +import static org.hamcrest.Matchers.matchesPattern; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.log; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +@ExtendWith({ MockitoExtension.class }) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Slf4j +class ShortCircuitParametersITest implements WithAssertions { + private static final String USER_ID = "userTestId"; + private static final UUID NETWORK_ID = UUID.randomUUID(); + private final String defaultParametersJson; + + public ShortCircuitParametersITest() throws Exception { + this.defaultParametersJson = Files.readString(Paths.get(this.getClass().getClassLoader().getResource("default_shorcircuit_parameters.json").toURI())).replaceAll("\\s+", ""); + } + + @Autowired + ObjectMapper objectMapper; + + @Autowired + MockMvc mockMvc; + + @Autowired + ParametersRepository parametersRepository; + + @MockBean + NotificationService notificationService; //to keep; for not testing notification part, it's tested in another class test + + @BeforeAll + void checkDatabaseClean() { + assertThat(parametersRepository.count()).as("parameters in database").isZero(); + } + + @AfterEach + void verifyMocks() { + verifyNoMoreInteractions(notificationService); + } + + @Test + void runAnalysis() throws Exception { + runAnalysisTest(req -> { }, headers -> headers, defaultParametersJson); + } + + @Test + void runAnalysisWithParameters() throws Exception { + final UUID paramsId = parametersRepository.save(new ShortCircuitParametersEntity()).getId(); + runAnalysisTest(req -> req.queryParam("parametersId", paramsId.toString()), headers -> headers, defaultParametersJson); + } + + @Test + void runAnalysisWithBusId() throws Exception { + final String busId = UUID.randomUUID().toString(); + runAnalysisTest( + req -> req.queryParam("busId", busId), + headers -> headers.put(HEADER_BUS_ID, busId), + defaultParametersJson.replace("\"withFortescueResult\":false", "\"withFortescueResult\":true")); + } + + private void runAnalysisTest(final Consumer requestSet, final UnaryOperator> headerSet, final String response) throws Exception { + final MockHttpServletRequestBuilder requestBuilder = post("/v1/networks/{networkUuid}/run-and-save", NETWORK_ID).header(HEADER_USER_ID, USER_ID); + requestSet.accept(requestBuilder); + final String resultId = StringUtils.strip(mockMvc.perform(requestBuilder) + .andDo(log()) + .andExpectAll( + status().isOk(), + content().contentType(MediaType.APPLICATION_JSON), + content().string(matchesPattern(TestUtils.UUID_IN_JSON)) + ) + .andReturn() + .getResponse() + .getContentAsString(), "\""); + final ArgumentCaptor> messageCaptor = ArgumentCaptor.forClass(Message.class); + //FIXME: to remove when https://github.com/powsybl/powsybl-ws-commons/pull/53 is merged + verify(notificationService).sendRunMessage(messageCaptor.capture()); + assertThat(messageCaptor.getValue()).as("message sent").usingRecursiveComparison() + .withEqualsForFields((String j1, String j2) -> { + //we can have more details than with simple string comparaison + try { + JSONAssert.assertEquals(j2, j1, true); + return true; + } catch (JSONException ex) { + log.error("payload not equals", ex); + return false; + } + }, "payload") + // message headers use an IdGenerator not overriden + .withEqualsForFields((UUID id1, UUID id2) -> (id1 == null) == (id2 == null), "headers." + MessageHeaders.ID) + // we must do a comparaison using AssertJ because message headers have real system timestamp + .withEqualsForFields((Long t1, Long t2) -> t1 < t2, "headers." + MessageHeaders.TIMESTAMP) + .isEqualTo(new GenericMessage<>(response, headerSet.apply(ImmutableMap.builder().putAll(Map.of( + MessageHeaders.ID, UUID.randomUUID(), + MessageHeaders.TIMESTAMP, System.currentTimeMillis(), + "resultUuid", resultId, + "networkUuid", NETWORK_ID.toString(), + HEADER_USER_ID, USER_ID + ))).build())); + } + + @Test + void deleteParameters() throws Exception { + final UUID paramsId = parametersRepository.save(new ShortCircuitParametersEntity()).getId(); + mockMvc.perform(delete("/v1/parameters/{id}", paramsId)).andExpectAll(status().isNoContent(), content().bytes(new byte[0])); + assertThat(parametersRepository.count()).isZero(); + } + + @Test + void createParameters() throws Exception { + final UUID pUuid = objectMapper.readValue(mockMvc.perform(post("/v1/parameters").contentType(MediaType.APPLICATION_JSON) + .content("{\"predefinedParameters\":\"ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP\",\"parameters\":" + defaultParametersJson + .replace("\"withLoads\":false", "\"withLoads\":true") + .replace("\"studyType\":\"TRANSIENT\"", "\"studyType\":\"STEADY_STATE\"") + "}")) + .andDo(log()).andExpectAll( + status().isOk(), + content().contentType(MediaType.APPLICATION_JSON), + content().string(matchesPattern(TestUtils.UUID_IN_JSON)) + ).andReturn().getResponse().getContentAsByteArray(), UUID.class); + assertThat(parametersRepository.findAll()).as("parameters in database") + .singleElement().as("parameters entity") + .usingRecursiveComparison() //because JPA entities haven't equals implemented + .isEqualTo(new ShortCircuitParametersEntity(pUuid, true, false, true, StudyType.STEADY_STATE, 20.0, ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP, + true, false, true, true, InitialVoltageProfileMode.NOMINAL)); + } + + @Test + void createDefaultParameters() throws Exception { + final UUID pUuid = objectMapper.readValue(mockMvc.perform(post("/v1/parameters/default")).andDo(log()).andExpectAll( + status().isOk(), + content().contentType(MediaType.APPLICATION_JSON), + content().string(matchesPattern(TestUtils.UUID_IN_JSON)) + ).andReturn().getResponse().getContentAsByteArray(), UUID.class); + assertThat(parametersRepository.findAll()).as("parameters in database") + .singleElement().as("parameters entity") + .usingRecursiveComparison() //because JPA entities haven't equals implemented + .isEqualTo(new ShortCircuitParametersEntity().setId(pUuid)); + } + + @Test + void retrieveParameters() throws Exception { + final UUID pId = parametersRepository.save(new ShortCircuitParametersEntity(true, true, true, StudyType.STEADY_STATE, Math.PI, + ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP, true, true, true, true, InitialVoltageProfileMode.NOMINAL)).getId(); + mockMvc.perform(get("/v1/parameters/{id}", pId)).andDo(log()).andExpectAll( + status().isOk(), + content().contentType(MediaType.APPLICATION_JSON), + content().json("{\"predefinedParameters\":\"ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP\",\"parameters\":" + defaultParametersJson + .replace("false", "true") + .replace("\"minVoltageDropProportionalThreshold\":20.0", "\"minVoltageDropProportionalThreshold\":3.141592653589793") + .replace("\"studyType\":\"TRANSIENT\"", "\"studyType\":\"STEADY_STATE\"") + + ",\"cei909VoltageRanges\":" + objectMapper.writeValueAsString(ShortCircuitService.CEI909_VOLTAGE_PROFILE) + "}", true) + ); + } + + @Test + void resetParameters() throws Exception { + final UUID pId = parametersRepository.save(new ShortCircuitParametersEntity(true, true, true, StudyType.STEADY_STATE, Math.PI, + ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP, true, true, true, true, InitialVoltageProfileMode.NOMINAL)).getId(); + mockMvc.perform(put("/v1/parameters/{id}", pId)).andDo(log()).andExpectAll( + status().isNoContent(), + content().bytes(new byte[0]) + ); + assertThat(parametersRepository.findAll()).as("parameters in database") + .singleElement().as("parameters entity") + .usingRecursiveComparison() //because JPA entities haven't equals implemented + .isEqualTo(new ShortCircuitParametersEntity().setId(pId)); + } + + @Test + void updateParameters() throws Exception { + final UUID pUuid = parametersRepository.save(new ShortCircuitParametersEntity(true, true, true, StudyType.STEADY_STATE, Math.PI, + ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP, true, true, true, true, InitialVoltageProfileMode.NOMINAL)).getId(); + mockMvc.perform(put("/v1/parameters/{id}", pUuid) + .contentType(MediaType.APPLICATION_JSON) + .content("{\"predefinedParameters\":\"ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP\",\"parameters\":" + defaultParametersJson + .replace("\"withLoads\":false", "\"withLoads\":true") + .replace("\"studyType\":\"TRANSIENT\"", "\"studyType\":\"STEADY_STATE\"") + "}")) + .andDo(log()).andExpectAll(status().isNoContent(), content().bytes(new byte[0])) + .andReturn().getResponse().getContentAsByteArray(); + assertThat(parametersRepository.findAll()).as("parameters in database") + .singleElement().as("parameters entity") + .usingRecursiveComparison() //because JPA entities haven't equals implemented + .isEqualTo(new ShortCircuitParametersEntity(pUuid, true, false, true, StudyType.STEADY_STATE, 20.0, ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP, + true, false, true, true, InitialVoltageProfileMode.NOMINAL)); + } + + @Test + void duplicateParameters() throws Exception { + final Supplier generatorEntity = () -> new ShortCircuitParametersEntity(false, false, false, StudyType.TRANSIENT, 1.234, + ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909, false, false, false, false, InitialVoltageProfileMode.NOMINAL); + final UUID pUuid = parametersRepository.save(generatorEntity.get()).getId(); + final UUID pUuidDuplicated = objectMapper.readValue(mockMvc.perform(post("/v1/parameters").queryParam(DUPLICATE_FROM, pUuid.toString())) + .andDo(log()).andExpectAll( + status().isOk(), + content().contentType(MediaType.APPLICATION_JSON), + content().string(matchesPattern(TestUtils.UUID_IN_JSON)) + ).andReturn().getResponse().getContentAsByteArray(), UUID.class); + assertThat(parametersRepository.findAll()).as("parameters in database") + .usingRecursiveComparison() //because JPA entities haven't equals implemented + .ignoringCollectionOrder() + .isEqualTo(List.of( + generatorEntity.get().setId(pUuid), + generatorEntity.get().setId(pUuidDuplicated) + )); + } + + @ParameterizedTest + @MethodSource("testWithInvalidParametersArgArgs") + void testWithInvalidParametersArg(final RequestBuilder requestBuilder, final ResultMatcher statusResult) throws Exception { + mockMvc.perform(requestBuilder).andDo(log()) + .andExpectAll(statusResult, content().bytes(new byte[0])); + } + + private static Stream testWithInvalidParametersArgArgs() { + return Stream.of( + Arguments.of(get("/v1/parameters/{parametersUuid}", UUID.randomUUID()), status().isNotFound()), + Arguments.of(delete("/v1/parameters/{parametersUuid}", UUID.randomUUID()), status().isNotFound()), + Arguments.of(post("/v1/parameters"), status().isBadRequest()), + Arguments.of(post("/v1/parameters").content("{}"), status().isBadRequest()), + Arguments.of(post("/v1/parameters").contentType(MediaType.TEXT_PLAIN).content("{}"), status().isBadRequest()), + Arguments.of(post("/v1/parameters").queryParam(DUPLICATE_FROM, ""), status().isBadRequest()), + Arguments.of(post("/v1/parameters").queryParam(DUPLICATE_FROM, UUID.randomUUID().toString()), status().isNotFound()), + Arguments.of(put("/v1/parameters/{parametersUuid}", UUID.randomUUID()), status().isNotFound()) + ); + } +} diff --git a/src/test/java/org/gridsuite/shortcircuit/server/TestUtils.java b/src/test/java/org/gridsuite/shortcircuit/server/TestUtils.java index f12a0e5a..6df0dc34 100644 --- a/src/test/java/org/gridsuite/shortcircuit/server/TestUtils.java +++ b/src/test/java/org/gridsuite/shortcircuit/server/TestUtils.java @@ -21,10 +21,10 @@ import java.lang.reflect.Constructor; import java.util.List; import java.util.UUID; +import java.util.regex.Pattern; import java.util.zip.ZipInputStream; import static com.vladmihalcea.sql.SQLStatementCountValidator.*; -import static com.vladmihalcea.sql.SQLStatementCountValidator.assertDeleteCount; import static org.junit.Assert.assertNull; /** @@ -33,6 +33,12 @@ public final class TestUtils { private static final long TIMEOUT = 100; + /** + * Matcher for {@link java.util.UUID UUID v4}. + */ + public static final Pattern UUID_V4 = Pattern.compile("[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}", Pattern.CASE_INSENSITIVE); + public static final Pattern UUID_IN_JSON = Pattern.compile("^\"" + UUID_V4.pattern() + "\"$", Pattern.CASE_INSENSITIVE); + private TestUtils() { throw new IllegalStateException("Not implemented exception"); } diff --git a/src/test/java/org/gridsuite/shortcircuit/server/dto/ShortCircuitParametersInfosTest.java b/src/test/java/org/gridsuite/shortcircuit/server/dto/ShortCircuitParametersInfosTest.java new file mode 100644 index 00000000..a1048ab0 --- /dev/null +++ b/src/test/java/org/gridsuite/shortcircuit/server/dto/ShortCircuitParametersInfosTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.shortcircuit.server.dto; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.powsybl.shortcircuit.ShortCircuitParameters; +import com.powsybl.shortcircuit.VoltageRange; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.assertj.core.api.WithAssertions; +import org.gridsuite.shortcircuit.server.RestTemplateConfig; +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.test.context.ContextConfiguration; + +import static org.gridsuite.shortcircuit.server.service.ShortCircuitService.CEI909_VOLTAGE_PROFILE; + +@ContextConfiguration(classes = { RestTemplateConfig.class }) +@JsonTest +class ShortCircuitParametersInfosTest implements WithAssertions { + private static final String DUMB_JSON = "\"predefinedParameters\":\"ICC_MAX_WITH_CEI909\", \"parameters\":{\"version\":\"1.3\",\"withLimitViolations\":true,\"withVoltageResult\":true,\"withFeederResult\":true,\"studyType\":\"TRANSIENT\",\"minVoltageDropProportionalThreshold\":0.0,\"withFortescueResult\":false,\"withLoads\":true,\"withShuntCompensators\":true,\"withVSCConverterStations\":true,\"withNeutralPosition\":false,\"initialVoltageProfileMode\":\"NOMINAL\",\"detailedReport\":true}"; + + @Autowired + ObjectMapper objectMapper; + + @SneakyThrows + private static JSONObject toJson(@NonNull final VoltageRange voltageRange) { + return new JSONObject().put("minimumNominalVoltage", voltageRange.getMinimumNominalVoltage()) + .put("maximumNominalVoltage", voltageRange.getMaximumNominalVoltage()) + .put("voltageRangeCoefficient", voltageRange.getRangeCoefficient()); + } + + @Test + void shouldSerializeCei909VoltageRanges() throws Exception { + final String jsonSerialized = objectMapper.writeValueAsString(new ShortCircuitParametersInfos(ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909, new ShortCircuitParameters())); + JSONAssert.assertEquals( + new JSONObject().put("predefinedParameters", ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909.toString()) + .put("parameters", new JSONObject().put("version", "1.3")) + .put("cei909VoltageRanges", CEI909_VOLTAGE_PROFILE.stream() + .map(ShortCircuitParametersInfosTest::toJson) + .reduce(new JSONArray(), JSONArray::put, (arr1, arr2) -> null)), + new JSONObject(jsonSerialized), + JSONCompareMode.STRICT_ORDER //extensible comparaison to not have to list others fields + ); + } + + @Test + void shouldIgnoreCei909VoltageRangesWhenDeserializeJsonWithVoltageRange() { + assertThatNoException().as("DTO with CEI909 field") + .isThrownBy(() -> objectMapper.readValue("{" + DUMB_JSON + ", \"cei909VoltageRanges\":[null,null]}", ShortCircuitParametersInfos.class)); + } + + @Test + void shouldIgnoreCei909VoltageRangesWhenDeserializeJsonWithoutVoltageRange() { + assertThatNoException().as("DTO without CEI909 field") + .isThrownBy(() -> objectMapper.readValue("{" + DUMB_JSON + "}", ShortCircuitParametersInfos.class)); + } +} diff --git a/src/test/java/org/gridsuite/shortcircuit/server/entities/ShortCircuitParametersEntityTest.java b/src/test/java/org/gridsuite/shortcircuit/server/entities/ShortCircuitParametersEntityTest.java new file mode 100644 index 00000000..b0880e25 --- /dev/null +++ b/src/test/java/org/gridsuite/shortcircuit/server/entities/ShortCircuitParametersEntityTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.shortcircuit.server.entities; + +import com.powsybl.shortcircuit.ShortCircuitConstants; +import org.assertj.core.api.WithAssertions; +import org.gridsuite.shortcircuit.server.dto.ShortCircuitPredefinedConfiguration; +import org.junit.jupiter.api.Test; + +class ShortCircuitParametersEntityTest implements WithAssertions { + @Test + void testUpdateSimple() { + ShortCircuitParametersEntity entity1 = new ShortCircuitParametersEntity(); + ShortCircuitParametersEntity entity2 = new ShortCircuitParametersEntity().setMinVoltageDropProportionalThreshold(Double.MAX_VALUE); + assertThat(entity1).as("verification").usingRecursiveComparison().isNotEqualTo(entity2); + entity1.updateWith(entity2); + assertThat(entity1).as("check").usingRecursiveComparison().isEqualTo(entity2); + } + + @Test + void testUpdate2() { + ShortCircuitParametersEntity entity1 = new ShortCircuitParametersEntity(); + ShortCircuitParametersEntity entity2 = new ShortCircuitParametersEntity( + ShortCircuitConstants.DEFAULT_WITH_LIMIT_VIOLATIONS, + ShortCircuitConstants.DEFAULT_WITH_VOLTAGE_RESULT, + //ShortCircuitConstants.DEFAULT_WITH_FORTESCUE_RESULT, + ShortCircuitConstants.DEFAULT_WITH_FEEDER_RESULT, + ShortCircuitConstants.DEFAULT_STUDY_TYPE, + ShortCircuitConstants.DEFAULT_MIN_VOLTAGE_DROP_PROPORTIONAL_THRESHOLD, + ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909, + ShortCircuitConstants.DEFAULT_WITH_LOADS, + ShortCircuitConstants.DEFAULT_WITH_SHUNT_COMPENSATORS, + ShortCircuitConstants.DEFAULT_WITH_VSC_CONVERTER_STATIONS, + ShortCircuitConstants.DEFAULT_WITH_NEUTRAL_POSITION, + ShortCircuitConstants.DEFAULT_INITIAL_VOLTAGE_PROFILE_MODE + //ShortCircuitConstants.DEFAULT_SUB_TRANSIENT_COEFFICIENT + //ShortCircuitConstants.DEFAULT_DETAILED_REPORT + ); + assertThat(entity1).as("verification").usingRecursiveComparison().isNotEqualTo(entity2); + entity1.updateWith(entity2); + assertThat(entity1).as("check").usingRecursiveComparison().isEqualTo(entity2); + } +} diff --git a/src/test/java/org/gridsuite/shortcircuit/server/service/ShortCircuitServiceTest.java b/src/test/java/org/gridsuite/shortcircuit/server/service/ShortCircuitServiceTest.java new file mode 100644 index 00000000..f34a00f6 --- /dev/null +++ b/src/test/java/org/gridsuite/shortcircuit/server/service/ShortCircuitServiceTest.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.shortcircuit.server.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.powsybl.shortcircuit.InitialVoltageProfileMode; +import com.powsybl.shortcircuit.ShortCircuitParameters; +import com.powsybl.shortcircuit.StudyType; +import org.assertj.core.api.WithAssertions; +import org.gridsuite.shortcircuit.server.computation.service.NotificationService; +import org.gridsuite.shortcircuit.server.computation.service.UuidGeneratorService; +import org.gridsuite.shortcircuit.server.dto.ShortCircuitParametersInfos; +import org.gridsuite.shortcircuit.server.dto.ShortCircuitPredefinedConfiguration; +import org.gridsuite.shortcircuit.server.entities.ShortCircuitParametersEntity; +import org.gridsuite.shortcircuit.server.repositories.ParametersRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import java.util.Optional; +import java.util.Random; +import java.util.UUID; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith({ MockitoExtension.class }) +@MockitoSettings(strictness = Strictness.STRICT_STUBS) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class ShortCircuitServiceTest implements WithAssertions { + private NotificationService notificationService; + private UuidGeneratorService uuidGeneratorService; + private ShortCircuitAnalysisResultService resultService; + private ParametersRepository parametersRepository; + private ObjectMapper objectMapper; + private ShortCircuitService shortCircuitService; + + @BeforeAll + void setUp() { + this.notificationService = mock(NotificationService.class); + this.uuidGeneratorService = spy(new UuidGeneratorService()); + this.resultService = mock(ShortCircuitAnalysisResultService.class); + this.parametersRepository = mock(ParametersRepository.class); + this.objectMapper = spy(new ObjectMapper()); + this.shortCircuitService = new ShortCircuitService(notificationService, uuidGeneratorService, resultService, parametersRepository, objectMapper); + } + + @AfterEach + void checkMocks() { + try { + Mockito.verifyNoMoreInteractions( + notificationService, + uuidGeneratorService, + resultService, + parametersRepository, + objectMapper + ); + } finally { + Mockito.reset( + notificationService, + uuidGeneratorService, + resultService, + parametersRepository, + objectMapper + ); + } + } + + private void checkParametersEntityHasBeenRead(final ShortCircuitParametersEntity pEntity) { + verify(pEntity).isWithLimitViolations(); + verify(pEntity).isWithVoltageResult(); + verify(pEntity).isWithFeederResult(); + verify(pEntity).getStudyType(); + verify(pEntity).getMinVoltageDropProportionalThreshold(); + verify(pEntity).getPredefinedParameters(); + verify(pEntity).isWithLoads(); + verify(pEntity).isWithShuntCompensators(); + verify(pEntity).isWithVscConverterStations(); + verify(pEntity).isWithNeutralPosition(); + verify(pEntity, atLeastOnce()).getInitialVoltageProfileMode(); + verify(pEntity, atMost(2)).getInitialVoltageProfileMode(); // for fromEntity() case + } + + @Test + void testGetNonExistingParameters() { + final UUID pUuid = UUID.randomUUID(); + when(parametersRepository.findById(any(UUID.class))).thenReturn(Optional.empty()); + assertThat(shortCircuitService.getParameters(pUuid)).as("service call result").isEmpty(); + verify(parametersRepository).findById(pUuid); + } + + @Test + void testGetExistingParametersAndConversionToDto() { + final UUID pUuid = UUID.randomUUID(); + final double minVoltDrop = new Random().nextDouble(); + final ShortCircuitParametersEntity pEntity = spy(new ShortCircuitParametersEntity(pUuid, false, false, false, StudyType.STEADY_STATE, minVoltDrop, + ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909, false, false, false, false, InitialVoltageProfileMode.NOMINAL)); + when(parametersRepository.findById(any(UUID.class))).thenReturn(Optional.of(pEntity)); + //can't spy call to fromEntity() + assertThat(shortCircuitService.getParameters(pUuid)).as("service call result") + .get().as("dto").usingRecursiveComparison().isEqualTo(new ShortCircuitParametersInfos( + ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909, new ShortCircuitParameters() + .setWithLimitViolations(false) + .setWithVoltageResult(false) + .setWithFeederResult(false) + .setStudyType(StudyType.STEADY_STATE) + .setMinVoltageDropProportionalThreshold(minVoltDrop) + .setWithLoads(false) + .setWithShuntCompensators(false) + .setWithVSCConverterStations(false) + .setWithNeutralPosition(false) + .setInitialVoltageProfileMode(InitialVoltageProfileMode.NOMINAL))); + checkParametersEntityHasBeenRead(pEntity); + verifyNoMoreInteractions(pEntity); + verify(parametersRepository).findById(pUuid); + } + + @Test + void testDeleteNonExistingParameters() { + final UUID pUuid = UUID.randomUUID(); + when(parametersRepository.existsById(any(UUID.class))).thenReturn(false); + assertThat(shortCircuitService.deleteParameters(pUuid)).as("service call result").isFalse(); + verify(parametersRepository).existsById(pUuid); + } + + @Test + void testDeleteExistingParameters() { + final UUID pUuid = UUID.randomUUID(); + when(parametersRepository.existsById(any(UUID.class))).thenReturn(true); + assertThat(shortCircuitService.deleteParameters(pUuid)).as("service call result").isTrue(); + verify(parametersRepository).existsById(pUuid); + verify(parametersRepository).deleteById(pUuid); + } + + @Test + void testDuplicateNonExistingParameters() { + final UUID pUuid = UUID.randomUUID(); + when(parametersRepository.findById(any(UUID.class))).thenReturn(Optional.empty()); + assertThat(shortCircuitService.duplicateParameters(pUuid)).as("service call result").isEmpty(); + verify(parametersRepository).findById(pUuid); + } + + @Test + void testDuplicateExistingParameters() { + final UUID pUuid = UUID.randomUUID(); + final ShortCircuitParametersEntity pEntityRaw = ShortCircuitParametersEntity.builder().id(UUID.randomUUID()).build(); + final ShortCircuitParametersEntity pEntity = spy(pEntityRaw); + final UUID pSavedUuid = UUID.randomUUID(); + final ShortCircuitParametersEntity pSavedEntity = spy(ShortCircuitParametersEntity.builder().id(pSavedUuid).build()); + when(parametersRepository.findById(any(UUID.class))).thenReturn(Optional.of(pEntity)); + when(parametersRepository.save(any(ShortCircuitParametersEntity.class))).thenReturn(pSavedEntity); + assertThat(shortCircuitService.duplicateParameters(pUuid)).as("service call result").get().isEqualTo(pSavedUuid); + verify(parametersRepository).findById(pUuid); + checkParametersEntityHasBeenRead(pEntity); + final ArgumentCaptor entityCaptor = ArgumentCaptor.forClass(ShortCircuitParametersEntity.class); + verify(parametersRepository).save(entityCaptor.capture()); + assertThat(entityCaptor.getValue()).as("saved entity").isNotSameAs(pEntity) + .usingRecursiveComparison().ignoringFields("id").isEqualTo(pEntityRaw); + verify(pSavedEntity).getId(); + verifyNoMoreInteractions(pEntity); + verifyNoMoreInteractions(pSavedEntity); + } + + @Test + void testCreateParameters() { + final UUID pUuid = UUID.randomUUID(); + final ShortCircuitParametersEntity pEntity = spy(ShortCircuitParametersEntity.builder().id(pUuid).build()); + when(parametersRepository.save(any(ShortCircuitParametersEntity.class))).thenReturn(pEntity); + //dto that must have differences with defaults + assertThat(shortCircuitService.createParameters(new ShortCircuitParametersInfos(ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909, new ShortCircuitParameters()))) + .as("service call result").isEqualTo(pUuid); + verify(pEntity).getId(); + final ArgumentCaptor captor = ArgumentCaptor.forClass(ShortCircuitParametersEntity.class); + verify(parametersRepository).save(captor.capture()); + assertThat(captor.getValue()).usingRecursiveComparison().isEqualTo(new ShortCircuitParametersEntity( + true, true, true, StudyType.TRANSIENT, 0.0, ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909, + true, true, true, false, InitialVoltageProfileMode.NOMINAL)); + } + + @Test + void testCreateDefaultParameters() { + final UUID pUuid = UUID.randomUUID(); + final ShortCircuitParametersEntity pEntity = spy(ShortCircuitParametersEntity.builder().id(pUuid).build()); + when(parametersRepository.save(any(ShortCircuitParametersEntity.class))).thenReturn(pEntity); + assertThat(shortCircuitService.createParameters(null)).as("service call result").isEqualTo(pUuid); + verify(pEntity).getId(); + final ArgumentCaptor captor = ArgumentCaptor.forClass(ShortCircuitParametersEntity.class); + verify(parametersRepository).save(captor.capture()); + assertThat(captor.getValue()).usingRecursiveComparison().isEqualTo(new ShortCircuitParametersEntity()); + } + + @Test + void testUpdateNonExistingParameters() { + final UUID pUuid = UUID.randomUUID(); + when(parametersRepository.findById(any(UUID.class))).thenReturn(Optional.empty()); + assertThat(shortCircuitService.updateOrResetParameters(pUuid, null)).as("service call result").isFalse(); + verify(parametersRepository).findById(pUuid); + } + + @Test + void testUpdateExistingParameters() { + final UUID pUuid = UUID.randomUUID(); + final ShortCircuitParametersEntity pEntity = spy(ShortCircuitParametersEntity.builder().id(pUuid).build()); + final ShortCircuitParameters pDtoUpdateParams = spy(new ShortCircuitParameters()); + final ShortCircuitParametersInfos pDtoUpdate = spy(new ShortCircuitParametersInfos(ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909, pDtoUpdateParams)); + //dto that must have differences with defaults + final ShortCircuitParametersEntity pEntityUpdate = new ShortCircuitParametersEntity(pUuid, true, true, true, StudyType.TRANSIENT, 0.0, + ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_CEI909, true, true, true, false, InitialVoltageProfileMode.NOMINAL); + when(parametersRepository.findById(any(UUID.class))).thenReturn(Optional.of(pEntity)); + assertThat(shortCircuitService.updateOrResetParameters(pUuid, pDtoUpdate)).as("service call result").isTrue(); + // verify we search the correct uid + verify(parametersRepository).findById(pUuid); + // verify DTO has been read + verify(pDtoUpdate).parameters(); + verify(pDtoUpdate).predefinedParameters(); + verify(pDtoUpdateParams).isWithLimitViolations(); + verify(pDtoUpdateParams).isWithVoltageResult(); + verify(pDtoUpdateParams).isWithFeederResult(); + verify(pDtoUpdateParams).getStudyType(); + verify(pDtoUpdateParams).getMinVoltageDropProportionalThreshold(); + verify(pDtoUpdateParams).isWithLoads(); + verify(pDtoUpdateParams).isWithShuntCompensators(); + verify(pDtoUpdateParams).isWithVSCConverterStations(); + verify(pDtoUpdateParams).isWithNeutralPosition(); + verify(pDtoUpdateParams).getInitialVoltageProfileMode(); + // verify the parameters has been update + checkParametersEntityHasBeenUpdate(pEntity, pEntityUpdate, "entity from dto"); + // verify no unwanted actions have been done + verifyNoMoreInteractions(pEntity, pDtoUpdate, pDtoUpdateParams); + } + + @Test + void testResetExistingParameters() { + final UUID pUuid = UUID.randomUUID(); + final ShortCircuitParametersEntity pEntity = spy(ShortCircuitParametersEntity.builder().id(pUuid).build()); + //entity that must have differences with defaults + final ShortCircuitParametersEntity pEntityUpdate = new ShortCircuitParametersEntity(pUuid, true, false, true, StudyType.TRANSIENT, 20.0, + ShortCircuitPredefinedConfiguration.ICC_MAX_WITH_NOMINAL_VOLTAGE_MAP, false, false, true, true, InitialVoltageProfileMode.NOMINAL); + when(parametersRepository.findById(any(UUID.class))).thenReturn(Optional.of(pEntity)); + assertThat(shortCircuitService.updateOrResetParameters(pUuid, null)).as("service call result").isTrue(); + // verify we search the correct uid + verify(parametersRepository).findById(pUuid); + // verify the parameters has been update + checkParametersEntityHasBeenUpdate(pEntity, pEntityUpdate, "default entity"); + // verify no unwanted actions have been done + verifyNoMoreInteractions(pEntity); + } + + private void checkParametersEntityHasBeenUpdate(final ShortCircuitParametersEntity pEntity, + final ShortCircuitParametersEntity pEntityUpdate, final String pEntityUpdateDesc) { + final ArgumentCaptor entityCaptor = ArgumentCaptor.forClass(ShortCircuitParametersEntity.class); + verify(pEntity).updateWith(entityCaptor.capture()); + assertThat(entityCaptor.getValue()).as(pEntityUpdateDesc) + .usingRecursiveComparison().ignoringFields("id").isEqualTo(pEntityUpdate); + + // verify possible updates (to pass verifyNoMoreInteractions) + verify(pEntity).setWithLimitViolations(pEntityUpdate.isWithLimitViolations()); + verify(pEntity).setWithVoltageResult(pEntityUpdate.isWithVoltageResult()); + verify(pEntity).setWithFeederResult(pEntityUpdate.isWithFeederResult()); + verify(pEntity).setStudyType(pEntityUpdate.getStudyType()); + verify(pEntity).setMinVoltageDropProportionalThreshold(pEntityUpdate.getMinVoltageDropProportionalThreshold()); + verify(pEntity).setPredefinedParameters(pEntityUpdate.getPredefinedParameters()); + verify(pEntity).setWithLoads(pEntityUpdate.isWithLoads()); + verify(pEntity).setWithShuntCompensators(pEntityUpdate.isWithShuntCompensators()); + verify(pEntity).setWithVscConverterStations(pEntityUpdate.isWithVscConverterStations()); + verify(pEntity).setWithNeutralPosition(pEntityUpdate.isWithNeutralPosition()); + verify(pEntity).setInitialVoltageProfileMode(pEntityUpdate.getInitialVoltageProfileMode()); + } +} diff --git a/src/test/resources/default_shorcircuit_parameters.json b/src/test/resources/default_shorcircuit_parameters.json new file mode 100644 index 00000000..4d0d4333 --- /dev/null +++ b/src/test/resources/default_shorcircuit_parameters.json @@ -0,0 +1,15 @@ +{ + "version": "1.3", + "withLimitViolations": true, + "withVoltageResult": false, + "withFeederResult": true, + "studyType": "TRANSIENT", + "minVoltageDropProportionalThreshold": 20.0, + "withFortescueResult": false, + "withLoads": false, + "withShuntCompensators": false, + "withVSCConverterStations": true, + "withNeutralPosition": true, + "initialVoltageProfileMode": "NOMINAL", + "detailedReport": false +} diff --git a/src/test/resources/org/gridsuite/shortcircuit/server/ShortCircuitParametersControllerTest.json b/src/test/resources/org/gridsuite/shortcircuit/server/ShortCircuitParametersControllerTest.json new file mode 100644 index 00000000..d95d866d --- /dev/null +++ b/src/test/resources/org/gridsuite/shortcircuit/server/ShortCircuitParametersControllerTest.json @@ -0,0 +1,42 @@ +{ + "predefinedParameters": null, + "parameters": null, + "cei909VoltageRanges": [ + { + "range": { + "comparator": "INSTANCE", + "maximum": 199.99, + "minimum": 10.0, + "naturalOrdering": true + }, + "rangeCoefficient": 1.1, + "voltage": "NaN", + "minimumNominalVoltage": 10.0, + "maximumNominalVoltage": 199.99 + }, + { + "range": { + "comparator": "INSTANCE", + "maximum": 299.99, + "minimum": 200.0, + "naturalOrdering": true + }, + "rangeCoefficient": 1.09, + "voltage": "NaN", + "minimumNominalVoltage": 200.0, + "maximumNominalVoltage": 299.99 + }, + { + "range": { + "comparator": "INSTANCE", + "maximum": 500.0, + "minimum": 300.0, + "naturalOrdering": true + }, + "rangeCoefficient": 1.05, + "voltage": "NaN", + "minimumNominalVoltage": 300.0, + "maximumNominalVoltage": 500.0 + } + ] +}