Add additional testscenarios for transaction management.

This commit is contained in:
Michael Simons
2019-03-26 23:21:08 +00:00
parent 727b37b56d
commit 7aa029926d
3 changed files with 108 additions and 48 deletions

View File

@@ -38,11 +38,11 @@ public class Neo4jResourceHolder extends ResourceHolderSupport {
private final Transaction transaction;
public Neo4jResourceHolder(Session session) {
Neo4jResourceHolder(Session session) {
this(session, TransactionConfig.empty());
}
public Neo4jResourceHolder(Session session, TransactionConfig transactionConfig) {
Neo4jResourceHolder(Session session, TransactionConfig transactionConfig) {
this.session = session;
this.transaction = this.session.beginTransaction(transactionConfig);
@@ -52,7 +52,7 @@ public class Neo4jResourceHolder extends ResourceHolderSupport {
return transaction;
}
public void commit() {
void commit() {
Assert.state(hasActiveTransaction(), "Transaction must be open, but has already been closed.");
Assert.state(!isRollbackOnly(), "Resource msut not be marked as rollback only.");
@@ -61,7 +61,7 @@ public class Neo4jResourceHolder extends ResourceHolderSupport {
transaction.close();
}
public void rollback() {
void rollback() {
Assert.state(hasActiveTransaction(), "Transaction must be open, but has already been closed.");
@@ -69,7 +69,7 @@ public class Neo4jResourceHolder extends ResourceHolderSupport {
transaction.close();
}
public void close() {
void close() {
Assert.state(hasActiveSession(), "Session must be open, but has already been closed.");
@@ -90,12 +90,12 @@ public class Neo4jResourceHolder extends ResourceHolderSupport {
throw new UnsupportedOperationException();
}
public boolean hasActiveSession() {
boolean hasActiveSession() {
return session.isOpen();
}
public boolean hasActiveTransaction() {
boolean hasActiveTransaction() {
return transaction.isOpen();
}

View File

@@ -89,8 +89,7 @@ public class Neo4jTransactionManager extends AbstractPlatformTransactionManager
resourceHolder.setSynchronizedWithTransaction(true);
TransactionSynchronizationManager.bindResource(driver, resourceHolder);
} catch (Exception ex) {
ex.printStackTrace();
throw new TransactionSystemException(String.format("Could not open a new Neo4j session:", ex));
throw new TransactionSystemException(String.format("Could not open a new Neo4j session: %s", ex.getMessage()));
}
}

View File

@@ -35,6 +35,8 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.neo4j.driver.v1.AccessMode;
import org.neo4j.driver.v1.Driver;
import org.neo4j.driver.v1.Session;
@@ -51,6 +53,7 @@ import org.springframework.transaction.support.TransactionTemplate;
* @author Michael J. Simons
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class DefaultNeo4jStatementRunnerSupplierTest {
@Mock
@@ -68,6 +71,7 @@ class DefaultNeo4jStatementRunnerSupplierTest {
AtomicBoolean sessionIsOpen = new AtomicBoolean(true);
AtomicBoolean transactionIsOpen = new AtomicBoolean(true);
when(driver.session()).thenReturn(session);
when(driver.session(AccessMode.WRITE, Collections.emptyList())).thenReturn(session);
when(session.beginTransaction(any(TransactionConfig.class))).thenReturn(transaction);
@@ -84,6 +88,100 @@ class DefaultNeo4jStatementRunnerSupplierTest {
when(transaction.isOpen()).thenAnswer(invocation -> transactionIsOpen.get());
}
@Test
void shouldWorkWithoutSynchronizations() {
StatementRunnerSupplier<StatementRunner> statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(
driver);
@SuppressWarnings({ "unused" })
StatementRunner statementRunner = statementRunnerSupplier.get();
verify(driver).session();
verifyNoMoreInteractions(driver, session, transaction);
}
@Nested
class BasedOnNeo4jTransactions {
@Test
void shouldOpenNewTransaction() {
Neo4jTransactionManager txManager = new Neo4jTransactionManager(driver);
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
StatementRunnerSupplier<StatementRunner> statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(
driver);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
assertThat(transactionStatus.isNewTransaction()).isTrue();
assertThat(TransactionSynchronizationManager.hasResource(driver)).isTrue();
@SuppressWarnings({ "unused" })
StatementRunner statementRunner = statementRunnerSupplier.get();
transactionStatus.setRollbackOnly();
}
});
verify(driver).session(AccessMode.WRITE, Collections.emptyList());
verify(session).isOpen();
verify(session).beginTransaction(any(TransactionConfig.class));
verify(session).close();
verify(transaction, times(2)).isOpen();
verify(transaction).failure();
verify(transaction).close();
}
@Test
void shouldParticipateInOngoingTransaction() {
Neo4jTransactionManager txManager = new Neo4jTransactionManager(driver);
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
StatementRunnerSupplier<StatementRunner> statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(
driver);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus outerStatus) {
@SuppressWarnings({ "unused" })
StatementRunner outerStatementRunner = statementRunnerSupplier.get();
assertThat(outerStatus.isNewTransaction()).isTrue();
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus innerStatus) {
assertThat(innerStatus.isNewTransaction()).isFalse();
@SuppressWarnings({ "unused" })
StatementRunner innerStatementRunner = statementRunnerSupplier.get();
}
});
outerStatus.setRollbackOnly();
}
});
verify(driver).session(AccessMode.WRITE, Collections.emptyList());
verify(session).isOpen();
verify(session).beginTransaction(any(TransactionConfig.class));
verify(session).close();
verify(transaction, times(2)).isOpen();
verify(transaction).failure();
verify(transaction).close();
}
}
@Nested
class BasedOnJtaTransactions {
@@ -107,6 +205,7 @@ class DefaultNeo4jStatementRunnerSupplierTest {
assertThat(transactionStatus.isNewTransaction()).isTrue();
assertThat(TransactionSynchronizationManager.hasResource(driver)).isFalse();
@SuppressWarnings({ "unused" })
StatementRunner statementRunner = statementRunnerSupplier.get();
assertThat(TransactionSynchronizationManager.hasResource(driver)).isTrue();
@@ -127,7 +226,7 @@ class DefaultNeo4jStatementRunnerSupplierTest {
}
@Test
public void shouldParticipateInOngoingTransactionWithRollback() throws Exception {
void shouldParticipateInOngoingTransactionWithRollback() throws Exception {
when(userTransaction.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE,
Status.STATUS_ACTIVE);
@@ -170,44 +269,6 @@ class DefaultNeo4jStatementRunnerSupplierTest {
}
}
@Nested
class BasedOnNeo4jTransactions {
@Test
public void shouldParticipateInOngoingTransaction() {
Neo4jTransactionManager txManager = new Neo4jTransactionManager(driver);
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
StatementRunnerSupplier<StatementRunner> statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(
driver);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
assertThat(transactionStatus.isNewTransaction()).isTrue();
assertThat(TransactionSynchronizationManager.hasResource(driver)).isTrue();
@SuppressWarnings({ "unused" })
StatementRunner statementRunner = statementRunnerSupplier.get();
transactionStatus.setRollbackOnly();
}
});
verify(driver).session(AccessMode.WRITE, Collections.emptyList());
verify(session).isOpen();
verify(session).beginTransaction(any(TransactionConfig.class));
verify(session).close();
verify(transaction, times(2)).isOpen();
verify(transaction).failure();
verify(transaction).close();
}
}
@AfterEach
void verifyTransactionSynchronizationManagerState() {