/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.core.internal.transaction;
import org.junit.Test;
import org.mockito.internal.util.reflection.Whitebox;
import org.seedstack.seed.transaction.TransactionConfig;
import org.seedstack.seed.transaction.spi.TransactionManager;
import org.seedstack.seed.transaction.spi.TransactionMetadata;
import org.seedstack.seed.SeedException;
import javax.naming.Context;
import javax.transaction.Status;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SuppressWarnings("unchecked")
public class JtaTransactionManagerTest extends AbstractTransactionManagerTest {
UserTransactionMock userTransaction;
TransactionManagerMock transactionManager;
private void setupActiveTransaction() throws Exception {
userTransaction.begin();
}
private void verifyNoDirectTransactionHandling() {
verify(transactionHandler, never()).doCreateTransaction();
verify(transactionHandler, never()).doBeginTransaction(any());
verify(transactionHandler, never()).doCommitTransaction(any());
verify(transactionHandler, never()).doRollbackTransaction(any());
verify(transactionHandler, never()).doReleaseTransaction(any());
}
@Override
protected TransactionManager doProvideTransactionManager() throws Exception {
userTransaction = new UserTransactionMock();
transactionManager = new TransactionManagerMock(userTransaction);
Context jndiContext = mock(Context.class);
when(jndiContext.lookup("java:comp/UserTransaction")).thenReturn(userTransaction);
when(jndiContext.lookup("java:comp/TransactionManager")).thenReturn(transactionManager);
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
Whitebox.setInternalState(jtaTransactionManager, "jndiContext", jndiContext);
Whitebox.setInternalState(jtaTransactionManager, "jtaConfig", new TransactionConfig.JtaConfig());
return jtaTransactionManager;
}
@Test
public void propagation_required_without_transaction() throws Throwable {
invoke(TransactionalMethods.Enum.REQUIRED);
verifyNoDirectTransactionHandling();
verify(transactionHandler).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler).doJoinGlobalTransaction();
verify(transactionHandler).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_required_with_transaction() throws Throwable {
setupActiveTransaction();
invoke(TransactionalMethods.Enum.REQUIRED);
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_mandatory_without_transaction() throws Throwable {
try {
invoke(TransactionalMethods.Enum.MANDATORY);
fail("exception should have been thrown");
} catch (SeedException e) {
assertThat(e.getErrorCode() == TransactionErrorCode.TRANSACTION_NEEDED_WHEN_USING_PROPAGATION_MANDATORY);
}
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler, never()).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_mandatory_with_transaction() throws Throwable {
setupActiveTransaction();
invoke(TransactionalMethods.Enum.MANDATORY);
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_nested() throws Throwable {
try {
invoke(TransactionalMethods.Enum.NESTED);
fail("should have thrown an exception");
} catch (SeedException e) {
assertThat(e.getErrorCode() == TransactionErrorCode.PROPAGATION_NOT_SUPPORTED);
}
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler, never()).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_supports_without_transaction() throws Throwable {
invoke(TransactionalMethods.Enum.SUPPORTS);
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler, never()).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_supports_with_transaction() throws Throwable {
setupActiveTransaction();
invoke(TransactionalMethods.Enum.SUPPORTS);
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_not_supported_without_transaction() throws Throwable {
invoke(TransactionalMethods.Enum.NOT_SUPPORTED);
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler, never()).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_not_supported_with_transaction() throws Throwable {
setupActiveTransaction();
invoke(TransactionalMethods.Enum.NOT_SUPPORTED);
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler, never()).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(1);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(1);
}
@Test
public void propagation_never_without_transaction() throws Throwable {
invoke(TransactionalMethods.Enum.NEVER);
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler, never()).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_never_with_transaction() throws Throwable {
setupActiveTransaction();
try {
invoke(TransactionalMethods.Enum.NEVER);
fail("should have thrown an exception");
} catch (SeedException e) {
assertThat(e.getErrorCode() == TransactionErrorCode.NO_TRANSACTION_ALLOWED_WHEN_USING_PROPAGATION_NEVER);
}
verifyNoDirectTransactionHandling();
verify(transactionHandler, never()).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler, never()).doJoinGlobalTransaction();
verify(transactionHandler, never()).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_requires_new_without_transaction() throws Throwable {
invoke(TransactionalMethods.Enum.REQUIRES_NEW);
verifyNoDirectTransactionHandling();
verify(transactionHandler).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler).doJoinGlobalTransaction();
verify(transactionHandler).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void propagation_requires_new_with_transaction() throws Throwable {
setupActiveTransaction();
invoke(TransactionalMethods.Enum.REQUIRES_NEW);
verifyNoDirectTransactionHandling();
verify(transactionHandler).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler).doJoinGlobalTransaction();
verify(transactionHandler).doCleanup();
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(1);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(1);
}
@Test
public void transaction_success_should_commit() throws Throwable {
invoke(TransactionalMethods.Enum.OK);
verifyNoDirectTransactionHandling();
verify(transactionHandler).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler).doJoinGlobalTransaction();
verify(transactionHandler).doCleanup();
assertThat(userTransaction.getStatus()).isEqualTo(Status.STATUS_COMMITTED);
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Test
public void transaction_failure_should_rollback() throws Throwable {
try {
invoke(TransactionalMethods.Enum.FAIL);
fail("exception should have been propagated");
} catch (Exception e) {
// nothing here
}
verifyNoDirectTransactionHandling();
verify(transactionHandler).doInitialize(any(TransactionMetadata.class));
verify(transactionHandler).doJoinGlobalTransaction();
verify(transactionHandler).doCleanup();
assertThat(userTransaction.getStatus()).isEqualTo(Status.STATUS_ROLLEDBACK);
assertThat(transactionManager.getSuspendCallCount()).isEqualTo(0);
assertThat(transactionManager.getResumeCallCount()).isEqualTo(0);
}
@Override
protected void doAssertRollbackOccurred() throws Exception {
assertThat(userTransaction.getStatus()).isEqualTo(Status.STATUS_ROLLEDBACK);
}
@Override
protected void doAssertCommitOccurred() throws Exception {
assertThat(userTransaction.getStatus()).isEqualTo(Status.STATUS_COMMITTED);
}
}