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