2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.controlloop.actorserviceprovider.impl;
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.assertj.core.api.Assertions.assertThatCode;
25 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
26 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertSame;
31 import static org.junit.Assert.assertTrue;
32 import static org.mockito.ArgumentMatchers.any;
33 import static org.mockito.ArgumentMatchers.eq;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
38 import ch.qos.logback.classic.Logger;
39 import java.util.Arrays;
40 import java.util.List;
41 import java.util.concurrent.CompletableFuture;
42 import java.util.concurrent.TimeUnit;
45 import org.junit.AfterClass;
46 import org.junit.Before;
47 import org.junit.BeforeClass;
48 import org.junit.Test;
49 import org.mockito.ArgumentCaptor;
50 import org.mockito.Captor;
51 import org.mockito.Mock;
52 import org.mockito.MockitoAnnotations;
53 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
54 import org.onap.policy.common.endpoints.utils.PropertyUtils.TriConsumer;
55 import org.onap.policy.common.utils.coder.Coder;
56 import org.onap.policy.common.utils.coder.CoderException;
57 import org.onap.policy.common.utils.coder.StandardCoder;
58 import org.onap.policy.common.utils.coder.StandardCoderObject;
59 import org.onap.policy.common.utils.test.log.logback.ExtractAppender;
60 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
61 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
62 import org.onap.policy.controlloop.actorserviceprovider.parameters.TopicPairParams;
63 import org.onap.policy.controlloop.actorserviceprovider.topic.Forwarder;
64 import org.onap.policy.controlloop.actorserviceprovider.topic.TopicPair;
65 import org.onap.policy.controlloop.policy.PolicyResult;
66 import org.slf4j.LoggerFactory;
68 public class TopicPairOperationTest {
69 private static final List<CommInfrastructure> INFRA_LIST =
70 Arrays.asList(CommInfrastructure.NOOP, CommInfrastructure.UEB);
71 private static final IllegalStateException EXPECTED_EXCEPTION = new IllegalStateException("expected exception");
72 private static final String ACTOR = "my-actor";
73 private static final String OPERATION = "my-operation";
74 private static final String REQ_ID = "my-request-id";
75 private static final String MY_SOURCE = "my-source";
76 private static final String MY_TARGET = "my-target";
77 private static final String TEXT = "some text";
78 private static final int TIMEOUT_SEC = 10;
79 private static final long TIMEOUT_MS = 1000 * TIMEOUT_SEC;
81 private static final StandardCoder coder = new StandardCoder();
84 * Used to attach an appender to the class' logger.
86 private static final Logger logger = (Logger) LoggerFactory.getLogger(TopicPairOperation.class);
87 private static final ExtractAppender appender = new ExtractAppender();
90 private TopicPairOperator operator;
92 private TopicPair pair;
94 private Forwarder forwarder;
97 private ArgumentCaptor<TriConsumer<CommInfrastructure, String, StandardCoderObject>> listenerCaptor;
99 private ControlLoopOperationParams params;
100 private TopicPairParams topicParams;
101 private OperationOutcome outcome;
102 private StandardCoderObject stdResponse;
103 private String responseText;
104 private MyExec executor;
105 private TopicPairOperation<MyRequest, MyResponse> oper;
108 * Attaches the appender to the logger.
111 public static void setUpBeforeClass() throws Exception {
113 * Attach appender to the logger.
115 appender.setContext(logger.getLoggerContext());
118 logger.addAppender(appender);
122 * Stops the appender.
125 public static void tearDownAfterClass() {
133 public void setUp() throws CoderException {
134 MockitoAnnotations.initMocks(this);
136 appender.clearExtractions();
138 topicParams = TopicPairParams.builder().source(MY_SOURCE).target(MY_TARGET).timeoutSec(TIMEOUT_SEC).build();
140 when(operator.getActorName()).thenReturn(ACTOR);
141 when(operator.getName()).thenReturn(OPERATION);
142 when(operator.getTopicPair()).thenReturn(pair);
143 when(operator.getForwarder()).thenReturn(forwarder);
144 when(operator.getParams()).thenReturn(topicParams);
145 when(operator.isAlive()).thenReturn(true);
147 when(pair.publish(any())).thenReturn(INFRA_LIST);
149 executor = new MyExec(100);
151 params = ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).executor(executor).build();
152 outcome = params.makeOutcome();
154 responseText = coder.encode(new MyResponse());
155 stdResponse = coder.decode(responseText, StandardCoderObject.class);
157 oper = new MyOperation();
161 public void testTopicPairOperation_testGetTopicPair_testGetForwarder_testGetPairParams() {
162 assertEquals(ACTOR, oper.getActorName());
163 assertEquals(OPERATION, oper.getName());
164 assertSame(pair, oper.getTopicPair());
165 assertSame(forwarder, oper.getForwarder());
166 assertSame(topicParams, oper.getPairParams());
167 assertEquals(TIMEOUT_MS, oper.getTimeoutMs());
168 assertSame(MyResponse.class, oper.getResponseClass());
172 public void testStartOperationAsync() throws Exception {
173 CompletableFuture<OperationOutcome> future = oper.startOperationAsync(1, outcome);
174 assertFalse(future.isDone());
176 verify(forwarder).register(eq(Arrays.asList(REQ_ID)), listenerCaptor.capture());
178 verify(forwarder, never()).unregister(any(), any());
180 verify(pair).publish(any());
182 // provide the response
183 listenerCaptor.getValue().accept(CommInfrastructure.NOOP, responseText, stdResponse);
186 assertTrue(executor.runAll());
188 assertTrue(future.isDone());
190 assertSame(outcome, future.get(5, TimeUnit.SECONDS));
191 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
193 verify(forwarder).unregister(eq(Arrays.asList(REQ_ID)), eq(listenerCaptor.getValue()));
197 * Tests startOperationAsync() when the publisher throws an exception.
200 public void testStartOperationAsyncException() throws Exception {
201 // indicate that nothing was published
202 when(pair.publish(any())).thenReturn(Arrays.asList());
204 assertThatIllegalStateException().isThrownBy(() -> oper.startOperationAsync(1, outcome));
206 verify(forwarder).register(eq(Arrays.asList(REQ_ID)), listenerCaptor.capture());
208 // must still unregister
209 verify(forwarder).unregister(eq(Arrays.asList(REQ_ID)), eq(listenerCaptor.getValue()));
213 public void testGetTimeoutMsInteger() {
215 assertEquals(TIMEOUT_MS, oper.getTimeoutMs(null));
216 assertEquals(TIMEOUT_MS, oper.getTimeoutMs(0));
218 // use provided value
219 assertEquals(5000, oper.getTimeoutMs(5));
223 public void testPublishRequest() {
224 oper.publishRequest(new MyRequest());
225 assertEquals(INFRA_LIST.size(), appender.getExtracted().size());
229 * Tests publishRequest() when nothing is published.
232 public void testPublishRequestUnpublished() {
233 when(pair.publish(any())).thenReturn(Arrays.asList());
234 assertThatIllegalStateException().isThrownBy(() -> oper.publishRequest(new MyRequest()));
238 * Tests publishRequest() when the request type is a String.
241 public void testPublishRequestString() {
242 MyStringOperation oper2 = new MyStringOperation();
243 oper2.publishRequest(TEXT);
244 assertEquals(INFRA_LIST.size(), appender.getExtracted().size());
248 * Tests publishRequest() when the coder throws an exception.
251 public void testPublishRequestException() {
252 setOperCoderException();
253 assertThatIllegalArgumentException().isThrownBy(() -> oper.publishRequest(new MyRequest()));
257 * Tests processResponse() when it's a success and the response type is a String.
260 public void testProcessResponseSuccessString() {
261 MyStringOperation oper2 = new MyStringOperation();
263 assertSame(outcome, oper2.processResponse(CommInfrastructure.NOOP, outcome, TEXT, null));
264 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
268 * Tests processResponse() when it's a success and the response type is a
269 * StandardCoderObject.
272 public void testProcessResponseSuccessSco() {
273 MyScoOperation oper2 = new MyScoOperation();
275 assertSame(outcome, oper2.processResponse(CommInfrastructure.NOOP, outcome, responseText, stdResponse));
276 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
280 * Tests processResponse() when it's a failure.
283 public void testProcessResponseFailure() throws CoderException {
284 // indicate error in the response
285 MyResponse resp = new MyResponse();
286 resp.setOutput("error");
288 responseText = coder.encode(resp);
289 stdResponse = coder.decode(responseText, StandardCoderObject.class);
291 assertSame(outcome, oper.processResponse(CommInfrastructure.NOOP, outcome, responseText, stdResponse));
292 assertEquals(PolicyResult.FAILURE, outcome.getResult());
296 * Tests processResponse() when the decoder succeeds.
299 public void testProcessResponseDecodeOk() throws CoderException {
300 assertSame(outcome, oper.processResponse(CommInfrastructure.NOOP, outcome, responseText, stdResponse));
301 assertEquals(PolicyResult.SUCCESS, outcome.getResult());
305 * Tests processResponse() when the decoder throws an exception.
308 public void testProcessResponseDecodeExcept() throws CoderException {
310 assertThatIllegalArgumentException().isThrownBy(
311 () -> oper.processResponse(CommInfrastructure.NOOP, outcome, "{invalid json", stdResponse));
316 public void testPostProcessResponse() {
317 assertThatCode(() -> oper.postProcessResponse(outcome, null, null)).doesNotThrowAnyException();
321 public void testLogTopicRequest() {
323 appender.clearExtractions();
324 oper.logTopicRequest(Arrays.asList(), new MyRequest());
325 assertEquals(0, appender.getExtracted().size());
327 // log structured data
328 appender.clearExtractions();
329 oper.logTopicRequest(INFRA_LIST, new MyRequest());
330 List<String> output = appender.getExtracted();
331 assertEquals(2, output.size());
333 assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString())
334 .contains("{\n \"theRequestId\": \"my-request-id\"\n}");
336 assertThat(output.get(1)).contains(CommInfrastructure.UEB.toString())
337 .contains("{\n \"theRequestId\": \"my-request-id\"\n}");
339 // log a plain string
340 appender.clearExtractions();
341 new MyStringOperation().logTopicRequest(Arrays.asList(CommInfrastructure.NOOP), TEXT);
342 output = appender.getExtracted();
343 assertEquals(1, output.size());
344 assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString()).contains(TEXT);
346 // log a null request
347 appender.clearExtractions();
348 oper.logTopicRequest(Arrays.asList(CommInfrastructure.NOOP), null);
349 output = appender.getExtracted();
350 assertEquals(1, output.size());
352 assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString()).contains("null");
354 // exception from coder
355 setOperCoderException();
357 appender.clearExtractions();
358 oper.logTopicRequest(Arrays.asList(CommInfrastructure.NOOP), new MyRequest());
359 output = appender.getExtracted();
360 assertEquals(2, output.size());
361 assertThat(output.get(0)).contains("cannot pretty-print request");
362 assertThat(output.get(1)).contains(CommInfrastructure.NOOP.toString());
366 public void testLogTopicResponse() {
367 // log structured data
368 appender.clearExtractions();
369 oper.logTopicResponse(CommInfrastructure.NOOP, new MyResponse());
370 List<String> output = appender.getExtracted();
371 assertEquals(1, output.size());
373 assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString())
374 .contains("{\n \"requestId\": \"my-request-id\"\n}");
376 // log a plain string
377 appender.clearExtractions();
378 new MyStringOperation().logTopicResponse(CommInfrastructure.NOOP, TEXT);
379 output = appender.getExtracted();
380 assertEquals(1, output.size());
381 assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString()).contains(TEXT);
383 // log a null response
384 appender.clearExtractions();
385 oper.logTopicResponse(CommInfrastructure.NOOP, null);
386 output = appender.getExtracted();
387 assertEquals(1, output.size());
389 assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString()).contains("null");
391 // exception from coder
392 setOperCoderException();
394 appender.clearExtractions();
395 oper.logTopicResponse(CommInfrastructure.NOOP, new MyResponse());
396 output = appender.getExtracted();
397 assertEquals(2, output.size());
398 assertThat(output.get(0)).contains("cannot pretty-print response");
399 assertThat(output.get(1)).contains(CommInfrastructure.NOOP.toString());
403 public void testMakeCoder() {
404 assertNotNull(oper.makeCoder());
408 * Creates a new {@link #oper} whose coder will throw an exception.
410 private void setOperCoderException() {
411 oper = new MyOperation() {
413 protected Coder makeCoder() {
414 return new StandardCoder() {
416 public String encode(Object object, boolean pretty) throws CoderException {
417 throw new CoderException(EXPECTED_EXCEPTION);
426 public static class MyRequest {
427 private String theRequestId = REQ_ID;
428 private String input;
433 public static class MyResponse {
434 private String requestId = REQ_ID;
435 private String output;
439 private class MyStringOperation extends TopicPairOperation<String, String> {
440 public MyStringOperation() {
441 super(TopicPairOperationTest.this.params, operator, String.class);
445 protected String makeRequest(int attempt) {
450 protected List<String> getExpectedKeyValues(int attempt, String request) {
451 return Arrays.asList(REQ_ID);
455 protected boolean isSuccess(String rawResponse, String response) {
456 return (response != null);
461 private class MyScoOperation extends TopicPairOperation<MyRequest, StandardCoderObject> {
462 public MyScoOperation() {
463 super(TopicPairOperationTest.this.params, operator, StandardCoderObject.class);
467 protected MyRequest makeRequest(int attempt) {
468 return new MyRequest();
472 protected List<String> getExpectedKeyValues(int attempt, MyRequest request) {
473 return Arrays.asList(REQ_ID);
477 protected boolean isSuccess(String rawResponse, StandardCoderObject response) {
478 return (response.getString("output") == null);
483 private class MyOperation extends TopicPairOperation<MyRequest, MyResponse> {
484 public MyOperation() {
485 super(TopicPairOperationTest.this.params, operator, MyResponse.class);
489 protected MyRequest makeRequest(int attempt) {
490 return new MyRequest();
494 protected List<String> getExpectedKeyValues(int attempt, MyRequest request) {
495 return Arrays.asList(REQ_ID);
499 protected boolean isSuccess(String rawResponse, MyResponse response) {
500 return (response.getOutput() == null);