2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.vid.utils;
23 import static net.javacrumbs.jsonunit.JsonMatchers.jsonEquals;
24 import static org.hamcrest.CoreMatchers.is;
25 import static org.hamcrest.CoreMatchers.not;
26 import static org.hamcrest.CoreMatchers.sameInstance;
27 import static org.hamcrest.MatcherAssert.assertThat;
28 import static org.hamcrest.Matchers.hasEntry;
29 import static org.hamcrest.Matchers.matchesPattern;
30 import static org.mockito.ArgumentMatchers.contains;
31 import static org.mockito.Mockito.mock;
32 import static org.mockito.Mockito.verify;
33 import static org.onap.vid.testUtils.RegExMatcher.matchesRegEx;
34 import static org.testng.AssertJUnit.assertEquals;
36 import com.att.eelf.configuration.EELFLogger;
37 import com.fasterxml.jackson.core.JsonLocation;
38 import com.fasterxml.jackson.core.JsonParseException;
39 import com.fasterxml.jackson.databind.JsonMappingException;
40 import com.google.common.collect.ImmutableMap;
41 import io.joshworks.restclient.http.HttpResponse;
42 import java.io.FileNotFoundException;
43 import java.io.IOException;
44 import java.nio.charset.StandardCharsets;
45 import java.security.UnrecoverableKeyException;
46 import java.security.cert.CertificateException;
47 import java.util.concurrent.Callable;
48 import java.util.function.Function;
49 import javax.crypto.BadPaddingException;
50 import javax.net.ssl.SSLHandshakeException;
51 import javax.ws.rs.ProcessingException;
52 import org.apache.commons.io.IOUtils;
53 import org.mockito.ArgumentCaptor;
54 import org.onap.vid.exceptions.GenericUncheckedException;
55 import org.onap.vid.testUtils.TestUtils;
57 import org.springframework.http.HttpMethod;
58 import org.testng.annotations.BeforeMethod;
59 import org.testng.annotations.DataProvider;
60 import org.testng.annotations.Test;
61 import sun.security.provider.certpath.SunCertPathBuilderException;
62 import sun.security.validator.ValidatorException;
64 public class LoggingUtilsTest {
66 private static final String TEST_OBJECT_JSON = "{\"key\":\"myNumber\",\"value\":42}";
68 public static class TestModel {
72 public TestModel(String key, int value) {
80 private EELFLogger loggerMock;
82 private Logging logginService = new Logging();
83 private String url = "someUrl";
84 private final TestModel testObject = new TestModel("myNumber", 42);
89 loggerMock = mock(EELFLogger.class);
93 public void whenLogRequest_thenLoggedInDebug() {
95 logginService.logRequest(loggerMock, HttpMethod.GET, url);
98 ArgumentCaptor<Object> argumentCaptor = ArgumentCaptor.forClass(Object.class);
99 verify(loggerMock).debug(contains("Sending"), argumentCaptor.capture());
100 assertEquals("GET", argumentCaptor.getAllValues().get(0));
101 assertEquals(url, argumentCaptor.getAllValues().get(1));
107 public void whenLogResponseOfHttpResponse_thenLoggedInDebug() throws Exception {
108 HttpResponse<TestModel> response = TestUtils.createTestHttpResponse(200, testObject, TestModel.class);
109 logginService.logResponse(loggerMock, HttpMethod.POST, url, response);
111 ArgumentCaptor<Object> argumentCaptor = ArgumentCaptor.forClass(Object.class);
112 ArgumentCaptor<String> messageCaptur = ArgumentCaptor.forClass(String.class);
113 verify(loggerMock).debug(messageCaptur.capture(), argumentCaptor.capture());
115 assertThat(messageCaptur.getValue(), matchesPattern("Received.*Status.*Body.*"));
116 assertEquals("POST", argumentCaptor.getAllValues().get(0));
117 assertEquals(url, argumentCaptor.getAllValues().get(1));
118 assertEquals(200, argumentCaptor.getAllValues().get(2));
119 assertEquals(TEST_OBJECT_JSON, argumentCaptor.getAllValues().get(3));
123 public void whenLogResponseOfHttpResponse_thenCanReadEntityAfterwards() throws Exception {
124 HttpResponse<TestModel> response = TestUtils.createTestHttpResponse(200, testObject, TestModel.class);
125 logginService.logResponse(loggerMock, HttpMethod.POST, url, response);
126 assertThat(response.getBody(), jsonEquals(TEST_OBJECT_JSON));
130 public void whenLogResponseOfHttpResponse_thenCanReadRawEntityAfterwards() throws Exception {
131 HttpResponse<TestModel> response = TestUtils.createTestHttpResponse(200, testObject, TestModel.class);
132 logginService.logResponse(loggerMock, HttpMethod.POST, url, response);
133 assertThat(IOUtils.toString(response.getRawBody(), StandardCharsets.UTF_8), jsonEquals(TEST_OBJECT_JSON));
137 public static Object[][] exceptions() {
138 Exception e0 = new CertificateException("No X509TrustManager implementation available");
139 Exception noTrustMngrImplException = new SSLHandshakeException(e0.toString());
140 noTrustMngrImplException.initCause(e0);
142 Exception e1 = new BadPaddingException("Given final block not properly padded");
143 Exception incorrectPasswordException = new IOException("keystore password was incorrect",
144 new UnrecoverableKeyException("failed to decrypt safe contents entry: " + e1));
145 String incorrectPasswordExceptionDescription = "" +
146 "java.io.IOException: keystore password was incorrect: " +
147 "java.security.UnrecoverableKeyException: failed to decrypt safe contents entry: " +
148 "javax.crypto.BadPaddingException: Given final block not properly padded";
150 Exception e2 = new SunCertPathBuilderException("unable to find valid certification path to requested target");
151 Exception noValidCert = new ProcessingException(new ValidatorException("PKIX path building failed: " + e2.toString(), e2));
152 String noValidCertDescription = "" +
153 "javax.ws.rs.ProcessingException: " +
154 "sun.security.validator.ValidatorException: PKIX path building failed: " +
155 "sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target";
157 RuntimeException codehausParseException = new RuntimeException(new JsonParseException("Unexpected character ('<' (code 60)):" +
158 " expected a valid value (number, String, array, object, 'true', 'false' or 'null')",
159 new JsonLocation("<html>i'm an error</html>", 25, 1, 1)));
160 String codehausParseDescription = "" +
161 "com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)):" +
162 " expected a valid value (number, String, array, object, 'true', 'false' or 'null')\n" +
163 " at [Source: (String)\"<html>i'm an error</html>\"; line: 1, column: 1]";
165 RuntimeException fasterxmlMappingException = new RuntimeException(new JsonMappingException("Can not deserialize instance of java.lang.String out of START_ARRAY token",
166 new com.fasterxml.jackson.core.JsonLocation("{ example json }", 15, 1, 20)));
167 String fasterxmlMappingDescription = "" +
168 "com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_ARRAY token\n" +
169 " at [Source: (String)\"{ example json }\"; line: 1, column: 20]";
171 return new Object[][]{
172 {"javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No X509TrustManager implementation available",
173 noTrustMngrImplException},
174 {"java.lang.StringIndexOutOfBoundsException: String index out of range: 4",
175 new StringIndexOutOfBoundsException(4)},
176 {"java.io.FileNotFoundException: vid/WEB-INF/cert/aai-client-cert.p12",
177 new FileNotFoundException("vid/WEB-INF/cert/aai-client-cert.p12")},
178 {"NullPointerException at LoggingUtilsTest.java:[0-9]+",
179 new NullPointerException("null")},
180 {incorrectPasswordExceptionDescription,
181 incorrectPasswordException},
182 {incorrectPasswordExceptionDescription,
183 new GenericUncheckedException(incorrectPasswordException)},
184 {"javax.ws.rs.ProcessingException: javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_expired",
185 new ProcessingException(new SSLHandshakeException("Received fatal alert: certificate_expired"))},
186 {noValidCertDescription,
188 {escapeBrackets(codehausParseDescription),
189 codehausParseException},
190 {escapeBrackets(fasterxmlMappingDescription),
191 fasterxmlMappingException},
192 {"org.onap.vid.exceptions.GenericUncheckedException: top message: org.onap.vid.exceptions.GenericUncheckedException: root message",
193 new GenericUncheckedException("top message", new IOException("sandwich message", new GenericUncheckedException("root message")))},
194 {"org.onap.vid.exceptions.GenericUncheckedException: basa",
195 new GenericUncheckedException("basa")}
200 @Test(dataProvider = "exceptions")
201 public void testExceptionToDescription(String expectedDescription, Exception exceptionToDescribe) {
202 String expectedButDotsEscaped = expectedDescription.replace(".", "\\.");
204 assertThat(Logging.exceptionToDescription(exceptionToDescribe), matchesRegEx(expectedButDotsEscaped));
208 public void testWithMDCInternal_whenGivenProvider_functionShouldBeExtractedWithMdc() {
209 Object myAnything = new Object();
211 Object result = logginService.withMDCInternal(ImmutableMap.of("my key", "my value"),
213 assertThat("MDC values should be installed when extracting the supplier",
214 MDC.getCopyOfContextMap(), hasEntry("my key", "my value"));
219 assertThat("withMDCInternal should extract my function", result, is(sameInstance(myAnything)));
220 assertThat("MDC values should be removed", MDC.getCopyOfContextMap(), not(hasEntry("k", "v")));
224 public void testWithMDC_whenGivenFunction_functionShouldBeEncapsulated() {
226 String[] stringsArray = {"before"};
228 Function<String, Integer> myFunction = s -> {
229 assertThat("MDC values should be installed when inside myFunction",
230 MDC.getCopyOfContextMap(), hasEntry("my key", "my value"));
236 Function<String, Integer> functionWithMDC =
237 logginService.withMDC(ImmutableMap.of("my key", "my value"), myFunction);
240 assertThat("invocation of function must not happen yet", stringsArray[0], is("before"));
242 Integer result = functionWithMDC.apply("after");
244 assertThat("invocation of my function should have been deferred", stringsArray[0], is("after"));
245 assertThat("apply should return function's value", result, is(42));
249 public void testWithMDC_whenGivenCallable_callableShouldBeEncapsulated() throws Exception {
251 String[] stringsArray = {"before"};
253 Callable<Integer> myCallable = () -> {
254 assertThat("MDC values should be installed when inside myCallable",
255 MDC.getCopyOfContextMap(), hasEntry("my key", "my value"));
256 stringsArray[0] = "after";
261 Callable<Integer> callableWithMDC = logginService.withMDC(ImmutableMap.of("my key", "my value"), myCallable);
264 assertThat("invocation of callable must not happen yet", stringsArray[0], is("before"));
266 Integer result = callableWithMDC.call();
268 assertThat("invocation of my callable should have been deferred", stringsArray[0], is("after"));
269 assertThat("apply should return function's value", result, is(42));
272 private static String escapeBrackets(String in) {
273 return in.replaceAll("[\\(\\[\\{\\)]", "\\\\$0");