Moving and Renaming eexisting subscription impl
[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-2023 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     @SpringSpy
46     NotificationProperties spyNotificationProperties
47     @SpringBean
48     NotificationPublisher mockNotificationPublisher = Mock()
49     @SpringBean
50     CpsDataUpdatedEventFactory mockCpsDataUpdatedEventFactory = Mock()
51     @SpringSpy
52     NotificationErrorHandler spyNotificationErrorHandler
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(anchor, '/', 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) >> cpsDataUpdatedEvent
88         when: 'dataUpdatedEvent is received'
89             def future = objectUnderTest.processDataUpdatedEvent(anchor, '/', Operation.CREATE, myObservedTimestamp)
90         and: 'wait for async processing to complete'
91             future.get(10, TimeUnit.SECONDS)
92         then: 'async process completed successfully'
93             future.isDone()
94         and: 'notification is sent'
95             expectedSendNotificationCount * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent)
96         where:
97             scenario                               | dataspaceName            || expectedSendNotificationCount
98             'dataspace name does not match filter' | 'does-not-match-pattern' || 0
99             'dataspace name matches filter'        | 'my-dataspace-published' || 1
100     }
101
102     def '#scenario are changed with xpath #xpath and operation #operation'() {
103         given: 'notification is enabled'
104             spyNotificationProperties.isEnabled() >> true
105         and: 'event factory creates event if operation is #operation'
106             def cpsDataUpdatedEvent = new CpsDataUpdatedEvent()
107             mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, expectedOperationInEvent) >>
108                     cpsDataUpdatedEvent
109         when: 'dataUpdatedEvent is received for #xpath'
110             def future = objectUnderTest.processDataUpdatedEvent(anchor, xpath, operation, myObservedTimestamp)
111         and: 'wait for async processing to complete'
112             future.get(10, TimeUnit.SECONDS)
113         then: 'async process completed successfully'
114             future.isDone()
115         and: 'notification is sent'
116             1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent)
117         where:
118             scenario                                   | xpath           | operation            || expectedOperationInEvent
119             'Same event is sent when root nodes'       | ''              | Operation.CREATE || Operation.CREATE
120             'Same event is sent when root nodes'       | ''              | Operation.UPDATE || Operation.UPDATE
121             'Same event is sent when root nodes'       | ''              | Operation.DELETE || Operation.DELETE
122             'Same event is sent when root nodes'       | '/'             | Operation.CREATE || Operation.CREATE
123             'Same event is sent when root nodes'       | '/'             | Operation.UPDATE || Operation.UPDATE
124             'Same event is sent when root nodes'       | '/'             | Operation.DELETE || Operation.DELETE
125             'Same event is sent when container nodes'  | '/parent'       | Operation.CREATE || Operation.CREATE
126             'Same event is sent when container nodes'  | '/parent'       | Operation.UPDATE || Operation.UPDATE
127             'Same event is sent when container nodes'  | '/parent'       | Operation.DELETE || Operation.DELETE
128             'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.CREATE || Operation.UPDATE
129             'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.UPDATE || Operation.UPDATE
130             'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.DELETE || Operation.UPDATE
131     }
132
133     def 'Error handling in notification service.'() {
134         given: 'notification is enabled'
135             spyNotificationProperties.isEnabled() >> true
136         and: 'event factory can not create event successfully'
137             mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, Operation.CREATE) >>
138                     { throw new Exception("Could not create event") }
139         when: 'event is sent for processing'
140             def future = objectUnderTest.processDataUpdatedEvent(anchor, '/', Operation.CREATE, myObservedTimestamp)
141         and: 'wait for async processing to complete'
142             future.get(10, TimeUnit.SECONDS)
143         then: 'async process completed successfully'
144             future.isDone()
145         and: 'error is handled and not thrown to caller'
146             notThrown Exception
147             1 * spyNotificationErrorHandler.onException(_, _, _, '/', Operation.CREATE)
148     }
149
150     def 'Disabled Notification services'() {
151         given: 'a notification service that is disabled'
152             spyNotificationProperties.enabled >> false
153             NotificationService notificationService = new NotificationService(spyNotificationProperties, mockNotificationPublisher, mockCpsDataUpdatedEventFactory, spyNotificationErrorHandler, mockCpsAdminService)
154             notificationService.init()
155         expect: 'it will not send notifications'
156             assert notificationService.shouldSendNotification('') == false
157     }
158 }