DATACASS-286 - Log all CQL queries executed with CqlTemplate.
Additional refactoring to simplify CqlTemplate logic as well as make CqlTemplate methods more behaviorally consistent. Origina pull request: #61.
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
|
||||
import com.datastax.driver.core.ColumnDefinitions;
|
||||
import com.datastax.driver.core.ResultSet;
|
||||
import com.datastax.driver.core.Row;
|
||||
import com.datastax.driver.core.Session;
|
||||
|
||||
/**
|
||||
* The CqlTemplateUnitTests class is a test suite of test cases testing the contract and functionality of the
|
||||
* {@link CqlTemplate} class.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.cassandra.core.CqlTemplate
|
||||
* @since 1.5.0
|
||||
*/
|
||||
// TODO: add many more unit tests until SUT test coverage is 100%!
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CqlTemplateUnitTests {
|
||||
|
||||
@Rule public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
private CqlTemplate template;
|
||||
|
||||
@Mock private Session mockSession;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
template = new CqlTemplate(mockSession);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void firstColumnToObjectReturnsColumnValue() {
|
||||
final Row mockRow = mock(Row.class);
|
||||
ColumnDefinitions mockColumnDefinitions = mock(ColumnDefinitions.class);
|
||||
Iterator<ColumnDefinitions.Definition> mockIterator = mock(Iterator.class);
|
||||
final ColumnDefinitions.Definition mockColumnDefinition = mock(ColumnDefinitions.Definition.class);
|
||||
|
||||
when(mockRow.getColumnDefinitions()).thenReturn(mockColumnDefinitions);
|
||||
when(mockColumnDefinitions.iterator()).thenReturn(mockIterator);
|
||||
when(mockIterator.hasNext()).thenReturn(true);
|
||||
when(mockIterator.next()).thenReturn(mockColumnDefinition);
|
||||
|
||||
template = new CqlTemplate() {
|
||||
@Override
|
||||
<T> T columnToObject(Row row, ColumnDefinitions.Definition columnDefinition) {
|
||||
assertThat(row, is(sameInstance(mockRow)));
|
||||
assertThat(columnDefinition, is(sameInstance(mockColumnDefinition)));
|
||||
return (T) "test";
|
||||
}
|
||||
};
|
||||
|
||||
assertThat(String.valueOf(template.firstColumnToObject(mockRow)), is(equalTo("test")));
|
||||
|
||||
verify(mockRow, times(1)).getColumnDefinitions();
|
||||
verify(mockColumnDefinitions, times(1)).iterator();
|
||||
verify(mockIterator, times(1)).hasNext();
|
||||
verify(mockIterator, times(1)).next();
|
||||
verifyZeroInteractions(mockColumnDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void firstColumnToObjectReturnsNull() {
|
||||
Row mockRow = mock(Row.class);
|
||||
ColumnDefinitions mockColumnDefinitions = mock(ColumnDefinitions.class);
|
||||
Iterator<ColumnDefinitions.Definition> mockIterator = mock(Iterator.class);
|
||||
|
||||
when(mockRow.getColumnDefinitions()).thenReturn(mockColumnDefinitions);
|
||||
when(mockColumnDefinitions.iterator()).thenReturn(mockIterator);
|
||||
when(mockIterator.hasNext()).thenReturn(false);
|
||||
|
||||
assertThat(template.firstColumnToObject(mockRow), is(nullValue(Object.class)));
|
||||
|
||||
verify(mockRow, times(1)).getColumnDefinitions();
|
||||
verify(mockColumnDefinitions, times(1)).iterator();
|
||||
verify(mockIterator, times(1)).hasNext();
|
||||
verify(mockIterator, never()).next();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void processOneIsSuccessful() {
|
||||
ResultSet mockResultSet = mock(ResultSet.class);
|
||||
Row mockRow = mock(Row.class);
|
||||
RowMapper<String> mockRowMapper = mock(RowMapper.class);
|
||||
|
||||
when(mockResultSet.one()).thenReturn(mockRow);
|
||||
when(mockResultSet.isExhausted()).thenReturn(true);
|
||||
when(mockRowMapper.mapRow(eq(mockRow), eq(0))).thenReturn("test");
|
||||
|
||||
assertThat(template.processOne(mockResultSet, mockRowMapper), is(equalTo("test")));
|
||||
|
||||
verify(mockResultSet, times(1)).one();
|
||||
verify(mockResultSet, times(1)).isExhausted();
|
||||
verify(mockRowMapper, times(1)).mapRow(eq(mockRow), eq(0));
|
||||
verifyZeroInteractions(mockRow);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processOneThrowsIncorrectResultSetSizeDataAccessExceptionWhenNoRowsFound() {
|
||||
ResultSet mockResultSet = mock(ResultSet.class);
|
||||
RowMapper mockRowMapper = mock(RowMapper.class);
|
||||
|
||||
when(mockResultSet.one()).thenReturn(null);
|
||||
|
||||
try {
|
||||
exception.expect(IncorrectResultSizeDataAccessException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(containsString("expected 1, actual 0"));
|
||||
|
||||
template.processOne(mockResultSet, mockRowMapper);
|
||||
|
||||
} finally {
|
||||
verify(mockResultSet, times(1)).one();
|
||||
verify(mockResultSet, never()).isExhausted();
|
||||
verifyZeroInteractions(mockRowMapper);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processOneThrowsIncorrectResultSetSizeDataAccessExceptionWhenTooManyRowsFound() {
|
||||
ResultSet mockResultSet = mock(ResultSet.class);
|
||||
Row mockRow = mock(Row.class);
|
||||
RowMapper mockRowMapper = mock(RowMapper.class);
|
||||
|
||||
when(mockResultSet.one()).thenReturn(mockRow);
|
||||
when(mockResultSet.isExhausted()).thenReturn(false);
|
||||
|
||||
try {
|
||||
exception.expect(IncorrectResultSizeDataAccessException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("ResultSet size exceeds 1");
|
||||
|
||||
template.processOne(mockResultSet, mockRowMapper);
|
||||
|
||||
} finally {
|
||||
verify(mockResultSet, times(1)).one();
|
||||
verify(mockResultSet, times(1)).isExhausted();
|
||||
verifyZeroInteractions(mockRowMapper);
|
||||
verifyZeroInteractions(mockRow);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processOnePassingNullResultSetThrowsIllegalArgumentException() {
|
||||
RowMapper mockRowMapper = mock(RowMapper.class);
|
||||
|
||||
try {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("ResultSet cannot be null");
|
||||
|
||||
template.processOne(null, mockRowMapper);
|
||||
} finally {
|
||||
verifyZeroInteractions(mockRowMapper);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void processOneWithRequiredTypeIsSuccessful() {
|
||||
ResultSet mockResultSet = mock(ResultSet.class);
|
||||
final Row mockRow = mock(Row.class);
|
||||
|
||||
when(mockResultSet.one()).thenReturn(mockRow);
|
||||
when(mockResultSet.isExhausted()).thenReturn(true);
|
||||
|
||||
template = new CqlTemplate() {
|
||||
@Override
|
||||
protected Object firstColumnToObject(Row row) {
|
||||
assertThat(row, is(equalTo(mockRow)));
|
||||
return 1l;
|
||||
}
|
||||
};
|
||||
|
||||
Number value = template.processOne(mockResultSet, Long.class);
|
||||
|
||||
assertThat(value, is(instanceOf(Long.class)));
|
||||
assertThat(value.longValue(), is(equalTo(1l)));
|
||||
|
||||
verify(mockResultSet, times(1)).one();
|
||||
verify(mockResultSet, times(1)).isExhausted();
|
||||
verifyZeroInteractions(mockRow);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processOneWithRequiredTypeThrowsIncorrectResultSetSizeDataAccessExceptionWhenNoRowsFound() {
|
||||
ResultSet mockResultSet = mock(ResultSet.class);
|
||||
|
||||
when(mockResultSet.one()).thenReturn(null);
|
||||
|
||||
try {
|
||||
exception.expect(IncorrectResultSizeDataAccessException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(containsString("expected 1, actual 0"));
|
||||
|
||||
template.processOne(mockResultSet, Integer.class);
|
||||
|
||||
} finally {
|
||||
verify(mockResultSet, times(1)).one();
|
||||
verify(mockResultSet, never()).isExhausted();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processOneWithRequiredTypeThrowsIncorrectResultSetSizeDataAccessExceptionWhenTooManyRowsFound() {
|
||||
ResultSet mockResultSet = mock(ResultSet.class);
|
||||
Row mockRow = mock(Row.class);
|
||||
|
||||
when(mockResultSet.one()).thenReturn(mockRow);
|
||||
when(mockResultSet.isExhausted()).thenReturn(false);
|
||||
|
||||
try {
|
||||
exception.expect(IncorrectResultSizeDataAccessException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(containsString("ResultSet size exceeds 1"));
|
||||
|
||||
template.processOne(mockResultSet, Double.class);
|
||||
|
||||
} finally {
|
||||
verify(mockResultSet, times(1)).one();
|
||||
verify(mockResultSet, times(1)).isExhausted();
|
||||
verifyZeroInteractions(mockRow);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processOneWithRequiredTypePassingNullResultSetThrowsIllegalArgumentException() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(is(equalTo("ResultSet cannot be null")));
|
||||
|
||||
template.processOne(null, String.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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.support;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import com.datastax.driver.core.Session;
|
||||
|
||||
/**
|
||||
* The CassandraAccessorUnitTests class is a test suite of test cases testing the contract and functionality of the
|
||||
* {@link CassandraAccessor} class.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.cassandra.support.CassandraAccessor
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CassandraAccessorUnitTests {
|
||||
|
||||
private CassandraAccessor cassandraAccessor;
|
||||
|
||||
@Mock private CassandraExceptionTranslator mockExceptionTranslator;
|
||||
|
||||
@Rule public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Mock private Session mockSession;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
cassandraAccessor = new CassandraAccessor();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterPropertiesSetWithUnitializedSessionThrowsIllegalStateException() {
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("Session must not be null");
|
||||
|
||||
cassandraAccessor.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetExceptionTranslator() {
|
||||
cassandraAccessor.setExceptionTranslator(mockExceptionTranslator);
|
||||
assertThat(cassandraAccessor.getExceptionTranslator(), is(sameInstance(mockExceptionTranslator)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setExceptionTranslatorToNullThrowsIllegalArgumentException() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(is(equalTo("CassandraExceptionTranslator must not be null")));
|
||||
|
||||
cassandraAccessor.setExceptionTranslator(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getUninitializedExceptionTranslatorReturnsDefault() {
|
||||
assertThat(cassandraAccessor.getExceptionTranslator(), is(equalTo(cassandraAccessor.exceptionTranslator)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetSession() {
|
||||
cassandraAccessor.setSession(mockSession);
|
||||
assertThat(cassandraAccessor.getSession(), is(sameInstance(mockSession)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setSessionToNullThrowsIllegalArgumentException() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(is(equalTo("Session must not be null")));
|
||||
|
||||
cassandraAccessor.setSession(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getUninitializedSessionThrowsIllegalStateException() {
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(is(equalTo("Session was not properly initialized")));
|
||||
|
||||
cassandraAccessor.getSession();
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ import org.springframework.cassandra.core.WriteOptions;
|
||||
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
|
||||
import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.datastax.driver.core.BoundStatement;
|
||||
@@ -607,19 +608,18 @@ public class CqlOperationsIntegrationTests extends AbstractKeyspaceCreatingInteg
|
||||
/**
|
||||
* Test that CQL for QueryForObject must only return 1 row or an IllegalArgumentException is thrown.
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@Test(expected = IncorrectResultSizeDataAccessException.class)
|
||||
public void queryForObjectTestCqlStringRowMapperNotOneRowReturned() {
|
||||
|
||||
// Insert our 3 test books.
|
||||
insertTestObjectArray();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
Book book = cqlTemplate.queryForObject("select * from book where isbn in ('1234','2345','3456')",
|
||||
Book book = cqlTemplate.queryForObject("SELECT * FROM book WHERE isbn IN('1234','2345','3456')",
|
||||
new RowMapper<Book>() {
|
||||
@Override
|
||||
public Book mapRow(Row row, int rowNum) throws DriverException {
|
||||
Book b = rowToBook(row);
|
||||
return b;
|
||||
return rowToBook(row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user