diff --git a/src/test/java/org/springframework/data/r2dbc/core/DefaultDatabaseClientUnitTests.java b/src/test/java/org/springframework/data/r2dbc/core/DefaultDatabaseClientUnitTests.java index bdfef8e..ba8b437 100644 --- a/src/test/java/org/springframework/data/r2dbc/core/DefaultDatabaseClientUnitTests.java +++ b/src/test/java/org/springframework/data/r2dbc/core/DefaultDatabaseClientUnitTests.java @@ -39,47 +39,49 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; - import org.springframework.beans.factory.annotation.Value; import org.springframework.data.annotation.Id; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.r2dbc.dialect.PostgresDialect; import org.springframework.data.r2dbc.mapping.SettableValue; -import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator; +import org.springframework.lang.Nullable; /** * Unit tests for {@link DefaultDatabaseClient}. * * @author Mark Paluch * @author Ferdinand Jacobs + * @author Jens Schauder */ @RunWith(MockitoJUnitRunner.class) public class DefaultDatabaseClientUnitTests { - @Mock ConnectionFactory connectionFactory; @Mock Connection connection; - @Mock R2dbcExceptionTranslator translator; + private DatabaseClient.Builder databaseClientBuilder; @Before public void before() { + + ConnectionFactory connectionFactory = Mockito.mock(ConnectionFactory.class); + when(connectionFactory.create()).thenReturn((Publisher) Mono.just(connection)); when(connection.close()).thenReturn(Mono.empty()); + + databaseClientBuilder = DatabaseClient.builder() // + .connectionFactory(connectionFactory) // + .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)); } @Test // gh-48 public void shouldCloseConnectionOnlyOnce() { - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)) - .exceptionTranslator(translator).build(); + DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) databaseClientBuilder.build(); - Flux flux = databaseClient.inConnectionMany(it -> { - return Flux.empty(); - }); + Flux flux = databaseClient.inConnectionMany(it -> Flux.empty()); flux.subscribe(new CoreSubscriber() { Subscription subscription; @@ -108,13 +110,9 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-128 public void executeShouldBindNullValues() { - Statement statement = mock(Statement.class); - when(connection.createStatement("SELECT * FROM table WHERE key = $1")).thenReturn(statement); - when(statement.execute()).thenReturn(Mono.empty()); + Statement statement = mockStatementFor("SELECT * FROM table WHERE key = $1"); - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("SELECT * FROM table WHERE key = $1") // .bindNull(0, String.class) // @@ -136,13 +134,9 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-162 public void executeShouldBindSettableValues() { - Statement statement = mock(Statement.class); - when(connection.createStatement("SELECT * FROM table WHERE key = $1")).thenReturn(statement); - when(statement.execute()).thenReturn(Mono.empty()); + Statement statement = mockStatementFor("SELECT * FROM table WHERE key = $1"); - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("SELECT * FROM table WHERE key = $1") // .bind(0, SettableValue.empty(String.class)) // @@ -164,13 +158,8 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-128 public void executeShouldBindNamedNullValues() { - Statement statement = mock(Statement.class); - when(connection.createStatement("SELECT * FROM table WHERE key = $1")).thenReturn(statement); - when(statement.execute()).thenReturn(Mono.empty()); - - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); + Statement statement = mockStatementFor("SELECT * FROM table WHERE key = $1"); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("SELECT * FROM table WHERE key = :key") // .bindNull("key", String.class) // @@ -184,14 +173,9 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-178 public void executeShouldBindNamedValuesFromIndexes() { - Statement statement = mock(Statement.class); - when(connection.createStatement("SELECT id, name, manual FROM legoset WHERE name IN ($1, $2, $3)")) - .thenReturn(statement); - when(statement.execute()).thenReturn(Mono.empty()); + Statement statement = mockStatementFor("SELECT id, name, manual FROM legoset WHERE name IN ($1, $2, $3)"); - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("SELECT id, name, manual FROM legoset WHERE name IN (:name)") // .bind(0, Arrays.asList("unknown", "dunno", "other")) // @@ -209,13 +193,9 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-128, gh-162 public void executeShouldBindValues() { - Statement statement = mock(Statement.class); - when(connection.createStatement("SELECT * FROM table WHERE key = $1")).thenReturn(statement); - when(statement.execute()).thenReturn(Mono.empty()); + Statement statement = mockStatementFor("SELECT * FROM table WHERE key = $1"); - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("SELECT * FROM table WHERE key = $1") // .bind(0, SettableValue.from("foo")) // @@ -237,14 +217,8 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-162 public void insertShouldAcceptNullValues() { - Statement statement = mock(Statement.class); - when(connection.createStatement("INSERT INTO foo (first, second) VALUES ($1, $2)")).thenReturn(statement); - when(statement.returnGeneratedValues()).thenReturn(statement); - when(statement.execute()).thenReturn(Mono.empty()); - - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); + Statement statement = mockStatementFor("INSERT INTO foo (first, second) VALUES ($1, $2)"); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.insert().into("foo") // .value("first", "foo") // @@ -260,14 +234,8 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-162 public void insertShouldAcceptSettableValue() { - Statement statement = mock(Statement.class); - when(connection.createStatement("INSERT INTO foo (first, second) VALUES ($1, $2)")).thenReturn(statement); - when(statement.returnGeneratedValues()).thenReturn(statement); - when(statement.execute()).thenReturn(Mono.empty()); - - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); + Statement statement = mockStatementFor("INSERT INTO foo (first, second) VALUES ($1, $2)"); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.insert().into("foo") // .value("first", SettableValue.from("foo")) // @@ -283,13 +251,8 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-128 public void executeShouldBindNamedValuesByIndex() { - Statement statement = mock(Statement.class); - when(connection.createStatement("SELECT * FROM table WHERE key = $1")).thenReturn(statement); - when(statement.execute()).thenReturn(Mono.empty()); - - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); + Statement statement = mockStatementFor("SELECT * FROM table WHERE key = $1"); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("SELECT * FROM table WHERE key = :key") // .bind("key", "foo") // @@ -303,14 +266,8 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-177 public void deleteNotInShouldRenderCorrectQuery() { - Statement statement = mock(Statement.class); - when(connection.createStatement("DELETE FROM tab WHERE tab.pole = $1 AND tab.id NOT IN ($2, $3)")) - .thenReturn(statement); - when(statement.execute()).thenReturn(Mono.empty()); - - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); + Statement statement = mockStatementFor("DELETE FROM tab WHERE tab.pole = $1 AND tab.id NOT IN ($2, $3)"); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.delete().from("tab").matching(where("pole").is("foo").and("id").notIn(1, 2)) // .then() // @@ -318,23 +275,18 @@ public class DefaultDatabaseClientUnitTests { .verifyComplete(); verify(statement).bind(0, "foo"); - verify(statement).bind(1, (Object) 1); - verify(statement).bind(2, (Object) 2); + verify(statement).bind(1, 1); + verify(statement).bind(2, 2); } @Test // gh-243 public void rowsUpdatedShouldEmitSingleValue() { - Statement statement = mock(Statement.class); - when(connection.createStatement("DROP TABLE tab;")).thenReturn(statement); Result result = mock(Result.class); - doReturn(Flux.just(result)).when(statement).execute(); - - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)).build(); - when(result.getRowsUpdated()).thenReturn(Mono.empty(), Mono.just(2), Flux.just(1, 2, 3)); + mockStatementFor("DROP TABLE tab;", result); + + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("DROP TABLE tab;") // .fetch() // @@ -361,10 +313,7 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-250 public void shouldThrowExceptionForSingleColumnObjectUpdate() { - DefaultDatabaseClient databaseClient = (DefaultDatabaseClient) DatabaseClient.builder() - .connectionFactory(connectionFactory) // - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)) // - .build(); + DatabaseClient databaseClient = databaseClientBuilder.build(); assertThatIllegalArgumentException().isThrownBy(() -> databaseClient.update() // .table(IdOnly.class) // @@ -375,20 +324,11 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-260 public void shouldProjectGenericExecuteAs() { - Statement statement = mock(Statement.class); - when(connection.createStatement(anyString())).thenReturn(statement); + MockResult result = mockSingleColumnResult(MockRow.builder().identified(0, Object.class, "Walter")); + mockStatement(result); - MockRowMetadata metadata = MockRowMetadata.builder() - .columnMetadata(MockColumnMetadata.builder().name("name").build()).build(); - MockResult result = MockResult.builder().rowMetadata(metadata) - .row(MockRow.builder().identified(0, Object.class, "Walter").build()).build(); - - doReturn(Flux.just(result)).when(statement).execute(); - - DatabaseClient databaseClient = DatabaseClient.builder() // - .connectionFactory(connectionFactory) // + DatabaseClient databaseClient = databaseClientBuilder // .projectionFactory(new SpelAwareProxyProjectionFactory()) // - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)) // .build(); databaseClient.execute("SELECT * FROM person") // @@ -408,20 +348,11 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-260 public void shouldProjectGenericSelectAs() { - Statement statement = mock(Statement.class); - when(connection.createStatement(anyString())).thenReturn(statement); + MockResult result = mockSingleColumnResult(MockRow.builder().identified(0, Object.class, "Walter")); + mockStatement(result); - MockRowMetadata metadata = MockRowMetadata.builder() - .columnMetadata(MockColumnMetadata.builder().name("name").build()).build(); - MockResult result = MockResult.builder().rowMetadata(metadata) - .row(MockRow.builder().identified(0, Object.class, "Walter").build()).build(); - - doReturn(Flux.just(result)).when(statement).execute(); - - DatabaseClient databaseClient = DatabaseClient.builder() // - .connectionFactory(connectionFactory) // + DatabaseClient databaseClient = databaseClientBuilder // .projectionFactory(new SpelAwareProxyProjectionFactory()) // - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)) // .build(); databaseClient.select().from("person") // @@ -442,20 +373,11 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-260 public void shouldProjectTypedSelectAs() { - Statement statement = mock(Statement.class); - when(connection.createStatement(anyString())).thenReturn(statement); + MockResult result = mockSingleColumnResult(MockRow.builder().identified("name", Object.class, "Walter")); + mockStatement(result); - MockRowMetadata metadata = MockRowMetadata.builder() - .columnMetadata(MockColumnMetadata.builder().name("name").build()).build(); - MockResult result = MockResult.builder().rowMetadata(metadata) - .row(MockRow.builder().identified("name", Object.class, "Walter").build()).build(); - - doReturn(Flux.just(result)).when(statement).execute(); - - DatabaseClient databaseClient = DatabaseClient.builder() // - .connectionFactory(connectionFactory) // + DatabaseClient databaseClient = databaseClientBuilder // .projectionFactory(new SpelAwareProxyProjectionFactory()) // - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)) // .build(); databaseClient.select().from(Person.class) // @@ -474,18 +396,12 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-189 public void shouldApplyExecuteFunction() { - Statement statement = mock(Statement.class); - when(connection.createStatement(anyString())).thenReturn(statement); + Statement statement = mockStatement(); + MockResult result = mockSingleColumnResult(MockRow.builder().identified(0, Object.class, "Walter")); - MockRowMetadata metadata = MockRowMetadata.builder() - .columnMetadata(MockColumnMetadata.builder().name("name").build()).build(); - MockResult result = MockResult.builder().rowMetadata(metadata) - .row(MockRow.builder().identified(0, Object.class, "Walter").build()).build(); - - DatabaseClient databaseClient = DatabaseClient.builder() // - .connectionFactory(connectionFactory) // - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)) // - .executeFunction(it -> Mono.just(result)).build(); + DatabaseClient databaseClient = databaseClientBuilder // + .executeFunction(it -> Mono.just(result)) // + .build(); databaseClient.execute("SELECT") // .fetch().all() // @@ -498,20 +414,13 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-189 public void shouldApplyStatementFilterFunctions() { - Statement statement = mock(Statement.class); - when(connection.createStatement(anyString())).thenReturn(statement); - when(statement.returnGeneratedValues(anyString())).thenReturn(statement); - MockRowMetadata metadata = MockRowMetadata.builder() .columnMetadata(MockColumnMetadata.builder().name("name").build()).build(); MockResult result = MockResult.builder().rowMetadata(metadata).build(); - doReturn(Flux.just(result)).when(statement).execute(); + Statement statement = mockStatement(result); - DatabaseClient databaseClient = DatabaseClient.builder() // - .connectionFactory(connectionFactory) // - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)) // - .build(); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("SELECT") // .filter((s, next) -> next.execute(s.returnGeneratedValues("foo"))) // @@ -530,20 +439,13 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-189 public void shouldApplyStatementFilterFunctionsToTypedExecute() { - Statement statement = mock(Statement.class); - when(connection.createStatement(anyString())).thenReturn(statement); - when(statement.returnGeneratedValues(anyString())).thenReturn(statement); - MockRowMetadata metadata = MockRowMetadata.builder() .columnMetadata(MockColumnMetadata.builder().name("name").build()).build(); MockResult result = MockResult.builder().rowMetadata(metadata).build(); - doReturn(Flux.just(result)).when(statement).execute(); + Statement statement = mockStatement(result); - DatabaseClient databaseClient = DatabaseClient.builder() // - .connectionFactory(connectionFactory) // - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)) // - .build(); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("SELECT") // .filter((s, next) -> next.execute(s.returnGeneratedValues("foo"))) // @@ -561,20 +463,11 @@ public class DefaultDatabaseClientUnitTests { @Test // gh-189 public void shouldApplySimpleStatementFilterFunctions() { - Statement statement = mock(Statement.class); - when(connection.createStatement(anyString())).thenReturn(statement); - when(statement.returnGeneratedValues(anyString())).thenReturn(statement); + MockResult result = mockSingleColumnEmptyResult(); - MockRowMetadata metadata = MockRowMetadata.builder() - .columnMetadata(MockColumnMetadata.builder().name("name").build()).build(); - MockResult result = MockResult.builder().rowMetadata(metadata).build(); + Statement statement = mockStatement(result); - doReturn(Flux.just(result)).when(statement).execute(); - - DatabaseClient databaseClient = DatabaseClient.builder() // - .connectionFactory(connectionFactory) // - .dataAccessStrategy(new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE)) // - .build(); + DatabaseClient databaseClient = databaseClientBuilder.build(); databaseClient.execute("SELECT") // .filter(s -> s.returnGeneratedValues("foo")) // @@ -590,6 +483,49 @@ public class DefaultDatabaseClientUnitTests { inOrder.verifyNoMoreInteractions(); } + private Statement mockStatement() { + return mockStatementFor(null, null); + } + + private Statement mockStatement(Result result) { + return mockStatementFor(null, result); + } + + private Statement mockStatementFor(String sql) { + return mockStatementFor(sql, null); + } + + private Statement mockStatementFor(@Nullable String sql, @Nullable Result result) { + + Statement statement = mock(Statement.class); + when(connection.createStatement(sql == null ? anyString() : eq(sql))).thenReturn(statement); + when(statement.returnGeneratedValues(anyString())).thenReturn(statement); + when(statement.returnGeneratedValues()).thenReturn(statement); + + doReturn(result == null ? Mono.empty() : Flux.just(result)).when(statement).execute(); + + return statement; + } + + private MockResult mockSingleColumnEmptyResult() { + return mockSingleColumnResult(null); + } + + /** + * Mocks a {@link Result} with a single column "name" and a single row if a non null row is provided. + */ + private MockResult mockSingleColumnResult(@Nullable MockRow.Builder row) { + + MockRowMetadata metadata = MockRowMetadata.builder() + .columnMetadata(MockColumnMetadata.builder().name("name").build()).build(); + + MockResult.Builder resultBuilder = MockResult.builder().rowMetadata(metadata); + if (row != null) { + resultBuilder = resultBuilder.row(row.build()); + } + return resultBuilder.build(); + } + static class Person { String name;