/*
* Copyright (C) 2012-2015 DataStax Inc.
*
* 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 com.datastax.driver.extras.codecs.guava;
import com.datastax.driver.core.*;
import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.utils.CassandraVersion;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.math.BigDecimal;
import java.util.List;
import static com.datastax.driver.core.TypeCodec.*;
import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
import static org.assertj.core.api.Assertions.assertThat;
public class OptionalCodecTest extends CCMTestsSupport {
private final OptionalCodec<List<String>> optionalCodec = new OptionalCodec<List<String>>(list(varchar()));
private final CodecRegistry registry = new CodecRegistry().register(optionalCodec);
private BuiltStatement insertStmt;
private BuiltStatement selectStmt;
@Override
public void onTestContextInitialized() {
execute("CREATE TABLE foo (c1 text, c2 text, c3 list<text>, c4 bigint, c5 decimal, PRIMARY KEY (c1, c2))");
}
@Override
public Cluster.Builder createClusterBuilder() {
return Cluster.builder().withCodecRegistry(registry);
}
@BeforeMethod(groups = "short")
public void createBuiltStatements() throws Exception {
insertStmt = insertInto("foo").value("c1", bindMarker()).value("c2", bindMarker()).value("c3", bindMarker());
selectStmt = select("c2", "c3").from("foo").where(eq("c1", bindMarker()));
}
/**
* <p>
* Validates that if a column is unset, that retrieving the value using {@link OptionalCodec} should return
* an {@link Optional#absent()} value. Since CQL Lists can't differentiate between null and empty lists, the
* OptionalCodec should be smart enough to map an empty list to absent.
* </p>
*
* @test_category data_types:serialization
* @expected_result an absent value.
* @jira_ticket JAVA-846
* @since 2.2.0
*/
@Test(groups = "short")
@CassandraVersion("2.2.0")
public void should_map_unset_value_to_absent() {
PreparedStatement insertPrep = session().prepare(this.insertStmt);
PreparedStatement selectPrep = session().prepare(this.selectStmt);
BoundStatement bs = insertPrep.bind();
bs.setString(0, "should_map_unset_value_to_absent");
bs.setString(1, "1");
session().execute(bs);
ResultSet results = session().execute(selectPrep.bind("should_map_unset_value_to_absent"));
assertThat(results.getAvailableWithoutFetching()).isEqualTo(1);
Row row = results.one();
assertThat(row.getString("c2")).isEqualTo("1");
assertThat(row.get("c3", optionalCodec.getJavaType())).isEqualTo(Optional.absent());
}
/**
* <p>
* Validates that if a column is set to {@link Optional#absent()} using {@link OptionalCodec} that it should be
* stored as null and that retrieving it should return {@link Optional#absent()} using {@link OptionalCodec}.
* </p>
*
* @test_category data_types:serialization
* @expected_result an absent value.
* @jira_ticket JAVA-846
* @since 2.2.0
*/
@Test(groups = "short")
public void should_map_absent_null_value_to_absent() {
PreparedStatement insertPrep = session().prepare(this.insertStmt);
PreparedStatement selectPrep = session().prepare(this.selectStmt);
BoundStatement bs = insertPrep.bind();
bs.setString(0, "should_map_absent_null_value_to_absent");
bs.setString(1, "1");
bs.set(2, Optional.<List<String>>absent(), optionalCodec.getJavaType());
session().execute(bs);
ResultSet results = session().execute(selectPrep.bind("should_map_absent_null_value_to_absent"));
assertThat(results.getAvailableWithoutFetching()).isEqualTo(1);
Row row = results.one();
assertThat(row.getString("c2")).isEqualTo("1");
assertThat(row.getList("c3", String.class)).isEmpty();
assertThat(row.get("c3", optionalCodec.getJavaType())).isEqualTo(Optional.absent());
}
/**
* <p>
* Validates that if a column is set to an {@link Optional} value using {@link OptionalCodec} that it should be
* stored as the option's value and that retrieving it should return an {@link Optional} using {@link OptionalCodec}
* and its actual value without using it.
* </p>
*
* @test_category data_types:serialization
* @expected_result The options value is stored appropriately and is retrievable with and without OptionalCodec.
* @jira_ticket JAVA-846
* @since 2.2.0
*/
@Test(groups = "short")
public void should_map_some_back_to_itself() {
PreparedStatement insertPrep = session().prepare(this.insertStmt);
PreparedStatement selectPrep = session().prepare(this.selectStmt);
List<String> data = Lists.newArrayList("1", "2", "3");
BoundStatement bs = insertPrep.bind();
bs.setString(0, "should_map_some_back_to_itself");
bs.setString(1, "1");
bs.set(2, Optional.of(data), optionalCodec.getJavaType());
session().execute(bs);
ResultSet results = session().execute(selectPrep.bind("should_map_some_back_to_itself"));
assertThat(results.getAvailableWithoutFetching()).isEqualTo(1);
Row row = results.one();
assertThat(row.getString("c2")).isEqualTo("1");
// Ensure data stored correctly.
assertThat(row.getList("c3", String.class)).isEqualTo(data);
// Ensure data retrievable using Option codec.
Optional<List<String>> returnData = row.get("c3", optionalCodec.getJavaType());
assertThat(returnData.isPresent()).isTrue();
assertThat(returnData.get()).isEqualTo(data);
}
@Test(groups = "short")
public void should_map_a_primitive_type_to_absent() {
OptionalCodec<Long> optionalLongCodec = new OptionalCodec<Long>(bigint());
cluster().getConfiguration().getCodecRegistry().register(optionalLongCodec);
PreparedStatement stmt = session().prepare("insert into foo (c1, c2, c4) values (?,?,?)");
BoundStatement bs = stmt.bind();
bs.setString(0, "should_map_a_primitive_type_to_absent");
bs.setString(1, "1");
bs.set(2, Optional.<Long>absent(), optionalLongCodec.getJavaType());
session().execute(bs);
PreparedStatement selectBigint = session().prepare("select c1, c4 from foo where c1=?");
ResultSet results = session().execute(selectBigint.bind("should_map_a_primitive_type_to_absent"));
assertThat(results.getAvailableWithoutFetching()).isEqualTo(1);
Row row = results.one();
assertThat(row.get("c4", optionalLongCodec.getJavaType())).isEqualTo(Optional.<Long>absent());
assertThat(row.getLong("c4")).isEqualTo(0L); // This will return a 0L since it returns the primitive value.
assertThat(row.get("c4", Long.class)).isNull();
}
@Test(groups = "short")
public void should_map_a_nullable_type_to_absent() {
OptionalCodec<BigDecimal> optionalDecimalCodec = new OptionalCodec<BigDecimal>(decimal());
cluster().getConfiguration().getCodecRegistry().register(optionalDecimalCodec);
PreparedStatement stmt = session().prepare("insert into foo (c1, c2, c5) values (?,?,?)");
BoundStatement bs = stmt.bind();
bs.setString(0, "should_map_a_nullable_type_to_absent");
bs.setString(1, "1");
bs.set(2, Optional.<BigDecimal>absent(), optionalDecimalCodec.getJavaType());
session().execute(bs);
PreparedStatement selectDecimal = session().prepare("select c1, c5 from foo where c1=?");
ResultSet results = session().execute(selectDecimal.bind("should_map_a_nullable_type_to_absent"));
assertThat(results.getAvailableWithoutFetching()).isEqualTo(1);
Row row = results.one();
assertThat(row.get("c5", optionalDecimalCodec.getJavaType())).isEqualTo(Optional.<BigDecimal>absent());
assertThat(row.getDecimal("c5")).isNull(); // Since BigDecimal is not a primitive it is nullable so expect null.
}
}