Merge "Fix sonar code smells"
[cps.git] / cps-ncmp-service / src / test / groovy / org / onap / cps / ncmp / api / inventory / InventoryPersistenceSpec.groovy
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2022 Nordix Foundation
4  *  Modifications Copyright (C) 2022 Bell Canada
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.ncmp.api.inventory
23
24 import com.fasterxml.jackson.databind.ObjectMapper
25 import org.onap.cps.api.CpsDataService
26 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
27 import org.onap.cps.spi.CpsDataPersistenceService
28 import org.onap.cps.spi.FetchDescendantsOption
29 import org.onap.cps.spi.exceptions.DataValidationException
30 import org.onap.cps.spi.model.DataNode
31 import org.onap.cps.spi.model.DataNodeBuilder
32 import org.onap.cps.utils.JsonObjectMapper
33 import spock.lang.Shared
34 import spock.lang.Specification
35
36 import java.time.OffsetDateTime
37 import java.time.ZoneOffset
38 import java.time.format.DateTimeFormatter
39
40 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
41 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
42
43 class InventoryPersistenceSpec extends Specification {
44
45     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
46
47     def mockCpsDataService = Mock(CpsDataService)
48
49     def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
50
51
52     def objectUnderTest = new InventoryPersistence(spiedJsonObjectMapper, mockCpsDataService, mockCpsDataPersistenceService)
53
54     def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
55             .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
56
57     def cmHandleId = 'some-cm-handle'
58     def leaves = ["dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"]
59     def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']"
60
61     @Shared
62     def childDataNodesForCmHandleWithAllProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"]),
63                                                       new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
64
65     @Shared
66     def childDataNodesForCmHandleWithDMIProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"])]
67
68     @Shared
69     def childDataNodesForCmHandleWithPublicProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
70
71     @Shared
72     def childDataNodesForCmHandleWithState = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/state", leaves: ['cm-handle-state': 'ADVISED'])]
73
74     def "Retrieve CmHandle using datanode with #scenario."() {
75         given: 'the cps data service returns a data node from the DMI registry'
76             def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves)
77             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
78         when: 'retrieving the yang modelled cm handle'
79             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
80         then: 'the result has the correct id and service names'
81             result.id == cmHandleId
82             result.dmiServiceName == 'common service name'
83             result.dmiDataServiceName == 'data service name'
84             result.dmiModelServiceName == 'model service name'
85         and: 'the expected DMI properties'
86             result.dmiProperties == expectedDmiProperties
87             result.publicProperties == expectedPublicProperties
88         and: 'the state details are returned'
89             result.compositeState.cmHandleState == expectedCompositeState
90         where: 'the following parameters are used'
91             scenario                    | childDataNodes                                || expectedDmiProperties                               || expectedPublicProperties                              || expectedCompositeState
92             'no properties'             | []                                            || []                                                  || []                                                    || null
93             'DMI and public properties' | childDataNodesForCmHandleWithAllProperties    || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")] || null
94             'just DMI properties'       | childDataNodesForCmHandleWithDMIProperties    || [new YangModelCmHandle.Property("name1", "value1")] || []                                                    || null
95             'just public properties'    | childDataNodesForCmHandleWithPublicProperties || []                                                  || [new YangModelCmHandle.Property("name2", "value2")]   || null
96             'with state details'        | childDataNodesForCmHandleWithState            || []                                                  || []                                                    || CmHandleState.ADVISED
97     }
98
99     def "Retrieve CmHandle using datanode with invalid CmHandle id."() {
100         when: 'retrieving the yang modelled cm handle with an invalid id'
101             def result = objectUnderTest.getYangModelCmHandle('cm handle id with spaces')
102         then: 'a data validation exception is thrown'
103             thrown(DataValidationException)
104         and: 'the result is not returned'
105             result == null
106     }
107
108     def "Handling missing service names as null CPS-1043."() {
109         given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
110             def dataNode = new DataNode(childDataNodes:[], leaves: [:])
111             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
112         when: 'retrieving the yang modelled cm handle'
113             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
114         then: 'the service names ae returned as null'
115             result.dmiServiceName == null
116             result.dmiDataServiceName == null
117             result.dmiModelServiceName == null
118     }
119
120     def 'Get a Cm Handle Composite State'() {
121         given: 'a valid cm handle id'
122             def cmHandleId = 'Some-Cm-Handle'
123             def dataNode = new DataNode(leaves: ['cm-handle-state': 'ADVISED'])
124         and: 'cps data service returns a valid data node'
125             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
126                     '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']/state', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
127         when: 'get cm handle state is invoked'
128             def result = objectUnderTest.getCmHandleState(cmHandleId)
129         then: 'result has returned the correct cm handle state'
130             result.cmHandleState == CmHandleState.ADVISED
131     }
132
133     def 'Update Cm Handle with #scenario State'() {
134         given: 'a cm handle and a composite state'
135             def cmHandleId = 'Some-Cm-Handle'
136             def compositeState = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
137         when: 'update cm handle state is invoked with the #scenario state'
138             objectUnderTest.saveCmHandleState(cmHandleId, compositeState)
139         then: 'update node leaves is invoked with the correct params'
140             1 * mockCpsDataService.replaceNodeTree('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']', expectedJsonData, _ as OffsetDateTime)
141         where: 'the following states are used'
142             scenario | cmHandleState        || expectedJsonData
143             'READY'   | CmHandleState.READY  || '{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
144             'LOCKED'  | CmHandleState.LOCKED || '{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
145     }
146
147     def 'Get Cm Handles By State'() {
148         given: 'a cm handle state to query'
149             def cmHandleState = CmHandleState.ADVISED
150         and: 'cps data service returns a list of data nodes'
151             def dataNodes = [new DataNode()]
152             mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
153                     '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> dataNodes
154         when: 'get cm handles by state is invoked'
155             def result = objectUnderTest.getCmHandlesByState(cmHandleState)
156         then: 'the returned result is a list of data nodes returned by cps data service'
157             assert result == dataNodes
158     }
159
160     def 'Retrieve cm handle by cps path '() {
161         given: 'a cm handle state to query based on the cps path'
162             def cmHandleDataNode = new DataNode(xpath: 'xpath', leaves: ['cm-handle-state': 'LOCKED'])
163             def cpsPath = '//cps-path'
164         and: 'cps data service returns a valid data node'
165             mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
166                     cpsPath, OMIT_DESCENDANTS)
167                     >> Arrays.asList(cmHandleDataNode)
168         when: 'get cm handles by cps path is invoked'
169             def result = objectUnderTest.getCmHandlesByCpsPath(cpsPath)
170         then: 'the returned result is a list of data nodes returned by cps data service'
171             assert result.contains(cmHandleDataNode)
172     }
173
174 }