Unable to change state from LOCKED to ADVISED
[cps.git] / cps-ncmp-service / src / test / groovy / org / onap / cps / ncmp / api / inventory / sync / SyncUtilsSpec.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.sync
23
24 import com.fasterxml.jackson.databind.JsonNode
25 import com.fasterxml.jackson.databind.ObjectMapper
26 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
27 import org.onap.cps.ncmp.api.impl.operations.DmiOperations
28 import org.onap.cps.ncmp.api.inventory.CmHandleState
29 import org.onap.cps.ncmp.api.inventory.CompositeState
30 import org.onap.cps.ncmp.api.inventory.InventoryPersistence
31 import org.onap.cps.ncmp.api.inventory.LockReasonCategory
32 import org.onap.cps.ncmp.api.inventory.SyncState
33 import org.onap.cps.spi.FetchDescendantsOption
34 import org.onap.cps.spi.model.DataNode
35 import org.onap.cps.utils.JsonObjectMapper
36 import org.springframework.http.HttpStatus
37 import org.springframework.http.ResponseEntity
38 import spock.lang.Shared
39 import spock.lang.Specification
40
41 class SyncUtilsSpec extends Specification{
42
43     def mockInventoryPersistence = Mock(InventoryPersistence)
44
45     def mockDmiDataOperations = Mock(DmiDataOperations)
46
47     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
48
49     def objectUnderTest = new SyncUtils(mockInventoryPersistence, mockDmiDataOperations, jsonObjectMapper)
50
51     @Shared
52     def dataNode = new DataNode(leaves: ['id': 'cm-handle-123'])
53
54     def 'Get an advised Cm-Handle where ADVISED cm handle #scenario'() {
55         given: 'the inventory persistence service returns a collection of data nodes'
56             mockInventoryPersistence.getCmHandlesByState(CmHandleState.ADVISED) >> dataNodeCollection
57         when: 'get advised cm handle is called'
58             objectUnderTest.getAnAdvisedCmHandle()
59         then: 'the returned data node collection is the correct size'
60             dataNodeCollection.size() == expectedDataNodeSize
61         and: 'get yang model cm handles is invoked the correct number of times'
62            expectedCallsToGetYangModelCmHandle * mockInventoryPersistence.getYangModelCmHandle('cm-handle-123')
63         where: 'the following scenarios are used'
64             scenario         | dataNodeCollection || expectedCallsToGetYangModelCmHandle | expectedDataNodeSize
65             'exists'         | [ dataNode ]       || 1                                   | 1
66             'does not exist' | [ ]                || 0                                   | 0
67
68     }
69
70     def 'Update Lock Reason, Details and Attempts where lock reason #scenario'() {
71         given: 'A locked state'
72            def compositeState = new CompositeState(lockReason: lockReason)
73         when: 'update cm handle details and attempts is called'
74             objectUnderTest.updateLockReasonDetailsAndAttempts(compositeState, LockReasonCategory.LOCKED_MISBEHAVING, 'new error message')
75         then: 'the composite state lock reason and details are updated'
76             assert compositeState.lockReason.lockReasonCategory == LockReasonCategory.LOCKED_MISBEHAVING
77             assert compositeState.lockReason.details == expectedDetails
78         where:
79             scenario         | lockReason                                                                                   || expectedDetails
80             'does not exist' | null                                                                                         || 'Attempt #1 failed: new error message'
81             'exists'         | CompositeState.LockReason.builder().details("Attempt #2 failed: some error message").build() || 'Attempt #3 failed: new error message'
82     }
83
84     def 'Get all locked Cm-Handle where Lock Reason is LOCKED_MISBEHAVING cm handle #scenario'() {
85         given: 'the cps (persistence service) returns a collection of data nodes'
86             mockInventoryPersistence.getCmHandleDataNodesByCpsPath(
87                     '//lock-reason[@reason="LOCKED_MISBEHAVING"]/ancestor::cm-handles',
88                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode ]
89         when: 'get locked Misbehaving cm handle is called'
90             def result = objectUnderTest.getLockedMisbehavingYangModelCmHandles()
91         then: 'the returned cm handle collection is the correct size'
92             result.size() == 1
93         and: 'the correct cm handle is returned'
94             result[0].id == 'cm-handle-123'
95     }
96
97     def 'Get a Cm-Handle where Operational Sync state is UnSynchronized and Cm-handle state is READY and #scenario'() {
98         given: 'the inventory persistence service returns a collection of data nodes'
99             mockInventoryPersistence.getCmHandlesByOperationalSyncState(SyncState.UNSYNCHRONIZED) >> unSynchronizedDataNodes
100             mockInventoryPersistence.getCmHandlesByIdAndState("cm-handle-123", CmHandleState.READY) >> readyDataNodes
101         when: 'get advised cm handle is called'
102             objectUnderTest.getAnUnSynchronizedReadyCmHandle()
103         then: 'the returned data node collection is the correct size'
104             readyDataNodes.size() == expectedDataNodeSize
105         and: 'get yang model cm handles is invoked the correct number of times'
106             expectedCallsToGetYangModelCmHandle * mockInventoryPersistence.getYangModelCmHandle('cm-handle-123')
107         where: 'the following scenarios are used'
108             scenario                             | unSynchronizedDataNodes | readyDataNodes || expectedCallsToGetYangModelCmHandle | expectedDataNodeSize
109             'exists'                             | [dataNode]              | [dataNode]     || 1                                   | 1
110             'unsynchronized exist but not ready' | [dataNode]              | []             || 0                                   | 0
111             'does not exist'                     | []                      | []             || 0                                   | 0
112     }
113
114     def 'Get resource data through DMI Operations #scenario'() {
115         given: 'the inventory persistence service returns a collection of data nodes'
116             def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
117             JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString);
118             def responseEntity = new ResponseEntity<>(jsonNode, HttpStatus.OK)
119             mockDmiDataOperations.getResourceDataFromDmi('cm-handle-123', DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, _) >> responseEntity
120         when: 'get resource data is called'
121             def result = objectUnderTest.getResourceData('cm-handle-123')
122         then: 'the returned data is correct'
123             result == jsonString
124     }
125 }