ab4abea689c5a82b9473160b373b7d11b41c5dd1
[cps.git] /
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2024-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.data.policyexecutor
22
23
24 import com.fasterxml.jackson.databind.ObjectMapper
25 import org.onap.cps.ncmp.api.exceptions.ProvMnSException
26 import org.onap.cps.ncmp.impl.provmns.RequestParameters
27 import org.onap.cps.ncmp.impl.provmns.model.PatchItem
28 import org.onap.cps.ncmp.impl.provmns.model.ResourceOneOf
29 import org.onap.cps.utils.JsonObjectMapper
30 import spock.lang.Specification
31
32 import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE
33 import static org.onap.cps.ncmp.api.data.models.OperationType.DELETE
34 import static org.onap.cps.ncmp.api.data.models.OperationType.UPDATE
35
36 class OperationDetailsFactorySpec extends Specification {
37
38     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
39
40     OperationDetailsFactory objectUnderTest = new OperationDetailsFactory(jsonObjectMapper)
41
42     static def complexValueAsResource = new ResourceOneOf(id: 'myId', attributes: ['myAttribute1:myValue1', 'myAttribute2:myValue2'], objectClass: 'myClassName')
43     static def simpleValueAsResource = new ResourceOneOf(id: 'myId', attributes: ['simpleAttribute:1'], objectClass: 'myClassName')
44
45     def 'Build create operation details with all properties.'() {
46         given: 'request parameters and resource'
47             def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'my uri', className: 'class in uri', id: 'my id')
48             def resource = new ResourceOneOf(id: 'some resource id', objectClass: 'class in resource')
49         when: 'create operation details are built'
50             def result = objectUnderTest.buildCreateOperationDetails(CREATE, requestPathParameters, resource)
51         then: 'all details are correct'
52             assert result.targetIdentifier == 'my uri'
53             assert result.changeRequest.keySet()[0] == 'class in resource'
54             assert result.changeRequest['class in resource'][0].id == 'my id'
55     }
56
57     def 'Build replace operation details with all properties where class name in body is #scenario.'() {
58         given: 'request parameters and resource'
59             def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'my uri', className: 'class in uri', id: 'some id')
60             def resource = new ResourceOneOf(id: 'some resource id', objectClass: classNameInBody)
61         when: 'replace operation details are built'
62             def result = objectUnderTest.buildCreateOperationDetails(CREATE, requestPathParameters, resource)
63         then: 'all details are correct'
64             assert result.targetIdentifier == 'my uri'
65             assert result.changeRequest.keySet()[0] == expectedChangeRequestKey
66         where:
67             scenario    | classNameInBody || expectedChangeRequestKey
68             'populated' | 'class in body' || 'class in body'
69             'empty'     | ''              || 'class in uri'
70             'null'      | null            || 'class in uri'
71     }
72
73     def 'Build delete operation details with all properties'() {
74         given: 'request parameters'
75             def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'my uri', className: 'classNameInUri', id: 'myId')
76         when: 'delete operation details are built'
77             def result = objectUnderTest.buildDeleteOperationDetails(requestPathParameters.toTargetFdn())
78         then: 'all details are correct'
79             assert result.targetIdentifier == 'my uri/classNameInUri=myId'
80     }
81
82     def 'Single patch operation with #patchOperationType checks correct operation type.'() {
83         given: 'request parameters and single patch item'
84             def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'some uri', className: 'some class')
85             def resource = new ResourceOneOf(id: 'some resource id')
86             def patchItem = new PatchItem(op: patchOperationType, 'path':'some uri', value: resource)
87         when: 'operation details is created'
88             def result = objectUnderTest.buildOperationDetails(requestPathParameters, patchItem)
89         then: 'it has the correct operation type (for Policy Executor check)'
90             assert result.operation() == expectedPolicyExecutorOperationType.name()
91         where: 'following operations are used'
92             patchOperationType | expectedPolicyExecutorOperationType
93             'ADD'              | CREATE
94             'REPLACE'          | UPDATE
95             'REMOVE'           | DELETE
96     }
97
98     def 'Build policy executor patch operation details with single replace operation and #scenario.'() {
99         given: 'a requestParameter and a patchItem list'
100             def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'some uri', className: 'some class')
101             def pathItem = new PatchItem(op: 'REPLACE', 'path':"some uri${suffix}", value: value)
102         when: 'patch operation details are checked'
103             def result = objectUnderTest.buildOperationDetails(requestPathParameters, pathItem)
104         then: 'Attribute Value in operation is correct'
105             result.changeRequest.values()[0].attributes[0] == expectedAttributesValueInOperation
106         where: 'attributes are set using # or resource'
107             scenario                           | suffix                         | value                  || expectedAttributesValueInOperation
108             'set simple value using #'         | '#/attributes/simpleAttribute' | 1                      || [simpleAttribute:1]
109             'set simple value using resource'  | ''                             | simpleValueAsResource  || ['simpleAttribute:1']
110             'set complex value using resource' | ''                             | complexValueAsResource || ["myAttribute1:myValue1","myAttribute2:myValue2"]
111     }
112
113     def 'Build an attribute map with different depths of hierarchy with #scenario.'() {
114         given: 'a patch item with a path'
115             def patchItem = new PatchItem(op: 'REPLACE', 'path':path, value: 123)
116         when: 'transforming the attributes'
117             def hierarchyMap = objectUnderTest.createNestedMap(patchItem)
118         then: 'the map depth is equal to the expected number of attributes'
119             assert hierarchyMap.get(expectedAttributeName).toString() == expectedAttributeValue
120         where: 'simple and complex attributes are tested'
121             scenario                                   | path                                                             || expectedAttributeName || expectedAttributeValue
122             'set a simple attribute'                   | 'myUriLdnFirstPart#/attributes/simpleAttribute'                  || 'simpleAttribute'     || '123'
123             'set a simple attribute with a trailing /' | 'myUriLdnFirstPart#/attributes/simpleAttribute/'                 || 'simpleAttribute'     || '123'
124             'set a complex attribute'                  | 'myUriLdnFirstPart#/attributes/complexAttribute/simpleAttribute' || 'complexAttribute'    || '[simpleAttribute:123]'
125     }
126
127     def 'Attempt to Build Operation details with unsupported op (MOVE).'() {
128         given: 'a provMnsRequestParameter and a patchItem'
129             def path = new RequestParameters(uriLdnFirstPart: 'some uri', className: 'some class')
130             def patchItem = new PatchItem(op: 'MOVE', 'path':'some uri')
131         when: 'a build is attempted with an unsupported op'
132             objectUnderTest.buildOperationDetails(path, patchItem)
133         then: 'the result is as expected (exception thrown)'
134             def exceptionThrown = thrown(ProvMnSException)
135             assert exceptionThrown.title == 'Unsupported Patch Operation Type: move'
136     }
137
138     def 'Build policy executor create operation details from ProvMnS request parameters where objectClass in resource #scenario.'() {
139         given: 'a provMnsRequestParameter and a resource'
140             def requestPathParameters = new RequestParameters(uriLdnFirstPart: 'some uri', className: 'class in uri', id:'my id')
141             def resource = new ResourceOneOf(id: 'some resource id', objectClass: objectInResouce)
142         when: 'a configurationManagementOperation is created and converted to JSON'
143             def result = objectUnderTest.buildCreateOperationDetails(CREATE, requestPathParameters, resource)
144         then: 'the result is as expected (using json to compare)'
145             String expectedJsonString = '{"operation":"CREATE","targetIdentifier":"some uri","changeRequest":{"' + changeRequestClassReference + '":[{"id":"my id","attributes":null}]}}'
146             assert jsonObjectMapper.asJsonString(result) == expectedJsonString
147         where:
148             scenario    | objectInResouce     || changeRequestClassReference
149             'populated' | 'class in resource' || 'class in resource'
150             'empty'     | ''                  || 'class in uri'
151             'null'      | null                || 'class in uri'
152     }
153
154 }