4e45b1abe31d06ce2122a8aa4b902cc24ed65efb
[policy/models.git] / models-interactions / model-actors / actorServiceProvider / src / test / java / org / onap / policy / controlloop / actorserviceprovider / impl / TopicPairOperationTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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=========================================================
19  */
20
21 package org.onap.policy.controlloop.actorserviceprovider.impl;
22
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;
37
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;
43 import lombok.Getter;
44 import lombok.Setter;
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;
67
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;
80
81     private static final StandardCoder coder = new StandardCoder();
82
83     /**
84      * Used to attach an appender to the class' logger.
85      */
86     private static final Logger logger = (Logger) LoggerFactory.getLogger(TopicPairOperation.class);
87     private static final ExtractAppender appender = new ExtractAppender();
88
89     @Mock
90     private TopicPairOperator operator;
91     @Mock
92     private TopicPair pair;
93     @Mock
94     private Forwarder forwarder;
95
96     @Captor
97     private ArgumentCaptor<TriConsumer<CommInfrastructure, String, StandardCoderObject>> listenerCaptor;
98
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;
106
107     /**
108      * Attaches the appender to the logger.
109      */
110     @BeforeClass
111     public static void setUpBeforeClass() throws Exception {
112         /**
113          * Attach appender to the logger.
114          */
115         appender.setContext(logger.getLoggerContext());
116         appender.start();
117
118         logger.addAppender(appender);
119     }
120
121     /**
122      * Stops the appender.
123      */
124     @AfterClass
125     public static void tearDownAfterClass() {
126         appender.stop();
127     }
128
129     /**
130      * Sets up.
131      */
132     @Before
133     public void setUp() throws CoderException {
134         MockitoAnnotations.initMocks(this);
135
136         appender.clearExtractions();
137
138         topicParams = TopicPairParams.builder().source(MY_SOURCE).target(MY_TARGET).timeoutSec(TIMEOUT_SEC).build();
139
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);
146
147         when(pair.publish(any())).thenReturn(INFRA_LIST);
148
149         executor = new MyExec(100);
150
151         params = ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).executor(executor).build();
152         outcome = params.makeOutcome();
153
154         responseText = coder.encode(new MyResponse());
155         stdResponse = coder.decode(responseText, StandardCoderObject.class);
156
157         oper = new MyOperation();
158     }
159
160     @Test
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());
169     }
170
171     @Test
172     public void testStartOperationAsync() throws Exception {
173         CompletableFuture<OperationOutcome> future = oper.startOperationAsync(1, outcome);
174         assertFalse(future.isDone());
175
176         verify(forwarder).register(eq(Arrays.asList(REQ_ID)), listenerCaptor.capture());
177
178         verify(forwarder, never()).unregister(any(), any());
179
180         verify(pair).publish(any());
181
182         // provide the response
183         listenerCaptor.getValue().accept(CommInfrastructure.NOOP, responseText, stdResponse);
184
185         // run the tasks
186         assertTrue(executor.runAll());
187
188         assertTrue(future.isDone());
189
190         assertSame(outcome, future.get(5, TimeUnit.SECONDS));
191         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
192
193         verify(forwarder).unregister(eq(Arrays.asList(REQ_ID)), eq(listenerCaptor.getValue()));
194     }
195
196     /**
197      * Tests startOperationAsync() when the publisher throws an exception.
198      */
199     @Test
200     public void testStartOperationAsyncException() throws Exception {
201         // indicate that nothing was published
202         when(pair.publish(any())).thenReturn(Arrays.asList());
203
204         assertThatIllegalStateException().isThrownBy(() -> oper.startOperationAsync(1, outcome));
205
206         verify(forwarder).register(eq(Arrays.asList(REQ_ID)), listenerCaptor.capture());
207
208         // must still unregister
209         verify(forwarder).unregister(eq(Arrays.asList(REQ_ID)), eq(listenerCaptor.getValue()));
210     }
211
212     @Test
213     public void testGetTimeoutMsInteger() {
214         // use default
215         assertEquals(TIMEOUT_MS, oper.getTimeoutMs(null));
216         assertEquals(TIMEOUT_MS, oper.getTimeoutMs(0));
217
218         // use provided value
219         assertEquals(5000, oper.getTimeoutMs(5));
220     }
221
222     @Test
223     public void testPublishRequest() {
224         oper.publishRequest(new MyRequest());
225         assertEquals(INFRA_LIST.size(), appender.getExtracted().size());
226     }
227
228     /**
229      * Tests publishRequest() when nothing is published.
230      */
231     @Test
232     public void testPublishRequestUnpublished() {
233         when(pair.publish(any())).thenReturn(Arrays.asList());
234         assertThatIllegalStateException().isThrownBy(() -> oper.publishRequest(new MyRequest()));
235     }
236
237     /**
238      * Tests publishRequest() when the request type is a String.
239      */
240     @Test
241     public void testPublishRequestString() {
242         MyStringOperation oper2 = new MyStringOperation();
243         oper2.publishRequest(TEXT);
244         assertEquals(INFRA_LIST.size(), appender.getExtracted().size());
245     }
246
247     /**
248      * Tests publishRequest() when the coder throws an exception.
249      */
250     @Test
251     public void testPublishRequestException() {
252         setOperCoderException();
253         assertThatIllegalArgumentException().isThrownBy(() -> oper.publishRequest(new MyRequest()));
254     }
255
256     /**
257      * Tests processResponse() when it's a success and the response type is a String.
258      */
259     @Test
260     public void testProcessResponseSuccessString() {
261         MyStringOperation oper2 = new MyStringOperation();
262
263         assertSame(outcome, oper2.processResponse(CommInfrastructure.NOOP, outcome, TEXT, null));
264         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
265     }
266
267     /**
268      * Tests processResponse() when it's a success and the response type is a
269      * StandardCoderObject.
270      */
271     @Test
272     public void testProcessResponseSuccessSco() {
273         MyScoOperation oper2 = new MyScoOperation();
274
275         assertSame(outcome, oper2.processResponse(CommInfrastructure.NOOP, outcome, responseText, stdResponse));
276         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
277     }
278
279     /**
280      * Tests processResponse() when it's a failure.
281      */
282     @Test
283     public void testProcessResponseFailure() throws CoderException {
284         // indicate error in the response
285         MyResponse resp = new MyResponse();
286         resp.setOutput("error");
287
288         responseText = coder.encode(resp);
289         stdResponse = coder.decode(responseText, StandardCoderObject.class);
290
291         assertSame(outcome, oper.processResponse(CommInfrastructure.NOOP, outcome, responseText, stdResponse));
292         assertEquals(PolicyResult.FAILURE, outcome.getResult());
293     }
294
295     /**
296      * Tests processResponse() when the decoder succeeds.
297      */
298     @Test
299     public void testProcessResponseDecodeOk() throws CoderException {
300         assertSame(outcome, oper.processResponse(CommInfrastructure.NOOP, outcome, responseText, stdResponse));
301         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
302     }
303
304     /**
305      * Tests processResponse() when the decoder throws an exception.
306      */
307     @Test
308     public void testProcessResponseDecodeExcept() throws CoderException {
309         // @formatter:off
310         assertThatIllegalArgumentException().isThrownBy(
311             () -> oper.processResponse(CommInfrastructure.NOOP, outcome, "{invalid json", stdResponse));
312         // @formatter:on
313     }
314
315     @Test
316     public void testPostProcessResponse() {
317         assertThatCode(() -> oper.postProcessResponse(outcome, null, null)).doesNotThrowAnyException();
318     }
319
320     @Test
321     public void testLogTopicRequest() {
322         // nothing to log
323         appender.clearExtractions();
324         oper.logTopicRequest(Arrays.asList(), new MyRequest());
325         assertEquals(0, appender.getExtracted().size());
326
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());
332
333         assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString())
334                         .contains("{\n  \"theRequestId\": \"my-request-id\"\n}");
335
336         assertThat(output.get(1)).contains(CommInfrastructure.UEB.toString())
337                         .contains("{\n  \"theRequestId\": \"my-request-id\"\n}");
338
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);
345
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());
351
352         assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString()).contains("null");
353
354         // exception from coder
355         setOperCoderException();
356
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());
363     }
364
365     @Test
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());
372
373         assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString())
374                         .contains("{\n  \"requestId\": \"my-request-id\"\n}");
375
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);
382
383         // log a null response
384         appender.clearExtractions();
385         oper.logTopicResponse(CommInfrastructure.NOOP, null);
386         output = appender.getExtracted();
387         assertEquals(1, output.size());
388
389         assertThat(output.get(0)).contains(CommInfrastructure.NOOP.toString()).contains("null");
390
391         // exception from coder
392         setOperCoderException();
393
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());
400     }
401
402     @Test
403     public void testMakeCoder() {
404         assertNotNull(oper.makeCoder());
405     }
406
407     /**
408      * Creates a new {@link #oper} whose coder will throw an exception.
409      */
410     private void setOperCoderException() {
411         oper = new MyOperation() {
412             @Override
413             protected Coder makeCoder() {
414                 return new StandardCoder() {
415                     @Override
416                     public String encode(Object object, boolean pretty) throws CoderException {
417                         throw new CoderException(EXPECTED_EXCEPTION);
418                     }
419                 };
420             }
421         };
422     }
423
424     @Getter
425     @Setter
426     public static class MyRequest {
427         private String theRequestId = REQ_ID;
428         private String input;
429     }
430
431     @Getter
432     @Setter
433     public static class MyResponse {
434         private String requestId = REQ_ID;
435         private String output;
436     }
437
438
439     private class MyStringOperation extends TopicPairOperation<String, String> {
440         public MyStringOperation() {
441             super(TopicPairOperationTest.this.params, operator, String.class);
442         }
443
444         @Override
445         protected String makeRequest(int attempt) {
446             return TEXT;
447         }
448
449         @Override
450         protected List<String> getExpectedKeyValues(int attempt, String request) {
451             return Arrays.asList(REQ_ID);
452         }
453
454         @Override
455         protected boolean isSuccess(String rawResponse, String response) {
456             return (response != null);
457         }
458     }
459
460
461     private class MyScoOperation extends TopicPairOperation<MyRequest, StandardCoderObject> {
462         public MyScoOperation() {
463             super(TopicPairOperationTest.this.params, operator, StandardCoderObject.class);
464         }
465
466         @Override
467         protected MyRequest makeRequest(int attempt) {
468             return new MyRequest();
469         }
470
471         @Override
472         protected List<String> getExpectedKeyValues(int attempt, MyRequest request) {
473             return Arrays.asList(REQ_ID);
474         }
475
476         @Override
477         protected boolean isSuccess(String rawResponse, StandardCoderObject response) {
478             return (response.getString("output") == null);
479         }
480     }
481
482
483     private class MyOperation extends TopicPairOperation<MyRequest, MyResponse> {
484         public MyOperation() {
485             super(TopicPairOperationTest.this.params, operator, MyResponse.class);
486         }
487
488         @Override
489         protected MyRequest makeRequest(int attempt) {
490             return new MyRequest();
491         }
492
493         @Override
494         protected List<String> getExpectedKeyValues(int attempt, MyRequest request) {
495             return Arrays.asList(REQ_ID);
496         }
497
498         @Override
499         protected boolean isSuccess(String rawResponse, MyResponse response) {
500             return (response.getOutput() == null);
501         }
502     }
503 }