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
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.ncmp.impl.datajobs.subscription.ncmp
24 import org.onap.cps.ncmp.impl.datajobs.subscription.client_to_ncmp.DataSelector
25 import org.onap.cps.ncmp.impl.datajobs.subscription.ncmp_to_dmi.DataJobSubscriptionDmiInEvent
26 import org.onap.cps.ncmp.impl.datajobs.subscription.dmi.DmiInEventMapper
27 import org.onap.cps.ncmp.impl.datajobs.subscription.dmi.EventProducer
28 import org.onap.cps.ncmp.impl.datajobs.subscription.utils.CmDataJobSubscriptionPersistenceService
29 import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
30 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
31 import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
32 import org.onap.cps.ncmp.impl.utils.JexParser
33 import spock.lang.Specification
35 class CmSubscriptionHandlerImplSpec extends Specification {
37 def mockCmSubscriptionPersistenceService = Mock(CmDataJobSubscriptionPersistenceService)
38 def mockDmiInEventMapper = Mock(DmiInEventMapper)
39 def mockDmiInEventProducer = Mock(EventProducer)
40 def mockInventoryPersistence = Mock(InventoryPersistence)
41 def mockAlternateIdMatcher = Mock(AlternateIdMatcher)
43 def objectUnderTest = new CmSubscriptionHandlerImpl(mockCmSubscriptionPersistenceService, mockDmiInEventMapper,
44 mockDmiInEventProducer, mockInventoryPersistence, mockAlternateIdMatcher)
46 def 'Process subscription CREATE request for new target [non existing]'() {
47 given: 'relevant subscription details'
48 def mySubId = 'dataJobId'
49 def myDataNodeSelectors = ['/parent[id="1"]'].toList()
50 def notificationTypes = []
51 def notificationFilter = ''
52 def dataSelector = new DataSelector(notificationTypes: notificationTypes, notificationFilter: notificationFilter)
53 and: 'alternate Id matcher returns cm handle id for given data node selector'
54 def fdn = getFdn(myDataNodeSelectors.iterator().next())
55 mockAlternateIdMatcher.getCmHandleId(fdn) >> 'myCmHandleId'
56 and: 'returns inactive data node selector(s)'
57 mockCmSubscriptionPersistenceService.getInactiveDataNodeSelectors(mySubId) >> ['/parent[id="1"]']
58 and: 'the inventory persistence service returns cm handle'
59 mockInventoryPersistence.getYangModelCmHandle('myCmHandleId') >> new YangModelCmHandle(dmiServiceName: 'myDmiService')
60 and: 'DMI in event mapper returns event'
61 def myDmiInEvent = new DataJobSubscriptionDmiInEvent()
62 mockDmiInEventMapper.toDmiInEvent(['myCmHandleId'], myDataNodeSelectors, notificationTypes, notificationFilter) >> myDmiInEvent
63 when: 'the method to process subscription create request is called'
64 objectUnderTest.processSubscriptionCreate(dataSelector, mySubId, myDataNodeSelectors)
65 then: 'the persistence service is called'
66 1 * mockCmSubscriptionPersistenceService.add(mySubId, '/parent[id="1"]')
67 and: 'the event is sent to correct DMI'
68 1 * mockDmiInEventProducer.send(mySubId, 'myDmiService', 'subscriptionCreateRequest', _)
71 def 'Process subscription CREATE request for new targets [non existing] to be sent to multiple DMIs'() {
72 given: 'relevant subscription details'
73 def mySubId = 'dataJobId'
74 def myDataNodeSelectors = [
75 '/parent[id="forDmi1"]',
76 '/parent[id="forDmi1"]/child',
77 '/parent[id="forDmi2"]'].toList()
80 def dataSelector = new DataSelector(notificationTypes: someAttr1, notificationFilter: someAttr2)
81 and: 'alternate Id matcher returns cm handle ids for given data node selectors'
82 def fdn1 = getFdn(myDataNodeSelectors.get(0))
83 def fdn2 = getFdn(myDataNodeSelectors.get(1))
84 def fdn3 = getFdn(myDataNodeSelectors.get(2))
85 mockAlternateIdMatcher.getCmHandleId(fdn1) >> 'myCmHandleId1'
86 mockAlternateIdMatcher.getCmHandleId(fdn2) >> 'myCmHandleId1'
87 mockAlternateIdMatcher.getCmHandleId(fdn3) >> 'myCmHandleId2'
88 and: 'returns inactive data node selector(s)'
89 mockCmSubscriptionPersistenceService.getInactiveDataNodeSelectors(mySubId) >> [
90 '/parent[id="forDmi1"]',
91 '/parent[id="forDmi1"]/child',
92 '/parent[id="forDmi2"]']
93 and: 'the inventory persistence service returns cm handles with dmi information'
94 mockInventoryPersistence.getYangModelCmHandle('myCmHandleId1') >> new YangModelCmHandle(dmiServiceName: 'myDmiService1')
95 mockInventoryPersistence.getYangModelCmHandle('myCmHandleId2') >> new YangModelCmHandle(dmiServiceName: 'myDmiService2')
96 and: 'DMI in event mapper returns events'
97 def myDmiInEvent1 = new DataJobSubscriptionDmiInEvent()
98 def myDmiInEvent2 = new DataJobSubscriptionDmiInEvent()
99 mockDmiInEventMapper.toDmiInEvent(['myCmHandleId1'], ['/parent[id="forDmi1"]', '/parent[id="forDmi1"]/child'], someAttr1, someAttr2) >> myDmiInEvent1
100 mockDmiInEventMapper.toDmiInEvent(['myCmHandleId2'], ['/parent[id="forDmi2"]'], someAttr1, someAttr2) >> myDmiInEvent2
101 when: 'the method to process subscription create request is called'
102 objectUnderTest.processSubscriptionCreate(dataSelector, mySubId, myDataNodeSelectors)
103 then: 'the persistence service is called'
104 myDataNodeSelectors.each { dataNodeSelector ->
105 1 * mockCmSubscriptionPersistenceService.add(_, dataNodeSelector)}
106 and: 'the event is sent to correct DMIs'
107 1 * mockDmiInEventProducer.send(mySubId, 'myDmiService1', 'subscriptionCreateRequest', myDmiInEvent1)
108 1 * mockDmiInEventProducer.send(mySubId, 'myDmiService2', 'subscriptionCreateRequest', myDmiInEvent2)
111 def 'Process subscription CREATE request for overlapping targets [non existing & existing]'() {
112 given: 'relevant subscription details'
113 def myNewSubId = 'newId'
114 def myDataNodeSelectors = ['/newDataNodeSelector[id=""]'].toList()
115 def dataSelector = new DataSelector(notificationTypes: [], notificationFilter: '')
116 and: 'alternate id matcher always returns a cm handle id'
117 mockAlternateIdMatcher.getCmHandleId(_) >> 'someCmHandleId'
118 and: 'the inventory persistence service returns cm handles with dmi information'
119 mockInventoryPersistence.getYangModelCmHandle(_) >> new YangModelCmHandle(dmiServiceName: 'myDmiService')
120 and: 'returns inactive data node selector(s)'
121 mockCmSubscriptionPersistenceService.getInactiveDataNodeSelectors(myNewSubId) >> inactiveDataNodeSelectors
122 when: 'the method to process subscription create request is called'
123 objectUnderTest.processSubscriptionCreate(dataSelector, myNewSubId, myDataNodeSelectors)
124 then: 'the persistence service is called'
125 1 * mockCmSubscriptionPersistenceService.add(_, myDataNodeSelectors.iterator().next())
126 and: 'the event is sent to correct DMIs'
127 expectedCallsToDmi * mockDmiInEventProducer.send(myNewSubId, 'myDmiService', 'subscriptionCreateRequest', _)
128 where: 'following data are used'
129 scenario | inactiveDataNodeSelectors || expectedCallsToDmi
130 'new target overlaps with ACCEPTED targets' | [] || 0
131 'new target overlaps with REJECTED targets' | ['/existingDataNodeSelector[id=""]','/newDataNodeSelector[id=""]']|| 1
132 'new target overlaps with UNKNOWN targets' | ['/existingDataNodeSelector[id=""]','/newDataNodeSelector[id=""]']|| 1
133 'new target does not overlap with existing targets'| ['/newDataNodeSelector[id=""]'] || 1
136 def getFdn(dataNodeSelector) {
137 return JexParser.extractFdnPrefix(dataNodeSelector).orElse("")