make Logging a service and inject it to SyncRestClient
[vid.git] / vid-app-common / src / test / java / org / onap / vid / aai / AaiClientTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * VID
4  * ================================================================================
5  * Copyright (C) 2017 - 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.vid.aai;
22
23 import static java.util.stream.Collectors.toList;
24 import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
25 import static org.hamcrest.MatcherAssert.assertThat;
26 import static org.hamcrest.Matchers.containsInAnyOrder;
27 import static org.hamcrest.Matchers.containsString;
28 import static org.hamcrest.Matchers.either;
29 import static org.hamcrest.Matchers.hasProperty;
30 import static org.hamcrest.Matchers.instanceOf;
31 import static org.hamcrest.Matchers.is;
32 import static org.hamcrest.Matchers.sameInstance;
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.ArgumentMatchers.anyBoolean;
35 import static org.mockito.ArgumentMatchers.anyString;
36 import static org.mockito.ArgumentMatchers.argThat;
37 import static org.mockito.ArgumentMatchers.eq;
38 import static org.mockito.ArgumentMatchers.isNull;
39 import static org.mockito.ArgumentMatchers.nullable;
40 import static org.mockito.Mockito.mock;
41 import static org.mockito.Mockito.verify;
42 import static org.mockito.Mockito.when;
43 import static org.onap.vid.utils.KotlinUtilsKt.JACKSON_OBJECT_MAPPER;
44 import static org.onap.vid.utils.Unchecked.toURI;
45 import static org.testng.Assert.assertEquals;
46 import static org.testng.Assert.assertFalse;
47 import static org.testng.Assert.assertNull;
48 import static org.testng.Assert.fail;
49
50 import com.fasterxml.jackson.core.JsonProcessingException;
51 import com.fasterxml.jackson.databind.ObjectMapper;
52 import com.google.common.collect.ImmutableList;
53 import java.io.FileNotFoundException;
54 import java.io.IOException;
55 import java.net.URI;
56 import java.security.cert.CertificateException;
57 import java.util.ArrayList;
58 import java.util.Map;
59 import java.util.function.BiConsumer;
60 import java.util.function.Function;
61 import java.util.stream.Stream;
62 import javax.crypto.BadPaddingException;
63 import javax.net.ssl.SSLHandshakeException;
64 import javax.servlet.ServletContext;
65 import javax.ws.rs.ProcessingException;
66 import javax.ws.rs.client.Client;
67 import javax.ws.rs.core.Response;
68 import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
69 import org.apache.commons.lang3.builder.ToStringStyle;
70 import org.apache.commons.lang3.exception.ExceptionUtils;
71 import org.apache.commons.lang3.reflect.FieldUtils;
72 import org.apache.commons.lang3.tuple.Pair;
73 import org.apache.http.HttpStatus;
74 import org.mockito.Mockito;
75 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
76 import org.onap.portalsdk.core.util.SystemProperties;
77 import org.onap.vid.aai.model.AaiGetTenatns.GetTenantsResponse;
78 import org.onap.vid.aai.model.CustomQuerySimpleResult;
79 import org.onap.vid.aai.model.ModelVer;
80 import org.onap.vid.aai.model.ModelVersions;
81 import org.onap.vid.aai.model.PortDetailsTranslator;
82 import org.onap.vid.aai.model.Properties;
83 import org.onap.vid.aai.model.RelatedToProperty;
84 import org.onap.vid.aai.model.ResourceType;
85 import org.onap.vid.aai.model.SimpleResult;
86 import org.onap.vid.aai.util.AAIRestInterface;
87 import org.onap.vid.aai.util.CacheProvider;
88 import org.onap.vid.aai.util.HttpsAuthClient;
89 import org.onap.vid.aai.util.ServletRequestHelper;
90 import org.onap.vid.aai.util.SystemPropertyHelper;
91 import org.onap.vid.controller.LocalWebConfig;
92 import org.onap.vid.exceptions.GenericUncheckedException;
93 import org.onap.vid.model.Subscriber;
94 import org.onap.vid.model.SubscriberList;
95 import org.onap.vid.model.probes.ExternalComponentStatus;
96 import org.onap.vid.model.probes.HttpRequestMetadata;
97 import org.onap.vid.model.probes.StatusMetadata;
98 import org.onap.vid.testUtils.TestUtils;
99 import org.onap.vid.utils.Logging;
100 import org.onap.vid.utils.Unchecked;
101 import org.springframework.http.HttpMethod;
102 import org.springframework.test.context.ContextConfiguration;
103 import org.springframework.test.context.web.WebAppConfiguration;
104 import org.testng.Assert;
105 import org.testng.annotations.BeforeMethod;
106 import org.testng.annotations.DataProvider;
107 import org.testng.annotations.Test;
108 import sun.security.provider.certpath.SunCertPathBuilderException;
109 import sun.security.validator.ValidatorException;
110
111 @ContextConfiguration(classes = {LocalWebConfig.class, SystemProperties.class})
112 @WebAppConfiguration
113 public class AaiClientTest {
114
115     private final String NO_LCP_REGION_AND_TENANTS_MSG = "A&AI has no LCP Region & Tenants associated to subscriber 'subscriberId' and service type 'serviceType'";
116     private AaiClient aaiClientMock;
117     private ServletContext servletContext;
118
119     @BeforeMethod
120     public void initMocks(){
121         aaiClientMock = mock(AaiClient.class);
122         aaiClientMock.logger = mock(EELFLoggerDelegate.class);
123         aaiClientMock.objectMapper = new ObjectMapper();
124         servletContext = mock(ServletContext.class);
125
126         when(servletContext.getRealPath(any(String.class))).thenReturn("");
127
128         when(aaiClientMock.doAaiGet(any(String.class),any(Boolean.class))).thenReturn(null);
129         when(aaiClientMock.doAaiGet(any(URI.class), anyBoolean(), anyBoolean())).thenReturn(null);
130     }
131
132     @DataProvider
133     public static Object[][] logicalLinkData() {
134         return new Object[][] {
135                 {"", "network/logical-links/logical-link/"},
136                 {"link", "network/logical-links/logical-link/link"}
137         };
138     }
139
140     @Test(dataProvider = "logicalLinkData")
141     public void getLogicalLink_Link_Is_Empty(String link, String expectedUrl) {
142
143         when(aaiClientMock.getLogicalLink(any(String.class))).thenCallRealMethod();
144         aaiClientMock.getLogicalLink(link);
145         verify(aaiClientMock).doAaiGet(argThat(s -> equalsIgnoreCase(s, expectedUrl)),any(Boolean.class));
146     }
147
148     @DataProvider
149     public static Object[][] subscribersResults() {
150         return new Object[][] {
151                 {new SubscriberList(new ArrayList<Subscriber>() {{ add(new Subscriber());  add(new Subscriber()); }}), true},
152                 {new SubscriberList(new ArrayList<Subscriber>() {{ add(new Subscriber()); }}), true},
153                 {new SubscriberList(new ArrayList<Subscriber>()), false}
154         };
155     }
156
157     @Test(dataProvider = "subscribersResults")
158     public void testProbeAaiGetAllSubscribers_returnsTwoToZeroSubscribers_ResultsAsExpected(SubscriberList subscribers, boolean isAvailable){
159         ExternalComponentStatus expectedStatus = new ExternalComponentStatus(ExternalComponentStatus.Component.AAI,isAvailable, new HttpRequestMetadata(
160                 HttpMethod.GET,
161                 200,
162                 "url",
163                 "rawData",
164                 isAvailable ? "OK" : "No subscriber received",
165                 0
166         ));
167         Mockito.when(aaiClientMock.getAllSubscribers(true)).thenReturn(
168                 new AaiResponseWithRequestInfo<>(
169                         HttpMethod.GET, "url", new AaiResponse<>(subscribers, null, 200),
170                         "rawData"));
171         Mockito.when(aaiClientMock.probeComponent()).thenCallRealMethod();
172         ExternalComponentStatus result  = aaiClientMock.probeComponent();
173         assertThat(statusDataReflected(result),is(statusDataReflected(expectedStatus)));
174         assertThat(requestMetadataReflected(result.getMetadata()),is(requestMetadataReflected(expectedStatus.getMetadata())));
175     }
176
177     @Test(expectedExceptions = Exception.class)
178     public void typedAaiGet_aaiRestInterfaceRestGetReturnsError_exceptionIsThrown() {
179         AAIRestInterface aaiRestInterface = mock(AAIRestInterface.class);
180         final ResponseWithRequestInfo responseWithRequestInfo = mockedResponseWithRequestInfo(Response.Status.INTERNAL_SERVER_ERROR, "entity");
181         mockForGetRequest(aaiRestInterface, responseWithRequestInfo);
182         final AaiClient aaiClient = new AaiClient(aaiRestInterface, null, null);
183
184         try {
185
186             aaiClient.typedAaiGet(toURI("/irrelevant/url"), RelatedToProperty.class);
187
188         } catch (Exception e) {
189             assertThat(ExceptionUtils.getStackTrace(e), e, instanceOf(ExceptionWithRequestInfo.class));
190             ExceptionWithRequestInfo e2 = ((ExceptionWithRequestInfo) e);
191             assertThat(e2.getHttpCode(), is(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()));
192             assertThat(e2.getRawData(), is("entity"));
193             assertThat(e2.getHttpMethod(), is(HttpMethod.GET));
194             assertThat(e2.getRequestedUrl(), is("/my/mocked/url"));
195
196             throw e;
197         }
198     }
199
200     @Test(expectedExceptions = Exception.class)
201     public void typedAaiGet_aaiRestInterfaceRestGetReturnsInparsableResponse_exceptionIsThrown() {
202         AAIRestInterface aaiRestInterface = mock(AAIRestInterface.class);
203         final ResponseWithRequestInfo responseWithRequestInfo = mockedResponseWithRequestInfo(Response.Status.OK, "entity");
204         mockForGetRequest(aaiRestInterface, responseWithRequestInfo);
205         final AaiClient aaiClient = new AaiClient(aaiRestInterface, null, null);
206
207         try {
208
209             aaiClient.typedAaiGet(toURI("/irrelevant/url"), RelatedToProperty.class);
210
211         } catch (Exception e) {
212             assertThat(ExceptionUtils.getStackTrace(e), e, instanceOf(ExceptionWithRequestInfo.class));
213             assertThat(e.getCause(),
214                     hasProperty("cause", is(instanceOf(com.fasterxml.jackson.core.JsonParseException.class)))
215             );
216             throw e;
217         }
218     }
219
220     @Test
221     public void typedAaiGet_aaiRestInterfaceRestGetReturns_objectIsFine() {
222         AAIRestInterface aaiRestInterface = mock(AAIRestInterface.class);
223         final ResponseWithRequestInfo responseWithRequestInfo = mockedResponseWithRequestInfo(Response.Status.OK,
224                 "{ \"property-key\": \"foo\", \"property-value\": \"bar\" }");
225         mockForGetRequest(aaiRestInterface, responseWithRequestInfo);
226
227         final AaiClient aaiClient = new AaiClient(aaiRestInterface, null, null);
228
229         final RelatedToProperty relatedToPropertyAaiResponse = aaiClient.typedAaiGet(toURI("/irrelevant/url"), RelatedToProperty.class);
230
231         assertThat(relatedToPropertyAaiResponse.getPropertyKey(), is("foo"));
232         assertThat(relatedToPropertyAaiResponse.getPropertyValue(), is("bar"));
233     }
234
235     private ResponseWithRequestInfo mockedResponseWithRequestInfo(Response.Status status, String entity) {
236         return mockedResponseWithRequestInfo(status, entity, "/my/mocked/url", HttpMethod.GET);
237     }
238
239     private ResponseWithRequestInfo mockedResponseWithRequestInfo(Response.Status status, String entity, String requestUrl, HttpMethod method) {
240         final Response mockResponse = mock(Response.class);
241         when(mockResponse.getStatus()).thenReturn(status.getStatusCode());
242         when(mockResponse.getStatusInfo()).thenReturn(status);
243         when(mockResponse.readEntity(String.class)).thenReturn(entity);
244         return new ResponseWithRequestInfo(mockResponse, requestUrl, method);
245     }
246
247     //serialize fields except of fields we cannot know ahead of time
248     private static String requestMetadataReflected(StatusMetadata metadata) {
249         return new ReflectionToStringBuilder(metadata, ToStringStyle.SHORT_PREFIX_STYLE)
250                 .setExcludeFieldNames("duration")
251                 .toString();
252     }
253
254     private static String statusDataReflected(ExternalComponentStatus status) {
255         return new ReflectionToStringBuilder(status, ToStringStyle.SHORT_PREFIX_STYLE)
256                 .setExcludeFieldNames("metadata")
257                 .toString();
258     }
259
260     @DataProvider
261     public static Object[][] rawData() {
262         return new Object[][]{
263                 {"errorMessage", }, {""}, {null}
264         };
265     }
266
267     @Test(dataProvider = "rawData")
268     public void testProbeAaiGetFullSubscribersWithNullResponse_returnsNotAvailableWithErrorRawData(String rawData){
269         Mockito.when(aaiClientMock.getAllSubscribers(true)).thenReturn(
270                 new AaiResponseWithRequestInfo<>(HttpMethod.GET, "url", null,
271                         rawData));
272         ExternalComponentStatus result = callProbeAaiGetAllSubscribersAndAssertNotAvailable();
273         assertThat(result.getMetadata(), instanceOf(HttpRequestMetadata.class));
274         assertEquals(((HttpRequestMetadata) result.getMetadata()).getRawData(), rawData);
275     }
276
277     @DataProvider
278     public static Object[][] exceptions() {
279         return new Object[][] {
280                 {"NullPointerException", "errorMessage",
281                         new ExceptionWithRequestInfo(HttpMethod.GET, "url",
282                                 "errorMessage", null, new NullPointerException())},
283                 {"RuntimeException", null,
284                         new ExceptionWithRequestInfo(HttpMethod.GET, "url",
285                                 null, null, new RuntimeException())},
286                 {"RuntimeException", null,
287                         new RuntimeException()},
288         };
289     }
290
291     @Test(dataProvider = "exceptions")
292     public void testProbeAaiGetFullSubscribersWithNullResponse_returnsNotAvailableWithErrorRawData(String description, String expectedRawData, Exception exception){
293         Mockito.when(aaiClientMock.getAllSubscribers(true)).thenThrow(exception);
294         ExternalComponentStatus result = callProbeAaiGetAllSubscribersAndAssertNotAvailable();
295         if (exception instanceof ExceptionWithRequestInfo) {
296             assertThat(result.getMetadata(), instanceOf(HttpRequestMetadata.class));
297             assertEquals(((HttpRequestMetadata) result.getMetadata()).getRawData(), expectedRawData);
298         }
299         assertThat(result.getMetadata().getDescription(), containsString(description));
300     }
301
302     private ExternalComponentStatus callProbeAaiGetAllSubscribersAndAssertNotAvailable() {
303         Mockito.when(aaiClientMock.probeComponent()).thenCallRealMethod();
304         ExternalComponentStatus result  = aaiClientMock.probeComponent();
305         assertFalse(result.isAvailable());
306         return result;
307     }
308
309
310     @Test
311     public void getTenants_Arguments_Are_Null_Or_Empty() {
312
313         when(aaiClientMock.getTenants(any(), any())).thenCallRealMethod();
314
315         AaiResponse response = aaiClientMock.getTenants("", "");
316
317         assertEquals(response.getErrorMessage(), "{\"statusText\":\" Failed to retrieve LCP Region & Tenants from A&AI, Subscriber ID or Service Type is missing.\"}");
318
319
320         response = aaiClientMock.getTenants(null, null);
321
322         assertEquals(response.getErrorMessage(), "{\"statusText\":\" Failed to retrieve LCP Region & Tenants from A&AI, Subscriber ID or Service Type is missing.\"}");
323     }
324
325     @Test(expectedExceptions = AaiClient.ParsingGetTenantsResponseFailure.class, expectedExceptionsMessageRegExp = NO_LCP_REGION_AND_TENANTS_MSG)
326     public void getTenants_Arguments_Are_Valid_But_Tenants_Not_Exist() {
327
328         when(aaiClientMock.getTenantsNonCached(any(String.class),any(String.class))).thenCallRealMethod();
329
330         Response generalEmptyResponse = mock(Response.class);
331         when(aaiClientMock.doAaiGet(any(String.class),any(Boolean.class))).thenReturn(generalEmptyResponse);
332
333         aaiClientMock.getTenantsNonCached("subscriberId", "serviceType");
334     }
335
336     @Test
337     public void whenCacheThrowException_thenGetTenantReturnAaiResponse() {
338         CacheProvider mockCacheProvider = mock(CacheProvider.class);
339         CacheProvider.Cache mockCache = mock(CacheProvider.Cache.class);
340         AaiClient aaiClientUnderTest = new AaiClient(null, null, mockCacheProvider);
341
342         when(mockCacheProvider.aaiClientCacheFor(any(), any())).thenReturn(mockCache);
343         when(mockCache.get(any())).thenThrow(new AaiClient.ParsingGetTenantsResponseFailure(NO_LCP_REGION_AND_TENANTS_MSG));
344         AaiResponse aaiResponse = aaiClientUnderTest.getTenants("subscriberId", "serviceType");
345         assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, aaiResponse.getHttpCode());
346         assertEquals("{\"statusText\":\""+NO_LCP_REGION_AND_TENANTS_MSG+"\"}", aaiResponse.getErrorMessage());
347     }
348
349     @Test
350     public void getTenants_Arguments_Are_Valid_Get_The_Tenanats() {
351
352         when(aaiClientMock.getTenantsNonCached(any(String.class),any(String.class))).thenCallRealMethod();
353
354
355         Response generalEmptyResponse = mock(Response.class);
356
357         when(generalEmptyResponse.readEntity(String.class)).thenReturn(tenantResponseRaw);
358         when(generalEmptyResponse.getStatus()).thenReturn(200);
359         when(generalEmptyResponse.getStatusInfo()).thenReturn(Response.Status.OK);
360
361
362         when(aaiClientMock.doAaiGet(any(String.class),any(Boolean.class))).thenReturn(generalEmptyResponse);
363
364         AaiResponse<GetTenantsResponse[]> response = aaiClientMock.getTenantsNonCached("subscriberId", "serviceType");
365
366         GetTenantsResponse[] tenants = response.getT();
367
368         Assert.assertTrue(response.t.length> 0);
369
370         Assert.assertEquals(tenants[0].cloudOwner,"irma-aic-cloud-owner");
371     }
372
373     final String tenantResponseRaw ="" +
374             "{" +
375             "\"service-type\": \"VIRTUAL USP\"," +
376             "\"resource-version\": \"1494001841964\"," +
377             "\"relationship-list\": {" +
378             "\"relationship\": [{" +
379             "\"related-to\": \"tenant\"," +
380             "\"related-link\": \"/aai/v11/cloud-infrastructure/cloud-regions/cloud-region/irma-aic/AAIAIC25/tenants/tenant/092eb9e8e4b7412e8787dd091bc58e86\"," +
381             "\"relationship-data\": [{" +
382             "\"relationship-key\": \"cloud-region.cloud-owner\"," +
383             "\"relationship-value\": \"irma-aic-cloud-owner\"" +
384             "}," +
385             "{" +
386             "\"relationship-key\": \"cloud-region.cloud-region-id\"," +
387             "\"relationship-value\": \"AAIAIC25\"" +
388             "}," +
389             "{" +
390             "\"relationship-key\": \"tenant.tenant-id\"," +
391             "\"relationship-value\": \"092eb9e8e4b7412e8787dd091bc58e86\"" +
392             "}" +
393             "]," +
394             "\"related-to-property\": [{" +
395             "\"property-key\": \"tenant.tenant-name\"," +
396             "\"property-value\": \"USP-SIP-IC-24335-T-01\"" +
397             "}]" +
398             "}]" +
399             "}" +
400             "}";
401
402     final String vfModuleHomingResponseRaw ="{" +
403             "    \"vf-module-id\": \"ed02354a-3217-45ce-a1cd-e0b69b7a8cea\"," +
404             "    \"vf-module-name\": \"apndns_az_02_module_1\"," +
405             "    \"heat-stack-id\": \"apndns_az_02_module_1/97a319f3-b095-4fff-befa-c657508ecaf8\"," +
406             "    \"orchestration-status\": \"active\"," +
407             "    \"is-base-vf-module\": false," +
408             "    \"resource-version\": \"1530559380383\"," +
409             "    \"model-invariant-id\": \"74450b48-0aa0-4743-8314-9163e92b7862\"," +
410             "    \"model-version-id\": \"6bc01a2b-bc48-4991-b9fe-e22c2215d801\"," +
411             "    \"model-customization-id\": \"74f638c2-0368-4212-8f73-e961005af17c\"," +
412             "    \"module-index\": 0," +
413             "    \"relationship-list\": {" +
414             "        \"relationship\": [" +
415             "            {" +
416             "                \"related-to\": \"l3-network\"," +
417             "                \"relationship-label\": \"org.onap.relationships.inventory.DependsOn\"," +
418             "                \"related-link\": \"/aai/v12/network/l3-networks/l3-network/335e62be-73a3-41e8-930b-1a677bcafea5\"," +
419             "                \"relationship-data\": [" +
420             "                    {" +
421             "                        \"relationship-key\": \"l3-network.network-id\"," +
422             "                        \"relationship-value\": \"335e62be-73a3-41e8-930b-1a677bcafea5\"" +
423             "                    }" +
424             "                ]," +
425             "                \"related-to-property\": [" +
426             "                    {" +
427             "                        \"property-key\": \"l3-network.network-name\"," +
428             "                        \"property-value\": \"MNS-FN-25180-T-02Shared_oam_protected_net_1\"" +
429             "                    }" +
430             "                ]" +
431             "            }," +
432             "            {" +
433             "                \"related-to\": \"l3-network\"," +
434             "                \"relationship-label\": \"org.onap.relationships.inventory.DependsOn\"," +
435             "                \"related-link\": \"/aai/v12/network/l3-networks/l3-network/2db4ee3e-2ac7-4fc3-8739-ecf53416459e\"," +
436             "                \"relationship-data\": [" +
437             "                    {" +
438             "                        \"relationship-key\": \"l3-network.network-id\"," +
439             "                        \"relationship-value\": \"2db4ee3e-2ac7-4fc3-8739-ecf53416459e\"" +
440             "                    }" +
441             "                ]," +
442             "                \"related-to-property\": [" +
443             "                    {" +
444             "                        \"property-key\": \"l3-network.network-name\"," +
445             "                        \"property-value\": \"Mobisupport-FN-27099-T-02_int_apn_dns_net_1\"" +
446             "                    }" +
447             "                ]" +
448             "            }," +
449             "            {" +
450             "                \"related-to\": \"volume-group\"," +
451             "                \"relationship-label\": \"org.onap.relationships.inventory.Uses\"," +
452             "                \"related-link\": \"/aai/v12/cloud-infrastructure/cloud-regions/cloud-region/irma-aic/rdm5b/volume-groups/volume-group/66013ebe-0c81-44b9-a24f-7c6acba73a39\"," +
453             "                \"relationship-data\": [" +
454             "                    {" +
455             "                        \"relationship-key\": \"cloud-region.cloud-owner\"," +
456             "                        \"relationship-value\": \"irma-aic\"" +
457             "                    }," +
458             "                    {" +
459             "                        \"relationship-key\": \"cloud-region.cloud-region-id\"," +
460             "                        \"relationship-value\": \"rdm5b\"" +
461             "                    }," +
462             "                    {" +
463             "                        \"relationship-key\": \"volume-group.volume-group-id\"," +
464             "                        \"relationship-value\": \"66013ebe-0c81-44b9-a24f-7c6acba73a39\"" +
465             "                    }" +
466             "                ]" +
467             "            }," +
468             "            {" +
469             "                \"related-to\": \"vserver\"," +
470             "                \"relationship-label\": \"org.onap.relationships.inventory.Uses\"," +
471             "                \"related-link\": \"/aai/v12/cloud-infrastructure/cloud-regions/cloud-region/irma-aic/rdm5b/tenants/tenant/db1818f7f2e34862b378bfb2cc520f91/vservers/vserver/5eef9f6d-9933-4bc6-9a1a-862d61309437\"," +
472             "                \"relationship-data\": [" +
473             "                    {" +
474             "                        \"relationship-key\": \"cloud-region.cloud-owner\"," +
475             "                        \"relationship-value\": \"irma-aic\"" +
476             "                    }," +
477             "                    {" +
478             "                        \"relationship-key\": \"cloud-region.cloud-region-id\"," +
479             "                        \"relationship-value\": \"rdm5b\"" +
480             "                    }," +
481             "                    {" +
482             "                        \"relationship-key\": \"tenant.tenant-id\"," +
483             "                        \"relationship-value\": \"db1818f7f2e34862b378bfb2cc520f91\"" +
484             "                    }," +
485             "                    {" +
486             "                        \"relationship-key\": \"vserver.vserver-id\"," +
487             "                        \"relationship-value\": \"5eef9f6d-9933-4bc6-9a1a-862d61309437\"" +
488             "                    }" +
489             "                ]," +
490             "                \"related-to-property\": [" +
491             "                    {" +
492             "                        \"property-key\": \"vserver.vserver-name\"," +
493             "                        \"property-value\": \"zrdm5bfapn01dns002\"" +
494             "                    }" +
495             "                ]" +
496             "            }" +
497             "        ]" +
498             "    }" +
499             "}";
500     @Test
501     public void get_homingDataForVfModule() {
502         when(aaiClientMock.getHomingDataByVfModule(any(String.class), any(String.class))).thenCallRealMethod();
503
504         Response homingResponse = mock(Response.class);
505
506         when(homingResponse.readEntity(String.class)).thenReturn(vfModuleHomingResponseRaw);
507         when(homingResponse.getStatus()).thenReturn(200);
508         when(homingResponse.getStatusInfo()).thenReturn(Response.Status.OK);
509
510
511         when(aaiClientMock.doAaiGet(any(String.class), any(Boolean.class))).thenReturn(homingResponse);
512
513         GetTenantsResponse tenant = aaiClientMock.getHomingDataByVfModule("vnfInstanceId", "vfModuleId");
514
515         Assert.assertEquals(tenant.cloudOwner,"irma-aic" );
516         Assert.assertEquals(tenant.cloudRegionID,"rdm5b");
517         Assert.assertEquals(tenant.tenantID,"db1818f7f2e34862b378bfb2cc520f91");
518
519     }
520     @Test(expectedExceptions = GenericUncheckedException.class, expectedExceptionsMessageRegExp = "A&AI has no homing data associated to vfModule 'vfModuleId' of vnf 'vnfInstanceId'")
521     public void getVfMoudule_Homing_Arguments_Are_Valid_But_Not_Exists() {
522         when(aaiClientMock.getHomingDataByVfModule(any(String.class), any(String.class))).thenCallRealMethod();
523
524         Response generalEmptyResponse = mock(Response.class);
525         when(aaiClientMock.doAaiGet(any(String.class),any(Boolean.class))).thenReturn(generalEmptyResponse);
526
527         aaiClientMock.getHomingDataByVfModule("vnfInstanceId", "vfModuleId");
528     }
529
530     @DataProvider
531     public static Object[][] invalidDataId() {
532         return new String[][] {
533                 {""},
534                 {null}
535         };
536     }
537
538     @Test(dataProvider = "invalidDataId", expectedExceptions = GenericUncheckedException.class, expectedExceptionsMessageRegExp = "Failed to retrieve homing data associated to vfModule from A&AI, VNF InstanceId or VF Module Id is missing.")
539     public void getVfMoudule_Homing_Arguments_Are_Empty_Or_Null(String data) {
540         when(aaiClientMock.getHomingDataByVfModule(any(), any())).thenCallRealMethod();
541              aaiClientMock.getHomingDataByVfModule(data, data);
542     }
543
544     @DataProvider
545     public static Object[][] resourceTypesProvider() {
546         return new Object[][] {
547                 {"service-instance", ResourceType.SERVICE_INSTANCE},
548                 {"generic-vnf", ResourceType.GENERIC_VNF},
549                 {"vf-module", ResourceType.VF_MODULE}
550         };
551     }
552
553     @DataProvider
554     public static Object[][] nameAndResourceTypeProvider() {
555         return new Object[][] {
556                 {"SRIOV_SVC", ResourceType.SERVICE_INSTANCE, "nodes/service-instances?service-instance-name=SRIOV_SVC"},
557                 {"b1707vidnf", ResourceType.GENERIC_VNF, "nodes/generic-vnfs?vnf-name=b1707vidnf"},
558                 {"connectivity_test", ResourceType.VF_MODULE, "nodes/vf-modules?vf-module-name=connectivity_test"},
559                 {"ByronPace", ResourceType.INSTANCE_GROUP, "nodes/instance-groups?instance-group-name=ByronPace"},
560                 {"MjVg1234", ResourceType.VOLUME_GROUP, "nodes/volume-groups?volume-group-name=MjVg1234"}
561         };
562     }
563
564     @Test(dataProvider = "nameAndResourceTypeProvider")
565     public void whenSearchNodeTypeByName_callRightAaiPath(String name, ResourceType type, String expectedUrl) {
566         AAIRestInterface aaiRestInterface = mock(AAIRestInterface.class);
567         ResponseWithRequestInfo responseWithRequestInfo = mockedResponseWithRequestInfo(Response.Status.OK, "{}");
568
569         when(aaiRestInterface.RestGet(anyString(), anyString(), eq(toURI(expectedUrl)), anyBoolean(), anyBoolean()))
570                 .thenReturn(responseWithRequestInfo);
571
572         AaiClient aaiClient = new AaiClient(aaiRestInterface, null, null);
573
574         aaiClient.isNodeTypeExistsByName(name, type);
575     }
576
577     @DataProvider
578     public static Object[][] aaiClientInternalExceptions() {
579         return Stream.<Pair<Class<? extends Throwable>, UncheckedBiConsumer<HttpsAuthClient, Client>>>of(
580
581                 // Exception out of httpsAuthClientMock
582                 Pair.of(CertificateException.class, (httpsAuthClientMock, javaxClientMock) -> {
583                     final CertificateException e0 = new CertificateException("No X509TrustManager implementation available");
584                     SSLHandshakeException e = new SSLHandshakeException(e0.toString());
585                     e.initCause(e0);
586
587                     when(httpsAuthClientMock.getClient(any())).thenThrow(e);
588                 }),
589
590                 Pair.of(StringIndexOutOfBoundsException.class, mockExceptionOnClientProvider(new StringIndexOutOfBoundsException(4))),
591
592                 Pair.of(NullPointerException.class, mockExceptionOnClientProvider(new NullPointerException("null"))),
593
594                 Pair.of(FileNotFoundException.class, mockExceptionOnClientProvider(new FileNotFoundException("vid/WEB-INF/cert/aai-client-cert.p12"))),
595
596                 Pair.of(BadPaddingException.class, mockExceptionOnClientProvider(
597                         new IOException("keystore password was incorrect", new BadPaddingException("Given final block not properly padded")))
598                 ),
599                 Pair.of(GenericUncheckedException.class, mockExceptionOnClientProvider(new GenericUncheckedException("basa"))),
600
601                 Pair.of(NullPointerException.class, (httpsAuthClientMock, javaxClientMock) ->
602                         when(httpsAuthClientMock.getClient(any())).thenReturn(null)),
603
604
605                 // Exception out of javax's Client
606                 Pair.of(SSLHandshakeException.class, (httpsAuthClientMock, javaxClientMock) -> {
607                     when(javaxClientMock.target(nullable(String.class))).thenThrow(
608                             new ProcessingException(new SSLHandshakeException("Received fatal alert: certificate_expired"))
609                     );
610                 }),
611
612                 Pair.of(SunCertPathBuilderException.class, (httpsAuthClientMock, javaxClientMock) -> {
613                     SunCertPathBuilderException e0 = new SunCertPathBuilderException("unable to find valid certification path to requested target");
614                     when(javaxClientMock.target(nullable(String.class))).thenThrow(
615                             new ProcessingException(new ValidatorException("PKIX path building failed: " + e0.toString(), e0))
616                     );
617                 }),
618
619                 Pair.of(GenericUncheckedException.class, (httpsAuthClientMock, javaxClientMock) ->
620                         when(javaxClientMock.target(nullable(String.class))).thenThrow(new GenericUncheckedException("basa")))
621
622         ).flatMap(l -> Stream.of(
623                 // double each case to propagateExceptions = true/false, to verify that "don't propagate" really still work
624                 ImmutableList.of(l.getLeft(), l.getRight(), true).toArray(),
625                 ImmutableList.of(l.getLeft(), l.getRight(), false).toArray()
626         )).collect(toList()).toArray(new Object[][]{});
627     }
628
629     private static UncheckedBiConsumer<HttpsAuthClient, Client> mockExceptionOnClientProvider(Exception e) {
630         return (httpsAuthClientMock, javaxClientMock) ->
631                 when(httpsAuthClientMock.getClient(any())).thenThrow(e);
632     }
633
634     @Test(dataProvider = "aaiClientInternalExceptions")
635     public void propagateExceptions_internalsThrowException_ExceptionRethrown(Class<? extends Throwable> expectedType, BiConsumer<HttpsAuthClient, Client> setupMocks, boolean propagateExceptions) throws Exception {
636         /*
637         Call chain is like:
638             this test -> AaiClient -> AAIRestInterface -> HttpsAuthClient -> javax's Client
639
640         In this test, *AaiClient* and *AAIRestInterface* are under test (actual
641         implementation is used), while HttpsAuthClient and the javax's Client are
642         mocked to return pseudo-responses or - better- throw exceptions.
643          */
644
645         // prepare mocks
646         HttpsAuthClient httpsAuthClientMock = mock(HttpsAuthClient.class);
647         TestUtils.JavaxRsClientMocks mocks = new TestUtils.JavaxRsClientMocks();
648         Client javaxClientMock = mocks.getFakeClient();
649         Response responseMock = mocks.getFakeResponse();
650
651         // prepare real AAIRestInterface and AaiClient, and wire mocks
652         AAIRestInterface aaiRestInterface = new AAIRestInterface(httpsAuthClientMock,
653             mock(ServletRequestHelper.class),
654             mock(SystemPropertyHelper.class),
655             mock(Logging.class));
656         final AaiClient aaiClient = new AaiClient(aaiRestInterface, null, null);
657         when(httpsAuthClientMock.getClient(any())).thenReturn(javaxClientMock);
658
659         // define atomic method under test, including reset of "aaiRestInterface.client"
660         final Function<Boolean, Response> doAaiGet = (propagateExceptions1) -> {
661             try {
662                 FieldUtils.writeField(aaiRestInterface, "client", null, true);
663                 return aaiClient.doAaiGet("uri", false, propagateExceptions1).getResponse();
664             } catch (IllegalAccessException e) {
665                 throw new RuntimeException(e);
666             }
667         };
668
669         // verify setup again
670         assertThat("mocks setup should make doAaiGet return our responseMock", doAaiGet.apply(true), is(sameInstance(responseMock)));
671
672
673         /// TEST:
674         setupMocks.accept(httpsAuthClientMock, javaxClientMock);
675
676         try {
677             final Response response = doAaiGet.apply(propagateExceptions);
678         } catch (Exception e) {
679             if (propagateExceptions) {
680                 assertThat("root cause incorrect for " + ExceptionUtils.getStackTrace(e), ExceptionUtils.getRootCause(e), instanceOf(expectedType));
681                 return; // ok, done
682             } else {
683                 // Verify that "don't propagate" really still work
684                 Assert.fail("calling doAaiGet when propagateExceptions is false must result with no exception", e);
685             }
686         }
687
688         // If no exception caught
689         // We're asserting that the legacy behaviour is still in place. Hopefully
690         // one day we will remove the non-propagateExceptions case
691         assertFalse(propagateExceptions, "calling doAaiGet when propagateExceptions is 'true' must result with an exception (in this test)");
692     }
693
694     @DataProvider
695     public static Object[][] aaiClientGetCloudOwnerByCloudRegionId() {
696
697         final String cloudRegion = "{" +
698                 "      \"cloud-owner\": \"mure-royo-ru22\"," +
699                 "      \"cloud-region-id\": \"ravitu\"," +
700                 "      \"cloud-type\": \"openstack\"," +
701                 "      \"resource-version\": \"1523631256125\"," +
702                 "      \"relationship-list\": {" +
703                 "        \"relationship\": [{" +
704                 "            \"related-to\": \"pserver\"" +
705                 "          }" +
706                 "        ]" +
707                 "      }" +
708                 "    }";
709
710         String bodyWith0 = "{  \"cloud-region\": [" + "  ]}";
711         String bodyWith1 = "{  \"cloud-region\": [" + cloudRegion + "  ]}";
712         String bodyWith2 = "{  \"cloud-region\": [" + cloudRegion + ", " + cloudRegion + "  ]}";
713         String bodyWithDifferent2 = "{  \"cloud-region\": [" + cloudRegion + ", " +
714                 cloudRegion.replace("mure-royo-ru22", "nolay-umaxo") +
715                 "]}";
716
717         return new Object[][] {
718                 { "regular single result", bodyWith1, false },
719                 { "exceptional empty result", bodyWith0, true },
720                 { "two same results", bodyWith2, false },
721                 { "two incoherent results", bodyWithDifferent2, true },
722         };
723     }
724
725     @Test(dataProvider = "aaiClientGetCloudOwnerByCloudRegionId")
726     public void getCloudOwnerByCloudRegionIdNonCached(String desc, String body, boolean expectingException) {
727         final String cloudRegion = "ravitu";
728         AAIRestInterface aaiRestInterface = mock(AAIRestInterface.class);
729         final ResponseWithRequestInfo responseWithRequestInfo = mockedResponseWithRequestInfo(Response.Status.OK, body);
730         when(aaiRestInterface.doRest(anyString(), anyString(), eq(Unchecked.toURI("cloud-infrastructure/cloud-regions?cloud-region-id=" + cloudRegion)),
731                 isNull(), eq(HttpMethod.GET), anyBoolean(), anyBoolean()))
732                 .thenReturn(responseWithRequestInfo);
733
734         final AaiClient aaiClient = new AaiClient(aaiRestInterface, null, null);
735
736         try {
737             final String result = aaiClient.getCloudOwnerByCloudRegionIdNonCached(cloudRegion);
738             if (expectingException) fail("expected failure on " + desc + ", got " + result);
739             else {
740                 assertThat(result, is("mure-royo-ru22"));
741             }
742         } catch (Exception e) {
743             if (!expectingException) throw e;
744             else {
745                 assertThat(e.toString(), either(
746                         containsString("No cloud-owner found for " + cloudRegion))
747                         .or(containsString("Conflicting cloud-owner found for " + cloudRegion)));
748             }
749         }
750     }
751
752     @DataProvider
753     public static Object[][]  cloudRegionAndTenantDataProvider() {
754         return new Object[][] {
755                 { "APPC-24595-T-IST-02C", "mtn23b" },
756                 { "APPC-24595-T-IST-02C", null },
757                 { null, "mtn23b" },
758                 { null, null },
759         };
760     }
761
762     @DataProvider
763     public static Object[][]  versionsDataProvider() {
764         return new Object[][] {
765                 { Stream.of("10","20","30"), "30" },
766                 { Stream.of("10","20","20"), "20" },
767                 { Stream.of("c","b","a"), "c" },
768                 { Stream.of("1.0","2.0","1.8"), "2.0" },
769                 { Stream.of("1.0.7","2.0.2","2.0.9"), "2.0.9" },
770                 { Stream.of("0","0","0"), "0" },
771                 { Stream.of("","10"), "10" },
772
773         };
774     }
775
776     @Test(dataProvider = "versionsDataProvider")
777     public void maxModelVer(Stream<String> input, String expected) {
778         Stream<ModelVer> modelVerStream = input.map(version -> {
779             ModelVer mv = new ModelVer();
780             mv.setModelVersion(version);
781             return mv;
782         });
783
784         final AaiClient aaiClient = new AaiClient(null, null, null);
785
786         assertThat(aaiClient.maxModelVer(modelVerStream), hasProperty("modelVersion", is(expected)));
787     }
788
789     @Test(expectedExceptions = GenericUncheckedException.class)
790     public void maxModelVerException() {
791         final AaiClient aaiClient = new AaiClient(null, null, null);
792         aaiClient.maxModelVer(Stream.of(new ModelVer()));
793     }
794     @Test(dataProvider = "cloudRegionAndTenantDataProvider")
795     public void getCloudRegionAndTenantByVnfId(String tenantName, String cloudRegionId) throws JsonProcessingException {
796         SimpleResult tenant = new SimpleResult();
797         if (tenantName != null) {
798             tenant.setJsonNodeType("tenant");
799             Properties tenantProps = new Properties();
800             tenantProps.setTenantName(tenantName);
801             tenant.setJsonProperties(tenantProps);
802         }
803
804         SimpleResult cloudRegion = new SimpleResult();
805         if (cloudRegionId != null) {
806             cloudRegion.setJsonNodeType("cloud-region");
807             Properties cloudRegionProps = new Properties();
808             cloudRegionProps.setCloudRegionId(cloudRegionId);
809             cloudRegion.setJsonProperties(cloudRegionProps);
810         }
811
812         CustomQuerySimpleResult customQuerySimpleResult = new CustomQuerySimpleResult(ImmutableList.of(tenant, cloudRegion));
813         String mockedBody = new ObjectMapper().writeValueAsString(customQuerySimpleResult);
814
815         AAIRestInterface aaiRestInterface = mock(AAIRestInterface.class);
816         final ResponseWithRequestInfo responseWithRequestInfo = mockedResponseWithRequestInfo(Response.Status.OK, mockedBody, "query?format=simple", HttpMethod.PUT);
817         when(aaiRestInterface.doRest(anyString(), anyString(), eq(Unchecked.toURI("query?format=simple")),
818                 any(), eq(HttpMethod.PUT), anyBoolean(), anyBoolean()))
819                 .thenReturn(responseWithRequestInfo);
820
821         final AaiClient aaiClient = new AaiClient(aaiRestInterface, null, null);
822         Map<String, Properties> result = aaiClient.getCloudRegionAndTenantByVnfId("anyVnfId");
823         if (tenantName != null) {
824             assertEquals(result.get("tenant").getTenantName(), tenantName);
825         } else {
826             assertNull(result.get("tenant"));
827         }
828
829         if (cloudRegionId != null) {
830             assertEquals(result.get("cloud-region").getCloudRegionId(), cloudRegionId);
831         } else {
832             assertNull(result.get("cloud-region"));
833         }
834     }
835
836     protected void mockForGetRequest(AAIRestInterface aaiRestInterface, ResponseWithRequestInfo responseWithRequestInfo) {
837         when(aaiRestInterface.doRest(anyString(), anyString(), any(URI.class), isNull(), eq(HttpMethod.GET) ,anyBoolean(), anyBoolean()))
838                 .thenReturn(responseWithRequestInfo);
839     }
840
841     @Test
842     public void shouldProperlyReadResponseOnceWhenSubscribersAreNotPresent() {
843         AAIRestInterface restInterface = mock(AAIRestInterface.class);
844         PortDetailsTranslator portDetailsTranslator = mock(PortDetailsTranslator.class);
845         Response response = mock(Response.class);
846         when(response.getStatus()).thenReturn(404);
847         when(response.readEntity(String.class)).thenReturn("sampleEntity");
848         when(response.getStatusInfo()).thenReturn(Response.Status.NOT_FOUND);
849         ResponseWithRequestInfo responseWithRequestInfo = new ResponseWithRequestInfo(response, "test", HttpMethod.GET);
850         when(restInterface.RestGet(eq("VidAaiController"), any(String.class),
851                 eq(Unchecked.toURI("business/customers?subscriber-type=INFRA&depth=0")), eq(false), eq(true))).thenReturn(responseWithRequestInfo);
852         AaiClient aaiClient = new AaiClient(restInterface, portDetailsTranslator, null);
853
854
855         aaiClient.getAllSubscribers(true);
856
857         verify(response).readEntity(String.class);
858     }
859
860     @FunctionalInterface
861     public interface UncheckedBiConsumer<T, U> extends BiConsumer<T, U> {
862         @Override
863         default void accept(T t, U u) {
864             try {
865                 acceptThrows(t, u);
866             } catch (Exception e) {
867                 throw new RuntimeException(e);
868             }
869         }
870
871         void acceptThrows(T t, U u) throws Exception;
872     }
873
874     @Test
875     public void getLatestVersionByInvariantId_verifyCallingExpectedApi(){
876
877         when(aaiClientMock.getLatestVersionByInvariantId(anyString())).thenCallRealMethod();
878
879         aaiClientMock.getLatestVersionByInvariantId("model-invariant-id");
880
881         Mockito.verify(aaiClientMock).doAaiPut(argThat(url -> url.endsWith("query?format=resource&depth=0")),argThat(payload -> payload.contains("service-design-and-creation/models/model/model-invariant-id")),anyBoolean());
882
883     }
884
885     @DataProvider
886     public static Object[][]  getSubscriberDataDataProvider() {
887         return new Object[][] {
888             { "Some-ID", true },
889             { "another id 123", false },
890         };
891     }
892
893     @Test(dataProvider = "getSubscriberDataDataProvider")
894     public void getSubscriberDataParams(String subscriberId, boolean omitServiceInstances) {
895         String depth = omitServiceInstances ? "1" : "2";
896         when(aaiClientMock.getSubscriberData(anyString(),anyBoolean())).thenCallRealMethod();
897         aaiClientMock.getSubscriberData(subscriberId, omitServiceInstances);
898         Mockito.verify(aaiClientMock).doAaiGet(argThat(s -> s.contains("customer/" + subscriberId + "?") && s.contains("depth=" + depth)),any(Boolean.class));
899     }
900
901     @Test
902     public void testToModelVerStream() throws IOException {
903
904         ModelVersions modelVersions = JACKSON_OBJECT_MAPPER.readValue("" +
905             "{\n" +
906             "    \"results\": [\n" +
907             "        {\n" +
908             "            \"model\": {\n" +
909             "                \"model-invariant-id\": \"f6342be5-d66b-4d03-a1aa-c82c3094c4ea\",\n" +
910             "                \"model-type\": \"service\",\n" +
911             "                \"resource-version\": \"1534274421300\"\n" +
912             "            }\n" +
913             "        },\n" +
914             "        {\n" +
915             "            \"model-ver\": {\n" +
916             "                \"model-version-id\": \"a92f899d-a3ec-465b-baed-1663b0a5aee1\",\n" +
917             "                \"model-name\": \"NCM_VLAN_SVC_ym161f\",\n" +
918             "                \"model-version\": \"bbb\",\n" +
919             "                \"distribution-status\": \"DISTRIBUTION_COMPLETE_OK\",\n" +
920             "                \"model-description\": \"Network Collection service for vLAN tagging\",\n" +
921             "                \"resource-version\": \"1534788756086\"\n" +
922             "            }\n" +
923             "        },\n" +
924             "        {\n" +
925             "            \"model-ver\": {\n" +
926             "                \"model-version-id\": \"d2fda667-e92e-4cfa-9620-5da5de01a319\",\n" +
927             "                \"model-name\": \"NCM_VLAN_SVC_ym161f\",\n" +
928             "                \"model-version\": \"aaa\",\n" +
929             "                \"distribution-status\": \"DISTRIBUTION_COMPLETE_OK\",\n" +
930             "                \"model-description\": \"Network Collection service for vLAN tagging\",\n" +
931             "                \"resource-version\": \"1534444087221\"\n" +
932             "            }\n" +
933             "        }]}", ModelVersions.class);
934
935
936         final AaiClient aaiClient = new AaiClient(null, null, null);
937
938         assertThat(aaiClient.toModelVerStream(modelVersions).collect(toList()),
939             containsInAnyOrder(
940                 hasProperty("modelVersionId", is("a92f899d-a3ec-465b-baed-1663b0a5aee1")),
941                 hasProperty("modelVersionId", is("d2fda667-e92e-4cfa-9620-5da5de01a319"))
942             ));
943
944     }
945 }