package org.scale7.cassandra.pelops; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.scale7.cassandra.pelops.ColumnFamilyManager.CFDEF_COMPARATOR_BYTES; import static org.scale7.cassandra.pelops.ColumnFamilyManager.CFDEF_TYPE_STANDARD; import static org.scale7.cassandra.pelops.ColumnFamilyManager.CFDEF_TYPE_SUPER; import java.util.Arrays; import java.util.List; import org.apache.cassandra.thrift.CfDef; import org.apache.cassandra.thrift.Column; import org.apache.cassandra.thrift.ConsistencyLevel; import org.apache.cassandra.thrift.Mutation; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; import org.scale7.cassandra.pelops.exceptions.ModelException; import org.scale7.cassandra.pelops.pool.DebuggingPool; import org.scale7.cassandra.pelops.pool.IThriftPool; import org.scale7.cassandra.pelops.support.AbstractIntegrationTest; /** * Tests the {@link Selector} class. */ public class MutatorIntegrationTest extends AbstractIntegrationTest { public static final String CF = "MUT_CF"; public static final String SCF = "MUT_SCF"; @BeforeClass public static void setup() throws Exception { setup(Arrays.asList( new CfDef(KEYSPACE, CF) .setColumn_type(CFDEF_TYPE_STANDARD) .setComparator_type(CFDEF_COMPARATOR_BYTES), new CfDef(KEYSPACE, SCF) .setColumn_type(CFDEF_TYPE_SUPER) .setComparator_type(CFDEF_COMPARATOR_BYTES) .setSubcomparator_type(CFDEF_COMPARATOR_BYTES))); } @Test public void testConstructorDeleteIfNullState() { IThriftPool pool = Mockito.mock(IThriftPool.class); Mutator mutator = new Mutator(pool, System.currentTimeMillis(), true); assertTrue("Mutator is not in the expected state", mutator.deleteIfNull); } @Test public void testConstructorDeleteIfNullFromPolicyState() { OperandPolicy policy = new OperandPolicy(); policy.setDeleteIfNull(true); IThriftPool pool = Mockito.mock(IThriftPool.class); Mockito.when(pool.getOperandPolicy()).thenReturn(policy); Mutator mutator = new Mutator(pool); assertTrue("Mutator is not in the expected state", mutator.deleteIfNull); } @Test public void testConstructorArgs() { IThriftPool pool = Mockito.mock(IThriftPool.class); Mutator mutator = new Mutator(pool, Long.MAX_VALUE, true, Integer.MAX_VALUE); assertEquals("Mutator timestamp is not in the expected state", Long.MAX_VALUE, mutator.timestamp); assertTrue("Mutator deleteIfNull is not in the expected state", mutator.deleteIfNull); assertEquals("Mutator TTL is not in the expected state", Integer.MAX_VALUE, (int) mutator.ttl); } @Test public void testNewColumnWithTTL() { IThriftPool pool = Mockito.mock(IThriftPool.class); Mutator mutator = new Mutator(pool, Long.MAX_VALUE, true, Integer.MAX_VALUE); Column column = mutator.newColumn(Bytes.fromUTF8("a"), Bytes.fromUTF8("b"), 1234); assertEquals("column name is not in the expected state", Bytes.fromUTF8("a").getBytes(), column.name); assertEquals("column value is not in the expected state", Bytes.fromUTF8("b").getBytes(), column.value); assertEquals("column TTL is not in the expected state", 1234, column.ttl); } @Test public void testNewColumnWithTTLDefaultFromMemberVariable() { IThriftPool pool = Mockito.mock(IThriftPool.class); Mutator mutator = new Mutator(pool, Long.MAX_VALUE, true, Integer.MAX_VALUE); Column column = mutator.newColumn(Bytes.fromUTF8("a"), Bytes.fromUTF8("b")); assertEquals("column name is not in the expected state", Bytes.fromUTF8("a").getBytes(), column.name); assertEquals("column value is not in the expected state", Bytes.fromUTF8("b").getBytes(), column.value); assertEquals("column TTL is not in the expected state", Integer.MAX_VALUE, column.ttl); } @Test public void testNewColumnWithTTLNotSet() { IThriftPool pool = Mockito.mock(IThriftPool.class); Mutator mutator = new Mutator(pool, Long.MAX_VALUE, true, Integer.MAX_VALUE); Column column = mutator.newColumn(Bytes.fromUTF8("a"), Bytes.fromUTF8("b"), Mutator.NO_TTL); assertEquals("column name is not in the expected state", Bytes.fromUTF8("a").getBytes(), column.name); assertEquals("column value is not in the expected state", Bytes.fromUTF8("b").getBytes(), column.value); assertFalse("column TTL is not in the expected state", column.isSetTtl()); } @Test public void testWriteColumnsWithDeleteIfNullFromConstructor() throws Exception { IThriftPool pool = new DebuggingPool(getCluster(), KEYSPACE, new OperandPolicy(3, true)); Bytes rowKey = Bytes.fromLong(Long.MAX_VALUE); Bytes colName1 = Bytes.fromInt(1); Bytes colName2 = Bytes.fromInt(2); Mutator mutator = pool.createMutator(); assertTrue("Mutator is not in a valid state for this test", mutator.deleteIfNull); List<Column> columns = mutator.newColumnList( mutator.newColumn(colName1, (Bytes) null), mutator.newColumn(colName2, Bytes.fromInt(1)) ); mutator.writeColumns(CF, rowKey, columns); // make sure there is at least one deletion boolean isOneDeletion = false; for (Mutation mutation : mutator.getMutationList(CF, rowKey)) { if (mutation.isSetDeletion()) { isOneDeletion = true; break; } } assertTrue("There should be one deletion", isOneDeletion); pool.shutdown(); } /** * Tests that a write replaces previous values and that the appropriate columns are deleted when null values are set * on the columns. */ @Test public void testWriteColumnsWithDeletesReplacingPrevious() throws Exception { Bytes rowKey = Bytes.fromLong(Long.MAX_VALUE); // write out the value to be replaced (deleted) Mutator mutator = getPool().createMutator(); List<Column> columns = mutator.newColumnList( mutator.newColumn(Bytes.fromInt(1), Bytes.fromChar('a')), mutator.newColumn(Bytes.fromInt(2), Bytes.fromChar('b')), mutator.newColumn(Bytes.fromInt(3), Bytes.fromChar('c')) ); mutator.writeColumns(CF, rowKey, columns); mutator.execute(ConsistencyLevel.ONE); // make sure the data was written as expected Selector selector = createSelector(); List<Column> persistedColumns = selector.getColumnsFromRow(CF, rowKey, false, ConsistencyLevel.ONE); verifyColumns(columns, persistedColumns); // write out the replacement values mutator = createMutator(); columns = mutator.newColumnList( mutator.newColumn(Bytes.fromInt(1), Bytes.fromChar('d')), mutator.newColumn(Bytes.fromInt(2), (Bytes) null), mutator.newColumn(Bytes.fromInt(3), Bytes.NULL) ); mutator.writeColumns(CF, rowKey, columns, true); mutator.execute(ConsistencyLevel.ONE); // make sure the data was written as expected and that the appropriate columns have been deleted selector = createSelector(); persistedColumns = selector.getColumnsFromRow(CF, rowKey, false, ConsistencyLevel.ONE); verifyColumns(columns.subList(0, 1), persistedColumns); } @Test public void testWriteColumnsDeleteIfNullDisabled() throws Exception { Bytes rowKey = Bytes.fromLong(Long.MAX_VALUE); Mutator mutator = createMutator(); assertFalse("Mutator is not in a valid state for this test", mutator.deleteIfNull); List<Column> columns = mutator.newColumnList( mutator.newColumn(Bytes.fromInt(1), (Bytes) null) ); try { mutator.writeColumns(CF, rowKey, columns); fail("Should not reach here..."); } catch (ModelException e) { } } /** * Tests that a write replaces previous values and that the appropriate columns are deleted when null values are set * on the columns. */ @Test public void testWriteSubColumnsWithDeletesReplacingPrevious() throws Exception { Bytes rowKey = Bytes.fromLong(Long.MAX_VALUE); Bytes columnName = Bytes.fromShort(Short.MAX_VALUE); // write out the value to be replaced (deleted) Mutator mutator = createMutator(); List<Column> columns = mutator.newColumnList( mutator.newColumn(Bytes.fromInt(1), Bytes.fromChar('a')), mutator.newColumn(Bytes.fromInt(2), Bytes.fromChar('b')), mutator.newColumn(Bytes.fromInt(3), Bytes.fromChar('c')) ); mutator.writeSubColumns(SCF, rowKey, columnName, columns); mutator.execute(ConsistencyLevel.ONE); // make sure the data was written as expected Selector selector = createSelector(); List<Column> persistedColumns = selector.getSubColumnsFromRow(SCF, rowKey, columnName, false, ConsistencyLevel.ONE); verifyColumns(columns, persistedColumns); // write out the replacement values mutator = createMutator(); columns = mutator.newColumnList( mutator.newColumn(Bytes.fromInt(1), Bytes.fromChar('d')), mutator.newColumn(Bytes.fromInt(2), (Bytes) null), mutator.newColumn(Bytes.fromInt(3), Bytes.NULL) ); mutator.writeSubColumns(SCF, rowKey, columnName, columns, true); mutator.execute(ConsistencyLevel.ONE); // make sure the data was written as expected and that the appropriate columns have been deleted selector = createSelector(); persistedColumns = selector.getSubColumnsFromRow(SCF, rowKey, columnName, false, ConsistencyLevel.ONE); verifyColumns(columns.subList(0, 1), persistedColumns); } @Test public void testWriteSubColumnsDeleteIfNullDisabled() throws Exception { Bytes rowKey = Bytes.fromLong(Long.MAX_VALUE); Bytes columnName = Bytes.fromShort(Short.MAX_VALUE); Mutator mutator = createMutator(); assertFalse("Mutator is not in a valid state for this test", mutator.deleteIfNull); List<Column> columns = mutator.newColumnList( mutator.newColumn(Bytes.fromInt(1), (Bytes) null) ); try { mutator.writeSubColumns(SCF, rowKey, columnName, columns); fail("Should not reach here..."); } catch (ModelException e) { } } @Test public void testDeleteSubColumns() throws Exception { Bytes rowKey = Bytes.fromLong(Long.MAX_VALUE); Bytes columnName = Bytes.fromShort(Short.MAX_VALUE); // write out the value to be deleted Mutator mutator = createMutator(); List<Column> columns = mutator.newColumnList( mutator.newColumn(Bytes.fromInt(1), Bytes.fromChar('a')), mutator.newColumn(Bytes.fromInt(2), Bytes.fromChar('b')), mutator.newColumn(Bytes.fromInt(3), Bytes.fromChar('c')) ); mutator.writeSubColumns(SCF, rowKey, columnName, columns); mutator.execute(ConsistencyLevel.ONE); // delete all the sub columns mutator = createMutator(); mutator.deleteSubColumns(SCF, rowKey, columnName); mutator.execute(ConsistencyLevel.ONE); // verify sub columns deleted List<Column> subColumnsFromRow = createSelector().getSubColumnsFromRow(SCF, rowKey, columnName, false, ConsistencyLevel.ONE); assertThat("Sub columns were not deleted", subColumnsFromRow.size(), is(equalTo(0))); } @Test public void testDeleteSubColumnsWithNullColumnNameThrowsCorrectException() throws Exception { Bytes rowKey = Bytes.fromLong(Long.MAX_VALUE); // delete all the sub columns Mutator mutator = createMutator(); try { mutator.deleteSubColumns(SCF, rowKey, null); fail("A ModelException exception should have been thrown"); } catch (ModelException e) { } } @Test public void testTProtocolExceptionDoesNotBreakPooledConnection() throws Exception { Bytes rowKey = Bytes.fromLong(Long.MAX_VALUE); Mutator mutator = createMutator(); assertFalse("Mutator is not in a valid state for this test", mutator.deleteIfNull); try { mutator.writeColumn(CF, rowKey, mutator.newColumn(Bytes.fromInt(1), (Bytes) null)); mutator.execute(ConsistencyLevel.ONE); } catch (Exception e) { // do nothing } createSelector().getColumnsFromRow(CF, rowKey, false, ConsistencyLevel.ONE); } @Test public void testNotFoundExceptionDoesNotBreakPooledConnection() throws Exception { Bytes rowKey = Bytes.fromLong(Long.MAX_VALUE); Mutator mutator = createMutator(); assertFalse("Mutator is not in a valid state for this test", mutator.deleteIfNull); try { createSelector().getColumnsFromRow(CF, rowKey, false, ConsistencyLevel.ONE); } catch (Exception e) { // do nothing } createSelector().getColumnsFromRow(CF, rowKey, false, ConsistencyLevel.ONE); } private void verifyColumns(List<Column> expectedColumns, List<Column> actualColumns) { assertEquals("Wrong number of columns", expectedColumns.size(), actualColumns.size()); for (int i = 0; i < expectedColumns.size(); i++) { Column expectedColumn = expectedColumns.get(i); Column actualColumn = actualColumns.get(i); assertEquals("Column names didn't match", Bytes.fromByteArray(expectedColumn.getName()), Bytes.fromByteArray(actualColumn.getName())); assertEquals("Column values didn't match", Bytes.fromByteArray(expectedColumn.getValue()), Bytes.fromByteArray(actualColumn.getValue())); } } /*private void verifyWriteColumnsMutations(List<Column> columns, Mutator.MutationList mutations, boolean deleteIfNull) { assertEquals("Wrong number of mutations", columns.size(), mutations.size()); for (Column column : columns) { Mutation mutation = null; for (Mutation candidate : mutations) { byte[] candidateColumnName = candidate.isSetColumn_or_supercolumn() ? candidate.getColumn_or_supercolumn().getColumn().getName() : candidate.getDeletion().getPredicate().getColumn_namesIterator().next(); if (Arrays.equals(column.getName(), candidateColumnName)) mutation = candidate; } assertNotNull("The mutation should not be null", mutation); if (!column.isSetValue()) { if (deleteIfNull) assertTrue("The mutation should have a deletion if deleteIfNull is true", mutation.isSetDeletion()); else assertFalse("The mutation should NOT have a deletion the column has a value", mutation.isSetDeletion()); } } }*/ }