Merge "Ensure Leaf value retains Integer type"
[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 org.hibernate.StaleStateException
22 import org.onap.cps.spi.FetchDescendantsOption
23 import org.onap.cps.spi.entities.FragmentEntity
24 import org.onap.cps.spi.exceptions.ConcurrencyException
25 import org.onap.cps.spi.model.DataNodeBuilder
26 import org.onap.cps.spi.repository.AnchorRepository
27 import org.onap.cps.spi.repository.DataspaceRepository
28 import org.onap.cps.spi.repository.FragmentRepository
29 import spock.lang.Specification
30
31
32 class CpsDataPersistenceServiceSpec extends Specification {
33
34     def mockDataspaceRepository = Mock(DataspaceRepository)
35     def mockAnchorRepository = Mock(AnchorRepository)
36     def mockFragmentRepository = Mock(FragmentRepository)
37
38     def objectUnderTest = new CpsDataPersistenceServiceImpl(
39             mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository)
40
41     def 'Handling of StaleStateException (caused by concurrent updates) during data node tree update.'() {
42
43         def parentXpath = 'parent-01'
44         def myDataspaceName = 'my-dataspace'
45         def myAnchorName = 'my-anchor'
46
47         given: 'data node object'
48             def submittedDataNode = new DataNodeBuilder()
49                     .withXpath(parentXpath)
50                     .withLeaves(['leaf-name': 'leaf-value'])
51                     .build()
52         and: 'fragment to be updated'
53             mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
54                 def fragmentEntity = new FragmentEntity()
55                 fragmentEntity.setXpath(parentXpath)
56                 fragmentEntity.setChildFragments(Collections.emptySet())
57                 return fragmentEntity
58             }
59         and: 'data node is concurrently updated by another transaction'
60             mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
61
62         when: 'attempt to update data node'
63             objectUnderTest.replaceDataNodeTree(myDataspaceName, myAnchorName, submittedDataNode)
64
65         then: 'concurrency exception is thrown'
66             def concurrencyException = thrown(ConcurrencyException)
67             assert concurrencyException.getDetails().contains(myDataspaceName)
68             assert concurrencyException.getDetails().contains(myAnchorName)
69             assert concurrencyException.getDetails().contains(parentXpath)
70     }
71
72     def 'Retrieving a data node with a property JSON value of #scenario'() {
73         given: 'a fragment with a property JSON value of #scenario'
74             mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
75                 new FragmentEntity(childFragments: Collections.emptySet(),
76                         attributes: "{\"some attribute\": ${dataString}}")
77             }
78         when: 'getting the data node represented by this fragment'
79             def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
80                     'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
81         then: 'the leaf is of the correct value and data type'
82             def attributeValue = dataNode.leaves.get('some attribute')
83             assert attributeValue == expectedValue
84             assert attributeValue.class == expectedDataClass
85         where: 'the following Data Type is passed'
86             scenario                              | dataString            || expectedValue     | expectedDataClass
87             'just numbers'                        | '15174'               || 15174             | Integer
88             'number with dot'                     | '15174.32'            || 15174.32          | Double
89             'number with 0 value after dot'       | '15174.0'             || 15174.0           | Double
90             'number with 0 value before dot'      | '0.32'                || 0.32              | Double
91             'number higher than max int'          | '2147483648'          || 2147483648        | Long
92             'just text'                           | '"Test"'              || 'Test'            | String
93             'number with exponent'                | '1.2345e5'            || 1.2345e5          | Double
94             'number higher than max int with dot' | '123456789101112.0'   || 123456789101112.0 | Double
95             'text and numbers'                    | '"String = \'1234\'"' || "String = '1234'" | String
96             'number as String'                    | '"12345"'             || '12345'           | String
97     }
98 }