From 92291eb22a46b0429c006db32dd5b634efca1fbc Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 12 Jul 2019 11:57:15 +0200 Subject: [PATCH] Use mocked driver for database selection tests. Multidatabase is an enterprise feature, so that test requires an enterprise image. We decided against an availibily guard as the transaction handling should be solid, regardless of edition. It also makes the tests faster. --- ...TransactionManagerMixedDatabasesTest.java} | 87 ++++++++++++------ ...TransactionManagerMixedDatabasesTest.java} | 91 +++++++++++++------ 2 files changed, 122 insertions(+), 56 deletions(-) rename spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/imperative/{MixedDatabasesTransactionIT.java => TransactionManagerMixedDatabasesTest.java} (65%) rename spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/reactive/{ReactiveMixedDatabasesTransactionIT.java => ReactiveTransactionManagerMixedDatabasesTest.java} (68%) diff --git a/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/imperative/MixedDatabasesTransactionIT.java b/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/imperative/TransactionManagerMixedDatabasesTest.java similarity index 65% rename from spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/imperative/MixedDatabasesTransactionIT.java rename to spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/imperative/TransactionManagerMixedDatabasesTest.java index 13b2bb193..54fb8dbb5 100644 --- a/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/imperative/MixedDatabasesTransactionIT.java +++ b/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/imperative/TransactionManagerMixedDatabasesTest.java @@ -19,25 +19,29 @@ package org.neo4j.springframework.data.integration.imperative; import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; import java.time.LocalDate; import java.util.Collection; import java.util.Collections; +import java.util.Map; import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.neo4j.driver.Driver; +import org.neo4j.driver.Record; import org.neo4j.driver.Session; -import org.neo4j.driver.exceptions.DatabaseException; +import org.neo4j.driver.StatementResult; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.Values; import org.neo4j.driver.internal.SessionConfig; import org.neo4j.springframework.data.config.AbstractNeo4jConfig; import org.neo4j.springframework.data.core.Neo4jClient; import org.neo4j.springframework.data.core.transaction.Neo4jTransactionManager; import org.neo4j.springframework.data.integration.shared.PersonWithAllConstructor; import org.neo4j.springframework.data.repository.config.EnableNeo4jRepositories; -import org.neo4j.springframework.data.test.Neo4jExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -48,19 +52,19 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; /** - * The goal of this integration tests is to ensure a sensible coexistence of declarative {@link Transactional @Transactional} + * The goal of this tests is to ensure a sensible coexistence of declarative {@link Transactional @Transactional} * transaction when the user uses the {@link Neo4jClient} in the same or another database. + *

+ * While it does not integrate against a real database (multidatabase is an enterprise feature), it is still an integration + * test due to the high integration with Spring framework code. */ @ExtendWith(SpringExtension.class) -@ExtendWith(Neo4jExtension.class) -@ContextConfiguration(classes = MixedDatabasesTransactionIT.Config.class) -public class MixedDatabasesTransactionIT { +@ContextConfiguration(classes = TransactionManagerMixedDatabasesTest.Config.class) +public class TransactionManagerMixedDatabasesTest { protected static final String DATABASE_NAME = "boom"; public static final String TEST_QUERY = "MATCH (n:DbTest) RETURN COUNT(n)"; - protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport; - private final Driver driver; private final Neo4jClient neo4jClient; @@ -70,7 +74,7 @@ public class MixedDatabasesTransactionIT { private final PersonRepository repository; @Autowired - public MixedDatabasesTransactionIT(Driver driver, Neo4jClient neo4jClient, + public TransactionManagerMixedDatabasesTest(Driver driver, Neo4jClient neo4jClient, Neo4jTransactionManager neo4jTransactionManager, PersonRepository repository) { this.driver = driver; @@ -79,24 +83,6 @@ public class MixedDatabasesTransactionIT { this.repository = repository; } - @BeforeEach - protected void setupDatabase() { - - try (Session session = driver.session(SessionConfig.forDatabase("system"))) { - session.run("DROP DATABASE " + DATABASE_NAME); - } catch (DatabaseException e) { - // Database does probably not exist - } - - try (Session session = driver.session(SessionConfig.forDatabase("system"))) { - session.run("CREATE DATABASE " + DATABASE_NAME); - } - - try (Session session = driver.session(SessionConfig.forDatabase(DATABASE_NAME))) { - session.run("CREATE (n:DbTest) RETURN n"); - } - } - @Test void withoutActiveTransactions() { @@ -165,7 +151,50 @@ public class MixedDatabasesTransactionIT { @Bean public Driver driver() { - return neo4jConnectionSupport.openConnection(); + + Record boomRecord = mock(Record.class); + when(boomRecord.size()).thenReturn(1); + when(boomRecord.get(0)).thenReturn(Values.value(1L)); + + Record defaultRecord = mock(Record.class); + when(defaultRecord.size()).thenReturn(1); + when(defaultRecord.get(0)).thenReturn(Values.value(0L)); + + StatementResult boomStatementResult = mock(StatementResult.class); + when(boomStatementResult.hasNext()).thenReturn(true); + when(boomStatementResult.single()).thenReturn(boomRecord); + + StatementResult defaultStatementResult = mock(StatementResult.class); + when(defaultStatementResult.hasNext()).thenReturn(true); + when(defaultStatementResult.single()).thenReturn(defaultRecord); + + Transaction boomTransaction = mock(Transaction.class); + when(boomTransaction.run(eq(TEST_QUERY), any(Map.class))).thenReturn(boomStatementResult); + when(boomTransaction.isOpen()).thenReturn(true); + + Transaction defaultTransaction = mock(Transaction.class); + when(defaultTransaction.run(eq(TEST_QUERY), any(Map.class))).thenReturn(defaultStatementResult); + when(defaultTransaction.isOpen()).thenReturn(true); + + Session boomSession = mock(Session.class); + when(boomSession.run(eq(TEST_QUERY), any(Map.class))).thenReturn(boomStatementResult); + when(boomSession.beginTransaction(any(TransactionConfig.class))).thenReturn(boomTransaction); + when(boomSession.isOpen()).thenReturn(true); + + Session defaultSession = mock(Session.class); + when(defaultSession.run(eq(TEST_QUERY), any(Map.class))).thenReturn(defaultStatementResult); + when(defaultSession.beginTransaction(any(TransactionConfig.class))).thenReturn(defaultTransaction); + when(defaultSession.isOpen()).thenReturn(true); + + Driver driver = mock(Driver.class); + when(driver.session()).thenReturn(defaultSession); + when(driver.session(any(SessionConfig.class))).then(invocation -> { + SessionConfig sessionConfig = invocation.getArgument(0); + return sessionConfig.database().map(n -> n.equals(DATABASE_NAME) ? boomSession : defaultSession) + .orElse(defaultSession); + }); + + return driver; } @Override diff --git a/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/reactive/ReactiveMixedDatabasesTransactionIT.java b/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/reactive/ReactiveTransactionManagerMixedDatabasesTest.java similarity index 68% rename from spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/reactive/ReactiveMixedDatabasesTransactionIT.java rename to spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/reactive/ReactiveTransactionManagerMixedDatabasesTest.java index fcfd196ad..48ee249e3 100644 --- a/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/reactive/ReactiveMixedDatabasesTransactionIT.java +++ b/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/reactive/ReactiveTransactionManagerMixedDatabasesTest.java @@ -18,20 +18,27 @@ */ package org.neo4j.springframework.data.integration.reactive; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import java.time.LocalDate; import java.util.Collection; import java.util.Collections; +import java.util.Map; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.neo4j.driver.Driver; -import org.neo4j.driver.Session; -import org.neo4j.driver.exceptions.DatabaseException; +import org.neo4j.driver.Record; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.Values; import org.neo4j.driver.internal.SessionConfig; +import org.neo4j.driver.reactive.RxSession; +import org.neo4j.driver.reactive.RxStatementResult; +import org.neo4j.driver.reactive.RxTransaction; import org.neo4j.springframework.data.config.AbstractReactiveNeo4jConfig; import org.neo4j.springframework.data.core.Neo4jClient; import org.neo4j.springframework.data.core.ReactiveNeo4jClient; @@ -49,13 +56,15 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.reactive.TransactionalOperator; /** - * The goal of this integration tests is to ensure a sensible coexistence of declarative {@link Transactional @Transactional} + * The goal of this tests is to ensure a sensible coexistence of declarative {@link Transactional @Transactional} * transaction when the user uses the {@link Neo4jClient} in the same or another database. + *

+ * While it does not integrate against a real database (multidatabase is an enterprise feature), it is still an integration + * test due to the high integration with Spring framework code. */ @ExtendWith(SpringExtension.class) -@ExtendWith(Neo4jExtension.class) -@ContextConfiguration(classes = ReactiveMixedDatabasesTransactionIT.Config.class) -public class ReactiveMixedDatabasesTransactionIT { +@ContextConfiguration(classes = ReactiveTransactionManagerMixedDatabasesTest.Config.class) +public class ReactiveTransactionManagerMixedDatabasesTest { protected static final String DATABASE_NAME = "boom"; public static final String TEST_QUERY = "MATCH (n:DbTest) RETURN COUNT(n)"; @@ -73,7 +82,7 @@ public class ReactiveMixedDatabasesTransactionIT { private final WrapperService wrappingComponent; @Autowired - public ReactiveMixedDatabasesTransactionIT(Driver driver, ReactiveNeo4jClient neo4jClient, + public ReactiveTransactionManagerMixedDatabasesTest(Driver driver, ReactiveNeo4jClient neo4jClient, ReactiveNeo4jTransactionManager neo4jTransactionManager, ReactivePersonRepository repository, WrapperService wrappingComponent) { @@ -84,24 +93,6 @@ public class ReactiveMixedDatabasesTransactionIT { this.wrappingComponent = wrappingComponent; } - @BeforeEach - protected void setupDatabase() { - - try (Session session = driver.session(SessionConfig.forDatabase("system"))) { - session.run("DROP DATABASE " + DATABASE_NAME); - } catch (DatabaseException e) { - // Database does probably not exist - } - - try (Session session = driver.session(SessionConfig.forDatabase("system"))) { - session.run("CREATE DATABASE " + DATABASE_NAME); - } - - try (Session session = driver.session(SessionConfig.forDatabase(DATABASE_NAME))) { - session.run("CREATE (n:DbTest) RETURN n"); - } - } - @Test void withoutActiveTransactions() { @@ -219,7 +210,53 @@ public class ReactiveMixedDatabasesTransactionIT { @Bean public Driver driver() { - return neo4jConnectionSupport.openConnection(); + + Record boomRecord = mock(Record.class); + when(boomRecord.size()).thenReturn(1); + when(boomRecord.get(0)).thenReturn(Values.value(1L)); + + Record defaultRecord = mock(Record.class); + when(defaultRecord.size()).thenReturn(1); + when(defaultRecord.get(0)).thenReturn(Values.value(0L)); + + RxStatementResult boomStatementResult = mock(RxStatementResult.class); + when(boomStatementResult.records()).thenReturn(Mono.just(boomRecord)); + + RxStatementResult defaultStatementResult = mock(RxStatementResult.class); + when(defaultStatementResult.records()).thenReturn(Mono.just(defaultRecord)); + + RxTransaction boomTransaction = mock(RxTransaction.class); + when(boomTransaction.run(eq(TEST_QUERY), any(Map.class))).thenReturn(boomStatementResult); + when(boomTransaction.commit()).thenReturn(Mono.empty()); + when(boomTransaction.rollback()).thenReturn(Mono.empty()); + + RxTransaction defaultTransaction = mock(RxTransaction.class); + when(defaultTransaction.run(eq(TEST_QUERY), any(Map.class))).thenReturn(defaultStatementResult); + when(defaultTransaction.commit()).thenReturn(Mono.empty()); + when(defaultTransaction.rollback()).thenReturn(Mono.empty()); + + RxSession boomSession = mock(RxSession.class); + when(boomSession.run(eq(TEST_QUERY), any(Map.class))).thenReturn(boomStatementResult); + when(boomSession.beginTransaction()).thenReturn(Mono.just(boomTransaction)); + when(boomSession.beginTransaction(any(TransactionConfig.class))).thenReturn(Mono.just(boomTransaction)); + when(boomSession.close()).thenReturn(Mono.empty()); + + RxSession defaultSession = mock(RxSession.class); + when(defaultSession.run(eq(TEST_QUERY), any(Map.class))).thenReturn(defaultStatementResult); + when(defaultSession.beginTransaction()).thenReturn(Mono.just(defaultTransaction)); + when(defaultSession.beginTransaction(any(TransactionConfig.class))) + .thenReturn(Mono.just(defaultTransaction)); + when(defaultSession.close()).thenReturn(Mono.empty()); + + Driver driver = mock(Driver.class); + when(driver.rxSession()).thenReturn(defaultSession); + when(driver.rxSession(any(SessionConfig.class))).then(invocation -> { + SessionConfig sessionConfig = invocation.getArgument(0); + return sessionConfig.database().map(n -> n.equals(DATABASE_NAME) ? boomSession : defaultSession) + .orElse(defaultSession); + }); + + return driver; } @Override