2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023-2024 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the 'License');
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an 'AS IS' BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.integration.base
23 import java.time.OffsetDateTime
24 import java.time.format.DateTimeFormatter
25 import org.onap.cps.api.CpsAnchorService
26 import org.onap.cps.api.CpsDataService
27 import org.onap.cps.api.CpsDataspaceService
28 import org.onap.cps.api.CpsModuleService
29 import org.onap.cps.api.CpsQueryService
30 import org.onap.cps.integration.DatabaseTestContainer
31 import org.onap.cps.integration.KafkaTestContainer
32 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
33 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
34 import org.onap.cps.ncmp.api.NetworkCmProxyQueryService
35 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
36 import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncWatchdog
37 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
38 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
39 import org.onap.cps.spi.exceptions.DataspaceNotFoundException
40 import org.onap.cps.spi.model.DataNode
41 import org.onap.cps.spi.repository.DataspaceRepository
42 import org.onap.cps.spi.utils.SessionManager
43 import org.onap.cps.utils.JsonObjectMapper
44 import org.springframework.beans.factory.annotation.Autowired
45 import org.springframework.boot.autoconfigure.EnableAutoConfiguration
46 import org.springframework.boot.autoconfigure.domain.EntityScan
47 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
48 import org.springframework.boot.test.context.SpringBootTest
49 import org.springframework.context.annotation.ComponentScan
50 import org.springframework.data.jpa.repository.config.EnableJpaRepositories
51 import org.springframework.http.HttpStatus
52 import org.springframework.http.MediaType
53 import org.springframework.test.web.client.ExpectedCount
54 import org.springframework.test.web.client.MockRestServiceServer
55 import org.springframework.test.web.servlet.MockMvc
56 import org.springframework.web.client.RestTemplate
57 import org.testcontainers.spock.Testcontainers
58 import spock.lang.Shared
59 import spock.lang.Specification
60 import spock.util.concurrent.PollingConditions
62 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
63 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
64 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT
65 import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo
66 import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus
68 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = [CpsDataspaceService])
70 @EnableAutoConfiguration
72 @EnableJpaRepositories(basePackageClasses = [DataspaceRepository])
73 @ComponentScan(basePackages = ['org.onap.cps'])
74 @EntityScan('org.onap.cps.spi.entities')
75 abstract class CpsIntegrationSpecBase extends Specification {
78 DatabaseTestContainer databaseTestContainer = DatabaseTestContainer.getInstance()
81 KafkaTestContainer kafkaTestContainer = KafkaTestContainer.getInstance()
87 CpsDataspaceService cpsDataspaceService
90 CpsAnchorService cpsAnchorService
93 CpsDataService cpsDataService
96 CpsModuleService cpsModuleService
99 CpsQueryService cpsQueryService
102 SessionManager sessionManager
105 NetworkCmProxyCmHandleQueryService networkCmProxyCmHandleQueryService
108 NetworkCmProxyDataService networkCmProxyDataService
111 NetworkCmProxyQueryService networkCmProxyQueryService
114 RestTemplate restTemplate
117 ModuleSyncWatchdog moduleSyncWatchdog
120 JsonObjectMapper jsonObjectMapper
122 MockRestServiceServer mockDmiServer = null
124 static DMI_URL = 'http://mock-dmi-server'
125 static NO_MODULE_SET_TAG = ''
126 static GENERAL_TEST_DATASPACE = 'generalTestDataspace'
127 static BOOKSTORE_SCHEMA_SET = 'bookstoreSchemaSet'
129 def static initialized = false
130 def now = OffsetDateTime.now()
134 cpsDataspaceService.createDataspace(GENERAL_TEST_DATASPACE)
135 createStandardBookStoreSchemaSet(GENERAL_TEST_DATASPACE)
138 mockDmiServer = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build()
141 def static readResourceDataFile(filename) {
142 return new File('src/test/resources/data/' + filename).text
145 // *** CPS Integration Test Utilities ***
147 def static countDataNodesInTree(DataNode dataNode) {
148 return 1 + countDataNodesInTree(dataNode.getChildDataNodes())
151 def static countDataNodesInTree(Collection<DataNode> dataNodes) {
153 for (DataNode parent : dataNodes) {
154 nodeCount += countDataNodesInTree(parent)
159 def getBookstoreYangResourcesNameToContentMap() {
160 def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
161 def bookstoreTypesFileContent = readResourceDataFile('bookstore/bookstore-types.yang')
162 return [bookstore: bookstoreModelFileContent, bookstoreTypes: bookstoreTypesFileContent]
165 def createStandardBookStoreSchemaSet(targetDataspace) {
166 cpsModuleService.createSchemaSet(targetDataspace, BOOKSTORE_SCHEMA_SET, getBookstoreYangResourcesNameToContentMap())
169 def createStandardBookStoreSchemaSet(targetDataspace, targetSchemaSet) {
170 cpsModuleService.createSchemaSet(targetDataspace, targetSchemaSet, getBookstoreYangResourcesNameToContentMap())
173 def dataspaceExists(dataspaceName) {
175 cpsDataspaceService.getDataspace(dataspaceName)
176 } catch (DataspaceNotFoundException ignored) {
182 def addAnchorsWithData(numberOfAnchors, dataspaceName, schemaSetName, anchorNamePrefix, data) {
183 (1..numberOfAnchors).each {
184 cpsAnchorService.createAnchor(dataspaceName, schemaSetName, anchorNamePrefix + it)
185 cpsDataService.saveData(dataspaceName, anchorNamePrefix + it, data.replace("Easons", "Easons-"+it.toString()), OffsetDateTime.now())
189 def createJsonArray(name, numberOfElements, keyName, keyValuePrefix, dataPerKey) {
190 def innerJson = (1..numberOfElements).collect {
191 '{"' + keyName + '":"' + keyValuePrefix + '-' + it + '"' + (dataPerKey.empty? '': ',' + dataPerKey) + '}'
193 return '{"' + name + '":[' + innerJson + ']}'
196 def createLeafList(name, numberOfElements, valuePrefix) {
197 def innerJson = (1..numberOfElements).collect {'"' + valuePrefix + '-' + it + '"'}.join(',')
198 return '"' + name + '":[' + innerJson + ']'
201 // *** NCMP Integration Test Utilities ***
203 def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag) {
204 def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, moduleSetTag: moduleSetTag)
205 networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: [cmHandleToCreate]))
206 moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
207 new PollingConditions().within(3, () -> {
208 CmHandleState.READY == networkCmProxyDataService.getCmHandleCompositeState(cmHandleId).cmHandleState
212 def deregisterCmHandle(dmiPlugin, cmHandleId) {
213 deregisterCmHandles(dmiPlugin, [cmHandleId])
216 def deregisterCmHandles(dmiPlugin, cmHandleIds) {
217 networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds))
220 def mockDmiResponsesForModuleSync(dmiPlugin, cmHandleId, dmiModuleReferencesResponse, dmiModuleResourcesResponse) {
221 mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/modules"))
222 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(dmiModuleReferencesResponse))
223 mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/moduleResources"))
224 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(dmiModuleResourcesResponse))
227 def mockDmiIsNotAvailableForModuleSync(dmiPlugin, cmHandleId) {
228 mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/modules"))
229 .andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE))
232 def mockDmiWillRespondToHealthChecks(dmiPlugin) {
233 mockDmiServer.expect(ExpectedCount.between(0, Integer.MAX_VALUE), requestTo("${dmiPlugin}/actuator/health"))
234 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body('{"status":"UP"}'))
237 def overrideCmHandleLastUpdateTime(cmHandleId, newUpdateTime) {
238 String ISO_TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
239 DateTimeFormatter ISO_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(ISO_TIMESTAMP_PATTERN);
240 def jsonForUpdate = '{ "state": { "last-update-time": "%s" } }'.formatted(ISO_TIMESTAMP_FORMATTER.format(newUpdateTime))
241 cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
242 NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='${cmHandleId}']", jsonForUpdate, now)