aboutsummaryrefslogtreecommitdiffstats
path: root/integration-test
diff options
context:
space:
mode:
Diffstat (limited to 'integration-test')
-rw-r--r--integration-test/pom.xml32
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy109
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy78
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy78
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy189
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/AnchorServiceIntegrationSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAnchorServiceIntegrationSpec.groovy)6
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy)62
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataspaceServiceIntegrationSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataspaceServiceIntegrationSpec.groovy)6
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy)17
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy)8
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/SessionManagerIntegrationSpec.groovy)4
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy54
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpBearerTokenPassthroughSpec.groovy)42
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy248
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpdateSpec.groovy89
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpgradeSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy)133
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmNotificationSubscriptionSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmNotificationSubscriptionSpec.groovy)51
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobResultServiceSpec.groovy44
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy23
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy65
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy156
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy72
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/RestApiSpec.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpRestApiSpec.groovy)54
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/WriteSubJobSpec.groovy75
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy22
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy4
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy5
-rw-r--r--integration-test/src/test/java/org/onap/cps/integration/DmiStubTestContainer.java61
-rw-r--r--integration-test/src/test/java/org/onap/cps/integration/KafkaTestContainer.java25
-rw-r--r--integration-test/src/test/java/org/onap/cps/integration/MicroMeterTestConfig.java41
-rw-r--r--integration-test/src/test/java/org/onap/cps/integration/ResourceMeter.java7
-rw-r--r--integration-test/src/test/resources/application-module-sync-delayed.yml23
-rw-r--r--integration-test/src/test/resources/application.yml22
-rw-r--r--integration-test/src/test/resources/data/inventory/cmHandleTemplate.json (renamed from integration-test/src/test/resources/data/ncmp-registry/innerNode.json)6
-rw-r--r--integration-test/src/test/resources/data/inventory/cmHandleWithAlternateIdTemplate.json (renamed from integration-test/src/test/resources/data/ncmp-registry/innerCmHandleNode.json)0
-rw-r--r--integration-test/src/test/resources/data/inventory/dmi-registry@2024-02-23.yang (renamed from integration-test/src/test/resources/data/ncmp-registry/dmi-registry@2024-02-23.yang)0
36 files changed, 1440 insertions, 471 deletions
diff --git a/integration-test/pom.xml b/integration-test/pom.xml
index a3112661e9..7ac9460f5e 100644
--- a/integration-test/pom.xml
+++ b/integration-test/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.5.1-SNAPSHOT</version>
+ <version>3.5.5-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -107,30 +107,10 @@
<artifactId>spock</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-json</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
-
- <profiles>
- <!-- Performance tests are run with maven-failsafe-plugin using a separate profile, so they will
- not affect Jacoco coverage. Heap size is set here to ensure consistent test environment. -->
- <profile>
- <id>include-performance</id>
- <properties>
- <failsafeArgLine>-Xms512m -Xmx512m</failsafeArgLine>
- </properties>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-failsafe-plugin</artifactId>
- <configuration>
- <includes>
- <include>**/*PerfTest.java</include>
- </includes>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
-
</project>
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 0afdfa1066..02a10cfa6b 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
@@ -21,6 +21,7 @@
package org.onap.cps.integration.base
+import com.hazelcast.collection.ISet
import okhttp3.mockwebserver.MockWebServer
import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDataService
@@ -37,21 +38,23 @@ import org.onap.cps.ncmp.impl.data.NetworkCmProxyQueryService
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
import org.onap.cps.ncmp.impl.inventory.ParameterizedCmHandleQueryService
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
+import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncService
import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncWatchdog
import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
+import org.onap.cps.ri.repository.DataspaceRepository
+import org.onap.cps.ri.utils.SessionManager
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
import org.onap.cps.spi.model.DataNode
-import org.onap.cps.spi.repository.DataspaceRepository
-import org.onap.cps.spi.utils.SessionManager
-import org.onap.cps.utils.ContentType
import org.onap.cps.utils.JsonObjectMapper
import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.ComponentScan
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
+import org.springframework.test.context.ActiveProfiles
import org.springframework.test.web.servlet.MockMvc
import org.testcontainers.spock.Testcontainers
import spock.lang.Shared
@@ -59,11 +62,7 @@ import spock.lang.Specification
import spock.util.concurrent.PollingConditions
import java.time.OffsetDateTime
-import java.time.format.DateTimeFormatter
-
-import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME
-import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
-import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT
+import java.util.concurrent.BlockingQueue
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = [CpsDataspaceService])
@Testcontainers
@@ -71,7 +70,8 @@ import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY
@AutoConfigureMockMvc
@EnableJpaRepositories(basePackageClasses = [DataspaceRepository])
@ComponentScan(basePackages = ['org.onap.cps'])
-@EntityScan('org.onap.cps.spi.entities')
+@EntityScan('org.onap.cps.ri.models')
+@ActiveProfiles('module-sync-delayed')
abstract class CpsIntegrationSpecBase extends Specification {
@Shared
@@ -117,6 +117,12 @@ abstract class CpsIntegrationSpecBase extends Specification {
ModuleSyncWatchdog moduleSyncWatchdog
@Autowired
+ ModuleSyncService moduleSyncService
+
+ @Autowired
+ BlockingQueue<DataNode> moduleSyncWorkQueue
+
+ @Autowired
JsonObjectMapper jsonObjectMapper
@Autowired
@@ -125,16 +131,31 @@ abstract class CpsIntegrationSpecBase extends Specification {
@Autowired
AlternateIdMatcher alternateIdMatcher
- MockWebServer mockDmiServer = null
- DmiDispatcher dmiDispatcher = new DmiDispatcher()
+ @Autowired
+ ISet<String> moduleSetTagsBeingProcessed
- def DMI_URL = null
+ @Value('${ncmp.policy-executor.server.port:8080}')
+ private String policyServerPort;
+
+ MockWebServer mockDmiServer1 = new MockWebServer()
+ MockWebServer mockDmiServer2 = new MockWebServer()
+ MockWebServer mockPolicyServer = new MockWebServer()
+
+ DmiDispatcher dmiDispatcher1 = new DmiDispatcher()
+ DmiDispatcher dmiDispatcher2 = new DmiDispatcher()
+
+ PolicyDispatcher policyDispatcher = new PolicyDispatcher();
+
+ def DMI1_URL = null
+ def DMI2_URL = null
static NO_MODULE_SET_TAG = ''
+ static NO_ALTERNATE_ID = ''
static GENERAL_TEST_DATASPACE = 'generalTestDataspace'
static BOOKSTORE_SCHEMA_SET = 'bookstoreSchemaSet'
+ static MODULE_SYNC_WAIT_TIME_IN_SECONDS = 10
- def static initialized = false
+ static initialized = false
def now = OffsetDateTime.now()
def setup() {
@@ -143,14 +164,24 @@ abstract class CpsIntegrationSpecBase extends Specification {
createStandardBookStoreSchemaSet(GENERAL_TEST_DATASPACE)
initialized = true
}
- mockDmiServer = new MockWebServer()
- mockDmiServer.setDispatcher(dmiDispatcher)
- mockDmiServer.start()
- DMI_URL = String.format("http://%s:%s", mockDmiServer.getHostName(), mockDmiServer.getPort())
+ mockDmiServer1.setDispatcher(dmiDispatcher1)
+ mockDmiServer1.start()
+
+ mockDmiServer2.setDispatcher(dmiDispatcher2)
+ mockDmiServer2.start()
+
+ mockPolicyServer.setDispatcher(policyDispatcher)
+ mockPolicyServer.start(Integer.valueOf(policyServerPort))
+
+ DMI1_URL = String.format("http://%s:%s", mockDmiServer1.getHostName(), mockDmiServer1.getPort())
+ DMI2_URL = String.format("http://%s:%s", mockDmiServer2.getHostName(), mockDmiServer2.getPort())
}
def cleanup() {
- mockDmiServer.shutdown()
+ mockDmiServer1.shutdown()
+ mockDmiServer2.shutdown()
+ mockPolicyServer.shutdown()
+ moduleSetTagsBeingProcessed.clear()
}
def static readResourceDataFile(filename) {
@@ -216,27 +247,49 @@ abstract class CpsIntegrationSpecBase extends Specification {
// *** NCMP Integration Test Utilities ***
def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag) {
- def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, moduleSetTag: moduleSetTag)
- networkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: [cmHandleToCreate]))
+ registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag, NO_ALTERNATE_ID)
+ }
+
+ def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag, alternateId) {
+ registerCmHandleWithoutWaitForReady(dmiPlugin, cmHandleId, moduleSetTag, alternateId)
moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
- new PollingConditions().within(3, () -> {
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
CmHandleState.READY == networkCmProxyInventoryFacade.getCmHandleCompositeState(cmHandleId).cmHandleState
})
}
+ def registerCmHandleWithoutWaitForReady(dmiPlugin, cmHandleId, moduleSetTag, alternateId) {
+ def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, moduleSetTag: moduleSetTag, alternateId: alternateId)
+ networkCmProxyInventoryFacade.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: [cmHandleToCreate]))
+ }
+
+ def registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(dmiPlugin, moduleSetTag, numberOfCmHandles, offset) {
+ def cmHandles = []
+ def id = offset
+ def moduleReferences = (1..200).collect { moduleSetTag + '_Module_' + it.toString() }
+ (1..numberOfCmHandles).each {
+ def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch-'+id, moduleSetTag: moduleSetTag, alternateId: NO_ALTERNATE_ID)
+ cmHandles.add(ncmpServiceCmHandle)
+ dmiDispatcher1.moduleNamesPerCmHandleId[ncmpServiceCmHandle.cmHandleId] = moduleReferences
+ dmiDispatcher2.moduleNamesPerCmHandleId[ncmpServiceCmHandle.cmHandleId] = moduleReferences
+ id++
+ }
+ networkCmProxyInventoryFacade.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: cmHandles))
+ }
+
def deregisterCmHandle(dmiPlugin, cmHandleId) {
deregisterCmHandles(dmiPlugin, [cmHandleId])
}
def deregisterCmHandles(dmiPlugin, cmHandleIds) {
- networkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds))
+ networkCmProxyInventoryFacade.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds))
}
- 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);
- def jsonForUpdate = '{ "state": { "last-update-time": "%s" } }'.formatted(ISO_TIMESTAMP_FORMATTER.format(newUpdateTime))
- cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='${cmHandleId}']", jsonForUpdate, now, ContentType.JSON)
+ def deregisterSequenceOfCmHandles(dmiPlugin, numberOfCmHandles, offset) {
+ def cmHandleIds = []
+ def id = offset
+ (1..numberOfCmHandles).each { cmHandleIds.add('ch-' + id++) }
+ networkCmProxyInventoryFacade.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds))
}
+
}
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
index 6676cb74c2..35a7b6a7c2 100644
--- 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
@@ -20,16 +20,18 @@
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 groovy.json.JsonSlurper
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.RecordedRequest
+import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
+import java.util.regex.Matcher
+
+import static org.onap.cps.integration.base.CpsIntegrationSpecBase.readResourceDataFile
+
/**
* This class simulates responses from the DMI server in NCMP integration tests.
*
@@ -54,51 +56,95 @@ class DmiDispatcher extends Dispatcher {
def isAvailable = true
- Map<String, List<String>> moduleNamesPerCmHandleId = [:]
+ def jsonSlurper = new JsonSlurper()
+ def moduleNamesPerCmHandleId = [:]
+ def receivedSubJobs = [:]
+ def lastAuthHeaderReceived
+ def dmiResourceDataUrl
@Override
MockResponse dispatch(RecordedRequest request) {
if (!isAvailable) {
- return new MockResponse().setResponseCode(HttpStatus.SERVICE_UNAVAILABLE.value())
+ return mockResponse(HttpStatus.SERVICE_UNAVAILABLE)
+ }
+ if (request.path == '/actuator/health') {
+ return mockResponseWithBody(HttpStatus.OK, '{"status":"UP"}')
}
+
+ lastAuthHeaderReceived = request.getHeader('Authorization')
switch (request.path) {
- case ~/^\/dmi\/v1\/ch\/(.*)\/modules$/:
+ // get module references for a CM-handle
+ case ~'^/dmi/v1/ch/(.*)/modules$':
def cmHandleId = Matcher.lastMatcher[0][1]
return getModuleReferencesResponse(cmHandleId)
- case ~/^\/dmi\/v1\/ch\/(.*)\/moduleResources$/:
+ // get module resources for a CM-handle
+ case ~'^/dmi/v1/ch/(.*)/moduleResources$':
def cmHandleId = Matcher.lastMatcher[0][1]
return getModuleResourcesResponse(cmHandleId)
+ // pass-through data operation for a CM-handle
+ case ~'^/dmi/v1/ch/(.*)/data/ds/(.*)$':
+ dmiResourceDataUrl = request.path
+ return mockResponseWithBody(HttpStatus.OK, '{}')
+
+ // legacy pass-through batch data operation
+ case ~'^/dmi/v1/data$':
+ return mockResponseWithBody(HttpStatus.ACCEPTED, '{}')
+
+ // get data job status
+ case ~'^/dmi/v1/cmwriteJob/dataProducer/(.*)/dataProducerJob/(.*)/status$':
+ return mockResponseWithBody(HttpStatus.OK, '{"status":"status details from mock service"}')
+
+ // get data job result
+ case ~'^/dmi/v1/cmwriteJob/dataProducer/(.*)/dataProducerJob/(.*)/result(.*)$':
+ return mockResponseWithBody(HttpStatus.OK, '{ "result": "some result"}')
+
+ // get write sub job response
+ case ~'^/dmi/v1/cmwriteJob(.*)$':
+ return mockWriteJobResponse(request)
+
default:
- throw new IllegalArgumentException('Mock DMI does not handle path ' + request.path)
+ throw new IllegalArgumentException('Mock DMI does not implement endpoint ' + request.path)
}
}
- private getModuleReferencesResponse(cmHandleId) {
+ def mockWriteJobResponse(request) {
+ def destination = Matcher.lastMatcher[0][1]
+ def subJobWriteRequest = jsonSlurper.parseText(request.getBody().readUtf8())
+ this.receivedSubJobs.put(destination, subJobWriteRequest)
+ def response = '{"subJobId":"some sub job id", "dmiServiceName":"some dmi service name", "dataProducerId":"some data producer id"}'
+ return mockResponseWithBody(HttpStatus.OK, response)
+ }
+
+ def getModuleReferencesResponse(cmHandleId) {
def moduleReferences = '{"schemas":[' + getModuleNamesForCmHandle(cmHandleId).collect {
MODULE_REFERENCES_RESPONSE_TEMPLATE.replaceAll("<MODULE_NAME>", it)
}.join(',') + ']}'
- return mockOkResponseWithBody(moduleReferences)
+ return mockResponseWithBody(HttpStatus.OK, moduleReferences)
}
- private getModuleResourcesResponse(cmHandleId) {
+ def getModuleResourcesResponse(cmHandleId) {
def moduleResources = '[' + getModuleNamesForCmHandle(cmHandleId).collect {
MODULE_RESOURCES_RESPONSE_TEMPLATE.replaceAll("<MODULE_NAME>", it)
}.join(',') + ']'
- return mockOkResponseWithBody(moduleResources)
+ return mockResponseWithBody(HttpStatus.OK, moduleResources)
}
- private getModuleNamesForCmHandle(cmHandleId) {
+ def 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) {
+ def static mockResponse(status) {
+ return new MockResponse().setResponseCode(status.value())
+ }
+
+ def static mockResponseWithBody(status, responseBody) {
return new MockResponse()
- .setResponseCode(HttpStatus.OK.value())
+ .setResponseCode(status.value())
.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
.setBody(responseBody)
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy
new file mode 100644
index 0000000000..b08d1c1548
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy
@@ -0,0 +1,78 @@
+/*
+ * ============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 okhttp3.mockwebserver.Dispatcher
+import okhttp3.mockwebserver.MockResponse
+import okhttp3.mockwebserver.RecordedRequest
+import org.springframework.http.HttpHeaders
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper
+
+import java.util.concurrent.TimeUnit
+
+/**
+ * This class simulates responses from the Policy Execution server in NCMP integration tests.
+ */
+class PolicyDispatcher extends Dispatcher {
+
+ def objectMapper = new ObjectMapper()
+ def expectedAuthorizationToken = 'ABC'
+ def allowAll = true; // Prevents legacy test being affected
+
+ @Override
+ MockResponse dispatch(RecordedRequest recordedRequest) {
+
+ if (!allowAll && !recordedRequest.getHeader('Authorization').contains(expectedAuthorizationToken)) {
+ return new MockResponse().setResponseCode(401)
+ }
+
+ if (recordedRequest.path != '/policy-executor/api/v1/execute') {
+ return new MockResponse().setResponseCode(400)
+ }
+
+ def body = objectMapper.readValue(recordedRequest.getBody().readUtf8(), Map.class)
+ def targetIdentifier = body.get('requests').get(0).get('data').get('targetIdentifier')
+ def responseAsMap = [:]
+ responseAsMap.put('decisionId',1)
+ if (targetIdentifier == "mock slow response") {
+ TimeUnit.SECONDS.sleep(2) // One second more then configured readTimeoutInSeconds
+ }
+ if (allowAll || targetIdentifier == 'fdn1') {
+ responseAsMap.put('decision','allow')
+ responseAsMap.put('message','')
+ } else {
+ responseAsMap.put('decision','deny from mock server (dispatcher)')
+ responseAsMap.put('message','I only like fdn1')
+ }
+ def responseAsString = objectMapper.writeValueAsString(responseAsMap)
+
+ return mockResponseWithBody(HttpStatus.OK, responseAsString)
+ }
+
+ static mockResponseWithBody(status, responseBody) {
+ return new MockResponse()
+ .setResponseCode(status.value())
+ .addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+ .setBody(responseBody)
+ }
+}
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
deleted file mode 100644
index 0f442a82f1..0000000000
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * ============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 org.apache.kafka.common.TopicPartition
-import org.apache.kafka.common.serialization.StringDeserializer
-import org.onap.cps.integration.KafkaTestContainer
-import org.onap.cps.integration.base.CpsIntegrationSpecBase
-import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade
-import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse
-import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
-import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
-import org.onap.cps.ncmp.events.lcm.v1.LcmEvent
-import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
-import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory
-import spock.util.concurrent.PollingConditions
-
-import java.time.Duration
-import java.time.OffsetDateTime
-
-class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase {
-
- NetworkCmProxyInventoryFacade objectUnderTest
-
- def kafkaConsumer = KafkaTestContainer.getConsumer('ncmp-group', StringDeserializer.class)
-
- def setup() {
- objectUnderTest = networkCmProxyInventoryFacade
- }
-
- def 'CM Handle registration is successful.'() {
- given: 'DMI will return modules when requested'
- dmiDispatcher.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
-
- and: 'consumer subscribed to topic'
- kafkaConsumer.subscribe(['ncmp-events'])
-
- when: 'a CM-handle is registered for creation'
- def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
- def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
- def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-
- then: 'registration gives successful response'
- assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse('ch-1')]
-
- and: 'CM-handle is initially in ADVISED state'
- assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
-
- when: 'module sync runs'
- moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
-
- then: 'CM-handle goes to READY state'
- new PollingConditions().within(3, () -> {
- assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
- })
-
- and: 'the messages is polled'
- def message = kafkaConsumer.poll(Duration.ofMillis(10000))
- def records = message.records(new TopicPartition('ncmp-events', 0))
-
- and: 'the newest lcm event notification is received with READY state'
- def notificationMessage = jsonObjectMapper.convertJsonString(records.last().value().toString(), LcmEvent)
- assert notificationMessage.event.newValues.cmHandleState.value() == 'READY'
-
- and: 'the CM-handle has expected modules'
- assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
-
- 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'
- dmiDispatcher.isAvailable = false
-
- when: 'a CM-handle is registered for creation'
- def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
- def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
- objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-
- and: 'module sync runs'
- moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
-
- then: 'CM-handle goes to LOCKED state with reason MODULE_SYNC_FAILED'
- new PollingConditions().within(3, () -> {
- def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState('ch-1')
- assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED
- assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_SYNC_FAILED
- })
-
- and: 'CM-handle has no modules'
- assert objectUnderTest.getYangResourcesModuleReferences('ch-1').empty
-
- cleanup: 'deregister CM handle'
- deregisterCmHandle(DMI_URL, 'ch-1')
- }
-
- def 'Create a CM-handle with existing moduleSetTag.'() {
- 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')
-
- when: 'a CM-handle is registered for creation with moduleSetTag "B"'
- def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B')
- objectUnderTest.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]))
-
- then: 'the CM-handle goes to READY state after module sync'
- moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
- new PollingConditions().within(3, () -> {
- assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-3').cmHandleState
- })
-
- and: 'the CM-handle has expected moduleSetTag'
- assert objectUnderTest.getNcmpServiceCmHandle('ch-3').moduleSetTag == 'B'
-
- and: 'the CM-handle has expected modules from module set "B": M1 and M3'
- assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort()
-
- cleanup: 'deregister CM handles'
- deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2', 'ch-3'])
- }
-
- def 'CM Handle retry after failed module sync.'() {
- given: 'DMI is not initially available to handle requests'
- dmiDispatcher.isAvailable = false
-
- when: 'CM-handles are registered for creation'
- def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1'), new NcmpServiceCmHandle(cmHandleId: 'ch-2')]
- def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: cmHandlesToCreate)
- objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
- and: 'module sync runs'
- moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
- then: 'CM-handles go to LOCKED state'
- new PollingConditions().within(3, () -> {
- assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.LOCKED
- assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.LOCKED
- })
-
- when: 'we wait for LOCKED CM handle retry time (actually just subtract 3 minutes from handles lastUpdateTime)'
- overrideCmHandleLastUpdateTime('ch-1', OffsetDateTime.now().minusMinutes(3))
- overrideCmHandleLastUpdateTime('ch-2', OffsetDateTime.now().minusMinutes(3))
- and: 'failed CM handles are reset'
- moduleSyncWatchdog.resetPreviouslyFailedCmHandles()
- then: 'CM-handles are ADVISED state'
- assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.ADVISED
- assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.ADVISED
-
- 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, () -> {
- assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.READY
- assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.READY
- })
- and: 'CM-handles have expected modules'
- assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
- assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort()
- and: 'CM-handles have expected module set tags (blank)'
- assert objectUnderTest.getNcmpServiceCmHandle('ch-1').moduleSetTag == ''
- assert objectUnderTest.getNcmpServiceCmHandle('ch-2').moduleSetTag == ''
-
- cleanup: 'deregister CM handle'
- deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2'])
- }
-}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAnchorServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/AnchorServiceIntegrationSpec.groovy
index 26857799a1..240ff5114b 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAnchorServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/AnchorServiceIntegrationSpec.groovy
@@ -19,18 +19,18 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.cps
import java.time.OffsetDateTime
import org.onap.cps.api.CpsAnchorService
-import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.integration.base.FunctionalSpecBase
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.onap.cps.spi.exceptions.AnchorNotFoundException
import org.onap.cps.utils.ContentType
-class CpsAnchorServiceIntegrationSpec extends CpsIntegrationSpecBase {
+class AnchorServiceIntegrationSpec extends FunctionalSpecBase {
CpsAnchorService objectUnderTest
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy
index 779c0b84c4..d49931eb7e 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy
@@ -19,7 +19,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.cps
import org.onap.cps.api.CpsDataService
import org.onap.cps.integration.base.FunctionalSpecBase
@@ -39,7 +39,7 @@ import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
-class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
+class DataServiceIntegrationSpec extends FunctionalSpecBase {
CpsDataService objectUnderTest
def originalCountBookstoreChildNodes
@@ -224,7 +224,7 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'a new (multiple-data-tree:invoice) datanodes'
def json = '{"bookstore-address":[{"bookstore-name":"Easons","address":"Bangalore,India","postal-code":"560043"}]}'
when: 'the new list elements are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/', json, now, ContentType.JSON)
then: 'they can be retrieved by their xpaths'
objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore-address[@bookstore-name="Easons"]', INCLUDE_ALL_DESCENDANTS)
and: 'there is one extra datanode'
@@ -239,7 +239,7 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'two new (categories) data nodes'
def json = '{"categories": [ {"code":"new1"}, {"code":"new2" } ] }'
when: 'the new list elements are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'they can be retrieved by their xpaths'
objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', DIRECT_CHILDREN_ONLY).size() == 1
objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', DIRECT_CHILDREN_ONLY).size() == 1
@@ -256,7 +256,7 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'two (categories) data nodes, one new and one existing'
def json = '{"categories": [ {"code":"1"}, {"code":"new1"} ] }'
when: 'attempt to save the list element'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'an exception that (one cps paths is) already defined is thrown '
def exceptionThrown = thrown(AlreadyDefinedException)
exceptionThrown.alreadyDefinedObjectNames == ['/bookstore/categories[@code=\'1\']' ] as Set
@@ -270,7 +270,7 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'a new (categories) data nodes'
def json = '{"categories": [ {"code":"new1"} ] }'
and: 'the new list element is saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
when: 'the new element is deleted'
objectUnderTest.deleteListOrListElement(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', now)
then: 'the original number of data nodes is restored'
@@ -281,7 +281,7 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'two new (categories) data nodes in a single batch'
def json = '{"categories": [ {"code":"new1"}, {"code":"new2"} ] }'
when: 'the batches of new list element(s) are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'they can be retrieved by their xpaths'
assert objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', DIRECT_CHILDREN_ONLY).size() == 1
assert objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', DIRECT_CHILDREN_ONLY).size() == 1
@@ -298,7 +298,7 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'one existing and one new (categories) data nodes in a single batch'
def json = '{"categories": [ {"code":"new1"}, {"code":"1"} ] }'
when: 'the batches of new list element(s) are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'an already defined (batch) exception is thrown for the existing path'
def exceptionThrown = thrown(AlreadyDefinedException)
assert exceptionThrown.alreadyDefinedObjectNames == ['/bookstore/categories[@code=\'1\']' ] as Set
@@ -396,7 +396,7 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
objectUnderTest.saveData(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
when: 'the webinfo (container) is updated'
json = '{"webinfo": {"domain-name":"newdomain.com" ,"contact-email":"info@newdomain.com" }}'
- objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now)
+ objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now, ContentType.JSON)
then: 'webinfo has been updated with teh new details'
def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/webinfo', DIRECT_CHILDREN_ONLY)
result.leaves.'domain-name'[0] == 'newdomain.com'
@@ -408,7 +408,7 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
def 'Update bookstore top-level container data node.'() {
when: 'the bookstore top-level container is updated'
def json = '{ "bookstore": { "bookstore-name": "new bookstore" }}'
- objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/', json, now)
+ objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/', json, now, ContentType.JSON)
then: 'bookstore name has been updated'
def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', DIRECT_CHILDREN_ONLY)
result.leaves.'bookstore-name'[0] == 'new bookstore'
@@ -459,17 +459,19 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
def 'Get delta between 2 anchors'() {
when: 'attempt to get delta report between anchors'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, '/', OMIT_DESCENDANTS)
+ and: 'report is ordered based on xpath'
+ result = result.toList().sort { it.xpath }
then: 'delta report contains expected number of changes'
result.size() == 3
- and: 'delta report contains UPDATE action with expected xpath'
- assert result[0].getAction() == 'update'
+ and: 'delta report contains REPLACE action with expected xpath'
+ assert result[0].getAction() == 'replace'
assert result[0].getXpath() == '/bookstore'
+ and: 'delta report contains CREATE action with expected xpath'
+ assert result[1].getAction() == 'create'
+ assert result[1].getXpath() == "/bookstore-address[@bookstore-name='Crossword Bookstores']"
and: 'delta report contains REMOVE action with expected xpath'
- assert result[1].getAction() == 'remove'
- assert result[1].getXpath() == "/bookstore-address[@bookstore-name='Easons-1']"
- and: 'delta report contains ADD action with expected xpath'
- assert result[2].getAction() == 'add'
- assert result[2].getXpath() == "/bookstore-address[@bookstore-name='Crossword Bookstores']"
+ assert result[2].getAction() == 'remove'
+ assert result[2].getXpath() == "/bookstore-address[@bookstore-name='Easons-1']"
}
def 'Get delta between 2 anchors returns empty response when #scenario'() {
@@ -513,11 +515,11 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
'is empty' | "/bookstore/container-without-leaves"
}
- def 'Get delta between anchors for add action, where target data node #scenario'() {
+ def 'Get delta between anchors for "create" action, where target data node #scenario'() {
when: 'attempt to get delta between leaves of data nodes present in 2 anchors'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, parentNodeXpath, INCLUDE_ALL_DESCENDANTS)
then: 'the expected action is present in delta report'
- result.get(0).getAction() == 'add'
+ result.get(0).getAction() == 'create'
and: 'the expected xapth is present in delta report'
result.get(0).getXpath() == parentNodeXpath
where: 'following data was used'
@@ -531,8 +533,8 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
def 'Get delta between anchors when leaves of existing data nodes are updated,: #scenario'() {
when: 'attempt to get delta between leaves of existing data nodes'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, sourceAnchor, targetAnchor, xpath, OMIT_DESCENDANTS)
- then: 'expected action is update'
- assert result[0].getAction() == 'update'
+ then: 'expected action is "replace"'
+ assert result[0].getAction() == 'replace'
and: 'the payload has expected leaf values'
def sourceData = result[0].getSourceData()
def targetData = result[0].getTargetData()
@@ -548,8 +550,8 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
def 'Get delta between anchors when child data nodes under existing parent data nodes are updated: #scenario'() {
when: 'attempt to get delta between leaves of existing data nodes'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, sourceAnchor, targetAnchor, xpath, DIRECT_CHILDREN_ONLY)
- then: 'expected action is update'
- assert result[0].getAction() == 'update'
+ then: 'expected action is "replace"'
+ assert result[0].getAction() == 'replace'
and: 'the delta report has expected child node xpaths'
def deltaReportEntities = getDeltaReportEntities(result)
def childNodeXpathsInDeltaReport = deltaReportEntities.get('xpaths')
@@ -571,8 +573,8 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
when: 'attempt to get delta between leaves of existing data nodes'
def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, parentNodeXpath, INCLUDE_ALL_DESCENDANTS)
def deltaReportEntities = getDeltaReportEntities(result)
- then: 'expected action is update'
- assert result[0].getAction() == 'update'
+ then: 'expected action is "replace"'
+ assert result[0].getAction() == 'replace'
and: 'the payload has expected parent node xpath'
assert deltaReportEntities.get('xpaths').contains(parentNodeXpath)
and: 'delta report has expected source and target data'
@@ -591,14 +593,14 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
def result = objectUnderTest.getDeltaByDataspaceAnchorAndPayload(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, '/', [:], jsonPayload, OMIT_DESCENDANTS)
then: 'delta report contains expected number of changes'
result.size() == 3
- and: 'delta report contains UPDATE action with expected xpath'
- assert result[0].getAction() == 'update'
+ and: 'delta report contains "replace" action with expected xpath'
+ assert result[0].getAction() == 'replace'
assert result[0].getXpath() == '/bookstore'
- and: 'delta report contains REMOVE action with expected xpath'
+ and: 'delta report contains "remove" action with expected xpath'
assert result[1].getAction() == 'remove'
assert result[1].getXpath() == "/bookstore-address[@bookstore-name='Easons-1']"
- and: 'delta report contains ADD action with expected xpath'
- assert result[2].getAction() == 'add'
+ and: 'delta report contains "create" action with expected xpath'
+ assert result[2].getAction() == 'create'
assert result[2].getXpath() == "/bookstore-address[@bookstore-name='Crossword Bookstores']"
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataspaceServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataspaceServiceIntegrationSpec.groovy
index 739e802244..d69f6cca0c 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataspaceServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataspaceServiceIntegrationSpec.groovy
@@ -18,15 +18,15 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.cps
import org.onap.cps.api.CpsDataspaceService
-import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.integration.base.FunctionalSpecBase
import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.onap.cps.spi.exceptions.DataspaceInUseException
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
-class CpsDataspaceServiceIntegrationSpec extends CpsIntegrationSpecBase {
+class DataspaceServiceIntegrationSpec extends FunctionalSpecBase {
CpsDataspaceService objectUnderTest
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy
index b7b6fa11a7..9e51d80d9e 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.cps
import org.onap.cps.api.CpsModuleService
import org.onap.cps.integration.base.FunctionalSpecBase
@@ -31,7 +31,7 @@ import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
import org.onap.cps.spi.model.ModuleDefinition
import org.onap.cps.spi.model.ModuleReference
-class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
+class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
CpsModuleService objectUnderTest
@@ -134,7 +134,7 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ 'newSchema1', 'newSchema2'])
}
- def 'Create schema set error scenario: #scenario.'() {
+ def 'Attempt to create schema set, error scenario: #scenario.'() {
when: 'attempt to store schema set #schemaSetName in dataspace #dataspaceName'
populateNewYangResourcesNameToContentMapAndAllModuleReferences(0)
objectUnderTest.createSchemaSet(dataspaceName, schemaSetName, newYangResourcesNameToContentMap)
@@ -146,6 +146,14 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
'schema set already exists' | FUNCTIONAL_TEST_DATASPACE_1 | BOOKSTORE_SCHEMA_SET || AlreadyDefinedException
}
+ def 'Attempt to create duplicate schema set from modules.'() {
+ when: 'attempt to store duplicate schema set from modules'
+ objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, newYangResourcesNameToContentMap, [])
+ then: 'an Already Defined Exception is thrown'
+ thrown(AlreadyDefinedException)
+ }
+
+
/*
R E A D S C H E M A S E T I N F O U S E - C A S E S
*/
@@ -215,7 +223,8 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
when: 'all schema sets are retrieved'
def result = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1)
then: 'the result contains all expected schema sets'
- assert result.name == [ 'bookstoreSchemaSet', 'newSchema1' ]
+ assert result.name.size() == 2
+ assert result.name.containsAll('bookstoreSchemaSet', 'newSchema1')
cleanup:
objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchema1'])
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy
index 146ea95e8b..5c2a4fc665 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation
+ * Copyright (C) 2023-2024 Nordix Foundation
* Modifications Copyright (C) 2023 TechMahindra Ltd
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the 'License');
@@ -19,7 +19,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.cps
import java.time.OffsetDateTime
import org.onap.cps.api.CpsQueryService
@@ -33,7 +33,7 @@ import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
import static org.onap.cps.spi.PaginationOption.NO_PAGINATION
-class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
+class QueryServiceIntegrationSpec extends FunctionalSpecBase {
CpsQueryService objectUnderTest
@@ -382,7 +382,7 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
def result = objectUnderTest.queryDataNodesAcrossAnchors(FUNCTIONAL_TEST_DATASPACE_1, '/bookstore', OMIT_DESCENDANTS, new PaginationOption(pageIndex, pageSize))
then: 'correct bookstore names are queried'
def bookstoreNames = result.collect { it.getLeaves().get('bookstore-name') }
- assert bookstoreNames.toList() == expectedBookstoreNames
+ assert bookstoreNames.toSet() == expectedBookstoreNames.toSet()
and: 'the correct number of page size is returned'
assert result.size() == expectedPageSize
and: 'the queried nodes have expected anchor names'
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/SessionManagerIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy
index e0a2602b23..ad153d6a4a 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/SessionManagerIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy
@@ -18,11 +18,11 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.cps
import org.onap.cps.integration.base.FunctionalSpecBase
+import org.onap.cps.ri.utils.SessionManager
import org.onap.cps.spi.exceptions.SessionManagerException
-import org.onap.cps.spi.utils.SessionManager
class SessionManagerIntegrationSpec extends FunctionalSpecBase {
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy
new file mode 100644
index 0000000000..222b3c0f6f
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy
@@ -0,0 +1,54 @@
+/*
+ * ============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.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+
+class AlternateIdSpec extends CpsIntegrationSpecBase {
+
+ def setup() {
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'alternateId')
+ }
+
+ def cleanup() {
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ }
+
+ def 'AlternateId in pass-through data operations should return OK status.'() {
+ given: 'the URL for the pass-through data request'
+ def url = '/ncmp/v1/ch/alternateId/data/ds/ncmp-datastore:passthrough-running'
+ when: 'a pass-through data request is sent to NCMP'
+ def response = mvc.perform(get(url)
+ .queryParam('resourceIdentifier', 'my-resource-id')
+ .contentType(MediaType.APPLICATION_JSON))
+ .andReturn().response
+ then: 'response status is Ok'
+ assert response.status == HttpStatus.OK.value()
+ }
+
+
+
+}
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/ncmp/BearerTokenPassthroughSpec.groovy
index 2a35313f74..a81058fbd6 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/ncmp/BearerTokenPassthroughSpec.groovy
@@ -18,15 +18,10 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.ncmp
-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
@@ -38,31 +33,18 @@ import static org.springframework.http.HttpMethod.PUT
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
-class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
-
- def lastAuthHeaderReceived = null
+class BearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
def setup() {
- dmiDispatcher.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
- registerCmHandle(DMI_URL, 'ch-1', NO_MODULE_SET_TAG)
-
- 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())
- }
- }
- })
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-2'] = ['M1', 'M3']
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'alt-1')
+ registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, 'alt-2')
}
def cleanup() {
- deregisterCmHandle(DMI_URL, 'ch-1')
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ deregisterCmHandle(DMI1_URL, 'ch-2')
}
def 'Bearer token is passed from NCMP to DMI in pass-through data operations.'() {
@@ -75,7 +57,7 @@ class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
.andExpect(status().is2xxSuccessful())
then: 'DMI has received request with bearer token'
- lastAuthHeaderReceived == 'Bearer some-bearer-token'
+ assert dmiDispatcher1.lastAuthHeaderReceived == 'Bearer some-bearer-token'
where: 'all HTTP operations are applied'
httpMethod << [GET, POST, PUT, PATCH, DELETE]
@@ -91,7 +73,7 @@ class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
.andExpect(status().is2xxSuccessful())
then: 'DMI has received request with no authorization header'
- lastAuthHeaderReceived == null
+ assert dmiDispatcher1.lastAuthHeaderReceived == null
where: 'all HTTP operations are applied'
httpMethod << [GET, POST, PUT, PATCH, DELETE]
@@ -104,7 +86,7 @@ class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
"operationId": "operational-1",
"datastore": "ncmp-datastore:passthrough-running",
"resourceIdentifier": "my-resource-id",
- "targetIds": ["ch-1"]
+ "targetIds": ["ch-1","alt-2"]
}]}"""
mvc.perform(request(POST, '/ncmp/v1/data')
.queryParam('topic', 'my-topic')
@@ -115,7 +97,7 @@ class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
then: 'DMI will receive the async request with bearer token'
new PollingConditions().within(3, () -> {
- assert lastAuthHeaderReceived == 'Bearer some-bearer-token'
+ assert dmiDispatcher1.lastAuthHeaderReceived == 'Bearer some-bearer-token'
})
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy
new file mode 100644
index 0000000000..ffcba025e8
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy
@@ -0,0 +1,248 @@
+/*
+ * ============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.ncmp
+
+import org.apache.kafka.clients.consumer.KafkaConsumer
+import org.apache.kafka.common.serialization.StringDeserializer
+import org.onap.cps.integration.KafkaTestContainer
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.NcmpResponseStatus
+import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade
+import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse
+import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
+import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
+import org.onap.cps.ncmp.events.lcm.v1.LcmEvent
+import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
+import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory
+import spock.util.concurrent.PollingConditions
+
+import java.time.Duration
+
+class CmHandleCreateSpec extends CpsIntegrationSpecBase {
+
+ NetworkCmProxyInventoryFacade objectUnderTest
+ def uniqueId = 'ch-unique-id-for-create-test'
+
+ static KafkaConsumer kafkaConsumer
+
+ def setup() {
+ objectUnderTest = networkCmProxyInventoryFacade
+ subscribeAndClearPreviousMessages()
+ }
+
+ def cleanupSpec() {
+ kafkaConsumer.unsubscribe()
+ kafkaConsumer.close()
+ }
+
+ def 'CM Handle registration.'() {
+ given: 'DMI will return modules when requested'
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+ dmiDispatcher1.moduleNamesPerCmHandleId[uniqueId] = ['M1', 'M2']
+
+ when: 'a CM-handle is registered for creation'
+ def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: uniqueId)
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: [cmHandleToCreate])
+ def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistration(dmiPluginRegistration)
+
+ then: 'registration gives successful response'
+ assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(uniqueId)]
+
+ and: 'CM-handle is initially in ADVISED state'
+ assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState(uniqueId).cmHandleState
+
+ then: 'the module sync watchdog is triggered'
+ moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
+
+ then: 'CM-handle goes to READY state after module sync'
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
+ assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(uniqueId).cmHandleState
+ })
+
+ and: 'the CM-handle has expected modules'
+ assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(uniqueId).moduleName.sort()
+
+ then: 'get the latest messages'
+ def consumerRecords = getLatestConsumerRecords()
+
+ and: 'both converted messages are for the correct cm handle'
+ def notificationMessages = []
+ for (def consumerRecord : consumerRecords) {
+ notificationMessages.add(jsonObjectMapper.convertJsonString(consumerRecord.value().toString(), LcmEvent))
+ }
+ assert notificationMessages.event.cmHandleId == [ uniqueId, uniqueId ]
+
+ and: 'the oldest event is about the update to ADVISED state'
+ notificationMessages[0].event.newValues.cmHandleState.value() == 'ADVISED'
+
+ and: 'the next event is about update to READY state'
+ notificationMessages[1].event.newValues.cmHandleState.value() == 'READY'
+
+ cleanup: 'deregister CM handle'
+ deregisterCmHandle(DMI1_URL, uniqueId)
+ }
+
+ def 'CM Handle goes to LOCKED state when DMI gives error during module sync.'() {
+ given: 'DMI is not available to handle requests'
+ dmiDispatcher1.isAvailable = false
+
+ when: 'a CM-handle is registered for creation'
+ def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: [cmHandleToCreate])
+ objectUnderTest.updateDmiRegistration(dmiPluginRegistration)
+
+ and: 'the module sync watchdog is triggered'
+ moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
+
+ then: 'CM-handle goes to LOCKED state with reason MODULE_SYNC_FAILED'
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
+ def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState('ch-1')
+ assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED
+ assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_SYNC_FAILED
+ })
+
+ and: 'CM-handle has no modules'
+ assert objectUnderTest.getYangResourcesModuleReferences('ch-1').empty
+
+ cleanup: 'deregister CM handle'
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ }
+
+ def 'Create a CM-handle with existing moduleSetTag.'() {
+ given: 'DMI will return modules when requested'
+ dmiDispatcher1.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(DMI1_URL, 'ch-1', 'A')
+ registerCmHandle(DMI1_URL, 'ch-2', 'B')
+
+ when: 'a CM-handle is registered for creation with moduleSetTag "B"'
+ def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B')
+ objectUnderTest.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: [cmHandleToCreate]))
+
+ and: 'the module sync watchdog is triggered'
+ moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
+
+ then: 'the CM-handle goes to READY state'
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
+ assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-3').cmHandleState
+ })
+
+ and: 'the CM-handle has expected moduleSetTag'
+ assert objectUnderTest.getNcmpServiceCmHandle('ch-3').moduleSetTag == 'B'
+
+ and: 'the CM-handle has expected modules from module set "B": M1 and M3'
+ assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort()
+
+ cleanup: 'deregister CM handles'
+ deregisterCmHandles(DMI1_URL, ['ch-1', 'ch-2', 'ch-3'])
+ }
+
+ def 'Create CM-handles with alternate IDs.'() {
+ given: 'DMI will return modules for all CM-handles when requested'
+ dmiDispatcher1.moduleNamesPerCmHandleId = (1..7).collectEntries{ ['ch-'+it, ['M1']] }
+ and: 'an existing CM-handle with an alternate ID'
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'existing-alt-id')
+ and: 'an existing CM-handle with no alternate ID'
+ registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, NO_ALTERNATE_ID)
+
+ when: 'a batch of CM-handles is registered for creation with various alternate IDs'
+ def cmHandlesToCreate = [
+ new NcmpServiceCmHandle(cmHandleId: 'ch-3', alternateId: NO_ALTERNATE_ID),
+ new NcmpServiceCmHandle(cmHandleId: 'ch-4', alternateId: 'unique-alt-id'),
+ new NcmpServiceCmHandle(cmHandleId: 'ch-5', alternateId: 'existing-alt-id'),
+ new NcmpServiceCmHandle(cmHandleId: 'ch-6', alternateId: 'duplicate-alt-id'),
+ new NcmpServiceCmHandle(cmHandleId: 'ch-7', alternateId: 'duplicate-alt-id'),
+ ]
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: cmHandlesToCreate)
+ def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistration(dmiPluginRegistration)
+
+ then: 'registration gives expected responses'
+ assert dmiPluginRegistrationResponse.createdCmHandles.sort { it.cmHandle } == [
+ CmHandleRegistrationResponse.createSuccessResponse('ch-3'),
+ CmHandleRegistrationResponse.createSuccessResponse('ch-4'),
+ CmHandleRegistrationResponse.createFailureResponse('ch-5', NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED),
+ CmHandleRegistrationResponse.createSuccessResponse('ch-6'),
+ CmHandleRegistrationResponse.createFailureResponse('ch-7', NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED),
+ ]
+
+ cleanup: 'deregister CM handles'
+ deregisterCmHandles(DMI1_URL, (1..7).collect{ 'ch-'+it })
+ }
+
+ def 'CM Handle retry after failed module sync.'() {
+ given: 'DMI is not initially available to handle requests'
+ dmiDispatcher1.isAvailable = false
+
+ when: 'CM-handles are registered for creation'
+ def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1'), new NcmpServiceCmHandle(cmHandleId: 'ch-2')]
+ def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: cmHandlesToCreate)
+ objectUnderTest.updateDmiRegistration(dmiPluginRegistration)
+
+ and: 'the module sync watchdog is triggered'
+ moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
+
+ then: 'CM-handles go to LOCKED state'
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
+ assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.LOCKED
+ })
+
+ when: 'DMI is available for retry'
+ dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2'], 'ch-2': ['M1', 'M2']]
+ dmiDispatcher1.isAvailable = true
+
+ and: 'the module sync watchdog is triggered TWICE'
+ 2.times { moduleSyncWatchdog.moduleSyncAdvisedCmHandles() }
+
+ then: 'Both CM-handles go to READY state'
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
+ ['ch-1', 'ch-2'].each { cmHandleId ->
+ assert objectUnderTest.getCmHandleCompositeState(cmHandleId).cmHandleState == CmHandleState.READY
+ }
+ })
+
+ and: 'Both CM-handles have expected modules'
+ ['ch-1', 'ch-2'].each { cmHandleId ->
+ assert objectUnderTest.getYangResourcesModuleReferences(cmHandleId).moduleName.sort() == ['M1', 'M2']
+ }
+
+ cleanup: 'deregister CM handles'
+ deregisterCmHandles(DMI1_URL, ['ch-1', 'ch-2'])
+ }
+
+ def subscribeAndClearPreviousMessages() {
+ kafkaConsumer = KafkaTestContainer.getConsumer('test-group', StringDeserializer.class)
+ kafkaConsumer.subscribe(['ncmp-events'])
+ kafkaConsumer.poll(Duration.ofMillis(500))
+ }
+
+ def getLatestConsumerRecords() {
+ def consumerRecords = []
+ def retryAttempts = 10
+ while (consumerRecords.size() < 2) {
+ retryAttempts--
+ consumerRecords.addAll(kafkaConsumer.poll(Duration.ofMillis(100)))
+ if (retryAttempts == 0)
+ break
+ }
+ return consumerRecords
+ }
+
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpdateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpdateSpec.groovy
new file mode 100644
index 0000000000..67011f811b
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleUpdateSpec.groovy
@@ -0,0 +1,89 @@
+/*
+ * ============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.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.NcmpResponseStatus
+import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade
+import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse
+import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
+import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
+
+class CmHandleUpdateSpec extends CpsIntegrationSpecBase {
+
+ NetworkCmProxyInventoryFacade objectUnderTest
+
+ def setup() {
+ objectUnderTest = networkCmProxyInventoryFacade
+ }
+
+ def 'Update of CM-handle with new or unchanged alternate ID succeeds.'() {
+ given: 'DMI will return modules when requested'
+ dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2']]
+ and: "existing CM-handle with alternate ID: $oldAlternateId"
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, oldAlternateId)
+
+ when: "CM-handle is registered for update with new alternate ID: $newAlternateId"
+ def cmHandleToUpdate = new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: newAlternateId)
+ def dmiPluginRegistrationResponse =
+ objectUnderTest.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: DMI1_URL, updatedCmHandles: [cmHandleToUpdate]))
+
+ then: 'registration gives successful response'
+ assert dmiPluginRegistrationResponse.updatedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse('ch-1')]
+
+ and: 'the CM-handle has expected alternate ID'
+ assert objectUnderTest.getNcmpServiceCmHandle('ch-1').alternateId == expectedAlternateId
+
+ cleanup: 'deregister CM handles'
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+
+ where:
+ oldAlternateId | newAlternateId || expectedAlternateId
+ '' | '' || ''
+ '' | 'new' || 'new'
+ 'old' | 'old' || 'old'
+ 'old' | null || 'old'
+ 'old' | '' || 'old'
+ 'old' | ' ' || 'old'
+ }
+
+ def 'Update of CM-handle with previously set alternate ID fails.'() {
+ given: 'DMI will return modules when requested'
+ dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2']]
+ and: 'existing CM-handle with alternate ID'
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'original')
+
+ when: 'a CM-handle is registered for update with new alternate ID'
+ def cmHandleToUpdate = new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: 'new')
+ def dmiPluginRegistrationResponse =
+ objectUnderTest.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: DMI1_URL, updatedCmHandles: [cmHandleToUpdate]))
+
+ then: 'registration gives failure response, due to alternate ID being already associated'
+ assert dmiPluginRegistrationResponse.updatedCmHandles == [CmHandleRegistrationResponse.createFailureResponse('ch-1', NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED)]
+
+ and: 'the CM-handle still has the old alternate ID'
+ assert objectUnderTest.getNcmpServiceCmHandle('ch-1').alternateId == 'original'
+
+ cleanup: 'deregister CM handles'
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ }
+
+}
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/ncmp/CmHandleUpgradeSpec.groovy
index 72e798335a..a5e3daf289 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/ncmp/CmHandleUpgradeSpec.groovy
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.ncmp
import org.onap.cps.integration.base.CpsIntegrationSpecBase
import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade
@@ -29,58 +29,59 @@ import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory
import spock.util.concurrent.PollingConditions
-class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase {
+class CmHandleUpgradeSpec extends CpsIntegrationSpecBase {
NetworkCmProxyInventoryFacade objectUnderTest
- static final CM_HANDLE_ID = 'ch-1'
- static final CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG = 'ch-2'
+ def cmHandleId = 'ch-1'
+ def cmHandleIdWithExistingModuleSetTag = 'ch-2'
def setup() {
objectUnderTest = networkCmProxyInventoryFacade
+ moduleSyncService.clearPrivateModuleSetCache()
}
def 'Upgrade CM-handle with new moduleSetTag or no moduleSetTag.'() {
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()
+ dmiDispatcher1.moduleNamesPerCmHandleId[cmHandleId] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, cmHandleId, initialModuleSetTag)
+ assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(cmHandleId).moduleName.sort()
when: "the CM-handle is upgraded with given moduleSetTag '${updatedModuleSetTag}'"
- def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: updatedModuleSetTag)
- def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule(
- new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade))
+ def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [cmHandleId], moduleSetTag: updatedModuleSetTag)
+ def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistration(
+ new DmiPluginRegistration(dmiPlugin: DMI1_URL, upgradedCmHandles: cmHandlesToUpgrade))
then: 'registration gives successful response'
- assert dmiPluginRegistrationResponse.upgradedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(CM_HANDLE_ID)]
+ assert dmiPluginRegistrationResponse.upgradedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)]
and: 'CM-handle is in LOCKED state due to MODULE_UPGRADE'
- def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID)
+ def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState(cmHandleId)
assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED
assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_UPGRADE
assert cmHandleCompositeState.lockReason.details == "Upgrade to ModuleSetTag: ${updatedModuleSetTag}"
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()
+ dmiDispatcher1.moduleNamesPerCmHandleId[cmHandleId] = ['M1', 'M3']
+
+ and: 'the module sync watchdog is triggered twice'
+ 2.times { moduleSyncWatchdog.moduleSyncAdvisedCmHandles() }
then: 'CM-handle goes to READY state'
- new PollingConditions().eventually {
- assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState
- }
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
+ assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(cmHandleId).cmHandleState
+ })
and: 'the CM-handle has expected moduleSetTag'
- assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == updatedModuleSetTag
+ assert objectUnderTest.getNcmpServiceCmHandle(cmHandleId).moduleSetTag == updatedModuleSetTag
and: 'CM-handle has expected updated modules: M1 and M3'
- assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
+ assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(cmHandleId).moduleName.sort()
cleanup: 'deregister CM-handle'
- deregisterCmHandle(DMI_URL, CM_HANDLE_ID)
+ deregisterCmHandle(DMI1_URL, cmHandleId)
- where:
+ where: 'following module set tags are used'
initialModuleSetTag | updatedModuleSetTag
NO_MODULE_SET_TAG | NO_MODULE_SET_TAG
NO_MODULE_SET_TAG | 'new'
@@ -90,40 +91,39 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase {
def 'Upgrade CM-handle with existing moduleSetTag.'() {
given: 'DMI will return modules for registration'
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG] = ['M1', 'M3']
+ dmiDispatcher1.moduleNamesPerCmHandleId[cmHandleId] = ['M1', 'M2']
+ dmiDispatcher1.moduleNamesPerCmHandleId[cmHandleIdWithExistingModuleSetTag] = ['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()
+ registerCmHandle(DMI1_URL, cmHandleIdWithExistingModuleSetTag, updatedModuleSetTag)
+ assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(cmHandleIdWithExistingModuleSetTag).moduleName.sort()
and: "a CM-handle with moduleSetTag '${initialModuleSetTag}' which will be upgraded"
- registerCmHandle(DMI_URL, CM_HANDLE_ID, initialModuleSetTag)
- assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
+ registerCmHandle(DMI1_URL, cmHandleId, initialModuleSetTag)
+ assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(cmHandleId).moduleName.sort()
when: "CM-handle is upgraded to moduleSetTag '${updatedModuleSetTag}'"
- def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: updatedModuleSetTag)
- def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule(
- new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade))
+ def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [cmHandleId], moduleSetTag: updatedModuleSetTag)
+ def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistration(
+ new DmiPluginRegistration(dmiPlugin: DMI1_URL, upgradedCmHandles: cmHandlesToUpgrade))
then: 'registration gives successful response'
- assert dmiPluginRegistrationResponse.upgradedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(CM_HANDLE_ID)]
+ assert dmiPluginRegistrationResponse.upgradedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)]
- when: 'module sync runs'
- moduleSyncWatchdog.resetPreviouslyFailedCmHandles()
- moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
+ and: 'the module sync watchdog is triggered twice'
+ 2.times { moduleSyncWatchdog.moduleSyncAdvisedCmHandles() }
- then: 'CM-handle goes to READY state'
- new PollingConditions().eventually {
- assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState
- }
+ and: 'CM-handle goes to READY state'
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
+ assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(cmHandleId).cmHandleState
+ })
and: 'the CM-handle has expected moduleSetTag'
- assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == updatedModuleSetTag
+ assert objectUnderTest.getNcmpServiceCmHandle(cmHandleId).moduleSetTag == updatedModuleSetTag
and: 'CM-handle has expected updated modules: M1 and M3'
- assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
+ assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(cmHandleId).moduleName.sort()
cleanup: 'deregister CM-handle'
- deregisterCmHandles(DMI_URL, [CM_HANDLE_ID, CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG])
+ deregisterCmHandles(DMI1_URL, [cmHandleId, cmHandleIdWithExistingModuleSetTag])
where:
initialModuleSetTag | updatedModuleSetTag
@@ -133,56 +133,55 @@ 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'
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
- registerCmHandle(DMI_URL, CM_HANDLE_ID, 'same')
- assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
+ dmiDispatcher1.moduleNamesPerCmHandleId[cmHandleId] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, cmHandleId, 'same')
+ assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(cmHandleId).moduleName.sort()
when: 'CM-handle is upgraded with the same moduleSetTag'
- def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'same')
- objectUnderTest.updateDmiRegistrationAndSyncModule(
- new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade))
+ def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [cmHandleId], moduleSetTag: 'same')
+ objectUnderTest.updateDmiRegistration(
+ new DmiPluginRegistration(dmiPlugin: DMI1_URL, upgradedCmHandles: cmHandlesToUpgrade))
then: 'CM-handle remains in READY state'
- assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState
+ assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(cmHandleId).cmHandleState
and: 'the CM-handle has same moduleSetTag as before'
- assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == 'same'
+ assert objectUnderTest.getNcmpServiceCmHandle(cmHandleId).moduleSetTag == 'same'
then: 'CM-handle has same modules as before: M1 and M2'
- assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
+ assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(cmHandleId).moduleName.sort()
cleanup: 'deregister CM-handle'
- deregisterCmHandle(DMI_URL, CM_HANDLE_ID)
+ deregisterCmHandle(DMI1_URL, cmHandleId)
}
def 'Upgrade of CM-handle fails due to DMI error.'() {
given: 'a CM-handle exists'
- dmiDispatcher.moduleNamesPerCmHandleId[CM_HANDLE_ID] = ['M1', 'M2']
- registerCmHandle(DMI_URL, CM_HANDLE_ID, 'oldTag')
+ dmiDispatcher1.moduleNamesPerCmHandleId[cmHandleId] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, cmHandleId, 'oldTag')
and: 'DMI is not available for upgrade'
- dmiDispatcher.isAvailable = false
+ dmiDispatcher1.isAvailable = false
when: 'the CM-handle is upgraded'
- def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'newTag')
- objectUnderTest.updateDmiRegistrationAndSyncModule(
- new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade))
+ def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [cmHandleId], moduleSetTag: 'newTag')
+ objectUnderTest.updateDmiRegistration(
+ new DmiPluginRegistration(dmiPlugin: DMI1_URL, upgradedCmHandles: cmHandlesToUpgrade))
- and: 'module sync runs'
- moduleSyncWatchdog.resetPreviouslyFailedCmHandles()
- moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
+ and: 'the module sync watchdog is triggered twice'
+ 2.times { moduleSyncWatchdog.moduleSyncAdvisedCmHandles() }
then: 'CM-handle goes to LOCKED state with reason MODULE_UPGRADE_FAILED'
- new PollingConditions(timeout: 3).eventually {
- def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID)
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
+ def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState(cmHandleId)
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'
+ assert objectUnderTest.getNcmpServiceCmHandle(cmHandleId).moduleSetTag == 'oldTag'
cleanup: 'deregister CM-handle'
- deregisterCmHandle(DMI_URL, CM_HANDLE_ID)
+ deregisterCmHandle(DMI1_URL, cmHandleId)
}
}
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/ncmp/CmNotificationSubscriptionSpec.groovy
index c2768f1942..a5f7d08c58 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/ncmp/CmNotificationSubscriptionSpec.groovy
@@ -18,34 +18,33 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.ncmp
import org.onap.cps.integration.base.CpsIntegrationSpecBase
-import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceService
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmSubscriptionPersistenceService
import org.springframework.beans.factory.annotation.Autowired
import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_RUNNING
-class NcmpCmNotificationSubscriptionSpec extends CpsIntegrationSpecBase {
+class CmNotificationSubscriptionSpec extends CpsIntegrationSpecBase {
@Autowired
- CmNotificationSubscriptionPersistenceService cmNotificationSubscriptionPersistenceService;
+ CmSubscriptionPersistenceService cmSubscriptionPersistenceService
def 'Adding a new cm notification subscription'() {
given: 'there is no ongoing cm subscription for the following'
def datastoreType = PASSTHROUGH_RUNNING
def cmHandleId = 'ch-1'
def xpath = '/x/y'
- assert cmNotificationSubscriptionPersistenceService.
- getOngoingCmNotificationSubscriptionIds(datastoreType,cmHandleId,xpath).size() == 0
+ assert cmSubscriptionPersistenceService.
+ getOngoingCmSubscriptionIds(datastoreType, cmHandleId, xpath).size() == 0
when: 'we add a new cm notification subscription'
- cmNotificationSubscriptionPersistenceService.addCmNotificationSubscription(datastoreType,cmHandleId,xpath,
+ cmSubscriptionPersistenceService.addCmSubscription(datastoreType, cmHandleId, xpath,
'subId-1')
then: 'there is an ongoing cm subscription for that CM handle and xpath'
- assert cmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(datastoreType,cmHandleId,xpath)
+ assert cmSubscriptionPersistenceService.isOngoingCmSubscription(datastoreType, cmHandleId, xpath)
and: 'only one subscription id is related to now ongoing cm subscription'
- assert cmNotificationSubscriptionPersistenceService.
- getOngoingCmNotificationSubscriptionIds(datastoreType,cmHandleId,xpath).size() == 1
+ assert cmSubscriptionPersistenceService.getOngoingCmSubscriptionIds(datastoreType, cmHandleId, xpath).size() == 1
}
def 'Adding a cm notification subscription to the already existing cm handle but non existing xpath'() {
@@ -53,18 +52,17 @@ class NcmpCmNotificationSubscriptionSpec extends CpsIntegrationSpecBase {
def datastoreType = PASSTHROUGH_RUNNING
def cmHandleId = 'ch-1'
def existingXpath = '/x/y'
- assert cmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(datastoreType,cmHandleId,existingXpath)
+ assert cmSubscriptionPersistenceService.isOngoingCmSubscription(datastoreType, cmHandleId, existingXpath)
and: 'a non existing cm subscription with same datastore name and cm handle but different xpath'
def nonExistingXpath = '/x2/y2'
- assert !cmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(datastoreType,cmHandleId,nonExistingXpath)
+ assert !cmSubscriptionPersistenceService.isOngoingCmSubscription(datastoreType, cmHandleId, nonExistingXpath)
when: 'a new cm notification subscription is made for the existing cm handle and non existing xpath'
- cmNotificationSubscriptionPersistenceService.addCmNotificationSubscription(datastoreType,cmHandleId, nonExistingXpath,
+ cmSubscriptionPersistenceService.addCmSubscription(datastoreType, cmHandleId, nonExistingXpath,
'subId-2')
then: 'there is an ongoing cm subscription for that CM handle and xpath'
- assert cmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(datastoreType,cmHandleId,nonExistingXpath)
+ assert cmSubscriptionPersistenceService.isOngoingCmSubscription(datastoreType, cmHandleId, nonExistingXpath)
and: 'only one subscription id is related to now ongoing cm subscription'
- assert cmNotificationSubscriptionPersistenceService.
- getOngoingCmNotificationSubscriptionIds(datastoreType,cmHandleId,nonExistingXpath).size() == 1
+ assert cmSubscriptionPersistenceService.getOngoingCmSubscriptionIds(datastoreType, cmHandleId, nonExistingXpath).size() == 1
}
def 'Adding a cm notification subscription to the already existing cm handle and xpath'() {
@@ -73,10 +71,10 @@ class NcmpCmNotificationSubscriptionSpec extends CpsIntegrationSpecBase {
def cmHandleId = 'ch-1'
def xpath = '/x/y'
when: 'a new cm notification subscription is made for the SAME CM handle and xpath'
- cmNotificationSubscriptionPersistenceService.addCmNotificationSubscription(datastoreType,cmHandleId,xpath,
+ cmSubscriptionPersistenceService.addCmSubscription(datastoreType, cmHandleId, xpath,
'subId-3')
then: 'it is added to the ongoing list of subscription ids'
- def subscriptionIds = cmNotificationSubscriptionPersistenceService.getOngoingCmNotificationSubscriptionIds(datastoreType,cmHandleId,xpath)
+ def subscriptionIds = cmSubscriptionPersistenceService.getOngoingCmSubscriptionIds(datastoreType, cmHandleId, xpath)
assert subscriptionIds.size() == 2
and: 'both subscription ids exists for the CM handle and xpath'
assert subscriptionIds.contains("subId-1") && subscriptionIds.contains("subId-3")
@@ -89,13 +87,12 @@ class NcmpCmNotificationSubscriptionSpec extends CpsIntegrationSpecBase {
def xpath = '/x/y'
and: 'the number of subscribers is as follows'
def originalNumberOfSubscribers =
- cmNotificationSubscriptionPersistenceService.getOngoingCmNotificationSubscriptionIds(datastoreType,cmHandleId,xpath).size()
+ cmSubscriptionPersistenceService.getOngoingCmSubscriptionIds(datastoreType, cmHandleId, xpath).size()
when: 'a subscriber is removed'
- cmNotificationSubscriptionPersistenceService.removeCmNotificationSubscription(datastoreType,cmHandleId,xpath,'subId-3')
+ cmSubscriptionPersistenceService.removeCmSubscription(datastoreType, cmHandleId, xpath, 'subId-3')
then: 'the number of subscribers is reduced by 1'
- def updatedNumberOfSubscribers =
- cmNotificationSubscriptionPersistenceService.getOngoingCmNotificationSubscriptionIds(datastoreType,cmHandleId,xpath).size()
- assert updatedNumberOfSubscribers == originalNumberOfSubscribers-1
+ def updatedNumberOfSubscribers = cmSubscriptionPersistenceService.getOngoingCmSubscriptionIds(datastoreType, cmHandleId, xpath).size()
+ assert updatedNumberOfSubscribers == originalNumberOfSubscribers - 1
}
def 'Removing the LAST cm notification subscriber for a given cm handle, datastore and xpath'() {
@@ -104,12 +101,12 @@ class NcmpCmNotificationSubscriptionSpec extends CpsIntegrationSpecBase {
def cmHandleId = 'ch-1'
def xpath = '/x/y'
and: 'there is only one subscriber'
- assert cmNotificationSubscriptionPersistenceService
- .getOngoingCmNotificationSubscriptionIds(datastoreType,cmHandleId,xpath).size() == 1
+ assert cmSubscriptionPersistenceService
+ .getOngoingCmSubscriptionIds(datastoreType, cmHandleId, xpath).size() == 1
when: 'only subscriber is removed'
- cmNotificationSubscriptionPersistenceService.removeCmNotificationSubscription(datastoreType,cmHandleId,xpath,'subId-1')
+ cmSubscriptionPersistenceService.removeCmSubscription(datastoreType, cmHandleId, xpath, 'subId-1')
then: 'there are no longer any subscriptions for the cm handle, datastore and xpath'
- assert !cmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(datastoreType, cmHandleId, xpath)
+ assert !cmSubscriptionPersistenceService.isOngoingCmSubscription(datastoreType, cmHandleId, xpath)
}
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobResultServiceSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobResultServiceSpec.groovy
new file mode 100644
index 0000000000..4d04eeeb81
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobResultServiceSpec.groovy
@@ -0,0 +1,44 @@
+/*
+ * ============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.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.datajobs.DataJobResultService
+import org.springframework.beans.factory.annotation.Autowired
+
+class DataJobResultServiceSpec extends CpsIntegrationSpecBase {
+
+ @Autowired
+ DataJobResultService dataJobResultService;
+
+ def 'Get the status of a data job from DMI.'() {
+ given: 'the required data about the data job'
+ def authorization = 'my authorization header'
+ def dmiServiceName = DMI1_URL
+ def dataProducerId = 'some-data-producer-id'
+ def dataProducerJobId = 'some-data-producer-job-id'
+ def destination = 'some-destination'
+ when: 'the data job status checked'
+ def result = dataJobResultService.getDataJobResult(authorization, dmiServiceName, dataProducerId, dataProducerJobId, destination)
+ then: 'the status is that defined in the mock service.'
+ assert result == '{ "result": "some result"}'
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy
new file mode 100644
index 0000000000..6e5c0e40c2
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy
@@ -0,0 +1,23 @@
+package org.onap.cps.integration.functional.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.datajobs.DataJobStatusService
+import org.springframework.beans.factory.annotation.Autowired
+
+class DataJobStatusServiceSpec extends CpsIntegrationSpecBase {
+
+ @Autowired
+ DataJobStatusService dataJobStatusService
+
+ def 'Get the status of a data job from DMI.'() {
+ given: 'the required data about the data job'
+ def dmiServiceName = DMI1_URL
+ def dataProducerId = 'some-data-producer-id'
+ def dataProducerJobId = 'some-data-producer-job-id'
+ def authorization = 'my authorization header'
+ when: 'the data job status checked'
+ def result = dataJobStatusService.getDataJobStatus(authorization, dmiServiceName, dataProducerId, dataProducerJobId)
+ then: 'the status is that defined in the mock service.'
+ assert result == 'status details from mock service'
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy
new file mode 100644
index 0000000000..4e9b809eff
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy
@@ -0,0 +1,65 @@
+/*
+ * ============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.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.springframework.http.MediaType
+
+import static org.springframework.http.HttpMethod.DELETE
+import static org.springframework.http.HttpMethod.GET
+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.servlet.request.MockMvcRequestBuilders.request
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
+
+class DmiUrlEncodingPassthroughSpec extends CpsIntegrationSpecBase {
+
+ def setup() {
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG)
+ }
+
+ def cleanup() {
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ }
+
+ def 'DMI URL encoding for pass-through operational data operations with GET request'() {
+ when: 'sending a GET pass-through data request to NCMP'
+ mvc.perform(request(GET, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-operational')
+ .queryParam('resourceIdentifier', 'parent/child')
+ .queryParam('options', '(a=1,b=2)'))
+ .andExpect(status().is2xxSuccessful())
+ then: 'verify that DMI received the request with the correctly encoded URL'
+ assert dmiDispatcher1.dmiResourceDataUrl == '/dmi/v1/ch/ch-1/data/ds/ncmp-datastore%3Apassthrough-operational?resourceIdentifier=parent%2Fchild&options=%28a%3D1%2Cb%3D2%29'
+ }
+
+ def 'DMI URL encoding for pass-through running data operations with POST request'() {
+ when: 'sending a pass-through data request to NCMP with various HTTP methods'
+ mvc.perform(request(POST, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running')
+ .queryParam('resourceIdentifier', 'parent/child')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content('{ "some-json": "data" }'))
+ .andExpect(status().is2xxSuccessful())
+ then: 'verify that DMI received the request with the correctly encoded URL'
+ assert dmiDispatcher1.dmiResourceDataUrl == '/dmi/v1/ch/ch-1/data/ds/ncmp-datastore%3Apassthrough-running?resourceIdentifier=parent%2Fchild'
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy
new file mode 100644
index 0000000000..963bc1fe61
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy
@@ -0,0 +1,156 @@
+/*
+ * ============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.ncmp
+
+import io.micrometer.core.instrument.MeterRegistry
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncWatchdog
+import org.springframework.beans.factory.annotation.Autowired
+import spock.util.concurrent.PollingConditions
+
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+
+class ModuleSyncWatchdogIntegrationSpec extends CpsIntegrationSpecBase {
+
+ ModuleSyncWatchdog objectUnderTest
+
+ @Autowired
+ MeterRegistry meterRegistry
+
+ def executorService = Executors.newFixedThreadPool(2)
+ def PARALLEL_SYNC_SAMPLE_SIZE = 100
+
+ def setup() {
+ objectUnderTest = moduleSyncWatchdog
+ }
+
+ def cleanup() {
+ try {
+ deregisterSequenceOfCmHandles(DMI1_URL, PARALLEL_SYNC_SAMPLE_SIZE, 1)
+ moduleSyncWorkQueue.clear()
+ } finally {
+ executorService.shutdownNow()
+ }
+ }
+
+ def 'Watchdog is disabled for test.'() {
+ given:
+ registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(DMI1_URL, NO_MODULE_SET_TAG, PARALLEL_SYNC_SAMPLE_SIZE, 1)
+ when: 'wait a while but less then the initial delay of 10 minutes'
+ Thread.sleep(3000)
+ then: 'the work queue remains empty'
+ assert moduleSyncWorkQueue.isEmpty()
+ }
+
+ def 'CPS-2478 Highlight module sync inefficiencies.'() {
+ given: 'register 250 cm handles with module set tag cps-2478-A'
+ def numberOfTags = 2
+ def cmHandlesPerTag = 250
+ def totalCmHandles = numberOfTags * cmHandlesPerTag
+ def offset = 1
+ registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(DMI1_URL, 'cps-2478-A', cmHandlesPerTag, offset)
+ and: 'register anther 250 cm handles with module set tag cps-2478-B'
+ offset += cmHandlesPerTag
+ registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(DMI1_URL, 'cps-2478-B', cmHandlesPerTag, offset)
+ and: 'clear any previous instrumentation'
+ meterRegistry.clear()
+ when: 'sync all advised cm handles'
+ objectUnderTest.moduleSyncAdvisedCmHandles()
+ Thread.sleep(100)
+ then: 'retry until all schema sets are stored in db (1 schema set for each cm handle)'
+ def dbSchemaSetStorageTimer = meterRegistry.get('cps.module.persistence.schemaset.store').timer()
+ new PollingConditions().within(10, () -> {
+ objectUnderTest.moduleSyncAdvisedCmHandles()
+ Thread.sleep(100)
+ assert dbSchemaSetStorageTimer.count() >= 500
+ })
+ then: 'wait till at least 5 batches of state updates are done (often more because of retries of locked cm handles)'
+ def dbStateUpdateTimer = meterRegistry.get('cps.ncmp.cmhandle.state.update.batch').timer()
+ new PollingConditions().within(10, () -> {
+ assert dbStateUpdateTimer.count() >= 5
+ })
+ and: 'the db has been queried for tags exactly 2 times.'
+ def dbModuleQueriesTimer = meterRegistry.get('cps.module.service.module.reference.query.by.attribute').timer()
+ assert dbModuleQueriesTimer.count() == 2
+ and: 'exactly 2 calls to DMI to get module references'
+ def dmiModuleRetrievalTimer = meterRegistry.get('cps.ncmp.inventory.module.references.from.dmi').timer()
+ assert dmiModuleRetrievalTimer.count() == 2
+ and: 'log the relevant instrumentation'
+ logInstrumentation(dbModuleQueriesTimer, 'query module references')
+ logInstrumentation(dmiModuleRetrievalTimer, 'get modules from DMI ')
+ logInstrumentation(dbSchemaSetStorageTimer, 'store schema sets ')
+ logInstrumentation(dbStateUpdateTimer, 'batch state updates ')
+ cleanup: 'remove all cm handles'
+ deregisterSequenceOfCmHandles(DMI1_URL, totalCmHandles, 1)
+ }
+
+ def 'Populate module sync work queue simultaneously on two parallel threads (CPS-2403).'() {
+ // This test failed before bug https://lf-onap.atlassian.net/browse/CPS-2403 was fixed
+ given: 'the queue is empty at the start'
+ registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(DMI1_URL, NO_MODULE_SET_TAG, PARALLEL_SYNC_SAMPLE_SIZE, 1)
+ assert moduleSyncWorkQueue.isEmpty()
+ when: 'attempt to populate the queue on the main (test) and another parallel thread at the same time'
+ objectUnderTest.populateWorkQueueIfNeeded()
+ executorService.execute(populateQueueWithoutDelay)
+ and: 'wait a little (to give all threads time to complete their task)'
+ Thread.sleep(50)
+ then: 'the queue size is exactly the sample size'
+ assert moduleSyncWorkQueue.size() == PARALLEL_SYNC_SAMPLE_SIZE
+ }
+
+ def 'Populate module sync work queue on two parallel threads with a slight difference in start time.'() {
+ // This test proved that the issue in CPS-2403 did not arise if the the queue was populated and given time to be distributed
+ given: 'the queue is empty at the start'
+ registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(DMI1_URL, NO_MODULE_SET_TAG, PARALLEL_SYNC_SAMPLE_SIZE, 1)
+ assert moduleSyncWorkQueue.isEmpty()
+ when: 'attempt to populate the queue on the main (test) and another parallel thread a little later'
+ objectUnderTest.populateWorkQueueIfNeeded()
+ executorService.execute(populateQueueWithDelay)
+ and: 'wait a little (to give all threads time to complete their task)'
+ Thread.sleep(50)
+ then: 'the queue size is exactly the sample size'
+ assert moduleSyncWorkQueue.size() == PARALLEL_SYNC_SAMPLE_SIZE
+ }
+
+ def logInstrumentation(timer, description) {
+ System.out.println('*** CPS-2478, ' + description + ' : ' + timer.count()+ ' times, total ' + timer.totalTime(TimeUnit.MILLISECONDS) + ' ms')
+ return true
+ }
+
+ def populateQueueWithoutDelay = () -> {
+ try {
+ objectUnderTest.populateWorkQueueIfNeeded()
+ } catch (InterruptedException e) {
+ e.printStackTrace()
+ }
+ }
+
+ def populateQueueWithDelay = () -> {
+ try {
+ Thread.sleep(10)
+ objectUnderTest.populateWorkQueueIfNeeded()
+ } catch (InterruptedException e) {
+ e.printStackTrace()
+ }
+ }
+
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy
new file mode 100644
index 0000000000..f897393a53
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy
@@ -0,0 +1,72 @@
+/*
+ * ============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.ncmp
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.springframework.http.HttpHeaders
+import org.springframework.http.MediaType
+
+import static org.springframework.http.HttpMethod.POST
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request
+
+class PolicyExecutorIntegrationSpec extends CpsIntegrationSpecBase {
+
+ def objectMapper = new ObjectMapper()
+
+ def setup() {
+ // Enable mocked policy executor logic
+ policyDispatcher.allowAll = false;
+ //minimum setup for cm handles with alternate ids
+ dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': [], 'ch-2': [], 'ch-3':[]]
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'fdn1')
+ registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, 'fdn2')
+ registerCmHandle(DMI1_URL, 'ch-3', NO_MODULE_SET_TAG, 'mock slow response')
+ }
+
+ def cleanup() {
+ deregisterSequenceOfCmHandles(DMI1_URL, 3, 1)
+ }
+
+ def 'Policy Executor create request with #scenario.'() {
+ when: 'a pass-through write request is sent to NCMP'
+ def response = mvc.perform(request(POST, "/ncmp/v1/ch/$cmHandle/data/ds/ncmp-datastore:passthrough-running")
+ .queryParam('resourceIdentifier', 'my-resource-id')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content('{ "some-json": "data" }')
+ .header(HttpHeaders.AUTHORIZATION, authorization))
+ .andReturn().response
+ then: 'the expected status code is returned'
+ response.getStatus() == execpectedStatusCode
+ and: 'when not allowed the response body contains the expected message'
+ if (expectedMessage!='allow') {
+ def bodyAsMap = objectMapper.readValue(response.getContentAsByteArray(), Map.class)
+ assert bodyAsMap.get('message').endsWith(expectedMessage)
+ }
+ where: 'following parameters are used'
+ scenario | cmHandle | authorization || execpectedStatusCode || expectedMessage
+ 'accepted cm handle' | 'ch-1' | 'mock expects "ABC"' || 201 || 'allow'
+ 'un-accepted cm handle' | 'ch-2' | 'mock expects "ABC"' || 409 || 'deny from mock server (dispatcher)'
+ 'timeout' | 'ch-3' | 'mock expects "ABC"' || 409 || 'test default decision'
+ 'invalid authorization' | 'ch-1' | 'something else' || 409 || 'test default decision'
+ }
+
+}
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/ncmp/RestApiSpec.groovy
index 5325f1a86e..7ce3cf5e17 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/ncmp/RestApiSpec.groovy
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.integration.functional
+package org.onap.cps.integration.functional.ncmp
import static org.hamcrest.Matchers.containsInAnyOrder
import static org.hamcrest.Matchers.hasSize
@@ -31,29 +31,29 @@ import org.onap.cps.integration.base.CpsIntegrationSpecBase
import org.springframework.http.MediaType
import spock.util.concurrent.PollingConditions
-class NcmpRestApiSpec extends CpsIntegrationSpecBase {
+class RestApiSpec extends CpsIntegrationSpecBase {
def 'Register CM Handles using REST API.'() {
given: 'DMI will return modules'
- dmiDispatcher.moduleNamesPerCmHandleId = [
+ dmiDispatcher1.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"}]}'
+ when: 'a POST request is made to register the CM Handles'
+ def requestBody = '{"dmiPlugin":"'+DMI1_URL+'","createdCmHandles":[{"cmHandle":"ch-1","alternateId":"alt-1"},{"cmHandle":"ch-2","alternateId":"alt-2"},{"cmHandle":"ch-3","alternateId":"alt-3"}]}'
mvc.perform(post('/ncmpInventory/v1/ch').contentType(MediaType.APPLICATION_JSON).content(requestBody))
.andExpect(status().is2xxSuccessful())
- when: 'module sync runs'
+ and: 'the module sync watchdog is triggered'
moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
then: 'CM-handles go to READY state'
- new PollingConditions().eventually {
+ new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> {
(1..3).each {
mvc.perform(get('/ncmp/v1/ch/ch-'+it))
.andExpect(status().isOk())
.andExpect(jsonPath('$.state.cmHandleState').value('READY'))
}
- }
+ })
}
def 'Search for CM Handles by module using REST API.'() {
@@ -67,20 +67,46 @@ class NcmpRestApiSpec extends CpsIntegrationSpecBase {
]
}""".formatted(moduleName)
expect: "a search for module ${moduleName} returns expected CM handles"
- mvc.perform(post('/ncmp/v1/ch/id-searches').contentType(MediaType.APPLICATION_JSON).content(requestBodyWithModuleCondition))
+ mvc.perform(post('/ncmp/v1/ch/id-searches'+outputAlternateId).contentType(MediaType.APPLICATION_JSON).content(requestBodyWithModuleCondition))
+ .andExpect(status().is2xxSuccessful())
+ .andExpect(jsonPath('$[*]', containsInAnyOrder(expectedCmHandleReferences.toArray())))
+ .andExpect(jsonPath('$', hasSize(expectedCmHandleReferences.size())));
+ where:
+ moduleName | outputAlternateId || expectedCmHandleReferences
+ 'M1' | '?outputAlternateId=false' || ['ch-1', 'ch-2', 'ch-3']
+ 'M2' | '?outputAlternateId=false' || ['ch-1', 'ch-2']
+ 'M3' | '?outputAlternateId=false' || ['ch-3']
+ 'M1' | '?outputAlternateId=true' || ['alt-1', 'alt-2', 'alt-3']
+ 'M2' | '?outputAlternateId=true' || ['alt-1', 'alt-2']
+ 'M3' | '?outputAlternateId=true' || ['alt-3']
+ 'M1' | '' || ['ch-1', 'ch-2', 'ch-3']
+
+ }
+
+ def 'Search for CM Handles using Cps Path Query.'() {
+ given: 'a JSON request body containing search parameter'
+ def requestBodyWithSearchCondition = """{
+ "cmHandleQueryParameters": [
+ {
+ "conditionName": "cmHandleWithCpsPath",
+ "conditionParameters": [ {"cpsPath" : "%s"} ]
+ }
+ ]
+ }""".formatted(cpsPath)
+ expect: "a search for cps path ${cpsPath} returns expected CM handles"
+ mvc.perform(post('/ncmp/v1/ch/id-searches').contentType(MediaType.APPLICATION_JSON).content(requestBodyWithSearchCondition))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath('$[*]', containsInAnyOrder(expectedCmHandles.toArray())))
.andExpect(jsonPath('$', hasSize(expectedCmHandles.size())));
where:
- moduleName || expectedCmHandles
- 'M1' || ['ch-1', 'ch-2', 'ch-3']
- 'M2' || ['ch-1', 'ch-2']
- 'M3' || ['ch-3']
+ scenario | cpsPath || expectedCmHandles
+ 'All Ready CM handles' | "//state[@cm-handle-state='READY']" || ['ch-1', 'ch-2', 'ch-3']
+ 'Having Alternate ID alt-3' | "//cm-handles[@alternate-id='alt-3']" || ['ch-3']
}
def 'De-register CM handles using REST API.'() {
when: 'a POST request is made to deregister the CM Handle'
- def requestBody = '{"dmiPlugin":"'+DMI_URL+'", "removedCmHandles": ["ch-1", "ch-2", "ch-3"]}'
+ def requestBody = '{"dmiPlugin":"'+DMI1_URL+'", "removedCmHandles": ["ch-1", "ch-2", "ch-3"]}'
mvc.perform(post('/ncmpInventory/v1/ch').contentType(MediaType.APPLICATION_JSON).content(requestBody))
.andExpect(status().is2xxSuccessful())
then: 'the CM handles are not found using GET'
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/WriteSubJobSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/WriteSubJobSpec.groovy
new file mode 100644
index 0000000000..834e1399e3
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/WriteSubJobSpec.groovy
@@ -0,0 +1,75 @@
+/*
+ * ============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.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.datajobs.DataJobService
+import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata
+import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest
+import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse
+import org.onap.cps.ncmp.api.datajobs.models.WriteOperation
+import org.springframework.beans.factory.annotation.Autowired
+
+class WriteSubJobSpec extends CpsIntegrationSpecBase {
+
+ @Autowired
+ DataJobService dataJobService
+
+ def setup() {
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1']
+ dmiDispatcher1.moduleNamesPerCmHandleId['ch-2'] = ['M2']
+ dmiDispatcher2.moduleNamesPerCmHandleId['ch-3'] = ['M3']
+ registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'p1')
+ registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, 'p2')
+ registerCmHandle(DMI2_URL, 'ch-3', NO_MODULE_SET_TAG, 'p3')
+ }
+
+ def cleanup() {
+ deregisterCmHandle(DMI1_URL, 'ch-1')
+ deregisterCmHandle(DMI1_URL, 'ch-2')
+ deregisterCmHandle(DMI2_URL, 'ch-3')
+ }
+
+ def 'Create a sub-job write request.'() {
+ given: 'the required input data for the write job'
+ def authorization = 'my authorization header'
+ def dataJobWriteRequest = new DataJobWriteRequest([new WriteOperation('p1', '', '', null), new WriteOperation('p2', '', '', null), new WriteOperation('p3', '', '', null)])
+ def myDataJobMetadata = new DataJobMetadata('d1', '', '')
+ def dataJobId = 'my-data-job-id'
+ when: 'sending a write job to NCMP with 2 sub-jobs for DMI 1 and 1 sub-job for DMI 2'
+ def response = dataJobService.writeDataJob(authorization, dataJobId, myDataJobMetadata, dataJobWriteRequest)
+ then: 'each DMI received the expected sub-jobs and the response has the expected values'
+ assert response.size() == 2
+ assert response[0].class == SubJobWriteResponse.class
+ assert response[0].subJobId == "some sub job id"
+ assert response[0].dmiServiceName == "some dmi service name"
+ assert response[0].dataProducerId == "some data producer id"
+ and: 'dmi 1 received the correct job details'
+ def receivedSubJobsForDispatcher1 = dmiDispatcher1.receivedSubJobs['?destination=d1']['data'].collect()
+ assert receivedSubJobsForDispatcher1.size() == 2
+ assert receivedSubJobsForDispatcher1[0]['path'] == 'p1'
+ assert receivedSubJobsForDispatcher1[1]['path'] == 'p2'
+ and: 'dmi 2 received the correct job details'
+ def receivedSubJobsForDispatcher2 = dmiDispatcher2.receivedSubJobs['?destination=d1']['data'].collect()
+ assert receivedSubJobsForDispatcher2.size() == 1
+ assert receivedSubJobsForDispatcher2[0]['path'] == 'p3'
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy
index 0ca200211a..fb5a0c3eb1 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2023-2024 Nordix Foundation
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
@@ -22,14 +23,17 @@ package org.onap.cps.integration.performance.base
import org.onap.cps.integration.ResourceMeter
import org.onap.cps.spi.FetchDescendantsOption
+import org.onap.cps.utils.ContentType
import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME
import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
+import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT
class NcmpPerfTestBase extends PerfTestBase {
def static NCMP_PERFORMANCE_TEST_DATASPACE = 'ncmpPerformance'
- def static REGISTRY_ANCHOR = 'ncmp-registry'
+ def static REGISTRY_ANCHOR = NCMP_DMI_REGISTRY_ANCHOR
+ def static REGISTRY_PARENT = NCMP_DMI_REGISTRY_PARENT
def static REGISTRY_SCHEMA_SET = 'registrySchemaSet'
def static TOTAL_CM_HANDLES = 20_000
def static CM_DATA_SUBSCRIPTIONS_ANCHOR = 'cm-data-subscriptions'
@@ -68,30 +72,30 @@ class NcmpPerfTestBase extends PerfTestBase {
}
def createRegistrySchemaSet() {
- def modelAsString = readResourceDataFile('ncmp-registry/dmi-registry@2024-02-23.yang')
+ def modelAsString = readResourceDataFile('inventory/dmi-registry@2024-02-23.yang')
cpsModuleService.createSchemaSet(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_SCHEMA_SET, [registry: modelAsString])
}
def addRegistryData() {
cpsAnchorService.createAnchor(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_SCHEMA_SET, REGISTRY_ANCHOR)
cpsDataService.saveData(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, '{"dmi-registry": []}', now)
- def innerNodeJsonTemplate = readResourceDataFile('ncmp-registry/innerNode.json')
+ def cmHandleJsonTemplate = readResourceDataFile('inventory/cmHandleTemplate.json')
def batchSize = 100
for (def i = 0; i < TOTAL_CM_HANDLES; i += batchSize) {
- def data = '{ "cm-handles": [' + (1..batchSize).collect { innerNodeJsonTemplate.replace('CMHANDLE_ID_HERE', (it + i).toString()) }.join(',') + ']}'
- cpsDataService.saveListElements(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, '/dmi-registry', data, now)
+ def data = '{ "cm-handles": [' + (1..batchSize).collect { cmHandleJsonTemplate.replace('CM_HANDLE_ID_HERE', (it + i).toString()) }.join(',') + ']}'
+ cpsDataService.saveListElements(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, REGISTRY_PARENT, data, now, ContentType.JSON)
}
}
def addRegistryDataWithAlternateIdAsPath() {
- def innerNodeJsonTemplate = readResourceDataFile('ncmp-registry/innerCmHandleNode.json')
+ def cmHandleWithAlternateIdTemplate = readResourceDataFile('inventory/cmHandleWithAlternateIdTemplate.json')
def batchSize = 10
for (def i = 0; i < TOTAL_CM_HANDLES; i += batchSize) {
def data = '{ "cm-handles": [' + (1..batchSize).collect {
- innerNodeJsonTemplate.replace('CM_HANDLE_ID_HERE', (it + i).toString())
+ cmHandleWithAlternateIdTemplate.replace('CM_HANDLE_ID_HERE', (it + i).toString())
.replace('ALTERNATE_ID_AS_PATH', (it + i).toString())
}.join(',') + ']}'
- cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry', data, now)
+ cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, REGISTRY_PARENT, data, now, ContentType.JSON)
}
}
@@ -115,7 +119,7 @@ class NcmpPerfTestBase extends PerfTestBase {
def result = cpsDataService.getDataNodes(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, '/', FetchDescendantsOption.OMIT_DESCENDANTS)
resourceMeter.stop()
then: 'expected data exists'
- assert result.xpath == ['/dmi-registry']
+ assert result.xpath == [REGISTRY_PARENT]
and: 'operation completes within expected time'
recordAndAssertResourceUsage('NCMP pre-load test data',
15, resourceMeter.totalTimeInSeconds,
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy
index 7bcec968e5..03abdb4b3f 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy
@@ -50,7 +50,7 @@ class UpdatePerfTest extends CpsPerfTestBase {
given: 'replacement JSON for node containing list of device nodes'
def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}'
when: 'the container node is updated'
- objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now)
+ objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now, ContentType.JSON)
then: 'there are the expected number of total nodes'
assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
where:
@@ -68,7 +68,7 @@ class UpdatePerfTest extends CpsPerfTestBase {
def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}'
when: 'the container node is updated'
resourceMeter.start()
- objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now)
+ objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now, ContentType.JSON)
resourceMeter.stop()
then: 'there are the expected number of total nodes'
assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
index 0195611740..9f6c78d5f5 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2023-2024 Nordix Foundation
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
@@ -20,6 +21,8 @@
package org.onap.cps.integration.performance.cps
+import org.onap.cps.utils.ContentType
+
import java.time.OffsetDateTime
import org.onap.cps.integration.performance.base.CpsPerfTestBase
@@ -87,7 +90,7 @@ class WritePerfTest extends CpsPerfTestBase {
']}'
when: 'device nodes are added'
resourceMeter.start()
- cpsDataService.saveListElements(CPS_PERFORMANCE_TEST_DATASPACE, WRITE_TEST_ANCHOR, '/openroadm-devices', jsonListData, OffsetDateTime.now())
+ cpsDataService.saveListElements(CPS_PERFORMANCE_TEST_DATASPACE, WRITE_TEST_ANCHOR, '/openroadm-devices', jsonListData, OffsetDateTime.now(), ContentType.JSON)
resourceMeter.stop()
then: 'the operation takes less than #expectedDuration and memory used is within limit'
recordAndAssertResourceUsage("Saving list of ${totalNodes} devices",
diff --git a/integration-test/src/test/java/org/onap/cps/integration/DmiStubTestContainer.java b/integration-test/src/test/java/org/onap/cps/integration/DmiStubTestContainer.java
new file mode 100644
index 0000000000..1bffb35c14
--- /dev/null
+++ b/integration-test/src/test/java/org/onap/cps/integration/DmiStubTestContainer.java
@@ -0,0 +1,61 @@
+/*
+ * ============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;
+
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.utility.DockerImageName;
+
+public class DmiStubTestContainer extends GenericContainer<DmiStubTestContainer> {
+
+ public static final String IMAGE_NAME_AND_VERSION =
+ "nexus3.onap.org:10003/onap/dmi-plugin-demo-and-csit-stub:latest";
+ public static final String DMI_STUB_URL = "http://localhost:8784";
+
+ private static DmiStubTestContainer dmiStubTestContainer;
+
+ private DmiStubTestContainer() {
+ super(DockerImageName.parse(IMAGE_NAME_AND_VERSION));
+ }
+
+ /**
+ * Provides an instance of the Dmi Plugin Stub test container wrapper.
+ * This will allow to interact with the DMI Stub in our acceptance tests.
+ *
+ * @return DmiStubTestContainer
+ */
+ public static DmiStubTestContainer getInstance() {
+ if (dmiStubTestContainer == null) {
+ dmiStubTestContainer = new DmiStubTestContainer();
+ dmiStubTestContainer.addFixedExposedPort(8784, 8092);
+ Runtime.getRuntime().addShutdownHook(new Thread(dmiStubTestContainer::close));
+ }
+ return dmiStubTestContainer;
+ }
+
+ @Override
+ public void start() {
+ super.start();
+ }
+
+ @Override
+ public void stop() {
+ // Method intentionally left blank
+ }
+}
diff --git a/integration-test/src/test/java/org/onap/cps/integration/KafkaTestContainer.java b/integration-test/src/test/java/org/onap/cps/integration/KafkaTestContainer.java
index d41f752912..ff4aec4175 100644
--- a/integration-test/src/test/java/org/onap/cps/integration/KafkaTestContainer.java
+++ b/integration-test/src/test/java/org/onap/cps/integration/KafkaTestContainer.java
@@ -21,6 +21,7 @@ package org.onap.cps.integration;
import java.util.HashMap;
import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
@@ -33,11 +34,12 @@ import org.testcontainers.utility.DockerImageName;
* This ensures only one instance of Kafka container across the integration tests.
* Avoid unnecessary resource and time consumption.
*/
+@Slf4j
public class KafkaTestContainer extends KafkaContainer {
private static final String IMAGE_NAME_AND_VERSION = "registry.nordix.org/onaptest/confluentinc/cp-kafka:6.2.1";
- private static KafkaTestContainer kafkaTestContainer;
+ private static volatile KafkaTestContainer kafkaTestContainer;
private KafkaTestContainer() {
super(DockerImageName.parse(IMAGE_NAME_AND_VERSION).asCompatibleSubstituteFor("confluentinc/cp-kafka"));
@@ -51,8 +53,15 @@ public class KafkaTestContainer extends KafkaContainer {
*/
public static KafkaTestContainer getInstance() {
if (kafkaTestContainer == null) {
- kafkaTestContainer = new KafkaTestContainer();
- Runtime.getRuntime().addShutdownHook(new Thread(kafkaTestContainer::close));
+ synchronized (KafkaTestContainer.class) {
+ if (kafkaTestContainer == null) {
+ kafkaTestContainer = new KafkaTestContainer();
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ log.info("Shutting down KafkaTestContainer...");
+ kafkaTestContainer.stop();
+ }));
+ }
+ }
}
return kafkaTestContainer;
}
@@ -63,8 +72,11 @@ public class KafkaTestContainer extends KafkaContainer {
@Override
public void start() {
- super.start();
- System.setProperty("spring.kafka.properties.bootstrap.servers", kafkaTestContainer.getBootstrapServers());
+ if (!isRunning()) {
+ super.start();
+ System.setProperty("spring.kafka.properties.bootstrap.servers", getBootstrapServers());
+ log.info("KafkaTestContainer started at {}", getBootstrapServers());
+ }
}
@Override
@@ -78,8 +90,9 @@ public class KafkaTestContainer extends KafkaContainer {
configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaTestContainer.getBootstrapServers());
configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, valueDeserializer);
- configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
+ configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
configProps.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroupId);
+ configProps.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, Integer.MAX_VALUE);
return configProps;
}
diff --git a/integration-test/src/test/java/org/onap/cps/integration/MicroMeterTestConfig.java b/integration-test/src/test/java/org/onap/cps/integration/MicroMeterTestConfig.java
new file mode 100644
index 0000000000..3b26f42c8a
--- /dev/null
+++ b/integration-test/src/test/java/org/onap/cps/integration/MicroMeterTestConfig.java
@@ -0,0 +1,41 @@
+/*
+ * ============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;
+
+import io.micrometer.core.aop.TimedAspect;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MicroMeterTestConfig {
+ @Bean
+ public MeterRegistry meterRegistry() {
+ return new SimpleMeterRegistry(); // Use a simple in-memory registry for testing
+ }
+
+ @Bean
+ public TimedAspect timedAspect(final MeterRegistry meterRegistry) {
+ return new TimedAspect(meterRegistry);
+ }
+}
+
diff --git a/integration-test/src/test/java/org/onap/cps/integration/ResourceMeter.java b/integration-test/src/test/java/org/onap/cps/integration/ResourceMeter.java
index f8a2ecb4df..46bfcf69e6 100644
--- a/integration-test/src/test/java/org/onap/cps/integration/ResourceMeter.java
+++ b/integration-test/src/test/java/org/onap/cps/integration/ResourceMeter.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation
+ * Copyright (C) 2023-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.
@@ -20,6 +20,8 @@
package org.onap.cps.integration;
+import static org.awaitility.Awaitility.await;
+
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
@@ -71,7 +73,7 @@ public class ResourceMeter {
static void performGcAndWait() {
final long gcCountBefore = getGcCount();
System.gc();
- while (getGcCount() == gcCountBefore) {}
+ await().until(() -> getGcCount() > gcCountBefore);
}
private static long getGcCount() {
@@ -94,4 +96,3 @@ public class ResourceMeter {
.forEach(MemoryPoolMXBean::resetPeakUsage);
}
}
-
diff --git a/integration-test/src/test/resources/application-module-sync-delayed.yml b/integration-test/src/test/resources/application-module-sync-delayed.yml
new file mode 100644
index 0000000000..7b9c6aea4f
--- /dev/null
+++ b/integration-test/src/test/resources/application-module-sync-delayed.yml
@@ -0,0 +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=========================================================
+
+test:
+ ncmp:
+ timers:
+ advised-modules-sync:
+ initial-delay-ms: 600000
+
diff --git a/integration-test/src/test/resources/application.yml b/integration-test/src/test/resources/application.yml
index 58e6287955..30598dfb90 100644
--- a/integration-test/src/test/resources/application.yml
+++ b/integration-test/src/test/resources/application.yml
@@ -179,9 +179,7 @@ ncmp:
timers:
advised-modules-sync:
- sleep-time-ms: 100000
- locked-modules-sync:
- sleep-time-ms: 300000
+ sleep-time-ms: 1000000
cm-handle-data-sync:
sleep-time-ms: 30000
subscription-forwarding:
@@ -193,7 +191,7 @@ ncmp:
modules-sync-watchdog:
async-executor:
- parallelism-level: 1
+ parallelism-level: 2
model-loader:
maximum-attempt-count: 20
@@ -213,8 +211,24 @@ ncmp:
init:
mode: ALWAYS
+ policy-executor:
+ enabled: true
+ defaultDecision: "test default decision"
+ server:
+ address: http://localhost
+ port: 8790
+ httpclient:
+ all-services:
+ maximumInMemorySizeInMegabytes: 1
+ maximumConnectionsTotal: 10
+ pendingAcquireMaxCount: 10
+ connectionTimeoutInSeconds: 30
+ readTimeoutInSeconds: 1
+ writeTimeoutInSeconds: 30
+
hazelcast:
cluster-name: cps-and-ncmp-test-caches
+ instance-config-name: "cps-and-ncmp-hazelcast-instance-test-config"
mode:
kubernetes:
enabled: false
diff --git a/integration-test/src/test/resources/data/ncmp-registry/innerNode.json b/integration-test/src/test/resources/data/inventory/cmHandleTemplate.json
index b6c65f3763..6577f4e560 100644
--- a/integration-test/src/test/resources/data/ncmp-registry/innerNode.json
+++ b/integration-test/src/test/resources/data/inventory/cmHandleTemplate.json
@@ -1,6 +1,6 @@
{
- "id": "cm-CMHANDLE_ID_HERE",
- "alternate-id": "alt-CMHANDLE_ID_HERE",
+ "id": "cm-CM_HANDLE_ID_HERE",
+ "alternate-id": "alt-CM_HANDLE_ID_HERE",
"module-set-tag": "my-module-set-tag",
"dmi-service-name": "http://ncmp-dmi-plugin-stub:8080",
"dmi-data-service-name": "",
@@ -21,4 +21,4 @@
}
}
}
-} \ No newline at end of file
+}
diff --git a/integration-test/src/test/resources/data/ncmp-registry/innerCmHandleNode.json b/integration-test/src/test/resources/data/inventory/cmHandleWithAlternateIdTemplate.json
index 88446c4a0f..88446c4a0f 100644
--- a/integration-test/src/test/resources/data/ncmp-registry/innerCmHandleNode.json
+++ b/integration-test/src/test/resources/data/inventory/cmHandleWithAlternateIdTemplate.json
diff --git a/integration-test/src/test/resources/data/ncmp-registry/dmi-registry@2024-02-23.yang b/integration-test/src/test/resources/data/inventory/dmi-registry@2024-02-23.yang
index d7b4ff7550..d7b4ff7550 100644
--- a/integration-test/src/test/resources/data/ncmp-registry/dmi-registry@2024-02-23.yang
+++ b/integration-test/src/test/resources/data/inventory/dmi-registry@2024-02-23.yang