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.MockRestServiceServer
54 import org.springframework.test.web.servlet.MockMvc
55 import org.springframework.web.client.RestTemplate
56 import org.testcontainers.spock.Testcontainers
57 import spock.lang.Shared
58 import spock.lang.Specification
59 import spock.util.concurrent.PollingConditions
61 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
62 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
63 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT
64 import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo
65 import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus
67 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = [CpsDataspaceService])
69 @EnableAutoConfiguration
71 @EnableJpaRepositories(basePackageClasses = [DataspaceRepository])
72 @ComponentScan(basePackages = ['org.onap.cps'])
73 @EntityScan('org.onap.cps.spi.entities')
74 abstract class CpsIntegrationSpecBase extends Specification {
77 DatabaseTestContainer databaseTestContainer = DatabaseTestContainer.getInstance()
80 KafkaTestContainer kafkaTestContainer = KafkaTestContainer.getInstance()
86 CpsDataspaceService cpsDataspaceService
89 CpsAnchorService cpsAnchorService
92 CpsDataService cpsDataService
95 CpsModuleService cpsModuleService
98 CpsQueryService cpsQueryService
101 SessionManager sessionManager
104 NetworkCmProxyCmHandleQueryService networkCmProxyCmHandleQueryService
107 NetworkCmProxyDataService networkCmProxyDataService
110 NetworkCmProxyQueryService networkCmProxyQueryService
113 RestTemplate restTemplate
116 ModuleSyncWatchdog moduleSyncWatchdog
119 JsonObjectMapper jsonObjectMapper
121 MockRestServiceServer mockDmiServer = null
123 static final DMI_URL = 'http://mock-dmi-server'
125 def static GENERAL_TEST_DATASPACE = 'generalTestDataspace'
126 def static BOOKSTORE_SCHEMA_SET = 'bookstoreSchemaSet'
128 def static initialized = false
129 def now = OffsetDateTime.now()
133 cpsDataspaceService.createDataspace(GENERAL_TEST_DATASPACE)
134 createStandardBookStoreSchemaSet(GENERAL_TEST_DATASPACE)
137 mockDmiServer = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build()
141 mockDmiServer.reset()
144 def static readResourceDataFile(filename) {
145 return new File('src/test/resources/data/' + filename).text
148 // *** CPS Integration Test Utilities ***
150 def static countDataNodesInTree(DataNode dataNode) {
151 return 1 + countDataNodesInTree(dataNode.getChildDataNodes())
154 def static countDataNodesInTree(Collection<DataNode> dataNodes) {
156 for (DataNode parent : dataNodes) {
157 nodeCount += countDataNodesInTree(parent)
162 def getBookstoreYangResourcesNameToContentMap() {
163 def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
164 def bookstoreTypesFileContent = readResourceDataFile('bookstore/bookstore-types.yang')
165 return [bookstore: bookstoreModelFileContent, bookstoreTypes: bookstoreTypesFileContent]
168 def createStandardBookStoreSchemaSet(targetDataspace) {
169 cpsModuleService.createSchemaSet(targetDataspace, BOOKSTORE_SCHEMA_SET, getBookstoreYangResourcesNameToContentMap())
172 def createStandardBookStoreSchemaSet(targetDataspace, targetSchemaSet) {
173 cpsModuleService.createSchemaSet(targetDataspace, targetSchemaSet, getBookstoreYangResourcesNameToContentMap())
176 def dataspaceExists(dataspaceName) {
178 cpsDataspaceService.getDataspace(dataspaceName)
179 } catch (DataspaceNotFoundException ignored) {
185 def addAnchorsWithData(numberOfAnchors, dataspaceName, schemaSetName, anchorNamePrefix, data) {
186 (1..numberOfAnchors).each {
187 cpsAnchorService.createAnchor(dataspaceName, schemaSetName, anchorNamePrefix + it)
188 cpsDataService.saveData(dataspaceName, anchorNamePrefix + it, data.replace("Easons", "Easons-"+it.toString()), OffsetDateTime.now())
192 def createJsonArray(name, numberOfElements, keyName, keyValuePrefix, dataPerKey) {
193 def innerJson = (1..numberOfElements).collect {
194 '{"' + keyName + '":"' + keyValuePrefix + '-' + it + '"' + (dataPerKey.empty? '': ',' + dataPerKey) + '}'
196 return '{"' + name + '":[' + innerJson + ']}'
199 def createLeafList(name, numberOfElements, valuePrefix) {
200 def innerJson = (1..numberOfElements).collect {'"' + valuePrefix + '-' + it + '"'}.join(',')
201 return '"' + name + '":[' + innerJson + ']'
204 // *** NCMP Integration Test Utilities ***
206 def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag, dmiModuleReferencesResponse, dmiModuleResourcesResponse) {
207 def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, moduleSetTag: moduleSetTag)
208 networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: [cmHandleToCreate]))
209 mockDmiResponsesForModuleSync(dmiPlugin, cmHandleId, dmiModuleReferencesResponse, dmiModuleResourcesResponse)
210 moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
211 new PollingConditions().within(3, () -> {
212 CmHandleState.READY == networkCmProxyDataService.getCmHandleCompositeState(cmHandleId).cmHandleState
214 mockDmiServer.reset()
217 def deregisterCmHandle(dmiPlugin, cmHandleId) {
218 deregisterCmHandles(dmiPlugin, [cmHandleId])
221 def deregisterCmHandles(dmiPlugin, cmHandleIds) {
222 networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds))
225 def mockDmiResponsesForModuleSync(dmiPlugin, cmHandleId, dmiModuleReferencesResponse, dmiModuleResourcesResponse) {
226 mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/modules"))
227 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(dmiModuleReferencesResponse))
228 mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/moduleResources"))
229 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(dmiModuleResourcesResponse))
232 def mockDmiIsNotAvailableForModuleSync(dmiPlugin, cmHandleId) {
233 mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/modules"))
234 .andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE))
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)