diff --git a/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowParametersEntity.java b/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowParametersEntity.java index 06fca1f1..31904b13 100644 --- a/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowParametersEntity.java +++ b/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowParametersEntity.java @@ -94,8 +94,8 @@ public class LoadFlowParametersEntity { @Column(name = "dcPowerFactor", columnDefinition = "double default 1.0") private double dcPowerFactor; - @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) - @JoinColumn(name = "load_flow_parameters_id") + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinColumn(name = "load_flow_parameters_entity_id", foreignKey = @ForeignKey(name = "loadFlowParametersEntity_specificParameters_fk")) private List specificParameters; public LoadFlowParametersEntity(LoadFlowParametersInfos loadFlowParametersInfos) { diff --git a/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowSpecificParameterEntity.java b/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowSpecificParameterEntity.java index ba051448..2b17c066 100644 --- a/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowSpecificParameterEntity.java +++ b/src/main/java/org/gridsuite/loadflow/server/entities/parameters/LoadFlowSpecificParameterEntity.java @@ -14,6 +14,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.Index; import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Getter; @@ -27,7 +28,9 @@ @AllArgsConstructor @Getter @Entity -@Table(name = "loadFlowSpecificParameters") +@Table(name = "loadFlowSpecificParameters", indexes = { + @Index(name = "loadFlowParametersEntity_specificParameters_index", columnList = "load_flow_parameters_entity_id") +}) public class LoadFlowSpecificParameterEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) diff --git a/src/main/java/org/gridsuite/loadflow/server/service/parameters/LoadFlowParametersService.java b/src/main/java/org/gridsuite/loadflow/server/service/parameters/LoadFlowParametersService.java index 2c31efc9..0ad52f13 100644 --- a/src/main/java/org/gridsuite/loadflow/server/service/parameters/LoadFlowParametersService.java +++ b/src/main/java/org/gridsuite/loadflow/server/service/parameters/LoadFlowParametersService.java @@ -30,7 +30,7 @@ public LoadFlowParametersService(LoadFlowParametersRepository loadFlowParameters } public UUID createParameters(LoadFlowParametersInfos parametersInfos) { - return loadFlowParametersRepository.save(parametersInfos.toEntity()).toLoadFlowParametersInfos().getUuid(); + return loadFlowParametersRepository.save(parametersInfos.toEntity()).getId(); } public LoadFlowParametersInfos getParameters(UUID parametersUuid) { @@ -59,6 +59,6 @@ public void deleteParameters(UUID parametersUuid) { public UUID duplicateParameters(UUID sourceParametersUuid) { LoadFlowParametersEntity sourceParameters = loadFlowParametersRepository.findById(sourceParametersUuid).orElseThrow(); LoadFlowParametersEntity duplicatedParameters = sourceParameters.copy(); - return loadFlowParametersRepository.save(duplicatedParameters).toLoadFlowParametersInfos().getUuid(); + return loadFlowParametersRepository.save(duplicatedParameters).getId(); } } diff --git a/src/main/resources/db/changelog/changesets/changelog_20231226T142800Z.xml b/src/main/resources/db/changelog/changesets/changelog_20231228T155403Z.xml similarity index 78% rename from src/main/resources/db/changelog/changesets/changelog_20231226T142800Z.xml rename to src/main/resources/db/changelog/changesets/changelog_20231228T155403Z.xml index cfa2e4a4..b4420ea6 100644 --- a/src/main/resources/db/changelog/changesets/changelog_20231226T142800Z.xml +++ b/src/main/resources/db/changelog/changesets/changelog_20231228T155403Z.xml @@ -1,6 +1,6 @@ - + @@ -22,7 +22,7 @@ - + @@ -30,7 +30,7 @@ - + @@ -38,18 +38,23 @@ - + - + - - + + + + - + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index a6de2ee5..ec5a6123 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -10,6 +10,6 @@ databaseChangeLog: file: changesets/changelog_20231115T10528Z.xml relativeToChangelogFile: true - include: - file: changesets/changelog_20231226T142800Z.xml + file: changesets/changelog_20231228T155403Z.xml relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/loadflow/server/LoadFlowControllerTest.java b/src/test/java/org/gridsuite/loadflow/server/LoadFlowControllerTest.java index aa6b1c42..9485bf29 100644 --- a/src/test/java/org/gridsuite/loadflow/server/LoadFlowControllerTest.java +++ b/src/test/java/org/gridsuite/loadflow/server/LoadFlowControllerTest.java @@ -34,6 +34,7 @@ import org.gridsuite.loadflow.server.service.LoadFlowExecutionService; import org.gridsuite.loadflow.server.service.ReportService; import org.gridsuite.loadflow.server.service.UuidGeneratorService; +import org.gridsuite.loadflow.server.service.parameters.LoadFlowParametersService; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -45,6 +46,7 @@ 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.boot.test.mock.mockito.SpyBean; import org.springframework.cloud.stream.binder.test.OutputDestination; import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; import org.springframework.http.MediaType; @@ -69,6 +71,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; 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; @@ -86,6 +89,7 @@ public class LoadFlowControllerTest { private static final UUID RESULT_UUID = UUID.fromString("0c8de370-3e6c-4d72-b292-d355a97e0d5d"); private static final UUID OTHER_RESULT_UUID = UUID.fromString("0c8de370-3e6c-4d72-b292-d355a97e0d5a"); private static final UUID REPORT_UUID = UUID.fromString("762b7298-8c0f-11ed-a1eb-0242ac120002"); + private static final UUID PARAMETERS_UUID = UUID.fromString("762b7298-8c0f-11ed-a1eb-0242ac120003"); private static final String VARIANT_1_ID = "variant_1"; private static final String VARIANT_2_ID = "variant_2"; @@ -123,6 +127,9 @@ private static final class LimitViolationsMock { @Autowired private LoadFlowExecutionService loadFlowExecutionService; + @SpyBean + private LoadFlowParametersService loadFlowParametersService; + @MockBean private UuidGeneratorService uuidGeneratorService; private final RestTemplateConfig restTemplateConfig = new RestTemplateConfig(); @@ -217,7 +224,7 @@ public void runTest() throws Exception { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); MvcResult result = mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "userId")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -267,11 +274,10 @@ public void testGetLimitViolations() throws Exception { .commonParameters(loadFlowParameters) .specificParameters(Collections.emptyMap()) .build(); - String jsonLoadFlowParameters = mapper.writeValueAsString(loadFlowParametersInfos); + doReturn(loadFlowParametersInfos).when(loadFlowParametersService).getParametersValues(any(), any()); MvcResult result = mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "&limitReduction=0.7", NETWORK_UUID) - .content(jsonLoadFlowParameters).contentType(MediaType.APPLICATION_JSON) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID + "&limitReduction=0.7", NETWORK_UUID) .header(HEADER_USER_ID, "userId")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -304,7 +310,7 @@ public void testDeleteResults() throws Exception { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); MvcResult result = mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "userId")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -342,7 +348,7 @@ public void stopTest() throws Exception { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&receiver=me&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "userId")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -393,7 +399,7 @@ public void runWithReportTest() { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&reporterId=myReporter&receiver=me&reportUuid=" + REPORT_UUID + "&variantId=" + VARIANT_2_ID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=LoadFlow&reporterId=myReporter&receiver=me&reportUuid=" + REPORT_UUID + "&variantId=" + VARIANT_2_ID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "user")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -412,7 +418,7 @@ public void runWithDefaultVariant() { .thenReturn(CompletableFuture.completedFuture(LoadFlowResultMock.RESULT)); mockMvc.perform(post( - "/" + VERSION + "/networks/{networkUuid}/run-and-save?reporterId=myReporter&receiver=me&reportUuid=" + REPORT_UUID, NETWORK_UUID) + "/" + VERSION + "/networks/{networkUuid}/run-and-save?reporterId=myReporter&receiver=me&reportUuid=" + REPORT_UUID + "¶metersUuid=" + PARAMETERS_UUID, NETWORK_UUID) .header(HEADER_USER_ID, "user")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) diff --git a/src/test/java/org/gridsuite/loadflow/server/LoadFlowParametersTest.java b/src/test/java/org/gridsuite/loadflow/server/LoadFlowParametersTest.java new file mode 100644 index 00000000..7cecfb32 --- /dev/null +++ b/src/test/java/org/gridsuite/loadflow/server/LoadFlowParametersTest.java @@ -0,0 +1,172 @@ +/** + * Copyright (c) 2023, 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.loadflow.server; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.gridsuite.loadflow.utils.assertions.Assertions.*; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersInfos; +import org.gridsuite.loadflow.server.entities.parameters.LoadFlowParametersEntity; +import org.gridsuite.loadflow.server.repositories.parameters.LoadFlowParametersRepository; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +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.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.http.MediaType; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.powsybl.loadflow.LoadFlowParameters; + +/** + * @author Ayoub LABIDI + */ +@RunWith(SpringRunner.class) +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +public class LoadFlowParametersTest { + + private static final String URI_PARAMETERS_BASE = "/v1/parameters"; + + private static final String URI_PARAMETERS_GET_PUT = URI_PARAMETERS_BASE + "/"; + + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected ObjectMapper mapper; + + @Autowired + private LoadFlowParametersRepository parametersRepository; + + @Before + public void setup() { + parametersRepository.deleteAll(); + } + + @After + public void tearOff() { + parametersRepository.deleteAll(); + } + + @Test + public void testCreate() throws Exception { + + LoadFlowParametersInfos parametersToCreate = buildParameters(); + String parametersToCreateJson = mapper.writeValueAsString(parametersToCreate); + + mockMvc.perform(post(URI_PARAMETERS_BASE).content(parametersToCreateJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andReturn(); + + LoadFlowParametersInfos createdParameters = parametersRepository.findAll().get(0).toLoadFlowParametersInfos(); + + assertThat(createdParameters).recursivelyEquals(parametersToCreate); + } + + @Test + public void testRead() throws Exception { + + LoadFlowParametersInfos parametersToRead = buildParameters(); + + UUID parametersUuid = saveAndRetunId(parametersToRead); + + MvcResult mvcResult = mockMvc.perform(get(URI_PARAMETERS_GET_PUT + parametersUuid)) + .andExpect(status().isOk()).andReturn(); + String resultAsString = mvcResult.getResponse().getContentAsString(); + LoadFlowParametersInfos receivedParameters = mapper.readValue(resultAsString, new TypeReference<>() { + }); + + assertThat(receivedParameters).recursivelyEquals(parametersToRead); + } + + @Test + public void testUpdate() throws Exception { + + LoadFlowParametersInfos parametersToUpdate = buildParameters(); + + UUID parametersUuid = saveAndRetunId(parametersToUpdate); + + parametersToUpdate = buildParametersUpdate(); + + String parametersToUpdateJson = mapper.writeValueAsString(parametersToUpdate); + + mockMvc.perform(put(URI_PARAMETERS_GET_PUT + parametersUuid).content(parametersToUpdateJson).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + LoadFlowParametersInfos updatedParameters = parametersRepository.findById(parametersUuid).get().toLoadFlowParametersInfos(); + + assertThat(updatedParameters).recursivelyEquals(parametersToUpdate); + } + + @Test + public void testDelete() throws Exception { + + LoadFlowParametersInfos parametersToDelete = buildParameters(); + + UUID parametersUuid = saveAndRetunId(parametersToDelete); + + mockMvc.perform(delete(URI_PARAMETERS_GET_PUT + parametersUuid)).andExpect(status().isOk()).andReturn(); + + List storedParameters = parametersRepository.findAll(); + + assertTrue(storedParameters.isEmpty()); + } + + @Test + public void testGetAll() throws Exception { + LoadFlowParametersInfos parameters1 = buildParameters(); + + LoadFlowParametersInfos parameters2 = buildParametersUpdate(); + + saveAndRetunId(parameters1); + + saveAndRetunId(parameters2); + + MvcResult mvcResult = mockMvc.perform(get(URI_PARAMETERS_BASE)) + .andExpect(status().isOk()).andReturn(); + String resultAsString = mvcResult.getResponse().getContentAsString(); + List receivedParameters = mapper.readValue(resultAsString, new TypeReference<>() { + }); + + assertThat(receivedParameters).hasSize(2); + } + + /** Save parameters into the repository and return its UUID. */ + protected UUID saveAndRetunId(LoadFlowParametersInfos parametersInfos) { + parametersRepository.save(parametersInfos.toEntity()); + return parametersRepository.findAll().get(0).getId(); + } + + protected LoadFlowParametersInfos buildParameters() { + return LoadFlowParametersInfos.builder() + .commonParameters(LoadFlowParameters.load()) + .specificParametersPerProvider(Map.of()) + .build(); + } + + protected LoadFlowParametersInfos buildParametersUpdate() { + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setDc(true); + return LoadFlowParametersInfos.builder() + .commonParameters(loadFlowParameters) + .specificParametersPerProvider(Map.of()) + .build(); + } +} diff --git a/src/test/java/org/gridsuite/loadflow/utils/assertions/Assertions.java b/src/test/java/org/gridsuite/loadflow/utils/assertions/Assertions.java new file mode 100644 index 00000000..00163888 --- /dev/null +++ b/src/test/java/org/gridsuite/loadflow/utils/assertions/Assertions.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2023, 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.loadflow.utils.assertions; + +import org.assertj.core.util.CheckReturnValue; +import org.gridsuite.loadflow.server.dto.parameters.LoadFlowParametersInfos; + +/** + * @author Tristan Chuine + * {@link org.assertj.core.api.Assertions Assertions} completed with our custom assertions classes. + */ +public class Assertions extends org.assertj.core.api.Assertions { + @CheckReturnValue + public static DTOAssert assertThat(LoadFlowParametersInfos actual) { + return new DTOAssert<>(actual); + } +} diff --git a/src/test/java/org/gridsuite/loadflow/utils/assertions/DTOAssert.java b/src/test/java/org/gridsuite/loadflow/utils/assertions/DTOAssert.java new file mode 100644 index 00000000..f6f0e522 --- /dev/null +++ b/src/test/java/org/gridsuite/loadflow/utils/assertions/DTOAssert.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2023, 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.loadflow.utils.assertions; + +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration; + +import java.time.ZonedDateTime; +import java.util.Date; +import java.util.UUID; + +/** + * @author Tristan Chuine + * @author Slimane Amar + */ +public class DTOAssert extends AbstractAssert, T> { + public DTOAssert(T actual) { + super(actual, DTOAssert.class); + } + + public DTOAssert recursivelyEquals(T other) { + isNotNull(); + usingRecursiveComparison(this.getRecursiveConfiguration()).isEqualTo(other); + return myself; + } + + private RecursiveComparisonConfiguration getRecursiveConfiguration() { + return RecursiveComparisonConfiguration.builder() + .withIgnoreAllOverriddenEquals(true) // For equals test, need specific tests + .withIgnoredFieldsOfTypes(UUID.class, Date.class, ZonedDateTime.class) // For these types, need specific tests (uuid from db for example) + .withIgnoreCollectionOrder(true) // For collection order test, need specific tests + .build(); + } +}