Generate notifications when policies change
[policy/pap.git] / main / src / test / java / org / onap / policy / pap / main / comm / PdpModifyRequestMapTest.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP PAP
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
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.pap.main.comm;
22
23 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.assertSame;
28 import static org.junit.Assert.assertTrue;
29 import static org.mockito.Matchers.any;
30 import static org.mockito.Matchers.eq;
31 import static org.mockito.Mockito.never;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
35
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.List;
40 import java.util.stream.Collectors;
41 import javax.ws.rs.core.Response.Status;
42 import org.junit.Before;
43 import org.junit.Test;
44 import org.mockito.ArgumentCaptor;
45 import org.mockito.Captor;
46 import org.mockito.Mock;
47 import org.mockito.MockitoAnnotations;
48 import org.onap.policy.models.base.PfModelException;
49 import org.onap.policy.models.pdp.concepts.Pdp;
50 import org.onap.policy.models.pdp.concepts.PdpGroup;
51 import org.onap.policy.models.pdp.concepts.PdpMessage;
52 import org.onap.policy.models.pdp.concepts.PdpStateChange;
53 import org.onap.policy.models.pdp.concepts.PdpStatus;
54 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
55 import org.onap.policy.models.pdp.concepts.PdpUpdate;
56 import org.onap.policy.models.pdp.enums.PdpState;
57 import org.onap.policy.pap.main.comm.msgdata.Request;
58 import org.onap.policy.pap.main.comm.msgdata.RequestListener;
59 import org.onap.policy.pap.main.parameters.PdpModifyRequestMapParams;
60 import org.powermock.reflect.Whitebox;
61
62 public class PdpModifyRequestMapTest extends CommonRequestBase {
63     private static final String MY_REASON = "my reason";
64
65     /**
66      * Used to capture input to dao.createPdpGroups().
67      */
68     @Captor
69     private ArgumentCaptor<List<PdpGroup>> createCaptor;
70
71
72     /**
73      * Used to capture input to dao.updatePdpGroups().
74      */
75     @Captor
76     private ArgumentCaptor<List<PdpGroup>> updateCaptor;
77
78     @Mock
79     private PdpRequests requests;
80
81     private MyMap map;
82     private PdpUpdate update;
83     private PdpStateChange change;
84     private PdpStatus response;
85
86     /**
87      * Sets up.
88      *
89      * @throws Exception if an error occurs
90      */
91     @Before
92     public void setUp() throws Exception {
93         super.setUp();
94
95         MockitoAnnotations.initMocks(this);
96
97         response = new PdpStatus();
98
99         update = makeUpdate(PDP1, MY_GROUP, MY_SUBGROUP);
100         change = makeStateChange(PDP1, MY_STATE);
101
102         when(requests.getPdpName()).thenReturn(PDP1);
103
104         response.setName(MY_NAME);
105         response.setState(MY_STATE);
106         response.setPdpGroup(update.getPdpGroup());
107         response.setPdpSubgroup(update.getPdpSubgroup());
108         response.setPolicies(Collections.emptyList());
109
110         map = new MyMap(mapParams);
111     }
112
113     @Test
114     public void testPdpModifyRequestMap() {
115         assertSame(mapParams, Whitebox.getInternalState(map, "params"));
116         assertSame(lock, Whitebox.getInternalState(map, "modifyLock"));
117         assertSame(daoFactory, Whitebox.getInternalState(map, "daoFactory"));
118     }
119
120     @Test
121     public void testStopPublishing() {
122         // try with non-existent PDP
123         map.stopPublishing(PDP1);
124
125         // now start a PDP and try it
126         map.addRequest(change);
127         map.stopPublishing(PDP1);
128         verify(requests).stopPublishing();
129
130         // try again - it shouldn't stop publishing again
131         map.stopPublishing(PDP1);
132         verify(requests, times(1)).stopPublishing();
133     }
134
135     @Test
136     public void testAddRequestPdpUpdatePdpStateChange_BothNull() {
137         // nulls should be ok
138         map.addRequest(null, null);
139     }
140
141     @Test
142     public void testAddRequestPdpUpdatePdpStateChange_NullUpdate() {
143         map.addRequest(null, change);
144
145         Request req = getSingletons(1).get(0);
146         assertSame(change, req.getMessage());
147         assertEquals("pdp_1 PdpStateChange", req.getName());
148     }
149
150     @Test
151     public void testAddRequestPdpUpdatePdpStateChange_NullStateChange() {
152         map.addRequest(update, null);
153
154         Request req = getSingletons(1).get(0);
155         assertSame(update, req.getMessage());
156         assertEquals("pdp_1 PdpUpdate", req.getName());
157     }
158
159     @Test
160     public void testAddRequestPdpUpdatePdpStateChange_BothProvided() {
161         map.addRequest(update, change);
162
163         // should have only allocated one request structure
164         assertEquals(1, map.nalloc);
165
166         // both requests should have been added
167         List<Request> values = getSingletons(2);
168
169         Request req = values.remove(0);
170         assertSame(update, req.getMessage());
171         assertEquals("pdp_1 PdpUpdate", req.getName());
172
173         req = values.remove(0);
174         assertSame(change, req.getMessage());
175         assertEquals("pdp_1 PdpStateChange", req.getName());
176     }
177
178     @Test
179     public void testAddRequestPdpUpdatePdpStateChange() {
180         // null should be ok
181         map.addRequest(null, null);
182
183         map.addRequest(change);
184
185         Request req = getSingletons(1).get(0);
186         assertSame(change, req.getMessage());
187         assertEquals("pdp_1 PdpStateChange", req.getName());
188
189         // broadcast should throw an exception
190         change.setName(null);
191         assertThatIllegalArgumentException().isThrownBy(() -> map.addRequest(change))
192                         .withMessageStartingWith("unexpected broadcast message: PdpStateChange");
193     }
194
195     @Test
196     public void testAddRequestPdpUpdate() {
197         // null should be ok
198         map.addRequest((PdpUpdate) null);
199
200         map.addRequest(update);
201
202         Request req = getSingletons(1).get(0);
203         assertSame(update, req.getMessage());
204         assertEquals("pdp_1 PdpUpdate", req.getName());
205
206         // broadcast should throw an exception
207         update.setName(null);
208         assertThatIllegalArgumentException().isThrownBy(() -> map.addRequest(update))
209                         .withMessageStartingWith("unexpected broadcast message: PdpUpdate");
210     }
211
212     @Test
213     public void testAddRequestPdpStateChange() {
214         // null should be ok
215         map.addRequest((PdpStateChange) null);
216
217         map.addRequest(change);
218
219         Request req = getSingletons(1).get(0);
220         assertSame(change, req.getMessage());
221         assertEquals("pdp_1 PdpStateChange", req.getName());
222
223         // broadcast should throw an exception
224         change.setName(null);
225         assertThatIllegalArgumentException().isThrownBy(() -> map.addRequest(change))
226                         .withMessageStartingWith("unexpected broadcast message: PdpStateChange");
227     }
228
229     @Test
230     public void testAddSingleton() {
231         map.addRequest(change);
232         assertEquals(1, map.nalloc);
233
234         // should have one singleton
235         getSingletons(1);
236
237         // add another request with the same PDP
238         map.addRequest(makeStateChange(PDP1, MY_STATE));
239         assertEquals(1, map.nalloc);
240
241         // should now have another singleton
242         getSingletons(2);
243
244
245         // add another request with a different PDP
246         map.addRequest(makeStateChange(DIFFERENT, MY_STATE));
247
248         // should now have another allocation
249         assertEquals(2, map.nalloc);
250
251         // should now have another singleton
252         getSingletons(3);
253     }
254
255     @Test
256     public void testStartNextRequest_NoMore() {
257         map.addRequest(change);
258
259         // indicate success
260         getListener(getSingletons(1).get(0)).success(PDP1);
261
262         /*
263          * the above should have removed the requests so next time should allocate a new
264          * one
265          */
266         map.addRequest(change);
267         assertEquals(2, map.nalloc);
268     }
269
270     @Test
271     public void testStartNextRequest_HaveMore() {
272         map.addRequest(update);
273         map.addRequest(change);
274
275         Request updateReq = getSingletons(2).get(0);
276
277         // indicate success with the update
278         when(requests.startNextRequest(updateReq)).thenReturn(true);
279         getListener(updateReq).success(PDP1);
280
281         // should have started the next request
282         verify(requests).startNextRequest(updateReq);
283
284         /*
285          * requests should still be there, so adding another request should not allocate a
286          * new one
287          */
288         map.addRequest(update);
289         assertEquals(1, map.nalloc);
290     }
291
292     @Test
293     public void testDisablePdp() throws Exception {
294         map.addRequest(update);
295
296         // put the PDP in a group
297         PdpGroup group = makeGroup(MY_GROUP);
298         group.setPdpSubgroups(Arrays.asList(makeSubGroup(MY_SUBGROUP, PDP1)));
299
300         when(dao.getFilteredPdpGroups(any())).thenReturn(Arrays.asList(group));
301
302         // indicate failure
303         invokeFailureHandler(1);
304
305         // should have stopped publishing
306         verify(requests).stopPublishing();
307
308         // should have generated a notification
309         verify(notifier).removePdp(PDP1);
310
311         // should have published a new update
312         PdpMessage msg2 = getSingletons(3).get(1).getMessage();
313         assertNotNull(msg2);
314         assertTrue(msg2 instanceof PdpUpdate);
315
316         // update should have null group & subgroup
317         update = (PdpUpdate) msg2;
318         assertEquals(PDP1, update.getName());
319         assertNull(update.getPdpGroup());
320         assertNull(update.getPdpSubgroup());
321
322         // should have published a state-change
323         msg2 = getSingletons(3).get(2).getMessage();
324         assertNotNull(msg2);
325         assertTrue(msg2 instanceof PdpStateChange);
326
327         change = (PdpStateChange) msg2;
328         assertEquals(PDP1, change.getName());
329         assertEquals(PdpState.PASSIVE, change.getState());
330     }
331
332     @Test
333     public void testDisablePdp_NotInGroup() {
334         map.addRequest(update);
335
336         // indicate failure
337         invokeFailureHandler(1);
338
339         // should have stopped publishing
340         verify(requests).stopPublishing();
341
342         // should have published a new state-change
343         PdpMessage msg2 = getSingletons(2).get(1).getMessage();
344         assertNotNull(msg2);
345         assertTrue(msg2 instanceof PdpStateChange);
346
347         change = (PdpStateChange) msg2;
348         assertEquals(PDP1, change.getName());
349         assertEquals(PdpState.PASSIVE, change.getState());
350     }
351
352     @Test
353     public void testDisablePdp_AlreadyRemoved() {
354         map.addRequest(change);
355         map.stopPublishing(PDP1);
356
357         invokeFailureHandler(1);
358
359         // should not have stopped publishing a second time
360         verify(requests, times(1)).stopPublishing();
361     }
362
363     @Test
364     public void testDisablePdp_NoGroup() {
365         map.addRequest(change);
366
367         invokeFailureHandler(1);
368
369         // should not have stopped publishing
370         verify(requests).stopPublishing();
371     }
372
373     @Test
374     public void testRemoveFromGroup() throws Exception {
375         map.addRequest(change);
376
377         PdpGroup group = makeGroup(MY_GROUP);
378         group.setPdpSubgroups(Arrays.asList(makeSubGroup(MY_SUBGROUP + "a", PDP1 + "a"),
379                         makeSubGroup(MY_SUBGROUP, PDP1), makeSubGroup(MY_SUBGROUP + "c", PDP1 + "c")));
380
381         when(dao.getFilteredPdpGroups(any())).thenReturn(Arrays.asList(group));
382
383         invokeFailureHandler(1);
384
385         // verify that the PDP was removed from the subgroup
386         List<PdpGroup> groups = getGroupUpdates();
387         assertEquals(1, groups.size());
388         assertSame(group, groups.get(0));
389
390         List<PdpSubGroup> subgroups = group.getPdpSubgroups();
391         assertEquals(3, subgroups.size());
392         assertEquals("[pdp_1a]", getPdpNames(subgroups.get(0)));
393         assertEquals("[]", getPdpNames(subgroups.get(1)));
394         assertEquals("[pdp_1c]", getPdpNames(subgroups.get(2)));
395     }
396
397     @Test
398     public void testRemoveFromGroup_DaoEx() throws Exception {
399         map.addRequest(change);
400
401         when(dao.getFilteredPdpGroups(any())).thenThrow(new PfModelException(Status.BAD_REQUEST, "expected exception"));
402
403         invokeFailureHandler(1);
404
405         // should still stop publishing
406         verify(requests).stopPublishing();
407
408         // requests should have been removed from the map so this should allocate another
409         map.addRequest(update);
410         assertEquals(2, map.nalloc);
411     }
412
413     @Test
414     public void testRemoveFromGroup_NoGroups() throws Exception {
415         map.addRequest(change);
416
417         invokeFailureHandler(1);
418
419         verify(dao, never()).updatePdpGroups(any());
420     }
421
422     @Test
423     public void testRemoveFromGroup_NoMatchingSubgroup() throws Exception {
424         map.addRequest(change);
425
426         PdpGroup group = makeGroup(MY_GROUP);
427         group.setPdpSubgroups(Arrays.asList(makeSubGroup(MY_SUBGROUP, DIFFERENT)));
428
429         when(dao.getFilteredPdpGroups(any())).thenReturn(Arrays.asList(group));
430
431         invokeFailureHandler(1);
432
433         verify(dao, never()).updatePdpGroups(any());
434     }
435
436     @Test
437     public void testRemoveFromSubgroup() throws Exception {
438         map.addRequest(change);
439
440         PdpGroup group = makeGroup(MY_GROUP);
441         group.setPdpSubgroups(Arrays.asList(makeSubGroup(MY_SUBGROUP, PDP1, PDP1 + "x", PDP1 + "y")));
442
443         when(dao.getFilteredPdpGroups(any())).thenReturn(Arrays.asList(group));
444
445         invokeFailureHandler(1);
446
447         // verify that the PDP was removed from the subgroup
448         List<PdpGroup> groups = getGroupUpdates();
449         assertEquals(1, groups.size());
450         assertSame(group, groups.get(0));
451
452         PdpSubGroup subgroup = group.getPdpSubgroups().get(0);
453         assertEquals(2, subgroup.getCurrentInstanceCount());
454         assertEquals("[pdp_1x, pdp_1y]", getPdpNames(subgroup));
455     }
456
457     @Test
458     public void testMakePdpRequests() {
459         // this should invoke the real method without throwing an exception
460         new PdpModifyRequestMap(mapParams).addRequest(change);
461
462         QueueToken<PdpMessage> token = queue.poll();
463         assertNotNull(token);
464         assertSame(change, token.get());
465
466         verify(dispatcher).register(eq(change.getRequestId()), any());
467         verify(timers).register(eq(change.getRequestId()), any());
468     }
469
470     @Test
471     public void testSingletonListenerFailure() throws Exception {
472         map.addRequest(change);
473
474         // invoke the method
475         invokeFailureHandler(1);
476
477         verify(requests).stopPublishing();
478     }
479
480     @Test
481     public void testSingletonListenerFailure_WrongPdpName() throws Exception {
482         map.addRequest(change);
483
484         // invoke the method - has wrong PDP name
485         when(requests.getPdpName()).thenReturn(DIFFERENT);
486         invokeFailureHandler(1);
487
488         verify(requests, never()).stopPublishing();
489     }
490
491     @Test
492     public void testSingletonListenerSuccess_LastRequest() throws Exception {
493         map.addRequest(change);
494
495         // invoke the method
496         invokeSuccessHandler(1);
497
498         verify(requests, never()).stopPublishing();
499
500         // requests should have been removed from the map so this should allocate another
501         map.addRequest(update);
502         assertEquals(2, map.nalloc);
503     }
504
505     @Test
506     public void testSingletonListenerSuccess_NameMismatch() throws Exception {
507         map.addRequest(change);
508
509         // invoke the method - with a different name
510         when(requests.getPdpName()).thenReturn(DIFFERENT);
511         invokeSuccessHandler(1);
512
513         verify(requests, never()).stopPublishing();
514
515         // no effect on the map
516         map.addRequest(update);
517         assertEquals(1, map.nalloc);
518     }
519
520     @Test
521     public void testSingletonListenerSuccess_AlreadyStopped() throws Exception {
522         map.addRequest(change);
523
524         map.stopPublishing(PDP1);
525
526         // invoke the method
527         invokeSuccessHandler(1);
528
529         // should have called this a second time
530         verify(requests, times(2)).stopPublishing();
531
532         // requests should have been removed from the map so this should allocate another
533         map.addRequest(update);
534         assertEquals(2, map.nalloc);
535     }
536
537     @Test
538     public void testSingletonListenerRetryCountExhausted() throws Exception {
539         map.addRequest(change);
540
541         // invoke the method
542         getListener(getSingletons(1).get(0)).retryCountExhausted();
543
544         verify(requests).stopPublishing();
545     }
546
547
548     /**
549      * Invokes the first request's listener.success() method.
550      *
551      * @param count expected number of requests
552      */
553     private void invokeSuccessHandler(int count) {
554         getListener(getSingletons(count).get(0)).success(PDP1);
555     }
556
557     /**
558      * Invokes the first request's listener.failure() method.
559      *
560      * @param count expected number of requests
561      */
562     private void invokeFailureHandler(int count) {
563         getListener(getSingletons(count).get(0)).failure(PDP1, MY_REASON);
564     }
565
566     /**
567      * Gets the name of the PDPs contained within a subgroup.
568      *
569      * @param subgroup subgroup of interest
570      * @return the name of the PDPs contained within the subgroup
571      */
572     private String getPdpNames(PdpSubGroup subgroup) {
573         return subgroup.getPdpInstances().stream().map(Pdp::getInstanceId).collect(Collectors.toList()).toString();
574     }
575
576     /**
577      * Gets the singleton requests added to {@link #requests}.
578      *
579      * @param count number of singletons expected
580      * @return the singleton requests
581      */
582     private List<Request> getSingletons(int count) {
583         ArgumentCaptor<Request> captor = ArgumentCaptor.forClass(Request.class);
584
585         verify(requests, times(count)).addSingleton(captor.capture());
586         return captor.getAllValues();
587     }
588
589     /**
590      * Gets the listener from a request.
591      *
592      * @param request request of interest
593      * @return the request's listener
594      */
595     private RequestListener getListener(Request request) {
596         return Whitebox.getInternalState(request, "listener");
597     }
598
599     private PdpGroup makeGroup(String name) {
600         PdpGroup group = new PdpGroup();
601
602         group.setName(name);
603
604         return group;
605     }
606
607     private PdpSubGroup makeSubGroup(String pdpType, String... pdpNames) {
608         PdpSubGroup subgroup = new PdpSubGroup();
609
610         subgroup.setPdpType(pdpType);
611         subgroup.setPdpInstances(Arrays.asList(pdpNames).stream().map(this::makePdp).collect(Collectors.toList()));
612
613         return subgroup;
614     }
615
616     private Pdp makePdp(String pdpName) {
617         Pdp pdp = new Pdp();
618         pdp.setInstanceId(pdpName);
619
620         return pdp;
621     }
622
623     /**
624      * Gets the input to the method.
625      *
626      * @return the input that was passed to the dao.updatePdpGroups() method
627      * @throws Exception if an error occurred
628      */
629     private List<PdpGroup> getGroupUpdates() throws Exception {
630         verify(dao).updatePdpGroups(updateCaptor.capture());
631
632         return copyList(updateCaptor.getValue());
633     }
634
635     /**
636      * Copies a list and sorts it by group name.
637      *
638      * @param source source list to copy
639      * @return a copy of the source list
640      */
641     private List<PdpGroup> copyList(List<PdpGroup> source) {
642         List<PdpGroup> newlst = new ArrayList<>(source);
643         Collections.sort(newlst, (left, right) -> left.getName().compareTo(right.getName()));
644         return newlst;
645     }
646
647     private class MyMap extends PdpModifyRequestMap {
648         /**
649          * Number of times requests were allocated.
650          */
651         private int nalloc = 0;
652
653         public MyMap(PdpModifyRequestMapParams params) {
654             super(params);
655         }
656
657         @Override
658         protected PdpRequests makePdpRequests(String pdpName) {
659             ++nalloc;
660             return requests;
661         }
662     }
663 }