2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2018 Nokia. All rights reserved.
6 * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
7 * =============================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
21 package org.onap.appc.adapter.chef.chefclient.impl;
23 import static junit.framework.TestCase.assertEquals;
24 import static org.mockito.BDDMockito.given;
25 import static org.mockito.Matchers.argThat;
26 import static org.mockito.Mockito.mock;
28 import com.google.common.collect.ImmutableMap;
29 import java.io.IOException;
31 import java.net.URISyntaxException;
32 import java.util.function.Supplier;
33 import org.apache.http.Header;
34 import org.apache.http.HttpResponse;
35 import org.apache.http.HttpStatus;
36 import org.apache.http.StatusLine;
37 import org.apache.http.client.HttpClient;
38 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
39 import org.apache.http.client.methods.HttpRequestBase;
40 import org.apache.http.entity.StringEntity;
41 import org.junit.Before;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 import org.mockito.ArgumentMatcher;
45 import org.mockito.InjectMocks;
46 import org.mockito.Mock;
47 import org.mockito.runners.MockitoJUnitRunner;
48 import org.onap.appc.adapter.chef.chefclient.ChefApiClientFactory;
49 import org.onap.appc.adapter.chef.chefclient.api.ChefApiClient;
50 import org.onap.appc.adapter.chef.chefclient.api.ChefResponse;
52 @RunWith(MockitoJUnitRunner.class)
53 public class ChefApiClientImplTest {
55 private static final String END_POINT = "https://chefServer";
56 private static final String ORGANIZATIONS_PATH = "onap";
57 private static final String USER_ID = "testUser";
58 private static final String REQUEST_PATH = "/test/path";
59 private static final String BODY = "SOME BODY STRING";
60 private static final String PEM_FILEPATH = "path/to/pemFile";
61 private static final ImmutableMap<String, String> HEADERS = ImmutableMap.<String, String>builder()
62 .put("Content-type", "application/json")
63 .put("Accept", "application/json")
64 .put("X-Ops-Timestamp", "1970-01-15T06:56:07Z")
65 .put("X-Ops-UserId", USER_ID)
66 .put("X-Chef-Version", "12.4.1")
67 .put("X-Ops-Content-Hash", BODY)
68 .put("X-Ops-Sign", "version=1.0").build();
71 private HttpClient httpClient;
73 private ChefApiHeaderFactory chefHttpHeaderFactory;
76 private ChefApiClientFactory chefApiClientFactory;
77 private ChefApiClient chefApiClient;
81 chefApiClient = chefApiClientFactory.create(
89 public void execute_HttpGet_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException {
91 String methodName = "GET";
93 Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.get(REQUEST_PATH);
96 assertChefApiClientCall(methodName, body, chefClientApiCall);
100 public void execute_HttpDelete_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException {
102 String methodName = "DELETE";
104 Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.delete(REQUEST_PATH);
107 assertChefApiClientCall(methodName, body, chefClientApiCall);
111 public void execute_HttpPost_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException {
113 String methodName = "POST";
114 Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.post(REQUEST_PATH, BODY);
117 assertChefApiClientCall(methodName, BODY, chefClientApiCall);
121 public void execute_HttpPut_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException {
123 String methodName = "PUT";
124 Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.put(REQUEST_PATH, BODY);
127 assertChefApiClientCall(methodName, BODY, chefClientApiCall);
130 private void assertChefApiClientCall(String methodName, String body, Supplier<ChefResponse> httpMethod)
133 given(chefHttpHeaderFactory.create(methodName, REQUEST_PATH, body, USER_ID, ORGANIZATIONS_PATH, PEM_FILEPATH))
134 .willReturn(HEADERS);
136 StatusLine statusLine = mock(StatusLine.class);
137 given(statusLine.getStatusCode()).willReturn(HttpStatus.SC_OK);
138 HttpResponse httpResponse = mock(HttpResponse.class);
139 given(httpResponse.getStatusLine()).willReturn(statusLine);
140 given(httpResponse.getEntity()).willReturn(new StringEntity("Successful Response String"));
141 given(httpClient.execute(argThat(new HttpRequestBaseMatcher(methodName))))
142 .willReturn(httpResponse);
145 ChefResponse chefResponse = httpMethod.get();
148 assertEquals("Successful Response String", chefResponse.getBody());
149 assertEquals(HttpStatus.SC_OK, chefResponse.getStatusCode());
153 public void execute_shouldHandleException_whenHttpClientExecutionFails() throws IOException {
156 given(chefHttpHeaderFactory.create("GET", REQUEST_PATH, "", USER_ID, ORGANIZATIONS_PATH, PEM_FILEPATH))
157 .willReturn(HEADERS);
159 String expectedErrorMsg = "HttpClient call failed";
160 given(httpClient.execute(argThat(new HttpRequestBaseMatcher("GET"))))
161 .willThrow(new IOException(expectedErrorMsg));
164 ChefResponse chefResponse = chefApiClient.get(REQUEST_PATH);
167 assertEquals(expectedErrorMsg, chefResponse.getBody());
168 assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, chefResponse.getStatusCode());
172 public void execute_shouldHandleException_whenEndpointURIisMalformed() {
174 String expectedErrorMsg = "Malformed escape pair at index 1: /%#@/";
177 ChefApiClient chefApiClient = chefApiClientFactory.create(
182 ChefResponse chefResponse = chefApiClient.get(REQUEST_PATH);
185 assertEquals(expectedErrorMsg, chefResponse.getBody());
186 assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, chefResponse.getStatusCode());
189 private class HttpRequestBaseMatcher extends ArgumentMatcher<HttpRequestBase> {
191 private final String methodName;
193 public HttpRequestBaseMatcher(String methodName) {
194 this.methodName = methodName;
198 public boolean matches(Object argument) {
199 HttpRequestBase httpRequestBase = (HttpRequestBase) argument;
202 return methodName.equals(httpRequestBase.getMethod())
203 && new URI(END_POINT + "/organizations/" + ORGANIZATIONS_PATH + REQUEST_PATH)
204 .equals(httpRequestBase.getURI())
205 && checkIfBodyMatches(httpRequestBase)
206 && checkIfHeadersMatch(httpRequestBase);
207 } catch (URISyntaxException e) {
213 public boolean checkIfBodyMatches(HttpRequestBase httpRequestBase) {
214 if (httpRequestBase instanceof HttpEntityEnclosingRequestBase) {
215 HttpEntityEnclosingRequestBase requestBaseWithBody = (HttpEntityEnclosingRequestBase) httpRequestBase;
216 StringEntity stringEntity = new StringEntity(BODY, "UTF-8");
217 stringEntity.setContentType("application/json");
218 return stringEntity.toString().equals(requestBaseWithBody.getEntity().toString());
223 private boolean checkIfHeadersMatch(HttpRequestBase httpRequestBase) {
224 Header[] generatedHeaders = httpRequestBase.getAllHeaders();
225 return generatedHeaders.length > 0
226 && generatedHeaders.length == HEADERS.size()
227 && HEADERS.entrySet().stream()
228 .allMatch(p -> httpRequestBase.getFirstHeader(p.getKey()).getValue().equals(p.getValue()));