diff options
author | sourabh_sourabh <sourabh.sourabh@est.tech> | 2024-05-02 10:26:16 +0100 |
---|---|---|
committer | sourabh_sourabh <sourabh.sourabh@est.tech> | 2024-05-03 12:49:23 +0100 |
commit | 7662e814e912e0eecccea583ff18ce9abe84ef9f (patch) | |
tree | c8ef0cfbcd5f1ee6d27a65de3fdc0f60c443be0b /integration-test/src/test/groovy/org | |
parent | c5f6c45eb5cd94f76f1f39e5a4593996a9f25474 (diff) |
Make NCMP integration tests use MockWebServer
Change from using MockRestServiceServer - which is only compatible
with RestTemplate - to MockWebServer, which will allow tests to work
using WebClient instead of RestTemplate.
Issue-ID: CPS-2183
Change-Id: I7494fe17cba6e92f7df81f0fd0185e1d2b5a5541
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
Diffstat (limited to 'integration-test/src/test/groovy/org')
7 files changed, 218 insertions, 147 deletions
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy index 51b02387ed..44fc258355 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy @@ -20,8 +20,13 @@ package org.onap.cps.integration.base +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT + import java.time.OffsetDateTime import java.time.format.DateTimeFormatter +import okhttp3.mockwebserver.MockWebServer import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsDataspaceService @@ -48,23 +53,12 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock import org.springframework.boot.test.context.SpringBootTest import org.springframework.context.annotation.ComponentScan import org.springframework.data.jpa.repository.config.EnableJpaRepositories -import org.springframework.http.HttpStatus -import org.springframework.http.MediaType -import org.springframework.test.web.client.ExpectedCount -import org.springframework.test.web.client.MockRestServiceServer import org.springframework.test.web.servlet.MockMvc -import org.springframework.web.client.RestTemplate import org.testcontainers.spock.Testcontainers import spock.lang.Shared import spock.lang.Specification import spock.util.concurrent.PollingConditions -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus - @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = [CpsDataspaceService]) @Testcontainers @EnableAutoConfiguration @@ -111,17 +105,16 @@ abstract class CpsIntegrationSpecBase extends Specification { NetworkCmProxyQueryService networkCmProxyQueryService @Autowired - RestTemplate restTemplate - - @Autowired ModuleSyncWatchdog moduleSyncWatchdog @Autowired JsonObjectMapper jsonObjectMapper - MockRestServiceServer mockDmiServer = null + MockWebServer mockDmiServer = null + DmiDispatcher dmiDispatcher = new DmiDispatcher() + + def DMI_URL = null - static DMI_URL = 'http://mock-dmi-server' static NO_MODULE_SET_TAG = '' static GENERAL_TEST_DATASPACE = 'generalTestDataspace' static BOOKSTORE_SCHEMA_SET = 'bookstoreSchemaSet' @@ -135,7 +128,14 @@ abstract class CpsIntegrationSpecBase extends Specification { createStandardBookStoreSchemaSet(GENERAL_TEST_DATASPACE) initialized = true } - mockDmiServer = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build() + mockDmiServer = new MockWebServer() + mockDmiServer.setDispatcher(dmiDispatcher) + mockDmiServer.start() + DMI_URL = String.format("http://%s:%s", mockDmiServer.getHostName(), mockDmiServer.getPort()) + } + + def cleanup() { + mockDmiServer.shutdown() } def static readResourceDataFile(filename) { @@ -217,23 +217,6 @@ abstract class CpsIntegrationSpecBase extends Specification { networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds)) } - def mockDmiResponsesForModuleSync(dmiPlugin, cmHandleId, dmiModuleReferencesResponse, dmiModuleResourcesResponse) { - mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/modules")) - .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(dmiModuleReferencesResponse)) - mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/moduleResources")) - .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(dmiModuleResourcesResponse)) - } - - def mockDmiIsNotAvailableForModuleSync(dmiPlugin, cmHandleId) { - mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/modules")) - .andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE)) - } - - def mockDmiWillRespondToHealthChecks(dmiPlugin) { - mockDmiServer.expect(ExpectedCount.between(0, Integer.MAX_VALUE), requestTo("${dmiPlugin}/actuator/health")) - .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body('{"status":"UP"}')) - } - def overrideCmHandleLastUpdateTime(cmHandleId, newUpdateTime) { String ISO_TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; DateTimeFormatter ISO_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(ISO_TIMESTAMP_PATTERN); diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy new file mode 100644 index 0000000000..6676cb74c2 --- /dev/null +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy @@ -0,0 +1,105 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.integration.base + +import static org.onap.cps.integration.base.CpsIntegrationSpecBase.readResourceDataFile + +import org.springframework.http.HttpHeaders +import java.util.regex.Matcher +import okhttp3.mockwebserver.Dispatcher +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.RecordedRequest +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType + +/** + * This class simulates responses from the DMI server in NCMP integration tests. + * + * It is to be used with a MockWebServer, using mockWebServer.setDispatcher(new DmiDispatcher()). + * + * It currently implements the following endpoints: + * - /actuator/health: healthcheck endpoint that responds with 200 OK / {"status":"UP"} + * - /dmi/v1/ch/{cmHandleId}/modules: returns module references for a CM handle + * - /dmi/v1/ch/{cmHandleId}/moduleResources: returns modules resources for a CM handle + * + * The module resource/reference responses are generated based on the module names in the map moduleNamesPerCmHandleId. + * To configure the DMI so that CM handle 'ch-1' will have modules 'M1' and 'M2', you may use: + * dmiDispatcher.moduleNamesPerCmHandleId.put('ch-1', ['M1', 'M2']); + * + * To simulate the DMI not being available, the boolean isAvailable may be set to false, in which case the mock server + * will always respond with 503 Service Unavailable. + */ +class DmiDispatcher extends Dispatcher { + + static final MODULE_REFERENCES_RESPONSE_TEMPLATE = readResourceDataFile('mock-dmi-responses/moduleReferencesTemplate.json') + static final MODULE_RESOURCES_RESPONSE_TEMPLATE = readResourceDataFile('mock-dmi-responses/moduleResourcesTemplate.json') + + def isAvailable = true + + Map<String, List<String>> moduleNamesPerCmHandleId = [:] + + @Override + MockResponse dispatch(RecordedRequest request) { + if (!isAvailable) { + return new MockResponse().setResponseCode(HttpStatus.SERVICE_UNAVAILABLE.value()) + } + switch (request.path) { + case ~/^\/dmi\/v1\/ch\/(.*)\/modules$/: + def cmHandleId = Matcher.lastMatcher[0][1] + return getModuleReferencesResponse(cmHandleId) + + case ~/^\/dmi\/v1\/ch\/(.*)\/moduleResources$/: + def cmHandleId = Matcher.lastMatcher[0][1] + return getModuleResourcesResponse(cmHandleId) + + default: + throw new IllegalArgumentException('Mock DMI does not handle path ' + request.path) + } + } + + private getModuleReferencesResponse(cmHandleId) { + def moduleReferences = '{"schemas":[' + getModuleNamesForCmHandle(cmHandleId).collect { + MODULE_REFERENCES_RESPONSE_TEMPLATE.replaceAll("<MODULE_NAME>", it) + }.join(',') + ']}' + return mockOkResponseWithBody(moduleReferences) + } + + private getModuleResourcesResponse(cmHandleId) { + def moduleResources = '[' + getModuleNamesForCmHandle(cmHandleId).collect { + MODULE_RESOURCES_RESPONSE_TEMPLATE.replaceAll("<MODULE_NAME>", it) + }.join(',') + ']' + return mockOkResponseWithBody(moduleResources) + } + + private getModuleNamesForCmHandle(cmHandleId) { + if (!moduleNamesPerCmHandleId.containsKey(cmHandleId)) { + throw new IllegalArgumentException('Mock DMI has no modules configured for ' + cmHandleId) + } + return moduleNamesPerCmHandleId.get(cmHandleId) + } + + private static mockOkResponseWithBody(responseBody) { + return new MockResponse() + .setResponseCode(HttpStatus.OK.value()) + .addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .setBody(responseBody) + } +} diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpBearerTokenPassthroughSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpBearerTokenPassthroughSpec.groovy index 28c4280468..4ffe586a99 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpBearerTokenPassthroughSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpBearerTokenPassthroughSpec.groovy @@ -20,35 +20,45 @@ package org.onap.cps.integration.functional -import java.time.Duration -import org.onap.cps.integration.base.CpsIntegrationSpecBase -import org.springframework.http.HttpHeaders -import org.springframework.http.HttpStatus -import org.springframework.http.MediaType -import org.springframework.test.web.client.match.MockRestRequestMatchers - import static org.springframework.http.HttpMethod.GET import static org.springframework.http.HttpMethod.DELETE import static org.springframework.http.HttpMethod.PATCH import static org.springframework.http.HttpMethod.POST import static org.springframework.http.HttpMethod.PUT -import static org.springframework.test.web.client.match.MockRestRequestMatchers.method -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import okhttp3.mockwebserver.Dispatcher +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.RecordedRequest +import org.jetbrains.annotations.NotNull +import org.onap.cps.integration.base.CpsIntegrationSpecBase +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import spock.util.concurrent.PollingConditions + class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase { - static final MODULE_REFERENCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json') - static final MODULE_RESOURCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json') + def lastAuthHeaderReceived = null def setup() { - mockDmiWillRespondToHealthChecks(DMI_URL) - mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE, MODULE_RESOURCES_RESPONSE) + dmiDispatcher.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2'] registerCmHandle(DMI_URL, 'ch-1', NO_MODULE_SET_TAG) - mockDmiServer.reset() - mockDmiWillRespondToHealthChecks(DMI_URL) + + mockDmiServer.setDispatcher(new Dispatcher() { + @Override + MockResponse dispatch(@NotNull RecordedRequest request) throws InterruptedException { + if (request.path == '/actuator/health') { + return new MockResponse() + .addHeader("Content-Type", MediaType.APPLICATION_JSON).setBody('{"status":"UP"}') + .setResponseCode(HttpStatus.OK.value()) + } else { + lastAuthHeaderReceived = request.getHeader('Authorization') + return new MockResponse().setResponseCode(HttpStatus.OK.value()) + } + } + }) } def cleanup() { @@ -56,12 +66,6 @@ class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase { } def 'Bearer token is passed from NCMP to DMI in pass-through data operations.'() { - given: 'DMI will expect to receive a request with a bearer token' - def targetDmiUrl = "$DMI_URL/dmi/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=my-resource-id" - mockDmiServer.expect(requestTo(targetDmiUrl)) - .andExpect(MockRestRequestMatchers.header(HttpHeaders.AUTHORIZATION, 'Bearer some-bearer-token')) - .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)) - when: 'a pass-through data request is sent to NCMP with a bearer token' mvc.perform(request(httpMethod, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running') .queryParam('resourceIdentifier', 'my-resource-id') @@ -71,19 +75,13 @@ class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase { .andExpect(status().is2xxSuccessful()) then: 'DMI has received request with bearer token' - mockDmiServer.verify() + lastAuthHeaderReceived == 'Bearer some-bearer-token' where: 'all HTTP operations are applied' httpMethod << [GET, POST, PUT, PATCH, DELETE] } def 'Basic auth header is NOT passed from NCMP to DMI in pass-through data operations.'() { - given: 'DMI will expect to receive a request with no authorization header' - def targetDmiUrl = "$DMI_URL/dmi/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=my-resource-id" - mockDmiServer.expect(requestTo(targetDmiUrl)) - .andExpect(MockRestRequestMatchers.headerDoesNotExist(HttpHeaders.AUTHORIZATION)) - .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)) - when: 'a pass-through data request is sent to NCMP with basic authentication' mvc.perform(request(httpMethod, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running') .queryParam('resourceIdentifier', 'my-resource-id') @@ -93,18 +91,13 @@ class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase { .andExpect(status().is2xxSuccessful()) then: 'DMI has received request with no authorization header' - mockDmiServer.verify() + lastAuthHeaderReceived == null where: 'all HTTP operations are applied' httpMethod << [GET, POST, PUT, PATCH, DELETE] } def 'Bearer token is passed from NCMP to DMI in async batch pass-through data operation.'() { - given: 'DMI will expect to receive a request with a bearer token' - mockDmiServer.expect(method(POST)) - .andExpect(MockRestRequestMatchers.header(HttpHeaders.AUTHORIZATION, 'Bearer some-bearer-token')) - .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)) - when: 'a pass-through async data request is sent to NCMP with a bearer token' def requestBody = """{"operations": [{ "operation": "read", @@ -121,7 +114,9 @@ class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase { .andExpect(status().is2xxSuccessful()) then: 'DMI will receive the async request with bearer token' - mockDmiServer.verify(Duration.ofSeconds(1)) + new PollingConditions().within(3, () -> { + assert lastAuthHeaderReceived == 'Bearer some-bearer-token' + }) } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy index a6b516cd74..5c337f179b 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy @@ -41,19 +41,13 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { def kafkaConsumer = KafkaTestContainer.getConsumer('ncmp-group', StringDeserializer.class) - static final MODULE_REFERENCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json') - static final MODULE_RESOURCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json') - static final MODULE_REFERENCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_Response.json') - static final MODULE_RESOURCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_ResourcesResponse.json') - def setup() { objectUnderTest = networkCmProxyDataService - mockDmiWillRespondToHealthChecks(DMI_URL) } def 'CM Handle registration is successful.'() { given: 'DMI will return modules when requested' - mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A) + dmiDispatcher.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2'] and: 'consumer subscribed to topic' kafkaConsumer.subscribe(['ncmp-events']) @@ -88,16 +82,13 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { and: 'the CM-handle has expected modules' assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort() - and: 'DMI received expected requests' - mockDmiServer.verify() - cleanup: 'deregister CM handle' deregisterCmHandle(DMI_URL, 'ch-1') } def 'CM Handle goes to LOCKED state when DMI gives error during module sync.'() { given: 'DMI is not available to handle requests' - mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-1') + dmiDispatcher.isAvailable = false when: 'a CM-handle is registered for creation' def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1') @@ -122,13 +113,11 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { } def 'Create a CM-handle with existing moduleSetTag.'() { - given: 'existing CM-handles cm-1 with moduleSetTag "A", and cm-2 with moduleSetTag "B"' - mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A) - mockDmiResponsesForModuleSync(DMI_URL, 'ch-2', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B) + given: 'DMI will return modules when requested' + dmiDispatcher.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2'], 'ch-2': ['M1', 'M3']] + and: 'existing CM-handles cm-1 with moduleSetTag "A", and cm-2 with moduleSetTag "B"' registerCmHandle(DMI_URL, 'ch-1', 'A') registerCmHandle(DMI_URL, 'ch-2', 'B') - assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort() - assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort() when: 'a CM-handle is registered for creation with moduleSetTag "B"' def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B') @@ -152,11 +141,7 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { def 'CM Handle retry after failed module sync.'() { given: 'DMI is not initially available to handle requests' - mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-1') - mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-2') - and: 'DMI will be available for retry' - mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A) - mockDmiResponsesForModuleSync(DMI_URL, 'ch-2', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B) + dmiDispatcher.isAvailable = false when: 'CM-handles are registered for creation' def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1'), new NcmpServiceCmHandle(cmHandleId: 'ch-2')] @@ -179,7 +164,11 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.ADVISED assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.ADVISED - when: 'module sync runs' + when: 'DMI is available for retry' + dmiDispatcher.isAvailable = true + and: 'DMI will return expected modules' + dmiDispatcher.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2'], 'ch-2': ['M1', 'M3']] + and: 'module sync runs' moduleSyncWatchdog.moduleSyncAdvisedCmHandles() then: 'CM-handles go to READY state' new PollingConditions().within(3, () -> { @@ -192,8 +181,6 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { and: 'CM-handles have expected module set tags (blank)' assert objectUnderTest.getNcmpServiceCmHandle('ch-1').moduleSetTag == '' assert objectUnderTest.getNcmpServiceCmHandle('ch-2').moduleSetTag == '' - and: 'DMI received expected requests' - mockDmiServer.verify() cleanup: 'deregister CM handle' deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2']) diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy index 5421ad3237..4d1d77e694 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy @@ -27,38 +27,26 @@ import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse import org.onap.cps.ncmp.api.models.DmiPluginRegistration import org.onap.cps.ncmp.api.models.UpgradedCmHandles -import org.springframework.http.HttpStatus import spock.util.concurrent.PollingConditions -import static org.springframework.test.web.client.match.MockRestRequestMatchers.anything -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus - class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { NetworkCmProxyDataService objectUnderTest - static final INITIAL_MODULE_REFERENCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json') - static final INITIAL_MODULE_RESOURCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json') - static final UPDATED_MODULE_REFERENCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_Response.json') - static final UPDATED_MODULE_RESOURCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_ResourcesResponse.json') static final CM_HANDLE_ID = 'ch-1' static final CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG = 'ch-2' def setup() { objectUnderTest = networkCmProxyDataService - mockDmiWillRespondToHealthChecks(DMI_URL) } def 'Upgrade CM-handle with new moduleSetTag or no moduleSetTag.'() { - given: 'DMI will return modules for initial registration' - mockDmiResponsesForModuleSync(DMI_URL, CM_HANDLE_ID, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) - and: 'DMI returns different modules for upgrade' - mockDmiResponsesForModuleSync(DMI_URL, CM_HANDLE_ID, UPDATED_MODULE_REFERENCES_RESPONSE, UPDATED_MODULE_RESOURCES_RESPONSE) - - when: 'a CM-handle is created with expected initial modules: M1 and M2' + given: 'a CM-handle is created with expected initial modules: M1 and M2' + dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2'] registerCmHandle(DMI_URL, CM_HANDLE_ID, initialModuleSetTag) assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() - and: "the CM-handle is upgraded with given moduleSetTag '${updatedModuleSetTag}'" + + when: "the CM-handle is upgraded with given moduleSetTag '${updatedModuleSetTag}'" def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: updatedModuleSetTag) def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule( new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) @@ -72,14 +60,16 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_UPGRADE assert cmHandleCompositeState.lockReason.details == "Upgrade to ModuleSetTag: ${updatedModuleSetTag}" - when: 'module sync runs' + when: 'DMI will return different modules for upgrade: M1 and M3' + dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M3'] + and: 'module sync runs' moduleSyncWatchdog.resetPreviouslyFailedCmHandles() moduleSyncWatchdog.moduleSyncAdvisedCmHandles() then: 'CM-handle goes to READY state' - new PollingConditions().within(3, () -> { + new PollingConditions().eventually { assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState - }) + } and: 'the CM-handle has expected moduleSetTag' assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == updatedModuleSetTag @@ -87,9 +77,6 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { and: 'CM-handle has expected updated modules: M1 and M3' assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() - and: 'DMI received expected requests' - mockDmiServer.verify() - cleanup: 'deregister CM-handle' deregisterCmHandle(DMI_URL, CM_HANDLE_ID) @@ -103,8 +90,8 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { def 'Upgrade CM-handle with existing moduleSetTag.'() { given: 'DMI will return modules for registration' - mockDmiResponsesForModuleSync(DMI_URL, CM_HANDLE_ID, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) - mockDmiResponsesForModuleSync(DMI_URL, CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG, UPDATED_MODULE_REFERENCES_RESPONSE, UPDATED_MODULE_RESOURCES_RESPONSE) + dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2'] + dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG] = ['M1', 'M3'] and: "an existing CM-handle handle with moduleSetTag '${updatedModuleSetTag}'" registerCmHandle(DMI_URL, CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG, updatedModuleSetTag) assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG).moduleName.sort() @@ -125,9 +112,9 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { moduleSyncWatchdog.moduleSyncAdvisedCmHandles() then: 'CM-handle goes to READY state' - new PollingConditions().within(3, () -> { + new PollingConditions().eventually { assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState - }) + } and: 'the CM-handle has expected moduleSetTag' assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == updatedModuleSetTag @@ -146,7 +133,7 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { def 'Skip upgrade of CM-handle with same moduleSetTag as before.'() { given: 'an existing CM-handle with expected initial modules: M1 and M2' - mockDmiResponsesForModuleSync(DMI_URL, CM_HANDLE_ID, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) + dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2'] registerCmHandle(DMI_URL, CM_HANDLE_ID, 'same') assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() @@ -169,14 +156,13 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { } def 'Upgrade of CM-handle fails due to DMI error.'() { - given: 'DMI will return modules for initial registration' - mockDmiResponsesForModuleSync(DMI_URL, CM_HANDLE_ID, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) - and: 'DMI returns error code for upgrade' - mockDmiServer.expect(anything()).andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE)) - - when: 'a CM-handle is created' + given: 'a CM-handle exists' + dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2'] registerCmHandle(DMI_URL, CM_HANDLE_ID, 'oldTag') - and: 'the CM-handle is upgraded' + and: 'DMI is not available for upgrade' + dmiDispatcher.isAvailable = false + + when: 'the CM-handle is upgraded' def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'newTag') networkCmProxyDataService.updateDmiRegistrationAndSyncModule( new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) @@ -186,11 +172,11 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { moduleSyncWatchdog.moduleSyncAdvisedCmHandles() then: 'CM-handle goes to LOCKED state with reason MODULE_UPGRADE_FAILED' - new PollingConditions().within(3, () -> { + new PollingConditions(timeout: 3).eventually { def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID) assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_UPGRADE_FAILED - }) + } and: 'the CM-handle has same moduleSetTag as before' assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == 'oldTag' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmNotificationSubscriptionSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmNotificationSubscriptionSpec.groovy index 9129f09fb5..302c7e512c 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmNotificationSubscriptionSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmNotificationSubscriptionSpec.groovy @@ -1,3 +1,23 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + package org.onap.cps.integration.functional import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING; diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpRestApiSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpRestApiSpec.groovy index d7f8771e18..950cd65e72 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpRestApiSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpRestApiSpec.groovy @@ -32,20 +32,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. class NcmpRestApiSpec extends CpsIntegrationSpecBase { - static final MODULE_REFERENCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json') - static final MODULE_RESOURCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json') - static final MODULE_REFERENCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_Response.json') - static final MODULE_RESOURCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_ResourcesResponse.json') - - def setup() { - mockDmiWillRespondToHealthChecks(DMI_URL) - } - def 'Register CM Handles using REST API.'() { given: 'DMI will return modules' - mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A) - mockDmiResponsesForModuleSync(DMI_URL, 'ch-2', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A) - mockDmiResponsesForModuleSync(DMI_URL, 'ch-3', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B) + dmiDispatcher.moduleNamesPerCmHandleId = [ + 'ch-1': ['M1', 'M2'], + 'ch-2': ['M1', 'M2'], + 'ch-3': ['M1', 'M3'] + ] and: 'a POST request is made to register the CM Handles' def requestBody = '{"dmiPlugin":"'+DMI_URL+'","createdCmHandles":[{"cmHandle":"ch-1"},{"cmHandle":"ch-2"},{"cmHandle":"ch-3"}]}' mvc.perform(post('/ncmpInventory/v1/ch').contentType(MediaType.APPLICATION_JSON).content(requestBody)) @@ -53,10 +46,12 @@ class NcmpRestApiSpec extends CpsIntegrationSpecBase { when: 'module sync runs' moduleSyncWatchdog.moduleSyncAdvisedCmHandles() then: 'CM-handles go to READY state' - new PollingConditions(timeout: 3, delay: 0.5).eventually { - mvc.perform(get('/ncmp/v1/ch/ch-1')) - .andExpect(status().isOk()) - .andExpect(jsonPath('$.state.cmHandleState').value('READY')) + new PollingConditions().eventually { + (1..3).each { + mvc.perform(get('/ncmp/v1/ch/ch-'+it)) + .andExpect(status().isOk()) + .andExpect(jsonPath('$.state.cmHandleState').value('READY')) + } } } @@ -71,7 +66,7 @@ class NcmpRestApiSpec extends CpsIntegrationSpecBase { ] }""".formatted(moduleName) expect: "a search for module ${moduleName} returns expected CM handles" - mvc.perform(post(DMI_URL+'/ncmp/v1/ch/id-searches').contentType(MediaType.APPLICATION_JSON).content(requestBodyWithModuleCondition)) + mvc.perform(post('/ncmp/v1/ch/id-searches').contentType(MediaType.APPLICATION_JSON).content(requestBodyWithModuleCondition)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath('$[*]', containsInAnyOrder(expectedCmHandles.toArray()))) .andExpect(jsonPath('$', hasSize(expectedCmHandles.size()))); |