8980d654903cd644adf46ca316540ca224045f66
[cps.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * Copyright (c) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
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
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  *  SPDX-License-Identifier: Apache-2.0
18  *  ============LICENSE_END=========================================================
19  */
20
21 package org.onap.cps.ncmp.impl.datajobs.subscription.ncmp
22
23 import static org.onap.cps.ncmp.impl.datajobs.subscription.models.CmSubscriptionStatus.ACCEPTED
24
25 import org.onap.cps.ncmp.impl.datajobs.subscription.client_to_ncmp.DataSelector
26 import org.onap.cps.ncmp.impl.datajobs.subscription.ncmp_to_dmi.DataJobSubscriptionDmiInEvent
27 import org.onap.cps.ncmp.impl.datajobs.subscription.dmi.DmiInEventMapper
28 import org.onap.cps.ncmp.impl.datajobs.subscription.dmi.EventProducer
29 import org.onap.cps.ncmp.impl.datajobs.subscription.utils.CmDataJobSubscriptionPersistenceService
30 import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
31 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
32 import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
33 import org.onap.cps.ncmp.impl.utils.JexParser
34 import spock.lang.Specification
35
36 class CmSubscriptionHandlerImplSpec extends Specification {
37
38     def mockCmSubscriptionPersistenceService = Mock(CmDataJobSubscriptionPersistenceService)
39     def mockDmiInEventMapper = Mock(DmiInEventMapper)
40     def mockDmiInEventProducer = Mock(EventProducer)
41     def mockInventoryPersistence = Mock(InventoryPersistence)
42     def mockAlternateIdMatcher = Mock(AlternateIdMatcher)
43
44     def objectUnderTest = new CmSubscriptionHandlerImpl(mockCmSubscriptionPersistenceService, mockDmiInEventMapper,
45         mockDmiInEventProducer, mockInventoryPersistence, mockAlternateIdMatcher)
46
47     def 'Process subscription CREATE request for new target [non existing]'() {
48         given: 'relevant subscription details'
49             def mySubId = 'dataJobId'
50             def myDataNodeSelectors = ['/parent[id="1"]'].toList()
51             def notificationTypes = []
52             def notificationFilter = ''
53             def dataSelector = new DataSelector(notificationTypes: notificationTypes, notificationFilter: notificationFilter)
54         and: 'alternate Id matcher returns cm handle id for given data node selector'
55             def fdn = getFdn(myDataNodeSelectors.iterator().next())
56             mockAlternateIdMatcher.getCmHandleId(fdn) >> 'myCmHandleId'
57         and: 'returns inactive data node selector(s)'
58             mockCmSubscriptionPersistenceService.getInactiveDataNodeSelectors(mySubId) >> ['/parent[id="1"]']
59         and: 'the inventory persistence service returns cm handle'
60             mockInventoryPersistence.getYangModelCmHandle('myCmHandleId') >> new YangModelCmHandle(dmiServiceName: 'myDmiService')
61         and: 'DMI in event mapper returns event'
62             def myDmiInEvent = new DataJobSubscriptionDmiInEvent()
63             mockDmiInEventMapper.toDmiInEvent(['myCmHandleId'], myDataNodeSelectors, notificationTypes, notificationFilter) >> myDmiInEvent
64         when: 'the method to process subscription create request is called'
65             objectUnderTest.processSubscriptionCreate(dataSelector, mySubId, myDataNodeSelectors)
66         then: 'the persistence service is called'
67             1 * mockCmSubscriptionPersistenceService.add(mySubId, '/parent[id="1"]')
68         and: 'the event is sent to correct DMI'
69             1 * mockDmiInEventProducer.send(mySubId, 'myDmiService', 'subscriptionCreateRequest', _)
70     }
71
72     def 'Process subscription CREATE request for new targets [non existing] to be sent to multiple DMIs'() {
73         given: 'relevant subscription details'
74             def mySubId = 'dataJobId'
75             def myDataNodeSelectors = [
76                 '/parent[id="forDmi1"]',
77                 '/parent[id="forDmi1"]/child',
78                 '/parent[id="forDmi2"]'].toList()
79             def someAttr1 = []
80             def someAttr2 = ''
81             def dataSelector = new DataSelector(notificationTypes: someAttr1, notificationFilter: someAttr2)
82         and: 'alternate Id matcher returns cm handle ids for given data node selectors'
83             def fdn1 = getFdn(myDataNodeSelectors.get(0))
84             def fdn2 = getFdn(myDataNodeSelectors.get(1))
85             def fdn3 = getFdn(myDataNodeSelectors.get(2))
86             mockAlternateIdMatcher.getCmHandleId(fdn1) >> 'myCmHandleId1'
87             mockAlternateIdMatcher.getCmHandleId(fdn2) >> 'myCmHandleId1'
88             mockAlternateIdMatcher.getCmHandleId(fdn3) >> 'myCmHandleId2'
89         and: 'returns inactive data node selector(s)'
90             mockCmSubscriptionPersistenceService.getInactiveDataNodeSelectors(mySubId) >> [
91                 '/parent[id="forDmi1"]',
92                 '/parent[id="forDmi1"]/child',
93                 '/parent[id="forDmi2"]']
94         and: 'the inventory persistence service returns cm handles with dmi information'
95             mockInventoryPersistence.getYangModelCmHandle('myCmHandleId1') >> new YangModelCmHandle(dmiServiceName: 'myDmiService1')
96             mockInventoryPersistence.getYangModelCmHandle('myCmHandleId2') >> new YangModelCmHandle(dmiServiceName: 'myDmiService2')
97         and: 'DMI in event mapper returns events'
98             def myDmiInEvent1 = new DataJobSubscriptionDmiInEvent()
99             def myDmiInEvent2 = new DataJobSubscriptionDmiInEvent()
100             mockDmiInEventMapper.toDmiInEvent(['myCmHandleId1'], ['/parent[id="forDmi1"]', '/parent[id="forDmi1"]/child'], someAttr1, someAttr2) >> myDmiInEvent1
101             mockDmiInEventMapper.toDmiInEvent(['myCmHandleId2'], ['/parent[id="forDmi2"]'], someAttr1, someAttr2) >> myDmiInEvent2
102         when: 'the method to process subscription create request is called'
103             objectUnderTest.processSubscriptionCreate(dataSelector, mySubId, myDataNodeSelectors)
104         then: 'the persistence service is called'
105             myDataNodeSelectors.each { dataNodeSelector ->
106                 1 * mockCmSubscriptionPersistenceService.add(_, dataNodeSelector)}
107         and: 'the event is sent to correct DMIs'
108             1 * mockDmiInEventProducer.send(mySubId, 'myDmiService1', 'subscriptionCreateRequest', myDmiInEvent1)
109             1 * mockDmiInEventProducer.send(mySubId, 'myDmiService2', 'subscriptionCreateRequest', myDmiInEvent2)
110     }
111
112     def 'Process subscription CREATE request for overlapping targets [non existing & existing]'() {
113         given: 'relevant subscription details'
114             def myNewSubId = 'newId'
115             def myDataNodeSelectors = ['/newDataNodeSelector[id=""]'].toList()
116             def dataSelector = new DataSelector(notificationTypes: [], notificationFilter: '')
117         and: 'alternate id matcher always returns a cm handle id'
118             mockAlternateIdMatcher.getCmHandleId(_) >> 'someCmHandleId'
119         and: 'the inventory persistence service returns cm handles with dmi information'
120             mockInventoryPersistence.getYangModelCmHandle(_) >> new YangModelCmHandle(dmiServiceName: 'myDmiService')
121         and: 'returns inactive data node selector(s)'
122             mockCmSubscriptionPersistenceService.getInactiveDataNodeSelectors(myNewSubId) >> inactiveDataNodeSelectors
123         when: 'the method to process subscription create request is called'
124             objectUnderTest.processSubscriptionCreate(dataSelector, myNewSubId, myDataNodeSelectors)
125         then: 'the persistence service is called'
126             1 * mockCmSubscriptionPersistenceService.add(_, myDataNodeSelectors.iterator().next())
127         and: 'the event is sent to correct DMIs'
128             expectedCallsToDmi * mockDmiInEventProducer.send(myNewSubId, 'myDmiService', 'subscriptionCreateRequest', _)
129         where: 'following data are used'
130             scenario                                           | inactiveDataNodeSelectors                                         || expectedCallsToDmi
131             'new target overlaps with ACCEPTED targets'        | []                                                                || 0
132             'new target overlaps with REJECTED targets'        | ['/existingDataNodeSelector[id=""]','/newDataNodeSelector[id=""]']|| 1
133             'new target overlaps with UNKNOWN targets'         | ['/existingDataNodeSelector[id=""]','/newDataNodeSelector[id=""]']|| 1
134             'new target does not overlap with existing targets'| ['/newDataNodeSelector[id=""]']                                   || 1
135     }
136
137     def 'Update subscription status to ACCEPTED: #scenario'() {
138         given: 'a subscription ID'
139             def mySubscriptionId = 'mySubId'
140         and: 'the persistence service returns all inactive data node selectors'
141             def myDataNodeSelectors = ['/myDataNodeSelector[id=""]'].asList()
142             mockCmSubscriptionPersistenceService.getInactiveDataNodeSelectors(mySubscriptionId) >> myDataNodeSelectors
143         and: 'alternate id matcher always returns a cm handle id'
144             mockAlternateIdMatcher.getCmHandleId(_) >> 'someCmHandleId'
145         and: 'the inventory persistence service returns a yang model with a dmi service name for the accepted subscription'
146             mockInventoryPersistence.getYangModelCmHandle(_) >>  new YangModelCmHandle(dmiServiceName: 'myDmi')
147         when: 'the method to update subscription status is called with status=ACCEPTED and dmi #dmiName'
148             objectUnderTest.updateCmSubscriptionStatus(mySubscriptionId, dmiName, ACCEPTED)
149         then: 'the persistence service to update subscription status is ONLY called for matching dmi name'
150             expectedCallsToPersistenceService * mockCmSubscriptionPersistenceService.updateCmSubscriptionStatus('/myDataNodeSelector[id=""]', ACCEPTED)
151         where: 'the following data are used'
152             scenario                          |dmiName        || expectedCallsToPersistenceService
153             'data node selector for "myDmi"'  |'myDmi'        || 1
154             'data node selector for other dmi'| 'someOtherDmi'|| 0
155     }
156
157
158     def getFdn(dataNodeSelector) {
159         return JexParser.extractFdnPrefix(dataNodeSelector).orElse("")
160     }
161 }