2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2022 Nordix Foundation
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.spi.utils
23 import com.google.common.util.concurrent.TimeLimiter
24 import com.google.common.util.concurrent.UncheckedExecutionException
25 import org.hibernate.HibernateException
26 import org.hibernate.Transaction
27 import org.onap.cps.spi.config.CpsSessionFactory
28 import org.onap.cps.spi.entities.AnchorEntity
29 import org.onap.cps.spi.exceptions.SessionManagerException
30 import org.onap.cps.spi.repository.AnchorRepository
31 import org.onap.cps.spi.repository.DataspaceRepository
32 import spock.lang.Specification
33 import org.hibernate.Session
34 import java.util.concurrent.ExecutionException
36 class SessionManagerSpec extends Specification {
38 def mockCpsSessionFactory = Mock(CpsSessionFactory)
39 def spiedTimeLimiterProvider = Spy(TimeLimiterProvider)
40 def mockDataspaceRepository = Mock(DataspaceRepository)
41 def mockAnchorRepository = Mock(AnchorRepository)
42 def mockSession1 = Mock(Session)
43 def mockSession2 = Mock(Session)
44 def mockTransaction1 = Mock(Transaction)
45 def mockTransaction2 = Mock(Transaction)
47 def objectUnderTest = new SessionManager(mockCpsSessionFactory, spiedTimeLimiterProvider, mockDataspaceRepository, mockAnchorRepository)
50 mockSession1.getTransaction() >> mockTransaction1
51 mockSession2.getTransaction() >> mockTransaction2
54 def 'Lock anchor entity with #exceptionDuringTest exception.'() {
55 given: 'a dummy session'
56 objectUnderTest.sessionMap.put('dummy-session', mockSession1)
57 and: 'the anchor name can be resolved'
58 def mockAnchorEntity = Mock(AnchorEntity)
59 mockAnchorEntity.getId() > 456
60 mockAnchorRepository.getByDataspaceAndName(_, _) >> mockAnchorEntity
61 and: 'timeLimiter throws an #exceptionDuringTest exception'
62 def mockTimeLimiter = Mock(TimeLimiter)
63 spiedTimeLimiterProvider.getTimeLimiter(_) >> mockTimeLimiter
64 mockTimeLimiter.callWithTimeout(*_) >> { throw exceptionDuringTest }
65 when: 'session tries to acquire anchor lock'
66 objectUnderTest.lockAnchor('dummy-session', 'some-dataspace', 'some-anchor', 123L)
67 then: 'a session manager exception is thrown with the expected detail'
68 def thrown = thrown(SessionManagerException)
69 thrown.details.contains(expectedExceptionDetail)
71 exceptionDuringTest || expectedExceptionDetail
72 new InterruptedException() || 'interrupted'
73 new UncheckedExecutionException() || 'aborted'
76 def 'Close a session' () {
77 given: 'a session in the session map'
78 objectUnderTest.sessionMap.putAll([testSessionId1:mockSession1])
79 when: 'the session manager closes session'
80 objectUnderTest.closeSession('testSessionId1', commit)
81 then: 'commit or rollback is called on the transaction as appropriate'
83 1 * mockTransaction1.commit()
85 1 * mockTransaction1.rollback()
87 and: 'the correct session is closed'
88 1 * mockSession1.close()
90 commit << [SessionManager.WITH_COMMIT, SessionManager.WITH_ROLLBACK]
93 def 'Close session that does not exist.'() {
94 when: 'attempt to close session that does not exist'
95 objectUnderTest.closeSession('unknown session id', SessionManager.WITH_COMMIT)
96 then: 'a session manager exception is thrown with the unknown id in the details'
97 def thrown = thrown(SessionManagerException)
98 assert thrown.details.contains('unknown session id')
101 def 'Hibernate exception while closing session.'() {
102 given: 'a test session in session map'
103 objectUnderTest.sessionMap.put('testSessionId', mockSession1)
104 and: 'an hibernate exception when closing that session'
105 def hibernateException = new HibernateException('test')
106 mockSession1.close() >> { throw hibernateException }
107 when: 'attempt to close session'
108 objectUnderTest.closeSession('testSessionId', SessionManager.WITH_COMMIT)
109 then: 'a session manager exception is thrown with the session id in the details'
110 def thrown = thrown(SessionManagerException)
111 assert thrown.details.contains('testSessionId')
112 and: 'the original exception as cause'
113 assert thrown.cause == hibernateException
116 def 'Attempt to lock anchor entity with session Id that does not exist'() {
117 when: 'attempt to acquire anchor lock with session that does not exist'
118 objectUnderTest.lockAnchor('unknown session id', '', '', 123L)
119 then: 'a session manager exception is thrown with the unknown id in the details'
120 def thrown = thrown(SessionManagerException)
121 thrown.details.contains('unknown session id')
124 def 'Close all sessions in shutdown.'() {
125 given: 'sessions that holds transactions in the session map'
126 objectUnderTest.sessionMap.putAll([testSessionId1:mockSession1, otherSessionId:mockSession2])
127 when: 'shutdown method to close all sessions is called'
128 objectUnderTest.closeAllSessionsInShutdown()
129 then: 'commit is called on each transaction'
130 1 * mockTransaction1.rollback()
131 1 * mockTransaction2.rollback()
132 and: 'each session is closed'
133 1 * mockSession1.close()
134 1 * mockSession2.close()
135 then: 'session factory is closed'
136 1 * mockCpsSessionFactory.closeSessionFactory()