0264282672b40b1c1587e32e37b1fde4995021ca
[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 import com.fasterxml.jackson.core.JsonProcessingException
24 import com.fasterxml.jackson.databind.ObjectMapper
25 import org.onap.cps.ncmp.api.exceptions.NcmpException;
26 import org.onap.cps.ncmp.impl.provmns.RequestPathParameters;
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
34 class OperationDetailsFactorySpec extends Specification {
35
36     def spiedObjectMapper = Spy(ObjectMapper)
37     def jsonObjectMapper = new JsonObjectMapper(spiedObjectMapper)
38
39     OperationDetailsFactory objectUnderTest = new OperationDetailsFactory(jsonObjectMapper, spiedObjectMapper)
40
41     static def complexValueAsResource = new ResourceOneOf(id: 'myId', attributes: ['myAttribute1:myValue1', 'myAttribute2:myValue2'], objectClass: 'myClassName')
42     static def simpleValueAsResource = new ResourceOneOf(id: 'myId', attributes: ['simpleAttribute:1'], objectClass: 'myClassName')
43
44
45     def 'Build policy executor patch operation details from ProvMnS request parameters where #scenario.'() {
46         given: 'a provMnsRequestParameter and a patchItem list'
47             def path = new RequestPathParameters(uriLdnFirstPart: 'myUriLdnFirstPart', className: 'classNameInUri', id: 'myId')
48             def resource = new ResourceOneOf(id: 'someResourceId', attributes: ['someAttribute1:someValue1', 'someAttribute2:someValue2'], objectClass: classNameInBody)
49             def patchItemsList = [new PatchItem(op: 'ADD', 'path':'myUriLdnFirstPart', value: resource), new PatchItem(op: 'REPLACE', 'path':'myUriLdnFirstPart', value: resource), new PatchItem(op: 'REMOVE', 'path':'myUriLdnFirstPart'),]
50         when: 'patch operation details are created'
51             def result = objectUnderTest.buildPatchOperationDetails(path, patchItemsList)
52         then: 'the result contain 3 operations of the correct types in the correct order'
53             result.operations.size() == 3
54         and: 'note that Add and Replace both are defined using Create Operation Details'
55             assert result.operations[0] instanceof CreateOperationDetails
56             assert result.operations[1] instanceof CreateOperationDetails
57             assert result.operations[2] instanceof DeleteOperationDetails
58         and: 'the add operation target identifier is just the uri first part'
59             assert result.operations[0]['targetIdentifier'] == 'myUriLdnFirstPart'
60         and: 'the replace operation target identifier is just the uri first part'
61             assert result.operations[1]['targetIdentifier'] == 'myUriLdnFirstPart'
62         and: 'the replace change request has the correct class name'
63             assert result.operations[1].changeRequest.keySet()[0] == expectedChangeRequestKey
64         and: 'the delete operation target identifier includes the target class and id'
65             assert result.operations[2]['targetIdentifier'] == 'myUriLdnFirstPart/classNameInUri=myId'
66         where: 'the following class names are used in the body'
67             scenario                          | classNameInBody || expectedChangeRequestKey
68             'class name in body is populated' | 'myClass'       || 'myClass'
69             'class name in body  is empty'    | ''              || 'classNameInUri'
70             'class name in body  is null'     | null            || 'classNameInUri'
71     }
72
73     def 'Build policy executor patch operation details with single replace operation and #scenario.'() {
74         given: 'a requestParameter and a patchItem list'
75             def path = new RequestPathParameters(uriLdnFirstPart: 'myUriLdnFirstPart', className: 'myClassName', id: 'myId')
76             def pathItems = [new PatchItem(op: 'REPLACE', 'path':"myUriLdnFirstPart${suffix}", value: value)]
77         when: 'patch operation details are created'
78             def result = objectUnderTest.buildPatchOperationDetails(path, pathItems)
79         then: 'the result has the correct type'
80             assert result instanceof PatchOperationsDetails
81         and: 'the change request contains the correct attributes value'
82             assert result.operations[0]['changeRequest']['myClassName'][0]['attributes'].toString() == attributesValueInOperation
83         where: 'attributes are set using # or resource'
84             scenario                           | suffix                         | value                  || attributesValueInOperation
85             'set simple value using #'         | '#/attributes/simpleAttribute' | 1                      || '[simpleAttribute:1]'
86             'set simple value using resource'  | ''                             | simpleValueAsResource  || '[simpleAttribute:1]'
87             'set complex value using resource' | ''                             | complexValueAsResource || '[myAttribute1:myValue1, myAttribute2:myValue2]'
88     }
89
90     def 'Build an attribute map with different depths of hierarchy with #scenario.'() {
91         given: 'a patch item with a path'
92             def patchItem = new PatchItem(op: 'REPLACE', 'path':path, value: 123)
93         when: 'transforming the attributes'
94             def hierarchyMap = objectUnderTest.createNestedMap(patchItem)
95         then: 'the map depth is equal to the expected number of attributes'
96             assert hierarchyMap.get(expectedAttributeName).toString() == expectedAttributeValue
97         where: 'simple and complex attributes are tested'
98             scenario                                   | path                                                             || expectedAttributeName || expectedAttributeValue
99             'set a simple attribute'                   | 'myUriLdnFirstPart#/attributes/simpleAttribute'                  || 'simpleAttribute'     || '123'
100             'set a simple attribute with a trailing /' | 'myUriLdnFirstPart#/attributes/simpleAttribute/'                 || 'simpleAttribute'     || '123'
101             'set a complex attribute'                  | 'myUriLdnFirstPart#/attributes/complexAttribute/simpleAttribute' || 'complexAttribute'    || '[simpleAttribute:123]'
102     }
103
104     def 'Build policy executor patch operation details from ProvMnS request parameters with invalid op.'() {
105         given: 'a provMnsRequestParameter and a patchItem list'
106             def path = new RequestPathParameters(uriLdnFirstPart: 'myUriLdnFirstPart', className: 'someClassName', id: 'someId')
107             def patchItemsList = [new PatchItem(op: 'TEST', 'path':'myUriLdnFirstPart')]
108         when: 'a configurationManagementOperation is created and converted to JSON'
109             def result = objectUnderTest.buildPatchOperationDetails(path, patchItemsList)
110         then: 'the result is as expected (using json to compare)'
111             def expectedJsonString = '{"permissionId":"Some Permission Id","changeRequestFormat":"cm-legacy","operations":[]}'
112             assert expectedJsonString == jsonObjectMapper.asJsonString(result)
113     }
114
115     def 'Build policy executor create operation details from ProvMnS request parameters where #scenario.'() {
116         given: 'a provMnsRequestParameter and a resource'
117             def path = new RequestPathParameters(uriLdnFirstPart: 'myUriLdnFirstPart', className: 'someClassName', id: 'someId')
118             def resource = new ResourceOneOf(id: 'someResourceId', attributes: ['someAttribute1:someValue1', 'someAttribute2:someValue2'], objectClass: objectClass)
119         when: 'a configurationManagementOperation is created and converted to JSON'
120             def result = objectUnderTest.buildCreateOperationDetails(CREATE, path, resource)
121         then: 'the result is as expected (using json to compare)'
122             String expectedJsonString = '{"operation":"CREATE","targetIdentifier":"myUriLdnFirstPart","changeRequest":{"' + changeRequestClassReference + '":[{"id":"someId","attributes":["someAttribute1:someValue1","someAttribute2:someValue2"]}]}}'
123             assert jsonObjectMapper.asJsonString(result) == expectedJsonString
124         where:
125             scenario                   | objectClass        || changeRequestClassReference
126             'objectClass is populated' | 'someObjectClass'  || 'someObjectClass'
127             'objectClass is empty'     | ''                 || 'someClassName'
128             'objectClass is null'      | null               || 'someClassName'
129     }
130
131     def 'Build Policy Executor Operation Details with a exception during conversion'() {
132         given: 'a provMnsRequestParameter and a resource'
133             def path = new RequestPathParameters(uriLdnFirstPart: 'myUriLdnFirstPart', className: 'someClassName', id: 'someId')
134             def resource = new ResourceOneOf(id: 'myResourceId', attributes: ['someAttribute1:someValue1', 'someAttribute2:someValue2'])
135         and: 'json object mapper throws an exception'
136             def originalException = new JsonProcessingException('some-exception')
137             spiedObjectMapper.readValue(*_) >> {throw originalException}
138         when: 'a configurationManagementOperation is created and converted to JSON'
139             objectUnderTest.buildCreateOperationDetails(CREATE, path, resource)
140         then: 'the expected exception is throw and matches the original'
141             def thrown = thrown(NcmpException)
142             assert thrown.message.contains('Cannot convert Resource Object')
143             assert thrown.details.contains('some-exception')
144     }
145
146 }