/*
* ============LICENSE_START=======================================================
* feature-session-persistence
* ================================================================================
* Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============LICENSE_END=========================================================
*/
package org.onap.policy.drools.persistence;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.onap.policy.drools.persistence.EntityMgrTrans.EntityMgrException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EntityMgrTransTest {
private static final Logger logger = LoggerFactory.getLogger(PersistenceFeatureTest.class);
private static UserTransaction savetrans;
private UserTransaction trans;
private EntityManager mgr;
/**
* Configure properties for JTA.
*/
@BeforeClass
public static void setUpBeforeClass() {
System.setProperty("com.arjuna.ats.arjuna.objectstore.objectStoreDir", "target/tm");
System.setProperty("ObjectStoreEnvironmentBean.objectStoreDir", "target/tm");
savetrans = EntityMgrTrans.getUserTrans();
}
@AfterClass
public static void tearDownAfterClass() {
EntityMgrTrans.setUserTrans(savetrans);
}
/**
* Creates a mock transaction and entity manager. Resets the "userTrans" field of the
* class under test.
*
* @throws Exception if an error occurs
*/
@Before
public void setUp() throws Exception {
trans = mock(UserTransaction.class);
mgr = mock(EntityManager.class);
when(trans.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE);
EntityMgrTrans.setUserTrans(trans);
}
/**
* Verifies that the constructor starts a transaction, but does not do anything extra
* before being closed.
*
* @throws Exception if an error occurs
*/
@Test
public void testEntityMgrTrans_Inactive() throws Exception {
EntityMgrTrans emt = new EntityMgrTrans(mgr);
// verify that transaction was started
verify(trans).begin();
verify(mgr).joinTransaction();
// verify not closed, committed, or rolled back yet
verify(trans, never()).commit();
verify(trans, never()).rollback();
verify(mgr, never()).close();
emt.close();
}
/**
* Verifies that the constructor does not start a transaction, because one is already
* active.
*
* @throws Exception if an error occurs
*/
@Test
public void testEntityMgrTrans_Active() throws Exception {
when(trans.getStatus()).thenReturn(Status.STATUS_ACTIVE);
EntityMgrTrans emt = new EntityMgrTrans(mgr);
// verify that transaction was not re-started started
verify(trans, never()).begin();
// verify that transaction was joined
verify(mgr).joinTransaction();
// verify not closed, committed, or rolled back yet
verify(trans, never()).commit();
verify(trans, never()).rollback();
verify(mgr, never()).close();
emt.close();
}
@Test(expected = EntityMgrException.class)
public void testEntityMgrTrans_RtEx() throws Exception {
doThrow(new IllegalArgumentException("expected exception")).when(trans).begin();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
// Empty
}
}
@Test(expected = EntityMgrException.class)
public void testEntityMgrTrans_NotSuppEx() throws Exception {
doThrow(new NotSupportedException("expected exception")).when(trans).begin();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
// Empty
}
}
@Test(expected = EntityMgrException.class)
public void testEntityMgrTrans_SysEx() throws Exception {
doThrow(new SystemException("expected exception")).when(trans).begin();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
// Empty
}
}
/**
* Verifies that the transaction is not rolled back, but the manager is closed when a
* transaction is already active.
*/
@Test
public void testClose_Active() throws Exception {
when(trans.getStatus()).thenReturn(Status.STATUS_ACTIVE);
EntityMgrTrans emt = new EntityMgrTrans(mgr);
emt.close();
// closed and rolled back, but not committed
verify(trans, never()).commit();
verify(trans, never()).rollback();
verify(mgr).close();
}
/**
* Verifies that the transaction is rolled back and the manager is closed when a
* transaction is begun by the constructor.
*/
@Test
public void testClose_Begun() throws Exception {
EntityMgrTrans emt = new EntityMgrTrans(mgr);
emt.close();
// closed and rolled back, but not committed
verify(trans, never()).commit();
verify(trans).rollback();
verify(mgr).close();
}
/**
* Verifies that the manager is closed, but that the transaction is not rolled
* back when no transaction is active.
*/
@Test
public void testClose_Inactive() throws Exception {
when(trans.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);
EntityMgrTrans emt = new EntityMgrTrans(mgr);
emt.close();
// closed, but not committed or rolled back
verify(mgr).close();
verify(trans, never()).commit();
verify(trans, never()).rollback();
}
@Test(expected = EntityMgrException.class)
public void testClose_IllStateEx() throws Exception {
doThrow(new IllegalStateException("expected exception")).when(trans).rollback();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
// Empty
}
}
@Test(expected = EntityMgrException.class)
public void testClose_SecEx() throws Exception {
doThrow(new SecurityException("expected exception")).when(trans).rollback();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
// Empty
}
}
@Test(expected = EntityMgrException.class)
public void testClose_SysEx() throws Exception {
doThrow(new SystemException("expected exception")).when(trans).rollback();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
// Empty
}
}
/**
* Verifies that the manager is closed and the transaction rolled back when "try"
* block exits normally and a transaction is active.
*/
@Test
public void testClose_TryWithoutExcept_Active() throws Exception {
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
// Empty
}
// closed and rolled back, but not committed
verify(trans, never()).commit();
verify(trans).rollback();
verify(mgr).close();
}
/**
* Verifies that the manager is closed, but that the transaction is not rolled
* back when "try" block exits normally and no transaction is active.
*/
@Test
public void testClose_TryWithoutExcept_Inactive() throws Exception {
when(trans.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
// Empty
}
// closed, but not rolled back or committed
verify(trans, never()).commit();
verify(trans, never()).rollback();
verify(mgr).close();
}
/**
* Verifies that the manager is closed and the transaction rolled back when "try"
* block throws an exception and a transaction is active.
*/
@Test
public void testClose_TryWithExcept_Active() throws Exception {
try {
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
throw new SystemException("expected exception");
}
} catch (Exception e) {
logger.trace("expected exception", e);
}
// closed and rolled back, but not committed
verify(trans, never()).commit();
verify(trans).rollback();
verify(mgr).close();
}
/**
* Verifies that the manager is closed, but that the transaction is not rolled
* back when "try" block throws an exception and no transaction is active.
*/
@Test
public void testClose_TryWithExcept_Inactive() throws Exception {
when(trans.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);
try {
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
throw new SystemException("expected exception");
}
} catch (Exception e) {
logger.trace("expected exception", e);
}
// closed, but not rolled back or committed
verify(trans, never()).commit();
verify(trans, never()).rollback();
verify(mgr).close();
}
/**
* Verifies that commit() only commits, and that the subsequent close() does not
* re-commit.
*/
@Test
public void testCommit() throws Exception {
EntityMgrTrans emt = new EntityMgrTrans(mgr);
emt.commit();
when(trans.getStatus()).thenReturn(Status.STATUS_COMMITTED);
// committed, but not closed or rolled back
verify(trans).commit();
verify(trans, never()).rollback();
verify(mgr, never()).close();
// closed, but not re-committed
emt.close();
verify(trans, times(1)).commit();
verify(mgr).close();
}
/**
* Verifies that commit() does nothing, and that the subsequent close() does not
* re-commit when a transaction is already active.
*/
@Test
public void testCommit_Active() throws Exception {
when(trans.getStatus()).thenReturn(Status.STATUS_ACTIVE);
EntityMgrTrans emt = new EntityMgrTrans(mgr);
emt.commit();
// nothing happened yet
verify(trans, never()).commit();
verify(trans, never()).rollback();
verify(mgr, never()).close();
// closed, but not re-committed
emt.close();
// still no commit or rollback
verify(trans, never()).commit();
verify(trans, never()).rollback();
verify(mgr).close();
}
@Test(expected = EntityMgrException.class)
public void testCommit_SecEx() throws Exception {
doThrow(new SecurityException("expected exception")).when(trans).commit();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
emt.commit();
}
}
@Test(expected = EntityMgrException.class)
public void testCommit_IllStateEx() throws Exception {
doThrow(new IllegalStateException("expected exception")).when(trans).commit();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
emt.commit();
}
}
@Test(expected = EntityMgrException.class)
public void testCommit_RbEx() throws Exception {
doThrow(new RollbackException("expected exception")).when(trans).commit();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
emt.commit();
}
}
@Test(expected = EntityMgrException.class)
public void testCommit_HmEx() throws Exception {
doThrow(new HeuristicMixedException("expected exception")).when(trans).commit();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
emt.commit();
}
}
@Test(expected = EntityMgrException.class)
public void testCommit_HrbEx() throws Exception {
doThrow(new HeuristicRollbackException("expected exception")).when(trans).commit();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
emt.commit();
}
}
@Test(expected = EntityMgrException.class)
public void testCommit_SysEx() throws Exception {
doThrow(new SystemException("expected exception")).when(trans).commit();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
emt.commit();
}
}
/**
* Verifies that rollback() only rolls back, and that the subsequent close() does not
* re-roll back.
*/
@Test
public void testRollback() throws Exception {
EntityMgrTrans emt = new EntityMgrTrans(mgr);
emt.rollback();
when(trans.getStatus()).thenReturn(Status.STATUS_ROLLEDBACK);
// rolled back, but not closed or committed
verify(trans, never()).commit();
verify(trans).rollback();
verify(mgr, never()).close();
// closed, but not re-rolled back
emt.close();
// still no commit or rollback
verify(trans, never()).commit();
verify(trans).rollback();
verify(mgr).close();
}
/**
* Verifies that rollback() does nothing, and that the subsequent close() does not
* re-roll back when a transaction is already active.
*/
@Test
public void testRollback_Active() throws Exception {
when(trans.getStatus()).thenReturn(Status.STATUS_ACTIVE);
EntityMgrTrans emt = new EntityMgrTrans(mgr);
emt.rollback();
// nothing happens
verify(trans, never()).commit();
verify(trans, never()).rollback();
verify(mgr, never()).close();
emt.close();
// still no commit or rollback
verify(trans, never()).commit();
verify(trans, never()).rollback();
verify(mgr).close();
}
@Test(expected = EntityMgrException.class)
public void testRollback_IllStateEx() throws Exception {
doThrow(new IllegalStateException("expected exception")).when(trans).rollback();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
emt.rollback();
}
}
@Test(expected = EntityMgrException.class)
public void testRollback_SecEx() throws Exception {
doThrow(new SecurityException("expected exception")).when(trans).rollback();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
emt.rollback();
}
}
@Test(expected = EntityMgrException.class)
public void testRollback_SysEx() throws Exception {
doThrow(new SystemException("expected exception")).when(trans).rollback();
try (EntityMgrTrans emt = new EntityMgrTrans(mgr)) {
emt.rollback();
}
}
@Test
public void testEntityMgrException() {
SecurityException secex = new SecurityException("expected exception");
EntityMgrException ex = new EntityMgrException(secex);
assertEquals(secex, ex.getCause());
}
/**
* Tests using real (i.e., not mocked) Persistence classes.
*/
@Test
public void testReal() {
EntityMgrTrans.setUserTrans(savetrans);
Map propMap = new HashMap<>();
propMap.put("javax.persistence.jdbc.driver", "org.h2.Driver");
propMap.put("javax.persistence.jdbc.url", "jdbc:h2:mem:EntityMgrTransTest");
EntityManagerFactory emf = Persistence.createEntityManagerFactory("junitDroolsSessionEntityPU", propMap);
try (EntityMgrTrans trans1 = new EntityMgrTrans(emf.createEntityManager())) {
// nest a transaction - should still be OK
try (EntityMgrTrans trans2 = new EntityMgrTrans(emf.createEntityManager())) {
// Empty
}
} catch (Exception e) {
logger.info("persistence error", e);
emf.close();
fail("persistence error");
}
emf.close();
}
}