2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2022 Nordix Foundation
4 * Modifications Copyright (C) 2023 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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.cps.ncmp.api.impl
24 import org.onap.cps.cpspath.parser.PathParsingException
25 import org.onap.cps.ncmp.api.inventory.CmHandleQueries
26 import org.onap.cps.ncmp.api.inventory.CmHandleQueriesImpl
27 import org.onap.cps.ncmp.api.inventory.InventoryPersistence
28 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters
29 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
30 import org.onap.cps.spi.FetchDescendantsOption
31 import org.onap.cps.spi.exceptions.DataInUseException
32 import org.onap.cps.spi.exceptions.DataValidationException
33 import org.onap.cps.spi.model.ConditionProperties
34 import org.onap.cps.spi.model.DataNode
35 import spock.lang.Specification
36 import java.util.stream.Collectors
38 class NetworkCmProxyCmHandlerQueryServiceSpec extends Specification {
40 def cmHandleQueries = Mock(CmHandleQueries)
41 def partiallyMockedCmHandleQueries = Spy(CmHandleQueriesImpl)
42 def mockInventoryPersistence = Mock(InventoryPersistence)
44 def static someCmHandleDataNode = [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'some-cmhandle-id\']', leaves: ['id':'some-cmhandle-id'])]
45 def dmiRegistry = new DataNode(xpath: '/dmi-registry', childDataNodes: createDataNodeList(['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4']))
47 static def queryResultCmHandleMap = createCmHandleMap(['H1', 'H2'])
49 def objectUnderTest = new NetworkCmProxyCmHandlerQueryServiceImpl(cmHandleQueries, mockInventoryPersistence)
50 def objectUnderTestSpy = new NetworkCmProxyCmHandlerQueryServiceImpl(partiallyMockedCmHandleQueries, mockInventoryPersistence)
52 def 'Retrieve cm handles with cpsPath when combined with no Module Query.'() {
53 given: 'a cmHandleWithCpsPath condition property'
54 def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
55 def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']])
56 cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
57 and: 'cmHandleQueries returns a non null query result'
58 cmHandleQueries.queryCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])]
59 and: 'CmHandleQueries returns cmHandles with the relevant query result'
60 cmHandleQueries.combineCmHandleQueries(*_) >> ['PNFDemo1': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo1'), 'PNFDemo3': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo3')]
61 when: 'the query is executed for both cm handle ids and details'
62 def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
63 def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
64 then: 'the correct expected cm handles ids are returned'
65 returnedCmHandlesJustIds == ['PNFDemo1', 'PNFDemo3'] as Set
66 and: 'the correct ncmp service cm handles are returned'
67 returnedCmHandlesWithData.stream().map(CmHandle -> CmHandle.cmHandleId).collect(Collectors.toSet()) == ['PNFDemo1', 'PNFDemo3'] as Set
70 def 'Retrieve cm handles with cpsPath where #scenario.'() {
71 given: 'a cmHandleWithCpsPath condition property'
72 def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
73 def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']])
74 cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
75 and: 'cmHandleQueries throws a path parsing exception'
76 cmHandleQueries.queryCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> { throw thrownException }
77 when: 'the query is executed for both cm handle ids and details'
78 objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
79 objectUnderTest.queryCmHandles(cmHandleQueryParameters)
80 then: 'a data validation exception is thrown'
81 thrown(expectedException)
82 where: 'the following data is used'
83 scenario | thrownException || expectedException
84 'a PathParsingException is thrown' | new PathParsingException('some message', 'some details') || DataValidationException
85 'any other Exception is thrown' | new DataInUseException('some message', 'some details') || DataInUseException
88 def 'Query cm handles with public properties when combined with empty modules query result.'() {
89 given: 'a public properties condition property'
90 def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
91 def conditionProperties = createConditionProperties('hasAllProperties', [['some-property-key': 'some-property-value']])
92 cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
93 and: 'CmHandleQueries returns cmHandles with the relevant query result'
94 cmHandleQueries.combineCmHandleQueries(*_) >> ['PNFDemo1': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo1'), 'PNFDemo3': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo3')]
95 when: 'the query is executed for both cm handle ids and details'
96 def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
97 def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
98 then: 'the correct expected cm handles ids are returned'
99 returnedCmHandlesJustIds == ['PNFDemo1', 'PNFDemo3'] as Set
100 and: 'the correct cm handle data objects are returned'
101 returnedCmHandlesWithData.stream().map(dataNode -> dataNode.cmHandleId).collect(Collectors.toSet()) == ['PNFDemo1', 'PNFDemo3'] as Set
104 def 'Retrieve cm handles with module names when #scenario from query.'() {
105 given: 'a modules condition property'
106 def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
107 def conditionProperties = createConditionProperties('hasAllModules', [['moduleName': 'some-module-name']])
108 cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
109 and: 'null is returned from the state and public property queries'
110 cmHandleQueries.combineCmHandleQueries(*_) >> null
111 and: '#scenario from the modules query'
112 mockInventoryPersistence.getCmHandleIdsWithGivenModules(*_) >> cmHandleIdsFromService
113 and: 'the same cmHandles are returned from the persistence service layer'
114 cmHandleIdsFromService.size() * mockInventoryPersistence.getDataNode(*_) >> returnedCmHandles
115 when: 'the query is executed for both cm handle ids and details'
116 def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
117 def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
118 then: 'the correct expected cm handles ids are returned'
119 returnedCmHandlesJustIds == cmHandleIdsFromService as Set
120 and: 'the correct cm handle data objects are returned'
121 returnedCmHandlesWithData.stream().map(dataNode -> dataNode.cmHandleId).collect(Collectors.toSet()) == cmHandleIdsFromService as Set
122 where: 'the following data is used'
123 scenario | cmHandleIdsFromService | returnedCmHandles
124 'One anchor returned' | ['some-cmhandle-id'] | someCmHandleDataNode
125 'No anchors are returned' | [] | null
128 def 'Retrieve cm handles with combined queries when #scenario.'() {
129 given: 'all condition properties used'
130 def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
131 def conditionPubProps = createConditionProperties('hasAllProperties', [['some-property-key': 'some-property-value']])
132 def conditionModules = createConditionProperties('hasAllModules', [['moduleName': 'some-module-name']])
133 def conditionState = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']])
134 cmHandleQueryParameters.setCmHandleQueryParameters([conditionPubProps, conditionModules, conditionState])
135 and: 'cmHandles are returned from the state and public property combined queries'
136 cmHandleQueries.combineCmHandleQueries(*_) >> combinedQueryMap
137 and: 'cmHandles are returned from the module names query'
138 mockInventoryPersistence.getCmHandleIdsWithGivenModules(['some-module-name']) >> anchorsForModuleQuery
139 and: 'cmHandleQueries returns a datanode result'
140 2 * cmHandleQueries.queryCmHandleDataNodesByCpsPath(*_) >> someCmHandleDataNode
141 when: 'the query is executed for both cm handle ids and details'
142 def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
143 def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
144 then: 'the correct expected cm handles ids are returned'
145 returnedCmHandlesJustIds == expectedCmHandleIds as Set
146 and: 'the correct cm handle data objects are returned'
147 returnedCmHandlesWithData.stream().map(dataNode -> dataNode.cmHandleId).collect(Collectors.toSet()) == expectedCmHandleIds as Set
148 where: 'the following data is used'
149 scenario | combinedQueryMap | anchorsForModuleQuery || expectedCmHandleIds
150 'combined and modules queries intersect' | ['PNFDemo1': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo1')] | ['PNFDemo1', 'PNFDemo2'] || ['PNFDemo1']
151 'only module query results exist' | [:] | ['PNFDemo1', 'PNFDemo2'] || []
152 'only combined query results exist' | ['PNFDemo1': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo1'), 'PNFDemo2': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo2')] | [] || []
153 'neither queries return results' | [:] | [] || []
154 'none intersect' | ['PNFDemo1': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo1')] | ['PNFDemo2'] || []
157 def 'Retrieve cm handles when the query is empty.'() {
158 given: 'We use an empty query'
159 def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
160 and: 'the inventory persistence returns the dmi registry datanode with just ids'
161 mockInventoryPersistence.getDataNode("/dmi-registry", FetchDescendantsOption.FETCH_DIRECT_CHILDREN_ONLY) >> [dmiRegistry]
162 and: 'the inventory persistence returns the dmi registry datanode with data'
163 mockInventoryPersistence.getDataNode("/dmi-registry") >> [dmiRegistry]
164 when: 'the query is executed for both cm handle ids and details'
165 def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
166 def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
167 then: 'the correct expected cm handles are returned'
168 returnedCmHandlesJustIds == ['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4'] as Set
169 returnedCmHandlesWithData.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4'] as Set
173 def 'Retrieve all CMHandleIds for empty query parameters' () {
174 given: 'We query without any parameters'
175 def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
176 and: 'the inventoryPersistence returns all four CmHandleIds'
177 mockInventoryPersistence.getDataNode(*_) >> [dmiRegistry]
178 when: 'the query executed'
179 def resultSet = objectUnderTest.queryCmHandleIdsForInventory(cmHandleQueryParameters)
180 then: 'the size of the result list equals the size of all cmHandleIds.'
181 resultSet.size() == 4
184 def 'Retrieve CMHandleIds when #scenario.' () {
185 given: 'a query object created with #condition'
186 def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
187 def conditionProperties = createConditionProperties(conditionName, [['some-key': 'some-value']])
188 cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
189 and: 'the inventoryPersistence returns different CmHandleIds'
190 partiallyMockedCmHandleQueries.queryCmHandlePublicProperties(*_) >> cmHandlesWithMatchingPublicProperties
191 partiallyMockedCmHandleQueries.queryCmHandleAdditionalProperties(*_) >> cmHandlesWithMatchingPrivateProperties
192 when: 'the query executed'
193 def result = objectUnderTestSpy.queryCmHandleIdsForInventory(cmHandleQueryParameters)
194 then: 'the expected number of results are returned.'
195 assert result.size() == expectedCmHandleIdsSize
196 where: 'the following data is used'
197 scenario | conditionName | cmHandlesWithMatchingPublicProperties | cmHandlesWithMatchingPrivateProperties || expectedCmHandleIdsSize
198 'all properties, only public matching' | 'hasAllProperties' | queryResultCmHandleMap | null || 2
199 'all properties, no matching cm handles' | 'hasAllProperties' | [:] | [:] || 0
200 'additional properties, some matching cm handles' | 'hasAllAdditionalProperties' | [:] | queryResultCmHandleMap || 2
201 'additional properties, no matching cm handles' | 'hasAllAdditionalProperties' | null | [:] || 0
204 def 'Retrieve CMHandleIds by different DMI properties with #scenario.' () {
205 given: 'a query object created with dmi plugin as condition'
206 def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
207 def conditionProperties = createConditionProperties('cmHandleWithDmiPlugin', [['some-key': 'some-value']])
208 cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
209 and: 'the inventoryPersistence returns different CmHandleIds'
210 partiallyMockedCmHandleQueries.getCmHandlesByDmiPluginIdentifier(*_) >> cmHandleQueryResult
211 when: 'the query executed'
212 def result = objectUnderTestSpy.queryCmHandleIdsForInventory(cmHandleQueryParameters)
213 then: 'the expected number of results are returned.'
214 assert result.size() == expectedCmHandleIdsSize
215 where: 'the following data is used'
216 scenario | cmHandleQueryResult || expectedCmHandleIdsSize
217 'some matches' | queryResultCmHandleMap.values() || 2
218 'no matches' | [] || 0
221 static def createCmHandleMap(cmHandleIds) {
222 def cmHandleMap = [:]
223 cmHandleIds.each{ cmHandleMap[it] = new NcmpServiceCmHandle(cmHandleId : it) }
227 def createConditionProperties(String conditionName, List<Map<String, String>> conditionParameters) {
228 return new ConditionProperties(conditionName : conditionName, conditionParameters : conditionParameters)
231 def static createDataNodeList(dataNodeIds) {
233 dataNodeIds.each{ dataNodes << new DataNode(xpath: "/dmi-registry/cm-handles[@id='${it}']", leaves: ['id':it]) }