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