Skip to content

Commit

Permalink
De-mockify registry tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gwbrown committed May 7, 2019
1 parent db674c6 commit 483cd51
Showing 1 changed file with 125 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
package org.elasticsearch.xpack.core.snapshotlifecycle.history;

import org.elasticsearch.Version;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterModule;
import org.elasticsearch.cluster.ClusterName;
Expand All @@ -24,15 +25,17 @@
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.TriFunction;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ClusterServiceUtils;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.client.NoOpClient;
import org.elasticsearch.threadpool.TestThreadPool;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.core.indexlifecycle.DeleteAction;
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
Expand All @@ -43,8 +46,8 @@
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
import org.elasticsearch.xpack.core.indexlifecycle.TimeseriesLifecycleType;
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction;
import org.junit.After;
import org.junit.Before;
import org.mockito.ArgumentCaptor;

import java.io.IOException;
import java.util.ArrayList;
Expand All @@ -53,51 +56,31 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static org.elasticsearch.mock.orig.Mockito.verify;
import static org.elasticsearch.mock.orig.Mockito.when;
import static org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings.SLM_HISTORY_INDEX_ENABLED_SETTING;
import static org.elasticsearch.xpack.core.snapshotlifecycle.history.SnapshotLifecycleTemplateRegistry.SLM_POLICY_NAME;
import static org.elasticsearch.xpack.core.snapshotlifecycle.history.SnapshotLifecycleTemplateRegistry.SLM_TEMPLATE_NAME;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.hamcrest.Matchers.instanceOf;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verifyZeroInteractions;

public class SnapshotLifecycleTemplateRegistryTests extends ESTestCase {
private SnapshotLifecycleTemplateRegistry registry;
private NamedXContentRegistry xContentRegistry;
private ClusterService clusterService;
private ThreadPool threadPool;
private Client client;
private VerifyingClient client;

@Before
public void createRegistryAndClient() {
threadPool = mock(ThreadPool.class);
when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY));
when(threadPool.generic()).thenReturn(EsExecutors.newDirectExecutorService());

client = mock(Client.class);
when(client.threadPool()).thenReturn(threadPool);
AdminClient adminClient = mock(AdminClient.class);
IndicesAdminClient indicesAdminClient = mock(IndicesAdminClient.class);
when(adminClient.indices()).thenReturn(indicesAdminClient);
when(client.admin()).thenReturn(adminClient);
doAnswer(invocationOnMock -> {
ActionListener<AcknowledgedResponse> listener =
(ActionListener<AcknowledgedResponse>) invocationOnMock.getArguments()[1];
listener.onResponse(new TestPutIndexTemplateResponse(true));
return null;
}).when(indicesAdminClient).putTemplate(any(PutIndexTemplateRequest.class), any(ActionListener.class));

