/* * Copyright 2016-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cassandra.core; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.cassandra.support.exception.CassandraConnectionFailureException; import org.springframework.cassandra.support.exception.CassandraInvalidQueryException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.util.concurrent.ListenableFuture; import com.datastax.driver.core.*; import com.datastax.driver.core.ConsistencyLevel; import com.datastax.driver.core.exceptions.InvalidQueryException; import com.datastax.driver.core.exceptions.NoHostAvailableException; import com.datastax.driver.core.policies.DowngradingConsistencyRetryPolicy; import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.SettableFuture; /** * Unit tests for {@link AsyncCqlTemplate}. * * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.class) public class AsyncCqlTemplateUnitTests { @Mock Session session; @Mock ResultSet resultSet; @Mock Row row; @Mock PreparedStatement preparedStatement; @Mock BoundStatement boundStatement; @Mock ColumnDefinitions columnDefinitions; AsyncCqlTemplate template; @Before public void setup() throws Exception { this.template = new AsyncCqlTemplate(); this.template.setSession(session); } // ------------------------------------------------------------------------- // Tests dealing with a plain com.datastax.driver.core.Session // ------------------------------------------------------------------------- @Test // DATACASS-292 public void executeCallbackShouldTranslateExceptions() throws Exception { try { template.execute((AsyncSessionCallback<String>) session -> { throw new InvalidQueryException("wrong query"); }); fail("Missing CassandraInvalidQueryException"); } catch (CassandraInvalidQueryException e) { assertThat(e).hasMessageContaining("wrong query"); } } @Test // DATACASS-292 public void executeCqlShouldTranslateExceptions() throws Exception { TestResultSetFuture resultSetFuture = TestResultSetFuture .failed(new NoHostAvailableException(Collections.emptyMap())); when(session.executeAsync(any(Statement.class))).thenReturn(resultSetFuture); ListenableFuture<Boolean> future = template.execute("UPDATE user SET a = 'b';"); try { future.get(); fail("Missing CassandraConnectionFailureException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class) .hasMessageContaining("tried for query failed"); } } // ------------------------------------------------------------------------- // Tests dealing with static CQL // ------------------------------------------------------------------------- @Test // DATACASS-292 public void executeCqlShouldCallExecution() { doTestStrings(null, null, null, asyncCqlTemplate -> { asyncCqlTemplate.execute("SELECT * from USERS"); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void executeCqlWithArgumentsShouldCallExecution() { doTestStrings(5, ConsistencyLevel.ONE, DowngradingConsistencyRetryPolicy.INSTANCE, asyncCqlTemplate -> { asyncCqlTemplate.execute("SELECT * from USERS"); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void queryForResultSetShouldCallExecution() { doTestStrings(null, null, null, asyncCqlTemplate -> { ResultSet resultSet = getUninterruptibly(asyncCqlTemplate.queryForResultSet("SELECT * from USERS")); assertThat(resultSet).hasSize(3); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void queryWithResultSetExtractorShouldCallExecution() { doTestStrings(null, null, null, asyncCqlTemplate -> { List<String> rows = getUninterruptibly( asyncCqlTemplate.query("SELECT * from USERS", (row, index) -> row.getString(0))); assertThat(rows).hasSize(3).contains("Walter", "Hank", " Jesse"); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void queryWithResultSetExtractorWithArgumentsShouldCallExecution() { doTestStrings(5, ConsistencyLevel.ONE, DowngradingConsistencyRetryPolicy.INSTANCE, asyncCqlTemplate -> { List<String> rows = getUninterruptibly( asyncCqlTemplate.query("SELECT * from USERS", (row, index) -> row.getString(0))); assertThat(rows).hasSize(3).contains("Walter", "Hank", " Jesse"); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void queryCqlShouldTranslateExceptions() throws Exception { TestResultSetFuture resultSetFuture = TestResultSetFuture .failed(new NoHostAvailableException(Collections.emptyMap())); when(session.executeAsync(any(Statement.class))).thenReturn(resultSetFuture); ListenableFuture<Boolean> future = template.query("UPDATE user SET a = 'b';", ResultSet::wasApplied); try { future.get(); fail("Missing CassandraConnectionFailureException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class) .hasMessageContaining("tried for query failed"); } } @Test // DATACASS-292 public void queryForObjectCqlShouldBeEmpty() throws Exception { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.emptyIterator()); ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK"); try { future.get(); fail("Missing IncorrectResultSizeDataAccessException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(EmptyResultDataAccessException.class) .hasMessageContaining("expected 1, actual 0"); } } @Test // DATACASS-292 public void queryForObjectCqlShouldReturnRecord() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK"); assertThat(getUninterruptibly(future)).isEqualTo("OK"); } @Test // DATACASS-292 public void queryForObjectCqlShouldReturnNullValue() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> null); assertThat(getUninterruptibly(future)).isNull(); } @Test // DATACASS-292 public void queryForObjectCqlShouldFailReturningManyRecords() throws Exception { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Arrays.asList(row, row).iterator()); ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK"); try { future.get(); fail("Missing IncorrectResultSizeDataAccessException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(IncorrectResultSizeDataAccessException.class) .hasMessageContaining("expected 1, actual 2"); } } @Test // DATACASS-292 public void queryForObjectCqlWithTypeShouldReturnRecord() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); when(row.getColumnDefinitions()).thenReturn(columnDefinitions); when(columnDefinitions.size()).thenReturn(1); when(row.getString(0)).thenReturn("OK"); ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", String.class); assertThat(getUninterruptibly(future)).isEqualTo("OK"); } @Test // DATACASS-292 public void queryForListCqlWithTypeShouldReturnRecord() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Arrays.asList(row, row).iterator()); when(row.getColumnDefinitions()).thenReturn(columnDefinitions); when(columnDefinitions.size()).thenReturn(1); when(row.getString(0)).thenReturn("OK", "NOT OK"); ListenableFuture<List<String>> future = template.queryForList("SELECT * FROM user", String.class); assertThat(getUninterruptibly(future)).contains("OK", "NOT OK"); } @Test // DATACASS-292 public void executeCqlShouldReturnWasApplied() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.wasApplied()).thenReturn(true); ListenableFuture<Boolean> future = template.execute("UPDATE user SET a = 'b';"); assertThat(getUninterruptibly(future)).isTrue(); } // ------------------------------------------------------------------------- // Tests dealing with com.datastax.driver.core.Statement // ------------------------------------------------------------------------- @Test // DATACASS-292 public void executeStatementShouldCallExecution() { doTestStrings(null, null, null, asyncCqlTemplate -> { asyncCqlTemplate.execute(new SimpleStatement("SELECT * from USERS")); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void executeStatementWithArgumentsShouldCallExecution() { doTestStrings(5, ConsistencyLevel.ONE, DowngradingConsistencyRetryPolicy.INSTANCE, asyncCqlTemplate -> { asyncCqlTemplate.execute(new SimpleStatement("SELECT * from USERS")); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void queryForResultStatementSetShouldCallExecution() { doTestStrings(null, null, null, asyncCqlTemplate -> { ListenableFuture<ResultSet> future = asyncCqlTemplate .queryForResultSet(new SimpleStatement("SELECT * from USERS")); assertThat(getUninterruptibly(future)).hasSize(3); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void queryWithResultSetStatementExtractorShouldCallExecution() { doTestStrings(null, null, null, asyncCqlTemplate -> { ListenableFuture<List<String>> future = asyncCqlTemplate.query(new SimpleStatement("SELECT * from USERS"), (row, index) -> row.getString(0)); assertThat(getUninterruptibly(future)).hasSize(3).contains("Walter", "Hank", " Jesse"); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void queryWithResultSetStatementExtractorWithArgumentsShouldCallExecution() { doTestStrings(5, ConsistencyLevel.ONE, DowngradingConsistencyRetryPolicy.INSTANCE, asyncCqlTemplate -> { ListenableFuture<List<String>> future = asyncCqlTemplate.query(new SimpleStatement("SELECT * from USERS"), (row, index) -> row.getString(0)); assertThat(getUninterruptibly(future)).hasSize(3).contains("Walter", "Hank", " Jesse"); verify(session).executeAsync(any(Statement.class)); }); } @Test // DATACASS-292 public void queryStatementShouldTranslateExceptions() throws Exception { TestResultSetFuture resultSetFuture = TestResultSetFuture .failed(new NoHostAvailableException(Collections.emptyMap())); when(session.executeAsync(any(Statement.class))).thenReturn(resultSetFuture); ListenableFuture<Boolean> future = template.query(new SimpleStatement("UPDATE user SET a = 'b';"), ResultSet::wasApplied); try { future.get(); fail("Missing CassandraConnectionFailureException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class) .hasMessageContaining("tried for query failed"); } } @Test // DATACASS-292 public void queryForObjectStatementShouldBeEmpty() throws Exception { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.emptyIterator()); ListenableFuture<String> future = template.queryForObject(new SimpleStatement("SELECT * FROM user"), (row, rowNum) -> "OK"); try { future.get(); fail("Missing IncorrectResultSizeDataAccessException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(EmptyResultDataAccessException.class) .hasMessageContaining("expected 1, actual 0"); } } @Test // DATACASS-292 public void queryForObjectStatementShouldReturnRecord() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); ListenableFuture<String> future = template.queryForObject(new SimpleStatement("SELECT * FROM user"), (row, rowNum) -> "OK"); assertThat(getUninterruptibly(future)).isEqualTo("OK"); } @Test // DATACASS-292 public void queryForObjectStatementShouldReturnNullValue() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); ListenableFuture<String> future = template.queryForObject(new SimpleStatement("SELECT * FROM user"), (row, rowNum) -> null); assertThat(getUninterruptibly(future)).isNull(); } @Test // DATACASS-292 public void queryForObjectStatementShouldFailReturningManyRecords() throws Exception { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Arrays.asList(row, row).iterator()); ListenableFuture<String> future = template.queryForObject(new SimpleStatement("SELECT * FROM user"), (row, rowNum) -> "OK"); try { future.get(); fail("Missing IncorrectResultSizeDataAccessException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(IncorrectResultSizeDataAccessException.class) .hasMessageContaining("expected 1, actual 2"); } } @Test // DATACASS-292 public void queryForObjectStatementWithTypeShouldReturnRecord() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); when(row.getColumnDefinitions()).thenReturn(columnDefinitions); when(columnDefinitions.size()).thenReturn(1); when(row.getString(0)).thenReturn("OK"); ListenableFuture<String> future = template.queryForObject(new SimpleStatement("SELECT * FROM user"), String.class); assertThat(getUninterruptibly(future)).isEqualTo("OK"); } @Test // DATACASS-292 public void queryForListStatementWithTypeShouldReturnRecord() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Arrays.asList(row, row).iterator()); when(row.getColumnDefinitions()).thenReturn(columnDefinitions); when(columnDefinitions.size()).thenReturn(1); when(row.getString(0)).thenReturn("OK", "NOT OK"); ListenableFuture<List<String>> future = template.queryForList(new SimpleStatement("SELECT * FROM user"), String.class); assertThat(getUninterruptibly(future)).contains("OK", "NOT OK"); } @Test // DATACASS-292 public void executeStatementShouldReturnWasApplied() { when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.wasApplied()).thenReturn(true); ListenableFuture<Boolean> future = template.execute(new SimpleStatement("UPDATE user SET a = 'b';")); assertThat(getUninterruptibly(future)).isTrue(); } // ------------------------------------------------------------------------- // Methods dealing with prepared statements // ------------------------------------------------------------------------- @Test // DATACASS-292 public void queryPreparedStatementWithCallbackShouldCallExecution() { doTestStrings(null, null, null, asyncCqlTemplate -> { ListenableFuture<ResultSetFuture> futureOfFuture = asyncCqlTemplate.execute("SELECT * from USERS", (session, ps) -> session.executeAsync(ps.bind("A"))); try { assertThat(getUninterruptibly(futureOfFuture).get()).hasSize(3); } catch (Exception e) { fail(e.getMessage(), e); } }); } @Test // DATACASS-292 public void executePreparedStatementWithCallbackShouldCallExecution() { doTestStrings(null, null, null, asyncCqlTemplate -> { when(this.preparedStatement.bind("White")).thenReturn(this.boundStatement); when(this.resultSet.wasApplied()).thenReturn(true); ListenableFuture<Boolean> applied = asyncCqlTemplate.execute("UPDATE users SET name = ?", "White"); assertThat(getUninterruptibly(applied)).isTrue(); }); } @Test // DATACASS-292 public void executePreparedStatementCreatorShouldTranslateStatementCreationExceptions() throws Exception { try { template.execute(session -> { throw new NoHostAvailableException(Collections.emptyMap()); }, (session, ps) -> session.executeAsync(boundStatement)); fail("Missing CassandraConnectionFailureException"); } catch (CassandraConnectionFailureException e) { assertThat(e).hasMessageContaining("tried for query"); } ListenableFuture<ResultSetFuture> future = template.execute( session -> AsyncResult.forExecutionException(new NoHostAvailableException(Collections.emptyMap())), (session, ps) -> session.executeAsync(boundStatement)); try { future.get(); fail("Missing CassandraConnectionFailureException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class) .hasMessageContaining("tried for query"); } } @Test // DATACASS-292 public void executePreparedStatementCreatorShouldTranslateStatementCallbackExceptions() throws Exception { ListenableFuture<ResultSetFuture> future = template.execute(session -> new AsyncResult<>(preparedStatement), (session, ps) -> { throw new NoHostAvailableException(Collections.emptyMap()); }); try { future.get(); fail("Missing CassandraConnectionFailureException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class) .hasMessageContaining("tried for query"); } } @Test // DATACASS-292 public void queryPreparedStatementCreatorShouldReturnResult() { when(preparedStatement.bind()).thenReturn(boundStatement); when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); ListenableFuture<Iterator<Row>> future = template.query(session -> new AsyncResult<>(preparedStatement), ResultSet::iterator); assertThat(getUninterruptibly(future)).hasSize(1).contains(row); verify(preparedStatement).bind(); } @Test // DATACASS-292 public void queryPreparedStatementCreatorAndBinderShouldReturnResult() { when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); ListenableFuture<ResultSet> future = template .query(session -> new AsyncResult<PreparedStatement>(preparedStatement), ps -> { ps.bind("a", "b"); return boundStatement; }, rs -> rs); assertThat(getUninterruptibly(future)).contains(row); verify(preparedStatement).bind("a", "b"); } @Test // DATACASS-292 public void queryPreparedStatementCreatorAndBinderShouldTranslatePrepareStatementExceptions() throws Exception { ListenableFuture<ResultSet> future = template.query( session -> AsyncResult.forExecutionException(new NoHostAvailableException(Collections.emptyMap())), ps -> { ps.bind("a", "b"); return boundStatement; }, rs -> rs); try { future.get(); fail("Missing CassandraConnectionFailureException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class); } } @Test // DATACASS-292 public void queryPreparedStatementCreatorAndBinderShouldTranslateBindExceptions() throws Exception { ListenableFuture<ResultSet> future = template .query(session -> new AsyncResult<PreparedStatement>(preparedStatement), ps -> { throw new NoHostAvailableException(Collections.emptyMap()); }, rs -> rs); try { future.get(); fail("Missing CassandraConnectionFailureException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class); } } @Test // DATACASS-292 public void queryPreparedStatementCreatorAndBinderShouldTranslateExecutionExceptions() throws Exception { TestResultSetFuture resultSetFuture = TestResultSetFuture .failed(new NoHostAvailableException(Collections.emptyMap())); when(session.executeAsync(boundStatement)).thenReturn(resultSetFuture); ListenableFuture<ResultSet> future = template .query(session -> new AsyncResult<PreparedStatement>(preparedStatement), ps -> { ps.bind("a", "b"); return boundStatement; }, rs -> rs); try { future.get(); fail("Missing CassandraConnectionFailureException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class); } } @Test // DATACASS-292 public void queryPreparedStatementCreatorAndBinderAndMapperShouldReturnResult() { when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); ListenableFuture<List<Row>> future = template.query(session -> new AsyncResult<>(preparedStatement), ps -> { ps.bind("a", "b"); return boundStatement; }, (row, rowNum) -> row); assertThat(getUninterruptibly(future)).hasSize(1).contains(row); verify(preparedStatement).bind("a", "b"); } @Test // DATACASS-292 public void queryForObjectPreparedStatementShouldBeEmpty() throws Exception { when(session.prepareAsync("SELECT * FROM user WHERE username = ?")) .thenReturn(new TestPreparedStatementFuture(preparedStatement)); when(preparedStatement.bind("Walter")).thenReturn(boundStatement); when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.emptyIterator()); ListenableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?", (row, rowNum) -> "OK", "Walter"); try { future.get(); fail("Missing IncorrectResultSizeDataAccessException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(EmptyResultDataAccessException.class) .hasMessageContaining("expected 1, actual 0"); } } @Test // DATACASS-292 public void queryForObjectPreparedStatementShouldReturnRecord() { when(session.prepareAsync("SELECT * FROM user WHERE username = ?")) .thenReturn(new TestPreparedStatementFuture(preparedStatement)); when(preparedStatement.bind("Walter")).thenReturn(boundStatement); when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); ListenableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?", (row, rowNum) -> "OK", "Walter"); assertThat(getUninterruptibly(future)).isEqualTo("OK"); } @Test // DATACASS-292 public void queryForObjectPreparedStatementShouldFailReturningManyRecords() throws Exception { when(session.prepareAsync("SELECT * FROM user WHERE username = ?")) .thenReturn(new TestPreparedStatementFuture(preparedStatement)); when(preparedStatement.bind("Walter")).thenReturn(boundStatement); when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Arrays.asList(row, row).iterator()); ListenableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?", (row, rowNum) -> "OK", "Walter"); try { future.get(); fail("Missing IncorrectResultSizeDataAccessException"); } catch (ExecutionException e) { assertThat(e).hasCauseInstanceOf(IncorrectResultSizeDataAccessException.class) .hasMessageContaining("expected 1, actual 2"); } } @Test // DATACASS-292 public void queryForObjectPreparedStatementWithTypeShouldReturnRecord() { when(session.prepareAsync("SELECT * FROM user WHERE username = ?")) .thenReturn(new TestPreparedStatementFuture(preparedStatement)); when(preparedStatement.bind("Walter")).thenReturn(boundStatement); when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Collections.singleton(row).iterator()); when(row.getColumnDefinitions()).thenReturn(columnDefinitions); when(columnDefinitions.size()).thenReturn(1); when(row.getString(0)).thenReturn("OK"); Future<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?", String.class, "Walter"); assertThat(getUninterruptibly(future)).isEqualTo("OK"); } @Test // DATACASS-292 public void queryForListPreparedStatementWithTypeShouldReturnRecord() { when(session.prepareAsync("SELECT * FROM user WHERE username = ?")) .thenReturn(new TestPreparedStatementFuture(preparedStatement)); when(preparedStatement.bind("Walter")).thenReturn(boundStatement); when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.iterator()).thenReturn(Arrays.asList(row, row).iterator()); when(row.getColumnDefinitions()).thenReturn(columnDefinitions); when(columnDefinitions.size()).thenReturn(1); when(row.getString(0)).thenReturn("OK", "NOT OK"); ListenableFuture<List<String>> future = template.queryForList("SELECT * FROM user WHERE username = ?", String.class, "Walter"); assertThat(getUninterruptibly(future)).contains("OK", "NOT OK"); } @Test // DATACASS-292 public void updatePreparedStatementShouldReturnApplied() { when(session.prepareAsync("UPDATE user SET username = ?")) .thenReturn(new TestPreparedStatementFuture(preparedStatement)); when(preparedStatement.bind("Walter")).thenReturn(boundStatement); when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet)); when(resultSet.wasApplied()).thenReturn(true); ListenableFuture<Boolean> future = template.execute("UPDATE user SET username = ?", "Walter"); assertThat(getUninterruptibly(future)).isTrue(); } private <T> void doTestStrings(Integer fetchSize, ConsistencyLevel consistencyLevel, com.datastax.driver.core.policies.RetryPolicy retryPolicy, Consumer<AsyncCqlTemplate> cqlTemplateConsumer) { String[] results = { "Walter", "Hank", " Jesse" }; when(this.session.executeAsync((Statement) any())).thenReturn(new TestResultSetFuture(resultSet)); when(this.resultSet.iterator()).thenReturn(Arrays.asList(row, row, row).iterator()); when(this.row.getString(0)).thenReturn(results[0], results[1], results[2]); SettableFuture<PreparedStatement> settableFuture = SettableFuture.create(); settableFuture.set(this.preparedStatement); when(this.session.prepareAsync(anyString())).thenReturn(settableFuture); AsyncCqlTemplate template = new AsyncCqlTemplate(); template.setSession(this.session); if (fetchSize != null) { template.setFetchSize(fetchSize); } if (retryPolicy != null) { template.setRetryPolicy(retryPolicy); } if (consistencyLevel != null) { template.setConsistencyLevel(consistencyLevel); } cqlTemplateConsumer.accept(template); ArgumentCaptor<Statement> statementArgumentCaptor = ArgumentCaptor.forClass(Statement.class); verify(this.session).executeAsync(statementArgumentCaptor.capture()); Statement statement = statementArgumentCaptor.getValue(); if (statement instanceof PreparedStatement || statement instanceof BoundStatement) { if (fetchSize != null) { verify(statement).setFetchSize(fetchSize.intValue()); } if (retryPolicy != null) { verify(statement).setRetryPolicy(retryPolicy); } if (consistencyLevel != null) { verify(statement).setConsistencyLevel(consistencyLevel); } } else { if (fetchSize != null) { assertThat(statement.getFetchSize()).isEqualTo(fetchSize.intValue()); } if (retryPolicy != null) { assertThat(statement.getRetryPolicy()).isEqualTo(retryPolicy); } if (consistencyLevel != null) { assertThat(statement.getConsistencyLevel()).isEqualTo(consistencyLevel); } } } private static <T> T getUninterruptibly(Future<T> future) { try { return future.get(); } catch (Exception e) { throw new IllegalStateException(e); } } private static class TestResultSetFuture extends AbstractFuture<ResultSet> implements ResultSetFuture { public TestResultSetFuture() {} public TestResultSetFuture(ResultSet resultSet) { set(resultSet); } @Override public boolean set(ResultSet value) { return super.set(value); } @Override public ResultSet getUninterruptibly() { return null; } @Override public ResultSet getUninterruptibly(long l, TimeUnit timeUnit) throws TimeoutException { return null; } @Override protected boolean setException(Throwable throwable) { return super.setException(throwable); } /** * Create a completed future that reports a failure given {@link Throwable}. * * @param throwable must not be {@literal null}. * @return the completed/failed {@link TestResultSetFuture}. */ public static TestResultSetFuture failed(Throwable throwable) { TestResultSetFuture future = new TestResultSetFuture(); future.setException(throwable); return future; } } private static class TestPreparedStatementFuture extends AbstractFuture<PreparedStatement> { public TestPreparedStatementFuture() {} public TestPreparedStatementFuture(PreparedStatement resultSet) { set(resultSet); } @Override public boolean set(PreparedStatement value) { return super.set(value); } } }