/*
* 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.core;
import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class StatementIdempotenceTest {
private Cluster cluster;
@BeforeMethod(groups = "unit")
public void setUpQueryBuilder() throws Exception {
CodecRegistry codecRegistry = new CodecRegistry();
cluster = mock(Cluster.class);
Configuration configuration = mock(Configuration.class);
ProtocolOptions protocolOptions = mock(ProtocolOptions.class);
when(cluster.getConfiguration()).thenReturn(configuration);
when(configuration.getCodecRegistry()).thenReturn(codecRegistry);
when(configuration.getProtocolOptions()).thenReturn(protocolOptions);
when(protocolOptions.getProtocolVersion()).thenReturn(ProtocolVersion.NEWEST_SUPPORTED);
}
@Test(groups = "unit")
public void should_default_to_false_when_not_set_on_statement_nor_query_options() {
QueryOptions queryOptions = new QueryOptions();
SimpleStatement statement = new SimpleStatement("", cluster);
assertThat(statement.isIdempotentWithDefault(queryOptions)).isFalse();
}
@Test(groups = "unit")
public void should_use_query_options_when_not_set_on_statement() {
QueryOptions queryOptions = new QueryOptions();
SimpleStatement statement = new SimpleStatement("", cluster);
for (boolean valueInOptions : new boolean[]{true, false}) {
queryOptions.setDefaultIdempotence(valueInOptions);
assertThat(statement.isIdempotentWithDefault(queryOptions)).isEqualTo(valueInOptions);
}
}
@Test(groups = "unit")
public void should_use_statement_when_set_on_statement() {
QueryOptions queryOptions = new QueryOptions();
SimpleStatement statement = new SimpleStatement("", cluster);
for (boolean valueInOptions : new boolean[]{true, false})
for (boolean valueInStatement : new boolean[]{true, false}) {
queryOptions.setDefaultIdempotence(valueInOptions);
statement.setIdempotent(valueInStatement);
assertThat(statement.isIdempotentWithDefault(queryOptions)).isEqualTo(valueInStatement);
}
}
@Test(groups = "unit")
public void should_infer_for_built_statement() {
for (BuiltStatement statement : idempotentBuiltStatements())
assertThat(statement.isIdempotent())
.as(statement.getQueryString())
.isTrue();
for (BuiltStatement statement : nonIdempotentBuiltStatements())
assertThat(statement.isIdempotent())
.as(statement.getQueryString())
.isFalse();
}
@Test(groups = "unit")
public void should_override_inferred_value_when_manually_set_on_built_statement() {
for (boolean manualValue : new boolean[]{true, false}) {
for (BuiltStatement statement : idempotentBuiltStatements()) {
statement.setIdempotent(manualValue);
assertThat(statement.isIdempotent()).isEqualTo(manualValue);
}
for (BuiltStatement statement : nonIdempotentBuiltStatements()) {
statement.setIdempotent(manualValue);
assertThat(statement.isIdempotent()).isEqualTo(manualValue);
}
}
}
private ImmutableList<BuiltStatement> idempotentBuiltStatements() {
return ImmutableList.<BuiltStatement>of(
update("foo").with(set("v", 1)).where(eq("k", 1)), // set simple value
update("foo").with(add("s", 1)).where(eq("k", 1)), // add to set
update("foo").with(put("m", "a", 1)).where(eq("k", 1)), // put in map
// select statements should be idempotent even with function calls
select().countAll().from("foo").where(eq("k", 1)),
select().ttl("v").from("foo").where(eq("k", 1)),
select().writeTime("v").from("foo").where(eq("k", 1)),
select().fcall("token", "k").from("foo").where(eq("k", 1))
);
}
private ImmutableList<BuiltStatement> nonIdempotentBuiltStatements() {
return ImmutableList.of(
update("foo").with(append("l", 1)).where(eq("k", 1)), // append to list
update("foo").with(set("v", 1)).and(prepend("l", 1)).where(eq("k", 1)), // prepend to list
update("foo").with(incr("c")).where(eq("k", 1)), // counter update
// function calls
insertInto("foo").value("k", 1).value("v", fcall("token", "k")),
insertInto("foo").value("k", 1).value("v", now()),
insertInto("foo").value("k", 1).value("v", uuid()),
insertInto("foo").value("k", 1).value("v", Sets.newHashSet(fcall("token", "k"))),
insertInto("foo").value("k", 1).value("v", Sets.newHashSet(now())),
insertInto("foo").value("k", 1).value("v", Sets.newHashSet(uuid())),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, fcall("token", "k")}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, now()}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, uuid()}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, ImmutableMap.of("foo", fcall("token", "k"))}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, ImmutableMap.of("foo", now())}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, ImmutableMap.of("foo", uuid())}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, ImmutableMap.of(fcall("token", "k"), "foo")}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, ImmutableMap.of(now(), "foo")}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, ImmutableMap.of(uuid(), "foo")}),
update("foo").with(set("v", fcall("token", "k"))).where(eq("k", 1)),
update("foo").with(set("v", now())).where(eq("k", 1)),
update("foo").with(set("v", uuid())).where(eq("k", 1)),
update("foo").with(set("v", Lists.newArrayList(fcall("token", "k")))).where(eq("k", 1)),
update("foo").with(set("v", Lists.newArrayList(now()))).where(eq("k", 1)),
update("foo").with(set("v", Lists.newArrayList(uuid()))).where(eq("k", 1)),
delete().from("foo").where(lt("k", fcall("now"))),
delete().from("foo").where(lt("k", now())),
update("foo").where(eq("k", fcall("now"))),
delete().listElt("a", 1).from("test_coll"),
// LWT
update("foo").where(eq("is", "charlie?")).ifExists(),
update("foo").where(eq("good", "drivers")).onlyIf(contains("developers", "datastax")),
update("foo").onlyIf().and(contains("developers", "datastax")).where(eq("good", "drivers")),
update("foo").onlyIf(contains("developers", "datastax")).with(set("v", 0)),
update("foo").with(set("v", 0)).onlyIf(contains("hello", "world")),
insertInto("foo").value("k", 1).value("v", Sets.newHashSet(now())).ifNotExists(),
delete().from("foo").where(eq("k", 2)).ifExists(),
delete().from("foo").onlyIf(eq("k", 2)),
// raw() calls
insertInto("foo").value("k", 1).value("v", raw("foo()")),
insertInto("foo").value("k", 1).value("v", Sets.newHashSet(raw("foo()"))),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, raw("foo()")}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, ImmutableMap.of("foo", raw("foo()"))}),
insertInto("foo").values(new String[]{"k", "v"}, new Object[]{1, ImmutableMap.of(raw("foo()"), "foo")}),
update("foo").with(set("v", raw("foo()"))).where(eq("k", 1)),
update("foo").with(set("v", Lists.newArrayList(raw("foo()")))).where(eq("k", 1))
);
}
}