Registration Response for Update and Delete cmhandles operations
[cps.git] / cps-ri / src / test / groovy / org / onap / cps / spi / impl / CpsDataPersistenceServiceSpec.groovy
1 /*
2  * ============LICENSE_START=======================================================
3  * Copyright (c) 2021 Bell Canada.
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  * ============LICENSE_END=========================================================
17 */
18
19 package org.onap.cps.spi.impl
20
21 import com.fasterxml.jackson.databind.ObjectMapper
22 import org.hibernate.StaleStateException
23 import org.onap.cps.spi.FetchDescendantsOption
24 import org.onap.cps.spi.entities.FragmentEntity
25 import org.onap.cps.spi.exceptions.ConcurrencyException
26 import org.onap.cps.spi.exceptions.DataValidationException
27 import org.onap.cps.spi.model.DataNodeBuilder
28 import org.onap.cps.spi.repository.AnchorRepository
29 import org.onap.cps.spi.repository.DataspaceRepository
30 import org.onap.cps.spi.repository.FragmentRepository
31 import org.onap.cps.utils.JsonObjectMapper
32 import spock.lang.Specification
33
34
35 class CpsDataPersistenceServiceSpec extends Specification {
36
37     def mockDataspaceRepository = Mock(DataspaceRepository)
38     def mockAnchorRepository = Mock(AnchorRepository)
39     def mockFragmentRepository = Mock(FragmentRepository)
40     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
41
42     def objectUnderTest = new CpsDataPersistenceServiceImpl(
43             mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper)
44
45     def 'Handling of StaleStateException (caused by concurrent updates) during data node tree update.'() {
46
47         def parentXpath = 'parent-01'
48         def myDataspaceName = 'my-dataspace'
49         def myAnchorName = 'my-anchor'
50
51         given: 'data node object'
52             def submittedDataNode = new DataNodeBuilder()
53                     .withXpath(parentXpath)
54                     .withLeaves(['leaf-name': 'leaf-value'])
55                     .build()
56         and: 'fragment to be updated'
57             mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
58                 def fragmentEntity = new FragmentEntity()
59                 fragmentEntity.setXpath(parentXpath)
60                 fragmentEntity.setChildFragments(Collections.emptySet())
61                 return fragmentEntity
62             }
63         and: 'data node is concurrently updated by another transaction'
64             mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
65
66         when: 'attempt to update data node'
67             objectUnderTest.replaceDataNodeTree(myDataspaceName, myAnchorName, submittedDataNode)
68
69         then: 'concurrency exception is thrown'
70             def concurrencyException = thrown(ConcurrencyException)
71             assert concurrencyException.getDetails().contains(myDataspaceName)
72             assert concurrencyException.getDetails().contains(myAnchorName)
73             assert concurrencyException.getDetails().contains(parentXpath)
74     }
75
76     def 'Retrieving a data node with a property JSON value of #scenario'() {
77         given: 'a fragment with a property JSON value of #scenario'
78             mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
79                 new FragmentEntity(childFragments: Collections.emptySet(),
80                         attributes: "{\"some attribute\": ${dataString}}")
81             }
82         when: 'getting the data node represented by this fragment'
83             def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
84                     'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
85         then: 'the leaf is of the correct value and data type'
86             def attributeValue = dataNode.leaves.get('some attribute')
87             assert attributeValue == expectedValue
88             assert attributeValue.class == expectedDataClass
89         where: 'the following Data Type is passed'
90             scenario                              | dataString            || expectedValue     | expectedDataClass
91             'just numbers'                        | '15174'               || 15174             | Integer
92             'number with dot'                     | '15174.32'            || 15174.32          | Double
93             'number with 0 value after dot'       | '15174.0'             || 15174.0           | Double
94             'number with 0 value before dot'      | '0.32'                || 0.32              | Double
95             'number higher than max int'          | '2147483648'          || 2147483648        | Long
96             'just text'                           | '"Test"'              || 'Test'            | String
97             'number with exponent'                | '1.2345e5'            || 1.2345e5          | Double
98             'number higher than max int with dot' | '123456789101112.0'   || 123456789101112.0 | Double
99             'text and numbers'                    | '"String = \'1234\'"' || "String = '1234'" | String
100             'number as String'                    | '"12345"'             || '12345'           | String
101     }
102
103     def 'Retrieving a data node with invalid JSON'() {
104         given: 'a fragment with invalid JSON'
105             mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
106                 new FragmentEntity(childFragments: Collections.emptySet(), attributes: '{invalid json')
107             }
108         when: 'getting the data node represented by this fragment'
109             def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
110                 'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
111         then: 'a data validation exception is thrown'
112             thrown(DataValidationException)
113     }
114
115 }