From 0b4ef678a8a998c44161ae8befa3fa5a042105ab Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Thu, 29 Jan 2026 14:05:01 +0100 Subject: [PATCH 1/3] TU sensitivity computation with wiremock Signed-off-by: SOUISSI Maissa (Externe) --- .../study/server/SensitivityAnalysisTest.java | 889 +++++++++--------- 1 file changed, 457 insertions(+), 432 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java b/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java index ef4f551e79..8d6083e958 100644 --- a/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java +++ b/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022, RTE (http://www.rte-france.com) + * Copyright (c) 2025, 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/. @@ -12,23 +12,13 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; import com.powsybl.commons.exceptions.UncheckedInterruptedException; +import com.powsybl.sensitivity.SensitivityAnalysisParameters; import com.powsybl.sensitivity.SensitivityFunctionType; -import com.powsybl.ws.commons.error.PowsyblWsProblemDetail; -import lombok.SneakyThrows; -import mockwebserver3.Dispatcher; -import mockwebserver3.MockResponse; -import mockwebserver3.MockWebServer; -import mockwebserver3.RecordedRequest; -import mockwebserver3.junit5.internal.MockWebServerExtension; -import okhttp3.Headers; -import okhttp3.HttpUrl; import org.gridsuite.study.server.dto.NodeReceiver; import org.gridsuite.study.server.dto.RootNetworkNodeInfo; import org.gridsuite.study.server.dto.sensianalysis.SensitivityAnalysisCsvFileInfos; import org.gridsuite.study.server.error.StudyException; -import org.gridsuite.study.server.networkmodificationtree.dto.InsertMode; -import org.gridsuite.study.server.networkmodificationtree.dto.NetworkModificationNode; -import org.gridsuite.study.server.networkmodificationtree.dto.RootNode; +import org.gridsuite.study.server.networkmodificationtree.dto.*; import org.gridsuite.study.server.notification.NotificationService; import org.gridsuite.study.server.repository.StudyEntity; import org.gridsuite.study.server.repository.StudyRepository; @@ -37,12 +27,9 @@ import org.gridsuite.study.server.utils.SendInput; import org.gridsuite.study.server.utils.TestUtils; import org.gridsuite.study.server.utils.elasticsearch.DisableElasticsearch; -import org.jetbrains.annotations.NotNull; +import org.gridsuite.study.server.utils.wiremock.*; import org.json.JSONObject; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.*; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,35 +46,34 @@ import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.MessageBuilder; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import java.util.*; +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NOT_FOUND; -import static org.gridsuite.study.server.StudyConstants.HEADER_RECEIVER; +import static org.gridsuite.study.server.StudyConstants.*; import static org.gridsuite.study.server.StudyConstants.HEADER_USER_ID; import static org.gridsuite.study.server.dto.ComputationType.SENSITIVITY_ANALYSIS; -import static org.gridsuite.study.server.notification.NotificationService.HEADER_UPDATE_TYPE; -import static org.gridsuite.study.server.notification.NotificationService.UPDATE_TYPE_COMPUTATION_PARAMETERS; -import static org.gridsuite.study.server.utils.TestUtils.USER_DEFAULT_PROFILE_JSON; -import static org.gridsuite.study.server.utils.TestUtils.getBinaryAsBuffer; +import static org.gridsuite.study.server.error.StudyBusinessErrorCode.NOT_FOUND; +import static org.gridsuite.study.server.notification.NotificationService.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** - * @author Franck Lecuyer + * @author Maissa SOUISSI */ -@ExtendWith(MockWebServerExtension.class) @AutoConfigureMockMvc @SpringBootTest @DisableElasticsearch @ContextConfigurationWithTestChannel class SensitivityAnalysisTest { + private static final Logger LOGGER = LoggerFactory.getLogger(SensitivityAnalysisTest.class); private static final String SENSITIVITY_ANALYSIS_RESULT_UUID = "b3a84c9b-9594-4e85-8ec7-07ea965d24eb"; @@ -109,8 +95,6 @@ class SensitivityAnalysisTest { private static final String CASE_3_UUID_STRING = "790769f9-bd31-43be-be46-e50296951e32"; private static final UUID CASE_3_UUID = UUID.fromString(CASE_3_UUID_STRING); - private static final String SENSITIVITY_ANALYSIS_PROFILE_PARAMETERS_JSON = "{\"flowFlowSensitivityValueThreshold\":30.0,\"voltageVoltageSensitivityValueThreshold\":0.4,\"flowVoltageSensitivityValueThreshold\":0.0,\"angleFlowSensitivityValueThreshold\":0.0}"; - private static final String VARIANT_ID = "variant_1"; private static final String VARIANT_ID_2 = "variant_2"; private static final String VARIANT_ID_3 = "variant_3"; @@ -129,16 +113,30 @@ class SensitivityAnalysisTest { private static final String USER_PROFILE_INVALID_PARAMS_JSON = "{\"id\":\"97bb1890-a90c-43c3-a004-e631246d42d6\",\"name\":\"Profile with broken params\",\"sensitivityAnalysisParameterId\":\"" + PROFILE_SENSITIVITY_ANALYSIS_INVALID_PARAMETERS_UUID_STRING + "\",\"allParametersLinksValid\":false}"; private static final String DUPLICATED_PARAMS_JSON = "\"" + PROFILE_SENSITIVITY_ANALYSIS_DUPLICATED_PARAMETERS_UUID_STRING + "\""; public static final String SENSITIVITY_ANALYSIS_DEFAULT_PARAMETERS_JSON = "{\"flowFlowSensitivityValueThreshold\":0.0,\"angleFlowSensitivityValueThreshold\":0.0,\"flowVoltageSensitivityValueThreshold\":0.0," + - "\"sensitivityInjectionsSet\":[],\"sensitivityInjection\":[],\"sensitivityHVDC\":[],\"sensitivityPST\":[],\"sensitivityNodes\":[]}"; + "\"sensitivityInjectionsSet\":[],\"sensitivityInjection\":[],\"sensitivityHVDC\":[],\"sensitivityPST\":[],\"sensitivityNodes\":[]}"; public static final String SENSITIVITY_ANALYSIS_UPDATED_PARAMETERS_JSON = "{\"flowFlowSensitivityValueThreshold\":90.0,\"angleFlowSensitivityValueThreshold\":0.6,\"flowVoltageSensitivityValueThreshold\":0.1,\"sensitivityInjectionsSet\":[{\"monitoredBranches\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"injections\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"distributionType\":\"PROPORTIONAL\",\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}],\"sensitivityInjection\":[{\"monitoredBranches\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"injections\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}],\"sensitivityHVDC\":[{\"monitoredBranches\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"sensitivityType\":\"DELTA_MW\",\"hvdcs\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}],\"sensitivityPST\":[{\"monitoredBranches\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"sensitivityType\":\"DELTA_MW\",\"psts\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}],\"sensitivityNodes\":[{\"monitoredVoltageLevels\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"equipmentsInVoltageRegulation\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}]}"; + private static final String SENSITIVITY_ANALYSIS_PROFILE_PARAMETERS_JSON = "{\"lowVoltageAbsoluteThreshold\":30.0,\"lowVoltageProportionalThreshold\":0.4,\"highVoltageAbsoluteThreshold\":0.0,\"highVoltageProportionalThreshold\":0.0,\"flowProportionalThreshold\":0.1}"; + + //output destinations + private static final String STUDY_UPDATE_DESTINATION = "study.update"; + private static final String ELEMENT_UPDATE_DESTINATION = "element.update"; + private static final String SENSITIVITY_ANALYSIS_RESULT_DESTINATION = "sensitivityanalysis.result"; + private static final String SENSITIVITY_ANALYSIS_STOPPED_DESTINATION = "sensitivityanalysis.stopped"; + private static final String SENSITIVITY_ANALYSIS_FAILED_DESTINATION = "sensitivityanalysis.run.dlx"; + + private static final byte[] SENSITIVITY_RESULTS_AS_ZIPPED_CSV = {0x00, 0x01}; + private static final long TIMEOUT = 1000; + private static WireMockServer wireMockServer; + private ComputationServerStubs computationServerStubs; + private ReportServerStubs reportServerStubs; + private UserAdminServerStubs userAdminServerStubs; + @Autowired private MockMvc mockMvc; - private WireMockServer wireMock; - @Autowired private OutputDestination output; @@ -177,233 +175,77 @@ class SensitivityAnalysisTest { @Autowired private ConsumerService consumerService; - //output destinations - private static final String STUDY_UPDATE_DESTINATION = "study.update"; - private static final String ELEMENT_UPDATE_DESTINATION = "element.update"; - private static final String SENSITIVITY_ANALYSIS_RESULT_DESTINATION = "sensitivityanalysis.result"; - private static final String SENSITIVITY_ANALYSIS_STOPPED_DESTINATION = "sensitivityanalysis.stopped"; - private static final String SENSITIVITY_ANALYSIS_FAILED_DESTINATION = "sensitivityanalysis.run.dlx"; - - private static final byte[] SENSITIVITY_RESULTS_AS_ZIPPED_CSV = {0x00, 0x01}; + private static final SensitivityAnalysisParameters SENSITIVITY_ANALYSIS_PARAMETERS = new SensitivityAnalysisParameters(); @Autowired private RootNetworkNodeInfoService rootNetworkNodeInfoService; @Autowired private TestUtils studyTestUtils; - @BeforeEach - void setup(final MockWebServer server) { - objectWriter = objectMapper.writer().withDefaultPrettyPrinter(); - - wireMock = new WireMockServer(wireMockConfig().dynamicPort().extensions(new SendInput(input))); - - // Start the server. - wireMock.start(); - - // Ask the server for its URL. You'll need this to make HTTP requests. - HttpUrl baseHttpUrl = server.url(""); - String baseUrl = baseHttpUrl.toString().substring(0, baseHttpUrl.toString().length() - 1); - sensitivityAnalysisService.setSensitivityAnalysisServerBaseUri(baseUrl); - actionsService.setActionsServerBaseUri(baseUrl); - reportService.setReportServerBaseUri(baseUrl); - loadFlowService.setLoadFlowServerBaseUri(baseUrl); - userAdminService.setUserAdminServerBaseUri(baseUrl); - - final Dispatcher dispatcher = new Dispatcher() { - @SneakyThrows - @Override - @NotNull - public MockResponse dispatch(RecordedRequest request) { - String path = Objects.requireNonNull(request.getPath()); - String method = Objects.requireNonNull(request.getMethod()); - - if (path.matches("/v1/networks/" + NETWORK_UUID_STRING + "/run-and-save.*")) { - String resultUuid = path.matches(".*variantId=" + VARIANT_ID_3 + ".*") ? SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID : SENSITIVITY_ANALYSIS_RESULT_UUID; - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), "\"" + resultUuid + "\""); - } else if (path.matches("/v1/networks/" + NETWORK_UUID_2_STRING + "/run-and-save.*")) { - input.send(MessageBuilder.withPayload("") - .setHeader("receiver", "%7B%22nodeUuid%22%3A%22" + request.getPath().split("%")[5].substring(4) + "%22%2C%20%22rootNetworkUuid%22%3A%20%22" + request.getPath().split("%")[11].substring(4) + "%22%2C%20%22userId%22%3A%22userId%22%7D") - .setHeader(HEADER_USER_ID, "testUserId") - .build(), SENSITIVITY_ANALYSIS_FAILED_DESTINATION); - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), "\"" + SENSITIVITY_ANALYSIS_ERROR_NODE_RESULT_UUID + "\""); - } else if (path.matches("/v1/networks/" + NETWORK_UUID_3_STRING + "/run-and-save.*")) { - input.send(MessageBuilder.withPayload("") - .setHeader(HEADER_USER_ID, "testUserId") - .build(), SENSITIVITY_ANALYSIS_FAILED_DESTINATION); - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), "\"" + SENSITIVITY_ANALYSIS_ERROR_NODE_RESULT_UUID + "\""); - } else if (path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID + "/stop.*") - || path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID + "/stop.*")) { - String resultUuid = path.matches(".*variantId=" + VARIANT_ID_3 + ".*") ? SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID : SENSITIVITY_ANALYSIS_RESULT_UUID; - input.send(MessageBuilder.withPayload("") - .setHeader("resultUuid", resultUuid) - .setHeader("receiver", "%7B%22nodeUuid%22%3A%22" + request.getPath().split("%")[5].substring(4) + "%22%2C%20%22rootNetworkUuid%22%3A%20%22" + request.getPath().split("%")[11].substring(4) + "%22%2C%20%22userId%22%3A%22userId%22%7D") - .build(), SENSITIVITY_ANALYSIS_STOPPED_DESTINATION); - return new MockResponse(200); - } else if (path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID)) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), FAKE_RESULT_JSON); - } else if (path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID + "/status") - || path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID + "/status")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), SENSITIVITY_ANALYSIS_STATUS_JSON); - } else if (path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID + "\\?.*") - || path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID + "\\?.*")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), FAKE_RESULT_JSON); - } else if (path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID + "/filter-options" + "\\?.*") - || path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID + "/filter-options" + "\\?.*")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), FAKE_RESULT_JSON); - } else if (path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID + "/csv\\?selector=fakeJsonSelector") - || path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID + "/csv\\?selector=fakeJsonSelector&filters=lineId2&globalFilters=ss&networkUuid=.*&variantId=.*") - || path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID + "/csv\\?selector=fakeJsonSelector") - || path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID + "/csv\\?selector=fakeJsonSelector&filters=lineId2&globalFilters=ss&networkUuid=.*&variantId=.*")) { - return new MockResponse.Builder().code(200).body(getBinaryAsBuffer(SENSITIVITY_RESULTS_AS_ZIPPED_CSV)) - .addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build(); - } else if (path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID) && request.getMethod().equals("DELETE")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), SENSITIVITY_ANALYSIS_STATUS_JSON); - } else if (path.matches("/v1/results/invalidate-status?resultUuid=" + SENSITIVITY_ANALYSIS_RESULT_UUID) - || path.matches("/v1/results/invalidate-status?resultUuid=" + SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID)) { - return new MockResponse(200); - } else if (path.matches("/v1/results\\?resultsUuids.*")) { - return new MockResponse(200); - } else if (path.matches("/v1/reports")) { - return new MockResponse(200); - } else if (path.matches("/v1/supervision/results-count")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), "1"); - } else if (path.matches("/v1/results/invalidate-status\\?resultUuid=.*")) { - return new MockResponse(200); - } else if (path.matches("/v1/networks/" + ".*" + "/factor-count")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), FAKE_RESULT_JSON); - } else if (path.matches("/v1/parameters")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), objectMapper.writeValueAsString(SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING)); - } else if (path.matches("/v1/users/" + NO_PROFILE_USER_ID + "/profile")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), USER_DEFAULT_PROFILE_JSON); - } else if (path.matches("/v1/users/" + NO_PARAMS_IN_PROFILE_USER_ID + "/profile")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), USER_PROFILE_NO_PARAMS_JSON); - } else if (path.matches("/v1/users/" + VALID_PARAMS_IN_PROFILE_USER_ID + "/profile")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), USER_PROFILE_VALID_PARAMS_JSON); - } else if (path.matches("/v1/users/" + INVALID_PARAMS_IN_PROFILE_USER_ID + "/profile")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), USER_PROFILE_INVALID_PARAMS_JSON); - } else if (path.matches("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID)) { - if (method.equals("GET")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), SENSITIVITY_ANALYSIS_DEFAULT_PARAMETERS_JSON); - } else { - //Method PUT - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), objectMapper.writeValueAsString(SENSITIVITY_ANALYSIS_PARAMETERS_UUID)); - } - } else if (path.matches("/v1/parameters\\?duplicateFrom=" + PROFILE_SENSITIVITY_ANALYSIS_INVALID_PARAMETERS_UUID_STRING) && method.equals("POST")) { - // params duplication request KO - return new MockResponse(404); - } else if (path.matches("/v1/parameters/" + PROFILE_SENSITIVITY_ANALYSIS_INVALID_PARAMETERS_UUID_STRING) && method.equals("GET")) { - return new MockResponse(404); - } else if (path.matches("/v1/parameters\\?duplicateFrom=" + PROFILE_SENSITIVITY_ANALYSIS_VALID_PARAMETERS_UUID_STRING) && method.equals("POST")) { - // params duplication request OK - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), DUPLICATED_PARAMS_JSON); - } else if (path.matches("/v1/parameters/" + PROFILE_SENSITIVITY_ANALYSIS_VALID_PARAMETERS_UUID_STRING) && method.equals("GET")) { - // profile params get request OK - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), SENSITIVITY_ANALYSIS_PROFILE_PARAMETERS_JSON); - } else if (path.matches("/v1/parameters") && method.equals("POST")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), objectMapper.writeValueAsString(SENSITIVITY_ANALYSIS_PARAMETERS_UUID)); - } else if (path.matches("/v1/parameters/default") && method.equals("POST")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), objectMapper.writeValueAsString(SENSITIVITY_ANALYSIS_PARAMETERS_UUID)); - } else if (path.matches("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID + "\\?filters=.*globalFilters=.*networkUuid=.*variantId.*sort=.*")) { - return new MockResponse(200, Headers.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE), SENSITIVITY_ANALYSIS_RESULT_UUID); - } else { - LOGGER.error("Unhandled method+path: {} {}", request.getMethod(), request.getPath()); - return new MockResponse.Builder().code(418).body("Unhandled method+path: " + request.getMethod() + " " + request.getPath()).build(); - } - } - }; - server.setDispatcher(dispatcher); + @BeforeAll + static void initWireMock(@Autowired InputDestination input) { + wireMockServer = new WireMockServer(wireMockConfig().dynamicPort().extensions(new SendInput(input))); + wireMockServer.start(); } - private void testSensitivityAnalysisWithRootNetworkUuidAndNodeUuid(final MockWebServer server, UUID studyUuid, UUID rootNetworkUuid, UUID nodeUuid, UUID resultUuid) throws Exception { - // sensitivity analysis not found - mockMvc.perform( - get( - "/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?selector=subjectId", - studyUuid, - rootNetworkUuid, - NOT_FOUND_NODE_UUID)) - .andExpect(status().isNotFound()); - - // run sensitivity analysis - mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", studyUuid, rootNetworkUuid, nodeUuid) - .header("userId", "userId") - .header(HEADER_USER_ID, "testUserId")).andExpect(status().isOk()); - - consumeSensitivityAnalysisResult(studyUuid, rootNetworkUuid, nodeUuid, resultUuid.toString()); + @AfterAll + static void shutdownWireMock() { + if (wireMockServer != null) { + wireMockServer.shutdown(); + } + } - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.matches("/v1/networks/" + NETWORK_UUID_STRING + "/run-and-save.*?receiver=.*nodeUuid.*"))); + @BeforeEach + void setup() throws JsonProcessingException { + computationServerStubs = new ComputationServerStubs(wireMockServer); + reportServerStubs = new ReportServerStubs(wireMockServer); + userAdminServerStubs = new UserAdminServerStubs(wireMockServer); - // get sensitivity analysis result - mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?selector={selector}", studyUuid, rootNetworkUuid, nodeUuid, "fakeJsonSelector")) - .andExpectAll(status().isOk(), content().string(FAKE_RESULT_JSON)); + objectWriter = objectMapper.writer().withDefaultPrettyPrinter(); - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.contains("/v1/results/" + resultUuid))); + sensitivityAnalysisService.setSensitivityAnalysisServerBaseUri(wireMockServer.baseUrl()); + actionsService.setActionsServerBaseUri(wireMockServer.baseUrl()); + reportService.setReportServerBaseUri(wireMockServer.baseUrl()); - // get sensitivity analysis result filter options - mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/filter-options?selector={selector}", studyUuid, rootNetworkUuid, nodeUuid, "fakeJsonSelector")) - .andExpectAll(status().isOk(), content().string(FAKE_RESULT_JSON)); - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.contains("/v1/results/" + resultUuid + "/filter-options"))); + loadFlowService.setLoadFlowServerBaseUri(wireMockServer.baseUrl()); + userAdminService.setUserAdminServerBaseUri(wireMockServer.baseUrl()); + } - // get result with filters , globalFilters and sort - mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?filters=lineId2&selector=subjectId&globalFilters=ss", studyUuid, rootNetworkUuid, nodeUuid)) - .andExpectAll(status().isOk(), content().string(FAKE_RESULT_JSON)); + private StudyEntity insertDummyStudy(UUID networkUuid, UUID caseUuid, UUID sensitivityAnalysisParametersUuid) { + StudyEntity studyEntity = TestUtils.createDummyStudy(networkUuid, "netId", caseUuid, "", "", null, UUID.randomUUID(), null, null, sensitivityAnalysisParametersUuid, null, null); + var study = studyRepository.save(studyEntity); + networkModificationTreeService.createRoot(studyEntity); + return study; + } - Set actualRequests = TestUtils.getRequestsDone(1, server); - assertTrue(actualRequests.stream().anyMatch(request -> request.contains("/v1/results/" + resultUuid) && request.contains("selector=subjectId") && request.contains("filters=lineId2") && request.contains("globalFilters=ss") - )); + private NetworkModificationNode createNetworkModificationNode(UUID studyUuid, UUID parentNodeUuid, + UUID modificationGroupUuid, String variantId, String nodeName) throws Exception { + NetworkModificationNode modificationNode = NetworkModificationNode.builder().name(nodeName) + .description("description").modificationGroupUuid(modificationGroupUuid).variantId(variantId) + .children(Collections.emptyList()).build(); - // get sensitivity analysis status - mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/status", studyUuid, rootNetworkUuid, nodeUuid)).andExpectAll( - status().isOk(), - content().string(SENSITIVITY_ANALYSIS_STATUS_JSON)); + // Only for tests + String mnBodyJson = objectWriter.writeValueAsString(modificationNode); + JSONObject jsonObject = new JSONObject(mnBodyJson); + jsonObject.put("variantId", variantId); + jsonObject.put("modificationGroupUuid", modificationGroupUuid); + mnBodyJson = jsonObject.toString(); - assertTrue(TestUtils.getRequestsDone(1, server).contains(String.format("/v1/results/%s/status", resultUuid))); + mockMvc.perform(post("/v1/studies/{studyUuid}/tree/nodes/{id}", studyUuid, parentNodeUuid).content(mnBodyJson).contentType(MediaType.APPLICATION_JSON).header("userId", "userId")) + .andExpect(status().isOk()); + var mess = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); + assertNotNull(mess); + modificationNode.setId(UUID.fromString(String.valueOf(mess.getHeaders().get(NotificationService.HEADER_NEW_NODE)))); + assertEquals(InsertMode.CHILD.name(), mess.getHeaders().get(NotificationService.HEADER_INSERT_MODE)); - // export sensitivity analysis result as csv - String content = objectMapper.writeValueAsString(SensitivityAnalysisCsvFileInfos.builder() - .sensitivityFunctionType(SensitivityFunctionType.BRANCH_CURRENT_1) - .resultTab("N") - .csvHeaders(List.of("h1", "h2", "h3")) - .language("en") - .build()); - - // error case - var result = mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/csv?selector=fakeJsonSelector", studyUuid, rootNetworkUuid, UUID.randomUUID()) - .contentType(MediaType.APPLICATION_JSON) - .header("userId", "userId") - .content(content)) - .andExpect(status().isNotFound()) - .andReturn(); - var problemDetail = objectMapper.readValue(result.getResponse().getContentAsString(), PowsyblWsProblemDetail.class); - assertEquals(NOT_FOUND.value(), problemDetail.getBusinessErrorCode()); - - // csv export with no filter - mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/csv?selector=fakeJsonSelector", studyUuid, rootNetworkUuid, nodeUuid) - .contentType(MediaType.APPLICATION_JSON) - .header("userId", "userId") - .content(content)) - .andExpectAll(status().isOk(), content().bytes(SENSITIVITY_RESULTS_AS_ZIPPED_CSV)); - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.contains("/v1/results/" + resultUuid + "/csv?selector=fakeJsonSelector"))); - - // csv export with filters - mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/csv?selector=fakeJsonSelector&filters=lineId2&globalFilters=ss", studyUuid, rootNetworkUuid, nodeUuid) - .contentType(MediaType.APPLICATION_JSON) - .header("userId", "userId") - .content(content)) - .andExpectAll(status().isOk(), content().bytes(SENSITIVITY_RESULTS_AS_ZIPPED_CSV)); - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.contains("/v1/results/" + resultUuid + "/csv") && r.contains("filters=lineId2") && r.contains("globalFilters=ss"))); - - // stop sensitivity analysis - mockMvc.perform(put("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/stop", studyUuid, rootNetworkUuid, nodeUuid) - .header(HEADER_USER_ID, "userId")) - .andExpect(status().isOk()); + rootNetworkNodeInfoService.updateRootNetworkNode(modificationNode.getId(), studyTestUtils.getOneRootNetworkUuid(studyUuid), + RootNetworkNodeInfo.builder().variantId(variantId).build()); - Message sensitivityAnalysisStatusMessage = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid, sensitivityAnalysisStatusMessage.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); - String updateType = (String) sensitivityAnalysisStatusMessage.getHeaders().get(HEADER_UPDATE_TYPE); - assertTrue(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS.equals(updateType) || NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_RESULT.equals(updateType)); + return modificationNode; + } - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.matches("/v1/results/" + resultUuid + "/stop\\?receiver=.*nodeUuid.*"))); + private UUID getRootNodeUuid(UUID studyUuid) { + return networkModificationTreeService.getStudyRootNodeUuid(studyUuid); } private void consumeSensitivityAnalysisResult(UUID studyUuid, UUID rootNetworkUuid, UUID nodeUuid, String resultUuid) throws JsonProcessingException { @@ -429,7 +271,7 @@ private void consumeSensitivityAnalysisResult(UUID studyUuid, UUID rootNetworkUu } @Test - void testSensitivityAnalysis(final MockWebServer server) throws Exception { + void testSensitivityAnalysis() throws Exception { //insert a study StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID, SENSITIVITY_ANALYSIS_PARAMETERS_UUID); UUID studyNameUserIdUuid = studyEntity.getId(); @@ -444,68 +286,194 @@ void testSensitivityAnalysis(final MockWebServer server) throws Exception { // run sensitivity analysis on root node (not allowed) mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", studyNameUserIdUuid, firstRootNetworkUuid, rootNodeUuid) - .header(HEADER_USER_ID, "testUserId")) + .header(HEADER_USER_ID, "testUserId")) .andExpect(status().isForbidden()); - testSensitivityAnalysisWithRootNetworkUuidAndNodeUuid(server, studyNameUserIdUuid, firstRootNetworkUuid, modificationNode1Uuid, UUID.fromString(SENSITIVITY_ANALYSIS_RESULT_UUID)); - testSensitivityAnalysisWithRootNetworkUuidAndNodeUuid(server, studyNameUserIdUuid, firstRootNetworkUuid, modificationNode3Uuid, UUID.fromString(SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID)); + testSensitivityAnalysisWithRootNetworkUuidAndNodeUuid(studyNameUserIdUuid, firstRootNetworkUuid, modificationNode1Uuid, SENSITIVITY_ANALYSIS_RESULT_UUID); + testSensitivityAnalysisWithRootNetworkUuidAndNodeUuid(studyNameUserIdUuid, firstRootNetworkUuid, modificationNode3Uuid, SENSITIVITY_ANALYSIS_OTHER_NODE_RESULT_UUID); + + // --- 1. Non-existing node → 404 / No Content --- + + wireMockServer.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/.*")) + .willReturn(WireMock.notFound().withBody("Node result not found"))); mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?selector={selector}", studyNameUserIdUuid, firstRootNetworkUuid, UUID.randomUUID(), "fakeJsonSelector")) - .andExpectAll(status().isNotFound()); + .andExpect(status().isNotFound()); + + wireMockServer.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/.*/filter-options")) + .willReturn(WireMock.noContent())); mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/filter-options?selector={selector}", - studyNameUserIdUuid, firstRootNetworkUuid, UUID.randomUUID(), "fakeJsonSelector")) - .andExpectAll(status().isNoContent()); + studyNameUserIdUuid, firstRootNetworkUuid, UUID.randomUUID(), "fakeJsonSelector")) + .andExpect(status().isNoContent()); - // run additional sensitivity analysis for deletion test - mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", studyNameUserIdUuid, firstRootNetworkUuid, modificationNode2Uuid) - .header(HEADER_USER_ID, "testUserId")).andExpect(status().isOk()) - .andReturn(); + // --- 2. Run additional sensitivity analysis for deletion test --- + computationServerStubs.stubComputationRun(NETWORK_UUID_STRING, null, SENSITIVITY_ANALYSIS_RESULT_UUID); + + mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", + studyNameUserIdUuid, firstRootNetworkUuid, modificationNode2Uuid) + .header(HEADER_USER_ID, "testUserId")) + .andExpect(status().isOk()); consumeSensitivityAnalysisResult(studyNameUserIdUuid, firstRootNetworkUuid, modificationNode2Uuid, SENSITIVITY_ANALYSIS_RESULT_UUID); - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.matches("/v1/networks/" + NETWORK_UUID_STRING + "/run-and-save.*?receiver=.*nodeUuid.*"))); + // --- 3. Test result count (dryRun) --- + computationServerStubs.stubResultsCount(1); - //Test result count mockMvc.perform(delete("/v1/supervision/computation/results") .queryParam("type", SENSITIVITY_ANALYSIS.toString()) .queryParam("dryRun", "true")) .andExpect(status().isOk()); - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.matches("/v1/supervision/results-count"))); - //Delete Sensitivity analysis results + computationServerStubs.verifyResultsCountGet(); + + // --- 4. Delete Sensitivity analysis results --- assertEquals(1, rootNetworkNodeInfoRepository.findAllBySensitivityAnalysisResultUuidNotNull().size()); + + computationServerStubs.stubDeleteResults("/v1/results"); + reportServerStubs.stubDeleteReport(); + mockMvc.perform(delete("/v1/supervision/computation/results") .queryParam("type", SENSITIVITY_ANALYSIS.toString()) .queryParam("dryRun", "false")) .andExpect(status().isOk()); - var requests = TestUtils.getRequestsDone(2, server); - assertTrue(requests.stream().anyMatch(r -> r.matches("/v1/results\\?resultsUuids"))); - assertTrue(requests.stream().anyMatch(r -> r.matches("/v1/reports"))); + // Verify delete requests + WireMockUtilsCriteria.verifyDeleteRequest(wireMockServer, "/v1/results", Map.of("resultsUuids", WireMock.matching(".*"))); + reportServerStubs.verifyDeleteReport(); + + // Verify repository is empty after deletion assertEquals(0, rootNetworkNodeInfoRepository.findAllBySensitivityAnalysisResultUuidNotNull().size()); - String baseUrlWireMock = wireMock.baseUrl(); - sensitivityAnalysisService.setSensitivityAnalysisServerBaseUri(baseUrlWireMock); + } - wireMock.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID)) - .willReturn(WireMock.notFound().withBody("Oups did I ever let think suc a thing existed ?"))); - mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?selector={selector}", - studyNameUserIdUuid, firstRootNetworkUuid, modificationNode1Uuid, "fakeJsonSelector")) - .andExpectAll(status().isNoContent()); + private void testSensitivityAnalysisWithRootNetworkUuidAndNodeUuid(UUID studyUuid, + UUID rootNetworkUuid, + UUID nodeUuid, + String resultUuid) throws Exception { - wireMock.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID)) - .willReturn(WireMock.serverError().withBody("{ \"message\": \"Oups\" }"))); + // --- 1. Sensitivity analysis not found --- mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?selector={selector}", - studyNameUserIdUuid, firstRootNetworkUuid, modificationNode1Uuid, "fakeJsonSelector")) - .andExpectAll(status().isNoContent()); + studyUuid, rootNetworkUuid, NOT_FOUND_NODE_UUID, "subjectId")) + .andExpect(status().isNotFound()); + + // --- 2. Run sensitivity analysis --- + computationServerStubs.stubComputationRun(NETWORK_UUID_STRING, null, resultUuid); + + mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", + studyUuid, rootNetworkUuid, nodeUuid) + .header(HEADER_USER_ID, "testUserId")) + .andExpect(status().isOk()); + + consumeSensitivityAnalysisResult(studyUuid, rootNetworkUuid, nodeUuid, resultUuid); + + // --- 3. GET sensitivity analysis result --- + wireMockServer.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/" + resultUuid + ".*")) + .willReturn(WireMock.ok().withBody(FAKE_RESULT_JSON))); - wireMock.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/" + SENSITIVITY_ANALYSIS_RESULT_UUID)) - .willReturn(WireMock.serverError().withBody("flat message"))); mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?selector={selector}", - studyNameUserIdUuid, firstRootNetworkUuid, modificationNode1Uuid, "fakeJsonSelector")) - .andExpectAll(status().isNoContent()); + studyUuid, rootNetworkUuid, nodeUuid, "fakeJsonSelector")) + .andExpectAll(status().isOk(), content().string(FAKE_RESULT_JSON)); + + wireMockServer.verify(1, WireMock.getRequestedFor(WireMock.urlPathMatching("/v1/results/" + resultUuid + ".*"))); + + // --- 4. GET sensitivity analysis filter-options --- + wireMockServer.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/" + resultUuid + "/filter-options.*")) + .willReturn(WireMock.ok().withBody(FAKE_RESULT_JSON))); + + mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/filter-options?selector={selector}", + studyUuid, rootNetworkUuid, nodeUuid, "fakeJsonSelector")) + .andExpectAll(status().isOk(), content().string(FAKE_RESULT_JSON)); + + wireMockServer.verify(1, WireMock.getRequestedFor(WireMock.urlPathMatching("/v1/results/" + resultUuid + "/filter-options.*"))); + + // --- 5. GET sensitivity analysis with filters and globalFilters --- + wireMockServer.stubFor(WireMock.get(WireMock.urlPathEqualTo("/v1/results/" + resultUuid)) + .withQueryParam("selector", WireMock.matching("subjectId")) + .withQueryParam("filters", WireMock.matching(".*")) // match any value + .withQueryParam("globalFilters", WireMock.matching(".*")) + .withQueryParam("networkUuid", WireMock.matching(".*")) + .withQueryParam("variantId", WireMock.matching(".*")) + .willReturn(WireMock.ok().withBody(FAKE_RESULT_JSON))); + + mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?filters=lineId2&selector=subjectId&globalFilters=ss", + studyUuid, rootNetworkUuid, nodeUuid)) + .andExpectAll(status().isOk(), content().string(FAKE_RESULT_JSON)); + + wireMockServer.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo("/v1/results/" + resultUuid)) + .withQueryParam("selector", WireMock.equalTo("subjectId")) + .withQueryParam("filters", WireMock.equalTo("lineId2")) + .withQueryParam("globalFilters", WireMock.equalTo("ss")) + .withQueryParam("networkUuid", WireMock.matching(".*")) + .withQueryParam("variantId", WireMock.matching(".*")) + ); + + // --- 6. GET sensitivity analysis status --- + computationServerStubs.stubGetResultStatus(resultUuid, SENSITIVITY_ANALYSIS_STATUS_JSON); + + mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/status", + studyUuid, rootNetworkUuid, nodeUuid)) + .andExpectAll(status().isOk(), content().string(SENSITIVITY_ANALYSIS_STATUS_JSON)); + + computationServerStubs.verifyGetResultStatus(resultUuid); + + // --- 7. Export CSV --- + String content = objectMapper.writeValueAsString(SensitivityAnalysisCsvFileInfos.builder() + .sensitivityFunctionType(SensitivityFunctionType.BRANCH_CURRENT_1) + .resultTab("N") + .csvHeaders(List.of("h1", "h2", "h3")) + .language("en") + .build()); + + // CSV error case + mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/csv?selector=fakeJsonSelector", + studyUuid, rootNetworkUuid, UUID.randomUUID()) + .contentType(MediaType.APPLICATION_JSON) + .header(HEADER_USER_ID, "userId") + .content(content)) + .andExpect(status().isNotFound()); + // CSV export success + wireMockServer.stubFor(WireMock.post(WireMock.urlPathMatching("/v1/results/" + resultUuid + "/csv.*")) + .willReturn(WireMock.aResponse() + .withStatus(200) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE) + .withBody(SENSITIVITY_RESULTS_AS_ZIPPED_CSV))); + + mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/csv?selector=fakeJsonSelector", + studyUuid, rootNetworkUuid, nodeUuid) + .contentType(MediaType.APPLICATION_JSON) + .header(HEADER_USER_ID, "userId") + .content(content)) + .andExpectAll(status().isOk(), content().bytes(SENSITIVITY_RESULTS_AS_ZIPPED_CSV)); + + wireMockServer.verify(1, WireMock.postRequestedFor(WireMock.urlPathMatching("/v1/results/" + resultUuid + "/csv.*"))); + + // --- 8. Stop sensitivity analysis --- + wireMockServer.stubFor(WireMock.put(WireMock.urlPathMatching("/v1/results/" + resultUuid + "/stop")) + .willReturn(WireMock.ok())); + + mockMvc.perform(put("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/stop", + studyUuid, rootNetworkUuid, nodeUuid) + .header(HEADER_USER_ID, "userId")) + .andExpect(status().isOk()); + + Message stoppedMessage = MessageBuilder.withPayload("") + .setHeader(HEADER_RECEIVER, objectMapper.writeValueAsString(new NodeReceiver(nodeUuid, rootNetworkUuid))) + .setHeader("resultUuid", resultUuid) + .build(); + + consumerService.consumeSensitivityAnalysisStopped().accept(stoppedMessage); + checkMessagesReceived(studyUuid, NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS); + + computationServerStubs.verifyComputationStop(resultUuid, Map.of("receiver", WireMock.matching(".*"))); + } + + private void checkMessagesReceived(UUID studyUuid, String updateTypeToCheck) { + Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); + assertEquals(studyUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + String updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); + assertEquals(updateType, updateTypeToCheck); } @Test @@ -515,13 +483,10 @@ void testGetSensitivityResultWithWrongId() throws Exception { UUID studyUuid = studyEntity.getId(); UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyUuid); mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?selector={selector}", studyUuid, firstRootNetworkUuid, UUID.randomUUID(), FAKE_RESULT_JSON)) - .andExpect(status().isNotFound()).andReturn(); + .andExpect(status().isNotFound()).andReturn(); mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/filter-options?selector={selector}", studyUuid, firstRootNetworkUuid, UUID.randomUUID(), FAKE_RESULT_JSON)) - .andExpect(status().isNoContent()).andReturn(); - - String baseUrlWireMock = wireMock.baseUrl(); - sensitivityAnalysisService.setSensitivityAnalysisServerBaseUri(baseUrlWireMock); + .andExpect(status().isNoContent()).andReturn(); UUID rootNodeUuid = getRootNodeUuid(studyUuid); NetworkModificationNode modificationNode1 = createNetworkModificationNode(studyUuid, rootNodeUuid, UUID.randomUUID(), VARIANT_ID, "node 1"); @@ -530,17 +495,48 @@ void testGetSensitivityResultWithWrongId() throws Exception { assertNotNull(rootNetworkNodeInfoService.getComputationResultUuid(modificationNodeUuid, firstRootNetworkUuid, SENSITIVITY_ANALYSIS)); assertEquals(notFoundSensitivityUuid, rootNetworkNodeInfoService.getComputationResultUuid(modificationNodeUuid, firstRootNetworkUuid, SENSITIVITY_ANALYSIS)); - wireMock.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/" + notFoundSensitivityUuid)) - .willReturn(WireMock.notFound())); + wireMockServer.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/" + notFoundSensitivityUuid)) + .willReturn(WireMock.notFound())); - wireMock.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/" + notFoundSensitivityUuid + "/filter-options" + ".*")) - .willReturn(WireMock.notFound())); + wireMockServer.stubFor(WireMock.get(WireMock.urlPathMatching("/v1/results/" + notFoundSensitivityUuid + "/filter-options" + ".*")) + .willReturn(WireMock.notFound())); mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result?selector={selector}", studyUuid, firstRootNetworkUuid, modificationNodeUuid, FAKE_RESULT_JSON)) - .andExpect(status().isNotFound()).andReturn(); + .andExpect(status().isNotFound()).andReturn(); mockMvc.perform(get("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/result/filter-options?selector={selector}", studyUuid, firstRootNetworkUuid, modificationNodeUuid, FAKE_RESULT_JSON)) - .andExpect(status().isNotFound()).andReturn(); + .andExpect(status().isNotFound()).andReturn(); + } + + @Test + void testResetUuidResultWhenSensitivityFailed() throws Exception { + UUID resultUuid = UUID.randomUUID(); + StudyEntity studyEntity = insertDummyStudy(UUID.randomUUID(), UUID.randomUUID(), SENSITIVITY_ANALYSIS_PARAMETERS_UUID); + UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyEntity.getId()); + RootNode rootNode = networkModificationTreeService.getStudyTree(studyEntity.getId(), null); + NetworkModificationNode modificationNode = createNetworkModificationNode(studyEntity.getId(), rootNode.getId(), UUID.randomUUID(), VARIANT_ID, "node 1"); + String resultUuidJson = objectMapper.writeValueAsString(new NodeReceiver(modificationNode.getId(), firstRootNetworkUuid)); + + // Set an uuid result in the database + rootNetworkNodeInfoService.updateComputationResultUuid(modificationNode.getId(), firstRootNetworkUuid, resultUuid, SENSITIVITY_ANALYSIS); + assertNotNull(rootNetworkNodeInfoService.getComputationResultUuid(modificationNode.getId(), firstRootNetworkUuid, SENSITIVITY_ANALYSIS)); + assertEquals(resultUuid, rootNetworkNodeInfoService.getComputationResultUuid(modificationNode.getId(), firstRootNetworkUuid, SENSITIVITY_ANALYSIS)); + + StudyService studyServiceMock = Mockito.mock(StudyService.class); + doAnswer(invocation -> { + input.send(MessageBuilder.withPayload("").setHeader(HEADER_RECEIVER, resultUuidJson).build(), SENSITIVITY_ANALYSIS_FAILED_DESTINATION); + return resultUuid; + }).when(studyServiceMock).runSensitivityAnalysis(any(), any(), any(), any()); + assertNotNull(studyEntity.getId()); + studyServiceMock.runSensitivityAnalysis(studyEntity.getId(), modificationNode.getId(), firstRootNetworkUuid, "testUserId"); + + // Test reset uuid result in the database + assertNull(rootNetworkNodeInfoService.getComputationResultUuid(modificationNode.getId(), firstRootNetworkUuid, SENSITIVITY_ANALYSIS)); + + Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); + assertEquals(studyEntity.getId(), message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + String updateType = (String) message.getHeaders().get(NotificationService.HEADER_UPDATE_TYPE); + assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_FAILED, updateType); } @Test @@ -562,6 +558,7 @@ void testResetUuidResultWhenSAFailed() throws Exception { input.send(MessageBuilder.withPayload("").setHeader(HEADER_RECEIVER, resultUuidJson).build(), SENSITIVITY_ANALYSIS_FAILED_DESTINATION); return resultUuid; }).when(studyServiceMock).runSensitivityAnalysis(any(), any(), any(), any()); + assertNotNull(studyEntity.getId()); studyServiceMock.runSensitivityAnalysis(studyEntity.getId(), modificationNode.getId(), firstRootNetworkUuid, "testUserId"); // Test reset uuid result in the database @@ -575,7 +572,8 @@ void testResetUuidResultWhenSAFailed() throws Exception { // test sensitivity analysis on network 2 will fail @Test - void testSensitivityAnalysisFailedForNotification(final MockWebServer server) throws Exception { + void testSensitivityAnalysisFailedForNotification() throws Exception { + // --- Setup study and node --- StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_2_STRING), CASE_2_UUID, SENSITIVITY_ANALYSIS_PARAMETERS_UUID); UUID studyUuid = studyEntity.getId(); UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyUuid); @@ -583,119 +581,107 @@ void testSensitivityAnalysisFailedForNotification(final MockWebServer server) th NetworkModificationNode modificationNode1 = createNetworkModificationNode(studyUuid, rootNodeUuid, UUID.randomUUID(), VARIANT_ID, "node 1"); UUID modificationNode1Uuid = modificationNode1.getId(); - //run failing sensitivity analysis (because in network 2) - mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", studyUuid, firstRootNetworkUuid, modificationNode1Uuid) - .header(HEADER_USER_ID, "testUserId")) + // --- Stub failing sensitivity analysis run --- + computationServerStubs.stubComputationRun(NETWORK_UUID_2_STRING, null, SENSITIVITY_ANALYSIS_ERROR_NODE_RESULT_UUID); + + // Run failing sensitivity analysis + mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", + studyUuid, firstRootNetworkUuid, modificationNode1Uuid) + .header(HEADER_USER_ID, "testUserId")) .andExpect(status().isOk()); - // failed sensitivity analysis + // Simulate failed result message + String receiverJson = objectMapper.writeValueAsString(new NodeReceiver(modificationNode1Uuid, firstRootNetworkUuid)); + Message failedMessage = MessageBuilder.withPayload("") + .setHeader(HEADER_RECEIVER, receiverJson) + .setHeader("resultUuid", SENSITIVITY_ANALYSIS_ERROR_NODE_RESULT_UUID) + .build(); + consumerService.consumeSensitivityAnalysisFailed().accept(failedMessage); + + // Verify message sent to frontend Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); assertEquals(studyUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); String updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_FAILED, updateType); + assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); - // message sent by run and save controller to notify frontend sensitivity analysis is running and should update status + // Status message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); assertEquals(studyUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); + assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_FAILED, updateType); - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.matches("/v1/networks/" + NETWORK_UUID_2_STRING + "/run-and-save.*?receiver=.*nodeUuid.*"))); + // Verify the "run-and-save" POST request was called + wireMockServer.verify(1, WireMock.postRequestedFor( + WireMock.urlPathMatching("/v1/networks/" + NETWORK_UUID_2_STRING + "/run-and-save.*") + )); - /* - * what follows is mostly for test coverage -> a failed message without receiver is sent -> will be ignored by consumer - */ - studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_3_STRING), CASE_3_UUID, SENSITIVITY_ANALYSIS_PARAMETERS_UUID); - UUID studyUuid2 = studyEntity.getId(); + // --- Test coverage: failed message without receiver --- + StudyEntity studyEntity2 = insertDummyStudy(UUID.fromString(NETWORK_UUID_3_STRING), CASE_3_UUID, SENSITIVITY_ANALYSIS_PARAMETERS_UUID); + UUID studyUuid2 = studyEntity2.getId(); UUID firstRootNetworkUuid2 = studyTestUtils.getOneRootNetworkUuid(studyUuid2); UUID rootNodeUuid2 = getRootNodeUuid(studyUuid2); NetworkModificationNode modificationNode2 = createNetworkModificationNode(studyUuid2, rootNodeUuid2, UUID.randomUUID(), VARIANT_ID, "node 2"); UUID modificationNode1Uuid2 = modificationNode2.getId(); - mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", studyUuid2, firstRootNetworkUuid2, modificationNode1Uuid2) - .header(HEADER_USER_ID, "testUserId")) + // Stub failing analysis for second study + computationServerStubs.stubComputationRun(NETWORK_UUID_3_STRING, null, SENSITIVITY_ANALYSIS_ERROR_NODE_RESULT_UUID); + // Run failing sensitivity analysis without receiver + mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", + studyUuid2, firstRootNetworkUuid2, modificationNode1Uuid2) + .header(HEADER_USER_ID, "testUserId")) .andExpect(status().isOk()); - // failed sensitivity analysis without receiver -> no failure message sent to frontend + // Consume failed message without receiver -> ignored + failedMessage = MessageBuilder.withPayload("") + .setHeader("resultUuid", SENSITIVITY_ANALYSIS_ERROR_NODE_RESULT_UUID) + .build(); + consumerService.consumeSensitivityAnalysisFailed().accept(failedMessage); - // message sent by run and save controller to notify frontend sensitivity analysis is running and should update status + // Status message still sent message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); assertEquals(studyUuid2, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.matches("/v1/networks/" + NETWORK_UUID_3_STRING + "/run-and-save.*?receiver=.*nodeUuid.*"))); - } - - private StudyEntity insertDummyStudy(UUID networkUuid, UUID caseUuid, UUID sensitivityAnalysisParametersUuid) { - StudyEntity studyEntity = TestUtils.createDummyStudy(networkUuid, "netId", caseUuid, "", "", null, UUID.randomUUID(), null, null, sensitivityAnalysisParametersUuid, null, null); - var study = studyRepository.save(studyEntity); - networkModificationTreeService.createRoot(studyEntity); - return study; - } - - private NetworkModificationNode createNetworkModificationNode(UUID studyUuid, UUID parentNodeUuid, - UUID modificationGroupUuid, String variantId, String nodeName) throws Exception { - NetworkModificationNode modificationNode = NetworkModificationNode.builder().name(nodeName) - .description("description").modificationGroupUuid(modificationGroupUuid).variantId(variantId) - .children(Collections.emptyList()).build(); - - // Only for tests - String mnBodyJson = objectWriter.writeValueAsString(modificationNode); - JSONObject jsonObject = new JSONObject(mnBodyJson); - jsonObject.put("variantId", variantId); - jsonObject.put("modificationGroupUuid", modificationGroupUuid); - mnBodyJson = jsonObject.toString(); - - mockMvc.perform(post("/v1/studies/{studyUuid}/tree/nodes/{id}", studyUuid, parentNodeUuid).content(mnBodyJson).contentType(MediaType.APPLICATION_JSON).header("userId", "userId")) - .andExpect(status().isOk()); - var mess = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertNotNull(mess); - modificationNode.setId(UUID.fromString(String.valueOf(mess.getHeaders().get(NotificationService.HEADER_NEW_NODE)))); - assertEquals(InsertMode.CHILD.name(), mess.getHeaders().get(NotificationService.HEADER_INSERT_MODE)); - - rootNetworkNodeInfoService.updateRootNetworkNode(modificationNode.getId(), studyTestUtils.getOneRootNetworkUuid(studyUuid), - RootNetworkNodeInfo.builder().variantId(variantId).build()); - - return modificationNode; + // Verify run-and-save POST request called + wireMockServer.verify(1, WireMock.postRequestedFor( + WireMock.urlPathMatching("/v1/networks/" + NETWORK_UUID_3_STRING + "/run-and-save.*") + )); } - private UUID getRootNodeUuid(UUID studyUuid) { - return networkModificationTreeService.getStudyRootNodeUuid(studyUuid); - } + private void createOrUpdateParametersAndDoChecks(UUID studyNameUserIdUuid, String parameters, String userId, HttpStatusCode status) throws Exception { + mockMvc.perform( + post("/v1/studies/{studyUuid}/sensitivity-analysis/parameters", studyNameUserIdUuid) + .header("userId", userId) + .contentType(MediaType.ALL) + .content(parameters)) + .andExpect(status().is(status.value())); - @AfterEach - void tearDown(final MockWebServer server) { - studyRepository.findAll().forEach(s -> networkModificationTreeService.doDeleteTree(s.getId())); - studyRepository.deleteAll(); + Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); + assertEquals(studyNameUserIdUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, message.getHeaders().get(NotificationService.HEADER_UPDATE_TYPE)); - List destinations = List.of(STUDY_UPDATE_DESTINATION, SENSITIVITY_ANALYSIS_FAILED_DESTINATION, SENSITIVITY_ANALYSIS_RESULT_DESTINATION, SENSITIVITY_ANALYSIS_STOPPED_DESTINATION); - TestUtils.assertQueuesEmptyThenClear(destinations, output); + message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); + assertEquals(studyNameUserIdUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + assertEquals(UPDATE_TYPE_COMPUTATION_PARAMETERS, message.getHeaders().get(NotificationService.HEADER_UPDATE_TYPE)); - try { - TestUtils.assertServerRequestsEmptyThenShutdown(server); - } catch (UncheckedInterruptedException e) { - LOGGER.error("Error while attempting to get the request done : ", e); - } + message = output.receive(TIMEOUT, ELEMENT_UPDATE_DESTINATION); + assertEquals(studyNameUserIdUuid, message.getHeaders().get(NotificationService.HEADER_ELEMENT_UUID)); } @Test void testSensitivityAnalysisParameters() throws Exception { - // Mocking with WireMock - String baseUrlWireMock = wireMock.baseUrl(); - sensitivityAnalysisService.setSensitivityAnalysisServerBaseUri(baseUrlWireMock); - wireMock.stubFor(WireMock.get(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID)) + wireMockServer.stubFor(WireMock.get(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID)) .willReturn(WireMock.ok().withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).withBody(SENSITIVITY_ANALYSIS_DEFAULT_PARAMETERS_JSON))); - wireMock.stubFor(WireMock.put(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID)) + wireMockServer.stubFor(WireMock.put(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID)) .willReturn(WireMock.ok())); - wireMock.stubFor(WireMock.post(WireMock.urlPathEqualTo("/v1/parameters")) + wireMockServer.stubFor(WireMock.post(WireMock.urlPathEqualTo("/v1/parameters")) .willReturn(WireMock.ok().withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).withBody(objectMapper.writeValueAsString(SENSITIVITY_ANALYSIS_PARAMETERS_UUID)))); - wireMock.stubFor(WireMock.post(WireMock.urlPathEqualTo("/v1/parameters/default")) + wireMockServer.stubFor(WireMock.post(WireMock.urlPathEqualTo("/v1/parameters/default")) .willReturn(WireMock.ok().withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).withBody(objectMapper.writeValueAsString(SENSITIVITY_ANALYSIS_PARAMETERS_UUID)))); - // end mocking UUID studyNameUserIdUuid = insertDummyStudy(UUID.randomUUID(), UUID.randomUUID(), SENSITIVITY_ANALYSIS_PARAMETERS_UUID).getId(); assertNotNull(studyNameUserIdUuid); @@ -705,12 +691,12 @@ void testSensitivityAnalysisParameters() throws Exception { status().isOk(), content().string(SENSITIVITY_ANALYSIS_DEFAULT_PARAMETERS_JSON)); - wireMock.verify(WireMock.getRequestedFor(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID))); + wireMockServer.verify(WireMock.getRequestedFor(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID))); // Set sensitivity analysis parameters (will update) createOrUpdateParametersAndDoChecks(studyNameUserIdUuid, SENSITIVITY_ANALYSIS_UPDATED_PARAMETERS_JSON, "userId", HttpStatus.OK); - wireMock.verify(WireMock.putRequestedFor(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID))); + wireMockServer.verify(WireMock.putRequestedFor(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID))); // Get sensitivity analysis (not existing, so it will create default) StudyEntity studyEntityToUpdate = studyRepository.findById(studyNameUserIdUuid).orElseThrow(() -> new StudyException(NOT_FOUND)); @@ -721,8 +707,8 @@ void testSensitivityAnalysisParameters() throws Exception { status().isOk(), content().string(SENSITIVITY_ANALYSIS_DEFAULT_PARAMETERS_JSON)); - wireMock.verify(WireMock.postRequestedFor(WireMock.urlPathEqualTo("/v1/parameters/default"))); - wireMock.verify(WireMock.getRequestedFor(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID))); + wireMockServer.verify(WireMock.postRequestedFor(WireMock.urlPathEqualTo("/v1/parameters/default"))); + wireMockServer.verify(WireMock.getRequestedFor(WireMock.urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID))); assertEquals(SENSITIVITY_ANALYSIS_PARAMETERS_UUID, studyRepository.findById(studyNameUserIdUuid).orElseThrow().getSensitivityAnalysisParametersUuid()); // Set sensitivity analysis parameters (will create) @@ -731,114 +717,153 @@ void testSensitivityAnalysisParameters() throws Exception { createOrUpdateParametersAndDoChecks(studyNameUserIdUuid, SENSITIVITY_ANALYSIS_UPDATED_PARAMETERS_JSON, "userId", HttpStatus.OK); - wireMock.verify(WireMock.postRequestedFor(WireMock.urlPathEqualTo("/v1/parameters"))); + wireMockServer.verify(WireMock.postRequestedFor(WireMock.urlPathEqualTo("/v1/parameters"))); assertEquals(SENSITIVITY_ANALYSIS_PARAMETERS_UUID, studyRepository.findById(studyNameUserIdUuid).orElseThrow().getSensitivityAnalysisParametersUuid()); } @Test - void testGetSensitivityAnalysisFactorCount(final MockWebServer server) throws Exception { - StudyEntity studyEntity = insertDummyStudy(UUID.randomUUID(), UUID.randomUUID(), SENSITIVITY_ANALYSIS_PARAMETERS_UUID); - UUID studyNameUserIdUuid = studyEntity.getId(); - UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyNameUserIdUuid); - UUID rootNodeUuid = getRootNodeUuid(studyNameUserIdUuid); - MockHttpServletRequestBuilder requestBuilder = post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/factor-count", studyNameUserIdUuid, firstRootNetworkUuid, rootNodeUuid); - requestBuilder.content(SENSITIVITY_ANALYSIS_UPDATED_PARAMETERS_JSON); - String resultAsString = mockMvc.perform(requestBuilder.header("userId", "userId")) - .andExpect(status().isOk()) - .andReturn() - .getResponse() - .getContentAsString(); - assertEquals(FAKE_RESULT_JSON, resultAsString); - - assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.matches("/v1/networks/" + ".*" + "/factor-count.*"))); - } - - private void createOrUpdateParametersAndDoChecks(UUID studyNameUserIdUuid, String parameters, String userId, HttpStatusCode status) throws Exception { - mockMvc.perform( - post("/v1/studies/{studyUuid}/sensitivity-analysis/parameters", studyNameUserIdUuid) - .header("userId", userId) - .contentType(MediaType.ALL) - .content(parameters)) - .andExpect(status().is(status.value())); - - Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyNameUserIdUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, message.getHeaders().get(NotificationService.HEADER_UPDATE_TYPE)); - - message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyNameUserIdUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); - assertEquals(UPDATE_TYPE_COMPUTATION_PARAMETERS, message.getHeaders().get(NotificationService.HEADER_UPDATE_TYPE)); - - message = output.receive(TIMEOUT, ELEMENT_UPDATE_DESTINATION); - assertEquals(studyNameUserIdUuid, message.getHeaders().get(NotificationService.HEADER_ELEMENT_UUID)); - } - - @Test - void testResetSensitivityAnalysisParametersUserHasNoProfile(final MockWebServer server) throws Exception { + void testResetSensitivityAnalysisParametersUserHasNoProfile() throws Exception { StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID, SENSITIVITY_ANALYSIS_PARAMETERS_UUID); UUID studyNameUserIdUuid = studyEntity.getId(); + userAdminServerStubs.stubGetUserProfile(NO_PROFILE_USER_ID, USER_PROFILE_NO_PARAMS_JSON); + computationServerStubs.stubParameterPut(wireMockServer, SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING, SENSITIVITY_ANALYSIS_PROFILE_PARAMETERS_JSON); + createOrUpdateParametersAndDoChecks(studyNameUserIdUuid, "", NO_PROFILE_USER_ID, HttpStatus.OK); - var requests = TestUtils.getRequestsDone(2, server); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/users/" + NO_PROFILE_USER_ID + "/profile"))); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING))); // update existing with dft + userAdminServerStubs.verifyGetUserProfile(NO_PROFILE_USER_ID); + computationServerStubs.verifyParameterPut(wireMockServer, SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING); } @Test - void testResetSensitivityAnalysisParametersUserHasNoParamsInProfile(final MockWebServer server) throws Exception { + void testResetSensitivityAnalysisParametersUserHasNoParamsInProfile() throws Exception { StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID, SENSITIVITY_ANALYSIS_PARAMETERS_UUID); UUID studyNameUserIdUuid = studyEntity.getId(); + userAdminServerStubs.stubGetUserProfile(NO_PARAMS_IN_PROFILE_USER_ID, USER_PROFILE_NO_PARAMS_JSON); + computationServerStubs.stubParameterPut(wireMockServer, SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING, SENSITIVITY_ANALYSIS_PROFILE_PARAMETERS_JSON); + createOrUpdateParametersAndDoChecks(studyNameUserIdUuid, "", NO_PARAMS_IN_PROFILE_USER_ID, HttpStatus.OK); - var requests = TestUtils.getRequestsDone(2, server); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/users/" + NO_PARAMS_IN_PROFILE_USER_ID + "/profile"))); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING))); // update existing with dft + userAdminServerStubs.verifyGetUserProfile(NO_PARAMS_IN_PROFILE_USER_ID); + computationServerStubs.verifyParameterPut(wireMockServer, SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING); } @Test - void testResetSensitivityAnalysisParametersUserHasInvalidParamsInProfile(final MockWebServer server) throws Exception { + void testResetSensitivityAnalysisParametersUserHasInvalidParamsInProfile() throws Exception { StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID, SENSITIVITY_ANALYSIS_PARAMETERS_UUID); UUID studyNameUserIdUuid = studyEntity.getId(); + + userAdminServerStubs.stubGetUserProfile(INVALID_PARAMS_IN_PROFILE_USER_ID, USER_PROFILE_INVALID_PARAMS_JSON); + computationServerStubs.stubParameterPut(wireMockServer, SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING, SENSITIVITY_ANALYSIS_PROFILE_PARAMETERS_JSON); + computationServerStubs.stubParametersDuplicateFromNotFound(PROFILE_SENSITIVITY_ANALYSIS_INVALID_PARAMETERS_UUID_STRING); createOrUpdateParametersAndDoChecks(studyNameUserIdUuid, "", INVALID_PARAMS_IN_PROFILE_USER_ID, HttpStatus.NO_CONTENT); - var requests = TestUtils.getRequestsDone(3, server); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/users/" + INVALID_PARAMS_IN_PROFILE_USER_ID + "/profile"))); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING))); // update existing with dft - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/parameters?duplicateFrom=" + PROFILE_SENSITIVITY_ANALYSIS_INVALID_PARAMETERS_UUID_STRING))); // post duplicate ko + userAdminServerStubs.verifyGetUserProfile(INVALID_PARAMS_IN_PROFILE_USER_ID); + computationServerStubs.verifyParameterPut(wireMockServer, SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING); + computationServerStubs.verifyParametersDuplicateFrom(PROFILE_SENSITIVITY_ANALYSIS_INVALID_PARAMETERS_UUID_STRING); } @Test - void testResetSensitivityAnalysisParametersUserHasValidParamsInProfile(final MockWebServer server) throws Exception { - StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID, SENSITIVITY_ANALYSIS_PARAMETERS_UUID); + void testResetSensitivityAnalysisParametersUserHasValidParamsInProfile() throws Exception { + + StudyEntity studyEntity = insertDummyStudy( + UUID.fromString(NETWORK_UUID_STRING), + CASE_UUID, + SENSITIVITY_ANALYSIS_PARAMETERS_UUID + ); + UUID studyUuid = studyEntity.getId(); UUID rootNodeUuid = getRootNodeUuid(studyUuid); UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyUuid); - NetworkModificationNode modificationNode1 = createNetworkModificationNode(studyUuid, rootNodeUuid, UUID.randomUUID(), VARIANT_ID, "node 1"); - // run computation - mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", - studyUuid, firstRootNetworkUuid, modificationNode1.getId()) - .header(HEADER_USER_ID, "testUserId")).andExpect(status().isOk()); - consumeSensitivityAnalysisResult(studyUuid, firstRootNetworkUuid, modificationNode1.getId(), SENSITIVITY_ANALYSIS_RESULT_UUID); + NetworkModificationNode modificationNode1 = createNetworkModificationNode( + studyUuid, + rootNodeUuid, + UUID.randomUUID(), + VARIANT_ID, + "node 1" + ); + + // ---------------- WireMock stubs ---------------- + wireMockServer.stubFor(post(urlPathMatching( + "/v1/networks/" + NETWORK_UUID_STRING + "/run-and-save.*")) + .willReturn(ok())); + + wireMockServer.stubFor(post(urlPathMatching("/v1/results/invalidate-status.*")) + .withQueryParam("resultUuid", matching(".*")) + .willReturn(ok())); + + userAdminServerStubs.stubGetUserProfile(VALID_PARAMS_IN_PROFILE_USER_ID, USER_PROFILE_VALID_PARAMS_JSON); + computationServerStubs.stubParameterPut(wireMockServer, SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING, objectWriter.writeValueAsString(SENSITIVITY_ANALYSIS_PARAMETERS)); + computationServerStubs.stubParametersDuplicateFrom(PROFILE_SENSITIVITY_ANALYSIS_VALID_PARAMETERS_UUID_STRING, DUPLICATED_PARAMS_JSON); + + // ---------------- Run sensitivity analysis ---------------- + mockMvc.perform(post( + "/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", studyUuid, firstRootNetworkUuid, modificationNode1.getId()) + .header(HEADER_USER_ID, "testUserId")) + .andExpect(status().isOk()); + + // consume sensitivity status event (REQUIRED) + Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); + assertEquals( + UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, + message.getHeaders().get(HEADER_UPDATE_TYPE) + ); createOrUpdateParametersAndDoChecks(studyUuid, "", VALID_PARAMS_IN_PROFILE_USER_ID, HttpStatus.OK); - var requests = TestUtils.getRequestsDone(5, server); - assertTrue(requests.stream().anyMatch(r -> r.matches("/v1/networks/" + NETWORK_UUID_STRING + "/run-and-save\\?reportUuid=.*&reporterId=.*&reportType=SensitivityAnalysis¶metersUuid=.*&loadFlowParametersUuid=.*&variantId=" + VARIANT_ID + "&receiver=.*"))); - assertTrue(requests.stream().anyMatch(r -> r.matches("/v1/results/invalidate-status\\?resultUuid=.*"))); // result has been invalidated by params reset - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/users/" + VALID_PARAMS_IN_PROFILE_USER_ID + "/profile"))); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING))); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/parameters?duplicateFrom=" + PROFILE_SENSITIVITY_ANALYSIS_VALID_PARAMETERS_UUID_STRING))); // post duplicate ok + // run-and-save called + wireMockServer.verify(postRequestedFor( + urlPathMatching("/v1/networks/" + NETWORK_UUID_STRING + "/run-and-save.*")) + .withQueryParam("reportType", equalTo("SensitivityAnalysis")) + .withQueryParam("variantId", equalTo(VARIANT_ID)) + .withQueryParam("receiver", matching(".*")) + ); + // parameters duplicated + computationServerStubs.stubParametersDuplicateFrom(PROFILE_SENSITIVITY_ANALYSIS_VALID_PARAMETERS_UUID_STRING, DUPLICATED_PARAMS_JSON); + wireMockServer.verify(deleteRequestedFor( + urlPathEqualTo("/v1/parameters/" + SENSITIVITY_ANALYSIS_PARAMETERS_UUID_STRING)) + ); + long invalidateCalls = wireMockServer.getAllServeEvents().stream() + .filter(e -> e.getRequest().getUrl().startsWith("/v1/results/invalidate-status")) + .count(); + assertTrue(invalidateCalls <= 1); } @Test - void testResetSensitivityAnalysisParametersUserHasValidParamsInProfileButNoExistingSensitivityAnalysisParams(final MockWebServer server) throws Exception { + void testResetSensitivityAnalysisParametersUserHasValidParamsInProfileButNoExistingSensitivityAnalysisParams() throws Exception { StudyEntity studyEntity = insertDummyStudy(UUID.fromString(NETWORK_UUID_STRING), CASE_UUID, null); UUID studyNameUserIdUuid = studyEntity.getId(); + userAdminServerStubs.stubGetUserProfile(VALID_PARAMS_IN_PROFILE_USER_ID, USER_PROFILE_VALID_PARAMS_JSON); + wireMockServer.stubFor(post(urlPathEqualTo("/v1/parameters")) + .withQueryParam("duplicateFrom", equalTo(PROFILE_SENSITIVITY_ANALYSIS_VALID_PARAMETERS_UUID_STRING)) + .willReturn(WireMock.ok() + .withBody(DUPLICATED_PARAMS_JSON) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + )); + createOrUpdateParametersAndDoChecks(studyNameUserIdUuid, "", VALID_PARAMS_IN_PROFILE_USER_ID, HttpStatus.OK); - var requests = TestUtils.getRequestsDone(2, server); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/users/" + VALID_PARAMS_IN_PROFILE_USER_ID + "/profile"))); - assertTrue(requests.stream().anyMatch(r -> r.equals("/v1/parameters?duplicateFrom=" + PROFILE_SENSITIVITY_ANALYSIS_VALID_PARAMETERS_UUID_STRING))); // post duplicate ok + // --- Verify requests --- + userAdminServerStubs.verifyGetUserProfile(VALID_PARAMS_IN_PROFILE_USER_ID); + wireMockServer.verify(postRequestedFor(urlPathEqualTo("/v1/parameters")) + .withQueryParam("duplicateFrom", equalTo(PROFILE_SENSITIVITY_ANALYSIS_VALID_PARAMETERS_UUID_STRING)) + ); + } + + @AfterEach + void tearDown() { + studyRepository.findAll().forEach(s -> networkModificationTreeService.doDeleteTree(s.getId())); + studyRepository.deleteAll(); + + List destinations = List.of(STUDY_UPDATE_DESTINATION, SENSITIVITY_ANALYSIS_FAILED_DESTINATION, SENSITIVITY_ANALYSIS_RESULT_DESTINATION, SENSITIVITY_ANALYSIS_STOPPED_DESTINATION); + TestUtils.assertQueuesEmptyThenClear(destinations, output); + + try { + TestUtils.assertQueuesEmptyThenClear( + destinations, output + ); + } catch (UncheckedInterruptedException e) { + LOGGER.error("Error while attempting to get the request done : ", e); + } } } From c4587209a4972b4eb73682cc5df0614844d5b939 Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Mon, 2 Feb 2026 11:35:58 +0100 Subject: [PATCH 2/3] fix Signed-off-by: SOUISSI Maissa (Externe) --- .../study/server/SensitivityAnalysisTest.java | 114 ++++++++---------- 1 file changed, 52 insertions(+), 62 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java b/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java index 8d6083e958..585717a484 100644 --- a/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java +++ b/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2025, RTE (http://www.rte-france.com) + * Copyright (c) 2026, 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/. @@ -19,7 +19,6 @@ import org.gridsuite.study.server.dto.sensianalysis.SensitivityAnalysisCsvFileInfos; import org.gridsuite.study.server.error.StudyException; import org.gridsuite.study.server.networkmodificationtree.dto.*; -import org.gridsuite.study.server.notification.NotificationService; import org.gridsuite.study.server.repository.StudyEntity; import org.gridsuite.study.server.repository.StudyRepository; import org.gridsuite.study.server.repository.rootnetwork.RootNetworkNodeInfoRepository; @@ -28,7 +27,6 @@ import org.gridsuite.study.server.utils.TestUtils; import org.gridsuite.study.server.utils.elasticsearch.DisableElasticsearch; import org.gridsuite.study.server.utils.wiremock.*; -import org.json.JSONObject; import org.junit.jupiter.api.*; import org.mockito.Mockito; import org.slf4j.Logger; @@ -46,6 +44,7 @@ import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.MessageBuilder; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import java.util.*; @@ -116,7 +115,7 @@ class SensitivityAnalysisTest { "\"sensitivityInjectionsSet\":[],\"sensitivityInjection\":[],\"sensitivityHVDC\":[],\"sensitivityPST\":[],\"sensitivityNodes\":[]}"; public static final String SENSITIVITY_ANALYSIS_UPDATED_PARAMETERS_JSON = "{\"flowFlowSensitivityValueThreshold\":90.0,\"angleFlowSensitivityValueThreshold\":0.6,\"flowVoltageSensitivityValueThreshold\":0.1,\"sensitivityInjectionsSet\":[{\"monitoredBranches\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"injections\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"distributionType\":\"PROPORTIONAL\",\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}],\"sensitivityInjection\":[{\"monitoredBranches\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"injections\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}],\"sensitivityHVDC\":[{\"monitoredBranches\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"sensitivityType\":\"DELTA_MW\",\"hvdcs\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}],\"sensitivityPST\":[{\"monitoredBranches\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"sensitivityType\":\"DELTA_MW\",\"psts\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}],\"sensitivityNodes\":[{\"monitoredVoltageLevels\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da321\",\"containerName\":\"identifiable1\"}],\"equipmentsInVoltageRegulation\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da322\",\"containerName\":\"identifiable2\"}],\"contingencies\":[{\"containerId\":\"cf399ef3-7f14-4884-8c82-1c90300da323\",\"containerName\":\"identifiable3\"}],\"activated\":true}]}"; - private static final String SENSITIVITY_ANALYSIS_PROFILE_PARAMETERS_JSON = "{\"lowVoltageAbsoluteThreshold\":30.0,\"lowVoltageProportionalThreshold\":0.4,\"highVoltageAbsoluteThreshold\":0.0,\"highVoltageProportionalThreshold\":0.0,\"flowProportionalThreshold\":0.1}"; + private static final String SENSITIVITY_ANALYSIS_PROFILE_PARAMETERS_JSON = "{\"flowFlowSensitivityValueThreshold\":30.0,\"voltageVoltageSensitivityValueThreshold\":0.4,\"flowVoltageSensitivityValueThreshold\":0.0,\"angleFlowSensitivityValueThreshold\":0.0}"; //output destinations private static final String STUDY_UPDATE_DESTINATION = "study.update"; @@ -196,7 +195,7 @@ static void shutdownWireMock() { } @BeforeEach - void setup() throws JsonProcessingException { + void setup() { computationServerStubs = new ComputationServerStubs(wireMockServer); reportServerStubs = new ReportServerStubs(wireMockServer); userAdminServerStubs = new UserAdminServerStubs(wireMockServer); @@ -224,19 +223,14 @@ private NetworkModificationNode createNetworkModificationNode(UUID studyUuid, UU .description("description").modificationGroupUuid(modificationGroupUuid).variantId(variantId) .children(Collections.emptyList()).build(); - // Only for tests String mnBodyJson = objectWriter.writeValueAsString(modificationNode); - JSONObject jsonObject = new JSONObject(mnBodyJson); - jsonObject.put("variantId", variantId); - jsonObject.put("modificationGroupUuid", modificationGroupUuid); - mnBodyJson = jsonObject.toString(); mockMvc.perform(post("/v1/studies/{studyUuid}/tree/nodes/{id}", studyUuid, parentNodeUuid).content(mnBodyJson).contentType(MediaType.APPLICATION_JSON).header("userId", "userId")) .andExpect(status().isOk()); var mess = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); assertNotNull(mess); - modificationNode.setId(UUID.fromString(String.valueOf(mess.getHeaders().get(NotificationService.HEADER_NEW_NODE)))); - assertEquals(InsertMode.CHILD.name(), mess.getHeaders().get(NotificationService.HEADER_INSERT_MODE)); + modificationNode.setId(UUID.fromString(String.valueOf(mess.getHeaders().get(HEADER_NEW_NODE)))); + assertEquals(InsertMode.CHILD.name(), mess.getHeaders().get(HEADER_INSERT_MODE)); rootNetworkNodeInfoService.updateRootNetworkNode(modificationNode.getId(), studyTestUtils.getOneRootNetworkUuid(studyUuid), RootNetworkNodeInfo.builder().variantId(variantId).build()); @@ -255,19 +249,19 @@ private void consumeSensitivityAnalysisResult(UUID studyUuid, UUID rootNetworkUu consumerService.consumeSensitivityAnalysisResult().accept(MessageBuilder.createMessage("", messageHeaders)); Message sensitivityAnalysisStatusMessage = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid, sensitivityAnalysisStatusMessage.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + assertEquals(studyUuid, sensitivityAnalysisStatusMessage.getHeaders().get(HEADER_STUDY_UUID)); String updateType = (String) sensitivityAnalysisStatusMessage.getHeaders().get(HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); + assertEquals(UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); sensitivityAnalysisStatusMessage = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid, sensitivityAnalysisStatusMessage.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + assertEquals(studyUuid, sensitivityAnalysisStatusMessage.getHeaders().get(HEADER_STUDY_UUID)); updateType = (String) sensitivityAnalysisStatusMessage.getHeaders().get(HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); + assertEquals(UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); Message sensitivityAnalysisUpdateMessage = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid, sensitivityAnalysisUpdateMessage.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + assertEquals(studyUuid, sensitivityAnalysisUpdateMessage.getHeaders().get(HEADER_STUDY_UUID)); updateType = (String) sensitivityAnalysisUpdateMessage.getHeaders().get(HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_RESULT, updateType); + assertEquals(UPDATE_TYPE_SENSITIVITY_ANALYSIS_RESULT, updateType); } @Test @@ -286,7 +280,7 @@ void testSensitivityAnalysis() throws Exception { // run sensitivity analysis on root node (not allowed) mockMvc.perform(post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/run", studyNameUserIdUuid, firstRootNetworkUuid, rootNodeUuid) - .header(HEADER_USER_ID, "testUserId")) + .header(HEADER_USER_ID, "testUserId")) .andExpect(status().isForbidden()); testSensitivityAnalysisWithRootNetworkUuidAndNodeUuid(studyNameUserIdUuid, firstRootNetworkUuid, modificationNode1Uuid, SENSITIVITY_ANALYSIS_RESULT_UUID); @@ -464,14 +458,14 @@ private void testSensitivityAnalysisWithRootNetworkUuidAndNodeUuid(UUID studyUui .build(); consumerService.consumeSensitivityAnalysisStopped().accept(stoppedMessage); - checkMessagesReceived(studyUuid, NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS); + checkMessagesReceived(studyUuid, UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS); computationServerStubs.verifyComputationStop(resultUuid, Map.of("receiver", WireMock.matching(".*"))); } private void checkMessagesReceived(UUID studyUuid, String updateTypeToCheck) { Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + assertEquals(studyUuid, message.getHeaders().get(HEADER_STUDY_UUID)); String updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); assertEquals(updateType, updateTypeToCheck); } @@ -534,40 +528,36 @@ void testResetUuidResultWhenSensitivityFailed() throws Exception { assertNull(rootNetworkNodeInfoService.getComputationResultUuid(modificationNode.getId(), firstRootNetworkUuid, SENSITIVITY_ANALYSIS)); Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyEntity.getId(), message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); - String updateType = (String) message.getHeaders().get(NotificationService.HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_FAILED, updateType); + assertEquals(studyEntity.getId(), message.getHeaders().get(HEADER_STUDY_UUID)); + String updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); + assertEquals(UPDATE_TYPE_SENSITIVITY_ANALYSIS_FAILED, updateType); } @Test - void testResetUuidResultWhenSAFailed() throws Exception { - UUID resultUuid = UUID.randomUUID(); + void testGetSensitivityAnalysisFactorCount() throws Exception { StudyEntity studyEntity = insertDummyStudy(UUID.randomUUID(), UUID.randomUUID(), SENSITIVITY_ANALYSIS_PARAMETERS_UUID); - UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyEntity.getId()); - RootNode rootNode = networkModificationTreeService.getStudyTree(studyEntity.getId(), null); - NetworkModificationNode modificationNode = createNetworkModificationNode(studyEntity.getId(), rootNode.getId(), UUID.randomUUID(), VARIANT_ID, "node 1"); - String resultUuidJson = objectMapper.writeValueAsString(new NodeReceiver(modificationNode.getId(), firstRootNetworkUuid)); - - // Set an uuid result in the database - rootNetworkNodeInfoService.updateComputationResultUuid(modificationNode.getId(), firstRootNetworkUuid, resultUuid, SENSITIVITY_ANALYSIS); - assertNotNull(rootNetworkNodeInfoService.getComputationResultUuid(modificationNode.getId(), firstRootNetworkUuid, SENSITIVITY_ANALYSIS)); - assertEquals(resultUuid, rootNetworkNodeInfoService.getComputationResultUuid(modificationNode.getId(), firstRootNetworkUuid, SENSITIVITY_ANALYSIS)); - - StudyService studyServiceMock = Mockito.mock(StudyService.class); - doAnswer(invocation -> { - input.send(MessageBuilder.withPayload("").setHeader(HEADER_RECEIVER, resultUuidJson).build(), SENSITIVITY_ANALYSIS_FAILED_DESTINATION); - return resultUuid; - }).when(studyServiceMock).runSensitivityAnalysis(any(), any(), any(), any()); - assertNotNull(studyEntity.getId()); - studyServiceMock.runSensitivityAnalysis(studyEntity.getId(), modificationNode.getId(), firstRootNetworkUuid, "testUserId"); + UUID studyNameUserIdUuid = studyEntity.getId(); + UUID firstRootNetworkUuid = studyTestUtils.getOneRootNetworkUuid(studyNameUserIdUuid); + UUID rootNodeUuid = getRootNodeUuid(studyNameUserIdUuid); - // Test reset uuid result in the database - assertNull(rootNetworkNodeInfoService.getComputationResultUuid(modificationNode.getId(), firstRootNetworkUuid, SENSITIVITY_ANALYSIS)); + wireMockServer.stubFor(post(urlPathMatching("/v1/networks/.*/factor-count")) + .willReturn(aResponse() + .withStatus(200) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withBody(FAKE_RESULT_JSON) + ) + ); - Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyEntity.getId(), message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); - String updateType = (String) message.getHeaders().get(NotificationService.HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_FAILED, updateType); + MockHttpServletRequestBuilder requestBuilder = post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/factor-count", studyNameUserIdUuid, firstRootNetworkUuid, rootNodeUuid); + requestBuilder.content(SENSITIVITY_ANALYSIS_UPDATED_PARAMETERS_JSON); + String resultAsString = mockMvc.perform(requestBuilder.header("userId", "userId")) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + assertEquals(FAKE_RESULT_JSON, resultAsString); + WireMockUtilsCriteria.verifyPostRequest(wireMockServer, "/v1/networks/" + ".*" + "/factor-count", + true, Map.of(), null, 1); } // test sensitivity analysis on network 2 will fail @@ -600,15 +590,15 @@ void testSensitivityAnalysisFailedForNotification() throws Exception { // Verify message sent to frontend Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + assertEquals(studyUuid, message.getHeaders().get(HEADER_STUDY_UUID)); String updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); + assertEquals(UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); // Status message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + assertEquals(studyUuid, message.getHeaders().get(HEADER_STUDY_UUID)); updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_FAILED, updateType); + assertEquals(UPDATE_TYPE_SENSITIVITY_ANALYSIS_FAILED, updateType); // Verify the "run-and-save" POST request was called wireMockServer.verify(1, WireMock.postRequestedFor( @@ -639,9 +629,9 @@ void testSensitivityAnalysisFailedForNotification() throws Exception { // Status message still sent message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid2, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); + assertEquals(studyUuid2, message.getHeaders().get(HEADER_STUDY_UUID)); updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); + assertEquals(UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, updateType); // Verify run-and-save POST request called wireMockServer.verify(1, WireMock.postRequestedFor( @@ -649,24 +639,24 @@ void testSensitivityAnalysisFailedForNotification() throws Exception { )); } - private void createOrUpdateParametersAndDoChecks(UUID studyNameUserIdUuid, String parameters, String userId, HttpStatusCode status) throws Exception { + private void createOrUpdateParametersAndDoChecks(UUID studyUuid, String parameters, String userId, HttpStatusCode status) throws Exception { mockMvc.perform( - post("/v1/studies/{studyUuid}/sensitivity-analysis/parameters", studyNameUserIdUuid) + post("/v1/studies/{studyUuid}/sensitivity-analysis/parameters", studyUuid) .header("userId", userId) .contentType(MediaType.ALL) .content(parameters)) .andExpect(status().is(status.value())); Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyNameUserIdUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); - assertEquals(NotificationService.UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, message.getHeaders().get(NotificationService.HEADER_UPDATE_TYPE)); + assertEquals(studyUuid, message.getHeaders().get(HEADER_STUDY_UUID)); + assertEquals(UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, message.getHeaders().get(HEADER_UPDATE_TYPE)); message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyNameUserIdUuid, message.getHeaders().get(NotificationService.HEADER_STUDY_UUID)); - assertEquals(UPDATE_TYPE_COMPUTATION_PARAMETERS, message.getHeaders().get(NotificationService.HEADER_UPDATE_TYPE)); + assertEquals(studyUuid, message.getHeaders().get(HEADER_STUDY_UUID)); + assertEquals(UPDATE_TYPE_COMPUTATION_PARAMETERS, message.getHeaders().get(HEADER_UPDATE_TYPE)); message = output.receive(TIMEOUT, ELEMENT_UPDATE_DESTINATION); - assertEquals(studyNameUserIdUuid, message.getHeaders().get(NotificationService.HEADER_ELEMENT_UUID)); + assertEquals(studyUuid, message.getHeaders().get(HEADER_ELEMENT_UUID)); } @Test From 7e849d6e48567abd695812dc6039f52c5b5eb2c0 Mon Sep 17 00:00:00 2001 From: "SOUISSI Maissa (Externe)" Date: Tue, 3 Feb 2026 14:39:39 +0100 Subject: [PATCH 3/3] fixes Signed-off-by: SOUISSI Maissa (Externe) --- .../study/server/SensitivityAnalysisTest.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java b/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java index 585717a484..6aed171b48 100644 --- a/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java +++ b/src/test/java/org/gridsuite/study/server/SensitivityAnalysisTest.java @@ -458,12 +458,12 @@ private void testSensitivityAnalysisWithRootNetworkUuidAndNodeUuid(UUID studyUui .build(); consumerService.consumeSensitivityAnalysisStopped().accept(stoppedMessage); - checkMessagesReceived(studyUuid, UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS); + checkMessageReceived(studyUuid, UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS); computationServerStubs.verifyComputationStop(resultUuid, Map.of("receiver", WireMock.matching(".*"))); } - private void checkMessagesReceived(UUID studyUuid, String updateTypeToCheck) { + private void checkMessageReceived(UUID studyUuid, String updateTypeToCheck) { Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); assertEquals(studyUuid, message.getHeaders().get(HEADER_STUDY_UUID)); String updateType = (String) message.getHeaders().get(HEADER_UPDATE_TYPE); @@ -550,7 +550,7 @@ void testGetSensitivityAnalysisFactorCount() throws Exception { MockHttpServletRequestBuilder requestBuilder = post("/v1/studies/{studyUuid}/root-networks/{rootNetworkUuid}/nodes/{nodeUuid}/sensitivity-analysis/factor-count", studyNameUserIdUuid, firstRootNetworkUuid, rootNodeUuid); requestBuilder.content(SENSITIVITY_ANALYSIS_UPDATED_PARAMETERS_JSON); - String resultAsString = mockMvc.perform(requestBuilder.header("userId", "userId")) + String resultAsString = mockMvc.perform(requestBuilder.header(HEADER_USER_ID, "userId")) .andExpect(status().isOk()) .andReturn() .getResponse() @@ -642,20 +642,14 @@ void testSensitivityAnalysisFailedForNotification() throws Exception { private void createOrUpdateParametersAndDoChecks(UUID studyUuid, String parameters, String userId, HttpStatusCode status) throws Exception { mockMvc.perform( post("/v1/studies/{studyUuid}/sensitivity-analysis/parameters", studyUuid) - .header("userId", userId) + .header(HEADER_USER_ID, userId) .contentType(MediaType.ALL) .content(parameters)) .andExpect(status().is(status.value())); - Message message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid, message.getHeaders().get(HEADER_STUDY_UUID)); - assertEquals(UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS, message.getHeaders().get(HEADER_UPDATE_TYPE)); - - message = output.receive(TIMEOUT, STUDY_UPDATE_DESTINATION); - assertEquals(studyUuid, message.getHeaders().get(HEADER_STUDY_UUID)); - assertEquals(UPDATE_TYPE_COMPUTATION_PARAMETERS, message.getHeaders().get(HEADER_UPDATE_TYPE)); - - message = output.receive(TIMEOUT, ELEMENT_UPDATE_DESTINATION); + checkMessageReceived(studyUuid, UPDATE_TYPE_SENSITIVITY_ANALYSIS_STATUS); + checkMessageReceived(studyUuid, UPDATE_TYPE_COMPUTATION_PARAMETERS); + Message message = output.receive(TIMEOUT, ELEMENT_UPDATE_DESTINATION); assertEquals(studyUuid, message.getHeaders().get(HEADER_ELEMENT_UUID)); }