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