package com.maxifier.guice.jpa;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceException;
import static org.mockito.Mockito.*;
/**
* @author Konstantin Lyamshin (2015-11-17 0:49)
*/
public class UnitOfWorkTest extends org.testng.Assert {
@Mock EntityManagerFactory emf;
@Mock EntityManager em;
@Mock EntityTransaction tr;
@BeforeMethod
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(emf.createEntityManager()).thenReturn(em);
when(em.getTransaction()).thenReturn(tr);
}
@AfterMethod
public void tearDown() throws Exception {
// cleanup thread-locals
while (UnitOfWork.get() != null) {
UnitOfWork.end();
}
}
@Test
public void testManualContext() throws Exception {
assertNull(UnitOfWork.get());
UnitOfWork.begin();
assertNotNull(UnitOfWork.get());
UnitOfWork.begin();
assertNotNull(UnitOfWork.get());
UnitOfWork.end();
assertNotNull(UnitOfWork.get());
UnitOfWork.end();
assertNull(UnitOfWork.get());
}
@Test
public void testConnect() throws Exception {
// non-transactional
UnitOfWork context1 = UnitOfWork.create();
assertSame(context1.getConnection(emf), em);
verify(emf, times(1)).createEntityManager();
verify(em, never()).getTransaction();
assertSame(UnitOfWork.get(), context1);
// transactional
UnitOfWork context2 = UnitOfWork.create();
context2.startTransaction();
assertSame(context2.getConnection(emf), em);
verify(emf, times(2)).createEntityManager();
verify(em).getTransaction();
verify(tr).begin();
assertSame(UnitOfWork.get(), context2);
// regular close
context2.releaseConnection();
verify(em, times(1)).close();
verify(tr, times(1)).isActive();
verify(tr, never()).commit();
verify(tr, never()).rollback();
assertSame(UnitOfWork.get(), context1);
// orphan transaction close
when(tr.isActive()).thenReturn(true);
context1.releaseConnection();
verify(em, times(2)).close();
verify(tr, times(2)).isActive();
verify(tr).commit();
assertSame(UnitOfWork.get(), null);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMismatchEMF() throws Exception {
UnitOfWork context = UnitOfWork.create();
context.getConnection(emf);
context.getConnection(mock(EntityManagerFactory.class));
}
@Test
public void testExceptionalRelease1() throws Exception {
// non-transactional
UnitOfWork context = UnitOfWork.create();
context.getConnection(emf);
assertSame(UnitOfWork.get(), context);
doThrow(new PersistenceException()).when(em).close();
try { context.releaseConnection(); fail("Exception expected"); } catch (PersistenceException ignored) {}
assertSame(UnitOfWork.get(), null);
}
@Test
public void testExceptionalRelease2() throws Exception {
// transactional
UnitOfWork context = UnitOfWork.create();
context.startTransaction();
context.getConnection(emf);
assertSame(UnitOfWork.get(), context);
when(tr.isActive()).thenReturn(true);
doThrow(new PersistenceException()).when(tr).commit();
try { context.releaseConnection(); fail("Exception expected"); } catch (PersistenceException ignored) {}
assertSame(UnitOfWork.get(), null);
}
@Test
public void testTransactionNoConnection() throws Exception {
UnitOfWork context = UnitOfWork.create();
assertEquals(context.toString(), "UnitOfWork{}");
assertEquals(context.startTransaction(), true);
assertEquals(context.startTransaction(), false);
assertEquals(context.toString(), "UnitOfWork{transactional}");
context.endTransaction();
assertEquals(context.toString(), "UnitOfWork{}");
}
@Test
public void testTransactionBeforeConnection() throws Exception {
UnitOfWork context = UnitOfWork.create();
assertEquals(context.toString(), "UnitOfWork{}");
assertEquals(context.startTransaction(), true);
assertEquals(context.toString(), "UnitOfWork{transactional}");
assertEquals(context.startTransaction(), false);
context.getConnection(emf);
verify(tr).begin();
context.endTransaction();
verify(tr).commit();
assertEquals(context.toString(), "UnitOfWork{connected}");
}
@Test
public void testTransactionLater() throws Exception {
UnitOfWork context = UnitOfWork.create();
context.getConnection(emf);
verify(tr, never()).begin();
assertEquals(context.toString(), "UnitOfWork{connected}");
assertEquals(context.startTransaction(), true);
assertEquals(context.startTransaction(), false);
assertEquals(context.toString(), "UnitOfWork{connected, transactional}");
verify(tr).begin();
context.endTransaction();
assertEquals(context.toString(), "UnitOfWork{connected}");
verify(tr).commit();
}
@Test
public void testRollback() throws Exception {
UnitOfWork context = UnitOfWork.create();
context.startTransaction();
context.getConnection(emf);
when(tr.isActive()).thenReturn(true);
when(tr.getRollbackOnly()).thenReturn(true);
verify(tr, times(0)).rollback();
context.endTransaction();
verify(tr, times(1)).rollback();
context.releaseConnection();
verify(tr, times(2)).rollback();
verify(tr, never()).commit();
}
@Test
public void testSetRollbackOnly() throws Exception {
UnitOfWork context = UnitOfWork.create();
context.setRollbackOnly(); // nop, no connection
context.getConnection(emf);
context.setRollbackOnly(); // nop, no transaction
verify(tr, never()).setRollbackOnly();
when(tr.isActive()).thenReturn(true);
context.setRollbackOnly();
verify(tr).setRollbackOnly();
}
}