testing coming along

This commit is contained in:
Matthew Adams
2013-11-22 19:11:49 -06:00
parent 7c6a003943
commit 8aa86d7270
29 changed files with 910 additions and 229 deletions

View File

@@ -60,6 +60,7 @@ dependencies {
testCompile("javax.annotation:jsr250-api:1.0", optional)
testCompile("com.thoughtworks.xstream:xstream:1.3", optional)
testCompile "org.cassandraunit:cassandra-unit:$cassandraUnitVersion"
testCompile "org.cassandraunit:cassandra-unit-spring:$cassandraUnitVersion"
testCompile "cglib:cglib-nodep:$cglibVersion"
}

View File

@@ -16,7 +16,7 @@ import org.springframework.cassandra.core.keyspace.Option;
*
* @author Matthew T. Adams
*/
public class AlterTableCqlGenerator extends AbstractTableOperationCqlGenerator<AlterTableSpecification> {
public class AlterTableCqlGenerator extends TableOptionsCqlGenerator<AlterTableSpecification> {
public AlterTableCqlGenerator(AlterTableSpecification specification) {
super(specification);

View File

@@ -17,7 +17,7 @@ import org.springframework.cassandra.core.keyspace.Option;
*
* @author Matthew T. Adams
*/
public class CreateTableCqlGenerator extends AbstractTableOperationCqlGenerator<CreateTableSpecification> {
public class CreateTableCqlGenerator extends TableCqlGenerator<CreateTableSpecification> {
public CreateTableCqlGenerator(CreateTableSpecification specification) {
super(specification);
@@ -110,7 +110,7 @@ public class CreateTableCqlGenerator extends AbstractTableOperationCqlGenerator<
clustering.append(")");
}
boolean parenthesize = partitionKeys.size() + primaryKeys.size() > 1;
boolean parenthesize = true;// partitionKeys.size() + primaryKeys.size() > 1;
cql.append(parenthesize ? "(" : "");
cql.append(partitions);
@@ -118,6 +118,8 @@ public class CreateTableCqlGenerator extends AbstractTableOperationCqlGenerator<
cql.append(primaries);
cql.append(parenthesize ? ")" : "");
// end primary key clause
cql.append(")");
// end columns
// begin options

View File

@@ -3,32 +3,20 @@ package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import org.springframework.cassandra.core.keyspace.DropTableSpecification;
import org.springframework.util.Assert;
/**
* CQL generator for generating a <code>DROP TABLE</code> statement.
*
* @author Matthew T. Adams
*/
public class DropTableCqlGenerator {
protected DropTableSpecification specification;
public class DropTableCqlGenerator extends TableNameCqlGenerator<DropTableSpecification> {
public DropTableCqlGenerator(DropTableSpecification specification) {
setSpecification(specification);
}
protected void setSpecification(DropTableSpecification specification) {
Assert.notNull(specification);
this.specification = specification;
super(specification);
}
public StringBuilder toCql(StringBuilder cql) {
return noNull(cql).append("DROP TABLE ").append(specification.getIfExists() ? "IF EXISTS " : "")
.append(specification.getNameAsIdentifier());
}
public String toCql() {
return toCql(null).toString();
return noNull(cql).append("DROP TABLE ").append(spec().getIfExists() ? "IF EXISTS " : "")
.append(spec().getNameAsIdentifier()).append(";");
}
}

View File

@@ -6,9 +6,8 @@ import static org.springframework.cassandra.core.cql.CqlStringUtils.singleQuote;
import java.util.Map;
import org.springframework.cassandra.core.keyspace.AbstractTableSpecification;
import org.springframework.cassandra.core.keyspace.Option;
import org.springframework.util.Assert;
import org.springframework.cassandra.core.keyspace.TableSpecification;
/**
* Base class that contains behavior common to CQL generation for table operations.
@@ -16,31 +15,16 @@ import org.springframework.util.Assert;
* @author Matthew T. Adams
* @param T The subtype of this class for which this is a CQL generator.
*/
public abstract class AbstractTableOperationCqlGenerator<T extends AbstractTableSpecification<T>> {
public abstract class TableCqlGenerator<T extends TableSpecification<T>> extends
TableOptionsCqlGenerator<TableSpecification<T>> {
public abstract StringBuilder toCql(StringBuilder cql);
private AbstractTableSpecification<T> specification;
public AbstractTableOperationCqlGenerator(AbstractTableSpecification<T> specification) {
setSpecification(specification);
}
protected void setSpecification(AbstractTableSpecification<T> specification) {
Assert.notNull(specification);
this.specification = specification;
public TableCqlGenerator(TableSpecification<T> specification) {
super(specification);
}
@SuppressWarnings("unchecked")
public T getSpecification() {
return (T) specification;
}
/**
* Convenient synonymous method of {@link #getSpecification()}.
*/
protected T spec() {
return getSpecification();
return (T) getSpecification();
}
protected StringBuilder optionValueMap(Map<Option, Object> valueMap, StringBuilder cql) {
@@ -78,8 +62,4 @@ public abstract class AbstractTableOperationCqlGenerator<T extends AbstractTable
return cql;
}
public String toCql() {
return toCql(null).toString();
}
}

View File

@@ -0,0 +1,36 @@
package org.springframework.cassandra.core.cql.generator;
import org.springframework.cassandra.core.keyspace.TableNameSpecification;
import org.springframework.util.Assert;
public abstract class TableNameCqlGenerator<T extends TableNameSpecification<T>> {
public abstract StringBuilder toCql(StringBuilder cql);
private TableNameSpecification<T> specification;
public TableNameCqlGenerator(TableNameSpecification<T> specification) {
setSpecification(specification);
}
protected void setSpecification(TableNameSpecification<T> specification) {
Assert.notNull(specification);
this.specification = specification;
}
@SuppressWarnings("unchecked")
public T getSpecification() {
return (T) specification;
}
/**
* Convenient synonymous method of {@link #getSpecification()}.
*/
protected T spec() {
return getSpecification();
}
public String toCql() {
return toCql(null).toString();
}
}

View File

@@ -0,0 +1,65 @@
package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.escapeSingle;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import static org.springframework.cassandra.core.cql.CqlStringUtils.singleQuote;
import java.util.Map;
import org.springframework.cassandra.core.keyspace.Option;
import org.springframework.cassandra.core.keyspace.TableOptionsSpecification;
/**
* Base class that contains behavior common to CQL generation for table operations.
*
* @author Matthew T. Adams
* @param T The subtype of this class for which this is a CQL generator.
*/
public abstract class TableOptionsCqlGenerator<T extends TableOptionsSpecification<T>> extends
TableNameCqlGenerator<TableOptionsSpecification<T>> {
public TableOptionsCqlGenerator(TableOptionsSpecification<T> specification) {
super(specification);
}
@SuppressWarnings("unchecked")
protected T spec() {
return (T) getSpecification();
}
protected StringBuilder optionValueMap(Map<Option, Object> valueMap, StringBuilder cql) {
cql = noNull(cql);
if (valueMap == null || valueMap.isEmpty()) {
return cql;
}
// else option value is a non-empty map
// append { 'name' : 'value', ... }
cql.append("{ ");
boolean mapFirst = true;
for (Map.Entry<Option, Object> entry : valueMap.entrySet()) {
if (mapFirst) {
mapFirst = false;
} else {
cql.append(", ");
}
Option option = entry.getKey();
cql.append(singleQuote(option.getName())); // entries in map keys are always quoted
cql.append(" : ");
Object entryValue = entry.getValue();
entryValue = entryValue == null ? "" : entryValue.toString();
if (option.escapesValue()) {
entryValue = escapeSingle(entryValue);
}
if (option.quotesValue()) {
entryValue = singleQuote(entryValue);
}
cql.append(entryValue);
}
cql.append(" }");
return cql;
}
}

View File

@@ -6,25 +6,45 @@ import java.util.List;
import com.datastax.driver.core.DataType;
public class AlterTableSpecification extends AbstractTableSpecification<AlterTableSpecification> {
/**
* Builder class to construct an <code>ALTER TABLE</code> specification.
*
* @author Matthew T. Adams
*/
public class AlterTableSpecification extends TableOptionsSpecification<AlterTableSpecification> {
/**
* The list of column changes.
*/
private List<ColumnChangeSpecification> changes = new ArrayList<ColumnChangeSpecification>();
/**
* Adds a <code>DROP</code> to the list of column changes.
*/
public AlterTableSpecification drop(String column) {
changes.add(new DropColumnSpecification(column));
return this;
}
/**
* Adds an <code>ADD</code> to the list of column changes.
*/
public AlterTableSpecification add(String column, DataType type) {
changes.add(new AddColumnSpecification(column, type));
return this;
}
/**
* Adds an <code>ALTER</code> to the list of column changes.
*/
public AlterTableSpecification alter(String column, DataType type) {
changes.add(new AlterColumnSpecification(column, type));
return this;
}
/**
* Returns an unmodifiable list of column changes.
*/
public List<ColumnChangeSpecification> getChanges() {
return Collections.unmodifiableList(changes);
}

View File

@@ -1,27 +1,13 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.data.cassandra.mapping.KeyType.PARTITION;
import static org.springframework.data.cassandra.mapping.KeyType.PRIMARY;
import static org.springframework.data.cassandra.mapping.Ordering.ASCENDING;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.data.cassandra.mapping.KeyType;
import org.springframework.data.cassandra.mapping.Ordering;
import com.datastax.driver.core.DataType;
/**
* Builder class to construct CQL for a <code>CREATE TABLE</code> statement. Not threadsafe.
* Builder class to construct a <code>CREATE TABLE</code> specification.
*
* @author Matthew T. Adams
*/
public class CreateTableSpecification extends AbstractTableSpecification<CreateTableSpecification> {
public class CreateTableSpecification extends TableSpecification<CreateTableSpecification> {
private boolean ifNotExists = false;
private List<ColumnSpecification> columns = new ArrayList<ColumnSpecification>();
/**
* Causes the inclusion of an <code>IF NOT EXISTS</code> clause.
@@ -42,36 +28,7 @@ public class CreateTableSpecification extends AbstractTableSpecification<CreateT
return this;
}
public CreateTableSpecification column(String name, DataType type) {
return column(name, type, null, null);
}
public CreateTableSpecification partitionKeyColumn(String name, DataType type) {
return column(name, type, PARTITION, null);
}
public CreateTableSpecification primaryKeyColumn(String name, DataType type) {
return primaryKeyColumn(name, type, ASCENDING);
}
public CreateTableSpecification primaryKeyColumn(String name, DataType type, Ordering order) {
return column(name, type, PRIMARY, order);
}
protected CreateTableSpecification column(String name, DataType type, KeyType keyType, Ordering ordering) {
columns().add(new ColumnSpecification().name(name).type(type).keyType(keyType).ordering(ordering));
return this;
}
protected List<ColumnSpecification> columns() {
return columns == null ? columns = new ArrayList<ColumnSpecification>() : columns;
}
public boolean getIfNotExists() {
return ifNotExists;
}
public List<ColumnSpecification> getColumns() {
return Collections.unmodifiableList(columns);
}
}

View File

@@ -0,0 +1,17 @@
package org.springframework.cassandra.core.keyspace;
/**
* Convenient default implementation of {@link TableDescriptor} as an extension of {@link TableSpecification} that
* doesn't require the use of generics.
*
* @author Matthew T. Adams
*/
public class DefaultTableDescriptor extends TableSpecification<DefaultTableDescriptor> {
/**
* Factory method to produce a new {@link DefaultTableDescriptor}. Convenient if imported statically.
*/
public static DefaultTableDescriptor table() {
return new DefaultTableDescriptor();
}
}

View File

@@ -1,19 +1,14 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
/**
* Builder class that supports the construction of <code>DROP TABLE</code> specifications.
*
* @author Matthew T. Adams
*/
public class DropTableSpecification extends TableNameSpecification<DropTableSpecification> {
public class DropTableSpecification {
private String name;
private boolean ifExists;
public DropTableSpecification name(String name) {
checkIdentifier(name);
this.name = name;
return this;
}
public DropTableSpecification ifExists() {
return ifExists(true);
}
@@ -26,8 +21,4 @@ public class DropTableSpecification {
public boolean getIfExists() {
return ifExists;
}
public String getNameAsIdentifier() {
return identifize(name);
}
}

View File

@@ -0,0 +1,52 @@
package org.springframework.cassandra.core.keyspace;
import java.util.List;
import java.util.Map;
/**
* Describes a table.
*
* @author Matthew T. Adams
*/
public interface TableDescriptor {
/**
* Returns the name of the table.
*/
String getName();
/**
* Returns the name of the table as an identifer or quoted identifier as appropriate.
*/
String getNameAsIdentifier();
/**
* Returns an unmodifiable {@link List} of {@link ColumnSpecification}s.
*/
List<ColumnSpecification> getColumns();
/**
* Returns an unmodifiable list of all partition key columns.
*/
public List<ColumnSpecification> getPartitionKeyColumns();
/**
* Returns an unmodifiable list of all primary key columns that are not also partition key columns.
*/
public List<ColumnSpecification> getPrimaryKeyColumns();
/**
* Returns an unmodifiable list of all partition and primary key columns.
*/
public List<ColumnSpecification> getKeyColumns();
/**
* Returns an unmodifiable list of all non-key columns.
*/
public List<ColumnSpecification> getNonKeyColumns();
/**
* Returns an unmodifiable {@link Map} of table options.
*/
Map<String, Object> getOptions();
}

View File

@@ -0,0 +1,38 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
/**
* Abstract builder class to support the construction of table specifications.
*
* @author Matthew T. Adams
* @param <T> The subtype of the {@link TableNameSpecification}
*/
public abstract class TableNameSpecification<T extends TableNameSpecification<T>> {
/**
* The name of the table.
*/
private String name;
/**
* Sets the table name.
*
* @return this
*/
@SuppressWarnings("unchecked")
public T name(String name) {
checkIdentifier(name);
this.name = name;
return (T) this;
}
public String getName() {
return name;
}
public String getNameAsIdentifier() {
return identifize(name);
}
}

View File

@@ -122,9 +122,13 @@ public enum TableOption implements Option {
this.value = value;
}
public String toString() {
public String getValue() {
return value;
}
public String toString() {
return getValue();
}
}
/**

View File

@@ -1,8 +1,6 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.escapeSingle;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import static org.springframework.cassandra.core.cql.CqlStringUtils.singleQuote;
import java.util.Collections;
@@ -12,39 +10,26 @@ import java.util.Map;
import org.springframework.cassandra.core.cql.CqlStringUtils;
/**
* Base class that contains behavior common to table operations.
* Abstract builder class to support the construction of table specifications that have table options, that is, those
* options normally specified by <code>WITH ... AND ...</code>.
* <p/>
* It is important to note that although this class depends on {@link TableOption} for convenient and typesafe use, it
* ultimately stores its options in a <code>Map<String,Object></code> for flexibility. This means that
* {@link #with(TableOption)} and {@link #with(TableOption, Object)} delegate to
* {@link #with(String, Object, boolean, boolean)}. This design allows the API to support new Cassandra options as they
* are introduced without having to update the code immediately.
*
* @author Matthew T. Adams
* @param T The subtype of AbstractTableBuilder.
* @param <T> The subtype of the {@link TableOptionsSpecification}.
*/
public abstract class AbstractTableSpecification<T extends AbstractTableSpecification<T>> {
private String name;
public abstract class TableOptionsSpecification<T extends TableOptionsSpecification<T>> extends
TableNameSpecification<TableOptionsSpecification<T>> {
protected Map<String, Object> options = new LinkedHashMap<String, Object>();
/**
* Sets the table name.
*
* @return this
*/
@SuppressWarnings("unchecked")
public T name(String name) {
setName(name);
return (T) this;
}
public void setName(String name) {
checkIdentifier(name);
this.name = name;
}
public String getName() {
return name;
}
public String getNameAsIdentifier() {
return identifize(name);
return (T) super.name(name);
}
/**

View File

@@ -0,0 +1,162 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.data.cassandra.mapping.KeyType.PARTITION;
import static org.springframework.data.cassandra.mapping.KeyType.PRIMARY;
import static org.springframework.data.cassandra.mapping.Ordering.ASCENDING;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.data.cassandra.mapping.KeyType;
import org.springframework.data.cassandra.mapping.Ordering;
import com.datastax.driver.core.DataType;
/**
* Builder class to support the construction of table specifications that have columns. This class can also be used as a
* standalone {@link TableDescriptor}, independent of {@link CreateTableSpecification}.
*
* @author Matthew T. Adams
*/
public class TableSpecification<T> extends TableOptionsSpecification<TableSpecification<T>> implements TableDescriptor {
/**
* List of all columns.
*/
private List<ColumnSpecification> columns = new ArrayList<ColumnSpecification>();
/**
* List of only those columns that comprise the partition key.
*/
private List<ColumnSpecification> partitionKeyColumns = new ArrayList<ColumnSpecification>();
/**
* List of only those columns that comprise the primary key that are not also part of the partition key.
*/
private List<ColumnSpecification> primaryKeyColumns = new ArrayList<ColumnSpecification>();
/**
* List of only those columns that are not partition or primary key columns.
*/
private List<ColumnSpecification> nonKeyColumns = new ArrayList<ColumnSpecification>();
/**
* Adds the given non-key column to the table. Must be specified after all primary key columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
*/
public T column(String name, DataType type) {
return column(name, type, null, null);
}
/**
* Adds the given partition key column to the table. Must be specified before any other columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
* @return this
*/
public T partitionKeyColumn(String name, DataType type) {
return column(name, type, PARTITION, null);
}
/**
* Adds the given primary key column to the table with ascending ordering. Must be specified after all partition key
* columns and before any non-key columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
* @return this
*/
public T primaryKeyColumn(String name, DataType type) {
return primaryKeyColumn(name, type, ASCENDING);
}
/**
* Adds the given primary key column to the table with the given ordering (<code>null</code> meaning ascending). Must
* be specified after all partition key columns and before any non-key columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
* @return this
*/
public T primaryKeyColumn(String name, DataType type, Ordering ordering) {
return column(name, type, PRIMARY, ordering);
}
/**
* Adds the given info as a new column to the table. Partition key columns must precede primary key columns, which
* must precede non-key columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
* @param keyType Indicates key type. Null means that the column is not a key column.
* @param ordering If the given {@link KeyType} is {@link KeyType#PRIMARY}, then the given ordering is used, else
* ignored.
* @return this
*/
@SuppressWarnings("unchecked")
protected T column(String name, DataType type, KeyType keyType, Ordering ordering) {
ColumnSpecification column = new ColumnSpecification().name(name).type(type).keyType(keyType)
.ordering(keyType == PRIMARY ? ordering : null);
columns.add(column);
if (keyType == KeyType.PARTITION) {
partitionKeyColumns.add(column);
}
if (keyType == KeyType.PRIMARY) {
primaryKeyColumns.add(column);
}
if (keyType == null) {
nonKeyColumns.add(column);
}
return (T) this;
}
/**
* Returns an unmodifiable list of all columns.
*/
public List<ColumnSpecification> getColumns() {
return Collections.unmodifiableList(columns);
}
/**
* Returns an unmodifiable list of all partition key columns.
*/
public List<ColumnSpecification> getPartitionKeyColumns() {
return Collections.unmodifiableList(partitionKeyColumns);
}
/**
* Returns an unmodifiable list of all primary key columns that are not also partition key columns.
*/
public List<ColumnSpecification> getPrimaryKeyColumns() {
return Collections.unmodifiableList(primaryKeyColumns);
}
/**
* Returns an unmodifiable list of all primary key columns that are not also partition key columns.
*/
public List<ColumnSpecification> getKeyColumns() {
ArrayList<ColumnSpecification> keyColumns = new ArrayList<ColumnSpecification>();
keyColumns.addAll(partitionKeyColumns);
keyColumns.addAll(primaryKeyColumns);
return Collections.unmodifiableList(keyColumns);
}
/**
* Returns an unmodifiable list of all non-key columns.
*/
public List<ColumnSpecification> getNonKeyColumns() {
return Collections.unmodifiableList(nonKeyColumns);
}
}

View File

@@ -1,4 +1,4 @@
package org.springframework.cassandra.core.keyspace;
package org.springframework.cassandra.core.util;
import java.util.Collection;
import java.util.LinkedHashMap;

View File

@@ -144,7 +144,7 @@ public class CassandraKeyspaceFactoryBean implements FactoryBean<Keyspace>, Init
// drop the old keyspace if needed
if (keyspaceExists && (keyspaceAttributes.isCreate() || keyspaceAttributes.isCreateDrop())) {
log.info("Drop keyspace " + keyspace + " on afterPropertiesSet");
session.execute("DROP KEYSPACE " + keyspace);
session.execute("DROP KEYSPACE " + keyspace + ";");
keyspaceExists = false;
}

View File

@@ -0,0 +1,83 @@
package org.springframework.cassandra.test.integration.core.cql.generator;
import java.io.IOException;
import java.util.UUID;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.thrift.transport.TTransportException;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.Session;
public abstract class AbstractEmbeddedCassandraIntegrationTest {
@BeforeClass
public static void beforeClass() throws ConfigurationException, TTransportException, IOException,
InterruptedException {
EmbeddedCassandraServerHelper.startEmbeddedCassandra("cassandra.yaml");
}
/**
* Whether to clear the cluster before the next test.
*/
protected boolean clear = true;
/**
* Whether to connect to Cassandra.
*/
protected boolean connect = true;
/**
* The {@link Cluster} that's connected to Cassandra.
*/
protected Cluster cluster;
/**
* If not <code>null</code>, get a {@link Session} for the from the {@link #cluster}.
*/
protected String keyspace = "ks" + UUID.randomUUID().toString().replace("-", "");
/**
* The {@link Session} for the {@link #keyspace} from the {@link #cluster}.
*/
protected Session session;
/**
* Returns whether we're currently connected to the cluster.
*/
public boolean connected() {
return session != null;
}
public Cluster cluster() {
return Cluster.builder().addContactPoint("localhost").withPort(9042).build();
}
@Before
public void before() {
if (connect && !connected()) {
cluster = cluster();
if (keyspace == null) {
session = cluster.connect();
} else {
KeyspaceMetadata kmd = cluster.getMetadata().getKeyspace(keyspace);
if (kmd == null) { // then create keyspace
session = cluster.connect();
session.execute("CREATE KEYSPACE " + keyspace
+ " WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1};");
session.execute("USE " + keyspace + ";");
} // else keyspace already exists
}
}
}
@After
public void after() {
if (clear && connected()) {
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}
}
}

View File

@@ -0,0 +1,144 @@
package org.springframework.cassandra.test.integration.core.cql.generator;
import static junit.framework.Assert.assertEquals;
import java.util.List;
import java.util.Map;
import org.springframework.cassandra.core.keyspace.ColumnSpecification;
import org.springframework.cassandra.core.keyspace.TableDescriptor;
import org.springframework.cassandra.core.keyspace.TableOption;
import org.springframework.cassandra.core.keyspace.TableOption.CachingOption;
import com.datastax.driver.core.ColumnMetadata;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.TableMetadata;
import com.datastax.driver.core.TableMetadata.Options;
public class CqlTableSpecificationAssertions {
public static double DELTA = 1e-6; // delta for comparisons of doubles
public static void assertTable(TableDescriptor expected, String keyspace, Session session) {
TableMetadata tmd = session.getCluster().getMetadata().getKeyspace(keyspace.toLowerCase())
.getTable(expected.getName());
assertEquals(expected.getName().toLowerCase(), tmd.getName().toLowerCase());
assertPartitionKeyColumns(expected, tmd);
assertPrimaryKeyColumns(expected, tmd);
assertColumns(expected.getColumns(), tmd.getColumns());
assertOptions(expected.getOptions(), tmd.getOptions());
}
public static void assertPartitionKeyColumns(TableDescriptor expected, TableMetadata actual) {
assertColumns(expected.getPartitionKeyColumns(), actual.getPartitionKey());
}
public static void assertPrimaryKeyColumns(TableDescriptor expected, TableMetadata actual) {
assertColumns(expected.getKeyColumns(), actual.getPrimaryKey());
}
public static void assertOptions(Map<String, Object> expected, Options actual) {
for (String key : expected.keySet()) {
Object value = expected.get(key);
TableOption tableOption = getTableOptionFor(key);
if (tableOption == null && key.equalsIgnoreCase(TableOption.COMPACT_STORAGE.getName())) {
// TODO: figure out how to tell if COMPACT STORAGE was used
continue;
}
assertOption(tableOption, key, value, getOptionFor(tableOption, tableOption.getType(), actual));
}
}
@SuppressWarnings({ "unchecked", "incomplete-switch" })
public static void assertOption(TableOption tableOption, String key, Object expected, Object actual) {
if (tableOption == null) { // then this is a string-only or unknown value
key.equalsIgnoreCase(actual.toString()); // TODO: determine if this is the right test
}
switch (tableOption) {
case BLOOM_FILTER_FP_CHANCE:
case READ_REPAIR_CHANCE:
case DCLOCAL_READ_REPAIR_CHANCE:
assertEquals((Double) expected, (Double) actual, DELTA);
return;
case CACHING:
assertEquals(CachingOption.valueOf((String) expected).getValue(), actual);
return;
case COMPACTION:
assertCompaction((Map<String, Object>) expected, (Map<String, String>) actual);
return;
case COMPRESSION:
assertCompression((Map<String, Object>) expected, (Map<String, String>) actual);
return;
}
assertEquals(expected, actual);
}
public static void assertCompaction(Map<String, Object> expected, Map<String, String> actual) {
// TODO
}
public static void assertCompression(Map<String, Object> expected, Map<String, String> actual) {
// TODO
}
public static TableOption getTableOptionFor(String key) {
try {
return TableOption.valueOf(key);
} catch (IllegalArgumentException x) {
return null;
}
}
@SuppressWarnings("unchecked")
public static <T> T getOptionFor(TableOption option, Class<?> type, Options options) {
switch (option) {
case BLOOM_FILTER_FP_CHANCE:
return (T) (Double) options.getBloomFilterFalsePositiveChance();
case CACHING:
return (T) options.getCaching();
case COMMENT:
return (T) options.getComment();
case COMPACTION:
return (T) options.getCompaction();
case COMPACT_STORAGE:
throw new Error(); // TODO: figure out
case COMPRESSION:
return (T) options.getCompression();
case DCLOCAL_READ_REPAIR_CHANCE:
return (T) (Double) options.getReadRepairChance();
case GC_GRACE_SECONDS:
return (T) new Long(options.getGcGraceInSeconds());
case READ_REPAIR_CHANCE:
return (T) (Double) options.getReadRepairChance();
case REPLICATE_ON_WRITE:
return (T) (Boolean) options.getReplicateOnWrite();
}
return null;
}
public static void assertColumns(List<ColumnSpecification> expected, List<ColumnMetadata> actual) {
for (int i = 0; i < expected.size(); i++) {
ColumnSpecification expectedColumn = expected.get(i);
ColumnMetadata actualColumn = actual.get(i);
assertColumn(expectedColumn, actualColumn);
}
}
public static void assertColumn(ColumnSpecification expected, ColumnMetadata actual) {
assertEquals(expected.getName().toLowerCase(), actual.getName().toLowerCase());
assertEquals(expected.getType(), actual.getType());
}
}

View File

@@ -0,0 +1,22 @@
package org.springframework.cassandra.test.integration.core.cql.generator;
import static org.springframework.cassandra.test.integration.core.cql.generator.CqlTableSpecificationAssertions.assertTable;
import org.junit.Test;
import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.BasicTest;
public class CreateTableCqlGeneratorIntegrationTests {
public static class BasicIntegrationTest extends AbstractEmbeddedCassandraIntegrationTest {
BasicTest unit = new BasicTest();
@Test
public void test() {
unit.prepare();
session.execute(unit.cql);
assertTable(unit.specification, keyspace, session);
}
}
}

View File

@@ -1,27 +0,0 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static org.springframework.cassandra.core.keyspace.TableOperations.alterTable;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
import com.datastax.driver.core.DataType;
public class AlterTableCqlGeneratorTest {
@Test
public void testAlterTableBuilder() throws Exception {
String name = "mytable";
DataType addedType = DataType.timeuuid();
String addedName = "added_column";
DataType alteredType = DataType.text();
String alteredName = "altered_column";
String droppedName = "dropped";
AlterTableSpecification alter = alterTable().name(name).add(addedName, addedType).alter(alteredName, alteredType)
.drop(droppedName);
AlterTableCqlGenerator generator = new AlterTableCqlGenerator(alter);
System.out.println(generator.toCql());
}
}

View File

@@ -0,0 +1,65 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static junit.framework.Assert.assertTrue;
import static org.springframework.cassandra.core.keyspace.TableOperations.alterTable;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
import com.datastax.driver.core.DataType;
public class AlterTableCqlGeneratorTests {
/**
* Asserts that the preamble is first & correctly formatted in the given CQL string.
*/
public static void assertPreamble(String tableName, String cql) {
assertTrue(cql.startsWith("ALTER TABLE " + tableName + " "));
}
/**
* Asserts that the given list of columns definitions are contained in the given CQL string properly.
*
* @param columnSpec IE, "foo text, bar blob"
*/
public static void assertColumnChanges(String columnSpec, String cql) {
assertTrue(cql.contains(""));
}
/**
* Convenient base class that other test classes can use so as not to repeat the generics declarations.
*/
public static abstract class AlterTableTest extends
TableOperationCqlGeneratorTest<AlterTableSpecification, AlterTableCqlGenerator> {
}
public static class BasicTest extends AlterTableTest {
public String name = "mytable";
public DataType alteredType = DataType.text();
public String altered = "altered";
public DataType addedType = DataType.text();
public String added = "added";
public String dropped = "dropped";
public AlterTableSpecification specification() {
return alterTable().name(name).alter(altered, alteredType).add(added, addedType).drop(dropped);
}
public AlterTableCqlGenerator generator() {
return new AlterTableCqlGenerator(specification);
}
@Test
public void test() {
prepare();
assertPreamble(name, cql);
assertColumnChanges(
String.format("ALTER %s TYPE %s, ADD %s %s, DROP %s", altered, alteredType, added, addedType, dropped), cql);
}
}
}

View File

@@ -1,47 +0,0 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static junit.framework.Assert.assertEquals;
import static org.springframework.cassandra.core.keyspace.MapBuilder.map;
import static org.springframework.cassandra.core.keyspace.TableOperations.createTable;
import static org.springframework.cassandra.core.keyspace.TableOption.BLOOM_FILTER_FP_CHANCE;
import static org.springframework.cassandra.core.keyspace.TableOption.CACHING;
import static org.springframework.cassandra.core.keyspace.TableOption.COMMENT;
import static org.springframework.cassandra.core.keyspace.TableOption.COMPACTION;
import static org.springframework.cassandra.core.keyspace.TableOption.CompactionOption.TOMBSTONE_THRESHOLD;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.cassandra.core.keyspace.TableOption.CachingOption;
import com.datastax.driver.core.DataType;
public class CreateTableCqlGeneratorTest {
@Test
public void createTableTest() {
String name = "my\"\"table";
DataType type0 = DataType.text();
String partKey0 = "partitionKey0";
String partition1 = "partitionKey1";
String primary0 = "primary0";
DataType type1 = DataType.text();
String column1 = "column1";
DataType type2 = DataType.bigint();
String column2 = "column2";
Object comment = "this is a comment";
Object bloom = "0.00075";
Object caching = CachingOption.KEYS_ONLY;
CreateTableSpecification create = createTable().ifNotExists().name(name).partitionKeyColumn(partKey0, type0)
.partitionKeyColumn(partition1, type0).primaryKeyColumn(primary0, type0).column(column1, type1)
.column(column2, type2).with(COMMENT, comment).with(BLOOM_FILTER_FP_CHANCE, bloom)
.with(COMPACTION, map().entry(TOMBSTONE_THRESHOLD, "0.15")).with(CACHING, caching);
CreateTableCqlGenerator generator = new CreateTableCqlGenerator(create);
String cql = generator.toCql();
assertEquals(
"CREATE TABLE IF NOT EXISTS \"my\"\"table\" (partitionKey0 text, partitionKey1 text, primary0 text, column1 text, column2 bigint, PRIMARY KEY ((partitionKey0, partitionKey1), primary0) WITH CLUSTERING ORDER BY (primary0 ASC) AND comment = 'this is a comment' AND bloom_filter_fp_chance = 0.00075 AND compaction = { 'tombstone_threshold' : 0.15 } AND caching = keys_only;",
cql);
}
}

View File

@@ -0,0 +1,71 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static junit.framework.Assert.assertTrue;
import static org.springframework.cassandra.core.keyspace.TableOperations.createTable;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import com.datastax.driver.core.DataType;
public class CreateTableCqlGeneratorTests {
/**
* Asserts that the preamble is first & correctly formatted in the given CQL string.
*/
public static void assertPreamble(String tableName, String cql) {
assertTrue(cql.startsWith("CREATE TABLE " + tableName + " "));
}
/**
* Asserts that the given primary key definition is contained in the given CQL string properly.
*
* @param primaryKeyString IE, "foo", "foo, bar, baz", "(foo, bar), baz", etc
*/
public static void assertPrimaryKey(String primaryKeyString, String cql) {
assertTrue(cql.contains(", PRIMARY KEY (" + primaryKeyString + "))"));
}
/**
* Asserts that the given list of columns definitions are contained in the given CQL string properly.
*
* @param columnSpec IE, "foo text, bar blob"
*/
public static void assertColumns(String columnSpec, String cql) {
assertTrue(cql.contains("(" + columnSpec + ","));
}
/**
* Convenient base class that other test classes can use so as not to repeat the generics declarations.
*/
public static abstract class CreateTableTest extends
TableOperationCqlGeneratorTest<CreateTableSpecification, CreateTableCqlGenerator> {
}
public static class BasicTest extends CreateTableTest {
public String name = "mytable";
public DataType partitionKeyType0 = DataType.text();
public String partitionKey0 = "partitionKey0";
public DataType columnType1 = DataType.text();
public String column1 = "column1";
public CreateTableSpecification specification() {
return createTable().name(name).partitionKeyColumn(partitionKey0, partitionKeyType0).column(column1, columnType1);
}
public CreateTableCqlGenerator generator() {
return new CreateTableCqlGenerator(specification);
}
@Test
public void test() {
prepare();
assertPreamble(name, cql);
assertColumns(partitionKey0 + " " + partitionKeyType0 + ", " + column1 + " " + columnType1, cql);
assertPrimaryKey(partitionKey0, cql);
}
}
}

View File

@@ -1,18 +0,0 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static org.springframework.cassandra.core.keyspace.TableOperations.dropTable;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.DropTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.DropTableSpecification;
public class DropTableCqlGeneratorTest {
@Test
public void testDropTableBuilder() throws Exception {
DropTableSpecification drop = dropTable().ifExists().name("mytable");
DropTableCqlGenerator generator = new DropTableCqlGenerator(drop);
System.out.println(generator.toCql());
}
}

View File

@@ -0,0 +1,54 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static junit.framework.Assert.assertTrue;
import static org.springframework.cassandra.core.keyspace.TableOperations.dropTable;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.DropTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.DropTableSpecification;
public class DropTableCqlGeneratorTests {
/**
* Asserts that the preamble is first & correctly formatted in the given CQL string.
*/
public static void assertStatement(String tableName, String cql) {
assertTrue(cql.equals("DROP TABLE " + tableName + ";"));
}
/**
* Asserts that the given list of columns definitions are contained in the given CQL string properly.
*
* @param columnSpec IE, "foo text, bar blob"
*/
public static void assertColumnChanges(String columnSpec, String cql) {
assertTrue(cql.contains(""));
}
/**
* Convenient base class that other test classes can use so as not to repeat the generics declarations.
*/
public static abstract class DropTableTest extends
TableOperationCqlGeneratorTest<DropTableSpecification, DropTableCqlGenerator> {
}
public static class BasicTest extends DropTableTest {
public String name = "mytable";
public DropTableSpecification specification() {
return dropTable().name(name);
}
public DropTableCqlGenerator generator() {
return new DropTableCqlGenerator(specification);
}
@Test
public void test() {
prepare();
assertStatement(name, cql);
}
}
}

View File

@@ -0,0 +1,36 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import org.springframework.cassandra.core.cql.generator.TableNameCqlGenerator;
import org.springframework.cassandra.core.keyspace.TableNameSpecification;
/**
* Useful test class that specifies just about as much as you can for a CQL generation test. Intended to be extended by
* classes that contain methods annotated with {@link Test}. Everything is public because this is a test class with no
* need for encapsulation, and it makes for easier reuse in other tests like integration tests (hint hint).
*
* @author Matthew T. Adams
*
* @param <S> The type of the {@link TableNameSpecification}
* @param <G> The type of the {@link TableNameCqlGenerator}
*/
public abstract class TableOperationCqlGeneratorTest<S extends TableNameSpecification<?>, G extends TableNameCqlGenerator<?>> {
public abstract S specification();
public abstract G generator();
public String tableName;
public S specification;
public G generator;
public String cql;
public void prepare() {
this.specification = specification();
this.generator = generator();
this.cql = generateCql();
}
public String generateCql() {
return generator.toCql();
}
}

View File

@@ -7,7 +7,7 @@
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:property-placeholder
location="classpath:/org/springframework/data/cassandra/config/cassandra.properties" />
location="classpath:/org/springframework/data/cassandra/test/integration/config/cassandra.properties" />
<cassandra:cluster id="cassandra-cluster"
contactPoints="${cassandra.contactPoints}" port="${cassandra.port}"