package org.jooby.internal.hbm; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.junit.Assert.assertEquals; import java.sql.Connection; import org.hibernate.FlushMode; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.context.internal.ManagedSessionContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.jooby.test.MockUnit; import org.jooby.test.MockUnit.Block; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest({RootUnitOfWork.class, ManagedSessionContext.class }) public class RootUnitOfWorkTest { private Block bind = unit -> { unit.mockStatic(ManagedSessionContext.class); expect(ManagedSessionContext.bind(unit.get(SessionImplementor.class))).andReturn(null); }; private Block begin = unit -> { Transaction trx = unit.get(Transaction.class); trx.begin(); }; private Block flush = unit -> { SessionImplementor session = unit.get(SessionImplementor.class); session.flush(); }; private Block commit = unit -> { Transaction trx = unit.get(Transaction.class); trx.commit(); }; private Block unbind = unit -> { SessionFactoryImplementor sf = unit.mock(SessionFactoryImplementor.class); unit.mockStatic(ManagedSessionContext.class); expect(ManagedSessionContext.unbind(sf)).andReturn(null); SessionImplementor session = unit.get(SessionImplementor.class); expect(session.getSessionFactory()).andReturn(sf); }; private Block rollback = unit -> { Transaction trx = unit.get(Transaction.class); trx.rollback(); }; @Test public void newUnitOfWork() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)); }); } @Test public void commitTrx() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(flush) .expect(trx(true)) .expect(commit) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .commit(); }); } @Test public void committedTrx() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(flush) .expect(trx(false)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .commit(); }); } @Test public void commitShouldIgnoreFlushOnReadOnlyTrx() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(setReadOnly(true)) .expect(trx(false)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .setReadOnly() .commit(); }); } @Test public void setReadonly() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(setReadOnly(true)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .setReadOnly(); }); } @Test public void setReadonlyFailure() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(setReadOnly(true, true)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .setReadOnly(); }); } @Test public void ignoreReadOnlyOnRollbackOnly() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .setRollbackOnly() .setReadOnly(); }); } @Test public void rollback() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(true)) .expect(rollback) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .rollback(); }); } @Test public void rollbackInactiveTransaction() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(false)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .rollback(); }); } @Test public void apply() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(false)) .expect(begin) .expect(flush) .expect(trx(true)) .expect(commit) .expect(close(false)) .expect(unbind) .run(unit -> { Session result = new RootUnitOfWork(unit.get(SessionImplementor.class)) .apply(session -> { return session; }); assertEquals(unit.get(SessionImplementor.class), result); }); } @Test public void applyRevertReadOnly() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(false)) .expect(setReadOnly(true)) .expect(begin) .expect(trx(true)) .expect(commit) .expect(setReadOnly(false)) .expect(close(false)) .expect(unbind) .run(unit -> { Session result = new RootUnitOfWork(unit.get(SessionImplementor.class)) .setReadOnly() .apply(session -> { return session; }); assertEquals(unit.get(SessionImplementor.class), result); }); } @Test(expected = IllegalStateException.class) public void applyShouldCloseSessionAndUnbind() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(false)) .expect(begin) .expect(flush) .expect(trx(true)) .expect(commit) .expect(unit -> { expectLastCall().andThrow(new IllegalStateException("intentional error")); }) .expect(close(false)) .expect(unbind) .run(unit -> { Session result = new RootUnitOfWork(unit.get(SessionImplementor.class)) .apply(session -> { return session; }); assertEquals(unit.get(SessionImplementor.class), result); }); } @Test public void accept() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(false)) .expect(begin) .expect(flush) .expect(trx(true)) .expect(commit) .expect(close(false)) .expect(unbind) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .accept(session -> { assertEquals(unit.get(SessionImplementor.class), session); }); }); } @Test public void applySilentCloseError() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(false)) .expect(begin) .expect(flush) .expect(trx(true)) .expect(commit) .expect(close(true)) .expect(unbind) .run(unit -> { Session result = new RootUnitOfWork(unit.get(SessionImplementor.class)) .apply(session -> { return session; }); assertEquals(unit.get(SessionImplementor.class), result); }); } @Test(expected = NullPointerException.class) public void applyRollback() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(false)) .expect(begin) .expect(flush) .expect(trx(true)) .expect(rollback) .expect(close(false)) .expect(unbind) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .apply(session -> { throw new NullPointerException(); }); }); } private Block close(final boolean b) { return unit -> { SessionImplementor session = unit.get(SessionImplementor.class); session.close(); if (b) { expectLastCall().andThrow(new IllegalStateException("intentional error")); } }; } private Block setReadOnly(final boolean b) { return setReadOnly(b, false); } private Block setReadOnly(final boolean b, final boolean ex) { return unit -> { Connection conn = unit.mock(Connection.class); conn.setReadOnly(b); if (ex) { expectLastCall().andThrow(new IllegalStateException("intentional err")); } SessionImplementor session = unit.get(SessionImplementor.class); expect(session.connection()).andReturn(conn); if (b) { session.setDefaultReadOnly(b); session.setHibernateFlushMode(FlushMode.MANUAL); } }; } @Test public void commitIgnoreWhenRollbackOnly() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .setRollbackOnly() .commit(); }); } @Test public void beginTrx() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(false)) .expect(begin) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .begin(); }); } @Test public void joinTrx() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .expect(trx(true)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .begin(); }); } @Test public void beginIgnoredOnRollbackOnly() throws Exception { new MockUnit(SessionImplementor.class) .expect(bind) .expect(flushMode(FlushMode.AUTO)) .run(unit -> { new RootUnitOfWork(unit.get(SessionImplementor.class)) .setRollbackOnly() .begin(); }); } private Block trx(final boolean active) { return unit -> { Transaction trx = unit.mock(Transaction.class); expect(trx.isActive()).andReturn(active); unit.registerMock(Transaction.class, trx); SessionImplementor session = unit.get(SessionImplementor.class); expect(session.getTransaction()).andReturn(trx); }; } private Block flushMode(final FlushMode flushMode) { return unit -> { SessionImplementor session = unit.get(SessionImplementor.class); session.setHibernateFlushMode(flushMode); }; } }