package org.springframework.data.neo4j.transaction; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.neo4j.ogm.model.Result; import org.neo4j.ogm.session.Session; import org.neo4j.ogm.session.SessionFactory; import org.neo4j.ogm.transaction.Transaction; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.UnexpectedRollbackException; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; /** * @author Mark Angrish */ public class Neo4jTransactionManagerTests { private SessionFactory sf; private Session session; private Transaction tx; private Neo4jTransactionManager tm; private TransactionTemplate tt; @Before public void setUp() throws Exception { sf = mock(SessionFactory.class); session = mock(Session.class); tx = mock(Transaction.class); tm = new Neo4jTransactionManager(sf); tt = new TransactionTemplate(tm); given(session.getTransaction()).willReturn(tx); given(sf.openSession()).willReturn(session); tm.setSessionFactory(sf); tt.setTransactionManager(tm); } @After public void tearDown() throws Exception { assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty()); assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); assertFalse(TransactionSynchronizationManager.isActualTransactionActive()); } @Test public void testTransactionCommit() throws Exception { final List list = new ArrayList(); HashMap<String, Object> entry = new HashMap<>(); entry.put("test", "test"); list.add(entry); Result res = mock(Result.class); given(session.query("some query string", Collections.<String, Object>emptyMap())).willReturn(res); given(res.queryResults()).willReturn(list); assertTrue("Transaction Synchronization already has a thread bound session", !TransactionSynchronizationManager.hasResource(sf)); assertTrue("Synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); Object result = tt.execute((TransactionCallback) status -> { assertTrue("Transaction Synchronization doesn't have a thread bound session", TransactionSynchronizationManager.hasResource(sf)); assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); return session.query("some query string", Collections.<String, Object>emptyMap()).queryResults(); }); assertTrue("Incorrect result list", result == list); assertTrue("Transaction Synchronization still has a thread bound session", !TransactionSynchronizationManager.hasResource(sf)); assertTrue("Synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); verify(session).beginTransaction(); verify(tx).commit(); verify(tx).close(); } @Test public void testTransactionRollback() throws Exception { assertTrue("Transaction Synchronization already has a thread bound session", !TransactionSynchronizationManager.hasResource(sf)); assertTrue("Synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); try { tt.execute(status -> { assertTrue("Transaction Synchronization doesn't have a thread bound session", TransactionSynchronizationManager.hasResource(sf)); throw new RuntimeException("application exception"); }); fail("Should have thrown RuntimeException"); } catch (RuntimeException ex) { // expected } assertTrue("Transaction Synchronization still has a thread bound session", !TransactionSynchronizationManager.hasResource(sf)); assertTrue("Synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); verify(session).beginTransaction(); verify(tx).rollback(); verify(tx).close(); } @Test public void testTransactionRollbackOnly() throws Exception { assertTrue("Transaction Synchronization already has a thread bound session", !TransactionSynchronizationManager.hasResource(sf)); tt.execute(status -> { assertTrue("Transaction Synchronization doesn't have a thread bound session", TransactionSynchronizationManager.hasResource(sf)); status.setRollbackOnly(); return null; }); assertTrue("Transaction Synchronization still has a thread bound session", !TransactionSynchronizationManager.hasResource(sf)); verify(session).beginTransaction(); verify(tx).rollback(); verify(tx).close(); } @Test public void testParticipatingTransactionWithCommit() throws Exception { final List l = new ArrayList(); l.add("test"); Object result = tt.execute(status -> tt.execute((TransactionCallback) status1 -> l)); assertTrue("Correct result list", result == l); verify(session).beginTransaction(); verify(tx).commit(); verify(tx).close(); } @Test public void testParticipatingTransactionWithRollback() throws Exception { try { tt.execute(status -> tt.execute(status1 -> { throw new RuntimeException("application exception"); })); fail("Should have thrown RuntimeException"); } catch (RuntimeException ex) { // expected } verify(session).beginTransaction(); verify(tx).rollback(); verify(tx).close(); } @Test @Ignore public void testParticipatingTransactionWithRollbackOnly() throws Exception { try { tt.execute(status -> tt.execute(status1 -> { status1.setRollbackOnly(); return null; })); fail("Should have thrown UnexpectedRollbackException"); } catch (UnexpectedRollbackException ex) { // expected } verify(session).beginTransaction(); verify(tx).rollback(); verify(tx).close(); } // // @Test // public void testTransactionCommitWithPreBound() throws Exception { // tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); // final List l = new ArrayList(); // l.add("test"); // assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); // TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session)); // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // // Object result = tt.execute(new TransactionCallback() { // @Override // public Object doInTransaction(TransactionStatus status) { // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); // assertTrue("Has thread transaction", sessionHolder.getSession().getTransaction() != null); // Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); // assertEquals(session, sess); // return l; // } // }); // assertTrue("Correct result list", result == l); // // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); // assertTrue("Hasn't thread transaction", sessionHolder.getSession().getTransaction() == null); // TransactionSynchronizationManager.unbindResource(sf); // assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); // // verify(tx).commit(); // } // // @Test // public void testTransactionRollbackWithPreBound() throws Exception { // // final Transaction tx1 = mock(Transaction.class); // final Transaction tx2 = mock(Transaction.class); // // given(session.beginTransaction()).willReturn(tx1, tx2); // // tm.setSessionFactory(sf); // assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); // TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session)); // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // // try { // tt.execute(new TransactionCallbackWithoutResult() { // @Override // public void doInTransactionWithoutResult(TransactionStatus status) { // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); // assertEquals(tx1, sessionHolder.getSession().getTransaction()); // tt.execute(new TransactionCallbackWithoutResult() { // @Override // public void doInTransactionWithoutResult(TransactionStatus status) { // status.setRollbackOnly(); // Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); // assertEquals(session, sess); // } // }); // } // }); // fail("Should have thrown UnexpectedRollbackException"); // } // catch (UnexpectedRollbackException ex) { // // expected // } // // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); // assertTrue("Hasn't thread transaction", sessionHolder.getSession().getTransaction() == null); // assertTrue("Not marked rollback-only", !sessionHolder.isRollbackOnly()); // // tt.execute(new TransactionCallbackWithoutResult() { // @Override // public void doInTransactionWithoutResult(TransactionStatus status) { // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); // assertEquals(tx2, sessionHolder.getSession().getTransaction()); // Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); // assertEquals(session, sess); // } // }); // // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // assertTrue("Hasn't thread transaction", sessionHolder.getSession().getTransaction() == null); // TransactionSynchronizationManager.unbindResource(sf); // assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); // // verify(tx1).rollback(); // verify(tx2).commit(); // InOrder ordered = inOrder(session); // ordered.verify(session).clear(); // } // // // @Test // public void testTransactionCommitWithNonExistingDatabase() throws Exception { // tm.setSessionFactory(sf); // tm.afterPropertiesSet(); // TransactionTemplate tt = new TransactionTemplate(tm); // tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); // tt.setTimeout(10); // assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); // assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); // // try { // tt.execute(new TransactionCallback() { // @Override // public Object doInTransaction(TransactionStatus status) { // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); // return session.query("from java.lang.Object", Collections.<String, Object>emptyMap()).queryResults(); // } // }); // fail("Should have thrown CannotCreateTransactionException"); // } // catch (CannotCreateTransactionException ex) { // // expected // } // // assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); // assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); // } // // @Test // public void testTransactionCommitWithPreBoundSessionAndNonExistingDatabase() throws Exception { // tm.setSessionFactory(sf); // tm.afterPropertiesSet(); // tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); // tt.setTimeout(10); // assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); // assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); // // Session session = sf.openSession(); // TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session)); // try { // tt.execute(new TransactionCallback() { // @Override // public Object doInTransaction(TransactionStatus status) { // assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); // Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); // return session.query("from java.lang.Object", Collections.<String, Object>emptyMap()).queryResults(); // } // }); // fail("Should have thrown CannotCreateTransactionException"); // } // catch (CannotCreateTransactionException ex) { // // expected // SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); // assertFalse(holder.isSynchronizedWithTransaction()); // } // finally { // TransactionSynchronizationManager.unbindResource(sf); // } // // assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); // assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); // } // // // @Test // public void testTransactionCommitWithRollbackException() { // willThrow(new RuntimeException()).given(tx).commit(); // // final List<String> l = new ArrayList<>(); // l.add("test"); // // assertTrue(!TransactionSynchronizationManager.hasResource(sf)); // assertTrue(!TransactionSynchronizationManager.isSynchronizationActive()); // // try { // Object result = tt.execute(new TransactionCallback() { // @Override // public Object doInTransaction(TransactionStatus status) { // assertTrue(TransactionSynchronizationManager.hasResource(sf)); // return l; // } // }); // assertSame(l, result); // } catch (TransactionSystemException tse) { // // expected // assertTrue(tse.getCause() instanceof RuntimeException); // } // // assertTrue(!TransactionSynchronizationManager.hasResource(sf)); // assertTrue(!TransactionSynchronizationManager.isSynchronizationActive()); // } // }