diff options
author | Jim Hahn <jrh3@att.com> | 2020-02-08 08:32:59 -0500 |
---|---|---|
committer | Jim Hahn <jrh3@att.com> | 2020-02-11 17:31:35 -0500 |
commit | 35867f2e63c26d47417bfefc9a0912f17c4a873a (patch) | |
tree | 77791874b4337725a0c109a2d1195ee0c4e5a3a6 /models-interactions/model-actors/actorServiceProvider/src/test | |
parent | fd79f7920d454c35d6a8c02d430d9beba434dcc2 (diff) |
Add more code to facilitate actor implementation
Added obtain() to Context
Added startGuardAsync(), in anticipation of adding guards.
Moved logRestXxx() from Util to HttpOperation.
Added actor.test to facilitate testing of actors.
Changed timeoutSec from long to int in various places.
Made a couple of methods public to support junit testing.
Most of the methods required Params to be passed, which indicated a
design issue. Split Operator and Operation so that the Params could
be kept in a field and thus need not be passed to every method.
Basically, renamed OperatorPartial.java to OperationPartial.java and
created a new OperatorPartial.java. Of course, this makes it look to
gerrit like it's all new code, when in fact, most of it is unchanged,
other than removing the Params argument to the method calls. That
accounts for about half of the "lines changed" count.
Issue-ID: POLICY-1625
Change-Id: I9e98c9dadcbed145bf84deb06c9db1c864a3c24a
Signed-off-by: Jim Hahn <jrh3@att.com>
Diffstat (limited to 'models-interactions/model-actors/actorServiceProvider/src/test')
14 files changed, 2275 insertions, 1509 deletions
diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/AsyncResponseHandlerTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/AsyncResponseHandlerTest.java deleted file mode 100644 index 31c6d2077..000000000 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/AsyncResponseHandlerTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * ONAP - * ================================================================================ - * Copyright (C) 2020 AT&T Intellectual Property. 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. - * 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. - * ============LICENSE_END========================================================= - */ - -package org.onap.policy.controlloop.actorserviceprovider; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.Before; -import org.junit.Test; -import org.onap.policy.controlloop.VirtualControlLoopEvent; -import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext; -import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; -import org.onap.policy.controlloop.policy.PolicyResult; - -public class AsyncResponseHandlerTest { - - private static final String ACTOR = "my-actor"; - private static final String OPERATION = "my-operation"; - private static final UUID REQ_ID = UUID.randomUUID(); - private static final String TEXT = "some text"; - - private VirtualControlLoopEvent event; - private ControlLoopEventContext context; - private ControlLoopOperationParams params; - private OperationOutcome outcome; - private MyHandler handler; - - /** - * Initializes all fields, including {@link #handler}. - */ - @Before - public void setUp() { - event = new VirtualControlLoopEvent(); - event.setRequestId(REQ_ID); - - context = new ControlLoopEventContext(event); - params = ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).context(context).build(); - outcome = params.makeOutcome(); - - handler = new MyHandler(params, outcome); - } - - @Test - public void testAsyncResponseHandler_testGetParams_testGetOutcome() { - assertSame(params, handler.getParams()); - assertSame(outcome, handler.getOutcome()); - } - - @Test - public void testHandle() { - CompletableFuture<String> future = new CompletableFuture<>(); - handler.handle(future).complete(outcome); - - assertTrue(future.isCancelled()); - } - - @Test - public void testCompleted() throws Exception { - CompletableFuture<OperationOutcome> result = handler.handle(new CompletableFuture<>()); - handler.completed(TEXT); - assertTrue(result.isDone()); - assertSame(outcome, result.get()); - assertEquals(PolicyResult.FAILURE_RETRIES, outcome.getResult()); - assertEquals(TEXT, outcome.getMessage()); - } - - /** - * Tests completed() when doCompleted() throws an exception. - */ - @Test - public void testCompletedException() throws Exception { - IllegalStateException except = new IllegalStateException(); - - outcome = params.makeOutcome(); - handler = new MyHandler(params, outcome) { - @Override - protected OperationOutcome doComplete(String rawResponse) { - throw except; - } - }; - - CompletableFuture<OperationOutcome> result = handler.handle(new CompletableFuture<>()); - handler.completed(TEXT); - assertTrue(result.isCompletedExceptionally()); - - AtomicReference<Throwable> thrown = new AtomicReference<>(); - result.whenComplete((unused, thrown2) -> thrown.set(thrown2)); - - assertSame(except, thrown.get()); - } - - @Test - public void testFailed() throws Exception { - IllegalStateException except = new IllegalStateException(); - - CompletableFuture<OperationOutcome> result = handler.handle(new CompletableFuture<>()); - handler.failed(except); - - assertTrue(result.isDone()); - assertSame(outcome, result.get()); - assertEquals(PolicyResult.FAILURE_GUARD, outcome.getResult()); - } - - /** - * Tests failed() when doFailed() throws an exception. - */ - @Test - public void testFailedException() throws Exception { - IllegalStateException except = new IllegalStateException(); - - outcome = params.makeOutcome(); - handler = new MyHandler(params, outcome) { - @Override - protected OperationOutcome doFailed(Throwable thrown) { - throw except; - } - }; - - CompletableFuture<OperationOutcome> result = handler.handle(new CompletableFuture<>()); - handler.failed(except); - assertTrue(result.isCompletedExceptionally()); - - AtomicReference<Throwable> thrown = new AtomicReference<>(); - result.whenComplete((unused, thrown2) -> thrown.set(thrown2)); - - assertSame(except, thrown.get()); - } - - private class MyHandler extends AsyncResponseHandler<String> { - - public MyHandler(ControlLoopOperationParams params, OperationOutcome outcome) { - super(params, outcome); - } - - @Override - protected OperationOutcome doComplete(String rawResponse) { - OperationOutcome outcome = getOutcome(); - outcome.setResult(PolicyResult.FAILURE_RETRIES); - outcome.setMessage(rawResponse); - return outcome; - } - - @Override - protected OperationOutcome doFailed(Throwable thrown) { - OperationOutcome outcome = getOutcome(); - outcome.setResult(PolicyResult.FAILURE_GUARD); - return outcome; - } - } -} diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/UtilTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/UtilTest.java index 4a3f321cf..0a2a5a90e 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/UtilTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/UtilTest.java @@ -39,16 +39,10 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import org.onap.policy.common.utils.coder.CoderException; -import org.onap.policy.common.utils.coder.StandardCoder; import org.onap.policy.common.utils.test.log.logback.ExtractAppender; import org.slf4j.LoggerFactory; public class UtilTest { - private static final String MY_REQUEST = "my-request"; - private static final String URL = "my-url"; - private static final String OUT_URL = "OUT|REST|my-url"; - private static final String IN_URL = "IN|REST|my-url"; protected static final String EXPECTED_EXCEPTION = "expected exception"; /** @@ -89,82 +83,6 @@ public class UtilTest { } @Test - public void testLogRestRequest() throws CoderException { - // log structured data - appender.clearExtractions(); - Util.logRestRequest(URL, new Abc(10, null, null)); - List<String> output = appender.getExtracted(); - assertEquals(1, output.size()); - - assertThat(output.get(0)).contains(OUT_URL).contains("{\n \"intValue\": 10\n}"); - - // log a plain string - appender.clearExtractions(); - Util.logRestRequest(URL, MY_REQUEST); - output = appender.getExtracted(); - assertEquals(1, output.size()); - - assertThat(output.get(0)).contains(OUT_URL).contains(MY_REQUEST); - - // exception from coder - StandardCoder coder = new StandardCoder() { - @Override - public String encode(Object object, boolean pretty) throws CoderException { - throw new CoderException(EXPECTED_EXCEPTION); - } - }; - - appender.clearExtractions(); - Util.logRestRequest(coder, URL, new Abc(11, null, null)); - output = appender.getExtracted(); - assertEquals(2, output.size()); - assertThat(output.get(0)).contains("cannot pretty-print request"); - assertThat(output.get(1)).contains(OUT_URL); - } - - @Test - public void testLogRestResponse() throws CoderException { - // log structured data - appender.clearExtractions(); - Util.logRestResponse(URL, new Abc(10, null, null)); - List<String> output = appender.getExtracted(); - assertEquals(1, output.size()); - - assertThat(output.get(0)).contains(IN_URL).contains("{\n \"intValue\": 10\n}"); - - // log null response - appender.clearExtractions(); - Util.logRestResponse(URL, null); - output = appender.getExtracted(); - assertEquals(1, output.size()); - - assertThat(output.get(0)).contains(IN_URL).contains("null"); - - // log a plain string - appender.clearExtractions(); - Util.logRestResponse(URL, MY_REQUEST); - output = appender.getExtracted(); - assertEquals(1, output.size()); - - assertThat(output.get(0)).contains(IN_URL).contains(MY_REQUEST); - - // exception from coder - StandardCoder coder = new StandardCoder() { - @Override - public String encode(Object object, boolean pretty) throws CoderException { - throw new CoderException(EXPECTED_EXCEPTION); - } - }; - - appender.clearExtractions(); - Util.logRestResponse(coder, URL, new Abc(11, null, null)); - output = appender.getExtracted(); - assertEquals(2, output.size()); - assertThat(output.get(0)).contains("cannot pretty-print response"); - assertThat(output.get(1)).contains(IN_URL); - } - - @Test public void testRunFunction() { // no exception, no log AtomicInteger count = new AtomicInteger(); diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/controlloop/ControlLoopEventContextTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/controlloop/ControlLoopEventContextTest.java index 0d917ad3e..b462043d5 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/controlloop/ControlLoopEventContextTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/controlloop/ControlLoopEventContextTest.java @@ -24,13 +24,21 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import org.junit.Before; import org.junit.Test; import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; +import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; public class ControlLoopEventContextTest { private static final UUID REQ_ID = UUID.randomUUID(); @@ -84,4 +92,39 @@ public class ControlLoopEventContextTest { int intValue = context.getProperty("def"); assertEquals(100, intValue); } + + @Test + public void testObtain() { + final ControlLoopOperationParams params = mock(ControlLoopOperationParams.class); + + // property is already loaded + context.setProperty("obtain-A", "value-A"); + assertNull(context.obtain("obtain-A", params)); + + // new property - should retrieve + CompletableFuture<OperationOutcome> future = new CompletableFuture<>(); + when(params.start()).thenReturn(future); + assertSame(future, context.obtain("obtain-B", params)); + + // repeat - should get the same future, without invoking start() again + assertSame(future, context.obtain("obtain-B", params)); + verify(params).start(); + + // arrange for another invoker to start while this one is starting + CompletableFuture<OperationOutcome> future2 = new CompletableFuture<>(); + + when(params.start()).thenAnswer(args -> { + + ControlLoopOperationParams params2 = mock(ControlLoopOperationParams.class); + when(params2.start()).thenReturn(future2); + + assertSame(future2, context.obtain("obtain-C", params2)); + return future; + }); + + assertSame(future2, context.obtain("obtain-C", params)); + + // should have canceled the interrupted future + assertTrue(future.isCancelled()); + } } diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/ActorImplTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/ActorImplTest.java index a209fb0d8..92cbbe774 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/ActorImplTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/ActorImplTest.java @@ -42,7 +42,9 @@ import org.junit.Before; import org.junit.Test; import org.onap.policy.common.parameters.ObjectValidationResult; import org.onap.policy.common.parameters.ValidationStatus; +import org.onap.policy.controlloop.actorserviceprovider.Operation; import org.onap.policy.controlloop.actorserviceprovider.Operator; +import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException; public class ActorImplTest { @@ -375,10 +377,15 @@ public class ActorImplTest { return actor; } - private static class MyOper extends OperatorPartial implements Operator { + private static class MyOper extends OperatorPartial { public MyOper(String name) { super(ACTOR_NAME, name); } + + @Override + public Operation buildOperation(ControlLoopOperationParams params) { + return null; + } } } diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpActorTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpActorTest.java index 2da789989..8ce3b3230 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpActorTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpActorTest.java @@ -38,7 +38,7 @@ public class HttpActorTest { private static final String ACTOR = "my-actor"; private static final String UNKNOWN = "unknown"; private static final String CLIENT = "my-client"; - private static final long TIMEOUT = 10L; + private static final int TIMEOUT = 10; private HttpActor actor; diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperationTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperationTest.java new file mode 100644 index 000000000..19f781d61 --- /dev/null +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperationTest.java @@ -0,0 +1,781 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. 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. + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.actorserviceprovider.impl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import ch.qos.logback.classic.Logger; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.InvocationCallback; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import lombok.Getter; +import lombok.Setter; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams; +import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams.TopicParamsBuilder; +import org.onap.policy.common.endpoints.http.client.HttpClient; +import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance; +import org.onap.policy.common.endpoints.http.server.HttpServletServer; +import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance; +import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; +import org.onap.policy.common.gson.GsonMessageBodyHandler; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.common.utils.network.NetworkUtil; +import org.onap.policy.common.utils.test.log.logback.ExtractAppender; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.actorserviceprovider.Operation; +import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; +import org.onap.policy.controlloop.actorserviceprovider.Util; +import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext; +import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; +import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams; +import org.onap.policy.controlloop.policy.PolicyResult; +import org.slf4j.LoggerFactory; + +public class HttpOperationTest { + + private static final IllegalStateException EXPECTED_EXCEPTION = new IllegalStateException("expected exception"); + private static final String ACTOR = "my-actor"; + private static final String OPERATION = "my-name"; + private static final String HTTP_CLIENT = "my-client"; + private static final String HTTP_NO_SERVER = "my-http-no-server-client"; + private static final String MEDIA_TYPE_APPLICATION_JSON = "application/json"; + private static final String MY_REQUEST = "my-request"; + private static final String BASE_URI = "oper"; + private static final String PATH = "/my-path"; + private static final String TEXT = "my-text"; + private static final UUID REQ_ID = UUID.randomUUID(); + + /** + * Used to attach an appender to the class' logger. + */ + private static final Logger logger = (Logger) LoggerFactory.getLogger(HttpOperation.class); + private static final ExtractAppender appender = new ExtractAppender(); + + /** + * {@code True} if the server should reject the request, {@code false} otherwise. + */ + private static boolean rejectRequest; + + // call counts of each method type in the server + private static int nget; + private static int npost; + private static int nput; + private static int ndelete; + + @Mock + private HttpClient client; + + @Mock + private Response response; + + private VirtualControlLoopEvent event; + private ControlLoopEventContext context; + private ControlLoopOperationParams params; + private OperationOutcome outcome; + private AtomicReference<InvocationCallback<Response>> callback; + private Future<Response> future; + private HttpOperator operator; + private MyGetOperation<String> oper; + + /** + * Starts the simulator. + */ + @BeforeClass + public static void setUpBeforeClass() throws Exception { + // allocate a port + int port = NetworkUtil.allocPort(); + + /* + * Start the simulator. Must use "Properties" to configure it, otherwise the + * server will use the wrong serialization provider. + */ + Properties svrprops = getServerProperties("my-server", port); + HttpServletServerFactoryInstance.getServerFactory().build(svrprops).forEach(HttpServletServer::start); + + if (!NetworkUtil.isTcpPortOpen("localhost", port, 100, 100)) { + HttpServletServerFactoryInstance.getServerFactory().destroy(); + throw new IllegalStateException("server is not running"); + } + + /* + * Start the clients, one to the server, and one to a non-existent server. + */ + TopicParamsBuilder builder = BusTopicParams.builder().managed(true).hostname("localhost").basePath(BASE_URI) + .serializationProvider(GsonMessageBodyHandler.class.getName()); + + HttpClientFactoryInstance.getClientFactory().build(builder.clientName(HTTP_CLIENT).port(port).build()); + + HttpClientFactoryInstance.getClientFactory() + .build(builder.clientName(HTTP_NO_SERVER).port(NetworkUtil.allocPort()).build()); + + /** + * Attach appender to the logger. + */ + appender.setContext(logger.getLoggerContext()); + appender.start(); + + logger.addAppender(appender); + } + + /** + * Destroys the Http factories and stops the appender. + */ + @AfterClass + public static void tearDownAfterClass() { + appender.stop(); + + HttpClientFactoryInstance.getClientFactory().destroy(); + HttpServletServerFactoryInstance.getServerFactory().destroy(); + } + + /** + * Initializes fields, including {@link #oper}, and resets the static fields used by + * the REST server. + */ + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + appender.clearExtractions(); + + rejectRequest = false; + nget = 0; + npost = 0; + nput = 0; + ndelete = 0; + + when(response.readEntity(String.class)).thenReturn(TEXT); + when(response.getStatus()).thenReturn(200); + + event = new VirtualControlLoopEvent(); + event.setRequestId(REQ_ID); + + context = new ControlLoopEventContext(event); + params = ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).context(context).build(); + + outcome = params.makeOutcome(); + + callback = new AtomicReference<>(); + future = new CompletableFuture<>(); + + operator = new HttpOperator(ACTOR, OPERATION) { + @Override + public Operation buildOperation(ControlLoopOperationParams params) { + return null; + } + + @Override + public HttpClient getClient() { + return client; + } + }; + + initOper(operator, HTTP_CLIENT); + + oper = new MyGetOperation<>(String.class); + } + + @Test + public void testHttpOperator() { + assertEquals(ACTOR, oper.getActorName()); + assertEquals(OPERATION, oper.getName()); + assertEquals(ACTOR + "." + OPERATION, oper.getFullName()); + } + + @Test + public void testMakeHeaders() { + assertEquals(Collections.emptyMap(), oper.makeHeaders()); + } + + @Test + public void testMakePath() { + assertEquals(PATH, oper.makePath()); + } + + @Test + public void testMakeUrl() { + // use a real client + client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT); + + assertThat(oper.makeUrl()).endsWith("/" + BASE_URI + PATH); + } + + @Test + public void testDoConfigureMapOfStringObject_testGetClient_testGetPath_testGetTimeoutMs() { + + // no default yet + assertEquals(0L, oper.getTimeoutMs(null)); + assertEquals(0L, oper.getTimeoutMs(0)); + + // should use given value + assertEquals(20 * 1000L, oper.getTimeoutMs(20)); + + // indicate we have a timeout value + operator = spy(operator); + when(operator.getTimeoutMs()).thenReturn(30L); + + oper = new MyGetOperation<String>(String.class); + + // should use default + assertEquals(30L, oper.getTimeoutMs(null)); + assertEquals(30L, oper.getTimeoutMs(0)); + + // should use given value + assertEquals(40 * 1000L, oper.getTimeoutMs(40)); + } + + /** + * Tests handleResponse() when it completes. + */ + @Test + public void testHandleResponseComplete() throws Exception { + CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> { + callback.set(cb); + return future; + }); + + assertFalse(future2.isDone()); + assertNotNull(callback.get()); + callback.get().completed(response); + + assertSame(outcome, future2.get(5, TimeUnit.SECONDS)); + + assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + } + + /** + * Tests handleResponse() when it fails. + */ + @Test + public void testHandleResponseFailed() throws Exception { + CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> { + callback.set(cb); + return future; + }); + + assertFalse(future2.isDone()); + assertNotNull(callback.get()); + callback.get().failed(EXPECTED_EXCEPTION); + + assertThatThrownBy(() -> future2.get(5, TimeUnit.SECONDS)).hasCause(EXPECTED_EXCEPTION); + + // future and future2 may be completed in parallel so we must wait again + assertThatThrownBy(() -> future.get(5, TimeUnit.SECONDS)).isInstanceOf(CancellationException.class); + assertTrue(future.isCancelled()); + } + + /** + * Tests processResponse() when it's a success and the response type is a String. + */ + @Test + public void testProcessResponseSuccessString() { + assertSame(outcome, oper.processResponse(outcome, PATH, response)); + assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + } + + /** + * Tests processResponse() when it's a failure. + */ + @Test + public void testProcessResponseFailure() { + when(response.getStatus()).thenReturn(555); + assertSame(outcome, oper.processResponse(outcome, PATH, response)); + assertEquals(PolicyResult.FAILURE, outcome.getResult()); + } + + /** + * Tests processResponse() when the decoder succeeds. + */ + @Test + public void testProcessResponseDecodeOk() throws CoderException { + when(response.readEntity(String.class)).thenReturn("10"); + + MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class); + + assertSame(outcome, oper2.processResponse(outcome, PATH, response)); + assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + } + + /** + * Tests processResponse() when the decoder throws an exception. + */ + @Test + public void testProcessResponseDecodeExcept() throws CoderException { + MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class); + + assertSame(outcome, oper2.processResponse(outcome, PATH, response)); + assertEquals(PolicyResult.FAILURE_EXCEPTION, outcome.getResult()); + } + + @Test + public void testPostProcessResponse() { + assertThatCode(() -> oper.postProcessResponse(outcome, PATH, null, null)).doesNotThrowAnyException(); + } + + @Test + public void testIsSuccess() { + when(response.getStatus()).thenReturn(200); + assertTrue(oper.isSuccess(response, null)); + + when(response.getStatus()).thenReturn(555); + assertFalse(oper.isSuccess(response, null)); + } + + /** + * Tests a GET. + */ + @Test + public void testGet() throws Exception { + // use a real client + client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT); + + MyGetOperation<MyResponse> oper2 = new MyGetOperation<>(MyResponse.class); + + OperationOutcome outcome = runOperation(oper2); + assertNotNull(outcome); + assertEquals(1, nget); + assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + } + + /** + * Tests a DELETE. + */ + @Test + public void testDelete() throws Exception { + // use a real client + client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT); + + MyDeleteOperation oper2 = new MyDeleteOperation(); + + OperationOutcome outcome = runOperation(oper2); + assertNotNull(outcome); + assertEquals(1, ndelete); + assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + } + + /** + * Tests a POST. + */ + @Test + public void testPost() throws Exception { + // use a real client + client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT); + + MyPostOperation oper2 = new MyPostOperation(); + + OperationOutcome outcome = runOperation(oper2); + assertNotNull(outcome); + assertEquals(1, npost); + assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + } + + /** + * Tests a PUT. + */ + @Test + public void testPut() throws Exception { + // use a real client + client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT); + + MyPutOperation oper2 = new MyPutOperation(); + + OperationOutcome outcome = runOperation(oper2); + assertNotNull(outcome); + assertEquals(1, nput); + assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + } + + @Test + public void testLogRestRequest() throws CoderException { + // log structured data + appender.clearExtractions(); + oper.logRestRequest(PATH, new MyRequest()); + List<String> output = appender.getExtracted(); + assertEquals(1, output.size()); + + assertThat(output.get(0)).contains(PATH).contains("{\n \"input\": \"some input\"\n}"); + + // log a plain string + appender.clearExtractions(); + oper.logRestRequest(PATH, MY_REQUEST); + output = appender.getExtracted(); + assertEquals(1, output.size()); + + assertThat(output.get(0)).contains(PATH).contains(MY_REQUEST); + + // log a null request + appender.clearExtractions(); + oper.logRestRequest(PATH, null); + output = appender.getExtracted(); + assertEquals(1, output.size()); + + // exception from coder + oper = new MyGetOperation<>(String.class) { + @Override + protected Coder makeCoder() { + return new StandardCoder() { + @Override + public String encode(Object object, boolean pretty) throws CoderException { + throw new CoderException(EXPECTED_EXCEPTION); + } + }; + } + }; + + appender.clearExtractions(); + oper.logRestRequest(PATH, new MyRequest()); + output = appender.getExtracted(); + assertEquals(2, output.size()); + assertThat(output.get(0)).contains("cannot pretty-print request"); + assertThat(output.get(1)).contains(PATH); + } + + @Test + public void testLogRestResponse() throws CoderException { + // log structured data + appender.clearExtractions(); + oper.logRestResponse(PATH, new MyResponse()); + List<String> output = appender.getExtracted(); + assertEquals(1, output.size()); + + assertThat(output.get(0)).contains(PATH).contains("{\n \"output\": \"some output\"\n}"); + + // log a plain string + appender.clearExtractions(); + oper.logRestResponse(PATH, MY_REQUEST); + output = appender.getExtracted(); + assertEquals(1, output.size()); + + // log a null response + appender.clearExtractions(); + oper.logRestResponse(PATH, null); + output = appender.getExtracted(); + assertEquals(1, output.size()); + + assertThat(output.get(0)).contains(PATH).contains("null"); + + // exception from coder + oper = new MyGetOperation<>(String.class) { + @Override + protected Coder makeCoder() { + return new StandardCoder() { + @Override + public String encode(Object object, boolean pretty) throws CoderException { + throw new CoderException(EXPECTED_EXCEPTION); + } + }; + } + }; + + appender.clearExtractions(); + oper.logRestResponse(PATH, new MyResponse()); + output = appender.getExtracted(); + assertEquals(2, output.size()); + assertThat(output.get(0)).contains("cannot pretty-print response"); + assertThat(output.get(1)).contains(PATH); + } + + @Test + public void testMakeDecoder() { + assertNotNull(oper.makeCoder()); + } + + /** + * Gets server properties. + * + * @param name server name + * @param port server port + * @return server properties + */ + private static Properties getServerProperties(String name, int port) { + final Properties props = new Properties(); + props.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES, name); + + final String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + name; + + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX, Server.class.getName()); + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, "localhost"); + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, String.valueOf(port)); + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "true"); + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false"); + + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER, + GsonMessageBodyHandler.class.getName()); + return props; + } + + /** + * Initializes the given operator. + * + * @param operator operator to be initialized + * @param clientName name of the client which it should use + */ + private void initOper(HttpOperator operator, String clientName) { + operator.stop(); + + HttpParams params = HttpParams.builder().clientName(clientName).path(PATH).build(); + Map<String, Object> mapParams = Util.translateToMap(OPERATION, params); + operator.configure(mapParams); + operator.start(); + } + + /** + * Runs the operation. + * + * @param operator operator on which to start the operation + * @return the outcome of the operation, or {@code null} if it does not complete in + * time + */ + private <T> OperationOutcome runOperation(HttpOperation<T> operator) + throws InterruptedException, ExecutionException, TimeoutException { + + CompletableFuture<OperationOutcome> future = operator.start(); + + return future.get(5, TimeUnit.SECONDS); + } + + @Getter + @Setter + public static class MyRequest { + private String input = "some input"; + } + + @Getter + @Setter + public static class MyResponse { + private String output = "some output"; + } + + private class MyGetOperation<T> extends HttpOperation<T> { + public MyGetOperation(Class<T> responseClass) { + super(HttpOperationTest.this.params, HttpOperationTest.this.operator, responseClass); + } + + @Override + protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) { + Map<String, Object> headers = makeHeaders(); + + headers.put("Accept", MediaType.APPLICATION_JSON); + String url = makeUrl(); + + logRestRequest(url, null); + + // @formatter:off + return handleResponse(outcome, url, + callback -> operator.getClient().get(callback, makePath(), headers)); + // @formatter:on + } + } + + private class MyPostOperation extends HttpOperation<MyResponse> { + public MyPostOperation() { + super(HttpOperationTest.this.params, HttpOperationTest.this.operator, MyResponse.class); + } + + @Override + protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) { + + MyRequest request = new MyRequest(); + + Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON); + + Map<String, Object> headers = makeHeaders(); + + headers.put("Accept", MediaType.APPLICATION_JSON); + String url = makeUrl(); + + logRestRequest(url, request); + + // @formatter:off + return handleResponse(outcome, url, + callback -> operator.getClient().post(callback, makePath(), entity, headers)); + // @formatter:on + } + } + + private class MyPutOperation extends HttpOperation<MyResponse> { + public MyPutOperation() { + super(HttpOperationTest.this.params, HttpOperationTest.this.operator, MyResponse.class); + } + + @Override + protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) { + + MyRequest request = new MyRequest(); + + Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON); + + Map<String, Object> headers = makeHeaders(); + + headers.put("Accept", MediaType.APPLICATION_JSON); + String url = makeUrl(); + + logRestRequest(url, request); + + // @formatter:off + return handleResponse(outcome, url, + callback -> operator.getClient().put(callback, makePath(), entity, headers)); + // @formatter:on + } + } + + private class MyDeleteOperation extends HttpOperation<String> { + public MyDeleteOperation() { + super(HttpOperationTest.this.params, HttpOperationTest.this.operator, String.class); + } + + @Override + protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) { + Map<String, Object> headers = makeHeaders(); + + headers.put("Accept", MediaType.APPLICATION_JSON); + String url = makeUrl(); + + logRestRequest(url, null); + + // @formatter:off + return handleResponse(outcome, url, + callback -> operator.getClient().delete(callback, makePath(), headers)); + // @formatter:on + } + } + + /** + * Simulator. + */ + @Path("/" + BASE_URI) + @Produces(MEDIA_TYPE_APPLICATION_JSON) + @Consumes(value = {MEDIA_TYPE_APPLICATION_JSON}) + public static class Server { + + /** + * Generates a response to a GET. + * + * @return resulting response + */ + @GET + @Path(PATH) + public Response getRequest() { + ++nget; + + if (rejectRequest) { + return Response.status(Status.BAD_REQUEST).build(); + + } else { + return Response.status(Status.OK).entity(new MyResponse()).build(); + } + } + + /** + * Generates a response to a POST. + * + * @param request incoming request + * @return resulting response + */ + @POST + @Path(PATH) + public Response postRequest(MyRequest request) { + ++npost; + + if (rejectRequest) { + return Response.status(Status.BAD_REQUEST).build(); + + } else { + return Response.status(Status.OK).entity(new MyResponse()).build(); + } + } + + /** + * Generates a response to a PUT. + * + * @param request incoming request + * @return resulting response + */ + @PUT + @Path(PATH) + public Response putRequest(MyRequest request) { + ++nput; + + if (rejectRequest) { + return Response.status(Status.BAD_REQUEST).build(); + + } else { + return Response.status(Status.OK).entity(new MyResponse()).build(); + } + } + + /** + * Generates a response to a DELETE. + * + * @return resulting response + */ + @DELETE + @Path(PATH) + public Response deleteRequest() { + ++ndelete; + + if (rejectRequest) { + return Response.status(Status.BAD_REQUEST).build(); + + } else { + return Response.status(Status.OK).entity(new MyResponse()).build(); + } + } + } +} diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperatorTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperatorTest.java index c006cf333..081bb346b 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperatorTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperatorTest.java @@ -23,19 +23,25 @@ package org.onap.policy.controlloop.actorserviceprovider.impl; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Map; +import java.util.concurrent.CompletableFuture; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.onap.policy.common.endpoints.http.client.HttpClient; import org.onap.policy.common.endpoints.http.client.HttpClientFactory; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.actorserviceprovider.Operation; +import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; import org.onap.policy.controlloop.actorserviceprovider.Util; +import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext; +import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams; import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException; @@ -43,62 +49,116 @@ public class HttpOperatorTest { private static final String ACTOR = "my-actor"; private static final String OPERATION = "my-name"; - private static final String CLIENT = "my-client"; - private static final String PATH = "my-path"; - private static final long TIMEOUT = 100; + private static final String HTTP_CLIENT = "my-client"; + private static final String PATH = "/my-path"; + private static final int TIMEOUT = 100; @Mock private HttpClient client; - private HttpOperator oper; + @Mock + private HttpClientFactory factory; + + private MyOperator oper; /** - * Initializes fields, including {@link #oper}. + * Initializes fields, including {@link #oper}, and resets the static fields used by + * the REST server. */ @Before public void setUp() { MockitoAnnotations.initMocks(this); - oper = new HttpOperator(ACTOR, OPERATION); + when(factory.get(HTTP_CLIENT)).thenReturn(client); + + oper = new MyOperator(); + + HttpParams params = HttpParams.builder().clientName(HTTP_CLIENT).path(PATH).timeoutSec(TIMEOUT).build(); + Map<String, Object> paramMap = Util.translateToMap(OPERATION, params); + oper.configure(paramMap); } @Test - public void testDoConfigureMapOfStringObject_testGetClient_testGetPath_testGetTimeoutSec() { + public void testHttpOperator() { + assertEquals(ACTOR, oper.getActorName()); + assertEquals(OPERATION, oper.getName()); + assertEquals(ACTOR + "." + OPERATION, oper.getFullName()); + } + + @Test + public void testGetClient() { + assertNotNull(oper.getClient()); + } + + @Test + public void testMakeOperator() { + HttpOperator oper2 = HttpOperator.makeOperator(ACTOR, OPERATION, MyOperation::new); + assertNotNull(oper2); + + VirtualControlLoopEvent event = new VirtualControlLoopEvent(); + ControlLoopEventContext context = new ControlLoopEventContext(event); + ControlLoopOperationParams params = + ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).context(context).build(); + + Operation operation1 = oper2.buildOperation(params); + assertNotNull(operation1); + + Operation operation2 = oper2.buildOperation(params); + assertNotNull(operation2); + assertNotSame(operation1, operation2); + } + + @Test + public void testDoConfigureMapOfStringObject_testGetClient_testGetPath_testGetTimeoutMs() { + // start with an UNCONFIGURED operator + oper.shutdown(); + oper = new MyOperator(); + assertNull(oper.getClient()); assertNull(oper.getPath()); - assertEquals(0L, oper.getTimeoutSec()); - - oper = new HttpOperator(ACTOR, OPERATION) { - @Override - protected HttpClientFactory getClientFactory() { - HttpClientFactory factory = mock(HttpClientFactory.class); - when(factory.get(CLIENT)).thenReturn(client); - return factory; - } - }; - - HttpParams params = HttpParams.builder().clientName(CLIENT).path(PATH).timeoutSec(TIMEOUT).build(); + + // no timeout yet + assertEquals(0L, oper.getTimeoutMs()); + + HttpParams params = HttpParams.builder().clientName(HTTP_CLIENT).path(PATH).timeoutSec(TIMEOUT).build(); Map<String, Object> paramMap = Util.translateToMap(OPERATION, params); oper.configure(paramMap); assertSame(client, oper.getClient()); assertEquals(PATH, oper.getPath()); - assertEquals(TIMEOUT, oper.getTimeoutSec()); + + // should use given value + assertEquals(TIMEOUT * 1000, oper.getTimeoutMs()); // test invalid parameters paramMap.remove("path"); assertThatThrownBy(() -> oper.configure(paramMap)).isInstanceOf(ParameterValidationRuntimeException.class); } - @Test - public void testHttpOperator() { - assertEquals(ACTOR, oper.getActorName()); - assertEquals(OPERATION, oper.getName()); - assertEquals(ACTOR + "." + OPERATION, oper.getFullName()); + private class MyOperator extends HttpOperator { + public MyOperator() { + super(ACTOR, OPERATION); + } + + @Override + public Operation buildOperation(ControlLoopOperationParams params) { + return null; + } + + @Override + protected HttpClientFactory getClientFactory() { + return factory; + } } - @Test - public void testGetClient() { - assertNotNull(oper.getClientFactory()); + private class MyOperation extends HttpOperation<String> { + public MyOperation(ControlLoopOperationParams params, HttpOperator operator) { + super(params, operator, String.class); + } + + @Override + protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) { + return null; + } } } diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperationPartialTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperationPartialTest.java new file mode 100644 index 000000000..0d5cb2444 --- /dev/null +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperationPartialTest.java @@ -0,0 +1,1302 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. 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. + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.actorserviceprovider.impl; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.time.Instant; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Queue; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.Getter; +import lombok.Setter; +import org.junit.Before; +import org.junit.Test; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.controlloop.ControlLoopOperation; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.actorserviceprovider.Operation; +import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; +import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext; +import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; +import org.onap.policy.controlloop.actorserviceprovider.pipeline.PipelineControllerFuture; +import org.onap.policy.controlloop.policy.PolicyResult; + +public class OperationPartialTest { + private static final int MAX_PARALLEL_REQUESTS = 10; + private static final String EXPECTED_EXCEPTION = "expected exception"; + private static final String ACTOR = "my-actor"; + private static final String OPERATION = "my-operation"; + private static final String TARGET = "my-target"; + private static final int TIMEOUT = 1000; + private static final UUID REQ_ID = UUID.randomUUID(); + + private static final List<PolicyResult> FAILURE_RESULTS = Arrays.asList(PolicyResult.values()).stream() + .filter(result -> result != PolicyResult.SUCCESS).collect(Collectors.toList()); + + private VirtualControlLoopEvent event; + private ControlLoopEventContext context; + private MyExec executor; + private ControlLoopOperationParams params; + + private MyOper oper; + + private int numStart; + private int numEnd; + + private Instant tstart; + + private OperationOutcome opstart; + private OperationOutcome opend; + + private OperatorPartial operator; + + /** + * Initializes the fields, including {@link #oper}. + */ + @Before + public void setUp() { + event = new VirtualControlLoopEvent(); + event.setRequestId(REQ_ID); + + context = new ControlLoopEventContext(event); + executor = new MyExec(); + + params = ControlLoopOperationParams.builder().completeCallback(this::completer).context(context) + .executor(executor).actor(ACTOR).operation(OPERATION).timeoutSec(TIMEOUT) + .startCallback(this::starter).targetEntity(TARGET).build(); + + operator = new OperatorPartial(ACTOR, OPERATION) { + @Override + public Executor getBlockingExecutor() { + return executor; + } + + @Override + public Operation buildOperation(ControlLoopOperationParams params) { + return null; + } + }; + + operator.configure(null); + operator.start(); + + oper = new MyOper(); + + tstart = null; + + opstart = null; + opend = null; + } + + @Test + public void testOperatorPartial_testGetActorName_testGetName() { + assertEquals(ACTOR, oper.getActorName()); + assertEquals(OPERATION, oper.getName()); + assertEquals(ACTOR + "." + OPERATION, oper.getFullName()); + } + + @Test + public void testGetBlockingThread() throws Exception { + CompletableFuture<Void> future = new CompletableFuture<>(); + + // use the real executor + OperatorPartial oper2 = new OperatorPartial(ACTOR, OPERATION) { + @Override + public Operation buildOperation(ControlLoopOperationParams params) { + return null; + } + }; + + oper2.getBlockingExecutor().execute(() -> future.complete(null)); + + assertNull(future.get(5, TimeUnit.SECONDS)); + } + + /** + * Exercises the doXxx() methods. + */ + @Test + public void testDoXxx() { + assertThatCode(() -> operator.doConfigure(null)).doesNotThrowAnyException(); + assertThatCode(() -> operator.doStart()).doesNotThrowAnyException(); + assertThatCode(() -> operator.doStop()).doesNotThrowAnyException(); + assertThatCode(() -> operator.doShutdown()).doesNotThrowAnyException(); + + } + + @Test + public void testStart() { + verifyRun("testStart", 1, 1, PolicyResult.SUCCESS); + } + + /** + * Tests startOperation() when the operator is not running. + */ + @Test + public void testStartNotRunning() { + // stop the operator + operator.stop(); + + assertThatIllegalStateException().isThrownBy(() -> oper.start()); + } + + /** + * Tests startOperation() when the operation has a preprocessor. + */ + @Test + public void testStartWithPreprocessor() { + AtomicInteger count = new AtomicInteger(); + + CompletableFuture<OperationOutcome> preproc = CompletableFuture.supplyAsync(() -> { + count.incrementAndGet(); + return makeSuccess(); + }, executor); + + oper.setGuard(preproc); + + verifyRun("testStartWithPreprocessor_testStartPreprocessor", 1, 1, PolicyResult.SUCCESS); + + assertEquals(1, count.get()); + } + + /** + * Tests start() with multiple running requests. + */ + @Test + public void testStartMultiple() { + for (int count = 0; count < MAX_PARALLEL_REQUESTS; ++count) { + oper.start(); + } + + assertTrue(executor.runAll()); + + assertNotNull(opstart); + assertNotNull(opend); + assertEquals(PolicyResult.SUCCESS, opend.getResult()); + + assertEquals(MAX_PARALLEL_REQUESTS, numStart); + assertEquals(MAX_PARALLEL_REQUESTS, oper.getCount()); + assertEquals(MAX_PARALLEL_REQUESTS, numEnd); + } + + /** + * Tests startPreprocessor() when the preprocessor returns a failure. + */ + @Test + public void testStartPreprocessorFailure() { + oper.setGuard(CompletableFuture.completedFuture(makeFailure())); + + verifyRun("testStartPreprocessorFailure", 1, 0, PolicyResult.FAILURE_GUARD); + } + + /** + * Tests startPreprocessor() when the preprocessor throws an exception. + */ + @Test + public void testStartPreprocessorException() { + // arrange for the preprocessor to throw an exception + oper.setGuard(CompletableFuture.failedFuture(new IllegalStateException(EXPECTED_EXCEPTION))); + + verifyRun("testStartPreprocessorException", 1, 0, PolicyResult.FAILURE_GUARD); + } + + /** + * Tests startPreprocessor() when the pipeline is not running. + */ + @Test + public void testStartPreprocessorNotRunning() { + // arrange for the preprocessor to return success, which will be ignored + oper.setGuard(CompletableFuture.completedFuture(makeSuccess())); + + oper.start().cancel(false); + assertTrue(executor.runAll()); + + assertNull(opstart); + assertNull(opend); + + assertEquals(0, numStart); + assertEquals(0, oper.getCount()); + assertEquals(0, numEnd); + } + + /** + * Tests startPreprocessor() when the preprocessor <b>builder</b> throws an exception. + */ + @Test + public void testStartPreprocessorBuilderException() { + oper = new MyOper() { + @Override + protected CompletableFuture<OperationOutcome> startPreprocessorAsync() { + throw new IllegalStateException(EXPECTED_EXCEPTION); + } + }; + + assertThatIllegalStateException().isThrownBy(() -> oper.start()); + + // should be nothing in the queue + assertEquals(0, executor.getQueueLength()); + } + + @Test + public void testStartPreprocessorAsync() { + assertNull(oper.startPreprocessorAsync()); + } + + @Test + public void testStartGuardAsync() { + assertNull(oper.startGuardAsync()); + } + + @Test + public void testStartOperationAsync() { + oper.start(); + assertTrue(executor.runAll()); + + assertEquals(1, oper.getCount()); + } + + @Test + public void testIsSuccess() { + OperationOutcome outcome = new OperationOutcome(); + + outcome.setResult(PolicyResult.SUCCESS); + assertTrue(oper.isSuccess(outcome)); + + for (PolicyResult failure : FAILURE_RESULTS) { + outcome.setResult(failure); + assertFalse("testIsSuccess-" + failure, oper.isSuccess(outcome)); + } + } + + @Test + public void testIsActorFailed() { + assertFalse(oper.isActorFailed(null)); + + OperationOutcome outcome = params.makeOutcome(); + + // incorrect outcome + outcome.setResult(PolicyResult.SUCCESS); + assertFalse(oper.isActorFailed(outcome)); + + outcome.setResult(PolicyResult.FAILURE_RETRIES); + assertFalse(oper.isActorFailed(outcome)); + + // correct outcome + outcome.setResult(PolicyResult.FAILURE); + + // incorrect actor + outcome.setActor(TARGET); + assertFalse(oper.isActorFailed(outcome)); + outcome.setActor(null); + assertFalse(oper.isActorFailed(outcome)); + outcome.setActor(ACTOR); + + // incorrect operation + outcome.setOperation(TARGET); + assertFalse(oper.isActorFailed(outcome)); + outcome.setOperation(null); + assertFalse(oper.isActorFailed(outcome)); + outcome.setOperation(OPERATION); + + // correct values + assertTrue(oper.isActorFailed(outcome)); + } + + @Test + public void testDoOperation() { + /* + * Use an operation that doesn't override doOperation(). + */ + OperationPartial oper2 = new OperationPartial(params, operator) {}; + + oper2.start(); + assertTrue(executor.runAll()); + + assertNotNull(opend); + assertEquals(PolicyResult.FAILURE_EXCEPTION, opend.getResult()); + } + + @Test + public void testTimeout() throws Exception { + + // use a real executor + params = params.toBuilder().executor(ForkJoinPool.commonPool()).build(); + + // trigger timeout very quickly + oper = new MyOper() { + @Override + protected long getTimeoutMs(Integer timeoutSec) { + return 1; + } + + @Override + protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) { + + OperationOutcome outcome2 = params.makeOutcome(); + outcome2.setResult(PolicyResult.SUCCESS); + + /* + * Create an incomplete future that will timeout after the operation's + * timeout. If it fires before the other timer, then it will return a + * SUCCESS outcome. + */ + CompletableFuture<OperationOutcome> future = new CompletableFuture<>(); + future = future.orTimeout(1, TimeUnit.SECONDS).handleAsync((unused1, unused2) -> outcome, + params.getExecutor()); + + return future; + } + }; + + assertEquals(PolicyResult.FAILURE_TIMEOUT, oper.start().get().getResult()); + } + + /** + * Tests retry functions, when the count is set to zero and retries are exhausted. + */ + @Test + public void testSetRetryFlag_testRetryOnFailure_ZeroRetries_testStartOperationAttempt() { + params = params.toBuilder().retry(0).build(); + + // new params, thus need a new operation + oper = new MyOper(); + + oper.setMaxFailures(10); + + verifyRun("testSetRetryFlag_testRetryOnFailure_ZeroRetries", 1, 1, PolicyResult.FAILURE); + } + + /** + * Tests retry functions, when the count is null and retries are exhausted. + */ + @Test + public void testSetRetryFlag_testRetryOnFailure_NullRetries() { + params = params.toBuilder().retry(null).build(); + + // new params, thus need a new operation + oper = new MyOper(); + + oper.setMaxFailures(10); + + verifyRun("testSetRetryFlag_testRetryOnFailure_NullRetries", 1, 1, PolicyResult.FAILURE); + } + + /** + * Tests retry functions, when retries are exhausted. + */ + @Test + public void testSetRetryFlag_testRetryOnFailure_RetriesExhausted() { + final int maxRetries = 3; + params = params.toBuilder().retry(maxRetries).build(); + + // new params, thus need a new operation + oper = new MyOper(); + + oper.setMaxFailures(10); + + verifyRun("testSetRetryFlag_testRetryOnFailure_RetriesExhausted", maxRetries + 1, maxRetries + 1, + PolicyResult.FAILURE_RETRIES); + } + + /** + * Tests retry functions, when a success follows some retries. + */ + @Test + public void testSetRetryFlag_testRetryOnFailure_SuccessAfterRetries() { + params = params.toBuilder().retry(10).build(); + + // new params, thus need a new operation + oper = new MyOper(); + + final int maxFailures = 3; + oper.setMaxFailures(maxFailures); + + verifyRun("testSetRetryFlag_testRetryOnFailure_SuccessAfterRetries", maxFailures + 1, maxFailures + 1, + PolicyResult.SUCCESS); + } + + /** + * Tests retry functions, when the outcome is {@code null}. + */ + @Test + public void testSetRetryFlag_testRetryOnFailure_NullOutcome() { + + // arrange to return null from doOperation() + oper = new MyOper() { + @Override + protected OperationOutcome doOperation(int attempt, OperationOutcome operation) { + + // update counters + super.doOperation(attempt, operation); + return null; + } + }; + + verifyRun("testSetRetryFlag_testRetryOnFailure_NullOutcome", 1, 1, PolicyResult.FAILURE, null, noop()); + } + + @Test + public void testSleep() throws Exception { + CompletableFuture<Void> future = oper.sleep(-1, TimeUnit.SECONDS); + assertTrue(future.isDone()); + assertNull(future.get()); + + // edge case + future = oper.sleep(0, TimeUnit.SECONDS); + assertTrue(future.isDone()); + assertNull(future.get()); + + /* + * Start a second sleep we can use to check the first while it's running. + */ + tstart = Instant.now(); + future = oper.sleep(100, TimeUnit.MILLISECONDS); + + CompletableFuture<Void> future2 = oper.sleep(10, TimeUnit.MILLISECONDS); + + // wait for second to complete and verify that the first has not completed + future2.get(); + assertFalse(future.isDone()); + + // wait for second to complete + future.get(); + + long diff = Instant.now().toEpochMilli() - tstart.toEpochMilli(); + assertTrue(diff >= 99); + } + + @Test + public void testIsSameOperation() { + assertFalse(oper.isSameOperation(null)); + + OperationOutcome outcome = params.makeOutcome(); + + // wrong actor - should be false + outcome.setActor(null); + assertFalse(oper.isSameOperation(outcome)); + outcome.setActor(TARGET); + assertFalse(oper.isSameOperation(outcome)); + outcome.setActor(ACTOR); + + // wrong operation - should be null + outcome.setOperation(null); + assertFalse(oper.isSameOperation(outcome)); + outcome.setOperation(TARGET); + assertFalse(oper.isSameOperation(outcome)); + outcome.setOperation(OPERATION); + + assertTrue(oper.isSameOperation(outcome)); + } + + /** + * Tests handleFailure() when the outcome is a success. + */ + @Test + public void testHandlePreprocessorFailureTrue() { + oper.setGuard(CompletableFuture.completedFuture(makeSuccess())); + verifyRun("testHandlePreprocessorFailureTrue", 1, 1, PolicyResult.SUCCESS); + } + + /** + * Tests handleFailure() when the outcome is <i>not</i> a success. + */ + @Test + public void testHandlePreprocessorFailureFalse() throws Exception { + oper.setGuard(CompletableFuture.completedFuture(makeFailure())); + verifyRun("testHandlePreprocessorFailureFalse", 1, 0, PolicyResult.FAILURE_GUARD); + } + + /** + * Tests handleFailure() when the outcome is {@code null}. + */ + @Test + public void testHandlePreprocessorFailureNull() throws Exception { + // arrange to return null from the preprocessor + oper.setGuard(CompletableFuture.completedFuture(null)); + + verifyRun("testHandlePreprocessorFailureNull", 1, 0, PolicyResult.FAILURE_GUARD); + } + + @Test + public void testFromException() { + // arrange to generate an exception when operation runs + oper.setGenException(true); + + verifyRun("testFromException", 1, 1, PolicyResult.FAILURE_EXCEPTION); + } + + /** + * Tests fromException() when there is no exception. + */ + @Test + public void testFromExceptionNoExcept() { + verifyRun("testFromExceptionNoExcept", 1, 1, PolicyResult.SUCCESS); + } + + /** + * Tests both flavors of anyOf(), because one invokes the other. + */ + @Test + public void testAnyOf() throws Exception { + // first task completes, others do not + List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); + + final OperationOutcome outcome = params.makeOutcome(); + + tasks.add(CompletableFuture.completedFuture(outcome)); + tasks.add(new CompletableFuture<>()); + tasks.add(new CompletableFuture<>()); + + CompletableFuture<OperationOutcome> result = oper.anyOf(tasks); + assertTrue(executor.runAll()); + + assertTrue(result.isDone()); + assertSame(outcome, result.get()); + + // second task completes, others do not + tasks = new LinkedList<>(); + + tasks.add(new CompletableFuture<>()); + tasks.add(CompletableFuture.completedFuture(outcome)); + tasks.add(new CompletableFuture<>()); + + result = oper.anyOf(tasks); + assertTrue(executor.runAll()); + + assertTrue(result.isDone()); + assertSame(outcome, result.get()); + + // third task completes, others do not + tasks = new LinkedList<>(); + + tasks.add(new CompletableFuture<>()); + tasks.add(new CompletableFuture<>()); + tasks.add(CompletableFuture.completedFuture(outcome)); + + result = oper.anyOf(tasks); + assertTrue(executor.runAll()); + + assertTrue(result.isDone()); + assertSame(outcome, result.get()); + } + + /** + * Tests both flavors of anyOf(), for edge cases: zero items, and one item. + */ + @Test + @SuppressWarnings("unchecked") + public void testAnyOfEdge() throws Exception { + List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); + + // zero items: check both using a list and using an array + assertThatIllegalArgumentException().isThrownBy(() -> oper.anyOf(tasks)); + assertThatIllegalArgumentException().isThrownBy(() -> oper.anyOf()); + + // one item: : check both using a list and using an array + CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>(); + tasks.add(future1); + + assertSame(future1, oper.anyOf(tasks)); + assertSame(future1, oper.anyOf(future1)); + } + + /** + * Tests both flavors of allOf(), because one invokes the other. + */ + @Test + public void testAllOf() throws Exception { + List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); + + final OperationOutcome outcome = params.makeOutcome(); + + CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>(); + CompletableFuture<OperationOutcome> future2 = new CompletableFuture<>(); + CompletableFuture<OperationOutcome> future3 = new CompletableFuture<>(); + + tasks.add(future1); + tasks.add(future2); + tasks.add(future3); + + CompletableFuture<OperationOutcome> result = oper.allOf(tasks); + + assertTrue(executor.runAll()); + assertFalse(result.isDone()); + future1.complete(outcome); + + // complete 3 before 2 + assertTrue(executor.runAll()); + assertFalse(result.isDone()); + future3.complete(outcome); + + assertTrue(executor.runAll()); + assertFalse(result.isDone()); + future2.complete(outcome); + + // all of them are now done + assertTrue(executor.runAll()); + assertTrue(result.isDone()); + assertSame(outcome, result.get()); + } + + /** + * Tests both flavors of allOf(), for edge cases: zero items, and one item. + */ + @Test + @SuppressWarnings("unchecked") + public void testAllOfEdge() throws Exception { + List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); + + // zero items: check both using a list and using an array + assertThatIllegalArgumentException().isThrownBy(() -> oper.allOf(tasks)); + assertThatIllegalArgumentException().isThrownBy(() -> oper.allOf()); + + // one item: : check both using a list and using an array + CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>(); + tasks.add(future1); + + assertSame(future1, oper.allOf(tasks)); + assertSame(future1, oper.allOf(future1)); + } + + @Test + public void testCombineOutcomes() throws Exception { + // only one outcome + verifyOutcomes(0, PolicyResult.SUCCESS); + verifyOutcomes(0, PolicyResult.FAILURE_EXCEPTION); + + // maximum is in different positions + verifyOutcomes(0, PolicyResult.FAILURE, PolicyResult.SUCCESS, PolicyResult.FAILURE_GUARD); + verifyOutcomes(1, PolicyResult.SUCCESS, PolicyResult.FAILURE, PolicyResult.FAILURE_GUARD); + verifyOutcomes(2, PolicyResult.SUCCESS, PolicyResult.FAILURE_GUARD, PolicyResult.FAILURE); + + // null outcome + final List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); + tasks.add(CompletableFuture.completedFuture(null)); + CompletableFuture<OperationOutcome> result = oper.allOf(tasks); + + assertTrue(executor.runAll()); + assertTrue(result.isDone()); + assertNull(result.get()); + + // one throws an exception during execution + IllegalStateException except = new IllegalStateException(EXPECTED_EXCEPTION); + + tasks.clear(); + tasks.add(CompletableFuture.completedFuture(params.makeOutcome())); + tasks.add(CompletableFuture.failedFuture(except)); + tasks.add(CompletableFuture.completedFuture(params.makeOutcome())); + result = oper.allOf(tasks); + + assertTrue(executor.runAll()); + assertTrue(result.isCompletedExceptionally()); + result.whenComplete((unused, thrown) -> assertSame(except, thrown)); + } + + private void verifyOutcomes(int expected, PolicyResult... results) throws Exception { + List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); + + + OperationOutcome expectedOutcome = null; + + for (int count = 0; count < results.length; ++count) { + OperationOutcome outcome = params.makeOutcome(); + outcome.setResult(results[count]); + tasks.add(CompletableFuture.completedFuture(outcome)); + + if (count == expected) { + expectedOutcome = outcome; + } + } + + CompletableFuture<OperationOutcome> result = oper.allOf(tasks); + + assertTrue(executor.runAll()); + assertTrue(result.isDone()); + assertSame(expectedOutcome, result.get()); + } + + private Function<OperationOutcome, CompletableFuture<OperationOutcome>> makeTask( + final OperationOutcome taskOutcome) { + + return outcome -> CompletableFuture.completedFuture(taskOutcome); + } + + @Test + public void testDetmPriority() throws CoderException { + assertEquals(1, oper.detmPriority(null)); + + OperationOutcome outcome = params.makeOutcome(); + + Map<PolicyResult, Integer> map = Map.of(PolicyResult.SUCCESS, 0, PolicyResult.FAILURE_GUARD, 2, + PolicyResult.FAILURE_RETRIES, 3, PolicyResult.FAILURE, 4, PolicyResult.FAILURE_TIMEOUT, 5, + PolicyResult.FAILURE_EXCEPTION, 6); + + for (Entry<PolicyResult, Integer> ent : map.entrySet()) { + outcome.setResult(ent.getKey()); + assertEquals(ent.getKey().toString(), ent.getValue().intValue(), oper.detmPriority(outcome)); + } + + /* + * Test null result. We can't actually set it to null, because the set() method + * won't allow it. Instead, we decode it from a structure. + */ + outcome = new StandardCoder().decode("{\"result\":null}", OperationOutcome.class); + assertEquals(1, oper.detmPriority(outcome)); + } + + /** + * Tests doTask(Future) when the controller is not running. + */ + @Test + public void testDoTaskFutureNotRunning() throws Exception { + CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>(); + + PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); + controller.complete(params.makeOutcome()); + + CompletableFuture<OperationOutcome> future = oper.doTask(controller, false, params.makeOutcome(), taskFuture); + assertFalse(future.isDone()); + assertTrue(executor.runAll()); + + // should not have run the task + assertFalse(future.isDone()); + + // should have canceled the task future + assertTrue(taskFuture.isCancelled()); + } + + /** + * Tests doTask(Future) when the previous outcome was successful. + */ + @Test + public void testDoTaskFutureSuccess() throws Exception { + CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>(); + final OperationOutcome taskOutcome = params.makeOutcome(); + + PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); + + CompletableFuture<OperationOutcome> future = oper.doTask(controller, true, params.makeOutcome(), taskFuture); + + taskFuture.complete(taskOutcome); + assertTrue(executor.runAll()); + + assertTrue(future.isDone()); + assertSame(taskOutcome, future.get()); + + // controller should not be done yet + assertFalse(controller.isDone()); + } + + /** + * Tests doTask(Future) when the previous outcome was failed. + */ + @Test + public void testDoTaskFutureFailure() throws Exception { + CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>(); + final OperationOutcome failedOutcome = params.makeOutcome(); + failedOutcome.setResult(PolicyResult.FAILURE); + + PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); + + CompletableFuture<OperationOutcome> future = oper.doTask(controller, true, failedOutcome, taskFuture); + assertFalse(future.isDone()); + assertTrue(executor.runAll()); + + // should not have run the task + assertFalse(future.isDone()); + + // should have canceled the task future + assertTrue(taskFuture.isCancelled()); + + // controller SHOULD be done now + assertTrue(controller.isDone()); + assertSame(failedOutcome, controller.get()); + } + + /** + * Tests doTask(Future) when the previous outcome was failed, but not checking + * success. + */ + @Test + public void testDoTaskFutureUncheckedFailure() throws Exception { + CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>(); + final OperationOutcome failedOutcome = params.makeOutcome(); + failedOutcome.setResult(PolicyResult.FAILURE); + + PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); + + CompletableFuture<OperationOutcome> future = oper.doTask(controller, false, failedOutcome, taskFuture); + assertFalse(future.isDone()); + + // complete the task + OperationOutcome taskOutcome = params.makeOutcome(); + taskFuture.complete(taskOutcome); + + assertTrue(executor.runAll()); + + // should have run the task + assertTrue(future.isDone()); + + assertTrue(future.isDone()); + assertSame(taskOutcome, future.get()); + + // controller should not be done yet + assertFalse(controller.isDone()); + } + + /** + * Tests doTask(Function) when the controller is not running. + */ + @Test + public void testDoTaskFunctionNotRunning() throws Exception { + AtomicBoolean invoked = new AtomicBoolean(); + + Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = outcome -> { + invoked.set(true); + return CompletableFuture.completedFuture(params.makeOutcome()); + }; + + PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); + controller.complete(params.makeOutcome()); + + CompletableFuture<OperationOutcome> future = oper.doTask(controller, false, task).apply(params.makeOutcome()); + assertFalse(future.isDone()); + assertTrue(executor.runAll()); + + // should not have run the task + assertFalse(future.isDone()); + + // should not have even invoked the task + assertFalse(invoked.get()); + } + + /** + * Tests doTask(Function) when the previous outcome was successful. + */ + @Test + public void testDoTaskFunctionSuccess() throws Exception { + final OperationOutcome taskOutcome = params.makeOutcome(); + + final OperationOutcome failedOutcome = params.makeOutcome(); + + Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = makeTask(taskOutcome); + + PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); + + CompletableFuture<OperationOutcome> future = oper.doTask(controller, true, task).apply(failedOutcome); + + assertTrue(future.isDone()); + assertSame(taskOutcome, future.get()); + + // controller should not be done yet + assertFalse(controller.isDone()); + } + + /** + * Tests doTask(Function) when the previous outcome was failed. + */ + @Test + public void testDoTaskFunctionFailure() throws Exception { + final OperationOutcome failedOutcome = params.makeOutcome(); + failedOutcome.setResult(PolicyResult.FAILURE); + + AtomicBoolean invoked = new AtomicBoolean(); + + Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = outcome -> { + invoked.set(true); + return CompletableFuture.completedFuture(params.makeOutcome()); + }; + + PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); + + CompletableFuture<OperationOutcome> future = oper.doTask(controller, true, task).apply(failedOutcome); + assertFalse(future.isDone()); + assertTrue(executor.runAll()); + + // should not have run the task + assertFalse(future.isDone()); + + // should not have even invoked the task + assertFalse(invoked.get()); + + // controller should have the failed task + assertTrue(controller.isDone()); + assertSame(failedOutcome, controller.get()); + } + + /** + * Tests doTask(Function) when the previous outcome was failed, but not checking + * success. + */ + @Test + public void testDoTaskFunctionUncheckedFailure() throws Exception { + final OperationOutcome taskOutcome = params.makeOutcome(); + + final OperationOutcome failedOutcome = params.makeOutcome(); + failedOutcome.setResult(PolicyResult.FAILURE); + + Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = makeTask(taskOutcome); + + PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); + + CompletableFuture<OperationOutcome> future = oper.doTask(controller, false, task).apply(failedOutcome); + + assertTrue(future.isDone()); + assertSame(taskOutcome, future.get()); + + // controller should not be done yet + assertFalse(controller.isDone()); + } + + /** + * Tests callbackStarted() when the pipeline has already been stopped. + */ + @Test + public void testCallbackStartedNotRunning() { + AtomicReference<Future<OperationOutcome>> future = new AtomicReference<>(); + + /* + * arrange to stop the controller when the start-callback is invoked, but capture + * the outcome + */ + params = params.toBuilder().startCallback(oper -> { + starter(oper); + future.get().cancel(false); + }).build(); + + // new params, thus need a new operation + oper = new MyOper(); + + future.set(oper.start()); + assertTrue(executor.runAll()); + + // should have only run once + assertEquals(1, numStart); + } + + /** + * Tests callbackCompleted() when the pipeline has already been stopped. + */ + @Test + public void testCallbackCompletedNotRunning() { + AtomicReference<Future<OperationOutcome>> future = new AtomicReference<>(); + + // arrange to stop the controller when the start-callback is invoked + params = params.toBuilder().startCallback(oper -> { + future.get().cancel(false); + }).build(); + + // new params, thus need a new operation + oper = new MyOper(); + + future.set(oper.start()); + assertTrue(executor.runAll()); + + // should not have been set + assertNull(opend); + assertEquals(0, numEnd); + } + + @Test + public void testSetOutcomeControlLoopOperationOutcomeThrowable() { + final CompletionException timex = new CompletionException(new TimeoutException(EXPECTED_EXCEPTION)); + + OperationOutcome outcome; + + outcome = new OperationOutcome(); + oper.setOutcome(outcome, timex); + assertEquals(ControlLoopOperation.FAILED_MSG, outcome.getMessage()); + assertEquals(PolicyResult.FAILURE_TIMEOUT, outcome.getResult()); + + outcome = new OperationOutcome(); + oper.setOutcome(outcome, new IllegalStateException(EXPECTED_EXCEPTION)); + assertEquals(ControlLoopOperation.FAILED_MSG, outcome.getMessage()); + assertEquals(PolicyResult.FAILURE_EXCEPTION, outcome.getResult()); + } + + @Test + public void testSetOutcomeControlLoopOperationOutcomePolicyResult() { + OperationOutcome outcome; + + outcome = new OperationOutcome(); + oper.setOutcome(outcome, PolicyResult.SUCCESS); + assertEquals(ControlLoopOperation.SUCCESS_MSG, outcome.getMessage()); + assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + + for (PolicyResult result : FAILURE_RESULTS) { + outcome = new OperationOutcome(); + oper.setOutcome(outcome, result); + assertEquals(result.toString(), ControlLoopOperation.FAILED_MSG, outcome.getMessage()); + assertEquals(result.toString(), result, outcome.getResult()); + } + } + + @Test + public void testIsTimeout() { + final TimeoutException timex = new TimeoutException(EXPECTED_EXCEPTION); + + assertFalse(oper.isTimeout(new IllegalStateException(EXPECTED_EXCEPTION))); + assertFalse(oper.isTimeout(new IllegalStateException(timex))); + assertFalse(oper.isTimeout(new CompletionException(new IllegalStateException(timex)))); + assertFalse(oper.isTimeout(new CompletionException(null))); + assertFalse(oper.isTimeout(new CompletionException(new CompletionException(timex)))); + + assertTrue(oper.isTimeout(timex)); + assertTrue(oper.isTimeout(new CompletionException(timex))); + } + + @Test + public void testGetRetry() { + assertEquals(0, oper.getRetry(null)); + assertEquals(10, oper.getRetry(10)); + } + + @Test + public void testGetRetryWait() { + // need an operator that doesn't override the retry time + OperationPartial oper2 = new OperationPartial(params, operator) {}; + assertEquals(OperationPartial.DEFAULT_RETRY_WAIT_MS, oper2.getRetryWaitMs()); + } + + @Test + public void testGetTimeOutMs() { + assertEquals(TIMEOUT * 1000, oper.getTimeoutMs(params.getTimeoutSec())); + + params = params.toBuilder().timeoutSec(null).build(); + + // new params, thus need a new operation + oper = new MyOper(); + + assertEquals(0, oper.getTimeoutMs(params.getTimeoutSec())); + } + + private void starter(OperationOutcome oper) { + ++numStart; + tstart = oper.getStart(); + opstart = oper; + } + + private void completer(OperationOutcome oper) { + ++numEnd; + opend = oper; + } + + /** + * Gets a function that does nothing. + * + * @param <T> type of input parameter expected by the function + * @return a function that does nothing + */ + private <T> Consumer<T> noop() { + return unused -> { + }; + } + + private OperationOutcome makeSuccess() { + OperationOutcome outcome = params.makeOutcome(); + outcome.setResult(PolicyResult.SUCCESS); + + return outcome; + } + + private OperationOutcome makeFailure() { + OperationOutcome outcome = params.makeOutcome(); + outcome.setResult(PolicyResult.FAILURE); + + return outcome; + } + + /** + * Verifies a run. + * + * @param testName test name + * @param expectedCallbacks number of callbacks expected + * @param expectedOperations number of operation invocations expected + * @param expectedResult expected outcome + */ + private void verifyRun(String testName, int expectedCallbacks, int expectedOperations, + PolicyResult expectedResult) { + + String expectedSubRequestId = + (expectedResult == PolicyResult.FAILURE_EXCEPTION ? null : String.valueOf(expectedOperations)); + + verifyRun(testName, expectedCallbacks, expectedOperations, expectedResult, expectedSubRequestId, noop()); + } + + /** + * Verifies a run. + * + * @param testName test name + * @param expectedCallbacks number of callbacks expected + * @param expectedOperations number of operation invocations expected + * @param expectedResult expected outcome + * @param expectedSubRequestId expected sub request ID + * @param manipulator function to modify the future returned by + * {@link OperationPartial#start(ControlLoopOperationParams)} before the tasks + * in the executor are run + */ + private void verifyRun(String testName, int expectedCallbacks, int expectedOperations, PolicyResult expectedResult, + String expectedSubRequestId, Consumer<CompletableFuture<OperationOutcome>> manipulator) { + + CompletableFuture<OperationOutcome> future = oper.start(); + + manipulator.accept(future); + + assertTrue(testName, executor.runAll()); + + assertEquals(testName, expectedCallbacks, numStart); + assertEquals(testName, expectedCallbacks, numEnd); + + if (expectedCallbacks > 0) { + assertNotNull(testName, opstart); + assertNotNull(testName, opend); + assertEquals(testName, expectedResult, opend.getResult()); + + assertSame(testName, tstart, opstart.getStart()); + assertSame(testName, tstart, opend.getStart()); + + try { + assertTrue(future.isDone()); + assertSame(testName, opend, future.get()); + + } catch (InterruptedException | ExecutionException e) { + throw new IllegalStateException(e); + } + + if (expectedOperations > 0) { + assertEquals(testName, expectedSubRequestId, opend.getSubRequestId()); + } + } + + assertEquals(testName, expectedOperations, oper.getCount()); + } + + private class MyOper extends OperationPartial { + @Getter + private int count = 0; + + @Setter + private boolean genException; + + @Setter + private int maxFailures = 0; + + @Setter + private CompletableFuture<OperationOutcome> guard; + + + public MyOper() { + super(OperationPartialTest.this.params, operator); + } + + @Override + protected OperationOutcome doOperation(int attempt, OperationOutcome operation) { + ++count; + if (genException) { + throw new IllegalStateException(EXPECTED_EXCEPTION); + } + + operation.setSubRequestId(String.valueOf(attempt)); + + if (count > maxFailures) { + operation.setResult(PolicyResult.SUCCESS); + } else { + operation.setResult(PolicyResult.FAILURE); + } + + return operation; + } + + @Override + protected CompletableFuture<OperationOutcome> startGuardAsync() { + return (guard != null ? guard : super.startGuardAsync()); + } + + @Override + protected long getRetryWaitMs() { + /* + * Sleep timers run in the background, but we want to control things via the + * "executor", thus we avoid sleep timers altogether by simply returning 0. + */ + return 0L; + } + } + + /** + * Executor that will run tasks until the queue is empty or a maximum number of tasks + * have been executed. Doesn't actually run anything until {@link #runAll()} is + * invoked. + */ + private static class MyExec implements Executor { + private static final int MAX_TASKS = MAX_PARALLEL_REQUESTS * 100; + + private Queue<Runnable> commands = new LinkedList<>(); + + public MyExec() { + // do nothing + } + + public int getQueueLength() { + return commands.size(); + } + + @Override + public void execute(Runnable command) { + commands.add(command); + } + + public boolean runAll() { + for (int count = 0; count < MAX_TASKS && !commands.isEmpty(); ++count) { + commands.remove().run(); + } + + return commands.isEmpty(); + } + } +} diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperatorPartialTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperatorPartialTest.java index 21bc656f2..370426fd4 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperatorPartialTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperatorPartialTest.java @@ -20,1271 +20,88 @@ package org.onap.policy.controlloop.actorserviceprovider.impl; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import java.time.Instant; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Queue; import java.util.TreeMap; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; -import lombok.Getter; -import lombok.Setter; import org.junit.Before; import org.junit.Test; -import org.onap.policy.controlloop.ControlLoopOperation; -import org.onap.policy.controlloop.VirtualControlLoopEvent; -import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; -import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext; +import org.onap.policy.controlloop.actorserviceprovider.Operation; import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; -import org.onap.policy.controlloop.actorserviceprovider.pipeline.PipelineControllerFuture; -import org.onap.policy.controlloop.policy.PolicyResult; public class OperatorPartialTest { - private static final int MAX_PARALLEL_REQUESTS = 10; - private static final String EXPECTED_EXCEPTION = "expected exception"; private static final String ACTOR = "my-actor"; - private static final String OPERATOR = "my-operator"; - private static final String TARGET = "my-target"; - private static final int TIMEOUT = 1000; - private static final UUID REQ_ID = UUID.randomUUID(); + private static final String OPERATION = "my-name"; - private static final List<PolicyResult> FAILURE_RESULTS = Arrays.asList(PolicyResult.values()).stream() - .filter(result -> result != PolicyResult.SUCCESS).collect(Collectors.toList()); - - private VirtualControlLoopEvent event; - private Map<String, Object> config; - private ControlLoopEventContext context; - private MyExec executor; - private ControlLoopOperationParams params; - - private MyOper oper; - - private int numStart; - private int numEnd; - - private Instant tstart; - - private OperationOutcome opstart; - private OperationOutcome opend; + private OperatorPartial operator; /** - * Initializes the fields, including {@link #oper}. + * Initializes {@link #operator}. */ @Before public void setUp() { - event = new VirtualControlLoopEvent(); - event.setRequestId(REQ_ID); - - config = new TreeMap<>(); - context = new ControlLoopEventContext(event); - executor = new MyExec(); - - params = ControlLoopOperationParams.builder().completeCallback(this::completer).context(context) - .executor(executor).actor(ACTOR).operation(OPERATOR).timeoutSec(TIMEOUT) - .startCallback(this::starter).targetEntity(TARGET).build(); - - oper = new MyOper(); - oper.configure(new TreeMap<>()); - oper.start(); - - tstart = null; - - opstart = null; - opend = null; - } - - @Test - public void testOperatorPartial_testGetActorName_testGetName() { - assertEquals(ACTOR, oper.getActorName()); - assertEquals(OPERATOR, oper.getName()); - assertEquals(ACTOR + "." + OPERATOR, oper.getFullName()); - } - - @Test - public void testGetBlockingExecutor() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - /* - * Use an operator that doesn't override getBlockingExecutor(). - */ - OperatorPartial oper2 = new OperatorPartial(ACTOR, OPERATOR) {}; - oper2.getBlockingExecutor().execute(() -> latch.countDown()); - - assertTrue(latch.await(5, TimeUnit.SECONDS)); - } - - @Test - public void testDoConfigure() { - oper = spy(new MyOper()); - - oper.configure(config); - verify(oper).configure(config); - - // repeat - SHOULD be run again - oper.configure(config); - verify(oper, times(2)).configure(config); - } - - @Test - public void testDoStart() { - oper = spy(new MyOper()); - - oper.configure(config); - oper.start(); - - verify(oper).doStart(); - - // others should not have been invoked - verify(oper, never()).doStop(); - verify(oper, never()).doShutdown(); - } - - @Test - public void testDoStop() { - oper = spy(new MyOper()); - - oper.configure(config); - oper.start(); - oper.stop(); - - verify(oper).doStop(); - - // should not have been re-invoked - verify(oper).doStart(); - - // others should not have been invoked - verify(oper, never()).doShutdown(); - } - - @Test - public void testDoShutdown() { - oper = spy(new MyOper()); - - oper.configure(config); - oper.start(); - oper.shutdown(); - - verify(oper).doShutdown(); - - // should not have been re-invoked - verify(oper).doStart(); - - // others should not have been invoked - verify(oper, never()).doStop(); - } - - @Test - public void testStartOperation() { - verifyRun("testStartOperation", 1, 1, PolicyResult.SUCCESS); - } - - /** - * Tests startOperation() when the operator is not running. - */ - @Test - public void testStartOperationNotRunning() { - // use a new operator, one that hasn't been started yet - oper = new MyOper(); - oper.configure(new TreeMap<>()); - - assertThatIllegalStateException().isThrownBy(() -> oper.startOperation(params)); - } - - /** - * Tests startOperation() when the operation has a preprocessor. - */ - @Test - public void testStartOperationWithPreprocessor() { - AtomicInteger count = new AtomicInteger(); - - CompletableFuture<OperationOutcome> preproc = CompletableFuture.supplyAsync(() -> { - count.incrementAndGet(); - return makeSuccess(); - }, executor); - - oper.setPreProcessor(preproc); - - verifyRun("testStartOperationWithPreprocessor_testStartPreprocessor", 1, 1, PolicyResult.SUCCESS); - - assertEquals(1, count.get()); - } - - /** - * Tests startOperation() with multiple running requests. - */ - @Test - public void testStartOperationMultiple() { - for (int count = 0; count < MAX_PARALLEL_REQUESTS; ++count) { - oper.startOperation(params); - } - - assertTrue(executor.runAll()); - - assertNotNull(opstart); - assertNotNull(opend); - assertEquals(PolicyResult.SUCCESS, opend.getResult()); - - assertEquals(MAX_PARALLEL_REQUESTS, numStart); - assertEquals(MAX_PARALLEL_REQUESTS, oper.getCount()); - assertEquals(MAX_PARALLEL_REQUESTS, numEnd); - } - - /** - * Tests startPreprocessor() when the preprocessor returns a failure. - */ - @Test - public void testStartPreprocessorFailure() { - oper.setPreProcessor(CompletableFuture.completedFuture(makeFailure())); - - verifyRun("testStartPreprocessorFailure", 1, 0, PolicyResult.FAILURE_GUARD); - } - - /** - * Tests startPreprocessor() when the preprocessor throws an exception. - */ - @Test - public void testStartPreprocessorException() { - // arrange for the preprocessor to throw an exception - oper.setPreProcessor(CompletableFuture.failedFuture(new IllegalStateException(EXPECTED_EXCEPTION))); - - verifyRun("testStartPreprocessorException", 1, 0, PolicyResult.FAILURE_GUARD); - } - - /** - * Tests startPreprocessor() when the pipeline is not running. - */ - @Test - public void testStartPreprocessorNotRunning() { - // arrange for the preprocessor to return success, which will be ignored - oper.setPreProcessor(CompletableFuture.completedFuture(makeSuccess())); - - oper.startOperation(params).cancel(false); - assertTrue(executor.runAll()); - - assertNull(opstart); - assertNull(opend); - - assertEquals(0, numStart); - assertEquals(0, oper.getCount()); - assertEquals(0, numEnd); - } - - /** - * Tests startPreprocessor() when the preprocessor <b>builder</b> throws an exception. - */ - @Test - public void testStartPreprocessorBuilderException() { - oper = new MyOper() { - @Override - protected CompletableFuture<OperationOutcome> startPreprocessorAsync(ControlLoopOperationParams params) { - throw new IllegalStateException(EXPECTED_EXCEPTION); - } - }; - - oper.configure(new TreeMap<>()); - oper.start(); - - assertThatIllegalStateException().isThrownBy(() -> oper.startOperation(params)); - - // should be nothing in the queue - assertEquals(0, executor.getQueueLength()); - } - - @Test - public void testStartPreprocessorAsync() { - assertNull(oper.startPreprocessorAsync(params)); - } - - @Test - public void testStartOperationAsync() { - oper.startOperation(params); - assertTrue(executor.runAll()); - - assertEquals(1, oper.getCount()); - } - - @Test - public void testIsSuccess() { - OperationOutcome outcome = new OperationOutcome(); - - outcome.setResult(PolicyResult.SUCCESS); - assertTrue(oper.isSuccess(outcome)); - - for (PolicyResult failure : FAILURE_RESULTS) { - outcome.setResult(failure); - assertFalse("testIsSuccess-" + failure, oper.isSuccess(outcome)); - } - } - - @Test - public void testIsActorFailed() { - assertFalse(oper.isActorFailed(null)); - - OperationOutcome outcome = params.makeOutcome(); - - // incorrect outcome - outcome.setResult(PolicyResult.SUCCESS); - assertFalse(oper.isActorFailed(outcome)); - - outcome.setResult(PolicyResult.FAILURE_RETRIES); - assertFalse(oper.isActorFailed(outcome)); - - // correct outcome - outcome.setResult(PolicyResult.FAILURE); - - // incorrect actor - outcome.setActor(TARGET); - assertFalse(oper.isActorFailed(outcome)); - outcome.setActor(null); - assertFalse(oper.isActorFailed(outcome)); - outcome.setActor(ACTOR); - - // incorrect operation - outcome.setOperation(TARGET); - assertFalse(oper.isActorFailed(outcome)); - outcome.setOperation(null); - assertFalse(oper.isActorFailed(outcome)); - outcome.setOperation(OPERATOR); - - // correct values - assertTrue(oper.isActorFailed(outcome)); - } - - @Test - public void testDoOperation() { - /* - * Use an operator that doesn't override doOperation(). - */ - OperatorPartial oper2 = new OperatorPartial(ACTOR, OPERATOR) { - @Override - protected Executor getBlockingExecutor() { - return executor; - } - }; - - oper2.configure(new TreeMap<>()); - oper2.start(); - - oper2.startOperation(params); - assertTrue(executor.runAll()); - - assertNotNull(opend); - assertEquals(PolicyResult.FAILURE_EXCEPTION, opend.getResult()); - } - - @Test - public void testTimeout() throws Exception { - - // use a real executor - params = params.toBuilder().executor(ForkJoinPool.commonPool()).build(); - - // trigger timeout very quickly - oper = new MyOper() { - @Override - protected long getTimeOutMillis(Integer timeoutSec) { - return 1; - } - - @Override - protected CompletableFuture<OperationOutcome> startOperationAsync(ControlLoopOperationParams params, - int attempt, OperationOutcome outcome) { - - OperationOutcome outcome2 = params.makeOutcome(); - outcome2.setResult(PolicyResult.SUCCESS); - - /* - * Create an incomplete future that will timeout after the operation's - * timeout. If it fires before the other timer, then it will return a - * SUCCESS outcome. - */ - CompletableFuture<OperationOutcome> future = new CompletableFuture<>(); - future = future.orTimeout(1, TimeUnit.SECONDS).handleAsync((unused1, unused2) -> outcome, - params.getExecutor()); - - return future; - } - }; - - oper.configure(new TreeMap<>()); - oper.start(); - - assertEquals(PolicyResult.FAILURE_TIMEOUT, oper.startOperation(params).get().getResult()); - } - - /** - * Verifies that the timer doesn't encompass the preprocessor and doesn't stop the - * operation once the preprocessor completes. - */ - @Test - public void testTimeoutInPreprocessor() throws Exception { - - // use a real executor - params = params.toBuilder().executor(ForkJoinPool.commonPool()).build(); - - // trigger timeout very quickly - oper = new MyOper() { + operator = new OperatorPartial(ACTOR, OPERATION) { @Override - protected long getTimeOutMillis(Integer timeoutSec) { - return 10; - } - - @Override - protected Executor getBlockingExecutor() { - return command -> { - Thread thread = new Thread(command); - thread.start(); - }; - } - - @Override - protected CompletableFuture<OperationOutcome> startPreprocessorAsync(ControlLoopOperationParams params) { - - OperationOutcome outcome = makeSuccess(); - - /* - * Create an incomplete future that will timeout after the operation's - * timeout. If it fires before the other timer, then it will return a - * SUCCESS outcome. - */ - CompletableFuture<OperationOutcome> future = new CompletableFuture<>(); - future = future.orTimeout(200, TimeUnit.MILLISECONDS).handleAsync((unused1, unused2) -> outcome, - params.getExecutor()); - - return future; - } - }; - - oper.configure(new TreeMap<>()); - oper.start(); - - OperationOutcome result = oper.startOperation(params).get(); - assertEquals(PolicyResult.SUCCESS, result.getResult()); - - assertNotNull(opstart); - assertNotNull(opend); - assertEquals(PolicyResult.SUCCESS, opend.getResult()); - - assertEquals(1, numStart); - assertEquals(1, oper.getCount()); - assertEquals(1, numEnd); - } - - /** - * Tests retry functions, when the count is set to zero and retries are exhausted. - */ - @Test - public void testSetRetryFlag_testRetryOnFailure_ZeroRetries_testStartOperationAttempt() { - params = params.toBuilder().retry(0).build(); - oper.setMaxFailures(10); - - verifyRun("testSetRetryFlag_testRetryOnFailure_ZeroRetries", 1, 1, PolicyResult.FAILURE); - } - - /** - * Tests retry functions, when the count is null and retries are exhausted. - */ - @Test - public void testSetRetryFlag_testRetryOnFailure_NullRetries() { - params = params.toBuilder().retry(null).build(); - oper.setMaxFailures(10); - - verifyRun("testSetRetryFlag_testRetryOnFailure_NullRetries", 1, 1, PolicyResult.FAILURE); - } - - /** - * Tests retry functions, when retries are exhausted. - */ - @Test - public void testSetRetryFlag_testRetryOnFailure_RetriesExhausted() { - final int maxRetries = 3; - params = params.toBuilder().retry(maxRetries).build(); - oper.setMaxFailures(10); - - verifyRun("testSetRetryFlag_testRetryOnFailure_RetriesExhausted", maxRetries + 1, maxRetries + 1, - PolicyResult.FAILURE_RETRIES); - } - - /** - * Tests retry functions, when a success follows some retries. - */ - @Test - public void testSetRetryFlag_testRetryOnFailure_SuccessAfterRetries() { - params = params.toBuilder().retry(10).build(); - - final int maxFailures = 3; - oper.setMaxFailures(maxFailures); - - verifyRun("testSetRetryFlag_testRetryOnFailure_SuccessAfterRetries", maxFailures + 1, maxFailures + 1, - PolicyResult.SUCCESS); - } - - /** - * Tests retry functions, when the outcome is {@code null}. - */ - @Test - public void testSetRetryFlag_testRetryOnFailure_NullOutcome() { - - // arrange to return null from doOperation() - oper = new MyOper() { - @Override - protected OperationOutcome doOperation(ControlLoopOperationParams params, int attempt, - OperationOutcome operation) { - - // update counters - super.doOperation(params, attempt, operation); + public Operation buildOperation(ControlLoopOperationParams params) { return null; } }; - - oper.configure(new TreeMap<>()); - oper.start(); - - verifyRun("testSetRetryFlag_testRetryOnFailure_NullOutcome", 1, 1, PolicyResult.FAILURE, null, noop()); - } - - @Test - public void testIsSameOperation() { - assertFalse(oper.isSameOperation(null)); - - OperationOutcome outcome = params.makeOutcome(); - - // wrong actor - should be false - outcome.setActor(null); - assertFalse(oper.isSameOperation(outcome)); - outcome.setActor(TARGET); - assertFalse(oper.isSameOperation(outcome)); - outcome.setActor(ACTOR); - - // wrong operation - should be null - outcome.setOperation(null); - assertFalse(oper.isSameOperation(outcome)); - outcome.setOperation(TARGET); - assertFalse(oper.isSameOperation(outcome)); - outcome.setOperation(OPERATOR); - - assertTrue(oper.isSameOperation(outcome)); - } - - /** - * Tests handleFailure() when the outcome is a success. - */ - @Test - public void testHandlePreprocessorFailureTrue() { - oper.setPreProcessor(CompletableFuture.completedFuture(makeSuccess())); - verifyRun("testHandlePreprocessorFailureTrue", 1, 1, PolicyResult.SUCCESS); - } - - /** - * Tests handleFailure() when the outcome is <i>not</i> a success. - */ - @Test - public void testHandlePreprocessorFailureFalse() throws Exception { - oper.setPreProcessor(CompletableFuture.completedFuture(makeFailure())); - verifyRun("testHandlePreprocessorFailureFalse", 1, 0, PolicyResult.FAILURE_GUARD); - } - - /** - * Tests handleFailure() when the outcome is {@code null}. - */ - @Test - public void testHandlePreprocessorFailureNull() throws Exception { - // arrange to return null from the preprocessor - oper.setPreProcessor(CompletableFuture.completedFuture(null)); - - verifyRun("testHandlePreprocessorFailureNull", 1, 0, PolicyResult.FAILURE_GUARD); - } - - @Test - public void testFromException() { - // arrange to generate an exception when operation runs - oper.setGenException(true); - - verifyRun("testFromException", 1, 1, PolicyResult.FAILURE_EXCEPTION); - } - - /** - * Tests fromException() when there is no exception. - */ - @Test - public void testFromExceptionNoExcept() { - verifyRun("testFromExceptionNoExcept", 1, 1, PolicyResult.SUCCESS); - } - - /** - * Tests both flavors of anyOf(), because one invokes the other. - */ - @Test - public void testAnyOf() throws Exception { - // first task completes, others do not - List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); - - final OperationOutcome outcome = params.makeOutcome(); - - tasks.add(CompletableFuture.completedFuture(outcome)); - tasks.add(new CompletableFuture<>()); - tasks.add(new CompletableFuture<>()); - - CompletableFuture<OperationOutcome> result = oper.anyOf(params, tasks); - assertTrue(executor.runAll()); - - assertTrue(result.isDone()); - assertSame(outcome, result.get()); - - // second task completes, others do not - tasks = new LinkedList<>(); - - tasks.add(new CompletableFuture<>()); - tasks.add(CompletableFuture.completedFuture(outcome)); - tasks.add(new CompletableFuture<>()); - - result = oper.anyOf(params, tasks); - assertTrue(executor.runAll()); - - assertTrue(result.isDone()); - assertSame(outcome, result.get()); - - // third task completes, others do not - tasks = new LinkedList<>(); - - tasks.add(new CompletableFuture<>()); - tasks.add(new CompletableFuture<>()); - tasks.add(CompletableFuture.completedFuture(outcome)); - - result = oper.anyOf(params, tasks); - assertTrue(executor.runAll()); - - assertTrue(result.isDone()); - assertSame(outcome, result.get()); - } - - /** - * Tests both flavors of allOf(), because one invokes the other. - */ - @Test - public void testAllOf() throws Exception { - List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); - - final OperationOutcome outcome = params.makeOutcome(); - - CompletableFuture<OperationOutcome> future1 = new CompletableFuture<>(); - CompletableFuture<OperationOutcome> future2 = new CompletableFuture<>(); - CompletableFuture<OperationOutcome> future3 = new CompletableFuture<>(); - - tasks.add(future1); - tasks.add(future2); - tasks.add(future3); - - CompletableFuture<OperationOutcome> result = oper.allOf(params, tasks); - - assertTrue(executor.runAll()); - assertFalse(result.isDone()); - future1.complete(outcome); - - // complete 3 before 2 - assertTrue(executor.runAll()); - assertFalse(result.isDone()); - future3.complete(outcome); - - assertTrue(executor.runAll()); - assertFalse(result.isDone()); - future2.complete(outcome); - - // all of them are now done - assertTrue(executor.runAll()); - assertTrue(result.isDone()); - assertSame(outcome, result.get()); - } - - @Test - public void testCombineOutcomes() throws Exception { - // only one outcome - verifyOutcomes(0, PolicyResult.SUCCESS); - verifyOutcomes(0, PolicyResult.FAILURE_EXCEPTION); - - // maximum is in different positions - verifyOutcomes(0, PolicyResult.FAILURE, PolicyResult.SUCCESS, PolicyResult.FAILURE_GUARD); - verifyOutcomes(1, PolicyResult.SUCCESS, PolicyResult.FAILURE, PolicyResult.FAILURE_GUARD); - verifyOutcomes(2, PolicyResult.SUCCESS, PolicyResult.FAILURE_GUARD, PolicyResult.FAILURE); - - // null outcome - final List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); - tasks.add(CompletableFuture.completedFuture(null)); - CompletableFuture<OperationOutcome> result = oper.allOf(params, tasks); - - assertTrue(executor.runAll()); - assertTrue(result.isDone()); - assertNull(result.get()); - - // one throws an exception during execution - IllegalStateException except = new IllegalStateException(EXPECTED_EXCEPTION); - - tasks.clear(); - tasks.add(CompletableFuture.completedFuture(params.makeOutcome())); - tasks.add(CompletableFuture.failedFuture(except)); - tasks.add(CompletableFuture.completedFuture(params.makeOutcome())); - result = oper.allOf(params, tasks); - - assertTrue(executor.runAll()); - assertTrue(result.isCompletedExceptionally()); - result.whenComplete((unused, thrown) -> assertSame(except, thrown)); - } - - private void verifyOutcomes(int expected, PolicyResult... results) throws Exception { - List<CompletableFuture<OperationOutcome>> tasks = new LinkedList<>(); - - - OperationOutcome expectedOutcome = null; - - for (int count = 0; count < results.length; ++count) { - OperationOutcome outcome = params.makeOutcome(); - outcome.setResult(results[count]); - tasks.add(CompletableFuture.completedFuture(outcome)); - - if (count == expected) { - expectedOutcome = outcome; - } - } - - CompletableFuture<OperationOutcome> result = oper.allOf(params, tasks); - - assertTrue(executor.runAll()); - assertTrue(result.isDone()); - assertSame(expectedOutcome, result.get()); - } - - private Function<OperationOutcome, CompletableFuture<OperationOutcome>> makeTask( - final OperationOutcome taskOutcome) { - - return outcome -> CompletableFuture.completedFuture(taskOutcome); - } - - @Test - public void testDetmPriority() { - assertEquals(1, oper.detmPriority(null)); - - OperationOutcome outcome = params.makeOutcome(); - - Map<PolicyResult, Integer> map = Map.of(PolicyResult.SUCCESS, 0, PolicyResult.FAILURE_GUARD, 2, - PolicyResult.FAILURE_RETRIES, 3, PolicyResult.FAILURE, 4, PolicyResult.FAILURE_TIMEOUT, 5, - PolicyResult.FAILURE_EXCEPTION, 6); - - for (Entry<PolicyResult, Integer> ent : map.entrySet()) { - outcome.setResult(ent.getKey()); - assertEquals(ent.getKey().toString(), ent.getValue().intValue(), oper.detmPriority(outcome)); - } - } - - /** - * Tests doTask(Future) when the controller is not running. - */ - @Test - public void testDoTaskFutureNotRunning() throws Exception { - CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>(); - - PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); - controller.complete(params.makeOutcome()); - - CompletableFuture<OperationOutcome> future = - oper.doTask(params, controller, false, params.makeOutcome(), taskFuture); - assertFalse(future.isDone()); - assertTrue(executor.runAll()); - - // should not have run the task - assertFalse(future.isDone()); - - // should have canceled the task future - assertTrue(taskFuture.isCancelled()); - } - - /** - * Tests doTask(Future) when the previous outcome was successful. - */ - @Test - public void testDoTaskFutureSuccess() throws Exception { - CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>(); - final OperationOutcome taskOutcome = params.makeOutcome(); - - PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); - - CompletableFuture<OperationOutcome> future = - oper.doTask(params, controller, true, params.makeOutcome(), taskFuture); - - taskFuture.complete(taskOutcome); - assertTrue(executor.runAll()); - - assertTrue(future.isDone()); - assertSame(taskOutcome, future.get()); - - // controller should not be done yet - assertFalse(controller.isDone()); - } - - /** - * Tests doTask(Future) when the previous outcome was failed. - */ - @Test - public void testDoTaskFutureFailure() throws Exception { - CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>(); - final OperationOutcome failedOutcome = params.makeOutcome(); - failedOutcome.setResult(PolicyResult.FAILURE); - - PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); - - CompletableFuture<OperationOutcome> future = oper.doTask(params, controller, true, failedOutcome, taskFuture); - assertFalse(future.isDone()); - assertTrue(executor.runAll()); - - // should not have run the task - assertFalse(future.isDone()); - - // should have canceled the task future - assertTrue(taskFuture.isCancelled()); - - // controller SHOULD be done now - assertTrue(controller.isDone()); - assertSame(failedOutcome, controller.get()); - } - - /** - * Tests doTask(Future) when the previous outcome was failed, but not checking - * success. - */ - @Test - public void testDoTaskFutureUncheckedFailure() throws Exception { - CompletableFuture<OperationOutcome> taskFuture = new CompletableFuture<>(); - final OperationOutcome failedOutcome = params.makeOutcome(); - failedOutcome.setResult(PolicyResult.FAILURE); - - PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); - - CompletableFuture<OperationOutcome> future = oper.doTask(params, controller, false, failedOutcome, taskFuture); - assertFalse(future.isDone()); - - // complete the task - OperationOutcome taskOutcome = params.makeOutcome(); - taskFuture.complete(taskOutcome); - - assertTrue(executor.runAll()); - - // should have run the task - assertTrue(future.isDone()); - - assertTrue(future.isDone()); - assertSame(taskOutcome, future.get()); - - // controller should not be done yet - assertFalse(controller.isDone()); - } - - /** - * Tests doTask(Function) when the controller is not running. - */ - @Test - public void testDoTaskFunctionNotRunning() throws Exception { - AtomicBoolean invoked = new AtomicBoolean(); - - Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = outcome -> { - invoked.set(true); - return CompletableFuture.completedFuture(params.makeOutcome()); - }; - - PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); - controller.complete(params.makeOutcome()); - - CompletableFuture<OperationOutcome> future = - oper.doTask(params, controller, false, task).apply(params.makeOutcome()); - assertFalse(future.isDone()); - assertTrue(executor.runAll()); - - // should not have run the task - assertFalse(future.isDone()); - - // should not have even invoked the task - assertFalse(invoked.get()); - } - - /** - * Tests doTask(Function) when the previous outcome was successful. - */ - @Test - public void testDoTaskFunctionSuccess() throws Exception { - final OperationOutcome taskOutcome = params.makeOutcome(); - - final OperationOutcome failedOutcome = params.makeOutcome(); - - Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = makeTask(taskOutcome); - - PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); - - CompletableFuture<OperationOutcome> future = oper.doTask(params, controller, true, task).apply(failedOutcome); - - assertTrue(future.isDone()); - assertSame(taskOutcome, future.get()); - - // controller should not be done yet - assertFalse(controller.isDone()); - } - - /** - * Tests doTask(Function) when the previous outcome was failed. - */ - @Test - public void testDoTaskFunctionFailure() throws Exception { - final OperationOutcome failedOutcome = params.makeOutcome(); - failedOutcome.setResult(PolicyResult.FAILURE); - - AtomicBoolean invoked = new AtomicBoolean(); - - Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = outcome -> { - invoked.set(true); - return CompletableFuture.completedFuture(params.makeOutcome()); - }; - - PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); - - CompletableFuture<OperationOutcome> future = oper.doTask(params, controller, true, task).apply(failedOutcome); - assertFalse(future.isDone()); - assertTrue(executor.runAll()); - - // should not have run the task - assertFalse(future.isDone()); - - // should not have even invoked the task - assertFalse(invoked.get()); - - // controller should have the failed task - assertTrue(controller.isDone()); - assertSame(failedOutcome, controller.get()); - } - - /** - * Tests doTask(Function) when the previous outcome was failed, but not checking - * success. - */ - @Test - public void testDoTaskFunctionUncheckedFailure() throws Exception { - final OperationOutcome taskOutcome = params.makeOutcome(); - - final OperationOutcome failedOutcome = params.makeOutcome(); - failedOutcome.setResult(PolicyResult.FAILURE); - - Function<OperationOutcome, CompletableFuture<OperationOutcome>> task = makeTask(taskOutcome); - - PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); - - CompletableFuture<OperationOutcome> future = oper.doTask(params, controller, false, task).apply(failedOutcome); - - assertTrue(future.isDone()); - assertSame(taskOutcome, future.get()); - - // controller should not be done yet - assertFalse(controller.isDone()); } - /** - * Tests callbackStarted() when the pipeline has already been stopped. - */ @Test - public void testCallbackStartedNotRunning() { - AtomicReference<Future<OperationOutcome>> future = new AtomicReference<>(); - - /* - * arrange to stop the controller when the start-callback is invoked, but capture - * the outcome - */ - params = params.toBuilder().startCallback(oper -> { - starter(oper); - future.get().cancel(false); - }).build(); - - future.set(oper.startOperation(params)); - assertTrue(executor.runAll()); - - // should have only run once - assertEquals(1, numStart); + public void testOperatorPartial_testGetActorName_testGetName() { + assertEquals(ACTOR, operator.getActorName()); + assertEquals(OPERATION, operator.getName()); + assertEquals(ACTOR + "." + OPERATION, operator.getFullName()); } - /** - * Tests callbackCompleted() when the pipeline has already been stopped. - */ @Test - public void testCallbackCompletedNotRunning() { - AtomicReference<Future<OperationOutcome>> future = new AtomicReference<>(); - - // arrange to stop the controller when the start-callback is invoked - params = params.toBuilder().startCallback(oper -> { - future.get().cancel(false); - }).build(); + public void testDoStart() { + operator.configure(null); - future.set(oper.startOperation(params)); - assertTrue(executor.runAll()); + operator = spy(operator); + operator.start(); - // should not have been set - assertNull(opend); - assertEquals(0, numEnd); + verify(operator).doStart(); } @Test - public void testSetOutcomeControlLoopOperationOutcomeThrowable() { - final CompletionException timex = new CompletionException(new TimeoutException(EXPECTED_EXCEPTION)); - - OperationOutcome outcome; + public void testDoStop() { + operator.configure(null); + operator.start(); - outcome = new OperationOutcome(); - oper.setOutcome(params, outcome, timex); - assertEquals(ControlLoopOperation.FAILED_MSG, outcome.getMessage()); - assertEquals(PolicyResult.FAILURE_TIMEOUT, outcome.getResult()); + operator = spy(operator); + operator.stop(); - outcome = new OperationOutcome(); - oper.setOutcome(params, outcome, new IllegalStateException(EXPECTED_EXCEPTION)); - assertEquals(ControlLoopOperation.FAILED_MSG, outcome.getMessage()); - assertEquals(PolicyResult.FAILURE_EXCEPTION, outcome.getResult()); + verify(operator).doStop(); } @Test - public void testSetOutcomeControlLoopOperationOutcomePolicyResult() { - OperationOutcome outcome; + public void testDoShutdown() { + operator.configure(null); + operator.start(); - outcome = new OperationOutcome(); - oper.setOutcome(params, outcome, PolicyResult.SUCCESS); - assertEquals(ControlLoopOperation.SUCCESS_MSG, outcome.getMessage()); - assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + operator = spy(operator); + operator.shutdown(); - for (PolicyResult result : FAILURE_RESULTS) { - outcome = new OperationOutcome(); - oper.setOutcome(params, outcome, result); - assertEquals(result.toString(), ControlLoopOperation.FAILED_MSG, outcome.getMessage()); - assertEquals(result.toString(), result, outcome.getResult()); - } + verify(operator).doShutdown(); } @Test - public void testIsTimeout() { - final TimeoutException timex = new TimeoutException(EXPECTED_EXCEPTION); + public void testDoConfigureMapOfStringObject() { + operator = spy(operator); - assertFalse(oper.isTimeout(new IllegalStateException(EXPECTED_EXCEPTION))); - assertFalse(oper.isTimeout(new IllegalStateException(timex))); - assertFalse(oper.isTimeout(new CompletionException(new IllegalStateException(timex)))); - assertFalse(oper.isTimeout(new CompletionException(null))); - assertFalse(oper.isTimeout(new CompletionException(new CompletionException(timex)))); + Map<String, Object> params = new TreeMap<>(); + operator.configure(params); - assertTrue(oper.isTimeout(timex)); - assertTrue(oper.isTimeout(new CompletionException(timex))); + verify(operator).doConfigure(params); } @Test - public void testGetTimeOutMillis() { - assertEquals(TIMEOUT * 1000, oper.getTimeOutMillis(params.getTimeoutSec())); - - params = params.toBuilder().timeoutSec(null).build(); - assertEquals(0, oper.getTimeOutMillis(params.getTimeoutSec())); - } - - private void starter(OperationOutcome oper) { - ++numStart; - tstart = oper.getStart(); - opstart = oper; - } - - private void completer(OperationOutcome oper) { - ++numEnd; - opend = oper; - } - - /** - * Gets a function that does nothing. - * - * @param <T> type of input parameter expected by the function - * @return a function that does nothing - */ - private <T> Consumer<T> noop() { - return unused -> { - }; - } - - private OperationOutcome makeSuccess() { - OperationOutcome outcome = params.makeOutcome(); - outcome.setResult(PolicyResult.SUCCESS); - - return outcome; - } - - private OperationOutcome makeFailure() { - OperationOutcome outcome = params.makeOutcome(); - outcome.setResult(PolicyResult.FAILURE); - - return outcome; - } - - /** - * Verifies a run. - * - * @param testName test name - * @param expectedCallbacks number of callbacks expected - * @param expectedOperations number of operation invocations expected - * @param expectedResult expected outcome - */ - private void verifyRun(String testName, int expectedCallbacks, int expectedOperations, - PolicyResult expectedResult) { - - String expectedSubRequestId = - (expectedResult == PolicyResult.FAILURE_EXCEPTION ? null : String.valueOf(expectedOperations)); - - verifyRun(testName, expectedCallbacks, expectedOperations, expectedResult, expectedSubRequestId, noop()); - } - - /** - * Verifies a run. - * - * @param testName test name - * @param expectedCallbacks number of callbacks expected - * @param expectedOperations number of operation invocations expected - * @param expectedResult expected outcome - * @param expectedSubRequestId expected sub request ID - * @param manipulator function to modify the future returned by - * {@link OperatorPartial#startOperation(ControlLoopOperationParams)} before - * the tasks in the executor are run - */ - private void verifyRun(String testName, int expectedCallbacks, int expectedOperations, PolicyResult expectedResult, - String expectedSubRequestId, Consumer<CompletableFuture<OperationOutcome>> manipulator) { - - CompletableFuture<OperationOutcome> future = oper.startOperation(params); - - manipulator.accept(future); - - assertTrue(testName, executor.runAll()); - - assertEquals(testName, expectedCallbacks, numStart); - assertEquals(testName, expectedCallbacks, numEnd); - - if (expectedCallbacks > 0) { - assertNotNull(testName, opstart); - assertNotNull(testName, opend); - assertEquals(testName, expectedResult, opend.getResult()); - - assertSame(testName, tstart, opstart.getStart()); - assertSame(testName, tstart, opend.getStart()); - - try { - assertTrue(future.isDone()); - assertSame(testName, opend, future.get()); - - } catch (InterruptedException | ExecutionException e) { - throw new IllegalStateException(e); - } - - if (expectedOperations > 0) { - assertEquals(testName, expectedSubRequestId, opend.getSubRequestId()); - } - } - - assertEquals(testName, expectedOperations, oper.getCount()); - } - - private class MyOper extends OperatorPartial { - @Getter - private int count = 0; - - @Setter - private boolean genException; - - @Setter - private int maxFailures = 0; - - @Setter - private CompletableFuture<OperationOutcome> preProcessor; - - public MyOper() { - super(ACTOR, OPERATOR); - } - - @Override - protected OperationOutcome doOperation(ControlLoopOperationParams params, int attempt, - OperationOutcome operation) { - ++count; - if (genException) { - throw new IllegalStateException(EXPECTED_EXCEPTION); - } - - operation.setSubRequestId(String.valueOf(attempt)); - - if (count > maxFailures) { - operation.setResult(PolicyResult.SUCCESS); - } else { - operation.setResult(PolicyResult.FAILURE); - } - - return operation; - } - - @Override - protected CompletableFuture<OperationOutcome> startPreprocessorAsync(ControlLoopOperationParams params) { - return (preProcessor != null ? preProcessor : super.startPreprocessorAsync(params)); - } - - @Override - protected Executor getBlockingExecutor() { - return executor; - } - } - - /** - * Executor that will run tasks until the queue is empty or a maximum number of tasks - * have been executed. - */ - private static class MyExec implements Executor { - private static final int MAX_TASKS = MAX_PARALLEL_REQUESTS * 100; - - private Queue<Runnable> commands = new LinkedList<>(); - - public MyExec() { - // do nothing - } - - public int getQueueLength() { - return commands.size(); - } - - @Override - public void execute(Runnable command) { - commands.add(command); - } - - public boolean runAll() { - for (int count = 0; count < MAX_TASKS && !commands.isEmpty(); ++count) { - commands.remove().run(); - } - - return commands.isEmpty(); - } + public void testGetBlockingExecutor() { + assertNotNull(operator.getBlockingExecutor()); } } diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/ControlLoopOperationParamsTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/ControlLoopOperationParamsTest.java index 9dd19d548..a5215a48f 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/ControlLoopOperationParamsTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/ControlLoopOperationParamsTest.java @@ -51,6 +51,7 @@ import org.mockito.MockitoAnnotations; import org.onap.policy.common.parameters.BeanValidationResult; import org.onap.policy.controlloop.VirtualControlLoopEvent; import org.onap.policy.controlloop.actorserviceprovider.ActorService; +import org.onap.policy.controlloop.actorserviceprovider.Operation; import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; import org.onap.policy.controlloop.actorserviceprovider.Operator; import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext; @@ -87,12 +88,15 @@ public class ControlLoopOperationParamsTest { private Executor executor; @Mock - private CompletableFuture<OperationOutcome> operation; + private CompletableFuture<OperationOutcome> operFuture; @Mock private Operator operator; @Mock + private Operation operation; + + @Mock private Consumer<OperationOutcome> starter; private Map<String, String> payload; @@ -110,7 +114,8 @@ public class ControlLoopOperationParamsTest { when(actorService.getActor(ACTOR)).thenReturn(actor); when(actor.getOperator(OPERATION)).thenReturn(operator); - when(operator.startOperation(any())).thenReturn(operation); + when(operator.buildOperation(any())).thenReturn(operation); + when(operation.start()).thenReturn(operFuture); when(event.getRequestId()).thenReturn(REQ_ID); @@ -128,7 +133,7 @@ public class ControlLoopOperationParamsTest { @Test public void testStart() { - assertSame(operation, params.start()); + assertSame(operFuture, params.start()); assertThatIllegalArgumentException().isThrownBy(() -> params.toBuilder().context(null).build().start()); } diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/HttpActorParamsTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/HttpActorParamsTest.java index 6c1f538ec..daa0affec 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/HttpActorParamsTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/HttpActorParamsTest.java @@ -41,7 +41,7 @@ public class HttpActorParamsTest { private static final String CONTAINER = "my-container"; private static final String CLIENT = "my-client"; - private static final long TIMEOUT = 10; + private static final int TIMEOUT = 10; private static final String PATH1 = "path #1"; private static final String PATH2 = "path #2"; diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/HttpParamsTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/HttpParamsTest.java index 6cf7328ca..ae4a79fe2 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/HttpParamsTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/HttpParamsTest.java @@ -36,7 +36,7 @@ public class HttpParamsTest { private static final String CONTAINER = "my-container"; private static final String CLIENT = "my-client"; private static final String PATH = "my-path"; - private static final long TIMEOUT = 10; + private static final int TIMEOUT = 10; private HttpParams params; diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/pipeline/PipelineControllerFutureTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/pipeline/PipelineControllerFutureTest.java index a6b11ef65..4a00c065e 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/pipeline/PipelineControllerFutureTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/pipeline/PipelineControllerFutureTest.java @@ -424,7 +424,7 @@ public class PipelineControllerFutureTest { } /** - * Tests add(Function) when the controller is canceled after the future is added. + * Tests wrap(Function) when the controller is canceled after the future is added. */ @Test public void testWrapFunctionCancel() throws Exception { @@ -442,7 +442,7 @@ public class PipelineControllerFutureTest { } /** - * Tests add(Function) when the controller is not running. + * Tests wrap(Function) when the controller is not running. */ @Test public void testWrapFunctionNotRunning() { diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/resources/logback-test.xml b/models-interactions/model-actors/actorServiceProvider/src/test/resources/logback-test.xml index c7fe46e47..860468821 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/resources/logback-test.xml +++ b/models-interactions/model-actors/actorServiceProvider/src/test/resources/logback-test.xml @@ -39,4 +39,9 @@ <logger name="org.onap.policy.controlloop.actorserviceprovider.Util" level="info" additivity="false"> <appender-ref ref="STDOUT" /> </logger> + + <!-- this is required for HttpOperationTest --> + <logger name="org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation" level="info" additivity="false"> + <appender-ref ref="STDOUT" /> + </logger> </configuration> |