blob: 1b2a1ce0f5156edf9cdca3232ab453a7bcb166b1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.cassandra.distributed.impl;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.junit.AssumptionViolatedException;
import org.junit.Test;
import org.apache.cassandra.distributed.api.IInstanceConfig;
import org.apache.cassandra.distributed.api.IInvokableInstance;
import org.apache.cassandra.distributed.api.TokenSupplier;
import org.apache.cassandra.distributed.impl.AbstractCluster.AbstractBuilder;
import org.apache.cassandra.distributed.shared.Versions;
import org.apache.cassandra.utils.FailingRunnable;
import org.assertj.core.api.Assertions;
import org.mockito.Mockito;
public class AbstractClusterTest
{
@Test
public void allowVnodeWithMultipleTokens()
{
AbstractBuilder builder = builder();
builder.withTokenCount(42);
unroll(() -> builder.createWithoutStarting());
}
@Test
public void allowVnodeWithSingleToken()
{
AbstractBuilder builder = builder();
builder.withTokenCount(1);
unroll(() -> builder.createWithoutStarting());
}
@Test
public void disallowVnodeWithMultipleTokens()
{
AbstractBuilder builder = builder();
builder.withoutVNodes();
builder.withTokenCount(42);
Assertions.assertThatThrownBy(() -> builder.createWithoutStarting())
.isInstanceOf(AssumptionViolatedException.class)
.hasMessage("vnode is not supported");
}
@Test
public void disallowVnodeWithSingleToken()
{
AbstractBuilder builder = builder();
builder.withoutVNodes();
builder.withTokenCount(1);
unroll(() -> builder.createWithoutStarting());
}
@Test
public void withoutVNodes()
{
AbstractBuilder builder = builder();
builder.withoutVNodes();
//TODO casting is annoying... what can be done to be smarter?
builder.withTokenSupplier((TokenSupplier) i -> Arrays.asList("a", "b", "c"));
Assertions.assertThatThrownBy(() -> builder.createWithoutStarting())
.isInstanceOf(AssumptionViolatedException.class)
.hasMessage("vnode is not supported");
}
@Test
public void vnodeButTokensDoNotMatch()
{
AbstractBuilder builder = builder();
builder.withTokenCount(1);
//TODO casting is annoying... what can be done to be smarter?
builder.withTokenSupplier((TokenSupplier) i -> Arrays.asList("a", "b", "c"));
Assertions.assertThatThrownBy(() -> builder.createWithoutStarting())
.isInstanceOf(AssumptionViolatedException.class)
.hasMessage("no-vnode is requested but not supported");
}
@Test
public void noVnodeButTokensDoNotMatch()
{
AbstractBuilder builder = builder();
builder.withTokenCount(42);
//TODO casting is annoying... what can be done to be smarter?
builder.withTokenSupplier((TokenSupplier) i -> Arrays.asList("a"));
Assertions.assertThatThrownBy(() -> builder.createWithoutStarting())
.isInstanceOf(AssumptionViolatedException.class)
.hasMessage("vnode is requested but not supported");
}
@Test
public void vnodeNotSupported()
{
ConfigUpdate config = ConfigUpdate.of("num_tokens", 1);
AbstractCluster<?> cluster = cluster(4, config);
config.check = true;
Assertions.assertThatThrownBy(() -> cluster.createInstanceConfig(1))
.isInstanceOf(AssumptionViolatedException.class)
.hasMessage("vnode is not supported");
}
@Test
public void noVnodeNotSupported()
{
ConfigUpdate config = ConfigUpdate.of("num_tokens", 4);
AbstractCluster<?> cluster = cluster(1, config);
config.check = true;
Assertions.assertThatThrownBy(() -> cluster.createInstanceConfig(1))
.isInstanceOf(AssumptionViolatedException.class)
.hasMessage("no-vnode is requested but not supported");
}
@Test
public void vnodeMismatch()
{
ConfigUpdate config = ConfigUpdate.of("num_tokens", 4);
AbstractCluster<?> cluster = cluster(2, config);
config.check = true;
Assertions.assertThatThrownBy(() -> cluster.createInstanceConfig(1))
.isInstanceOf(AssumptionViolatedException.class)
.hasMessage("vnode is enabled and num_tokens is defined in test without GOSSIP or setting initial_token");
}
@Test
public void vnodeMismatchDefinesTokens()
{
ConfigUpdate config = ConfigUpdate.of("num_tokens", 4, "initial_token", "some values");
AbstractCluster<?> cluster = cluster(2, config);
config.check = true;
unroll(() -> cluster.createInstanceConfig(1));
}
private static void unroll(FailingRunnable r)
{
try
{
r.run();
}
catch (AssumptionViolatedException e)
{
AssertionError e2 = new AssertionError(e.getMessage());
e2.setStackTrace(e.getStackTrace());
throw e2;
}
}
private static AbstractCluster<?> cluster(int tokenCount, Consumer<IInstanceConfig> fn)
{
try
{
return (AbstractCluster<?>) builder()
.withTokenCount(tokenCount)
.withConfig(fn)
.createWithoutStarting();
}
catch (IOException e)
{
throw new AssertionError(e);
}
}
private static AbstractBuilder builder()
{
return new Builder().withNodes(1);
}
private static class Builder extends AbstractBuilder<IInvokableInstance, Cluster, Builder>
{
public Builder()
{
super(Cluster::new);
}
}
private static class Cluster extends AbstractCluster<IInvokableInstance>
{
protected Cluster(AbstractBuilder builder)
{
super(builder);
}
@Override
protected IInvokableInstance newInstanceWrapper(Versions.Version version, IInstanceConfig config)
{
IInvokableInstance inst = Mockito.mock(IInvokableInstance.class);
Mockito.when(inst.config()).thenReturn(config);
return inst;
}
}
private static class ConfigUpdate implements Consumer<IInstanceConfig>
{
public boolean check = false;
private final Map<String, Object> override;
private ConfigUpdate(Map<String, Object> override)
{
this.override = override;
}
public static ConfigUpdate of(Object... args)
{
Map<String, Object> override = new HashMap<>();
for (int i = 0; i < args.length; i = i + 2)
override.put((String) args[i], args[i + 1]);
return new ConfigUpdate(override);
}
@Override
public void accept(IInstanceConfig config)
{
if (!check)
return;
for (Map.Entry<String, Object> e : override.entrySet())
config.set(e.getKey(), e.getValue());
}
}
}