From bcf1b51ccc2510e2bcbc2eccf677666d85529c80 Mon Sep 17 00:00:00 2001 From: Sarat Vemulapalli Date: Tue, 5 Jan 2021 15:47:00 -0800 Subject: [PATCH] Adding test coverage for Preview Transport Action --- ...PreviewAnomalyDetectorTransportAction.java | 42 +-- .../ad/TestHelpers.java | 22 ++ .../PreviewAnomalyDetectorActionTests.java | 168 --------- ...ewAnomalyDetectorTransportActionTests.java | 335 ++++++++++++++++++ 4 files changed, 375 insertions(+), 192 deletions(-) create mode 100644 src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorTransportActionTests.java diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorTransportAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorTransportAction.java index d16e4e35..9fe6a746 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorTransportAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorTransportAction.java @@ -33,6 +33,7 @@ import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.CheckedConsumer; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; @@ -108,24 +109,22 @@ private ActionListener> getPreviewDetectorActionListener( ActionListener listener, AnomalyDetector detector ) { - return new ActionListener>() { + return ActionListener.wrap(new CheckedConsumer, Exception>() { @Override - public void onResponse(List anomalyResults) { - PreviewAnomalyDetectorResponse response = new PreviewAnomalyDetectorResponse(anomalyResults, detector); + public void accept(List anomalyResult) throws Exception { + PreviewAnomalyDetectorResponse response = new PreviewAnomalyDetectorResponse(anomalyResult, detector); listener.onResponse(response); } - - @Override - public void onFailure(Exception e) { - listener - .onFailure( - new ElasticsearchException( - "Unexpected error running anomaly detector " + detector.getDetectorId(), - RestStatus.INTERNAL_SERVER_ERROR - ) - ); - } - }; + }, exception -> { + logger.error("Unexpected error running anomaly detector " + detector.getDetectorId(), exception); + listener + .onFailure( + new ElasticsearchException( + "Unexpected error running anomaly detector " + detector.getDetectorId(), + RestStatus.INTERNAL_SERVER_ERROR + ) + ); + }); } private void previewAnomalyDetector( @@ -138,7 +137,7 @@ private void previewAnomalyDetector( GetRequest getRequest = new GetRequest(AnomalyDetector.ANOMALY_DETECTORS_INDEX).id(detectorId); client.get(getRequest, onGetAnomalyDetectorResponse(listener, startTime, endTime)); } else { - listener.onFailure(new ElasticsearchException("Wrong input, no detector id", RestStatus.NOT_FOUND)); + listener.onFailure(new ElasticsearchException("Wrong input, no detector id", RestStatus.BAD_REQUEST)); } } @@ -147,9 +146,9 @@ private ActionListener onGetAnomalyDetectorResponse( Instant startTime, Instant endTime ) { - return new ActionListener() { + return ActionListener.wrap(new CheckedConsumer() { @Override - public void onResponse(GetResponse response) { + public void accept(GetResponse response) throws Exception { if (!response.isExists()) { listener .onFailure( @@ -170,11 +169,6 @@ public void onResponse(GetResponse response) { listener.onFailure(e); } } - - @Override - public void onFailure(Exception e) { - listener.onFailure(new ElasticsearchException("Could not execute get query to find detector")); - } - }; + }, exception -> { listener.onFailure(new ElasticsearchException("Could not execute get query to find detector")); }); } } diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/TestHelpers.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/TestHelpers.java index 8b958768..82144629 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/ad/TestHelpers.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/TestHelpers.java @@ -32,6 +32,7 @@ import java.nio.ByteBuffer; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -113,6 +114,8 @@ import com.amazon.opendistroforelasticsearch.ad.constant.CommonName; import com.amazon.opendistroforelasticsearch.ad.constant.CommonValue; +import com.amazon.opendistroforelasticsearch.ad.feature.Features; +import com.amazon.opendistroforelasticsearch.ad.ml.ThresholdingResult; import com.amazon.opendistroforelasticsearch.ad.model.ADTask; import com.amazon.opendistroforelasticsearch.ad.model.ADTaskState; import com.amazon.opendistroforelasticsearch.ad.model.ADTaskType; @@ -477,6 +480,25 @@ public static Feature randomFeature(String featureName, String aggregationName, return new Feature(randomAlphaOfLength(5), featureName, enabled, testAggregation); } + public static Features randomFeatures() { + List> ranges = Arrays.asList(new AbstractMap.SimpleEntry<>(0L, 1L)); + double[][] unprocessed = new double[][] { { randomDouble(), randomDouble() } }; + double[][] processed = new double[][] { { randomDouble(), randomDouble() } }; + + return new Features(ranges, unprocessed, processed); + } + + public static List randomThresholdingResults() { + double grade = 1.; + double confidence = 0.5; + double score = 1.; + + ThresholdingResult thresholdingResult = new ThresholdingResult(grade, confidence, score); + List results = new ArrayList<>(); + results.add(thresholdingResult); + return results; + } + public static User randomUser() { return new User( randomAlphaOfLength(8), diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorActionTests.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorActionTests.java index 833fd9d7..cb57a0c7 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorActionTests.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorActionTests.java @@ -15,197 +15,29 @@ package com.amazon.opendistroforelasticsearch.ad.transport; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.concurrent.TimeUnit; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; -import org.elasticsearch.action.support.ActionFilters; -import org.elasticsearch.client.Client; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; -import org.elasticsearch.common.settings.ClusterSettings; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESSingleNodeTestCase; -import org.elasticsearch.transport.TransportService; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; -import com.amazon.opendistroforelasticsearch.ad.AnomalyDetectorRunner; import com.amazon.opendistroforelasticsearch.ad.TestHelpers; -import com.amazon.opendistroforelasticsearch.ad.feature.FeatureManager; -import com.amazon.opendistroforelasticsearch.ad.ml.ModelManager; import com.amazon.opendistroforelasticsearch.ad.model.AnomalyDetector; import com.amazon.opendistroforelasticsearch.ad.model.AnomalyResult; -import com.amazon.opendistroforelasticsearch.ad.settings.AnomalyDetectorSettings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; public class PreviewAnomalyDetectorActionTests extends ESSingleNodeTestCase { - private ActionListener response; - private PreviewAnomalyDetectorTransportAction action; - private AnomalyDetectorRunner runner; - private ClusterService clusterService; - private Task task; - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - task = mock(Task.class); - clusterService = mock(ClusterService.class); - ClusterSettings clusterSettings = new ClusterSettings( - Settings.EMPTY, - Collections.unmodifiableSet(new HashSet<>(Arrays.asList(AnomalyDetectorSettings.MAX_ANOMALY_FEATURES))) - ); - when(clusterService.getClusterSettings()).thenReturn(clusterSettings); - runner = new AnomalyDetectorRunner( - mock(ModelManager.class), - mock(FeatureManager.class), - AnomalyDetectorSettings.MAX_PREVIEW_RESULTS - ); - action = new PreviewAnomalyDetectorTransportAction( - Settings.EMPTY, - mock(TransportService.class), - clusterService, - mock(ActionFilters.class), - client(), - runner, - xContentRegistry() - ); - } @Override protected NamedWriteableRegistry writableRegistry() { return getInstanceFromNode(NamedWriteableRegistry.class); } - @Test - public void testPreviewTransportActionWithNoFeature() throws IOException { - // Detector with no feature, Preview should fail - AnomalyDetector detector = TestHelpers.randomAnomalyDetector(Collections.emptyList()); - PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest( - detector, - detector.getDetectorId(), - Instant.now(), - Instant.now() - ); - ActionListener previewResponse = new ActionListener() { - @Override - public void onResponse(PreviewAnomalyDetectorResponse response) { - Assert.assertTrue(false); - } - - @Override - public void onFailure(Exception e) { - Assert.assertTrue(e.getMessage().contains("Can't preview detector without feature")); - } - }; - action.doExecute(task, request, previewResponse); - } - - @Test - public void testPreviewTransportActionWithNoDetector() throws IOException { - // When detectorId is null, preview should fail - PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest(null, "", Instant.now(), Instant.now()); - ActionListener previewResponse = new ActionListener() { - @Override - public void onResponse(PreviewAnomalyDetectorResponse response) { - Assert.assertTrue(false); - } - - @Override - public void onFailure(Exception e) { - Assert.assertTrue(e.getMessage().contains("Wrong input, no detector id")); - } - }; - action.doExecute(task, request, previewResponse); - } - - @Test - public void testPreviewTransportActionWithDetectorID() throws IOException { - // When AD index does not exist, cannot query the detector - PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest(null, "1234", Instant.now(), Instant.now()); - ActionListener previewResponse = new ActionListener() { - @Override - public void onResponse(PreviewAnomalyDetectorResponse response) { - Assert.assertTrue(false); - } - - @Override - public void onFailure(Exception e) { - Assert.assertTrue(e.getMessage().contains("Could not execute get query to find detector")); - } - }; - action.doExecute(task, request, previewResponse); - } - - @Test - public void testPreviewTransportActionWithIndex() throws IOException { - // When AD index exists, and detector does not exist - PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest(null, "1234", Instant.now(), Instant.now()); - Settings indexSettings = Settings.builder().put("index.number_of_shards", 5).put("index.number_of_replicas", 1).build(); - CreateIndexRequest indexRequest = new CreateIndexRequest(AnomalyDetector.ANOMALY_DETECTORS_INDEX, indexSettings); - client().admin().indices().create(indexRequest).actionGet(); - ActionListener previewResponse = new ActionListener() { - @Override - public void onResponse(PreviewAnomalyDetectorResponse response) { - Assert.assertTrue(false); - } - - @Override - public void onFailure(Exception e) { - Assert.assertTrue(e.getMessage().contains("Can't find anomaly detector with id:1234")); - } - }; - action.doExecute(task, request, previewResponse); - } - - @Test - public void testPreviewTransportActionNoContext() throws IOException { - Client client = mock(Client.class); - PreviewAnomalyDetectorTransportAction previewAction = new PreviewAnomalyDetectorTransportAction( - Settings.EMPTY, - mock(TransportService.class), - clusterService, - mock(ActionFilters.class), - client, - runner, - xContentRegistry() - ); - AnomalyDetector detector = TestHelpers.randomAnomalyDetector(ImmutableMap.of("testKey", "testValue"), Instant.now()); - PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest( - detector, - detector.getDetectorId(), - Instant.now(), - Instant.now() - ); - ActionListener previewResponse = new ActionListener() { - @Override - public void onResponse(PreviewAnomalyDetectorResponse response) { - Assert.assertTrue(false); - } - - @Override - public void onFailure(Exception e) { - Assert.assertTrue(e.getClass() == NullPointerException.class); - } - }; - previewAction.doExecute(task, request, previewResponse); - } - @Test public void testPreviewRequest() throws Exception { BytesStreamOutput out = new BytesStreamOutput(); diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorTransportActionTests.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorTransportActionTests.java new file mode 100644 index 00000000..7f410e7a --- /dev/null +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/PreviewAnomalyDetectorTransportActionTests.java @@ -0,0 +1,335 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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.amazon.opendistroforelasticsearch.ad.transport; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.WriteRequest; +import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.transport.TransportService; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.amazon.opendistroforelasticsearch.ad.AnomalyDetectorRunner; +import com.amazon.opendistroforelasticsearch.ad.TestHelpers; +import com.amazon.opendistroforelasticsearch.ad.feature.FeatureManager; +import com.amazon.opendistroforelasticsearch.ad.feature.Features; +import com.amazon.opendistroforelasticsearch.ad.indices.AnomalyDetectionIndices; +import com.amazon.opendistroforelasticsearch.ad.ml.ModelManager; +import com.amazon.opendistroforelasticsearch.ad.model.AnomalyDetector; +import com.amazon.opendistroforelasticsearch.ad.model.AnomalyResult; +import com.amazon.opendistroforelasticsearch.ad.settings.AnomalyDetectorSettings; +import com.amazon.opendistroforelasticsearch.ad.util.RestHandlerUtils; +import com.google.common.collect.ImmutableMap; + +public class PreviewAnomalyDetectorTransportActionTests extends ESSingleNodeTestCase { + private ActionListener response; + private PreviewAnomalyDetectorTransportAction action; + private AnomalyDetectorRunner runner; + private ClusterService clusterService; + private FeatureManager featureManager; + private ModelManager modelManager; + private Task task; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + task = mock(Task.class); + clusterService = mock(ClusterService.class); + ClusterSettings clusterSettings = new ClusterSettings( + Settings.EMPTY, + Collections.unmodifiableSet(new HashSet<>(Arrays.asList(AnomalyDetectorSettings.MAX_ANOMALY_FEATURES))) + ); + when(clusterService.getClusterSettings()).thenReturn(clusterSettings); + featureManager = mock(FeatureManager.class); + modelManager = mock(ModelManager.class); + runner = new AnomalyDetectorRunner(modelManager, featureManager, AnomalyDetectorSettings.MAX_PREVIEW_RESULTS); + action = new PreviewAnomalyDetectorTransportAction( + Settings.EMPTY, + mock(TransportService.class), + clusterService, + mock(ActionFilters.class), + client(), + runner, + xContentRegistry() + ); + } + + @SuppressWarnings("unchecked") + @Test + public void testPreviewTransportAction() throws IOException, InterruptedException { + final CountDownLatch inProgressLatch = new CountDownLatch(1); + AnomalyDetector detector = TestHelpers.randomAnomalyDetector(ImmutableMap.of("testKey", "testValue"), Instant.now()); + PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest( + detector, + detector.getDetectorId(), + Instant.now(), + Instant.now() + ); + ActionListener previewResponse = new ActionListener() { + @Override + public void onResponse(PreviewAnomalyDetectorResponse response) { + try { + XContentBuilder previewBuilder = response.toXContent(TestHelpers.builder(), ToXContent.EMPTY_PARAMS); + Assert.assertNotNull(previewBuilder); + Map map = TestHelpers.XContentBuilderToMap(previewBuilder); + List results = (List) map.get("anomaly_result"); + Assert.assertNotNull(results); + Assert.assertTrue(results.size() > 0); + inProgressLatch.countDown(); + } catch (IOException e) { + // Should not reach here + Assert.assertTrue(false); + } + } + + @Override + public void onFailure(Exception e) { + // onFailure should not be called + Assert.assertTrue(false); + } + }; + + doReturn(TestHelpers.randomThresholdingResults()).when(modelManager).getPreviewResults(any()); + + doAnswer(responseMock -> { + Long startTime = responseMock.getArgument(1); + ActionListener listener = responseMock.getArgument(3); + listener.onResponse(TestHelpers.randomFeatures()); + return null; + }).when(featureManager).getPreviewFeatures(anyObject(), anyLong(), anyLong(), any()); + action.doExecute(task, request, previewResponse); + assertTrue(inProgressLatch.await(100, TimeUnit.SECONDS)); + } + + @Test + public void testPreviewTransportActionWithNoFeature() throws IOException, InterruptedException { + // Detector with no feature, Preview should fail + final CountDownLatch inProgressLatch = new CountDownLatch(1); + AnomalyDetector detector = TestHelpers.randomAnomalyDetector(Collections.emptyList()); + PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest( + detector, + detector.getDetectorId(), + Instant.now(), + Instant.now() + ); + ActionListener previewResponse = new ActionListener() { + @Override + public void onResponse(PreviewAnomalyDetectorResponse response) { + Assert.assertTrue(false); + } + + @Override + public void onFailure(Exception e) { + Assert.assertTrue(e.getMessage().contains("Can't preview detector without feature")); + inProgressLatch.countDown(); + } + }; + action.doExecute(task, request, previewResponse); + assertTrue(inProgressLatch.await(100, TimeUnit.SECONDS)); + } + + @Test + public void testPreviewTransportActionWithNoDetector() throws IOException, InterruptedException { + // When detectorId is null, preview should fail + final CountDownLatch inProgressLatch = new CountDownLatch(1); + PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest(null, "", Instant.now(), Instant.now()); + ActionListener previewResponse = new ActionListener() { + @Override + public void onResponse(PreviewAnomalyDetectorResponse response) { + Assert.assertTrue(false); + } + + @Override + public void onFailure(Exception e) { + Assert.assertTrue(e.getMessage().contains("Wrong input, no detector id")); + inProgressLatch.countDown(); + } + }; + action.doExecute(task, request, previewResponse); + assertTrue(inProgressLatch.await(100, TimeUnit.SECONDS)); + } + + @Test + public void testPreviewTransportActionWithDetectorID() throws IOException, InterruptedException { + // When AD index does not exist, cannot query the detector + final CountDownLatch inProgressLatch = new CountDownLatch(1); + PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest(null, "1234", Instant.now(), Instant.now()); + ActionListener previewResponse = new ActionListener() { + @Override + public void onResponse(PreviewAnomalyDetectorResponse response) { + Assert.assertTrue(false); + } + + @Override + public void onFailure(Exception e) { + Assert.assertTrue(e.getMessage().contains("Could not execute get query to find detector")); + inProgressLatch.countDown(); + } + }; + action.doExecute(task, request, previewResponse); + assertTrue(inProgressLatch.await(100, TimeUnit.SECONDS)); + } + + @Test + public void testPreviewTransportActionWithIndex() throws IOException, InterruptedException { + // When AD index exists, and detector does not exist + final CountDownLatch inProgressLatch = new CountDownLatch(1); + PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest(null, "1234", Instant.now(), Instant.now()); + Settings indexSettings = Settings.builder().put("index.number_of_shards", 5).put("index.number_of_replicas", 1).build(); + CreateIndexRequest indexRequest = new CreateIndexRequest(AnomalyDetector.ANOMALY_DETECTORS_INDEX, indexSettings); + client().admin().indices().create(indexRequest).actionGet(); + ActionListener previewResponse = new ActionListener() { + @Override + public void onResponse(PreviewAnomalyDetectorResponse response) { + Assert.assertTrue(false); + } + + @Override + public void onFailure(Exception e) { + Assert.assertTrue(e.getMessage().contains("Can't find anomaly detector with id:1234")); + inProgressLatch.countDown(); + } + }; + action.doExecute(task, request, previewResponse); + assertTrue(inProgressLatch.await(100, TimeUnit.SECONDS)); + } + + @Test + public void testPreviewTransportActionNoContext() throws IOException, InterruptedException { + final CountDownLatch inProgressLatch = new CountDownLatch(1); + Client client = mock(Client.class); + PreviewAnomalyDetectorTransportAction previewAction = new PreviewAnomalyDetectorTransportAction( + Settings.EMPTY, + mock(TransportService.class), + clusterService, + mock(ActionFilters.class), + client, + runner, + xContentRegistry() + ); + AnomalyDetector detector = TestHelpers.randomAnomalyDetector(ImmutableMap.of("testKey", "testValue"), Instant.now()); + PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest( + detector, + detector.getDetectorId(), + Instant.now(), + Instant.now() + ); + ActionListener previewResponse = new ActionListener() { + @Override + public void onResponse(PreviewAnomalyDetectorResponse response) { + Assert.assertTrue(false); + } + + @Override + public void onFailure(Exception e) { + Assert.assertTrue(e.getClass() == NullPointerException.class); + inProgressLatch.countDown(); + } + }; + previewAction.doExecute(task, request, previewResponse); + assertTrue(inProgressLatch.await(100, TimeUnit.SECONDS)); + } + + @SuppressWarnings("unchecked") + @Test + public void testPreviewTransportActionWithDetector() throws IOException, InterruptedException { + final CountDownLatch inProgressLatch = new CountDownLatch(1); + CreateIndexResponse createResponse = TestHelpers + .createIndex(client().admin(), AnomalyDetector.ANOMALY_DETECTORS_INDEX, AnomalyDetectionIndices.getAnomalyDetectorMappings()); + Assert.assertNotNull(createResponse); + + AnomalyDetector detector = TestHelpers.randomAnomalyDetector(ImmutableMap.of("testKey", "testValue"), Instant.now()); + IndexRequest indexRequest = new IndexRequest(AnomalyDetector.ANOMALY_DETECTORS_INDEX) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .source(detector.toXContent(XContentFactory.jsonBuilder(), RestHandlerUtils.XCONTENT_WITH_TYPE)); + IndexResponse indexResponse = client().index(indexRequest).actionGet(5_000); + assertEquals(RestStatus.CREATED, indexResponse.status()); + + PreviewAnomalyDetectorRequest request = new PreviewAnomalyDetectorRequest( + null, + indexResponse.getId(), + Instant.now(), + Instant.now() + ); + ActionListener previewResponse = new ActionListener() { + @Override + public void onResponse(PreviewAnomalyDetectorResponse response) { + try { + XContentBuilder previewBuilder = response.toXContent(TestHelpers.builder(), ToXContent.EMPTY_PARAMS); + Assert.assertNotNull(previewBuilder); + Map map = TestHelpers.XContentBuilderToMap(previewBuilder); + List results = (List) map.get("anomaly_result"); + Assert.assertNotNull(results); + Assert.assertTrue(results.size() > 0); + inProgressLatch.countDown(); + } catch (IOException e) { + // Should not reach here + Assert.assertTrue(false); + } + } + + @Override + public void onFailure(Exception e) { + // onFailure should not be called + Assert.assertTrue(false); + } + }; + doReturn(TestHelpers.randomThresholdingResults()).when(modelManager).getPreviewResults(any()); + + doAnswer(responseMock -> { + Long startTime = responseMock.getArgument(1); + ActionListener listener = responseMock.getArgument(3); + listener.onResponse(TestHelpers.randomFeatures()); + return null; + }).when(featureManager).getPreviewFeatures(anyObject(), anyLong(), anyLong(), any()); + action.doExecute(task, request, previewResponse); + assertTrue(inProgressLatch.await(100, TimeUnit.SECONDS)); + } +}