Merge "Part 1: Refactor CPS Delta code to utility class"
[cps.git] / integration-test / src / test / groovy / org / onap / cps / integration / base / CpsIntegrationSpecBase.groovy
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
4  *  Modifications Copyright (C) 2024-2025 TechMahindra Ltd.
5  *  ================================================================================
6  *  Licensed under the Apache License, Version 2.0 (the 'License');
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an 'AS IS' BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21
22 package org.onap.cps.integration.base
23
24 import com.hazelcast.map.IMap
25 import okhttp3.mockwebserver.MockWebServer
26 import org.onap.cps.api.CpsAnchorService
27 import org.onap.cps.api.CpsDataService
28 import org.onap.cps.api.CpsDataspaceService
29 import org.onap.cps.api.CpsDeltaService
30 import org.onap.cps.api.CpsModuleService
31 import org.onap.cps.api.CpsQueryService
32 import org.onap.cps.api.exceptions.DataspaceNotFoundException
33 import org.onap.cps.api.model.DataNode
34 import org.onap.cps.integration.DatabaseTestContainer
35 import org.onap.cps.integration.KafkaTestContainer
36 import org.onap.cps.ncmp.api.inventory.models.CmHandleState
37 import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
38 import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
39 import org.onap.cps.ncmp.impl.NetworkCmProxyInventoryFacadeImpl
40 import org.onap.cps.ncmp.impl.data.NetworkCmProxyFacade
41 import org.onap.cps.ncmp.impl.data.NetworkCmProxyQueryService
42 import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
43 import org.onap.cps.ncmp.impl.inventory.ParameterizedCmHandleQueryService
44 import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncService
45 import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncWatchdog
46 import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
47 import org.onap.cps.ncmp.rest.controller.NetworkCmProxyInventoryController
48 import org.onap.cps.ri.repository.DataspaceRepository
49 import org.onap.cps.ri.repository.SchemaSetRepository
50 import org.onap.cps.ri.utils.SessionManager
51 import org.onap.cps.spi.CpsModulePersistenceService
52 import org.onap.cps.utils.JsonObjectMapper
53 import org.springframework.beans.factory.annotation.Autowired
54 import org.springframework.beans.factory.annotation.Value
55 import org.springframework.boot.autoconfigure.EnableAutoConfiguration
56 import org.springframework.boot.autoconfigure.domain.EntityScan
57 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
58 import org.springframework.boot.test.context.SpringBootTest
59 import org.springframework.context.annotation.ComponentScan
60 import org.springframework.data.jpa.repository.config.EnableJpaRepositories
61 import org.springframework.test.context.ActiveProfiles
62 import org.springframework.test.web.servlet.MockMvc
63 import org.testcontainers.spock.Testcontainers
64 import spock.lang.Shared
65 import spock.lang.Specification
66
67 import java.time.OffsetDateTime
68 import java.util.concurrent.BlockingQueue
69
70 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = [CpsDataspaceService])
71 @Testcontainers
72 @EnableAutoConfiguration
73 @AutoConfigureMockMvc
74 @EnableJpaRepositories(basePackageClasses = [DataspaceRepository])
75 @ComponentScan(basePackages = ['org.onap.cps'])
76 @EntityScan('org.onap.cps.ri.models')
77 @ActiveProfiles('module-sync-delayed')
78 abstract class CpsIntegrationSpecBase extends Specification {
79
80     @Shared
81     DatabaseTestContainer databaseTestContainer = DatabaseTestContainer.getInstance()
82
83     @Shared
84     KafkaTestContainer kafkaTestContainer = KafkaTestContainer.getInstance()
85
86     @Autowired
87     MockMvc mvc
88
89     @Autowired
90     NetworkCmProxyInventoryController networkCmProxyInventoryController
91
92     @Autowired
93     CpsDataspaceService cpsDataspaceService
94
95     @Autowired
96     CpsAnchorService cpsAnchorService
97
98     @Autowired
99     CpsDataService cpsDataService
100
101     @Autowired
102     CpsDeltaService cpsDeltaService
103
104     @Autowired
105     CpsModuleService cpsModuleService
106
107     @Autowired
108     CpsQueryService cpsQueryService
109
110     @Autowired
111     SessionManager sessionManager
112
113     @Autowired
114     CpsModulePersistenceService cpsModulePersistenceService
115
116     @Autowired
117     DataspaceRepository dataspaceRepository
118
119     @Autowired
120     SchemaSetRepository schemaSetRepository
121
122     @Autowired
123     ParameterizedCmHandleQueryService networkCmProxyCmHandleQueryService
124
125     @Autowired
126     NetworkCmProxyFacade networkCmProxyFacade
127
128     @Autowired
129     NetworkCmProxyInventoryFacadeImpl NetworkCmProxyInventoryFacade
130
131     @Autowired
132     NetworkCmProxyQueryService networkCmProxyQueryService
133
134     @Autowired
135     ModuleSyncWatchdog moduleSyncWatchdog
136
137     @Autowired
138     ModuleSyncService moduleSyncService
139
140     @Autowired
141     BlockingQueue<String> moduleSyncWorkQueue
142
143     @Autowired
144     IMap<String, String> cpsAndNcmpLock
145
146     @Autowired
147     JsonObjectMapper jsonObjectMapper
148
149     @Autowired
150     InventoryPersistence inventoryPersistence
151
152     @Autowired
153     AlternateIdMatcher alternateIdMatcher
154
155     @Value('${ncmp.policy-executor.server.port:8080}')
156     private String policyServerPort;
157
158     MockWebServer mockDmiServer1 = new MockWebServer()
159     MockWebServer mockDmiServer2 = new MockWebServer()
160     MockWebServer mockPolicyServer = new MockWebServer()
161
162     DmiDispatcher dmiDispatcher1 = new DmiDispatcher()
163     DmiDispatcher dmiDispatcher2 = new DmiDispatcher()
164
165     PolicyDispatcher policyDispatcher = new PolicyDispatcher();
166
167     def DMI1_URL = null
168     def DMI2_URL = null
169
170     static NO_MODULE_SET_TAG = ''
171     static NO_ALTERNATE_ID = ''
172     static GENERAL_TEST_DATASPACE = 'generalTestDataspace'
173     static BOOKSTORE_SCHEMA_SET = 'bookstoreSchemaSet'
174
175     static initialized = false
176     def now = OffsetDateTime.now()
177
178     enum ModuleNameStrategy { UNIQUE, OVERLAPPING }
179
180     def setup() {
181         if (!initialized) {
182             cpsDataspaceService.createDataspace(GENERAL_TEST_DATASPACE)
183             createStandardBookStoreSchemaSet(GENERAL_TEST_DATASPACE)
184             initialized = true
185         }
186         mockDmiServer1.setDispatcher(dmiDispatcher1)
187         mockDmiServer1.start()
188
189         mockDmiServer2.setDispatcher(dmiDispatcher2)
190         mockDmiServer2.start()
191
192         mockPolicyServer.setDispatcher(policyDispatcher)
193         mockPolicyServer.start(Integer.valueOf(policyServerPort))
194
195         DMI1_URL = String.format("http://%s:%s", mockDmiServer1.getHostName(), mockDmiServer1.getPort())
196         DMI2_URL = String.format("http://%s:%s", mockDmiServer2.getHostName(), mockDmiServer2.getPort())
197     }
198
199     def cleanup() {
200         mockDmiServer1.shutdown()
201         mockDmiServer2.shutdown()
202         mockPolicyServer.shutdown()
203         cpsModuleService.deleteAllUnusedYangModuleData('NFP-Operational')
204     }
205
206     def static readResourceDataFile(filename) {
207         return new File('src/test/resources/data/' + filename).text
208     }
209
210     // *** CPS Integration Test Utilities ***
211
212     def static countDataNodesInTree(DataNode dataNode) {
213         return 1 + countDataNodesInTree(dataNode.getChildDataNodes())
214     }
215
216     def static countDataNodesInTree(Collection<DataNode> dataNodes) {
217         int nodeCount = 0
218         for (DataNode parent : dataNodes) {
219             nodeCount += countDataNodesInTree(parent)
220         }
221         return nodeCount
222     }
223
224     def getBookstoreyangResourceContentPerName() {
225         def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
226         def bookstoreTypesFileContent = readResourceDataFile('bookstore/bookstore-types.yang')
227         return [bookstore: bookstoreModelFileContent, bookstoreTypes: bookstoreTypesFileContent]
228     }
229
230     def createStandardBookStoreSchemaSet(targetDataspace) {
231         cpsModuleService.createSchemaSet(targetDataspace, BOOKSTORE_SCHEMA_SET, getBookstoreyangResourceContentPerName())
232     }
233
234     def createStandardBookStoreSchemaSet(targetDataspace, targetSchemaSet) {
235         cpsModuleService.createSchemaSet(targetDataspace, targetSchemaSet, getBookstoreyangResourceContentPerName())
236     }
237
238     def dataspaceExists(dataspaceName) {
239         try {
240             cpsDataspaceService.getDataspace(dataspaceName)
241         } catch (DataspaceNotFoundException ignored) {
242             return false
243         }
244         return true
245     }
246
247     def addAnchorsWithData(numberOfAnchors, dataspaceName, schemaSetName, anchorNamePrefix, data, contentType) {
248         (1..numberOfAnchors).each {
249             cpsAnchorService.createAnchor(dataspaceName, schemaSetName, anchorNamePrefix + it)
250             cpsDataService.saveData(dataspaceName, anchorNamePrefix + it, data.replace("Easons", "Easons-"+it.toString()), OffsetDateTime.now(), contentType)
251         }
252     }
253
254     def createJsonArray(name, numberOfElements, keyName, keyValuePrefix, dataPerKey) {
255         def innerJson = (1..numberOfElements).collect {
256             '{"' + keyName + '":"' + keyValuePrefix + '-' + it + '"' + (dataPerKey.empty? '': ',' + dataPerKey) + '}'
257         }.join(',')
258         return '{"' + name + '":[' + innerJson + ']}'
259     }
260
261     def createLeafList(name, numberOfElements, valuePrefix) {
262         def innerJson = (1..numberOfElements).collect {'"' + valuePrefix + '-' + it + '"'}.join(',')
263         return '"' + name + '":[' + innerJson + ']'
264     }
265
266     // *** NCMP Integration Test Utilities ***
267
268     def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag) {
269         registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag, NO_ALTERNATE_ID)
270     }
271
272     def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag, alternateId) {
273         registerCmHandleWithoutWaitForReady(dmiPlugin, cmHandleId, moduleSetTag, alternateId)
274         moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
275         CmHandleState.READY == networkCmProxyInventoryFacade.getCmHandleCompositeState(cmHandleId).cmHandleState
276     }
277
278     def registerCmHandleWithoutWaitForReady(dmiPlugin, cmHandleId, moduleSetTag, alternateId) {
279         def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, moduleSetTag: moduleSetTag, alternateId: alternateId, dataProducerIdentifier: 'some data producer id')
280         networkCmProxyInventoryFacade.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: [cmHandleToCreate]))
281     }
282
283     def registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(dmiPlugin, moduleSetTag, numberOfCmHandles, offset) {
284         registerSequenceOfCmHandles(dmiPlugin, moduleSetTag, numberOfCmHandles, offset, ModuleNameStrategy.UNIQUE, { id -> "alt=${id}" })
285     }
286
287     def registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(dmiPlugin, moduleSetTag, numberOfCmHandles, offset, altIdPrefix) {
288         registerSequenceOfCmHandles(dmiPlugin, moduleSetTag, numberOfCmHandles, offset, ModuleNameStrategy.UNIQUE, { id -> "${altIdPrefix}alt=${id}" })
289     }
290
291
292     def registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(dmiPlugin, moduleSetTag, numberOfCmHandles, offset, ModuleNameStrategy moduleNameStrategy) {
293         registerSequenceOfCmHandles(dmiPlugin, moduleSetTag, numberOfCmHandles, offset, moduleNameStrategy, { id -> "alt=${id}" })
294     }
295
296     def registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(dmiPlugin, moduleSetTag, numberOfCmHandles, offset, ModuleNameStrategy moduleNameStrategy, Closure<String> alternateIdGenerator) {
297         registerSequenceOfCmHandles(dmiPlugin, moduleSetTag, numberOfCmHandles, offset, moduleNameStrategy, alternateIdGenerator)
298     }
299
300     def registerSequenceOfCmHandles(dmiPlugin, moduleSetTag, numberOfCmHandles, offset, ModuleNameStrategy moduleNameStrategy, Closure<String> alternateIdGenerator) {
301         def cmHandles = []
302         def id = offset
303         def modulePrefix = moduleNameStrategy.OVERLAPPING.equals(moduleNameStrategy) ? 'same' : moduleSetTag
304         def moduleReferences = (1..200).collect { "${modulePrefix}Module${it}" }
305
306         (1..numberOfCmHandles).each {
307             def alternateId = alternateIdGenerator(id)
308             def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: "ch-${id}", moduleSetTag: moduleSetTag, alternateId: alternateId)
309             cmHandles.add(ncmpServiceCmHandle)
310             dmiDispatcher1.moduleNamesPerCmHandleId[ncmpServiceCmHandle.cmHandleId] = moduleReferences
311             dmiDispatcher2.moduleNamesPerCmHandleId[ncmpServiceCmHandle.cmHandleId] = moduleReferences
312             id++
313         }
314         networkCmProxyInventoryFacade.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: cmHandles))
315     }
316
317     def deregisterCmHandle(dmiPlugin, cmHandleId) {
318         deregisterCmHandles(dmiPlugin, [cmHandleId])
319     }
320
321     def deregisterCmHandles(dmiPlugin, cmHandleIds) {
322         networkCmProxyInventoryFacade.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds))
323     }
324
325     def deregisterSequenceOfCmHandles(dmiPlugin, numberOfCmHandles, offset) {
326         def cmHandleIds = []
327         def id = offset
328         (1..numberOfCmHandles).each { cmHandleIds.add('ch-' + id++) }
329         networkCmProxyInventoryFacade.updateDmiRegistration(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds))
330     }
331
332 }