clusterService = mock(ClusterService.class);
threadPool = new TestThreadPool(this.getClass().getName());
client = new VerifyingClient(threadPool);
clusterService = ClusterServiceUtils.createClusterService(threadPool);
List<NamedXContentRegistry.Entry> entries = new ArrayList<>(ClusterModule.getNamedXWriteables());
entries.addAll(Arrays.asList(
new NamedXContentRegistry.Entry(LifecycleType.class, new ParseField(TimeseriesLifecycleType.TYPE),
Expand All @@ -107,6 +90,13 @@ public void createRegistryAndClient() {
registry = new SnapshotLifecycleTemplateRegistry(Settings.EMPTY, clusterService, threadPool, client, xContentRegistry);
}

@After
@Override
public void tearDown() throws Exception {
super.tearDown();
threadPool.shutdownNow();
}

public void testDisabledDoesNotAddTemplates() {
Settings settings = Settings.builder().put(SLM_HISTORY_INDEX_ENABLED_SETTING.getKey(), false).build();
SnapshotLifecycleTemplateRegistry disabledRegistry = new SnapshotLifecycleTemplateRegistry(settings, clusterService, threadPool,
Expand All @@ -115,34 +105,67 @@ public void testDisabledDoesNotAddTemplates() {
assertThat(disabledRegistry.getPolicyConfigs(), hasSize(0));
}

public void testThatNonExistingTemplatesAreAddedImmediately() {
public void testThatNonExistingTemplatesAreAddedImmediately() throws Exception {
DiscoveryNode node = new DiscoveryNode("node", ESTestCase.buildNewFakeTransportAddress(), Version.CURRENT);
DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build();

ClusterChangedEvent event = createClusterChangedEvent(Collections.emptyList(), nodes);

AtomicInteger calledTimes = new AtomicInteger(0);
client.setVerifier((action, request, listener) -> {
if (action instanceof PutIndexTemplateAction) {
calledTimes.incrementAndGet();
assertThat(action, instanceOf(PutIndexTemplateAction.class));
assertThat(request, instanceOf(PutIndexTemplateRequest.class));
final PutIndexTemplateRequest putRequest = (PutIndexTemplateRequest) request;
assertThat(putRequest.name(), equalTo(SLM_TEMPLATE_NAME));
assertThat(putRequest.settings().get("index.lifecycle.name"), equalTo(SLM_POLICY_NAME));
assertNotNull(listener);
return new TestPutIndexTemplateResponse(true);
} else if (action instanceof PutLifecycleAction) {
// Ignore this, it's verified in another test
return new PutLifecycleAction.Response(true);
} else {
fail("client called with unexpected request:" + request.toString());
return null;
}
});
registry.clusterChanged(event);
ArgumentCaptor<PutIndexTemplateRequest> argumentCaptor = ArgumentCaptor.forClass(PutIndexTemplateRequest.class);
verify(client.admin().indices(), times(registry.getTemplateConfigs().size())).putTemplate(argumentCaptor.capture(), anyObject());
assertBusy(() -> assertThat(calledTimes.get(), equalTo(registry.getTemplateConfigs().size())));

calledTimes.set(0);
// now delete one template from the cluster state and lets retry
ClusterChangedEvent newEvent = createClusterChangedEvent(Collections.emptyList(), nodes);
registry.clusterChanged(newEvent);
ArgumentCaptor<PutIndexTemplateRequest> captor = ArgumentCaptor.forClass(PutIndexTemplateRequest.class);
verify(client.admin().indices(), times(registry.getTemplateConfigs().size() + 1)).putTemplate(captor.capture(), anyObject());
PutIndexTemplateRequest req = captor.getAllValues().stream()
.filter(r -> r.name().equals(SLM_TEMPLATE_NAME))
.findFirst()
.orElseThrow(() -> new AssertionError("expected the slm history template to be put"));
assertThat(req.settings().get("index.lifecycle.name"), equalTo(SLM_POLICY_NAME));
assertBusy(() -> assertThat(calledTimes.get(), equalTo(1)));
}

public void testThatNonExistingPoliciesAreAddedImmediately() {
public void testThatNonExistingPoliciesAreAddedImmediately() throws Exception {
DiscoveryNode node = new DiscoveryNode("node", ESTestCase.buildNewFakeTransportAddress(), Version.CURRENT);
DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build();

AtomicInteger calledTimes = new AtomicInteger(0);
client.setVerifier((action, request, listener) -> {
if (action instanceof PutLifecycleAction) {
calledTimes.incrementAndGet();
assertThat(action, instanceOf(PutLifecycleAction.class));
assertThat(request, instanceOf(PutLifecycleAction.Request.class));
final PutLifecycleAction.Request putRequest = (PutLifecycleAction.Request) request;
assertThat(putRequest.getPolicy().getName(), equalTo(SLM_POLICY_NAME));
assertNotNull(listener);
return new PutLifecycleAction.Response(true);
} else if (action instanceof PutIndexTemplateAction) {
// Ignore this, it's verified in another test
return new TestPutIndexTemplateResponse(true);
} else {
fail("client called with unexpected request:" + request.toString());
return null;
}
});

ClusterChangedEvent event = createClusterChangedEvent(Collections.emptyList(), nodes);
registry.clusterChanged(event);
verify(client, times(registry.getPolicyConfigs().size())).execute(eq(PutLifecycleAction.INSTANCE), anyObject(), anyObject());
assertBusy(() -> assertThat(calledTimes.get(), equalTo(1)));
}

public void testPolicyAlreadyExists() {
Expand All @@ -156,12 +179,24 @@ public void testPolicyAlreadyExists() {
assertThat(policies, hasSize(1));
LifecyclePolicy policy = policies.get(0);
policyMap.put(policy.getName(), policy);

client.setVerifier((action, request, listener) -> {
if (action instanceof PutIndexTemplateAction) {
// Ignore this, it's verified in another test
return new TestPutIndexTemplateResponse(true);
} else if (action instanceof PutLifecycleAction) {
fail("if the policy already exists it should be re-put");
} else {
fail("client called with unexpected request:" + request.toString());
}
return null;
});

ClusterChangedEvent event = createClusterChangedEvent(Collections.emptyList(), policyMap, nodes);
registry.clusterChanged(event);
verify(client, times(0)).execute(eq(PutLifecycleAction.INSTANCE), anyObject(), anyObject());
}

public void testThatTemplatesExist() throws IOException {
public void testPolicyAlreadyExistsButDiffers() throws IOException {
DiscoveryNode node = new DiscoveryNode("node", ESTestCase.buildNewFakeTransportAddress(), Version.CURRENT);
DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build();

Expand All @@ -172,45 +207,39 @@ public void testThatTemplatesExist() throws IOException {
.collect(Collectors.toList());
assertThat(policies, hasSize(1));
LifecyclePolicy policy = policies.get(0);

client.setVerifier((action, request, listener) -> {
if (action instanceof PutIndexTemplateAction) {
// Ignore this, it's verified in another test
return new TestPutIndexTemplateResponse(true);
} else if (action instanceof PutLifecycleAction) {
fail("if the policy already exists it should be re-put");
} else {
fail("client called with unexpected request:" + request.toString());
}
return null;
});

try (XContentParser parser = XContentType.JSON.xContent()
.createParser(xContentRegistry, LoggingDeprecationHandler.THROW_UNSUPPORTED_OPERATION, policyStr)) {
.createParser(xContentRegistry, LoggingDeprecationHandler.THROW_UNSUPPORTED_OPERATION, policyStr)) {
LifecyclePolicy different = LifecyclePolicy.parse(parser, policy.getName());
policyMap.put(policy.getName(), different);
ClusterChangedEvent event = createClusterChangedEvent(Collections.emptyList(), policyMap, nodes);
registry.clusterChanged(event);
verify(client, times(0)).execute(eq(PutLifecycleAction.INSTANCE), anyObject(), anyObject());
}
}

public void testThatMissingMasterNodeDoesNothing() {
DiscoveryNode localNode = new DiscoveryNode("node", ESTestCase.buildNewFakeTransportAddress(), Version.CURRENT);
DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").add(localNode).build();

client.setVerifier((a,r,l) -> {
fail("if the master is missing nothing should happen");
return null;
});

ClusterChangedEvent event = createClusterChangedEvent(Arrays.asList(SLM_TEMPLATE_NAME), nodes);
registry.clusterChanged(event);

verifyZeroInteractions(client);
}

public void testPolicyAlreadyExistsButDiffers() throws IOException {
DiscoveryNode node = new DiscoveryNode("node", ESTestCase.buildNewFakeTransportAddress(), Version.CURRENT);
DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build();

Map<String, LifecyclePolicy> policyMap = new HashMap<>();
String policyStr = "{\"phases\":{\"delete\":{\"min_age\":\"1m\",\"actions\":{\"delete\":{}}}}}";
List<LifecyclePolicy> policies = registry.getPolicyConfigs().stream()
.map(policyConfig -> policyConfig.load(xContentRegistry))
.collect(Collectors.toList());
assertThat(policies, hasSize(1));
LifecyclePolicy policy = policies.get(0);
try (XContentParser parser = XContentType.JSON.xContent()
.createParser(xContentRegistry, LoggingDeprecationHandler.THROW_UNSUPPORTED_OPERATION, policyStr)) {
LifecyclePolicy different = LifecyclePolicy.parse(parser, policy.getName());
policyMap.put(policy.getName(), different);
ClusterChangedEvent event = createClusterChangedEvent(Collections.emptyList(), policyMap, nodes);
registry.clusterChanged(event);
verify(client, times(0)).execute(eq(PutLifecycleAction.INSTANCE), anyObject(), anyObject());
}
}

public void testValidate() {
Expand All @@ -226,6 +255,31 @@ public void testValidate() {

// -------------

/**
* A client that delegates to a verifying function for action/request/listener
*/
public static class VerifyingClient extends NoOpClient {

private TriFunction<Action<?>, ActionRequest, ActionListener<?>, ActionResponse> verifier;

VerifyingClient(ThreadPool threadPool) {
super(threadPool);
}

@Override
@SuppressWarnings("unchecked")
protected <Request extends ActionRequest, Response extends ActionResponse> void doExecute(Action<Response> action,
Request request,
ActionListener<Response> listener) {
listener.onResponse((Response) verifier.apply(action, request, listener));
}

public VerifyingClient setVerifier(TriFunction<Action<?>, ActionRequest, ActionListener<?>, ActionResponse> verifier) {
this.verifier = verifier;
return this;
}
}

private ClusterChangedEvent createClusterChangedEvent(List<String> existingTemplateNames, DiscoveryNodes nodes) {
return createClusterChangedEvent(existingTemplateNames, Collections.emptyMap(), nodes);
}
Expand Down

0 comments on commit 483cd51

Please sign in to comment.