d71d413e43c8022f4c8e5a3b5962757c25098aa3
[policy/common.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019-2021 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.common.endpoints.listeners;
22
23 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertTrue;
26 import static org.mockito.Mockito.doThrow;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.times;
30 import static org.mockito.Mockito.verify;
31
32 import ch.qos.logback.classic.Level;
33 import ch.qos.logback.classic.Logger;
34 import org.junit.After;
35 import org.junit.AfterClass;
36 import org.junit.Before;
37 import org.junit.BeforeClass;
38 import org.junit.Test;
39 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
40 import org.onap.policy.common.utils.coder.Coder;
41 import org.onap.policy.common.utils.coder.CoderException;
42 import org.onap.policy.common.utils.coder.StandardCoder;
43 import org.onap.policy.common.utils.coder.StandardCoderObject;
44 import org.onap.policy.common.utils.test.log.logback.ExtractAppender;
45 import org.slf4j.LoggerFactory;
46
47 public class RequestIdDispatcherTest {
48
49     /**
50      * Used to attach an appender to the class' logger.
51      */
52     private static final Logger logger = (Logger) LoggerFactory.getLogger(RequestIdDispatcher.class);
53     private static final ExtractAppender appender = new ExtractAppender();
54
55     /**
56      * Original logging level for the logger.
57      */
58     private static Level saveLevel;
59
60     private static final CommInfrastructure INFRA = CommInfrastructure.NOOP;
61     private static final String REQID_FIELD = "requestId";
62     private static final String TOPIC = "my-topic";
63     private static final String REQID1 = "request-1";
64     private static final String REQID2 = "request-2";
65
66     private static final Coder coder = new StandardCoder();
67
68     private RequestIdDispatcher<MyMessage> primary;
69     private TypedMessageListener<MyMessage> secondary1;
70     private TypedMessageListener<MyMessage> secondary2;
71     private TypedMessageListener<MyMessage> secondary3;
72     private TypedMessageListener<MyMessage> secondary4;
73     private MyMessage status;
74
75     /**
76      * Initializes statics.
77      */
78     @BeforeClass
79     public static void setUpBeforeClass() {
80         saveLevel = logger.getLevel();
81         logger.setLevel(Level.INFO);
82
83         appender.setContext(logger.getLoggerContext());
84         appender.start();
85     }
86
87     @AfterClass
88     public static void tearDownAfterClass() {
89         logger.setLevel(saveLevel);
90         appender.stop();
91     }
92
93     /**
94      * Create various mocks and primary listener.
95      */
96     @SuppressWarnings("unchecked")
97     @Before
98     public void setUp() {
99         appender.clearExtractions();
100
101         secondary1 = mock(TypedMessageListener.class);
102         secondary2 = mock(TypedMessageListener.class);
103         secondary3 = mock(TypedMessageListener.class);
104         secondary4 = mock(TypedMessageListener.class);
105
106         primary = new RequestIdDispatcher<>(MyMessage.class, REQID_FIELD);
107     }
108
109     @After
110     public void tearDown() {
111         logger.detachAppender(appender);
112     }
113
114     @Test
115     public void testRegisterMessageListener() {
116         primary.register(secondary1);
117
118         // should process message that does not have a request id
119         status = new MyMessage();
120         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
121         verify(secondary1).onTopicEvent(INFRA, TOPIC, status);
122
123         // should process again
124         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
125         verify(secondary1, times(2)).onTopicEvent(INFRA, TOPIC, status);
126
127         // should NOT process a message that has a request id
128         status = new MyMessage(REQID1);
129         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
130         verify(secondary1, never()).onTopicEvent(INFRA, TOPIC, status);
131     }
132
133     @Test
134     public void testRegisterStringMessageListener() {
135         primary.register(REQID1, secondary1);
136
137         // should NOT process message that does not have a request id
138         status = new MyMessage();
139         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
140         verify(secondary1, never()).onTopicEvent(INFRA, TOPIC, status);
141
142         // should process a message that has the desired request id
143         status = new MyMessage(REQID1);
144         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
145         verify(secondary1).onTopicEvent(INFRA, TOPIC, status);
146
147         // should process again
148         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
149         verify(secondary1, times(2)).onTopicEvent(INFRA, TOPIC, status);
150
151         // should NOT process a message that does NOT have the desired request id
152         status = new MyMessage(REQID2);
153         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
154         verify(secondary1, never()).onTopicEvent(INFRA, TOPIC, status);
155
156         // null request id => exception
157         assertThatIllegalArgumentException().isThrownBy(() -> primary.register(null, secondary1));
158
159         // empty request id => exception
160         assertThatIllegalArgumentException().isThrownBy(() -> primary.register("", secondary1));
161     }
162
163     @Test
164     public void testUnregisterMessageListener() {
165         primary.register(secondary1);
166         primary.register(secondary2);
167
168         // should process message
169         status = new MyMessage();
170         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
171         verify(secondary1).onTopicEvent(INFRA, TOPIC, status);
172         verify(secondary2).onTopicEvent(INFRA, TOPIC, status);
173
174         primary.unregister(secondary1);
175
176         // should NOT process again
177         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
178         verify(secondary1, times(1)).onTopicEvent(INFRA, TOPIC, status);
179
180         // other listener should still have processed it
181         verify(secondary2, times(2)).onTopicEvent(INFRA, TOPIC, status);
182     }
183
184     @Test
185     public void testUnregisterString() {
186         primary.register(REQID1, secondary1);
187         primary.register(REQID2, secondary2);
188
189         // should process a message that has the desired request id
190         status = new MyMessage(REQID1);
191         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
192         verify(secondary1).onTopicEvent(INFRA, TOPIC, status);
193
194         primary.unregister(REQID1);
195
196         // should NOT re-process
197         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
198         verify(secondary1, times(1)).onTopicEvent(INFRA, TOPIC, status);
199
200         // secondary should still be able to process
201         status = new MyMessage(REQID2);
202         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
203         verify(secondary2).onTopicEvent(INFRA, TOPIC, status);
204     }
205
206     @Test
207     public void testOnTopicEvent() {
208         primary.register(REQID1, secondary1);
209         primary.register(REQID2, secondary2);
210         primary.register(secondary3);
211         primary.register(secondary4);
212
213         // without request id
214         status = new MyMessage();
215         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
216         verify(secondary1, never()).onTopicEvent(INFRA, TOPIC, status);
217         verify(secondary2, never()).onTopicEvent(INFRA, TOPIC, status);
218         verify(secondary3).onTopicEvent(INFRA, TOPIC, status);
219         verify(secondary4).onTopicEvent(INFRA, TOPIC, status);
220
221         // with request id
222         status = new MyMessage(REQID1);
223         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
224         verify(secondary1).onTopicEvent(INFRA, TOPIC, status);
225         verify(secondary2, never()).onTopicEvent(INFRA, TOPIC, status);
226         verify(secondary3, never()).onTopicEvent(INFRA, TOPIC, status);
227         verify(secondary4, never()).onTopicEvent(INFRA, TOPIC, status);
228     }
229
230     @Test
231     public void testOfferToListener() {
232         logger.addAppender(appender);
233
234         // no listener for this
235         status = new MyMessage(REQID1);
236         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
237
238         assertFalse(appender.getExtracted().toString().contains("failed to process message"));
239
240         // listener throws an exception
241         primary.register(secondary1);
242
243         status = new MyMessage();
244
245         RuntimeException ex = new RuntimeException("expected exception");
246         doThrow(ex).when(secondary1).onTopicEvent(INFRA, TOPIC, status);
247
248         primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
249         assertTrue(appender.getExtracted().toString().contains("failed to process message"));
250     }
251
252     /**
253      * Makes a standard object from a status message.
254      *
255      * @param source message to be converted
256      * @return a standard object representing the message
257      */
258     private StandardCoderObject makeSco(MyMessage source) {
259         try {
260             return coder.toStandard(source);
261
262         } catch (CoderException e) {
263             throw new RuntimeException(e);
264         }
265     }
266
267     protected static class MyMessage {
268         private String requestId;
269
270         public MyMessage() {
271             super();
272         }
273
274         public MyMessage(String requestId) {
275             this.requestId = requestId;
276         }
277
278         @Override
279         public int hashCode() {
280             final int prime = 31;
281             int result = 1;
282             result = prime * result + ((requestId == null) ? 0 : requestId.hashCode());
283             return result;
284         }
285
286         @Override
287         public boolean equals(Object obj) {
288             if (this == obj) {
289                 return true;
290             }
291             if (obj == null) {
292                 return false;
293             }
294             if (getClass() != obj.getClass()) {
295                 return false;
296             }
297             MyMessage other = (MyMessage) obj;
298             if (requestId == null) {
299                 if (other.requestId != null) {
300                     return false;
301                 }
302             } else if (!requestId.equals(other.requestId)) {
303                 return false;
304             }
305             return true;
306         }
307     }
308 }