Update ONAP DMI version in NCMP
[cps.git] / cps-ncmp-service / src / test / groovy / org / onap / cps / ncmp / api / impl / events / cmsubscription / service / CmNotificationSubscriptionPersistenceServiceImplSpec.groovy
1 /*
2  * ============LICENSE_START=======================================================
3  * Copyright (c) 2024 Nordix Foundation.
4  * Modifications Copyright (C) 2024 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.ncmp.api.impl.events.cmsubscription.service
23
24 import org.onap.cps.utils.ContentType
25
26 import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID;
27 import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE;
28 import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH;
29 import org.onap.cps.api.CpsDataService
30 import org.onap.cps.api.CpsQueryService
31 import org.onap.cps.ncmp.api.impl.operations.DatastoreType
32 import org.onap.cps.spi.FetchDescendantsOption
33 import org.onap.cps.spi.model.DataNode
34 import org.onap.cps.utils.JsonObjectMapper
35 import com.fasterxml.jackson.databind.ObjectMapper
36 import spock.lang.Specification
37
38 class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification {
39
40     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
41     def mockCpsQueryService = Mock(CpsQueryService)
42     def mockCpsDataService = Mock(CpsDataService)
43
44     def objectUnderTest = new CmNotificationSubscriptionPersistenceServiceImpl(jsonObjectMapper, mockCpsQueryService, mockCpsDataService)
45
46     def 'Check ongoing cm subscription #scenario'() {
47         given: 'a valid cm subscription query'
48             def cpsPathQuery = "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-1']/filters/filter[@xpath='/cps/path']";
49         and: 'datanodes optionally returned'
50             1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
51                 cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNode
52         when: 'we check for an ongoing cm subscription'
53             def response = objectUnderTest.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/cps/path')
54         then: 'we get expected response'
55             assert response == isOngoingCmSubscription
56         where: 'following scenarios are used'
57             scenario                  | dataNode                                                                            || isOngoingCmSubscription
58             'valid datanodes present' | [new DataNode(xpath: '/cps/path', leaves: ['subscriptionIds': ['sub-1', 'sub-2']])] || true
59             'no datanodes present'    | []                                                                                  || false
60     }
61
62     def 'Checking uniqueness of incoming subscription ID'() {
63         given: 'a cps path with a subscription ID for querying'
64             def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted('some-sub')
65         and: 'relevant datanodes are returned'
66             1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >>
67                 dataNodes
68         when: 'a subscription ID is tested for uniqueness'
69             def result = objectUnderTest.isUniqueSubscriptionId('some-sub')
70         then: 'result is as expected'
71             assert result == isValidSubscriptionId
72         where: 'following scenarios are used'
73             scenario               | dataNodes        || isValidSubscriptionId
74             'datanodes present'    | [new DataNode()] || false
75             'no datanodes present' | []               || true
76     }
77
78     def 'Add new subscriber to an ongoing cm notification subscription'() {
79         given: 'a valid cm subscription path query'
80             def cpsPathQuery =CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y')
81         and: 'a dataNode exists for the given cps path query'
82             mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
83                 cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])]
84         when: 'the method to add/update cm notification subscription is called'
85             objectUnderTest.addCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1','/x/y', 'newSubId')
86         then: 'data service method to update list of subscribers is called once'
87             1 * mockCpsDataService.updateNodeLeaves(
88                 'NCMP-Admin',
89                 'cm-data-subscriptions',
90                 '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters',
91                 objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-1','newSubId']), _,ContentType.JSON)
92     }
93
94     def 'Add new cm notification subscription for #datastoreType'() {
95         given: 'a valid cm subscription path query'
96             def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y')
97             def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
98         and: 'a parent node xpath for the cm subscription path above'
99             def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles'
100         and: 'a datanode does not exist for cm subscription path query'
101             mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
102                 cmSubscriptionCpsPathQuery,
103                 FetchDescendantsOption.OMIT_DESCENDANTS) >> []
104         and: 'a datanode does not exist for the given cm handle subscription path query'
105             mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
106                 cmHandleForSubscriptionPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> []
107         and: 'subscription is mapped as JSON'
108             def subscriptionAsJson = '{"cm-handle":[{"id":"ch-1","filters":' +
109                 objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']) + '}]}'
110         when: 'the method to add/update cm notification subscription is called'
111             objectUnderTest.addCmNotificationSubscription(datastoreType, 'ch-1','/x/y', 'newSubId')
112         then: 'data service method to create new subscription for given subscriber is called once with the correct parameters'
113             1 * mockCpsDataService.saveData(
114                 'NCMP-Admin',
115                 'cm-data-subscriptions',
116                 parentNodeXpath.formatted(datastoreName),
117                 subscriptionAsJson,_, ContentType.JSON)
118         where:
119             scenario                  | datastoreType                          || datastoreName
120             'passthrough_running'     | DatastoreType.PASSTHROUGH_RUNNING      || "ncmp-datastore:passthrough-running"
121             'passthrough_operational' | DatastoreType.PASSTHROUGH_OPERATIONAL  || "ncmp-datastore:passthrough-operational"
122     }
123
124     def 'Add new cm notification subscription when xpath does not exist for existing subscription cm handle'() {
125         given: 'a valid cm subscription path query'
126             def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y')
127             def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
128         and: 'a parent node xpath for given cm handle for subscription path above'
129             def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles/cm-handle[@id=\'%s\']/filters'
130         and: 'a datanode does not exist for cm subscription path query'
131             mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
132                 cmSubscriptionCpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> []
133         and: 'a datanode exists for the given cm handle subscription path query'
134             mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
135                 cmHandleForSubscriptionPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode()]
136         when: 'the method to add/update cm notification subscription is called'
137             objectUnderTest.addCmNotificationSubscription(datastoreType, 'ch-1','/x/y', 'newSubId')
138         then: 'data service method to create new subscription for given subscriber is called once with the correct parameters'
139             1 * mockCpsDataService.saveListElements(
140                 'NCMP-Admin',
141                 'cm-data-subscriptions',
142                 parentNodeXpath.formatted(datastoreName, 'ch-1'),
143                 objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']),_)
144         where:
145             scenario                  | datastoreType                          || datastoreName
146             'passthrough_running'     | DatastoreType.PASSTHROUGH_RUNNING      || "ncmp-datastore:passthrough-running"
147             'passthrough_operational' | DatastoreType.PASSTHROUGH_OPERATIONAL  || "ncmp-datastore:passthrough-operational"
148     }
149
150     def 'Remove subscriber from a list of an ongoing cm notification subscription'() {
151         given: 'a subscription exists when queried'
152             def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y')
153             mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
154                 cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1', 'sub-2']])]
155         when: 'the subscriber is removed'
156             objectUnderTest.removeCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'sub-1')
157         then: 'the list of subscribers is updated'
158             1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'cm-data-subscriptions',
159                 '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters',
160                 objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-2']), _, ContentType.JSON)
161     }
162
163     def 'Removing last ongoing subscription for datastore and cmhandle and xpath'(){
164         given: 'a subscription exists when queried but has only 1 subscriber'
165             mockCpsQueryService.queryDataNodes(
166                 'NCMP-Admin',
167                 'cm-data-subscriptions',
168                 CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y'),
169                 FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])]
170         and: 'the #scenario'
171             mockCpsQueryService.queryDataNodes(
172                 'NCMP-Admin',
173                 'cm-data-subscriptions',
174                 CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted('ncmp-datastore:passthrough-running', 'ch-1'),
175                 FetchDescendantsOption.DIRECT_CHILDREN_ONLY) >> [new DataNode(childDataNodes: listOfChildNodes)]
176         when: 'that last ongoing subscription is removed'
177             objectUnderTest.removeCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'sub-1')
178         then: 'the subscription with empty subscriber list is removed'
179             1 * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions',
180                 '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters/filter[@xpath=\'/x/y\']',
181                 _)
182         and: 'method call to delete the cm handle is called the correct number of times'
183             numberOfCallsToDeleteCmHandle * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions',
184                 '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']',
185                 _)
186         where:
187             scenario                                                            | listOfChildNodes  || numberOfCallsToDeleteCmHandle
188             'cm handle in same datastore is used for other subscriptions'       | [new DataNode()]  || 0
189             'cm handle in same datastore is NOT used for other subscriptions'   | []                || 1
190     }
191
192 }