Generate notifications when policies change
[policy/pap.git] / main / src / test / java / org / onap / policy / pap / main / rest / depundep / TestProviderBase.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.rest.depundep;
22
23 import static org.assertj.core.api.Assertions.assertThatThrownBy;
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertSame;
26 import static org.junit.Assert.assertTrue;
27 import static org.mockito.Matchers.any;
28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.times;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.LinkedList;
36 import java.util.List;
37 import java.util.Queue;
38 import java.util.TreeSet;
39 import javax.ws.rs.core.Response.Status;
40 import org.junit.AfterClass;
41 import org.junit.Before;
42 import org.junit.Test;
43 import org.mockito.ArgumentCaptor;
44 import org.onap.policy.common.utils.services.Registry;
45 import org.onap.policy.models.base.PfModelException;
46 import org.onap.policy.models.base.PfModelRuntimeException;
47 import org.onap.policy.models.pap.concepts.PdpDeployPolicies;
48 import org.onap.policy.models.pdp.concepts.PdpGroup;
49 import org.onap.policy.models.pdp.concepts.PdpUpdate;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
51 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifierOptVersion;
53 import org.onap.policy.pap.main.notification.PolicyPdpNotificationData;
54 import org.powermock.reflect.Whitebox;
55
56 public class TestProviderBase extends ProviderSuper {
57     private static final String EXPECTED_EXCEPTION = "expected exception";
58
59     private static final String POLICY1_NAME = "policyA";
60     private static final String POLICY1_VERSION = "1.2.3";
61     private static final String GROUP1_NAME = "groupA";
62     private static final String GROUP2_NAME = "groupB";
63     private static final String PDP1_TYPE = "pdpTypeA";
64     private static final String PDP2_TYPE = "pdpTypeB";
65     private static final String PDP3_TYPE = "pdpTypeC";
66     private static final String PDP4_TYPE = "pdpTypeD";
67     private static final String PDP1 = "pdpA";
68     private static final String PDP2 = "pdpB";
69     private static final String PDP3 = "pdpC";
70     private static final String PDP4 = "pdpD";
71
72     private MyProvider prov;
73
74
75     @AfterClass
76     public static void tearDownAfterClass() {
77         Registry.newRegistry();
78     }
79
80     /**
81      * Configures mocks and objects.
82      *
83      * @throws Exception if an error occurs
84      */
85     @Before
86     public void setUp() throws Exception {
87
88         super.setUp();
89
90         when(dao.getFilteredPolicyList(any())).thenReturn(loadPolicies("daoPolicyList.json"));
91
92         prov = new MyProvider();
93     }
94
95     @Test
96     public void testProviderBase() {
97         assertSame(lockit, Whitebox.getInternalState(prov, "updateLock"));
98         assertSame(reqmap, Whitebox.getInternalState(prov, "requestMap"));
99         assertSame(daofact, Whitebox.getInternalState(prov, "daoFactory"));
100     }
101
102     @Test
103     public void testProcess() throws Exception {
104         prov.process(loadRequest(), this::handle);
105
106         assertGroup(getGroupUpdates(), GROUP1_NAME);
107
108         assertUpdate(getUpdateRequests(1), GROUP1_NAME, PDP1_TYPE, PDP1);
109
110         ArgumentCaptor<PolicyPdpNotificationData> captor = ArgumentCaptor.forClass(PolicyPdpNotificationData.class);
111         verify(notifier, times(2)).addDeploymentData(captor.capture());
112         assertNotifier(captor, PDP1, PDP3);
113
114         captor = ArgumentCaptor.forClass(PolicyPdpNotificationData.class);
115         verify(notifier, times(2)).addUndeploymentData(captor.capture());
116         assertNotifier(captor, PDP2, PDP4);
117     }
118
119     private void assertNotifier(ArgumentCaptor<PolicyPdpNotificationData> captor, String firstPdp, String secondPdp) {
120         assertEquals(1, captor.getAllValues().get(0).getPdps().size());
121         assertEquals(1, captor.getAllValues().get(1).getPdps().size());
122
123         // ensure the order by using a TreeSet
124         TreeSet<String> pdps = new TreeSet<>(captor.getAllValues().get(0).getPdps());
125         pdps.addAll(captor.getAllValues().get(1).getPdps());
126         assertEquals("[" + firstPdp + ", " + secondPdp + "]", pdps.toString());
127     }
128
129     @Test
130     public void testProcess_CreateEx() throws Exception {
131         PfModelException ex = new PfModelException(Status.BAD_REQUEST, EXPECTED_EXCEPTION);
132         when(daofact.create()).thenThrow(ex);
133
134         assertThatThrownBy(() -> prov.process(loadEmptyRequest(), this::handle)).isSameAs(ex);
135     }
136
137     @Test
138     public void testProcess_PfRtEx() throws Exception {
139         PfModelRuntimeException ex = new PfModelRuntimeException(Status.BAD_REQUEST, EXPECTED_EXCEPTION);
140         when(daofact.create()).thenThrow(ex);
141
142         assertThatThrownBy(() -> prov.process(loadEmptyRequest(), this::handle)).isSameAs(ex);
143     }
144
145     @Test
146     public void testProcess_RuntimeEx() throws Exception {
147         RuntimeException ex = new RuntimeException(EXPECTED_EXCEPTION);
148         when(daofact.create()).thenThrow(ex);
149
150         assertThatThrownBy(() -> prov.process(loadEmptyRequest(), this::handle)).isInstanceOf(PfModelException.class)
151                         .hasMessage("request failed").hasCause(ex);
152     }
153
154     @Test
155     public void testProcessPolicy_NoGroups() throws Exception {
156         when(dao.getFilteredPdpGroups(any())).thenReturn(Collections.emptyList());
157
158         SessionData session = new SessionData(dao);
159         ToscaPolicyIdentifierOptVersion ident = new ToscaPolicyIdentifierOptVersion(POLICY1_NAME, POLICY1_VERSION);
160         assertThatThrownBy(() -> prov.processPolicy(session, ident)).isInstanceOf(PfModelException.class)
161                         .hasMessage("policy not supported by any PDP group: policyA 1.2.3");
162
163     }
164
165     @Test
166     public void testGetPolicy() throws Exception {
167         PfModelException exc = new PfModelException(Status.CONFLICT, EXPECTED_EXCEPTION);
168         when(dao.getFilteredPolicyList(any())).thenThrow(exc);
169
170         assertThatThrownBy(() -> prov.process(loadRequest(), this::handle)).isInstanceOf(PfModelRuntimeException.class)
171                         .hasCause(exc);
172     }
173
174     @Test
175     public void testGetPolicy_NotFound() throws Exception {
176         when(dao.getFilteredPolicyList(any())).thenReturn(Collections.emptyList());
177
178         assertThatThrownBy(() -> prov.process(loadRequest(), this::handle)).isInstanceOf(PfModelRuntimeException.class)
179                         .hasMessage("cannot find policy: policyA 1.2.3")
180                         .extracting(ex -> ((PfModelRuntimeException) ex).getErrorResponse().getResponseCode())
181                         .isEqualTo(Status.NOT_FOUND);
182     }
183
184     @Test
185     public void testGetGroup() throws Exception {
186         when(dao.getFilteredPdpGroups(any())).thenReturn(loadGroups("getGroupDao.json"))
187                         .thenReturn(loadGroups("groups.json"));
188
189         prov.process(loadRequest(), this::handle);
190
191         assertGroup(getGroupUpdates(), GROUP1_NAME);
192     }
193
194     @Test
195     public void testUpgradeGroup() throws Exception {
196         /*
197          * Each subgroup has a different PDP type and name.
198          *
199          * Type is not supported by the first subgroup.
200          *
201          * Second subgroup matches.
202          *
203          * Third subgroup already contains the policy.
204          *
205          * Last subgroup matches.
206          */
207
208         when(dao.getFilteredPdpGroups(any())).thenReturn(loadGroups("upgradeGroupDao.json"));
209
210         prov.clear();
211         prov.add(false, true, false, true);
212
213         prov.process(loadRequest(), this::handle);
214
215         assertGroup(getGroupUpdates(), GROUP1_NAME);
216
217         List<PdpUpdate> requests = getUpdateRequests(2);
218         assertUpdate(requests, GROUP1_NAME, PDP2_TYPE, PDP2);
219         assertUpdate(requests, GROUP1_NAME, PDP4_TYPE, PDP4);
220     }
221
222     @Test
223     public void testUpgradeGroup_Multiple() throws Exception {
224         /*
225          * Policy data in the DB: policy1=type1, policy2=type2, policy3=type3,
226          * policy4=type1
227          *
228          * Group data in the DB: group1=(type1=pdp1, type3=pdp3) group2=(type2=pdp2)
229          *
230          * Request specifies: policy1, policy2, policy3, policy4
231          *
232          * Should create new versions of group1 and group2.
233          *
234          * Should update old versions of group1 and group2. Should also update new version
235          * of group1 twice.
236          *
237          * Should generate updates to pdp1, pdp2, and pdp3.
238          */
239
240         when(dao.getFilteredPolicyList(any())).thenReturn(loadPolicies("daoPolicyList.json"))
241                         .thenReturn(loadPolicies("upgradeGroupPolicy2.json"))
242                         .thenReturn(loadPolicies("upgradeGroupPolicy3.json"))
243                         .thenReturn(loadPolicies("upgradeGroupPolicy4.json"));
244
245         List<PdpGroup> groups1 = loadGroups("upgradeGroupGroup1.json");
246         List<PdpGroup> groups2 = loadGroups("upgradeGroupGroup2.json");
247
248         /*
249          * these are in pairs of (get-group, get-max-group) matching each policy in the
250          * request
251          */
252         // @formatter:off
253         when(dao.getFilteredPdpGroups(any()))
254             .thenReturn(groups1).thenReturn(groups1)
255             .thenReturn(groups2).thenReturn(groups2)
256             .thenReturn(groups1).thenReturn(groups1)
257             .thenReturn(groups1).thenReturn(groups1);
258         // @formatter:on
259
260         // multiple policies in the request
261         PdpDeployPolicies request = loadFile("updateGroupReqMultiple.json", PdpDeployPolicies.class);
262
263         prov.process(request, (data, deploy) -> {
264             for (ToscaPolicyIdentifierOptVersion policy : deploy.getPolicies()) {
265                 handle(data, policy);
266             }
267         });
268
269         // verify updates
270         List<PdpGroup> changes = getGroupUpdates();
271         assertGroup(changes, GROUP1_NAME);
272         assertGroup(changes, GROUP2_NAME);
273
274         List<PdpUpdate> requests = getUpdateRequests(3);
275         assertUpdateIgnorePolicy(requests, GROUP1_NAME, PDP1_TYPE, PDP1);
276         assertUpdateIgnorePolicy(requests, GROUP2_NAME, PDP2_TYPE, PDP2);
277         assertUpdateIgnorePolicy(requests, GROUP1_NAME, PDP3_TYPE, PDP3);
278     }
279
280     @Test
281     public void testUpgradeGroup_NothingUpdated() throws Exception {
282         prov.clear();
283         prov.add(false);
284
285         prov.process(loadRequest(), this::handle);
286
287         verify(dao, never()).createPdpGroups(any());
288         verify(dao, never()).updatePdpGroups(any());
289         verify(reqmap, never()).addRequest(any(PdpUpdate.class));
290     }
291
292
293     protected void assertUpdate(List<PdpUpdate> updates, String groupName, String pdpType, String pdpName) {
294
295         PdpUpdate update = updates.remove(0);
296
297         assertEquals(groupName, update.getPdpGroup());
298         assertEquals(pdpType, update.getPdpSubgroup());
299         assertEquals(pdpName, update.getName());
300         assertTrue(update.getPolicies().contains(policy1));
301     }
302
303     /**
304      * Loads a standard request.
305      *
306      * @return a standard request
307      */
308     protected ToscaPolicyIdentifierOptVersion loadRequest() {
309         return loadRequest("requestBase.json");
310     }
311
312     /**
313      * Loads a request from a JSON file.
314      *
315      * @param fileName name of the file from which to load
316      * @return the request that was loaded
317      */
318     protected ToscaPolicyIdentifierOptVersion loadRequest(String fileName) {
319         return loadFile(fileName, ToscaPolicyIdentifierOptVersion.class);
320     }
321
322     /**
323      * Loads an empty request.
324      *
325      * @return an empty request
326      */
327     protected ToscaPolicyIdentifierOptVersion loadEmptyRequest() {
328         return loadRequest("emptyRequestBase.json");
329     }
330
331     /**
332      * Handles a request by invoking the provider's processPolicy method.
333      *
334      * @param data session data
335      * @param request request to be handled
336      * @throws PfModelException if an error occurred
337      */
338     private void handle(SessionData data, ToscaPolicyIdentifierOptVersion request) throws PfModelException {
339         prov.processPolicy(data, request);
340     }
341
342
343     private static class MyProvider extends ProviderBase {
344         /**
345          * Used to determine whether or not to make an update when
346          * {@link #makeUpdater(ToscaPolicy)} is called. The updater function removes an
347          * item from this queue each time it is invoked.
348          */
349         private final Queue<Boolean> shouldUpdate = new LinkedList<>();
350
351         /**
352          * Constructs the object and queues up several successful updates.
353          */
354         public MyProvider() {
355             for (int x = 0; x < 10; ++x) {
356                 shouldUpdate.add(true);
357             }
358         }
359
360         public void clear() {
361             shouldUpdate.clear();
362         }
363
364         public void add(Boolean... update) {
365             shouldUpdate.addAll(Arrays.asList(update));
366         }
367
368         @Override
369         protected Updater makeUpdater(SessionData data, ToscaPolicy policy,
370                         ToscaPolicyIdentifierOptVersion desiredPolicy) {
371
372             return (group, subgroup) -> {
373                 if (shouldUpdate.remove()) {
374                     // queue indicated that the update should succeed
375                     subgroup.getPolicies().add(policy.getIdentifier());
376
377                     data.trackDeploy(policy.getIdentifier(), Collections.singleton(PDP1));
378                     data.trackUndeploy(policy.getIdentifier(), Collections.singleton(PDP2));
379
380                     ToscaPolicyIdentifier ident2 = new ToscaPolicyIdentifier(POLICY1_NAME, "9.9.9");
381                     data.trackDeploy(ident2, Collections.singleton(PDP3));
382                     data.trackUndeploy(ident2, Collections.singleton(PDP4));
383                     return true;
384
385                 } else {
386                     // queue indicated that no update should be made this time
387                     return false;
388                 }
389             };
390         }
391     }
392 }