From 0b80343610a215f26a7d764cc849f8e9ca44fea0 Mon Sep 17 00:00:00 2001 From: emaclee Date: Fri, 6 May 2022 10:36:29 +0100 Subject: Add graceful shutdown for Session Manager Introduce singleton session manager Add unit test for session manager Issue-Id: CPS-898 Signed-off-by: emaclee Change-Id: Iaf91f1aa6c1ebfe0ab907e7f7d80a01e940a0fdd --- .../spi/impl/CpsDataPersistenceServiceSpec.groovy | 2 +- .../spi/utils/SessionManagerIntegrationSpec.groovy | 18 ++++- .../onap/cps/spi/utils/SessionManagerSpec.groovy | 77 ++++++++++++++++------ 3 files changed, 75 insertions(+), 22 deletions(-) (limited to 'cps-ri/src/test/groovy/org/onap') diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy index b37f471e76..a96b6aff9b 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy @@ -127,7 +127,7 @@ class CpsDataPersistenceServiceSpec extends Specification { when: 'close session method is called with session ID as parameter' objectUnderTest.closeSession(someSessionId) then: 'the session manager method to close session is invoked with parameter' - 1 * mockSessionManager.closeSession(someSessionId) + 1 * mockSessionManager.closeSession(someSessionId, mockSessionManager.WITH_COMMIT) } def 'Lock anchor.'(){ diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy index 9b58c8bc32..a1f6d580fd 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy @@ -20,6 +20,7 @@ package org.onap.cps.spi.utils +import org.onap.cps.spi.config.CpsSessionFactory import org.onap.cps.spi.exceptions.SessionManagerException import org.onap.cps.spi.impl.CpsPersistenceSpecBase import org.springframework.beans.factory.annotation.Autowired @@ -32,6 +33,9 @@ class SessionManagerIntegrationSpec extends CpsPersistenceSpecBase{ @Autowired SessionManager objectUnderTest + @Autowired + CpsSessionFactory cpsSessionFactory + def sessionId def shortTimeoutForTesting = 200L @@ -40,7 +44,7 @@ class SessionManagerIntegrationSpec extends CpsPersistenceSpecBase{ } def cleanup(){ - objectUnderTest.closeSession(sessionId) + objectUnderTest.closeSession(sessionId, objectUnderTest.WITH_COMMIT) } @Sql([CLEAR_DATA, SET_DATA]) @@ -62,8 +66,18 @@ class SessionManagerIntegrationSpec extends CpsPersistenceSpecBase{ def thrown = thrown(SessionManagerException) thrown.message.contains('Timeout') then: 'when the other session holding the lock is closed, lock can finally be acquired' - objectUnderTest.closeSession(otherSessionId) + objectUnderTest.closeSession(otherSessionId, objectUnderTest.WITH_COMMIT) objectUnderTest.lockAnchor(sessionId,DATASPACE_NAME,ANCHOR_NAME1,shortTimeoutForTesting) } + @Sql([CLEAR_DATA, SET_DATA]) + def 'Lock anchor twice using the same session.'(){ + given: 'session that already holds an anchor lock' + objectUnderTest.lockAnchor(sessionId, DATASPACE_NAME, ANCHOR_NAME1, shortTimeoutForTesting) + when: 'same session tries to acquire same anchor lock' + objectUnderTest.lockAnchor(sessionId, DATASPACE_NAME, ANCHOR_NAME1, shortTimeoutForTesting) + then: 'no exception is thrown' + noExceptionThrown() + } + } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerSpec.groovy index a2df06ef0e..db766cd1f3 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerSpec.groovy @@ -23,28 +23,36 @@ package org.onap.cps.spi.utils import com.google.common.util.concurrent.TimeLimiter import org.hibernate.HibernateException import org.hibernate.Transaction +import org.onap.cps.spi.config.CpsSessionFactory import org.onap.cps.spi.entities.AnchorEntity import org.onap.cps.spi.exceptions.SessionManagerException import org.onap.cps.spi.repository.AnchorRepository import org.onap.cps.spi.repository.DataspaceRepository -import org.testcontainers.shaded.com.google.common.util.concurrent.UncheckedExecutionException import spock.lang.Specification import org.hibernate.Session - import java.util.concurrent.ExecutionException class SessionManagerSpec extends Specification { + def mockCpsSessionFactory = Mock(CpsSessionFactory) def spiedTimeLimiterProvider = Spy(TimeLimiterProvider) def mockDataspaceRepository = Mock(DataspaceRepository) def mockAnchorRepository = Mock(AnchorRepository) - def mockSession = Mock(Session) + def mockSession1 = Mock(Session) + def mockSession2 = Mock(Session) + def mockTransaction1 = Mock(Transaction) + def mockTransaction2 = Mock(Transaction) + + def objectUnderTest = new SessionManager(mockCpsSessionFactory, spiedTimeLimiterProvider, mockDataspaceRepository, mockAnchorRepository) - def objectUnderTest = new SessionManager(spiedTimeLimiterProvider, mockDataspaceRepository, mockAnchorRepository) + def setup(){ + mockSession1.getTransaction() >> mockTransaction1 + mockSession2.getTransaction() >> mockTransaction2 + } - def 'Lock anchor entity with #exceptionDuringTest exception.'(){ + def 'Lock anchor entity with #exceptionDuringTest exception.'() { given: 'a dummy session' - objectUnderTest.sessionMap.put('dummySession', mockSession) + objectUnderTest.sessionMap.put('dummy-session', mockSession1) and: 'the anchor name can be resolved' def mockAnchorEntity = Mock(AnchorEntity) mockAnchorEntity.getId() > 456 @@ -54,33 +62,49 @@ class SessionManagerSpec extends Specification { spiedTimeLimiterProvider.getTimeLimiter(_) >> mockTimeLimiter mockTimeLimiter.callWithTimeout(*_) >> { throw exceptionDuringTest } when: 'session tries to acquire anchor lock' - objectUnderTest.lockAnchor('dummySession', 'some-dataspace','some-anchor', 123L) + objectUnderTest.lockAnchor('dummy-session', 'some-dataspace', 'some-anchor', 123L) then: 'a session manager exception is thrown with the expected detail' def thrown = thrown(SessionManagerException) thrown.details.contains(expectedExceptionDetail) where: - exceptionDuringTest || expectedExceptionDetail - new InterruptedException() || 'interrupted' - new ExecutionException() || 'aborted' + exceptionDuringTest || expectedExceptionDetail + new InterruptedException() || 'interrupted' + new ExecutionException() || 'aborted' + } + + def 'Close a session' () { + given: 'a session in the session map' + objectUnderTest.sessionMap.putAll([testSessionId1:mockSession1]) + when: 'the session manager closes session' + objectUnderTest.closeSession('testSessionId1', commit) + then: 'commit or rollback is called on the transaction as appropriate' + if (commit) { + 1 * mockTransaction1.commit() + } else { + 1 * mockTransaction1.rollback() + } + and: 'the correct session is closed' + 1 * mockSession1.close() + where: + commit << [SessionManager.WITH_COMMIT, SessionManager.WITH_ROLLBACK] } def 'Close session that does not exist.'() { when: 'attempt to close session that does not exist' - objectUnderTest.closeSession('unknown session id') + objectUnderTest.closeSession('unknown session id', SessionManager.WITH_COMMIT) then: 'a session manager exception is thrown with the unknown id in the details' def thrown = thrown(SessionManagerException) assert thrown.details.contains('unknown session id') } def 'Hibernate exception while closing session.'() { - given: 'a test session with a transaction' - objectUnderTest.sessionMap.put('testSessionId', mockSession) - mockSession.getTransaction() >> Mock(Transaction) + given: 'a test session in session map' + objectUnderTest.sessionMap.put('testSessionId', mockSession1) and: 'an hibernate exception when closing that session' def hibernateException = new HibernateException('test') - mockSession.close() >> { throw hibernateException } + mockSession1.close() >> { throw hibernateException } when: 'attempt to close session' - objectUnderTest.closeSession('testSessionId') + objectUnderTest.closeSession('testSessionId', SessionManager.WITH_COMMIT) then: 'a session manager exception is thrown with the session id in the details' def thrown = thrown(SessionManagerException) assert thrown.details.contains('testSessionId') @@ -88,12 +112,27 @@ class SessionManagerSpec extends Specification { assert thrown.cause == hibernateException } - def 'Attempt to lock anchor entity with session Id that does not exists'(){ - when: 'attempt to acquire anchor lock with session that does not exists' - objectUnderTest.lockAnchor('unknown session id','','',123L) + def 'Attempt to lock anchor entity with session Id that does not exist'() { + when: 'attempt to acquire anchor lock with session that does not exist' + objectUnderTest.lockAnchor('unknown session id', '', '', 123L) then: 'a session manager exception is thrown with the unknown id in the details' def thrown = thrown(SessionManagerException) thrown.details.contains('unknown session id') } + def 'Close all sessions in shutdown.'() { + given: 'sessions that holds transactions in the session map' + objectUnderTest.sessionMap.putAll([testSessionId1:mockSession1, otherSessionId:mockSession2]) + when: 'shutdown method to close all sessions is called' + objectUnderTest.closeAllSessionsInShutdown() + then: 'commit is called on each transaction' + 1 * mockTransaction1.rollback() + 1 * mockTransaction2.rollback() + and: 'each session is closed' + 1 * mockSession1.close() + 1 * mockSession2.close() + then: 'session factory is closed' + 1 * mockCpsSessionFactory.closeSessionFactory() + } + } -- cgit 1.2.3-korg