Merge "Performance Improvement: Insert Yang Resources"
[cps.git] / cps-service / src / test / groovy / org / onap / cps / notification / NotificationServiceSpec.groovy
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (c) 2021-2022 Bell Canada.
4  *  Modifications Copyright (C) 2022 Nordix Foundation
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.notification
23
24 import org.onap.cps.api.CpsAdminService
25 import org.onap.cps.config.AsyncConfig
26 import org.onap.cps.event.model.CpsDataUpdatedEvent
27 import org.onap.cps.spi.model.Anchor
28 import org.spockframework.spring.SpringBean
29 import org.spockframework.spring.SpringSpy
30 import org.springframework.beans.factory.annotation.Autowired
31 import org.springframework.boot.context.properties.EnableConfigurationProperties
32 import org.springframework.boot.test.context.SpringBootTest
33 import org.springframework.test.context.ContextConfiguration
34 import spock.lang.Shared
35 import spock.lang.Specification
36
37 import java.time.OffsetDateTime
38 import java.util.concurrent.TimeUnit
39
40 @SpringBootTest
41 @EnableConfigurationProperties
42 @ContextConfiguration(classes = [NotificationProperties, NotificationService, NotificationErrorHandler, AsyncConfig])
43 class NotificationServiceSpec extends Specification {
44
45     @SpringBean
46     NotificationPublisher mockNotificationPublisher = Mock()
47     @SpringBean
48     CpsDataUpdatedEventFactory mockCpsDataUpdatedEventFactory = Mock()
49     @SpringSpy
50     NotificationErrorHandler spyNotificationErrorHandler
51     @SpringSpy
52     NotificationProperties spyNotificationProperties
53     @SpringBean
54     CpsAdminService mockCpsAdminService = Mock()
55
56     @Autowired
57     NotificationService objectUnderTest
58
59     @Shared
60     def dataspaceName = 'my-dataspace-published'
61     @Shared
62     def anchorName = 'my-anchorname'
63     @Shared
64     def anchor = new Anchor('my-anchorname', 'my-dataspace-published', 'my-schemaset-name')
65     def myObservedTimestamp = OffsetDateTime.now()
66
67     def setup() {
68         mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor
69     }
70
71     def 'Skip sending notification when disabled.'() {
72         given: 'notification is disabled'
73             spyNotificationProperties.isEnabled() >> false
74         when: 'dataUpdatedEvent is received'
75             objectUnderTest.processDataUpdatedEvent(dataspaceName, anchorName, '/', Operation.CREATE, myObservedTimestamp)
76         then: 'the notification is not sent'
77             0 * mockNotificationPublisher.sendNotification(_)
78     }
79
80     def 'Send notification when enabled: #scenario.'() {
81         given: 'notification is enabled'
82             spyNotificationProperties.isEnabled() >> true
83         and: 'an anchor is in dataspace where #scenario'
84             def anchor = new Anchor('my-anchorname', dataspaceName, 'my-schemaset-name')
85         and: 'event factory can create event successfully'
86             def cpsDataUpdatedEvent = new CpsDataUpdatedEvent()
87             mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, Operation.CREATE) >>
88                     cpsDataUpdatedEvent
89         when: 'dataUpdatedEvent is received'
90             def future = objectUnderTest.processDataUpdatedEvent(dataspaceName, anchorName,
91                 '/', Operation.CREATE, myObservedTimestamp)
92         and: 'wait for async processing to complete'
93             future.get(10, TimeUnit.SECONDS)
94         then: 'async process completed successfully'
95             future.isDone()
96         and: 'notification is sent'
97             expectedSendNotificationCount * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent)
98         where:
99             scenario                               | dataspaceName            || expectedSendNotificationCount
100             'dataspace name does not match filter' | 'does-not-match-pattern' || 0
101             'dataspace name matches filter'        | 'my-dataspace-published' || 1
102     }
103
104     def '#scenario are changed with xpath #xpath and operation #operation'() {
105         given: 'notification is enabled'
106             spyNotificationProperties.isEnabled() >> true
107         and: 'event factory creates event if operation is #operation'
108             def cpsDataUpdatedEvent = new CpsDataUpdatedEvent()
109             mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, expectedOperationInEvent) >>
110                     cpsDataUpdatedEvent
111         when: 'dataUpdatedEvent is received for #xpath'
112             def future = objectUnderTest.processDataUpdatedEvent(dataspaceName, anchorName, xpath, operation, myObservedTimestamp)
113         and: 'wait for async processing to complete'
114             future.get(10, TimeUnit.SECONDS)
115         then: 'async process completed successfully'
116             future.isDone()
117         and: 'notification is sent'
118             1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent)
119         where:
120             scenario                                   | xpath           | operation            || expectedOperationInEvent
121             'Same event is sent when root nodes'       | ''              | Operation.CREATE || Operation.CREATE
122             'Same event is sent when root nodes'       | ''              | Operation.UPDATE || Operation.UPDATE
123             'Same event is sent when root nodes'       | ''              | Operation.DELETE || Operation.DELETE
124             'Same event is sent when root nodes'       | '/'             | Operation.CREATE || Operation.CREATE
125             'Same event is sent when root nodes'       | '/'             | Operation.UPDATE || Operation.UPDATE
126             'Same event is sent when root nodes'       | '/'             | Operation.DELETE || Operation.DELETE
127             'Same event is sent when container nodes'  | '/parent'       | Operation.CREATE || Operation.CREATE
128             'Same event is sent when container nodes'  | '/parent'       | Operation.UPDATE || Operation.UPDATE
129             'Same event is sent when container nodes'  | '/parent'       | Operation.DELETE || Operation.DELETE
130             'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.CREATE || Operation.UPDATE
131             'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.UPDATE || Operation.UPDATE
132             'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.DELETE || Operation.UPDATE
133     }
134
135     def 'Error handling in notification service.'() {
136         given: 'notification is enabled'
137             spyNotificationProperties.isEnabled() >> true
138         and: 'event factory can not create event successfully'
139             mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, Operation.CREATE) >>
140                     { throw new Exception("Could not create event") }
141         when: 'event is sent for processing'
142             def future = objectUnderTest.processDataUpdatedEvent(dataspaceName, anchorName, '/', Operation.CREATE, myObservedTimestamp)
143         and: 'wait for async processing to complete'
144             future.get(10, TimeUnit.SECONDS)
145         then: 'async process completed successfully'
146             future.isDone()
147         and: 'error is handled and not thrown to caller'
148             notThrown Exception
149             1 * spyNotificationErrorHandler.onException(_, _, _, '/', Operation.CREATE)
150     }
151 }