/*
* 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.data.cassandra.mapping;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.cassandra.core.keyspace.ColumnSpecification;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.convert.CassandraCustomConversions;
import org.springframework.data.cassandra.domain.AllPossibleTypes;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.CollectionType;
import com.datastax.driver.core.DataType.Name;
import com.datastax.driver.core.UDTValue;
import com.datastax.driver.core.UserType;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Unit tests for {@link BasicCassandraMappingContext} targeted on {@link CreateTableSpecification}.
*
* @author Mark Paluch
* @soundtrack Black Rose - Volbeat
*/
public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
private BasicCassandraMappingContext ctx = new BasicCassandraMappingContext();
@Before
public void setUp() throws Exception {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new PersonReadConverter());
converters.add(new PersonWriteConverter());
CassandraCustomConversions customConversions = new CassandraCustomConversions(converters);
ctx.setCustomConversions(customConversions);
}
@Test // DATACASS-296
public void customConversionTestShouldCreateCorrectTableDefinition() {
CassandraPersistentEntity<?> persistentEntity = ctx.getRequiredPersistentEntity(Employee.class);
CreateTableSpecification specification = ctx.getCreateTableSpecificationFor(persistentEntity);
assertThat(getColumnType("human", specification)).isEqualTo(DataType.varchar());
ColumnSpecification friends = getColumn("friends", specification);
assertThat(friends.getType().isCollection()).isTrue();
CollectionType friendsCollection = (CollectionType) friends.getType();
assertThat(friendsCollection.getName()).isEqualTo(Name.LIST);
assertThat(friendsCollection.getTypeArguments()).hasSize(1);
assertThat(friendsCollection.getTypeArguments().get(0)).isEqualTo(DataType.varchar());
ColumnSpecification people = getColumn("people", specification);
assertThat(people.getType().isCollection()).isTrue();
CollectionType peopleCollection = (CollectionType) people.getType();
assertThat(peopleCollection.getName()).isEqualTo(Name.SET);
assertThat(peopleCollection.getTypeArguments()).hasSize(1);
assertThat(peopleCollection.getTypeArguments().get(0)).isEqualTo(DataType.varchar());
}
@Test // DATACASS-296
public void customConversionTestShouldHonorTypeAnnotationAndCreateCorrectTableDefinition() {
CassandraPersistentEntity<?> persistentEntity = ctx.getRequiredPersistentEntity(Employee.class);
CreateTableSpecification specification = ctx.getCreateTableSpecificationFor(persistentEntity);
assertThat(getColumnType("floater", specification)).isEqualTo(DataType.cfloat());
ColumnSpecification enemies = getColumn("enemies", specification);
assertThat(enemies.getType().isCollection()).isTrue();
CollectionType enemiesCollection = (CollectionType) enemies.getType();
assertThat(enemiesCollection.getName()).isEqualTo(Name.SET);
assertThat(enemiesCollection.getTypeArguments()).hasSize(1);
assertThat(enemiesCollection.getTypeArguments().get(0)).isEqualTo(DataType.bigint());
}
@Test // DATACASS-296
public void columnsShouldMapToVarchar() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("id", specification)).isEqualTo(DataType.varchar());
assertThat(getColumnType("zoneId", specification)).isEqualTo(DataType.varchar());
assertThat(getColumnType("bpZoneId", specification)).isEqualTo(DataType.varchar());
assertThat(getColumnType("anEnum", specification)).isEqualTo(DataType.varchar());
}
@Test // DATACASS-296
public void columnsShouldMapToTinyInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("boxedByte", specification)).isEqualTo(DataType.tinyint());
assertThat(getColumnType("primitiveByte", specification)).isEqualTo(DataType.tinyint());
}
@Test // DATACASS-296
public void columnsShouldMapToSmallInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("boxedShort", specification)).isEqualTo(DataType.smallint());
assertThat(getColumnType("primitiveShort", specification)).isEqualTo(DataType.smallint());
}
@Test // DATACASS-296
public void columnsShouldMapToBigInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("boxedLong", specification)).isEqualTo(DataType.bigint());
assertThat(getColumnType("primitiveLong", specification)).isEqualTo(DataType.bigint());
}
@Test // DATACASS-296
public void columnsShouldMapToVarInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("bigInteger", specification)).isEqualTo(DataType.varint());
}
@Test // DATACASS-296
public void columnsShouldMapToDecimal() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("bigDecimal", specification)).isEqualTo(DataType.decimal());
}
@Test // DATACASS-296
public void columnsShouldMapToInt() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("boxedInteger", specification)).isEqualTo(DataType.cint());
assertThat(getColumnType("primitiveInteger", specification)).isEqualTo(DataType.cint());
}
@Test // DATACASS-296
public void columnsShouldMapToFloat() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("boxedFloat", specification)).isEqualTo(DataType.cfloat());
assertThat(getColumnType("primitiveFloat", specification)).isEqualTo(DataType.cfloat());
}
@Test // DATACASS-296
public void columnsShouldMapToDouble() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("boxedDouble", specification)).isEqualTo(DataType.cdouble());
assertThat(getColumnType("primitiveDouble", specification)).isEqualTo(DataType.cdouble());
}
@Test // DATACASS-296
public void columnsShouldMapToBoolean() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("boxedBoolean", specification)).isEqualTo(DataType.cboolean());
assertThat(getColumnType("primitiveBoolean", specification)).isEqualTo(DataType.cboolean());
}
@Test // DATACASS-296
public void columnsShouldMapToDate() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("date", specification)).isEqualTo(DataType.date());
assertThat(getColumnType("localDate", specification)).isEqualTo(DataType.date());
assertThat(getColumnType("jodaLocalDate", specification)).isEqualTo(DataType.date());
assertThat(getColumnType("bpLocalDate", specification)).isEqualTo(DataType.date());
}
@Test // DATACASS-296
public void columnsShouldMapToTimestamp() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("timestamp", specification)).isEqualTo(DataType.timestamp());
assertThat(getColumnType("localDateTime", specification)).isEqualTo(DataType.timestamp());
assertThat(getColumnType("instant", specification)).isEqualTo(DataType.timestamp());
assertThat(getColumnType("jodaLocalDateTime", specification)).isEqualTo(DataType.timestamp());
assertThat(getColumnType("jodaDateTime", specification)).isEqualTo(DataType.timestamp());
assertThat(getColumnType("bpLocalDateTime", specification)).isEqualTo(DataType.timestamp());
assertThat(getColumnType("bpInstant", specification)).isEqualTo(DataType.timestamp());
}
@Test // DATACASS-296
public void columnsShouldMapToTimestampUsingOverrides() {
CreateTableSpecification specification = getCreateTableSpecificationFor(TypeWithOverrides.class);
assertThat(getColumnType("localDate", specification)).isEqualTo(DataType.timestamp());
assertThat(getColumnType("jodaLocalDate", specification)).isEqualTo(DataType.timestamp());
}
@Test // DATACASS-296
public void columnsShouldMapToBlob() {
CreateTableSpecification specification = getCreateTableSpecificationFor(AllPossibleTypes.class);
assertThat(getColumnType("blob", specification)).isEqualTo(DataType.blob());
}
@Test // DATACASS-172
public void columnsShouldMapToUdt() {
final UserType human_udt = mock(UserType.class, "human_udt");
final UserType species_udt = mock(UserType.class, "species_udt");
final UserType peeps_udt = mock(UserType.class, "peeps_udt");
ctx.setUserTypeResolver(typeName -> {
if (typeName.toCql().equals(human_udt.toString())) {
return human_udt;
}
if (typeName.toCql().equals(species_udt.toString())) {
return species_udt;
}
if (typeName.toCql().equals(peeps_udt.toString())) {
return peeps_udt;
}
return null;
});
CreateTableSpecification specification = getCreateTableSpecificationFor(WithUdtFields.class);
assertThat(getColumnType("human", specification)).isEqualTo(human_udt);
assertThat(getColumnType("friends", specification)).isEqualTo(DataType.list(species_udt));
assertThat(getColumnType("people", specification)).isEqualTo(DataType.set(peeps_udt));
}
@Test // DATACASS-172
public void columnsShouldMapToMapped() {
final UserType mappedUdt = mock(UserType.class, "mappedudt");
ctx.setUserTypeResolver(typeName -> {
if (typeName.toCql().equals(mappedUdt.toString())) {
return mappedUdt;
}
return null;
});
CreateTableSpecification specification = getCreateTableSpecificationFor(WithMappedUdtFields.class);
assertThat(getColumnType("human", specification)).isEqualTo(mappedUdt);
assertThat(getColumnType("friends", specification)).isEqualTo(DataType.list(mappedUdt));
assertThat(getColumnType("people", specification)).isEqualTo(DataType.set(mappedUdt));
assertThat(getColumnType("stringToUdt", specification)).isEqualTo(DataType.map(DataType.varchar(), mappedUdt));
assertThat(getColumnType("udtToString", specification)).isEqualTo(DataType.map(mappedUdt, DataType.varchar()));
}
private CreateTableSpecification getCreateTableSpecificationFor(Class<?> persistentEntityClass) {
CassandraCustomConversions customConversions = new CassandraCustomConversions(Collections.EMPTY_LIST);
ctx.setCustomConversions(customConversions);
CassandraPersistentEntity<?> persistentEntity = ctx.getRequiredPersistentEntity(persistentEntityClass);
return ctx.getCreateTableSpecificationFor(persistentEntity);
}
private DataType getColumnType(String columnName, CreateTableSpecification specification) {
return getColumn(columnName, specification).getType();
}
private ColumnSpecification getColumn(String columnName, CreateTableSpecification specification) {
for (ColumnSpecification columnSpecification : specification.getColumns()) {
if (columnSpecification.getName().equals(CqlIdentifier.cqlId(columnName))) {
return columnSpecification;
}
}
throw new IllegalArgumentException(
String.format("Cannot find column '%s' amongst '%s'", columnName, specification.getColumns()));
}
@Data
@Table
private static class Employee {
@Id String id;
Human human;
List<Human> friends;
Set<Human> people;
@CassandraType(type = Name.FLOAT) Human floater;
@CassandraType(type = Name.SET, typeArguments = Name.BIGINT) List<Human> enemies;
}
@Data
@Table
private static class WithUdtFields {
@Id String id;
@CassandraType(type = Name.UDT, userTypeName = "human_udt") UDTValue human;
@CassandraType(type = Name.LIST, typeArguments = Name.UDT, userTypeName = "species_udt") List<UDTValue> friends;
@CassandraType(type = Name.SET, typeArguments = Name.UDT, userTypeName = "peeps_udt") Set<UDTValue> people;
}
@Data
@Table
private static class WithMappedUdtFields {
@Id String id;
MappedUdt human;
List<MappedUdt> friends;
Set<MappedUdt> people;
Map<String, MappedUdt> stringToUdt;
Map<MappedUdt, String> udtToString;
}
@UserDefinedType
private static class MappedUdt {}
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Human {
String firstname;
String lastname;
}
@Data
@Table
private static class TypeWithOverrides {
@Id String id;
@CassandraType(type = Name.TIMESTAMP) java.time.LocalDate localDate;
@CassandraType(type = Name.TIMESTAMP) org.joda.time.LocalDate jodaLocalDate;
}
private static class PersonReadConverter implements Converter<String, Human> {
public Human convert(String source) {
if (StringUtils.hasText(source)) {
try {
return new ObjectMapper().readValue(source, Human.class);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
return null;
}
}
private static class PersonWriteConverter implements Converter<Human, String> {
public String convert(Human source) {
try {
return new ObjectMapper().writeValueAsString(source);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
}