Test many POJOs getters/setters
[vid.git] / vid-app-common / src / test / java / org / onap / vid / testUtils / TestUtils.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.testUtils;
22
23 import static com.fasterxml.jackson.module.kotlin.ExtensionsKt.jacksonObjectMapper;
24 import static java.util.stream.Collectors.toList;
25 import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptors;
26 import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
27 import static org.apache.commons.text.CharacterPredicates.DIGITS;
28 import static org.apache.commons.text.CharacterPredicates.LETTERS;
29 import static org.apache.http.HttpVersion.HTTP_1_1;
30 import static org.mockito.ArgumentMatchers.any;
31 import static org.mockito.Mockito.RETURNS_DEFAULTS;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.when;
34 import static org.mockito.Mockito.withSettings;
35 import static org.onap.vid.utils.KotlinUtilsKt.JACKSON_OBJECT_MAPPER;
36 import static org.onap.vid.utils.KotlinUtilsKt.JOSHWORKS_JACKSON_OBJECT_MAPPER;
37 import static org.testng.Assert.fail;
38
39 import com.fasterxml.jackson.databind.DeserializationFeature;
40 import com.fasterxml.jackson.databind.ObjectMapper;
41 import com.google.code.beanmatchers.BeanMatchers;
42 import com.google.common.collect.ImmutableList;
43 import io.joshworks.restclient.http.HttpResponse;
44 import java.beans.PropertyDescriptor;
45 import java.io.ByteArrayInputStream;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.Serializable;
49 import java.lang.reflect.Field;
50 import java.net.URI;
51 import java.nio.charset.StandardCharsets;
52 import java.util.Arrays;
53 import java.util.Collections;
54 import java.util.Iterator;
55 import java.util.LinkedList;
56 import java.util.List;
57 import java.util.function.Predicate;
58 import javax.ws.rs.client.Client;
59 import javax.ws.rs.client.Invocation;
60 import javax.ws.rs.client.WebTarget;
61 import javax.ws.rs.core.GenericType;
62 import javax.ws.rs.core.MediaType;
63 import javax.ws.rs.core.Response;
64 import org.apache.commons.io.IOUtils;
65 import org.apache.commons.lang3.RandomUtils;
66 import org.apache.commons.lang3.exception.ExceptionUtils;
67 import org.apache.commons.lang3.reflect.FieldUtils;
68 import org.apache.commons.lang3.reflect.MethodUtils;
69 import org.apache.commons.text.RandomStringGenerator;
70 import org.apache.http.HttpResponseFactory;
71 import org.apache.http.entity.InputStreamEntity;
72 import org.apache.http.entity.StringEntity;
73 import org.apache.http.impl.DefaultHttpResponseFactory;
74 import org.apache.http.message.BasicStatusLine;
75 import org.apache.log4j.LogManager;
76 import org.apache.log4j.Logger;
77 import org.json.JSONArray;
78 import org.json.JSONObject;
79 import org.junit.Assert;
80 import org.mockito.InjectMocks;
81 import org.mockito.Mock;
82 import org.mockito.MockSettings;
83 import org.mockito.Mockito;
84 import org.mockito.MockitoAnnotations;
85 import org.mockito.invocation.InvocationOnMock;
86 import org.mockito.stubbing.Answer;
87 import org.mockito.stubbing.OngoingStubbing;
88 import org.onap.portalsdk.core.util.SystemProperties;
89 import org.onap.vid.asdc.AsdcCatalogException;
90 import org.onap.vid.asdc.beans.Service;
91 import org.onap.vid.model.VidNotions;
92 import org.onap.vid.model.VidNotions.InstantiationType;
93 import org.onap.vid.model.VidNotions.InstantiationUI;
94 import org.onap.vid.model.VidNotions.ModelCategory;
95 import org.onap.vid.mso.model.CloudConfiguration;
96 import org.springframework.core.env.Environment;
97 import org.testng.annotations.DataProvider;
98
99 /**
100  * Created by Oren on 6/7/17.
101  */
102 public class TestUtils {
103
104     private static final Logger logger = LogManager.getLogger(TestUtils.class);
105
106     /**
107      * The method compares between two jsons. the function assert that the actual object does not reduce or change the functionallity/parsing of the expected json.
108      * This means that if the expected JSON has a key which is null or the JSON doesn't have a key which contained in the expected JSON the assert will succeed and the will pass.
109      * For example : For JSON expected = {a:null} and actual {a:3} the test will pass
110      * Other example : For JSON expected = {a:3} and actual {a:null} the test will fail
111      *
112      * @param expected JSON
113      * @param actual JSON
114      */
115     public static void assertJsonStringEqualsIgnoreNulls(String expected, String actual) {
116         if (expected == null || expected == JSONObject.NULL) {return;}
117
118         JSONObject expectedJSON = new JSONObject(expected);
119         JSONObject actualJSON = new JSONObject(actual);
120         Iterator<?> keys = expectedJSON.keys();
121
122         while( keys.hasNext() ) {
123             String key = (String)keys.next();
124             Object expectedValue = expectedJSON.get(key);
125             if (expectedValue == JSONObject.NULL){
126                 continue;
127             }
128
129             Object actualValue = actualJSON.get(key);
130
131             if (expectedValue instanceof JSONObject) {
132                 String expectedVal = expectedValue.toString();
133                 String actualVal = actualValue.toString();
134                 assertJsonStringEqualsIgnoreNulls(expectedVal, actualVal);
135             }
136             else if (expectedValue instanceof JSONArray) {
137                 if (actualValue instanceof JSONArray) {
138                     JSONArray expectedJSONArray = (JSONArray)expectedValue;
139                     JSONArray actualJSONArray = (JSONArray)actualValue;
140                     for (int i = 0; i < expectedJSONArray.length(); i++) {
141                         String expectedItem = expectedJSONArray.get(i).toString();
142                         String actualItem = actualJSONArray.get(i).toString();
143                         if (expectedValue instanceof JSONObject)
144                             assertJsonStringEqualsIgnoreNulls(expectedItem, actualItem);
145                     }
146                 }
147                 else {
148                     fail("expected: " + expectedValue + " got:" + actualValue);
149                 }
150             }
151             else {
152                 Assert.assertEquals("assertion fail for key:"+key, expectedValue, actualValue);
153             }
154         }
155     }
156
157     public static <T> T readJsonResourceFileAsObject(String pathInResource, Class<T> valueType) {
158         return readJsonResourceFileAsObject(pathInResource, valueType, false);
159     }
160
161     public static <T> T readJsonResourceFileAsObject(String pathInResource, Class<T> valueType, boolean failOnUnknownProperties) {
162         ObjectMapper objectMapper =
163             jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
164
165         try {
166             return objectMapper.readValue(TestUtils.class.getResource(pathInResource), valueType);
167         } catch (IOException e) {
168             return ExceptionUtils.rethrow(e);
169         }
170     }
171
172     public static String readFileAsString(String pathInResource) {
173         try {
174             return IOUtils.toString(TestUtils.class.getResource(pathInResource), "UTF-8");
175         } catch (IOException e) {
176             throw new RuntimeException(e);
177         }
178     }
179
180     public static String[] allPropertiesOf(Class<?> aClass) {
181         return getPropertyDescriptorsRecursively(aClass).stream()
182             .map(PropertyDescriptor::getDisplayName)
183             .distinct()
184             .toArray(String[]::new);
185     }
186
187     private static List<PropertyDescriptor> getPropertyDescriptorsRecursively(Class<?> aClass) {
188         List<PropertyDescriptor> result = new LinkedList<>();
189
190         for (Class<?> i = aClass; i != null && i != Object.class; i = i.getSuperclass()) {
191             Collections.addAll(result, getPropertyDescriptors(i));
192         }
193
194         return result;
195     }
196
197     private static <T> List<String> allStringFieldsOf(T object) {
198         return FieldUtils.getAllFieldsList(object.getClass()).stream()
199             .filter(field -> field.getType().isAssignableFrom(String.class))
200             .map(Field::getName)
201             .distinct()
202             .collect(toList());
203     }
204
205     private static List<Field> allMockitoFieldsOf(Object object) {
206         final Predicate<Field> hasMockAnnotation = field -> field.getAnnotation(Mock.class) != null;
207         final Predicate<Field> hasInjectMocksAnnotation = field -> field.getAnnotation(InjectMocks.class) != null;
208
209         return Arrays.stream(FieldUtils.getAllFields(object.getClass()))
210             .filter(hasMockAnnotation.or(hasInjectMocksAnnotation))
211             .collect(toList());
212     }
213
214     /**
215      * Calls MockitoAnnotations.initMocks after nullifying any field which is annotated @Mocke or @InjectMock.
216      * This makes a "hard rest" to any mocked state or instance. Expected to be invoked between any @Tests in class, by
217      * being called in TestNG's @BeforeMethod (or equivalently JUnit's @BeforeTest).
218      */
219     public static void initMockitoMocks(Object testClass) {
220         for (Field field : allMockitoFieldsOf(testClass)) {
221             try {
222                 // Write null to fields
223                 FieldUtils.writeField(field, testClass, null, true);
224             } catch (ReflectiveOperationException e) {
225                 ExceptionUtils.rethrow(e);
226             }
227         }
228
229         MockitoAnnotations.initMocks(testClass);
230     }
231
232     /**
233      * Sets each String property with a value equal to the name of
234      * the property; e.g.: { name: "name", city: "city" }
235      * @param object
236      * @param <T>
237      * @return The modified object
238      */
239     public static <T> T setStringsInStringFields(T object) {
240         allStringFieldsOf(object).forEach(it -> {
241             try {
242                 FieldUtils.writeField(object, it, it, true);
243             } catch (IllegalAccessException e) {
244                 // YOLO
245             }
246         });
247
248         return object;
249     }
250
251     public static void registerCloudConfigurationValueGenerator() {
252         BeanMatchers.registerValueGenerator(() -> new CloudConfiguration(
253                 randomAlphabetic(7), randomAlphabetic(7), randomAlphabetic(7)
254             ), CloudConfiguration.class);
255     }
256
257     public static void registerVidNotionsValueGenerator() {
258         BeanMatchers.registerValueGenerator(() -> new VidNotions(
259             randomEnum(InstantiationUI.class), randomEnum(ModelCategory.class),
260             randomEnum(InstantiationUI.class), randomEnum(InstantiationType.class)
261         ), VidNotions.class);
262     }
263
264     private static <T> T randomEnum(Class<T> enumClass) {
265         T[] values = enumClass.getEnumConstants();
266         return values[RandomUtils.nextInt(0, values.length)];
267     }
268
269     public static OngoingStubbing<InputStream> mockGetRawBodyWithStringBody(HttpResponse<String> httpResponse, String body) {
270         try {
271             return when(httpResponse.getRawBody()).thenReturn(IOUtils.toInputStream(body, StandardCharsets.UTF_8.name()));
272         } catch (IOException e) {
273             ExceptionUtils.rethrow(e);
274         }
275         return null; //never shall get here
276     }
277
278     public static HttpResponse<String> createTestHttpResponse(int statusCode, String entity) throws Exception {
279         HttpResponseFactory factory = new DefaultHttpResponseFactory();
280         org.apache.http.HttpResponse response = factory.newHttpResponse(new BasicStatusLine(HTTP_1_1, statusCode, null), null);
281         if (entity != null) {
282             response.setEntity(new StringEntity(entity));
283         }
284         return new HttpResponse<>(response, String.class, null);
285     }
286
287     public static <T> HttpResponse<T> createTestHttpResponse(int statusCode, T entity, final Class<T> entityClass) throws Exception {
288         HttpResponseFactory factory = new DefaultHttpResponseFactory();
289         org.apache.http.HttpResponse response = factory.newHttpResponse(new BasicStatusLine(HTTP_1_1, statusCode, null), null);
290         if (entity != null) {
291             InputStream inputStream = IOUtils.toInputStream(JACKSON_OBJECT_MAPPER.writeValueAsString(entity), StandardCharsets.UTF_8.name());
292             response.setEntity(new InputStreamEntity(inputStream));
293         }
294         return new HttpResponse(response, entityClass, JOSHWORKS_JACKSON_OBJECT_MAPPER);
295     }
296
297
298     public static class JavaxRsClientMocks {
299         private final javax.ws.rs.client.Client fakeClient;
300         private final javax.ws.rs.client.Invocation.Builder fakeBuilder;
301         private final javax.ws.rs.client.Invocation fakeInvocation;
302         private final Response fakeResponse;
303
304         public javax.ws.rs.client.Client getFakeClient() {
305             return fakeClient;
306         }
307
308         public javax.ws.rs.client.Invocation.Builder getFakeBuilder() {
309             return fakeBuilder;
310         }
311
312         public Response getFakeResponse() {
313             return fakeResponse;
314         }
315
316         public JavaxRsClientMocks() {
317             final MockSettings mockSettings = withSettings().defaultAnswer(new TriesToReturnMockByType());
318
319             fakeClient = mock(javax.ws.rs.client.Client.class, mockSettings);
320             fakeBuilder = mock(javax.ws.rs.client.Invocation.Builder.class, mockSettings);
321             fakeInvocation = mock(javax.ws.rs.client.Invocation.class, mockSettings);
322             fakeResponse = mock(Response.class, mockSettings);
323             final javax.ws.rs.client.WebTarget fakeWebTarget = mock(javax.ws.rs.client.WebTarget.class, mockSettings);
324
325             TriesToReturnMockByType.setAvailableMocks(
326                     fakeClient,
327                     fakeWebTarget,
328                     fakeBuilder,
329                     fakeInvocation,
330                     fakeResponse
331             );
332             Mockito.when(fakeBuilder.get(any(Class.class))).thenReturn(null);
333             Mockito.when(fakeBuilder.get(any(GenericType.class))).thenReturn(null);
334             Mockito.when(fakeResponse.getStatus()).thenReturn(200);
335             Mockito.when(fakeResponse.getStatusInfo()).thenReturn(Response.Status.OK);
336             Mockito.when(fakeResponse.readEntity(Service.class)).thenReturn(null);
337             Mockito.when(fakeResponse.readEntity(InputStream.class)).thenReturn(new ByteArrayInputStream(new byte[]{}));
338             Mockito.when(fakeResponse.readEntity(String.class)).thenReturn(null);
339         }
340     }
341
342     /*
343        inspired out from newer Mockito version
344         returns a mock from given list if it's a matching return-type
345     */
346     private static class TriesToReturnMockByType implements Answer<Object>, Serializable {
347         private final Answer<Object> defaultReturn = RETURNS_DEFAULTS;
348         private static List<Object> availableMocks = ImmutableList.of();
349
350         static void setAvailableMocks(Object... mocks) {
351             availableMocks = ImmutableList.copyOf(mocks);
352         }
353
354         public Object answer(InvocationOnMock invocation) throws Throwable {
355             Class<?> methodReturnType = invocation.getMethod().getReturnType();
356
357             return availableMocks.stream()
358                     .filter(mock -> methodReturnType.isAssignableFrom(mock.getClass()))
359                     //.peek(m -> logger.info("found a mock: " + m.getClass().getName()))
360                     .findFirst()
361                     .orElse(defaultReturn.answer(invocation));
362         }
363     }
364
365
366     //The method mocks only some methods used in my case
367     //You may add some other when for your test here
368     public static Response mockResponseForJavaxClient(Client javaxClientMock) {
369         Response  mockResponse = mock(Response.class);
370         WebTarget webTarget = mock(WebTarget.class);
371         Invocation.Builder builder = mock(Invocation.Builder.class);
372         when(javaxClientMock.target(any(URI.class))).thenReturn(webTarget);
373         when(webTarget.path(any())).thenReturn(webTarget);
374         when(webTarget.request(any(MediaType.class))).thenReturn(builder);
375         when(builder.headers(any())).thenReturn(builder);
376         when(builder.header(any(), any())).thenReturn(builder);
377         when(builder.get()).thenReturn(mockResponse);
378         return mockResponse;
379     }
380
381
382     public interface Test {
383
384         void apply() throws AsdcCatalogException;
385     }
386
387     public static void testWithSystemProperty(String key, String value, Test test) throws Exception {
388         SystemProperties systemProperties = new SystemProperties();
389         //use reflection to invoke protected method
390         Environment originalEnvironment = (Environment) MethodUtils
391             .invokeMethod(systemProperties, true, "getEnvironment");
392
393         try {
394             Environment environment = mock(Environment.class);
395             systemProperties.setEnvironment(environment);
396             when(environment.getRequiredProperty(key)).thenReturn(value);
397             when(environment.containsProperty(key)).thenReturn(true);
398             test.apply();
399         }
400         finally {
401             systemProperties.setEnvironment(originalEnvironment);
402         }
403     }
404
405     private static RandomStringGenerator generator = new RandomStringGenerator.Builder()
406             .withinRange('0', 'z')
407             .filteredBy(LETTERS, DIGITS)
408             .build();
409
410     public static String generateRandomAlphaNumeric(int length) {
411         return generator.generate(length);
412     }
413
414     @DataProvider
415     public static Object[][] trueAndFalse() {
416         return new Object[][]{{true}, {false}};
417     }
418
419     @DataProvider
420     public static Object[][] trueAndFalseAndNull() {
421         return new Boolean[][]{{Boolean.TRUE}, {Boolean.FALSE}, {null}};
422     }
423
424 }