Add Start and Stop sessions on JAVA API
[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.spi.utils.SessionManager
32 import org.onap.cps.utils.JsonObjectMapper
33 import spock.lang.Specification
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     def mockSessionManager = Mock(SessionManager)
42
43     def objectUnderTest = new CpsDataPersistenceServiceImpl(
44             mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper,mockSessionManager)
45
46     def 'Handling of StaleStateException (caused by concurrent updates) during data node tree update.'() {
47
48         def parentXpath = 'parent-01'
49         def myDataspaceName = 'my-dataspace'
50         def myAnchorName = 'my-anchor'
51
52         given: 'data node object'
53         def submittedDataNode = new DataNodeBuilder()
54                 .withXpath(parentXpath)
55                 .withLeaves(['leaf-name': 'leaf-value'])
56                 .build()
57         and: 'fragment to be updated'
58         mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
59             def fragmentEntity = new FragmentEntity()
60             fragmentEntity.setXpath(parentXpath)
61             fragmentEntity.setChildFragments(Collections.emptySet())
62             return fragmentEntity
63         }
64         and: 'data node is concurrently updated by another transaction'
65         mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
66
67         when: 'attempt to update data node'
68         objectUnderTest.replaceDataNodeTree(myDataspaceName, myAnchorName, submittedDataNode)
69
70         then: 'concurrency exception is thrown'
71         def concurrencyException = thrown(ConcurrencyException)
72         assert concurrencyException.getDetails().contains(myDataspaceName)
73         assert concurrencyException.getDetails().contains(myAnchorName)
74         assert concurrencyException.getDetails().contains(parentXpath)
75     }
76
77     def 'Retrieving a data node with a property JSON value of #scenario'() {
78         given: 'a fragment with a property JSON value of #scenario'
79         mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
80             new FragmentEntity(childFragments: Collections.emptySet(),
81                     attributes: "{\"some attribute\": ${dataString}}")
82         }
83         when: 'getting the data node represented by this fragment'
84         def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
85                 'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
86         then: 'the leaf is of the correct value and data type'
87         def attributeValue = dataNode.leaves.get('some attribute')
88         assert attributeValue == expectedValue
89         assert attributeValue.class == expectedDataClass
90         where: 'the following Data Type is passed'
91         scenario                              | dataString            || expectedValue     | expectedDataClass
92         'just numbers'                        | '15174'               || 15174             | Integer
93         'number with dot'                     | '15174.32'            || 15174.32          | Double
94         'number with 0 value after dot'       | '15174.0'             || 15174.0           | Double
95         'number with 0 value before dot'      | '0.32'                || 0.32              | Double
96         'number higher than max int'          | '2147483648'          || 2147483648        | Long
97         'just text'                           | '"Test"'              || 'Test'            | String
98         'number with exponent'                | '1.2345e5'            || 1.2345e5          | Double
99         'number higher than max int with dot' | '123456789101112.0'   || 123456789101112.0 | Double
100         'text and numbers'                    | '"String = \'1234\'"' || "String = '1234'" | String
101         'number as String'                    | '"12345"'             || '12345'           | String
102     }
103
104     def 'Retrieving a data node with invalid JSON'() {
105         given: 'a fragment with invalid JSON'
106         mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
107             new FragmentEntity(childFragments: Collections.emptySet(), attributes: '{invalid json')
108         }
109         when: 'getting the data node represented by this fragment'
110         def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
111                 'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
112         then: 'a data validation exception is thrown'
113         thrown(DataValidationException)
114     }
115
116     def 'start session'() {
117         when: 'start session'
118             objectUnderTest.startSession()
119         then: 'the session manager method to start session is invoked'
120             1 * mockSessionManager.startSession()
121     }
122
123     def 'close session'() {
124         given: 'session ID'
125             def someSessionId = 'someSessionId'
126         when: 'close session method is called with session ID as parameter'
127             objectUnderTest.closeSession(someSessionId)
128         then: 'the session manager method to close session is invoked with parameter'
129             1 * mockSessionManager.closeSession(someSessionId)
130     }
131 }