3822ba3f95eb6c5f3aca96ab6cce74e6bfd666ca
[cps.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * Copyright (c) 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.cmavc
22
23 import com.fasterxml.jackson.databind.ObjectMapper
24 import org.onap.cps.api.CpsAnchorService
25 import org.onap.cps.api.CpsDataService
26 import org.onap.cps.api.model.Anchor
27 import org.onap.cps.cpspath.parser.CpsPathUtil
28 import org.onap.cps.ncmp.events.avc1_0_0.AvcEvent
29 import org.onap.cps.ncmp.events.avc1_0_0.Data
30 import org.onap.cps.ncmp.events.avc1_0_0.DatastoreChanges
31 import org.onap.cps.ncmp.events.avc1_0_0.Edit
32 import org.onap.cps.ncmp.events.avc1_0_0.IetfYangPatchYangPatch
33 import org.onap.cps.ncmp.events.avc1_0_0.PushChangeUpdate
34 import org.onap.cps.utils.JsonObjectMapper
35 import org.onap.cps.utils.YangParser
36 import spock.lang.Specification
37
38 class CmAvcEventServiceSpec extends Specification {
39
40     def mockCpsDataService = Mock(CpsDataService)
41     def mockCpsAnchorService = Mock(CpsAnchorService)
42     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
43     def mockYangParser = Mock(YangParser)
44
45     def static NO_TIMESTAMP = null
46     def static NO_XPATH = ''
47
48     def objectUnderTest = new CmAvcEventService(
49         mockCpsDataService,
50         mockCpsAnchorService,
51         jsonObjectMapper,
52         mockYangParser
53     )
54
55     def cmHandleId = 'test-cmhandle-id'
56     def sampleJson = '{"some-data": "test-data"}'
57
58     def 'process CREATE operation'() {
59         given: 'An edit with CREATE operation'
60             def testAvcEventForCreate = testAvcEvent('create', NO_XPATH)
61         when: 'The AVC event is processed'
62             objectUnderTest.processCmAvcEvent(cmHandleId, testAvcEventForCreate)
63         then: 'data is saved via cps service'
64             1 * mockCpsDataService.saveData(_, cmHandleId, sampleJson, NO_TIMESTAMP)
65     }
66
67     def 'Process UPDATE operation'() {
68         given: 'An edit with UPDATE operation and a valid target path'
69             def targetPath = '/test/path'
70             def anchor = Mock(Anchor)
71             mockCpsAnchorService.getAnchor(_, cmHandleId) >> anchor
72             mockYangParser.getCpsPathFromRestConfStylePath(anchor, targetPath) >> '/parsed/cps/path'
73             def testAvcEventForUpdate = testAvcEvent('update', targetPath)
74         when: 'The AVC event is processed'
75             objectUnderTest.processCmAvcEvent(cmHandleId, testAvcEventForUpdate)
76         then: 'Data node and descendants are updated via CPS service'
77             1 * mockCpsDataService.updateDataNodeAndDescendants(_, cmHandleId, _, sampleJson, NO_TIMESTAMP, _)
78     }
79
80     def 'Process PATCH operation'() {
81         given: 'An edit with PATCH operation and a valid target path'
82             def targetPath = '/test/path'
83             def anchor = Mock(Anchor)
84             mockCpsAnchorService.getAnchor(_, cmHandleId) >> anchor
85             mockYangParser.getCpsPathFromRestConfStylePath(anchor, targetPath) >> '/parsed/cps/path'
86             def testAvcEventForPatch = testAvcEvent('patch', targetPath)
87         when: 'The AVC event is processed'
88             objectUnderTest.processCmAvcEvent(cmHandleId, testAvcEventForPatch)
89         then: 'Node leaves are updated via CPS service'
90             1 * mockCpsDataService.updateNodeLeaves(_, cmHandleId, _, sampleJson, NO_TIMESTAMP, _)
91     }
92
93     def 'Process DELETE operation with target'() {
94         given: 'An edit with DELETE operation and a specific target path'
95             def targetPath = '/test/path'
96             def anchor = Mock(Anchor)
97             mockCpsAnchorService.getAnchor(_, cmHandleId) >> anchor
98             mockYangParser.getCpsPathFromRestConfStylePath(anchor, targetPath) >> '/parsed/cps/path'
99             def testAvcEventForDelete = testAvcEvent('delete', targetPath)
100         when: 'The AVC event is processed'
101             objectUnderTest.processCmAvcEvent(cmHandleId, testAvcEventForDelete)
102         then: 'Data node is deleted at the given path'
103             1 * mockCpsDataService.deleteDataNode(_, cmHandleId, '/parsed/cps/path', NO_TIMESTAMP)
104     }
105
106     def 'Process DELETE operation with no target (delete all)'() {
107         given: 'An edit with DELETE operation and no target'
108             def testAvcEventForDelete = testAvcEvent('delete', NO_XPATH)
109         when: 'The AVC event is processed'
110             objectUnderTest.processCmAvcEvent(cmHandleId, testAvcEventForDelete)
111         then: 'All data nodes for the cmHandleId are deleted'
112             1 * mockCpsDataService.deleteDataNodes(_, cmHandleId, NO_TIMESTAMP)
113         where: 'following targets are used'
114             target << [null, '']
115     }
116
117     def 'Resolve parent xpath correctly: #scenario'() {
118         expect: 'Parent xpath is resolved as expected'
119             assert objectUnderTest.resolveParentNodeXpath(inputXpath) == expectedXpath
120         where: 'following scenarios are used'
121             scenario                        | inputXpath   || expectedXpath
122             'when parentXpath is empty'     | ''           || CpsPathUtil.ROOT_NODE_XPATH
123             'when parentXpath is not empty' | '/test/path' || '/test/path'
124     }
125
126     def testAvcEvent(operation, targetXpath) {
127         new AvcEvent(
128             data: new Data(
129                 pushChangeUpdate: new PushChangeUpdate(
130                     datastoreChanges: new DatastoreChanges(
131                         ietfYangPatchYangPatch: new IetfYangPatchYangPatch(
132                             patchId: 'test-patch-id',
133                             edit: [
134                                 new Edit(
135                                     operation: operation,
136                                     editId: 'test-edit-id',
137                                     target: targetXpath,
138                                     value: sampleJson
139                                 )
140                             ]
141                         )
142                     )
143                 )
144             )
145         )
146     }
147
148 }