From c72d565bb58226b20625b2bce5f0019046bee649 Mon Sep 17 00:00:00 2001 From: "Sonsino, Ofir (os0695)" Date: Tue, 10 Jul 2018 14:20:54 +0300 Subject: Merge 1806 code of vid-common Change-Id: I75d52abed4a24dfe3827d79edc4a2938726aa87a Issue-ID: VID-208 Signed-off-by: Sonsino, Ofir (os0695) --- .../test/java/org/onap/vid/aai/AaiClientTest.java | 459 +++++++++++++++++++-- 1 file changed, 436 insertions(+), 23 deletions(-) (limited to 'vid-app-common/src/test/java/org/onap/vid/aai/AaiClientTest.java') diff --git a/vid-app-common/src/test/java/org/onap/vid/aai/AaiClientTest.java b/vid-app-common/src/test/java/org/onap/vid/aai/AaiClientTest.java index 8010447ce..32e894d0a 100644 --- a/vid-app-common/src/test/java/org/onap/vid/aai/AaiClientTest.java +++ b/vid-app-common/src/test/java/org/onap/vid/aai/AaiClientTest.java @@ -1,40 +1,453 @@ package org.onap.vid.aai; -import static org.junit.Assert.*; -import java.util.*; - -import org.json.simple.JSONObject; -import org.junit.Assert; -import org.junit.Test; -import org.onap.vid.aai.model.AaiGetPnfs.Pnf; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.mockito.Mockito; +import org.onap.vid.aai.model.AaiGetTenatns.GetTenantsResponse; +import org.onap.vid.aai.model.AaiNodeQueryResponse; +import org.onap.vid.aai.model.ResourceType; +import org.onap.vid.aai.util.AAIRestInterface; +import org.onap.vid.aai.util.HttpsAuthClient; +import org.onap.vid.exceptions.GenericUncheckedException; +import org.onap.vid.model.Subscriber; import org.onap.vid.model.SubscriberList; +import org.onap.vid.model.probes.ExternalComponentStatus; +import org.onap.vid.model.probes.HttpRequestMetadata; +import org.onap.vid.model.probes.StatusMetadata; +import org.onap.vid.controllers.LocalWebConfig; +import org.onap.vid.testUtils.TestUtils; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.portalsdk.core.util.SystemProperties; +import org.springframework.http.HttpMethod; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.web.WebAppConfiguration; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import sun.security.provider.certpath.SunCertPathBuilderException; +import sun.security.validator.ValidatorException; + +import javax.crypto.BadPaddingException; +import javax.net.ssl.SSLHandshakeException; +import javax.servlet.ServletContext; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.core.Response; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.*; +@ContextConfiguration(classes = {LocalWebConfig.class, SystemProperties.class}) +@WebAppConfiguration public class AaiClientTest { - private AaiClient createTestSubject() { - return new AaiClient(); + private AaiClient aaiClientMock; + private ServletContext servletContext; + + @BeforeMethod + public void initMocks(){ + aaiClientMock = mock(AaiClient.class); + aaiClientMock.logger = mock(EELFLoggerDelegate.class); + servletContext = mock(ServletContext.class); + + when(servletContext.getRealPath(any(String.class))).thenReturn(""); + + when(aaiClientMock.doAaiGet(any(String.class),any(Boolean.class))).thenReturn(null); + } + + @DataProvider + public static Object[][] logicalLinkData() { + return new Object[][] { + {"", "network/logical-links/logical-link/"}, + {"link", "network/logical-links/logical-link/link"} + }; + } + + @Test(dataProvider = "logicalLinkData") + public void getLogicalLink_Link_Is_Empty(String link, String expectedUrl) { + + when(aaiClientMock.getLogicalLink(any(String.class))).thenCallRealMethod(); + aaiClientMock.getLogicalLink(link); + Mockito.verify(aaiClientMock).doAaiGet(argThat(equalToIgnoringCase(expectedUrl)),any(Boolean.class)); + } + + @DataProvider + public static Object[][] subscribersResults() { + return new Object[][] { + {new SubscriberList(new ArrayList() {{ add(new Subscriber()); add(new Subscriber()); }}), true}, + {new SubscriberList(new ArrayList() {{ add(new Subscriber()); }}), true}, + {new SubscriberList(new ArrayList()), false} + }; + } + + @Test(dataProvider = "subscribersResults") + public void testProbeAaiGetAllSubscribers_returnsTwoToZeroSubscribers_ResultsAsExpected(SubscriberList subscribers, boolean isAvailable){ + ExternalComponentStatus expectedStatus = new ExternalComponentStatus(ExternalComponentStatus.Component.AAI,isAvailable, new HttpRequestMetadata( + HttpMethod.GET, + 200, + "url", + "rawData", + isAvailable ? "OK" : "No subscriber received", + 0 + )); + Mockito.when(aaiClientMock.getAllSubscribers(true)).thenReturn( + new AaiResponseWithRequestInfo<>( + HttpMethod.GET, "url", new AaiResponse<>(subscribers, null, 200), + "rawData")); + Mockito.when(aaiClientMock.probeAaiGetAllSubscribers()).thenCallRealMethod(); + ExternalComponentStatus result = aaiClientMock.probeAaiGetAllSubscribers(); + assertThat(statusDataReflected(result),is(statusDataReflected(expectedStatus))); + assertThat(requestMetadataReflected(result.getMetadata()),is(requestMetadataReflected(expectedStatus.getMetadata()))); + } + + //serialize fields except of fields we cannot know ahead of time + private static String requestMetadataReflected(StatusMetadata metadata) { + return new ReflectionToStringBuilder(metadata, ToStringStyle.SHORT_PREFIX_STYLE) + .setExcludeFieldNames("duration") + .toString(); + } + + private static String statusDataReflected(ExternalComponentStatus status) { + return new ReflectionToStringBuilder(status, ToStringStyle.SHORT_PREFIX_STYLE) + .setExcludeFieldNames("metadata") + .toString(); + } + + @DataProvider + public static Object[][] rawData() { + return new Object[][]{ + {"errorMessage", }, {""}, {null} + }; + } + + @Test(dataProvider = "rawData") + public void testProbeAaiGetFullSubscribersWithNullResponse_returnsNotAvailableWithErrorRawData(String rawData){ + Mockito.when(aaiClientMock.getAllSubscribers(true)).thenReturn( + new AaiResponseWithRequestInfo<>(HttpMethod.GET, "url", null, + rawData)); + ExternalComponentStatus result = callProbeAaiGetAllSubscribersAndAssertNotAvailable(); + assertThat(result.getMetadata(), instanceOf(HttpRequestMetadata.class)); + assertEquals(((HttpRequestMetadata) result.getMetadata()).getRawData(), rawData); + } + + @DataProvider + public static Object[][] exceptions() { + return new Object[][] { + {"NullPointerException", "errorMessage", + new ExceptionWithRequestInfo(HttpMethod.GET, "url", + "errorMessage", null, new NullPointerException())}, + {"RuntimeException", null, + new ExceptionWithRequestInfo(HttpMethod.GET, "url", + null, null, new RuntimeException())}, + {"RuntimeException", null, + new RuntimeException()}, + }; + } + + @Test(dataProvider = "exceptions") + public void testProbeAaiGetFullSubscribersWithNullResponse_returnsNotAvailableWithErrorRawData(String description, String expectedRawData, Exception exception){ + Mockito.when(aaiClientMock.getAllSubscribers(true)).thenThrow(exception); + ExternalComponentStatus result = callProbeAaiGetAllSubscribersAndAssertNotAvailable(); + if (exception instanceof ExceptionWithRequestInfo) { + assertThat(result.getMetadata(), instanceOf(HttpRequestMetadata.class)); + assertEquals(((HttpRequestMetadata) result.getMetadata()).getRawData(), expectedRawData); + } + assertThat(result.getMetadata().getDescription(), containsString(description)); + } + + private ExternalComponentStatus callProbeAaiGetAllSubscribersAndAssertNotAvailable() { + Mockito.when(aaiClientMock.probeAaiGetAllSubscribers()).thenCallRealMethod(); + ExternalComponentStatus result = aaiClientMock.probeAaiGetAllSubscribers(); + assertFalse(result.isAvailable()); + return result; } @Test - public void testDoAaiGet() throws Exception { - AaiClient testSubject; - String certiPath = ""; - String uri = ""; - boolean xml = false; + public void getTenants_Arguments_Are_Null_Or_Empty() { + + when(aaiClientMock.getTenants(any(String.class), any(String.class))).thenCallRealMethod(); - // default test - testSubject = createTestSubject(); - testSubject.doAaiGet(certiPath, uri, xml); + AaiResponse response = aaiClientMock.getTenants("", ""); + + assertEquals(response.getErrorMessage(), "{\"statusText\":\" Failed to retrieve LCP Region & Tenants from A&AI, Subscriber ID or Service Type is missing.\"}"); + + + response = aaiClientMock.getTenants(null, null); + + assertEquals(response.getErrorMessage(), "{\"statusText\":\" Failed to retrieve LCP Region & Tenants from A&AI, Subscriber ID or Service Type is missing.\"}"); } @Test - public void testParseServiceSubscriptionObjectForTenants() throws Exception { - JSONObject jsonObject = null; - String result; + public void getTenants_Arguments_Are_Valid_But_Tenants_Not_Exist() { + + when(aaiClientMock.getTenants(any(String.class), any(String.class))).thenCallRealMethod(); + + Response generalEmptyResponse = mock(Response.class); + when(aaiClientMock.doAaiGet(any(String.class),any(Boolean.class))).thenReturn(generalEmptyResponse); + + AaiResponse response = aaiClientMock.getTenants("subscriberId", "serviceType"); + + assertEquals(response.getErrorMessage(), "{\"statusText\":\" A&AI has no LCP Region & Tenants associated to subscriber 'subscriberId' and service type 'serviceType'\"}"); - // default test - result = AaiClient.parseServiceSubscriptionObjectForTenants(jsonObject); } -} \ No newline at end of file + @Test + public void getTenants_Arguments_Are_Valid_Get_The_Tenanats() { + + when(aaiClientMock.getTenants(any(String.class), any(String.class))).thenCallRealMethod(); + + + Response generalEmptyResponse = mock(Response.class); + + when(generalEmptyResponse.readEntity(String.class)).thenReturn(tenantResponseRaw); + when(generalEmptyResponse.getStatus()).thenReturn(200); + when(generalEmptyResponse.getStatusInfo()).thenReturn(new Response.StatusType() { + @Override + public int getStatusCode() { + return 200; + } + + @Override + public Response.Status.Family getFamily() { + return Response.Status.Family.SUCCESSFUL; + } + + @Override + public String getReasonPhrase() { + return null; + } + }); + + + when(aaiClientMock.doAaiGet(any(String.class),any(Boolean.class))).thenReturn(generalEmptyResponse); + + AaiResponse response = aaiClientMock.getTenants("subscriberId", "serviceType"); + + Assert.assertTrue(response.t.length> 0); + } + + final String tenantResponseRaw ="" + + "{" + + "\"service-type\": \"VIRTUAL USP\"," + + "\"resource-version\": \"1494001841964\"," + + "\"relationship-list\": {" + + "\"relationship\": [{" + + "\"related-to\": \"tenant\"," + + "\"related-link\": \"/aai/v11/cloud-infrastructure/cloud-regions/cloud-region/att-aic/AAIAIC25/tenants/tenant/092eb9e8e4b7412e8787dd091bc58e86\"," + + "\"relationship-data\": [{" + + "\"relationship-key\": \"cloud-region.cloud-owner\"," + + "\"relationship-value\": \"att-aic\"" + + "}," + + "{" + + "\"relationship-key\": \"cloud-region.cloud-region-id\"," + + "\"relationship-value\": \"AAIAIC25\"" + + "}," + + "{" + + "\"relationship-key\": \"tenant.tenant-id\"," + + "\"relationship-value\": \"092eb9e8e4b7412e8787dd091bc58e86\"" + + "}" + + "]," + + "\"related-to-property\": [{" + + "\"property-key\": \"tenant.tenant-name\"," + + "\"property-value\": \"USP-SIP-IC-24335-T-01\"" + + "}]" + + "}]" + + "}" + + "}"; + + @DataProvider + public static Object[][] resourceTypesProvider() { + return new Object[][] { + {"service-instance", ResourceType.SERVICE_INSTANCE}, + {"generic-vnf", ResourceType.GENERIC_VNF}, + {"vf-module", ResourceType.VF_MODULE} + }; + } + + @Test(dataProvider = "resourceTypesProvider") + public void aaiNodeQueryResponseDeserializationTest(String resourceType, ResourceType expectedResourceType) throws IOException { + String link = "/aai/v12/business/customers/customer/a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb/service-subscriptions/service-subscription/Nimbus/service-instances/service-instance/7131d483-b450-406f-8e30-0c650645fc67"; + String json = + "{\"result-data\": [{" + + "\"resource-type\": \""+resourceType+"\"," + + "\"resource-link\": \""+ link+ "\"" + + "}]}"; + + AaiNodeQueryResponse nodeQueryResponse = new ObjectMapper().readValue(json, AaiNodeQueryResponse.class); + assertThat(nodeQueryResponse.resultData.get(0).resourceLink, equalTo(link)); + assertThat(nodeQueryResponse.resultData.get(0).resourceType, is(expectedResourceType)); + } + + @Test + public void aaiNodeQueryEmptyResponseDeserializationTest() throws IOException{ + String json = "{}"; + AaiNodeQueryResponse nodeQueryResponse = new ObjectMapper().readValue(json, AaiNodeQueryResponse.class); + assertNull(nodeQueryResponse.resultData); + } + + @DataProvider + public static Object[][] nameAndResourceTypeProvider() { + return new Object[][] { + {"SRIOV_SVC", ResourceType.SERVICE_INSTANCE, "search/nodes-query?search-node-type=service-instance&filter=service-instance-name:EQUALS:SRIOV_SVC"}, + {"b1707vidnf", ResourceType.GENERIC_VNF, "search/nodes-query?search-node-type=generic-vnf&filter=vnf-name:EQUALS:b1707vidnf"}, + {"connectivity_test", ResourceType.VF_MODULE, "search/nodes-query?search-node-type=vf-module&filter=vf-module-name:EQUALS:connectivity_test"}, + {"MjVg1234", ResourceType.VOLUME_GROUP, "search/nodes-query?search-node-type=volume-group&filter=volume-group-name:EQUALS:MjVg1234"} + }; + } + + @Test(dataProvider = "nameAndResourceTypeProvider") + public void whenSearchNodeTypeByName_callRightAaiPath(String name, ResourceType type, String expectedUrl) { + when(aaiClientMock.searchNodeTypeByName(any(String.class), any(ResourceType.class))).thenCallRealMethod(); + aaiClientMock.searchNodeTypeByName(name, type); + Mockito.verify(aaiClientMock).doAaiGet(eq(expectedUrl), eq(false)); + } + + @DataProvider + public static Object[][] aaiClientInternalExceptions() { + return Stream., UncheckedBiConsumer>>of( + + // Exception out of httpsAuthClientMock + Pair.of(CertificateException.class, (httpsAuthClientMock, javaxClientMock) -> { + final CertificateException e0 = new CertificateException("No X509TrustManager implementation available"); + SSLHandshakeException e = new SSLHandshakeException(e0.toString()); + e.initCause(e0); + + when(httpsAuthClientMock.getClient(any())).thenThrow(e); + }), + + Pair.of(StringIndexOutOfBoundsException.class, mockExceptionOnClientProvider(new StringIndexOutOfBoundsException(4))), + + Pair.of(NullPointerException.class, mockExceptionOnClientProvider(new NullPointerException("null"))), + + Pair.of(FileNotFoundException.class, mockExceptionOnClientProvider(new FileNotFoundException("vid/WEB-INF/cert/aai-client-cert.p12"))), + + Pair.of(BadPaddingException.class, mockExceptionOnClientProvider( + new IOException("keystore password was incorrect", new BadPaddingException("Given final block not properly padded"))) + ), + Pair.of(GenericUncheckedException.class, mockExceptionOnClientProvider(new GenericUncheckedException("basa"))), + + Pair.of(NullPointerException.class, (httpsAuthClientMock, javaxClientMock) -> + when(httpsAuthClientMock.getClient(any())).thenReturn(null)), + + + // Exception out of javax's Client + Pair.of(SSLHandshakeException.class, (httpsAuthClientMock, javaxClientMock) -> { + when(javaxClientMock.target(anyString())).thenThrow( + new ProcessingException(new SSLHandshakeException("Received fatal alert: certificate_expired")) + ); + }), + + Pair.of(SunCertPathBuilderException.class, (httpsAuthClientMock, javaxClientMock) -> { + SunCertPathBuilderException e0 = new SunCertPathBuilderException("unable to find valid certification path to requested target"); + when(javaxClientMock.target(anyString())).thenThrow( + new ProcessingException(new ValidatorException("PKIX path building failed: " + e0.toString(), e0)) + ); + }), + + Pair.of(GenericUncheckedException.class, (httpsAuthClientMock, javaxClientMock) -> + when(javaxClientMock.target(anyString())).thenThrow(new GenericUncheckedException("basa"))) + + ).flatMap(l -> Stream.of( + // double each case to propagateExceptions = true/false, to verify that "don't propagate" really still work + ImmutableList.of(l.getLeft(), l.getRight(), true).toArray(), + ImmutableList.of(l.getLeft(), l.getRight(), false).toArray() + )).collect(Collectors.toList()).toArray(new Object[][]{}); + } + + private static UncheckedBiConsumer mockExceptionOnClientProvider(Exception e) { + return (httpsAuthClientMock, javaxClientMock) -> + when(httpsAuthClientMock.getClient(any())).thenThrow(e); + } + + @Test(dataProvider = "aaiClientInternalExceptions") + public void propagateExceptions_internalsThrowException_ExceptionRethrown(Class expectedType, BiConsumer setupMocks, boolean propagateExceptions) throws Exception { + /* + Call chain is like: + this test -> AaiClient -> AAIRestInterface -> HttpsAuthClient -> javax's Client + + In this test, *AaiClient* and *AAIRestInterface* are under test (actual + implementation is used), while HttpsAuthClient and the javax's Client are + mocked to return pseudo-responses or - better- throw exceptions. + */ + + // prepare mocks + HttpsAuthClient httpsAuthClientMock = mock(HttpsAuthClient.class); + TestUtils.JavaxRsClientMocks mocks = new TestUtils.JavaxRsClientMocks(); + Client javaxClientMock = mocks.getFakeClient(); + Response responseMock = mocks.getFakeResponse(); + + // prepare real AAIRestInterface and AaiClient, and wire mocks + AAIRestInterface aaiRestInterface = new AAIRestInterface(httpsAuthClientMock); + final AaiClient aaiClient = new AaiClient(aaiRestInterface, null); + when(httpsAuthClientMock.getClient(any())).thenReturn(javaxClientMock); + + // define atomic method under test, including reset of "aaiRestInterface.client" + final Function doAaiGet = (propagateExceptions1) -> { + try { + FieldUtils.writeField(aaiRestInterface, "client", null, true); + return aaiClient.doAaiGet("uri", false, propagateExceptions1).getResponse(); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }; + + // verify setup again + assertThat("mocks setup should make doAaiGet return our responseMock", doAaiGet.apply(true), is(sameInstance(responseMock))); + + + /// TEST: + setupMocks.accept(httpsAuthClientMock, javaxClientMock); + + try { + final Response response = doAaiGet.apply(propagateExceptions); + } catch (Exception e) { + if (propagateExceptions) { + assertThat("root cause incorrect for " + ExceptionUtils.getStackTrace(e), ExceptionUtils.getRootCause(e), instanceOf(expectedType)); + return; // ok, done + } else { + // Verify that "don't propagate" really still work + Assert.fail("calling doAaiGet when propagateExceptions is false must result with no exception", e); + } + } + + // If no exception caught + // We're asserting that the legacy behaviour is still in place. Hopefully + // one day we will remove the non-propagateExceptions case + assertFalse(propagateExceptions, "calling doAaiGet when propagateExceptions is 'true' must result with an exception (in this test)"); + } + + @FunctionalInterface + public interface UncheckedBiConsumer extends BiConsumer { + @Override + default void accept(T t, U u) { + try { + acceptThrows(t, u); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + void acceptThrows(T t, U u) throws Exception; + } +} -- cgit 1.2.3-korg