2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019 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.Matchers.eq;
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;
33 import ch.qos.logback.classic.Level;
34 import ch.qos.logback.classic.Logger;
35 import org.junit.After;
36 import org.junit.AfterClass;
37 import org.junit.Before;
38 import org.junit.BeforeClass;
39 import org.junit.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;
48 public class RequestIdDispatcherTest {
51 * Used to attach an appender to the class' logger.
53 private static final Logger logger = (Logger) LoggerFactory.getLogger(RequestIdDispatcher.class);
54 private static final ExtractAppender appender = new ExtractAppender();
57 * Original logging level for the logger.
59 private static Level saveLevel;
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";
67 private static final Coder coder = new StandardCoder();
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;
77 * Initializes statics.
80 public static void setUpBeforeClass() {
81 saveLevel = logger.getLevel();
82 logger.setLevel(Level.INFO);
84 appender.setContext(logger.getLoggerContext());
89 public static void tearDownAfterClass() {
90 logger.setLevel(saveLevel);
95 * Create various mocks and primary listener.
97 @SuppressWarnings("unchecked")
100 appender.clearExtractions();
102 secondary1 = mock(TypedMessageListener.class);
103 secondary2 = mock(TypedMessageListener.class);
104 secondary3 = mock(TypedMessageListener.class);
105 secondary4 = mock(TypedMessageListener.class);
107 primary = new RequestIdDispatcher<>(MyMessage.class, REQID_FIELD);
111 public void tearDown() {
112 logger.detachAppender(appender);
116 public void testRegisterMessageListener() {
117 primary.register(secondary1);
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(eq(INFRA), eq(TOPIC), eq(status));
124 // should process again
125 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
126 verify(secondary1, times(2)).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
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(eq(INFRA), eq(TOPIC), eq(status));
135 public void testRegisterStringMessageListener() {
136 primary.register(REQID1, secondary1);
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(eq(INFRA), eq(TOPIC), eq(status));
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(eq(INFRA), eq(TOPIC), eq(status));
148 // should process again
149 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
150 verify(secondary1, times(2)).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
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(eq(INFRA), eq(TOPIC), eq(status));
157 // null request id => exception
158 assertThatIllegalArgumentException().isThrownBy(() -> primary.register(null, secondary1));
160 // empty request id => exception
161 assertThatIllegalArgumentException().isThrownBy(() -> primary.register("", secondary1));
165 public void testUnregisterMessageListener() {
166 primary.register(secondary1);
167 primary.register(secondary2);
169 // should process message
170 status = new MyMessage();
171 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
172 verify(secondary1).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
173 verify(secondary2).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
175 primary.unregister(secondary1);
177 // should NOT process again
178 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
179 verify(secondary1, times(1)).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
181 // other listener should still have processed it
182 verify(secondary2, times(2)).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
186 public void testUnregisterString() {
187 primary.register(REQID1, secondary1);
188 primary.register(REQID2, secondary2);
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(eq(INFRA), eq(TOPIC), eq(status));
195 primary.unregister(REQID1);
197 // should NOT re-process
198 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
199 verify(secondary1, times(1)).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
201 // secondary should still be able to process
202 status = new MyMessage(REQID2);
203 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
204 verify(secondary2).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
208 public void testOnTopicEvent() {
209 primary.register(REQID1, secondary1);
210 primary.register(REQID2, secondary2);
211 primary.register(secondary3);
212 primary.register(secondary4);
214 // without request id
215 status = new MyMessage();
216 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
217 verify(secondary1, never()).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
218 verify(secondary2, never()).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
219 verify(secondary3).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
220 verify(secondary4).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
223 status = new MyMessage(REQID1);
224 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
225 verify(secondary1).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
226 verify(secondary2, never()).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
227 verify(secondary3, never()).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
228 verify(secondary4, never()).onTopicEvent(eq(INFRA), eq(TOPIC), eq(status));
232 public void testOfferToListener() {
233 logger.addAppender(appender);
235 // no listener for this
236 status = new MyMessage(REQID1);
237 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
239 assertFalse(appender.getExtracted().toString().contains("failed to process message"));
241 // listener throws an exception
242 primary.register(secondary1);
244 status = new MyMessage();
246 RuntimeException ex = new RuntimeException("expected exception");
247 doThrow(ex).when(secondary1).onTopicEvent(INFRA, TOPIC, status);
249 primary.onTopicEvent(INFRA, TOPIC, makeSco(status));
250 assertTrue(appender.getExtracted().toString().contains("failed to process message"));
254 * Makes a standard object from a status message.
256 * @param source message to be converted
257 * @return a standard object representing the message
259 private StandardCoderObject makeSco(MyMessage source) {
261 return coder.toStandard(source);
263 } catch (CoderException e) {
264 throw new RuntimeException(e);
268 protected static class MyMessage {
269 private String requestId;
275 public MyMessage(String requestId) {
276 this.requestId = requestId;
280 public int hashCode() {
281 final int prime = 31;
283 result = prime * result + ((requestId == null) ? 0 : requestId.hashCode());
288 public boolean equals(Object obj) {
295 if (getClass() != obj.getClass()) {
298 MyMessage other = (MyMessage) obj;
299 if (requestId == null) {
300 if (other.requestId != null) {
303 } else if (!requestId.equals(other.requestId)) {