2 * ============LICENSE_START=======================================================
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
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.common.endpoints.listeners;
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;
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;
47 public class RequestIdDispatcherTest {
50 * Used to attach an appender to the class' logger.
52 private static final Logger logger = (Logger) LoggerFactory.getLogger(RequestIdDispatcher.class);
53 private static final ExtractAppender appender = new ExtractAppender();
56 * Original logging level for the logger.
58 private static Level saveLevel;
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";
66 private static final Coder coder = new StandardCoder();
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;
76 * Initializes statics.
79 public static void setUpBeforeClass() {
80 saveLevel = logger.getLevel();
81 logger.setLevel(Level.INFO);
83 appender.setContext(logger.getLoggerContext());
88 public static void tearDownAfterClass() {
89 logger.setLevel(saveLevel);
94 * Create various mocks and primary listener.
96 @SuppressWarnings("unchecked")
99 appender.clearExtractions();
101 secondary1 = mock(TypedMessageListener.class);
102 secondary2 = mock(TypedMessageListener.class);
103 secondary3 = mock(TypedMessageListener.class);
104 secondary4 = mock(TypedMessageListener.class);
106 primary = new RequestIdDispatcher<>(MyMessage.class, REQID_FIELD);
110 public void tearDown() {
111 logger.detachAppender(appender);
115 public void testRegisterMessageListener() {
116 primary.register(secondary1);
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);
123 // should process again
124 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
125 verify(secondary1, times(2)).onTopicEvent(INFRA, TOPIC, status);
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);
134 public void testRegisterStringMessageListener() {
135 primary.register(REQID1, secondary1);
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);
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);
147 // should process again
148 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
149 verify(secondary1, times(2)).onTopicEvent(INFRA, TOPIC, status);
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);
156 // null request id => exception
157 assertThatIllegalArgumentException().isThrownBy(() -> primary.register(null, secondary1));
159 // empty request id => exception
160 assertThatIllegalArgumentException().isThrownBy(() -> primary.register("", secondary1));
164 public void testUnregisterMessageListener() {
165 primary.register(secondary1);
166 primary.register(secondary2);
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);
174 primary.unregister(secondary1);
176 // should NOT process again
177 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
178 verify(secondary1, times(1)).onTopicEvent(INFRA, TOPIC, status);
180 // other listener should still have processed it
181 verify(secondary2, times(2)).onTopicEvent(INFRA, TOPIC, status);
185 public void testUnregisterString() {
186 primary.register(REQID1, secondary1);
187 primary.register(REQID2, secondary2);
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);
194 primary.unregister(REQID1);
196 // should NOT re-process
197 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
198 verify(secondary1, times(1)).onTopicEvent(INFRA, TOPIC, status);
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);
207 public void testOnTopicEvent() {
208 primary.register(REQID1, secondary1);
209 primary.register(REQID2, secondary2);
210 primary.register(secondary3);
211 primary.register(secondary4);
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);
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);
231 public void testOfferToListener() {
232 logger.addAppender(appender);
234 // no listener for this
235 status = new MyMessage(REQID1);
236 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
238 assertFalse(appender.getExtracted().toString().contains("failed to process message"));
240 // listener throws an exception
241 primary.register(secondary1);
243 status = new MyMessage();
245 RuntimeException ex = new RuntimeException("expected exception");
246 doThrow(ex).when(secondary1).onTopicEvent(INFRA, TOPIC, status);
248 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
249 assertTrue(appender.getExtracted().toString().contains("failed to process message"));
253 * Makes a standard object from a status message.
255 * @param source message to be converted
256 * @return a standard object representing the message
258 private StandardCoderObject makeSco(MyMessage source) {
260 return coder.toStandard(source);
262 } catch (CoderException e) {
263 throw new RuntimeException(e);
267 protected static class MyMessage {
268 private String requestId;
274 public MyMessage(String requestId) {
275 this.requestId = requestId;
279 public int hashCode() {
280 final int prime = 31;
282 result = prime * result + ((requestId == null) ? 0 : requestId.hashCode());
287 public boolean equals(Object obj) {
294 if (getClass() != obj.getClass()) {
297 MyMessage other = (MyMessage) obj;
298 if (requestId == null) {
299 if (other.requestId != null) {
302 } else if (!requestId.equals(other.requestId)) {