5e2c29b8288ff36576ad1cc22ffe047c7dcd2ac9
[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.cmnotificationsubscription.cache
22
23 import com.fasterxml.jackson.databind.ObjectMapper
24 import io.cloudevents.CloudEvent
25 import io.cloudevents.core.builder.CloudEventBuilder
26 import org.apache.kafka.clients.consumer.ConsumerRecord
27 import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus
28 import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails
29 import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmDataJobSubscriptionPersistenceService
30 import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.NcmpInEvent
31 import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
32 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
33 import org.onap.cps.ncmp.utils.TestUtils
34 import org.onap.cps.ncmp.utils.events.MessagingBaseSpec
35 import org.onap.cps.utils.JsonObjectMapper
36 import org.spockframework.spring.SpringBean
37 import org.springframework.beans.factory.annotation.Autowired
38 import org.springframework.boot.test.context.SpringBootTest
39
40 import static org.onap.cps.ncmp.utils.events.CloudEventMapper.toTargetEvent
41
42 @SpringBootTest(classes = [ObjectMapper, JsonObjectMapper])
43 class DmiCacheHandlerSpec extends MessagingBaseSpec {
44
45     @Autowired
46     JsonObjectMapper jsonObjectMapper
47     @Autowired
48     ObjectMapper objectMapper
49     @SpringBean
50     InventoryPersistence mockInventoryPersistence = Mock(InventoryPersistence)
51     @SpringBean
52     CmDataJobSubscriptionPersistenceService mockCmSubscriptionPersistenceService = Mock(CmDataJobSubscriptionPersistenceService)
53
54     def testCache = [:]
55     def objectUnderTest = new DmiCacheHandler(mockCmSubscriptionPersistenceService, testCache, mockInventoryPersistence)
56
57     NcmpInEvent ncmpInEvent
58     def yangModelCmHandle1 = new YangModelCmHandle(id:'ch1',dmiServiceName:'dmi-1')
59     def yangModelCmHandle2 = new YangModelCmHandle(id:'ch2',dmiServiceName:'dmi-2')
60     def yangModelCmHandle3 = new YangModelCmHandle(id:'ch3',dmiServiceName:'dmi-1')
61     def yangModelCmHandle4 = new YangModelCmHandle(id:'ch4',dmiServiceName:'dmi-2')
62
63     def setup() {
64         setUpTestEvent()
65         initialiseMockInventoryPersistenceResponses()
66     }
67
68     def 'Load CM subscription event to cache with predicates'() {
69         given: 'a subscription event with id'
70             def subscriptionId = ncmpInEvent.getData().getSubscriptionId()
71         and: 'list of predicates'
72             def predicates = ncmpInEvent.getData().getPredicates()
73         when: 'subscription is loaded to cache with predicates'
74             objectUnderTest.add(subscriptionId, predicates)
75         then: 'the number of entries in cache is correct'
76             assert testCache.size() == 1
77         and: 'the cache contains the correct entries'
78             assert testCache.containsKey(subscriptionId)
79     }
80
81     def 'Load CM subscription event to cache with dmi subscription details per dmi'() {
82         given: 'a subscription event with id'
83             def subscriptionId = ncmpInEvent.getData().getSubscriptionId()
84         and: 'dmi subscription details per dmi'
85             def dmiSubscriptionsPerDmi = [:]
86         when: 'subscription is loaded to cache with dmi subscription details per dmi'
87             objectUnderTest.add(subscriptionId, dmiSubscriptionsPerDmi)
88         then: 'the number of entries in cache is correct'
89             assert testCache.size() == 1
90         and: 'the cache contains the correct entries'
91             assert testCache.containsKey(subscriptionId)
92         and: 'the entry for the subscription ID matches the provided DMI subscription details'
93             assert testCache.get(subscriptionId) == dmiSubscriptionsPerDmi
94     }
95
96     def 'Get cache entry via subscription id'() {
97         given: 'the cache contains value for some-id'
98             testCache.put('some-id',[:])
99         when: 'the get method is called'
100             def result = objectUnderTest.get('some-id')
101         then: 'correct value is returned as expected'
102             assert result == [:]
103     }
104
105     def 'Remove accepted and rejected entries from cache via subscription id'() {
106         given: 'a map as the value for cache entry for some-id'
107             def testMap = [:]
108             testMap.put("dmi-1",
109                 new DmiCmSubscriptionDetails([],CmSubscriptionStatus.ACCEPTED))
110             testMap.put("dmi-2",
111                 new DmiCmSubscriptionDetails([],CmSubscriptionStatus.REJECTED))
112             testMap.put("dmi-3",
113                 new DmiCmSubscriptionDetails([],CmSubscriptionStatus.PENDING))
114             testCache.put("test-id", testMap)
115             assert testCache.get("test-id").size() == 3
116         when: 'the method to remove accepted and rejected entries for test-id is called'
117             objectUnderTest.removeAcceptedAndRejectedDmiSubscriptionEntries("test-id")
118         then: 'all entries with status accepted/rejected are no longer present for test-id'
119             testCache.get("test-id").each { key, testResultMap ->
120                 assert testResultMap.cmSubscriptionStatus != CmSubscriptionStatus.ACCEPTED
121                     || testResultMap.cmSubscriptionStatus != CmSubscriptionStatus.REJECTED
122             }
123         and: 'the size of the map for cache entry test-id is as expected'
124             assert testCache.get("test-id").size() == 1
125     }
126
127     def 'Create map for DMI cm notification subscription per DMI service name'() {
128         given: 'list of predicates from the create subscription event'
129             def predicates = ncmpInEvent.getData().getPredicates()
130         when: 'method to create map of DMI cm notification subscription per DMI service name is called'
131             def result = objectUnderTest.createDmiSubscriptionsPerDmi(predicates)
132         then: 'the result size of resulting map is correct to the number of DMIs'
133             assert result.size() == 2
134         and: 'the cache objects per DMI exists'
135             def resultMapForDmi1 = result.get('dmi-1')
136             def resultMapForDmi2 = result.get('dmi-2')
137             assert resultMapForDmi1 != null
138             assert resultMapForDmi2 != null
139         and: 'the size of predicates in each object is correct'
140             assert resultMapForDmi1.dmiCmSubscriptionPredicates.size() == 2
141             assert resultMapForDmi2.dmiCmSubscriptionPredicates.size() == 2
142         and: 'the subscription status in each object is correct'
143             assert resultMapForDmi1.cmSubscriptionStatus.toString() == 'PENDING'
144             assert resultMapForDmi2.cmSubscriptionStatus.toString() == 'PENDING'
145         and: 'the target cmHandles for each predicate is correct'
146             assert resultMapForDmi1.dmiCmSubscriptionPredicates[0].targetCmHandleIds == ['ch1'].toSet()
147             assert resultMapForDmi1.dmiCmSubscriptionPredicates[1].targetCmHandleIds == ['ch3'].toSet()
148
149             assert resultMapForDmi2.dmiCmSubscriptionPredicates[0].targetCmHandleIds == ['ch2'].toSet()
150             assert resultMapForDmi2.dmiCmSubscriptionPredicates[1].targetCmHandleIds == ['ch4'].toSet()
151         and: 'the list of xpath for each is correct'
152             assert resultMapForDmi1.dmiCmSubscriptionPredicates[0].xpaths
153                 && resultMapForDmi2.dmiCmSubscriptionPredicates[0].xpaths == ['/x1/y1', 'x2/y2'].toSet()
154
155             assert resultMapForDmi1.dmiCmSubscriptionPredicates[1].xpaths
156                 && resultMapForDmi2.dmiCmSubscriptionPredicates[1].xpaths == ['/x3/y3', 'x4/y4'].toSet()
157     }
158
159     def 'Get map for cm handle IDs by DMI service name'() {
160         given: 'the predicate from the test request CM subscription event'
161             def targetFilter = ncmpInEvent.getData().getPredicates().get(0).getTargetFilter()
162         when: 'the method to group all target CM handles by DMI service name is called'
163             def mapOfCMHandleIDsByDmi = objectUnderTest.groupTargetCmHandleIdsByDmi(targetFilter)
164         then: 'the size of the resulting map is correct'
165             assert mapOfCMHandleIDsByDmi.size() == 2
166         and: 'the values in the map is as expected'
167             assert mapOfCMHandleIDsByDmi.get('dmi-1') == ['ch1'].toSet()
168             assert mapOfCMHandleIDsByDmi.get('dmi-2') == ['ch2'].toSet()
169     }
170
171     def 'Update subscription status in cache per DMI service name'() {
172         given: 'populated cache'
173             def predicates = ncmpInEvent.getData().getPredicates()
174             def subscriptionId = ncmpInEvent.getData().getSubscriptionId()
175             objectUnderTest.add(subscriptionId, predicates)
176         when: 'subscription status per dmi is updated in cache'
177             objectUnderTest.updateDmiSubscriptionStatus(subscriptionId,'dmi-1', CmSubscriptionStatus.ACCEPTED)
178         then: 'verify status has been updated in cache'
179             def predicate = testCache.get(subscriptionId)
180             assert predicate.get('dmi-1').cmSubscriptionStatus == CmSubscriptionStatus.ACCEPTED
181     }
182
183     def 'Persist Cache into database per dmi'() {
184         given: 'populated cache'
185             def predicates = ncmpInEvent.getData().getPredicates()
186             def subscriptionId = ncmpInEvent.getData().getSubscriptionId()
187             objectUnderTest.add(subscriptionId, predicates)
188         when: 'subscription is persisted in database'
189             objectUnderTest.persistIntoDatabasePerDmi(subscriptionId,'dmi-1')
190         then: 'persistence service is called the correct number of times per dmi'
191             2 * mockCmSubscriptionPersistenceService.addSubscription(*_)
192     }
193
194     def 'Remove subscription from database per dmi'() {
195         given: 'populated cache'
196             def predicates = ncmpInEvent.getData().getPredicates()
197             def subscriptionId = ncmpInEvent.getData().getSubscriptionId()
198             objectUnderTest.add(subscriptionId, predicates)
199         when: 'subscription is persisted in database'
200             objectUnderTest.removeFromDatabase(subscriptionId,'dmi-1')
201         then: 'persistence service is called the correct number of times per dmi'
202             2 * mockCmSubscriptionPersistenceService.removeSubscription(*_)
203     }
204
205     def setUpTestEvent(){
206         def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json')
207         def testEventSent = jsonObjectMapper.convertJsonString(jsonData, NcmpInEvent.class)
208         def testCloudEventSent = CloudEventBuilder.v1()
209             .withData(objectMapper.writeValueAsBytes(testEventSent))
210             .withId('subscriptionCreated')
211             .withType('subscriptionCreated')
212             .withSource(URI.create('some-resource'))
213             .withExtension('correlationid', 'test-cmhandle1').build()
214         def consumerRecord = new ConsumerRecord<String, CloudEvent>('topic-name', 0, 0, 'event-key', testCloudEventSent)
215         def cloudEvent = consumerRecord.value()
216
217         ncmpInEvent = toTargetEvent(cloudEvent, NcmpInEvent.class);
218     }
219
220     def initialiseMockInventoryPersistenceResponses(){
221         mockInventoryPersistence.getYangModelCmHandles(['ch1','ch2'])
222             >> [yangModelCmHandle1, yangModelCmHandle2]
223
224         mockInventoryPersistence.getYangModelCmHandles(['ch3','ch4'])
225             >> [yangModelCmHandle3, yangModelCmHandle4]
226     }
227
228 }