Java 17 / Spring 6 / Spring Boot 3 Upgrade
[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, 2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2020-2021, 2023 Nordix Foundation.
7  * Modifications Copyright (C) 2022 Bell Canada. All rights reserved.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.pap.main.comm;
24
25 import static org.assertj.core.api.Assertions.assertThat;
26 import static org.assertj.core.api.Assertions.assertThatCode;
27 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
28 import static org.junit.jupiter.api.Assertions.assertEquals;
29 import static org.junit.jupiter.api.Assertions.assertFalse;
30 import static org.junit.jupiter.api.Assertions.assertNotNull;
31 import static org.junit.jupiter.api.Assertions.assertSame;
32 import static org.junit.jupiter.api.Assertions.assertTrue;
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.ArgumentMatchers.eq;
35 import static org.mockito.Mockito.doAnswer;
36 import static org.mockito.Mockito.mock;
37 import static org.mockito.Mockito.never;
38 import static org.mockito.Mockito.times;
39 import static org.mockito.Mockito.verify;
40 import static org.mockito.Mockito.when;
41
42 import jakarta.ws.rs.core.Response.Status;
43 import java.time.Instant;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.Comparator;
49 import java.util.Iterator;
50 import java.util.List;
51 import java.util.stream.Collectors;
52 import org.assertj.core.api.Assertions;
53 import org.junit.jupiter.api.AfterEach;
54 import org.junit.jupiter.api.BeforeEach;
55 import org.junit.jupiter.api.Test;
56 import org.mockito.ArgumentCaptor;
57 import org.mockito.Captor;
58 import org.mockito.Mock;
59 import org.mockito.MockitoAnnotations;
60 import org.onap.policy.models.base.PfModelException;
61 import org.onap.policy.models.base.PfModelRuntimeException;
62 import org.onap.policy.models.pdp.concepts.Pdp;
63 import org.onap.policy.models.pdp.concepts.PdpGroup;
64 import org.onap.policy.models.pdp.concepts.PdpMessage;
65 import org.onap.policy.models.pdp.concepts.PdpStateChange;
66 import org.onap.policy.models.pdp.concepts.PdpStatus;
67 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
68 import org.onap.policy.models.pdp.concepts.PdpUpdate;
69 import org.onap.policy.models.pdp.enums.PdpState;
70 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
71 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
72 import org.onap.policy.pap.main.comm.msgdata.Request;
73 import org.onap.policy.pap.main.comm.msgdata.RequestListener;
74 import org.onap.policy.pap.main.parameters.PdpModifyRequestMapParams;
75 import org.onap.policy.pap.main.service.PdpGroupService;
76 import org.onap.policy.pap.main.service.PolicyStatusService;
77 import org.springframework.test.util.ReflectionTestUtils;
78
79 class PdpModifyRequestMapTest extends CommonRequestBase {
80     private static final String MY_REASON = "my reason";
81     private static final int EXPIRED_SECONDS = 100;
82
83     /**
84      * Used to capture input to dao.createPdpGroups().
85      */
86     @Captor
87     private ArgumentCaptor<List<PdpGroup>> createCaptor;
88
89
90     /**
91      * Used to capture input to dao.updatePdpGroups().
92      */
93     @Captor
94     private ArgumentCaptor<List<PdpGroup>> updateCaptor;
95
96     /**
97      * Used to capture input to undeployer.undeploy().
98      */
99     @Captor
100     private ArgumentCaptor<Collection<ToscaConceptIdentifier>> undeployCaptor;
101
102     @Mock
103     private PdpRequests requests;
104
105     @Mock
106     private PolicyUndeployer undeployer;
107
108     @Mock
109     private PdpStatusMessageHandler responseHandler;
110
111     @Mock
112     private PdpGroupService pdpGroupService;
113
114     @Mock
115     private PolicyStatusService policyStatusService;
116
117     private MyMap map;
118     private PdpUpdate update;
119     private PdpStateChange change;
120     private PdpStatus response;
121
122     AutoCloseable autoCloseable;
123
124     /**
125      * Sets up.
126      *
127      * @throws Exception if an error occurs
128      */
129     @BeforeEach
130     @Override
131     public void setUp() throws Exception {
132         super.setUp();
133         autoCloseable = MockitoAnnotations.openMocks(this);
134
135         response = new PdpStatus();
136
137         update = makeUpdate(PDP1, MY_GROUP, MY_SUBGROUP);
138         change = makeStateChange(PDP1, MY_STATE);
139
140         when(requests.getPdpName()).thenReturn(PDP1);
141         when(requests.isFirstInQueue(any())).thenReturn(true);
142
143         response.setName(MY_NAME);
144         response.setState(MY_STATE);
145         response.setPdpGroup(update.getPdpGroup());
146         response.setPdpSubgroup(update.getPdpSubgroup());
147         response.setPolicies(Collections.emptyList());
148
149         map = new MyMap(mapParams);
150     }
151
152     @AfterEach
153     void tearDown() throws Exception {
154         autoCloseable.close();
155     }
156
157     @Test
158     void testPdpModifyRequestMap() {
159         assertSame(mapParams, ReflectionTestUtils.getField(map, "params"));
160         assertSame(lock, ReflectionTestUtils.getField(map, "modifyLock"));
161     }
162
163     @Test
164     void testIsEmpty() {
165         assertTrue(map.isEmpty());
166
167         map.addRequest(change);
168         assertFalse(map.isEmpty());
169
170         // indicate success
171         getListener(getSingletons(1).get(0)).success(PDP1, response);
172
173         assertTrue(map.isEmpty());
174         verify(responseHandler, never()).handlePdpStatus(response);
175     }
176
177     @Test
178     void testStopPublishing() {
179         // try with non-existent PDP
180         map.stopPublishing(PDP1);
181
182         // now start a PDP and try it
183         map.addRequest(change);
184         map.stopPublishing(PDP1);
185         verify(requests).stopPublishing();
186
187         // try again - it shouldn't stop publishing again
188         map.stopPublishing(PDP1);
189         verify(requests, times(1)).stopPublishing();
190     }
191
192     @Test
193     void testAddRequestPdpUpdatePdpStateChange_BothNull() {
194         // nulls should be ok
195         Assertions.assertThatCode(() -> map.addRequest(null, null)).doesNotThrowAnyException();
196     }
197
198     @Test
199     void testAddRequestPdpUpdatePdpStateChange_NullUpdate() {
200         map.addRequest(null, change);
201
202         Request req = getSingletons(1).get(0);
203         assertSame(change, req.getMessage());
204         assertEquals("pdp_1 PdpStateChange", req.getName());
205     }
206
207     @Test
208     void testAddRequestPdpUpdatePdpStateChange_NullStateChange() {
209         map.addRequest(update, null);
210
211         Request req = getSingletons(1).get(0);
212         assertSame(update, req.getMessage());
213         assertEquals("pdp_1 PdpUpdate", req.getName());
214     }
215
216     /**
217      * Tests addRequest() when two requests are provided and the second is an "activate"
218      * message.
219      */
220     @Test
221     void testAddRequestPdpUpdatePdpStateChange_BothProvided_Active() {
222         change.setState(PdpState.ACTIVE);
223         map.addRequest(update, change);
224
225         // should have only allocated one request structure
226         assertEquals(1, map.nalloc);
227
228         // both requests should have been added
229         List<Request> values = getSingletons(2);
230
231         // update should appear first
232         Request req = values.remove(0);
233         assertSame(update, req.getMessage());
234         assertEquals("pdp_1 PdpUpdate", req.getName());
235
236         req = values.remove(0);
237         assertSame(change, req.getMessage());
238         assertEquals("pdp_1 PdpStateChange", req.getName());
239     }
240
241     /**
242      * Tests addRequest() when two requests are provided and the second is "deactivate"
243      * message.
244      */
245     @Test
246     void testAddRequestPdpUpdatePdpStateChange_BothProvided_Passive() {
247         change.setState(PdpState.PASSIVE);
248         map.addRequest(update, change);
249
250         // should have only allocated one request structure
251         assertEquals(1, map.nalloc);
252
253         // both requests should have been added
254         List<Request> values = getSingletons(2);
255
256         // state-change should appear first
257         Request req = values.remove(0);
258         assertSame(change, req.getMessage());
259         assertEquals("pdp_1 PdpStateChange", req.getName());
260
261         req = values.remove(0);
262         assertSame(update, req.getMessage());
263         assertEquals("pdp_1 PdpUpdate", req.getName());
264     }
265
266     @Test
267     void testAddRequestPdpUpdatePdpStateChange() {
268         // null should be ok
269         map.addRequest(null, null);
270
271         map.addRequest(change);
272
273         Request req = getSingletons(1).get(0);
274         assertSame(change, req.getMessage());
275         assertEquals("pdp_1 PdpStateChange", req.getName());
276
277         // broadcast should throw an exception
278         change.setName(null);
279         assertThatIllegalArgumentException().isThrownBy(() -> map.addRequest(change))
280             .withMessageStartingWith("unexpected broadcast message: PdpStateChange");
281     }
282
283     @Test
284     void testAddRequestPdpUpdate() {
285         // null should be ok
286         map.addRequest((PdpUpdate) null);
287
288         map.addRequest(update);
289
290         Request req = getSingletons(1).get(0);
291         assertSame(update, req.getMessage());
292         assertEquals("pdp_1 PdpUpdate", req.getName());
293
294         // broadcast should throw an exception
295         update.setName(null);
296         assertThatIllegalArgumentException().isThrownBy(() -> map.addRequest(update))
297             .withMessageStartingWith("unexpected broadcast message: PdpUpdate");
298     }
299
300     @Test
301     void testAddRequestPdpStateChange() {
302         // null should be ok
303         map.addRequest((PdpStateChange) null);
304
305         map.addRequest(change);
306
307         Request req = getSingletons(1).get(0);
308         assertSame(change, req.getMessage());
309         assertEquals("pdp_1 PdpStateChange", req.getName());
310
311         // broadcast should throw an exception
312         change.setName(null);
313         assertThatIllegalArgumentException().isThrownBy(() -> map.addRequest(change))
314             .withMessageStartingWith("unexpected broadcast message: PdpStateChange");
315     }
316
317     @Test
318     void testAddSingleton() {
319         map.addRequest(change);
320         assertEquals(1, map.nalloc);
321
322         // should have one singleton
323         getSingletons(1);
324
325         // add another request with the same PDP
326         map.addRequest(makeStateChange(PDP1, MY_STATE));
327         assertEquals(1, map.nalloc);
328
329         // should now have another singleton
330         getSingletons(2);
331
332
333         // add another request with a different PDP
334         map.addRequest(makeStateChange(DIFFERENT, MY_STATE));
335
336         // should now have another allocation
337         assertEquals(2, map.nalloc);
338
339         // should now have another singleton
340         getSingletons(3);
341     }
342
343     @Test
344     void testStartNextRequest_NoMore() {
345         map.addRequest(change);
346
347         // indicate success
348         getListener(getSingletons(1).get(0)).success(PDP1, response);
349
350         verify(responseHandler, never()).handlePdpStatus(response);
351
352         /*
353          * the above should have removed the requests so next time should allocate a new
354          * one
355          */
356         map.addRequest(change);
357         assertEquals(2, map.nalloc);
358     }
359
360     @Test
361     void testStartNextRequest_HaveMore() {
362         map.addRequest(update);
363         map.addRequest(change);
364
365         Request updateReq = getSingletons(2).get(0);
366
367         // indicate success with the update
368         when(requests.startNextRequest(updateReq)).thenReturn(true);
369         getListener(updateReq).success(PDP1, response);
370
371         // should be called for the update
372         verify(responseHandler).handlePdpStatus(response);
373
374         // should have started the next request
375         verify(requests).startNextRequest(updateReq);
376
377         /*
378          * requests should still be there, so adding another request should not allocate a
379          * new one
380          */
381         map.addRequest(update);
382         assertEquals(1, map.nalloc);
383     }
384
385     @Test
386     void testRemoveExpiredPdps() {
387         PdpGroup group1 = makeGroup(MY_GROUP);
388         group1.setPdpSubgroups(List.of(makeSubGroup(MY_SUBGROUP, PDP1)));
389
390         PdpGroup group2 = makeGroup(MY_GROUP2);
391         group2.setPdpSubgroups(List.of(makeSubGroup(MY_SUBGROUP, PDP2, PDP3), makeSubGroup(MY_SUBGROUP2, PDP4)));
392
393         // expire all items in group2's first subgroup
394         Instant expired = Instant.now().minusSeconds(EXPIRED_SECONDS);
395         group2.getPdpSubgroups().get(0).getPdpInstances().forEach(pdp -> pdp.setLastUpdate(expired));
396
397         when(pdpGroupService.getFilteredPdpGroups(any())).thenReturn(List.of(group1, group2));
398
399         // run it
400         map.removeExpiredPdps();
401
402         // should have removed from the group
403         List<PdpGroup> groups = getGroupUpdates();
404         assertThat(groups).hasSize(1);
405         assertThat(groups.get(0)).isSameAs(group2);
406         assertThat(group2.getPdpSubgroups()).hasSize(2);
407
408         final Iterator<PdpSubGroup> iter = group2.getPdpSubgroups().iterator();
409
410         PdpSubGroup subgrp = iter.next();
411         assertThat(subgrp.getPdpInstances()).isEmpty();
412         assertThat(subgrp.getCurrentInstanceCount()).isZero();
413
414         subgrp = iter.next();
415         assertThat(subgrp.getPdpInstances()).hasSize(1);
416         assertThat(subgrp.getCurrentInstanceCount()).isEqualTo(1);
417         assertThat(subgrp.getPdpInstances().get(0).getInstanceId()).isEqualTo(PDP4);
418     }
419
420     @Test
421     void testRemoveExpiredPdps_NothingExpired() {
422         PdpGroup group1 = makeGroup(MY_GROUP);
423         group1.setPdpSubgroups(List.of(makeSubGroup(MY_SUBGROUP, PDP1)));
424
425         when(pdpGroupService.getFilteredPdpGroups(any())).thenReturn(List.of(group1));
426
427         // run it
428         map.removeExpiredPdps();
429
430         verify(pdpGroupService, never()).updatePdpGroups(any());
431         verify(publisher, never()).enqueue(any());
432     }
433
434     @Test
435     void testRemoveExpiredPdps_DaoEx() {
436         when(pdpGroupService.getFilteredPdpGroups(any())).thenThrow(makeRuntimeException());
437
438         assertThatCode(map::removeExpiredPdps).doesNotThrowAnyException();
439     }
440
441     @Test
442     void testRemoveExpiredPdps_DaoRtEx() {
443         when(pdpGroupService.getFilteredPdpGroups(any())).thenThrow(makeRuntimeException());
444
445         assertThatCode(map::removeExpiredPdps).doesNotThrowAnyException();
446     }
447
448     @Test
449     void testRemoveFromSubgroup() {
450         PdpGroup group = makeGroup(MY_GROUP);
451         group.setPdpSubgroups(List.of(makeSubGroup(MY_SUBGROUP, PDP1, PDP2, PDP3)));
452
453         // expire pdp1 and pdp3
454         Instant expired = Instant.now().minusSeconds(EXPIRED_SECONDS);
455         List<Pdp> pdps = group.getPdpSubgroups().get(0).getPdpInstances();
456         pdps.get(0).setLastUpdate(expired);
457         pdps.get(2).setLastUpdate(expired);
458         when(pdpGroupService.getFilteredPdpGroups(any())).thenReturn(List.of(group));
459
460         // run it
461         map.removeExpiredPdps();
462
463         // should have removed from the group
464         List<PdpGroup> groups = getGroupUpdates();
465         assertThat(groups).hasSize(1);
466         assertThat(groups.get(0)).isSameAs(group);
467         assertThat(group.getPdpSubgroups()).hasSize(1);
468         assertThat(group.getPdpSubgroups().get(0).getCurrentInstanceCount()).isEqualTo(1);
469
470         pdps = group.getPdpSubgroups().get(0).getPdpInstances();
471         assertThat(pdps).hasSize(1);
472         assertThat(pdps.get(0).getInstanceId()).isEqualTo(PDP2);
473     }
474
475     protected PfModelException makeException() {
476         return new PfModelException(Status.BAD_REQUEST, "expected exception");
477     }
478
479     protected PfModelRuntimeException makeRuntimeException() {
480         return new PfModelRuntimeException(Status.BAD_REQUEST, "expected exception");
481     }
482
483     @Test
484     void testMakePdpRequests() {
485         // this should invoke the real method without throwing an exception
486         PdpModifyRequestMap reqMap =
487             new PdpModifyRequestMap(pdpGroupService, policyStatusService, responseHandler, undeployer, notifier);
488         reqMap.initialize(mapParams);
489         reqMap.addRequest(change);
490
491         QueueToken<PdpMessage> token = queue.poll();
492         assertNotNull(token);
493         assertSame(change, token.get());
494
495         verify(dispatcher).register(eq(change.getRequestId()), any());
496         verify(timers).register(eq(change.getRequestId()), any());
497     }
498
499     @Test
500     void testSingletonListenerFailure() throws Exception {
501         map.addRequest(change);
502
503         // invoke the method
504         invokeFailureHandler();
505
506         verify(undeployer, never()).undeploy(any(), any(), any());
507         verify(requests, never()).stopPublishing();
508
509         // requests should have been removed from the map so this should allocate another
510         map.addRequest(update);
511         assertEquals(2, map.nalloc);
512     }
513
514     /**
515      * Tests Listener.failure() when something has to be undeployed.
516      */
517     @Test
518     void testSingletonListenerFailureUndeploy() throws Exception {
519
520         ToscaConceptIdentifier ident = new ToscaConceptIdentifier("undeployed", "2.3.4");
521         ToscaPolicy policy = mock(ToscaPolicy.class);
522         when(policy.getIdentifier()).thenReturn(ident);
523
524         // add some policies to the update
525         update.setPoliciesToBeDeployed(List.of(policy));
526
527         map.addRequest(update);
528
529         /*
530          * Reconfigure the request when undeploy() is called. Also arrange for undeploy()
531          * to throw an exception.
532          */
533         Request req = getSingletons(1).get(0);
534
535         doAnswer(ans -> {
536             PdpUpdate update2 = new PdpUpdate(update);
537             update2.setPoliciesToBeDeployed(Collections.emptyList());
538             update2.setPoliciesToBeUndeployed(List.of(policy.getIdentifier()));
539             assertTrue(req.reconfigure(update2));
540             throw makeException();
541         }).when(undeployer).undeploy(any(), any(), any());
542
543         // indicate that all policies failed (because response has no policies)
544         response.setName(PDP1);
545         req.setNotifier(notifier);
546         req.checkResponse(response);
547
548         // invoke the method
549         invokeFailureHandler();
550
551         verify(undeployer).undeploy(eq(MY_GROUP), eq(MY_SUBGROUP), undeployCaptor.capture());
552         assertEquals(List.of(ident).toString(), undeployCaptor.getValue().toString());
553
554         // no effect on the map
555         map.addRequest(update);
556         assertEquals(1, map.nalloc);
557     }
558
559     /**
560      * Tests Listener.failure() when something has to be undeployed, but the message
561      * remains unchanged.
562      */
563     @Test
564     void testSingletonListenerFailureUndeployMessageUnchanged() throws Exception {
565
566         ToscaConceptIdentifier ident = new ToscaConceptIdentifier("msg-unchanged", "8.7.6");
567         ToscaPolicy policy = mock(ToscaPolicy.class);
568         when(policy.getIdentifier()).thenReturn(ident);
569
570         // add some policies to the update
571         update.setPoliciesToBeDeployed(List.of(policy));
572
573         map.addRequest(update);
574
575         // indicate that all policies failed (because response has no policies)
576         response.setName(PDP1);
577         Request req = getSingletons(1).get(0);
578         req.setNotifier(notifier);
579         req.checkResponse(response);
580
581         // invoke the method
582         invokeFailureHandler();
583
584         verify(undeployer).undeploy(eq(MY_GROUP), eq(MY_SUBGROUP), undeployCaptor.capture());
585         assertEquals(List.of(ident).toString(), undeployCaptor.getValue().toString());
586
587         // requests should have been removed from the map so this should allocate another
588         map.addRequest(update);
589         assertEquals(2, map.nalloc);
590     }
591
592     @Test
593     void testSingletonListenerSuccess() {
594         map.addRequest(change);
595
596         // invoke the method
597         invokeSuccessHandler();
598
599         verify(requests, never()).stopPublishing();
600
601         // requests should have been removed from the map so this should allocate another
602         map.addRequest(update);
603         assertEquals(2, map.nalloc);
604     }
605
606     @Test
607     void testRequestCompleted_LastRequest() {
608         map.addRequest(change);
609
610         // invoke the method
611         invokeSuccessHandler();
612
613         verify(requests, never()).stopPublishing();
614
615         // requests should have been removed from the map so this should allocate another
616         map.addRequest(update);
617         assertEquals(2, map.nalloc);
618     }
619
620     @Test
621     void testRequestCompleted_NameMismatch() {
622         // use a different name
623         when(requests.getPdpName()).thenReturn(DIFFERENT);
624
625         map.addRequest(change);
626
627         // put the PDP in a group
628         PdpGroup group = makeGroup(MY_GROUP);
629         group.setPdpSubgroups(List.of(makeSubGroup(MY_SUBGROUP, PDP1, DIFFERENT)));
630
631         // invoke the method - with a different name (i.e., PDP1 instead of DIFFERENT)
632         invokeSuccessHandler();
633
634         verify(requests, never()).stopPublishing();
635
636         // no effect on the map
637         map.addRequest(update);
638         assertEquals(1, map.nalloc);
639
640         // no updates
641         verify(pdpGroupService, never()).updatePdpGroups(any());
642     }
643
644     @Test
645     void testRequestCompleted_AlreadyStopped() {
646         map.addRequest(change);
647
648         map.stopPublishing(PDP1);
649
650         // invoke the method
651         invokeSuccessHandler();
652
653         // should have called this a second time
654         verify(requests, times(2)).stopPublishing();
655
656         // requests should have been removed from the map so this should allocate another
657         map.addRequest(update);
658         assertEquals(2, map.nalloc);
659     }
660
661     @Test
662     void testRequestCompleted_NotFirstInQueue() {
663         map.addRequest(change);
664
665         when(requests.isFirstInQueue(any())).thenReturn(false);
666
667         // invoke the method
668         invokeSuccessHandler();
669
670         // should not have called this
671         verify(requests, never()).stopPublishing();
672
673         // no effect on the map
674         map.addRequest(update);
675         assertEquals(1, map.nalloc);
676     }
677
678     @Test
679     void testSingletonListenerRetryCountExhausted() {
680         final var request = map.addRequest(change);
681
682         // invoke the method
683         invokeLastRetryHandler(request);
684
685         verify(requests).stopPublishing();
686     }
687
688
689     /**
690      * Invokes the first request's listener.success() method.
691      */
692     private void invokeSuccessHandler() {
693         getListener(getSingletons(1).get(0)).success(PDP1, response);
694     }
695
696     /**
697      * Invokes the first request's listener.failure() method.
698      */
699     private void invokeFailureHandler() {
700         getListener(getSingletons(1).get(0)).failure(PDP1, MY_REASON);
701     }
702
703     /**
704      * Invokes the first request's listener.retryCountExhausted() method.
705      *
706      * @param request request whose count was exhausted
707      */
708     private void invokeLastRetryHandler(Request request) {
709         getListener(getSingletons(1).get(0)).retryCountExhausted(request);
710     }
711
712     /**
713      * Gets the singleton requests added to {@link #requests}.
714      *
715      * @param count number of singletons expected
716      * @return the singleton requests
717      */
718     private List<Request> getSingletons(int count) {
719         ArgumentCaptor<Request> captor = ArgumentCaptor.forClass(Request.class);
720
721         verify(requests, times(count)).addSingleton(captor.capture());
722         return captor.getAllValues();
723     }
724
725     /**
726      * Gets the listener from a request.
727      *
728      * @param request request of interest
729      * @return the request's listener
730      */
731     private RequestListener getListener(Request request) {
732         return (RequestListener) ReflectionTestUtils.getField(request, "listener");
733     }
734
735     private PdpGroup makeGroup(String name) {
736         PdpGroup group = new PdpGroup();
737
738         group.setName(name);
739
740         return group;
741     }
742
743     private PdpSubGroup makeSubGroup(String pdpType, String... pdpNames) {
744         PdpSubGroup subgroup = new PdpSubGroup();
745
746         subgroup.setPdpType(pdpType);
747         subgroup.setCurrentInstanceCount(pdpNames.length);
748         subgroup.setPdpInstances(Arrays.stream(pdpNames).map(this::makePdp).collect(Collectors.toList()));
749
750         return subgroup;
751     }
752
753     private Pdp makePdp(String pdpName) {
754         Pdp pdp = new Pdp();
755         pdp.setInstanceId(pdpName);
756         pdp.setLastUpdate(Instant.now());
757
758         return pdp;
759     }
760
761     /**
762      * Gets the input to the method.
763      *
764      * @return the input that was passed to the dao.updatePdpGroups() method
765      */
766     private List<PdpGroup> getGroupUpdates() {
767         verify(pdpGroupService).updatePdpGroups(updateCaptor.capture());
768
769         return copyList(updateCaptor.getValue());
770     }
771
772     /**
773      * Copies a list and sorts it by group name.
774      *
775      * @param source source list to copy
776      * @return a copy of the source list
777      */
778     private List<PdpGroup> copyList(List<PdpGroup> source) {
779         List<PdpGroup> newlst = new ArrayList<>(source);
780         newlst.sort(Comparator.comparing(PdpGroup::getName));
781         return newlst;
782     }
783
784     private class MyMap extends PdpModifyRequestMap {
785         /**
786          * Number of times requests were allocated.
787          */
788         private int nalloc = 0;
789
790         public MyMap(PdpModifyRequestMapParams params) {
791             super(pdpGroupService, policyStatusService, responseHandler, undeployer, notifier);
792             super.initialize(params);
793             ;
794         }
795
796         @Override
797         protected PdpRequests makePdpRequests(String pdpName) {
798             ++nalloc;
799             return requests;
800         }
801     }
802 }