diff options
Diffstat (limited to 'adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java')
11 files changed, 2202 insertions, 0 deletions
diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiClientImplTest.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiClientImplTest.java new file mode 100644 index 000000000..6f9601c7d --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiClientImplTest.java @@ -0,0 +1,231 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.impl; + +import com.google.common.collect.ImmutableMap; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.function.Supplier; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.ChefApiClientFactory; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefApiClient; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse; + +import static junit.framework.TestCase.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; + +@RunWith(MockitoJUnitRunner.class) +public class ChefApiClientImplTest { + + private static final String END_POINT = "https://chefServer"; + private static final String ORGANIZATIONS_PATH = "onap"; + private static final String USER_ID = "testUser"; + private static final String REQUEST_PATH = "/test/path"; + private static final String BODY = "SOME BODY STRING"; + private static final String PEM_FILEPATH = "path/to/pemFile"; + private static final ImmutableMap<String, String> HEADERS = ImmutableMap.<String, String>builder() + .put("Content-type", "application/json") + .put("Accept", "application/json") + .put("X-Ops-Timestamp", "1970-01-15T06:56:07Z") + .put("X-Ops-UserId", USER_ID) + .put("X-Chef-Version", "12.4.1") + .put("X-Ops-Content-Hash", BODY) + .put("X-Ops-Sign", "version=1.0").build(); + + @Mock + private HttpClient httpClient; + @Mock + private org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.ChefApiHeaderFactory chefHttpHeaderFactory; + + @InjectMocks + private ChefApiClientFactory chefApiClientFactory; + private ChefApiClient chefApiClient; + + @Before + public void setUp() { + chefApiClient = chefApiClientFactory.create( + END_POINT, + ORGANIZATIONS_PATH, + USER_ID, + PEM_FILEPATH); + } + + @Test + public void execute_HttpGet_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException { + // GIVEN + String methodName = "GET"; + String body = ""; + Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.get(REQUEST_PATH); + + // WHEN //THEN + assertChefApiClientCall(methodName, body, chefClientApiCall); + } + + @Test + public void execute_HttpDelete_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException { + // GIVEN + String methodName = "DELETE"; + String body = ""; + Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.delete(REQUEST_PATH); + + // WHEN //THEN + assertChefApiClientCall(methodName, body, chefClientApiCall); + } + + @Test + public void execute_HttpPost_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException { + // GIVEN + String methodName = "POST"; + Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.post(REQUEST_PATH, BODY); + + // WHEN //THEN + assertChefApiClientCall(methodName, BODY, chefClientApiCall); + } + + @Test + public void execute_HttpPut_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException { + // GIVEN + String methodName = "PUT"; + Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.put(REQUEST_PATH, BODY); + + // WHEN //THEN + assertChefApiClientCall(methodName, BODY, chefClientApiCall); + } + + private void assertChefApiClientCall(String methodName, String body, Supplier<ChefResponse> httpMethod) + throws IOException { + // GIVEN + given(chefHttpHeaderFactory.create(methodName, REQUEST_PATH, body, USER_ID, ORGANIZATIONS_PATH, PEM_FILEPATH)) + .willReturn(HEADERS); + + StatusLine statusLine = mock(StatusLine.class); + given(statusLine.getStatusCode()).willReturn(HttpStatus.SC_OK); + HttpResponse httpResponse = mock(HttpResponse.class); + given(httpResponse.getStatusLine()).willReturn(statusLine); + given(httpResponse.getEntity()).willReturn(new StringEntity("Successful Response String")); + given(httpClient.execute(argThat(new HttpRequestBaseMatcher(methodName)))) + .willReturn(httpResponse); + + // WHEN + ChefResponse chefResponse = httpMethod.get(); + + // THEN + assertEquals("Successful Response String", chefResponse.getBody()); + assertEquals(HttpStatus.SC_OK, chefResponse.getStatusCode()); + } + + @Test + public void execute_shouldHandleException_whenHttpClientExecutionFails() throws IOException { + + // GIVEN + given(chefHttpHeaderFactory.create("GET", REQUEST_PATH, "", USER_ID, ORGANIZATIONS_PATH, PEM_FILEPATH)) + .willReturn(HEADERS); + + String expectedErrorMsg = "HttpClient call failed"; + given(httpClient.execute(argThat(new HttpRequestBaseMatcher("GET")))) + .willThrow(new IOException(expectedErrorMsg)); + + // WHEN + ChefResponse chefResponse = chefApiClient.get(REQUEST_PATH); + + // THEN + assertEquals(expectedErrorMsg, chefResponse.getBody()); + assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, chefResponse.getStatusCode()); + } + + @Test + public void execute_shouldHandleException_whenEndpointURIisMalformed() { + // GIVEN + String expectedErrorMsg = "Malformed escape pair at index 1: /%#@/"; + + // WHEN + ChefApiClient chefApiClient = chefApiClientFactory.create( + "/%#@/", + ORGANIZATIONS_PATH, + USER_ID, + PEM_FILEPATH); + ChefResponse chefResponse = chefApiClient.get(REQUEST_PATH); + + // THEN + assertEquals(expectedErrorMsg, chefResponse.getBody()); + assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, chefResponse.getStatusCode()); + } + + private class HttpRequestBaseMatcher extends ArgumentMatcher<HttpRequestBase> { + + private final String methodName; + + public HttpRequestBaseMatcher(String methodName) { + this.methodName = methodName; + } + + @Override + public boolean matches(Object arguments) { + HttpRequestBase httpRequestBase = (HttpRequestBase) arguments; + try { + return methodName.equals(httpRequestBase.getMethod()) + && new URI(END_POINT + "/organizations/" + ORGANIZATIONS_PATH + REQUEST_PATH).equals(httpRequestBase.getURI()) + && checkIfBodyMatches(httpRequestBase) + && checkIfHeadersMatch(httpRequestBase); + } catch (URISyntaxException e) { + e.printStackTrace(); + return false; + } + } + + public boolean checkIfBodyMatches(HttpRequestBase httpRequestBase) { + if (httpRequestBase instanceof HttpEntityEnclosingRequestBase) { + HttpEntityEnclosingRequestBase requestBaseWithBody = (HttpEntityEnclosingRequestBase) httpRequestBase; + StringEntity stringEntity = new StringEntity(BODY, "UTF-8"); + stringEntity.setContentType("application/json"); + return stringEntity.toString().equals(requestBaseWithBody.getEntity().toString()); + } + return true; + } + + private boolean checkIfHeadersMatch(HttpRequestBase httpRequestBase) { + Header[] generatedHeaders = httpRequestBase.getAllHeaders(); + return generatedHeaders.length > 0 + && generatedHeaders.length == HEADERS.size() + && HEADERS.entrySet().stream().allMatch(p -> httpRequestBase.getFirstHeader(p.getKey()).getValue().equals(p.getValue())); + } + + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiHeaderFactoryTest.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiHeaderFactoryTest.java new file mode 100644 index 000000000..f890d9249 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiHeaderFactoryTest.java @@ -0,0 +1,85 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.impl; + +import com.google.common.collect.ImmutableMap; +import java.util.Date; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static junit.framework.TestCase.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; + +@RunWith(MockitoJUnitRunner.class) +public class ChefApiHeaderFactoryTest { + + private static final String ORGANIZATIONS_PATH = "onap"; + private static final String USER_ID = "testUser"; + private static final String REQUEST_PATH = "/test/path"; + private static final String EXPECTED_TIMESTAMP = "1970-01-15T06:56:07Z"; + private static final String EMPTY_BODY = ""; + + @Mock + private org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.FormattedTimestamp formattedTimestamp; + + @InjectMocks + private org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.ChefApiHeaderFactory chefApiHeaderFactory; + + @Test + public void create_shouldCreateProperChefHeaders_withHashedAuthorizationString() { + // GIVEN + given(formattedTimestamp.format(any(Date.class))).willReturn(EXPECTED_TIMESTAMP); + String pemFilePath = getClass().getResource("/testclient.pem").getPath(); + + // WHEN + ImmutableMap<String, String> headers = chefApiHeaderFactory + .create("GET", REQUEST_PATH, "", USER_ID, ORGANIZATIONS_PATH, pemFilePath); + + // THEN + assertEquals(headers, createExpectedHeaders()); + } + + private ImmutableMap<String, String> createExpectedHeaders() { + String hashedBody = org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.Utils.sha1AndBase64(EMPTY_BODY); + ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); + builder + .put("Content-type", "application/json") + .put("Accept", "application/json") + .put("X-Ops-Timestamp", EXPECTED_TIMESTAMP) + .put("X-Ops-UserId", USER_ID) + .put("X-Chef-Version", "12.4.1") + .put("X-Ops-Content-Hash", hashedBody) + .put("X-Ops-Sign", "version=1.0") + .put("X-Ops-Authorization-1", "i+HGCso703727yd2ZQWMZIIpGKgTzm41fA31LIExNxEf9mOUMcpesIHjH/Wr") + .put("X-Ops-Authorization-2", "QEvsX/Gy1ay9KsUtqhy9GA6PB8UfDeMNoVUisqR4HQW+S6IOfvqBjW+2afzE") + .put("X-Ops-Authorization-3", "RdRReB/TJIF3s6ZC8vNpbEdY9kHmwiDglhxmS8X2FS+ArSh/DK/i7MqBbjux") + .put("X-Ops-Authorization-4", "49iiOlRVG7aTr/FA115hlBYP9CYCIQWKIBUOK3JyV9fXNdVqc9R0r1XdjxUl") + .put("X-Ops-Authorization-5", "EDGw6tuE8YW8mH5wkgHCjKpXG3WjmWt2X6kUrdIu44qCBK2N3sZziSub2fJA") + .put("X-Ops-Authorization-6", "hPBuOhjiYDZuFUqC99lCryM0Hf5RMw1uTlkYsBEZmA=="); + + return builder.build(); + } + +}
\ No newline at end of file diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/FormattedTimestampTest.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/FormattedTimestampTest.java new file mode 100644 index 000000000..8d5f10f19 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/FormattedTimestampTest.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.impl; + +import java.util.Date; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class FormattedTimestampTest { + + @Test + public void format_shouldFormatGivenDate_withCorrectTimezoneSet() { + // GIVEN + String expectedFormattedDate = "1970-01-15T06:56:07Z"; + + // WHEN + String formattedDateWithTimezone = new org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.FormattedTimestamp().format(new Date(1234567890)); + + // THEN + assertEquals(expectedFormattedDate, formattedDateWithTimezone); + } + +}
\ No newline at end of file diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplDataRetrieverTest.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplDataRetrieverTest.java new file mode 100644 index 000000000..16f0e0174 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplDataRetrieverTest.java @@ -0,0 +1,83 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.impl; + +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; + +public class ChefAdaptorImplDataRetrieverTest { + + private static final String KEY_PARAM = "key"; + private static final String DG_CONTEXT_PARAM = "dgContext"; + private static final String ALL_CONFIG_PARAM = "allConfig"; + private static final String KEY_VALUE = "keyValue"; + private static final String DG_CONTEXT_VALUE = "contextValue"; + + @Test + public void retrieveData_shouldSetContextData_withExtractedJsonString() { + // GIVEN + Map<String, String> params = givenParamMapWithJson("{" + KEY_VALUE + ":testValue}"); + SvcLogicContext svcLogicContext = new SvcLogicContext(); + + // WHEN + new org.onap.ccsdk.sli.adaptors.chef.impl.ChefAdaptorFactory().create().retrieveData(params, svcLogicContext); + + // THEN + Assertions.assertThat(svcLogicContext.getAttribute(DG_CONTEXT_VALUE)).isEqualTo("testValue"); + } + + @Test + public void retrieveData_shouldSetContextData_withExtractedJsonObject() { + // GIVEN + Map<String, String> params = givenParamMapWithJson("{" + KEY_VALUE + ": {param : testValue} }"); + SvcLogicContext svcLogicContext = new SvcLogicContext(); + + // WHEN + new org.onap.ccsdk.sli.adaptors.chef.impl.ChefAdaptorFactory().create().retrieveData(params, svcLogicContext); + + // THEN + Assertions.assertThat(svcLogicContext.getAttribute(DG_CONTEXT_VALUE)).isEqualTo("{\"param\":\"testValue\"}"); + } + + @Test + public void retrieveData_shouldSetContextData_withExtractedJsonArray() { + // GIVEN + Map<String, String> params = givenParamMapWithJson("{" + KEY_VALUE + ": [val1, val2, val3] }"); + SvcLogicContext svcLogicContext = new SvcLogicContext(); + + // WHEN + new org.onap.ccsdk.sli.adaptors.chef.impl.ChefAdaptorFactory().create().retrieveData(params, svcLogicContext); + + // THEN + Assertions.assertThat(svcLogicContext.getAttribute(DG_CONTEXT_VALUE)).isEqualTo("[\"val1\",\"val2\",\"val3\"]"); + } + + private Map<String, String> givenParamMapWithJson(String json) { + return ImmutableMap + .of(KEY_PARAM, KEY_VALUE, + DG_CONTEXT_PARAM, DG_CONTEXT_VALUE, + ALL_CONFIG_PARAM, json); + } + +}
\ No newline at end of file diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplHttpMethodTest.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplHttpMethodTest.java new file mode 100644 index 000000000..283b51ebb --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplHttpMethodTest.java @@ -0,0 +1,185 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.impl; + +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.apache.http.HttpStatus; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.onap.ccsdk.sli.adaptors.chef.ChefAdaptor; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.ChefApiClientFactory; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefApiClient; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.verifyZeroInteractions; + +@RunWith(MockitoJUnitRunner.class) +public class ChefAdaptorImplHttpMethodTest { + + private static final String CLIENT_PRIVATE_KEY_PATH = "/opt/onap/appc/chef/localhost/onap/testclient.pem"; + private static final String RESULT_CODE_ATTR_KEY = "chefServerResult.code"; + private static final String RESULT_MESSAGE_ATTR_KEY = "chefServerResult.message"; + private static final String EXPECTED_RESPONSE_MSG = "chefResponseMessage"; + private static final String ACTION_PARAM = "action"; + private static final String REQUEST_BODY_DATA = "requestBodyData"; + private static final Map<String, String> PARAMS = ImmutableMap + .of("username", "testclient", + "serverAddress", "localhost", + "organizations", "onap", + "chefAction", ACTION_PARAM, + "chefRequestBody", REQUEST_BODY_DATA); + @Mock + private org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker privateKeyChecker; + @Mock + private ChefApiClientFactory chefApiClientFactory; + @Mock + private ChefApiClient chefApiClient; + + @InjectMocks + private org.onap.ccsdk.sli.adaptors.chef.impl.ChefAdaptorFactory chefAdaptorFactory; + private SvcLogicContext svcLogicContext; + + @Before + public void setUp() { + svcLogicContext = new SvcLogicContext(); + } + + @Test + public void chefGet_shouldExecuteHttpClient_andSetChefResponseInContext_whenPrivateKeyFileExists() { + assertSuccessfulChefHttpCallFor(() -> chefApiClient.get(ACTION_PARAM), this :: chefGet); + } + + @Test + public void chefGet_shouldNotExecuteHttpClient_andSetErrorResponseInContext_whenPrivateKeyFileDoesNotExist() { + assertNoChefCallOccurFor(this :: chefGet); + } + + @Test + public void chefDelete_shouldExecuteHttpClient_andSetChefResponseInContext_whenPrivateKeyFileExists() { + assertSuccessfulChefHttpCallFor(() -> chefApiClient.delete(ACTION_PARAM), this :: chefDelete); + } + + @Test + public void chefDelete_shouldNotExecuteHttpClient_andSetErrorResponseInContext_whenPrivateKeyFileDoesNotExist() { + assertNoChefCallOccurFor(this :: chefDelete); + } + + @Test + public void chefPut_shouldExecuteHttpClient_andSetChefResponseInContext_whenPrivateKeyFileExists() { + assertSuccessfulChefHttpCallFor(() -> chefApiClient.put(ACTION_PARAM, REQUEST_BODY_DATA), this :: chefPut); + } + + @Test + public void chefPut_shouldNotExecuteHttpClient_andSetErrorResponseInContext_whenPrivateKeyFileDoesNotExist() { + assertNoChefCallOccurFor(this :: chefPut); + } + + @Test + public void chefPost_shouldExecuteHttpClient_andSetChefResponseInContext_whenPrivateKeyFileExists() { + assertSuccessfulChefHttpCallFor(() -> chefApiClient.post(ACTION_PARAM, REQUEST_BODY_DATA), this :: chefPost); + } + + @Test + public void chefPost_shouldNotExecuteHttpClient_andSetErrorResponseInContext_whenPrivateKeyFileDoesNotExist() { + assertNoChefCallOccurFor(this :: chefPost); + } + + public void assertSuccessfulChefHttpCallFor(Supplier<ChefResponse> responseSupplier, + Consumer<ChefAdaptor> chefAdaptorCall) { + // GIVEN + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(true); + given(chefApiClientFactory.create("https://localhost/organizations/onap", + "onap", + "testclient", + CLIENT_PRIVATE_KEY_PATH)).willReturn(chefApiClient); + given(responseSupplier.get()).willReturn(ChefResponse.create(HttpStatus.SC_OK, EXPECTED_RESPONSE_MSG)); + + // WHEN + chefAdaptorCall.accept(chefAdaptorFactory.create()); + + // THEN + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_OK)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(EXPECTED_RESPONSE_MSG); + } + + public void assertNoChefCallOccurFor(Consumer<ChefAdaptor> chefAdaptorCall) { + // GIVEN + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(false); + + // WHEN + chefAdaptorCall.accept(chefAdaptorFactory.create()); + + // THEN + verifyZeroInteractions(chefApiClient); + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)) + .isEqualTo(Integer.toString(HttpStatus.SC_INTERNAL_SERVER_ERROR)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo( + "Cannot find the private key in the APPC file system, please load the private key to " + + CLIENT_PRIVATE_KEY_PATH); + } + + public void chefGet(ChefAdaptor chefAdaptor) { + try { + chefAdaptor.chefGet(PARAMS, svcLogicContext); + } catch (SvcLogicException e) { + e.printStackTrace(); + } + } + + public void chefDelete(ChefAdaptor chefAdaptor) { + try { + chefAdaptor.chefDelete(PARAMS, svcLogicContext); + } catch (SvcLogicException e) { + e.printStackTrace(); + } + } + + public void chefPost(ChefAdaptor chefAdaptor) { + try { + chefAdaptor.chefPost(PARAMS, svcLogicContext); + } catch (SvcLogicException e) { + e.printStackTrace(); + } + } + + public void chefPut(ChefAdaptor chefAdaptor) { + try { + chefAdaptor.chefPut(PARAMS, svcLogicContext); + } catch (SvcLogicException e) { + e.printStackTrace(); + } + } + +}
\ No newline at end of file diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplJobPusherTest.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplJobPusherTest.java new file mode 100644 index 000000000..4bf5a2280 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplJobPusherTest.java @@ -0,0 +1,269 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.impl; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import java.util.Map; +import java.util.Map.Entry; +import org.apache.http.HttpStatus; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.ChefApiClientFactory; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefApiClient; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import static com.google.common.collect.Maps.immutableEntry; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.BDDMockito.given; + +@RunWith(MockitoJUnitRunner.class) +public class ChefAdaptorImplJobPusherTest { + + private static final String CLIENT_PRIVATE_KEY_PATH = "/opt/onap/appc/chef/localhost/onap/testclient.pem"; + private static final String RESULT_CODE_ATTR_KEY = "chefServerResult.code"; + private static final String RESULT_MESSAGE_ATTR_KEY = "chefServerResult.message"; + private static final String EXPECTED_RESPONSE_MSG = "jobs/{666}/"; + + private static final String USERNAME = "testclient"; + private static final String SERVER_ADDRESS = "localhost"; + private static final String ORGANIZATIONS = "onap"; + private static final String ACTION_PARAM = "/pushy/jobs"; + private static final String REQUEST_BODY_DATA = "requestBodyData"; + private static final String JOB_ID = "jobID"; + + @Mock + private org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker privateKeyChecker; + + @Mock + private ChefApiClientFactory chefApiClientFactory; + + @Mock + private ChefApiClient chefApiClient; + + @InjectMocks + private org.onap.ccsdk.sli.adaptors.chef.impl.ChefAdaptorFactory chefAdaptorFactory; + private SvcLogicContext svcLogicContext; + + @Before + public void setUp() { + svcLogicContext = new SvcLogicContext(); + } + + @Test + public void pushJob_shouldSuccessfullyMakePostCall_andUpdateSvcLogicContext_whenReturnedStatusIsDifferentThan_201() + throws SvcLogicException { + assertSuccessfulPostCallForStatus(HttpStatus.SC_OK); + assertThat(svcLogicContext.getAttribute(JOB_ID)).isBlank(); + } + + @Test + public void pushJob_shouldSuccessfullyMakePostCall_andUpdateSvcLogicContext_withReturnedStatusIs_201() + throws SvcLogicException { + assertSuccessfulPostCallForStatus(HttpStatus.SC_CREATED); + assertThat(svcLogicContext.getAttribute(JOB_ID)).isEqualTo("666"); + } + + @SuppressWarnings("unchecked") + public void assertSuccessfulPostCallForStatus(int expectedHttpStatus) throws SvcLogicException { + // GIVEN + Map<String, String> params = givenInputParams( + immutableEntry("chefAction", ACTION_PARAM), + immutableEntry("pushRequest", REQUEST_BODY_DATA)); + given(chefApiClientFactory.create("https://localhost/organizations/onap", ORGANIZATIONS, USERNAME, + CLIENT_PRIVATE_KEY_PATH)).willReturn(chefApiClient); + given(chefApiClient.post(ACTION_PARAM, REQUEST_BODY_DATA)) + .willReturn(ChefResponse.create(expectedHttpStatus, EXPECTED_RESPONSE_MSG)); + + // WHEN + chefAdaptorFactory.create().pushJob(params, svcLogicContext); + + // THEN + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(expectedHttpStatus)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(EXPECTED_RESPONSE_MSG); + } + + @SuppressWarnings("unchecked") + @Test + public void pushJob_shouldHandleAllOccurringExceptions_duringMethodExecution() { + // GIVEN + Map<String, String> params = givenInputParams(); + String expectedErrorMessage = "Something went wrong"; + given(chefApiClientFactory.create("https://localhost/organizations/onap", ORGANIZATIONS, USERNAME, + CLIENT_PRIVATE_KEY_PATH)).willThrow(new NullPointerException(expectedErrorMessage)); + + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().pushJob(params, svcLogicContext)) + .withMessage("Chef Adaptor error:" + expectedErrorMessage); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(expectedErrorMessage); + assertThat(svcLogicContext.getAttribute(JOB_ID)).isBlank(); + } + + @SuppressWarnings("unchecked") + @Test + public void checkPushJob_shouldSetFailStatusAndMsgInContext_andThrowException_whenRetryTimesParamIsMissing() { + // GIVEN + Map<String, String> params = givenInputParams( + immutableEntry("retryInterval", "1"), + immutableEntry("jobid", "666")); + + // WHEN // THEN + assertIfInputParamsAreValidated(params); + } + + @SuppressWarnings("unchecked") + @Test + public void checkPushJob_shouldSetFailStatusAndMsgInContext_andThrowException_whenRetryIntervalParamIsMissing() { + // GIVEN + Map<String, String> params = givenInputParams( + immutableEntry("retryTimes", "4"), + immutableEntry("jobid", "666")); + + // WHEN // THEN + assertIfInputParamsAreValidated(params); + } + + @SuppressWarnings("unchecked") + @Test + public void checkPushJob_shouldSetFailStatusAndMsgInContext_andThrowException_whenJobIdParamIsMissing() { + // GIVEN + Map<String, String> params = givenInputParams( + immutableEntry("retryTimes", "4"), + immutableEntry("retryInterval", "1")); + assertIfInputParamsAreValidated(params); + } + + public void assertIfInputParamsAreValidated(Map<String, String> params) { + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().checkPushJob(params, svcLogicContext)) + .withMessage("Chef Adaptor error:" + "Missing Mandatory param(s) retryTimes , retryInterval "); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo("Missing Mandatory param(s) retryTimes , retryInterval "); + } + + @Test + public void checkPushJob_shouldCheckJobStatusOnlyOnce_withoutAdditionalRetries_whenFirstReturnedJobStatusIs_Complete() + throws SvcLogicException { + String expectedHttpStatus = Integer.toString(HttpStatus.SC_OK); + //String expectedMessage = "{status:complete}"; + String expectedMessage = "{\"nodes\":{\"succeeded\":[\"NODE1.atttest.com\"]},\"id\":\"26d\",\"command\":\"chef-client\",\"status\":\"complete\"} "; + assertCheckJobStatusFor( + expectedHttpStatus, + expectedMessage, + ChefResponse.create(HttpStatus.SC_OK, expectedMessage), + ChefResponse.create(HttpStatus.SC_OK, "{status:running}")); + } + + @Test + public void checkPushJob_withFailedNode_whenFirstReturnedJobStatusIs_Complete() + throws SvcLogicException { + String expectedHttpStatus = "401"; + String message = "{\"nodes\":{\"failed\":[\"NODE1.atttest.com\"]},\"id\":\"26d\",\"command\":\"chef-client\",\"status\":\"complete\"} "; + String expectedMessage = "PushJob Status Complete but check failed nodes in the message :" + message; + + assertCheckJobStatusFor( + expectedHttpStatus, + expectedMessage, + ChefResponse.create(HttpStatus.SC_OK, message)); + } + + @Test + public void checkPushJob_shouldCheckJobStatusExpectedNumberOf_ThreeRetryTimes_whenEachReturnedStatusIs_Running() + throws SvcLogicException { + String expectedHttpStatus = Integer.toString(HttpStatus.SC_ACCEPTED); + String expectedMessage = "chef client runtime out"; + + assertCheckJobStatusFor( + expectedHttpStatus, + expectedMessage, + ChefResponse.create(HttpStatus.SC_OK, "{status:running}"), + ChefResponse.create(HttpStatus.SC_OK, "{status:running}"), + ChefResponse.create(HttpStatus.SC_OK, "{status:running}")); + } + + @Test + public void checkPushJob_shouldCheckJobStatusOnlyOnce_withoutAdditionalRetries_whenFirstReturnedJobStatusIsNot_Running() + throws SvcLogicException { + + String expectedHttpStatus = Integer.toString(HttpStatus.SC_INTERNAL_SERVER_ERROR); + String expectedMessage = "{status:unexpectedStatus}"; + + assertCheckJobStatusFor( + expectedHttpStatus, + expectedMessage, + ChefResponse.create(HttpStatus.SC_OK, "{status:unexpectedStatus}"), + ChefResponse.create(HttpStatus.SC_OK, "{status:running}")); + } + + @SuppressWarnings("unchecked") + public void assertCheckJobStatusFor(String expectedHttpStatus, String expectedMessage, ChefResponse firstResponse, + ChefResponse... nextResponses) throws SvcLogicException { + + // GIVEN + Map<String, String> params = givenInputParams( + immutableEntry("jobid", "666"), + immutableEntry("retryTimes", "3"), + immutableEntry("retryInterval", "1")); + given(chefApiClientFactory.create("https://localhost/organizations/onap", ORGANIZATIONS, USERNAME, + CLIENT_PRIVATE_KEY_PATH)).willReturn(chefApiClient); + given(chefApiClient.get(ACTION_PARAM + "/" + params.get("jobid"))) + .willReturn(firstResponse, nextResponses); + + // WHEN + chefAdaptorFactory.create().checkPushJob(params, svcLogicContext); + + // THEN + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(expectedHttpStatus); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(expectedMessage); + } + + @SuppressWarnings("unchecked") + private Map<String, String> givenInputParams(Entry<String, String>... entries) { + Builder<String, String> paramsBuilder = ImmutableMap.builder(); + paramsBuilder.put("username", USERNAME) + .put("serverAddress", SERVER_ADDRESS) + .put("organizations", ORGANIZATIONS); + + for (Entry<String, String> entry : entries) { + paramsBuilder.put(entry); + } + return paramsBuilder.build(); + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplTest.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplTest.java new file mode 100644 index 000000000..1fa6bc168 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplTest.java @@ -0,0 +1,197 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.impl; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.util.Collections; +import java.util.Map; +import org.apache.http.HttpStatus; +import org.json.JSONObject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.ChefApiClientFactory; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Answers.RETURNS_DEEP_STUBS; +import static org.mockito.BDDMockito.given; + +@RunWith(MockitoJUnitRunner.class) +public class ChefAdaptorImplTest { + + private static final String EXPECTED_NODE_OBJECT_ATTR_NAME = "chef.nodeObject"; + private static final String RESULT_CODE_ATTR_KEY = "chefClientResult.code"; + private static final String RESULT_MESSAGE_ATTR_KEY = "chefClientResult.message"; + private static final String EXPECTED_RESPONSE_MSG = "chefResponseMessage"; + private static final String IP_PARAM = "ip"; + private static final String ENDPOINT_IP = "http://127.0.0.1"; + private static final String CHEF_AGENT_CODE_KEY = "chefAgent.code"; + private static final String CHEF_AGENT_MESSAGE_KEY = "chefAgent.message"; + + @Mock(answer = RETURNS_DEEP_STUBS) + private ChefApiClientFactory chefApiClientFactory; + @Mock + private org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker privateKeyChecker; + + @InjectMocks + private org.onap.ccsdk.sli.adaptors.chef.impl.ChefAdaptorFactory chefAdaptorFactory; + + @Test + public void nodeObjectBuilder_shouldBuildJsonNodeObject_forPassedParams_andAddToSvcLogicContext() { + // GIVEN + Map<String, String> params = givenInputParams(); + SvcLogicContext svcLogicContext = new SvcLogicContext(); + + // WHEN + chefAdaptorFactory.create().nodeObejctBuilder(params, svcLogicContext); + + // THEN + assertThat(resultJson(svcLogicContext)).isEqualTo(expectedJson()); + } + + private String resultJson(SvcLogicContext svcLogicContext) { + String resultJsonString = svcLogicContext.getAttribute(EXPECTED_NODE_OBJECT_ATTR_NAME); + return new JSONObject(resultJsonString).toString(); + } + + private Map<String, String> givenInputParams() { + return ImmutableMap.<String, String>builder() + .put("nodeobject.name", "testNodeName") + .put("nodeobject.normal", "val:normal") + .put("nodeobject.overrides", "val:override") + .put("nodeobject.defaults", "val:default") + .put("nodeobject.run_list", "val1,val2,val3") + .put("nodeobject.chef_environment", "testChefEnvVal") + .build(); + } + + private String expectedJson() { + JSONObject expectedJson = new JSONObject(); + expectedJson.put("json_class", "Chef::Node"); + expectedJson.put("chef_type", "node"); + expectedJson.put("automatic", Collections.emptyMap()); + expectedJson.put("name", "testNodeName"); + expectedJson.put("normal", ImmutableMap.of("val", "normal")); + expectedJson.put("override", ImmutableMap.of("val", "override")); + expectedJson.put("default", ImmutableMap.of("val", "default")); + expectedJson.put("run_list", ImmutableList.of("val1", "val2", "val3")); + expectedJson.put("chef_environment", "testChefEnvVal"); + return expectedJson.toString(); + } + + @Test + public void combineStrings_shouldConcatenateTwoParamStrings_andSetThemInSvcContext() { + // GIVEN + Map<String, String> params = ImmutableMap + .of("dgContext", "contextValue", "String1", "paramString1", "String2", "paramString2"); + SvcLogicContext svcLogicContext = new SvcLogicContext(); + + // WHEN + chefAdaptorFactory.create().combineStrings(params, svcLogicContext); + + // THEN + assertThat(svcLogicContext.getAttribute("contextValue")).isEqualTo("paramString1paramString2"); + } + + @Test + public void trigger_shouldTriggerTargetEndpoint_andUpdateSvclogicContext() { + // GIVEN + Map<String, String> params = ImmutableMap.of(IP_PARAM, ENDPOINT_IP); + SvcLogicContext svcLogicContext = new SvcLogicContext(); + given(chefApiClientFactory.create(ENDPOINT_IP, "").get("")) + .willReturn(ChefResponse.create(HttpStatus.SC_OK, EXPECTED_RESPONSE_MSG)); + + // WHEN + chefAdaptorFactory.create().trigger(params, svcLogicContext); + + // THEN + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(CHEF_AGENT_CODE_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_OK)); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_OK)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(EXPECTED_RESPONSE_MSG); + } + + @Test + public void trigger_shouldUpdateSvcLogicContext_withFailStatusAndMsg_whenExceptionOccurs() { + // GIVEN + Map<String, String> params = ImmutableMap.of(IP_PARAM, ENDPOINT_IP); + SvcLogicContext svcLogicContext = new SvcLogicContext(); + given(chefApiClientFactory.create(ENDPOINT_IP, "")).willThrow(new RuntimeException()); + + // WHEN + chefAdaptorFactory.create().trigger(params, svcLogicContext); + + // THEN + assertThat(svcLogicContext.getAttribute(CHEF_AGENT_CODE_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_INTERNAL_SERVER_ERROR)); + assertThat(svcLogicContext.getAttribute(CHEF_AGENT_MESSAGE_KEY)).isEqualTo(new RuntimeException().toString()); + } + + @Test + public void chefInfo_shouldUpdateSvcLogicContext_withFailStatusAndMsg_andThrowException_whenUsernameParamIsMissing() { + Map<String, String> params = ImmutableMap.of( + "serverAddress", "http://chefAddress", + "organizations", "onap"); + checkIfInputParamsAreValidated(params); + } + + @Test + public void chefInfo_shouldUpdateSvcLogicContext_withFailStatusAndMsg_andThrowException_whenServerAddressParamIsMissing() { + Map<String, String> params = ImmutableMap.of( + "username", "TestUsername", + "organizations", "onap"); + checkIfInputParamsAreValidated(params); + } + + @Test + public void chefInfo_shouldUpdateSvcLogicContext_withFailStatusAndMsg_andThrowException_whenOrganizationsParamIsMissing() { + Map<String, String> params = ImmutableMap.of( + "username", "TestUsername", + "serverAddress", "http://chefAddress"); + checkIfInputParamsAreValidated(params); + } + + private void checkIfInputParamsAreValidated(Map<String, String> params) { + // GIVEN + String expectedErrorMsg = "Missing mandatory param(s) such as username, serverAddress, organizations"; + SvcLogicContext svcLogicContext = new SvcLogicContext(); + + // WHEN// THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().chefGet(params, svcLogicContext)) + .withMessage("Chef Adaptor error:" + + expectedErrorMsg); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute("chefServerResult.code")).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute("chefServerResult.message")).isEqualTo(expectedErrorMsg); + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplVNFCOperationsTest.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplVNFCOperationsTest.java new file mode 100644 index 000000000..253cfcc9f --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImplVNFCOperationsTest.java @@ -0,0 +1,452 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.impl; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import java.util.Map; +import java.util.Map.Entry; +import org.apache.http.HttpStatus; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.ChefApiClientFactory; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefApiClient; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import static com.google.common.collect.Maps.immutableEntry; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.mockito.BDDMockito.given; + +@RunWith(MockitoJUnitRunner.class) +public class ChefAdaptorImplVNFCOperationsTest { + + private static final String CHEF_END_POINT = "https://localhost/organizations/onap"; + private static final String USERNAME = "testclient"; + private static final String ORGANIZATIONS = "onap"; + private static final String SERVER_ADDRESS = "localhost"; + private static final String CLIENT_PRIVATE_KEY_PATH = "/opt/onap/appc/chef/localhost/onap/testclient.pem"; + private static final String RESULT_CODE_ATTR_KEY = "chefServerResult.code"; + private static final String RESULT_MESSAGE_ATTR_KEY = "chefServerResult.message"; + private static final String FAILURE_STATUS = "failure"; + private static final String SUCCESS_STATUS = "success"; + private static final String CHEF_ADAPTOR_ERROR_PREFIX = "Chef Adaptor error:"; + private static final String ENV_PARAM_KEY = "Environment"; + private static final String ENV_JSON_VALUE = "{name:envName}"; + + @Mock + private org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker privateKeyChecker; + + @Mock + private ChefApiClientFactory chefApiClientFactory; + + @Mock + private ChefApiClient chefApiClient; + + @InjectMocks + private org.onap.ccsdk.sli.adaptors.chef.impl.ChefAdaptorFactory chefAdaptorFactory; + private SvcLogicContext svcLogicContext; + + @Before + public void setUp() { + svcLogicContext = new SvcLogicContext(); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcEnvironment_shouldSkipEnvironmentCreation_whenEnvParamIsEmpty() throws SvcLogicException { + // GIVEN + Map<String, String> params = givenInputParams(immutableEntry(ENV_PARAM_KEY, "")); + + // WHEN + chefAdaptorFactory.create().vnfcEnvironment(params, svcLogicContext); + + // THEN + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_OK)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo("Skip Environment block "); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcEnvironment_shouldCreateNewEnvironment_forEnvParam_whenRequestedEnvDoesNotExist() + throws SvcLogicException { + // GIVEN + String expectedErrorMessage = "New Environment Created"; + Map<String, String> params = givenInputParams(immutableEntry(ENV_PARAM_KEY, ENV_JSON_VALUE)); + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(true); + given(chefApiClientFactory.create(CHEF_END_POINT, ORGANIZATIONS, USERNAME, + CLIENT_PRIVATE_KEY_PATH)).willReturn(chefApiClient); + given(chefApiClient.put("/environments/" + "envName", ENV_JSON_VALUE)) + .willReturn(ChefResponse.create(HttpStatus.SC_NOT_FOUND, "")); + given(chefApiClient.post("/environments", ENV_JSON_VALUE)) + .willReturn(ChefResponse.create(HttpStatus.SC_CREATED, expectedErrorMessage)); + + // WHEN + chefAdaptorFactory.create().vnfcEnvironment(params, svcLogicContext); + + // THEN + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_CREATED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(expectedErrorMessage); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcEnvironment_shouldNotAttemptEnvCreation_andThrowException_whenPrivateKeyCheckFails() { + // GIVEN + String expectedErrorMsg = "Cannot find the private key in the APPC file system, please load the private key to "; + Map<String, String> params = givenInputParams(immutableEntry(ENV_PARAM_KEY, ENV_JSON_VALUE)); + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(false); + + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().vnfcEnvironment(params, svcLogicContext)) + .withMessage(CHEF_ADAPTOR_ERROR_PREFIX + expectedErrorMsg + CLIENT_PRIVATE_KEY_PATH); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_INTERNAL_SERVER_ERROR)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(expectedErrorMsg + CLIENT_PRIVATE_KEY_PATH); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcEnvironment_shouldNotAttemptEnvCreation_andHandleJSONException_whenJSONParamsAreMalformed() { + // GIVEN + String expectedErrorMessage = "Error posting request due to invalid JSON block: "; + Map<String, String> params = givenInputParams(immutableEntry(ENV_PARAM_KEY, "MALFORMED_JSON")); + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(true); + + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().vnfcEnvironment(params, svcLogicContext)) + .withMessageStartingWith(CHEF_ADAPTOR_ERROR_PREFIX + expectedErrorMessage); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).startsWith(expectedErrorMessage); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcEnvironment_shouldNotAttemptEnvCreation_andHandleException_whenExceptionOccursDuringExecution() { + // GIVEN + String expectedErrorMessage = "Error posting request: "; + Map<String, String> params = givenInputParams(immutableEntry(ENV_PARAM_KEY, ENV_JSON_VALUE)); + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(true); + given(chefApiClientFactory.create(CHEF_END_POINT, ORGANIZATIONS, USERNAME, + CLIENT_PRIVATE_KEY_PATH)).willThrow(new NullPointerException("Null value encountered")); + + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().vnfcEnvironment(params, svcLogicContext)) + .withMessage(CHEF_ADAPTOR_ERROR_PREFIX + expectedErrorMessage + "vnfcEnvironmentNull value encountered"); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).startsWith(expectedErrorMessage); + } + + @Test + public void vnfcNodeObjects_shouldUpdateNodeObjects_andSetCodeAndMessageFromLastSuccessfulResponseInSvcLogicContext() + throws SvcLogicException { + // GIVEN + ChefResponse firstNodeResponse = ChefResponse.create(HttpStatus.SC_OK, "firstMessage"); + ChefResponse secondNodeResponse = ChefResponse.create(HttpStatus.SC_OK, "secondMessage"); + int expectedHttpStatus = HttpStatus.SC_OK; + String expectedMessage = "secondMessage"; + + assertNodeObjectsAreUpdatedFor(firstNodeResponse, secondNodeResponse, expectedHttpStatus, expectedMessage); + } + + @Test + public void vnfcNodeObjects_shouldStopProcessingNodeObjectUpdates_whenFirstReturnedResponseIsOtherThan_200() + throws SvcLogicException { + ChefResponse firstNodeResponse = ChefResponse.create(HttpStatus.SC_ACCEPTED, "firstMessage"); + ChefResponse secondNodeResponse = ChefResponse.create(HttpStatus.SC_OK, "secondMessage"); + int expectedHttpStatus = HttpStatus.SC_ACCEPTED; + String expectedMessage = "firstMessage"; + + assertNodeObjectsAreUpdatedFor(firstNodeResponse, secondNodeResponse, expectedHttpStatus, expectedMessage); + } + + @SuppressWarnings("unchecked") + public void assertNodeObjectsAreUpdatedFor(ChefResponse firstNodeResponse, ChefResponse secondNodeResponse, + int expectedHttpStatus, String expectedMessage) throws SvcLogicException { + // GIVEN + Map<String, String> params = givenInputParams( + immutableEntry("NodeList", "[\"test1.vnf_b.onap.com\", \"test2.vnf_b.onap.com\"]"), + immutableEntry("Node", "{name:nodeName}")); + + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(true); + given(chefApiClientFactory.create(CHEF_END_POINT, ORGANIZATIONS, USERNAME, + CLIENT_PRIVATE_KEY_PATH)).willReturn(chefApiClient); + given(chefApiClient.put("/nodes/" + "test1.vnf_b.onap.com", "{\"name\":\"test1.vnf_b.onap.com\"}")) + .willReturn(firstNodeResponse); + given(chefApiClient.put("/nodes/" + "test2.vnf_b.onap.com", "{\"name\":\"test2.vnf_b.onap.com\"}")) + .willReturn(secondNodeResponse); + + // WHEN + chefAdaptorFactory.create().vnfcNodeobjects(params, svcLogicContext); + + // THEN + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(expectedHttpStatus)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(expectedMessage); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcNodeObjects_shouldThrowSvcLogicException_whenNodeListParamIsEmpty() { + Map<String, String> params = givenInputParams( + immutableEntry("NodeList", ""), + immutableEntry("Node", "{name:nodeName}")); + checkMissingParamsAreValidated(params); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcNodeObjects_shouldThrowSvcLogicException_whenNodeParamIsEmpty() { + Map<String, String> params = givenInputParams( + immutableEntry("NodeList", "[\"test1.vnf_b.onap.com\", \"test2.vnf_b.onap.com\"]"), + immutableEntry("Node", "")); + checkMissingParamsAreValidated(params); + } + + public void checkMissingParamsAreValidated(Map<String, String> params) { + // GIVEN + String expectedErrorMsg = "vnfcNodeobjectsMissing Mandatory param(s) Node , NodeList "; + + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().vnfcNodeobjects(params, svcLogicContext)) + .withMessage(CHEF_ADAPTOR_ERROR_PREFIX + "Error posting request: " + expectedErrorMsg); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo("Error posting request: " + expectedErrorMsg); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcNodeObjects_shouldNotUpdateNodes_andHandleJSONException_whenJSONParamsAreMalformed() { + // GIVEN + Map<String, String> params = givenInputParams( + immutableEntry("NodeList", "[\"test1.vnf_b.onap.com\", \"test2.vnf_b.onap.com\"]"), + immutableEntry("Node", "MALFORMED_JSON")); + String expectedErrorMessage = "Error posting request due to invalid JSON block: "; + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(true); + + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().vnfcNodeobjects(params, svcLogicContext)) + .withMessageStartingWith(CHEF_ADAPTOR_ERROR_PREFIX + expectedErrorMessage); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).startsWith(expectedErrorMessage); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcPushJob_shouldUpdateSvcContextWithJobId_whenPushJobWasSuccessfullyCreatedWithCallbackUrl() + throws SvcLogicException { + Map<String, String> params = givenInputParams( + immutableEntry("NodeList", "[\"test1.vnf_b.onap.com\", \"test2.vnf_b.onap.com\"]"), + immutableEntry("CallbackCapable", "true"), + immutableEntry("RequestId", "666"), + immutableEntry("CallbackUrl", "someURLForCallback")); + int expectedResponseStatus = HttpStatus.SC_CREATED; + String expectedResponseMessage = "jobs:666-9"; + + assertVnfcPushJobExecutionFor(params, buildJsonRequestWithCallback(), expectedResponseStatus, expectedResponseMessage); + assertThat(svcLogicContext.getAttribute("jobID")).isEqualTo("666"); + } + + private String buildJsonRequestWithCallback() { + return "{" + "\"command\": \"chef-client\"," + "\"run_timeout\": 300," + "\"nodes\":" + + "[\"test1.vnf_b.onap.com\", \"test2.vnf_b.onap.com\"]" + "," + "\"env\": {\"RequestId\": \"" + "666" + + "\", \"CallbackUrl\": \"" + + "someURLForCallback" + "\"}," + "\"capture_output\": true" + "}"; + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcPushJob_shouldUpdateSvcContextWithJobId_whenPushJobWasSuccessfullyCreatedWithoutCallbackUrl() + throws SvcLogicException { + Map<String, String> params = givenInputParams( + immutableEntry("NodeList", "[\"test1.vnf_b.onap.com\", \"test2.vnf_b.onap.com\"]"), + immutableEntry("RequestId", "666")); + int expectedResponseStatus = HttpStatus.SC_OK; + String expectedResponseMessage = "jobs:666-9"; + + assertVnfcPushJobExecutionFor(params, buildJsonRequestWithoutCallback(), expectedResponseStatus, expectedResponseMessage); + assertThat(svcLogicContext.getAttribute("jobID")).isBlank(); + } + + private String buildJsonRequestWithoutCallback() { + return "{" + "\"command\": \"chef-client\"," + "\"run_timeout\": 300," + "\"nodes\":" + + "[\"test1.vnf_b.onap.com\", \"test2.vnf_b.onap.com\"]" + "," + "\"env\": {}," + "\"capture_output\": true" + + "}"; + } + + public void assertVnfcPushJobExecutionFor(Map<String, String> params, String pushRequestWithCallback, + int expectedResponseStatus, String expectedResponseMessage) throws SvcLogicException { + // GIVEN + given(chefApiClientFactory.create(CHEF_END_POINT, ORGANIZATIONS, USERNAME, + CLIENT_PRIVATE_KEY_PATH)).willReturn(chefApiClient); + given(chefApiClient.post("/pushy/jobs", pushRequestWithCallback)) + .willReturn(ChefResponse.create(expectedResponseStatus, expectedResponseMessage)); + + // WHEN + chefAdaptorFactory.create().vnfcPushJob(params, svcLogicContext); + + // THEN + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(expectedResponseStatus)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(expectedResponseMessage); + } + + @SuppressWarnings("unchecked") + @Test + public void vnfcPushJob_shouldNotPushJob_andThrowException_whenNodeListParamIsEmpty() { + // GIVEN + String expectedErrorMessage = "Error posting request: vnfcPushJobMissing Mandatory param(s) NodeList "; + Map<String, String> params = givenInputParams(); + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().vnfcPushJob(params, svcLogicContext)) + .withMessageStartingWith(CHEF_ADAPTOR_ERROR_PREFIX + expectedErrorMessage); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(expectedErrorMessage); + } + + @SuppressWarnings("unchecked") + @Test + public void fetchResults_shouldNotFetchResults_andThrowException_whenNodeListParamIsEmpty() { + // GIVEN + String expectedErrorMessage = "Error posting request: fetchResultsMissing Mandatory param(s) NodeList "; + Map<String, String> params = givenInputParams(); + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().fetchResults(params, svcLogicContext)) + .withMessageStartingWith(CHEF_ADAPTOR_ERROR_PREFIX + expectedErrorMessage); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(expectedErrorMessage); + } + + @SuppressWarnings("unchecked") + @Test + public void fetchResults_shouldNotFetchResults_andThrowException_whenPrivateKeyCheckFails() { + // GIVEN + Map<String, String> params = givenInputParams( + immutableEntry("NodeList", "[\"test1.vnf_b.onap.com\", \"test2.vnf_b.onap.com\"]")); + String expectedErrorMessage = + "Error posting request: fetchResults" + + CHEF_ADAPTOR_ERROR_PREFIX + + "Cannot find the private key in the APPC file system, please load the private key to " + + CLIENT_PRIVATE_KEY_PATH; + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(false); + + // WHEN // THEN + assertThatExceptionOfType(SvcLogicException.class) + .isThrownBy(() -> chefAdaptorFactory.create().fetchResults(params, svcLogicContext)) + .withMessage(CHEF_ADAPTOR_ERROR_PREFIX + expectedErrorMessage); + + assertFalse(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_UNAUTHORIZED)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo(expectedErrorMessage); + } + + @SuppressWarnings("unchecked") + @Test + public void fetchResults_shouldUpdateSvcLogicContextWithJsonResponse_fromSuccessfulChefServerCall() + throws SvcLogicException { + // GIVEN + String json = "{normal:{PushJobOutput : \"ssh start/running, process 1090\"}}"; + Map<String, String> params = givenInputParams( + immutableEntry("NodeList", "[\"test1.vnf_b.onap.com\"]")); + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(true); + given(chefApiClientFactory.create(CHEF_END_POINT, ORGANIZATIONS, USERNAME, + CLIENT_PRIVATE_KEY_PATH)).willReturn(chefApiClient); + given(chefApiClient.get("/nodes/" + "test1.vnf_b.onap.com")) + .willReturn(ChefResponse.create(HttpStatus.SC_OK, json)); + + // WHEN + chefAdaptorFactory.create().fetchResults(params, svcLogicContext); + + // THEN + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_OK)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)) + .isEqualTo("{\"test1.vnf_b.onap.com\":{\"PushJobOutput\":\"ssh start/running, process 1090\"}}"); + } + + @SuppressWarnings("unchecked") + @Test + public void fetchResults_shouldUpdateSvcLogicContextWithFailedMessage_whenReturnedJSONMessageIsMissingAttribute() + throws SvcLogicException { + // GIVEN + String json = "{normal:{invalidKey : \"ssh start/running, process 1090\"}}"; + Map<String, String> params = givenInputParams( + immutableEntry("NodeList", "[\"test1.vnf_b.onap.com\"]")); + given(privateKeyChecker.doesExist(CLIENT_PRIVATE_KEY_PATH)).willReturn(true); + given(chefApiClientFactory.create(CHEF_END_POINT, ORGANIZATIONS, USERNAME, + CLIENT_PRIVATE_KEY_PATH)).willReturn(chefApiClient); + given(chefApiClient.get("/nodes/" + "test1.vnf_b.onap.com")) + .willReturn(ChefResponse.create(HttpStatus.SC_OK, json)); + + // WHEN + chefAdaptorFactory.create().fetchResults(params, svcLogicContext); + + // THEN + assertTrue(svcLogicContext.isSuccess()); + assertThat(svcLogicContext.getAttribute(RESULT_CODE_ATTR_KEY)).isEqualTo(Integer.toString(HttpStatus.SC_INTERNAL_SERVER_ERROR)); + assertThat(svcLogicContext.getAttribute(RESULT_MESSAGE_ATTR_KEY)).isEqualTo("Cannot find PushJobOutput"); + } + + @SuppressWarnings("unchecked") + private Map<String, String> givenInputParams(Entry<String, String>... entries) { + Builder<String, String> paramsBuilder = ImmutableMap.builder(); + paramsBuilder.put("username", USERNAME) + .put("serverAddress", SERVER_ADDRESS) + .put("organizations", ORGANIZATIONS); + + for (Entry<String, String> entry : entries) { + paramsBuilder.put(entry); + } + return paramsBuilder.build(); + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/PrivateKeyCheckerTest.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/PrivateKeyCheckerTest.java new file mode 100644 index 000000000..e8d383ab1 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/sli/adaptors/chef/impl/PrivateKeyCheckerTest.java @@ -0,0 +1,41 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.impl; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class PrivateKeyCheckerTest { + + @Test + public void doesExist_shouldReturnTrue_whenFileExists() { + String pemFilePath = getClass().getResource("/testclient.pem").getPath(); + assertTrue(new org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker().doesExist(pemFilePath)); + } + + @Test + public void doesExist_shouldReturnFalse_whenFileDoesNotExist() { + assertFalse(new org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker().doesExist("dummyPemFile")); + } + +}
\ No newline at end of file diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/test/ExecutorHarness.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/test/ExecutorHarness.java new file mode 100644 index 000000000..f3878148e --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/test/ExecutorHarness.java @@ -0,0 +1,170 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.test; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; + +/** + * This class is used as a test harness to wrap the call to an executor node. + */ + +public class ExecutorHarness { + + /** + * The executor to be tested + */ + private SvcLogicJavaPlugin executor; + + /** + * The collection of all exec methods found on the class + */ + private Map<String, Method> methods; + + /** + * The field of the class being tested that contains the reference to the logger to be used. This is modified to + * point to our interception logger for the test. + */ + private Field contextLogger; + + /** + * The interception logger that buffers all messages logged and allows us to look at them as part of the test case. + */ + private org.onap.ccsdk.test.InterceptLogger logger; + + /** + * Create the harness and initialize it + * + * @throws SecurityException If a security manager, s, is present and any of the following conditions is met: + * <ul> + * <li>invocation of s.checkMemberAccess(this, Member.DECLARED) denies access to the declared field</li> + * <li>the caller's class loader is not the same as or an ancestor of the class loader for the current + * class and invocation of s.checkPackageAccess() denies access to the package of this class</li> + * </ul> + * @throws NoSuchFieldException if a field with the specified name is not found. + * @throws IllegalAccessException if this Field object is enforcing Java language access control and the underlying field is either + * inaccessible or final. + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring the underlying field + * (or a subclass or implementor thereof), or if an unwrapping conversion fails. + */ + @SuppressWarnings("nls") + public ExecutorHarness() throws NoSuchFieldException, SecurityException, IllegalArgumentException, + IllegalAccessException { + methods = new HashMap<>(); + new SvcLogicContext(); + + Class<?> contextClass = SvcLogicContext.class; + contextLogger = contextClass.getDeclaredField("LOG"); + contextLogger.setAccessible(true); + logger = new org.onap.ccsdk.test.InterceptLogger(); + contextLogger.set(null, logger); + } + + /** + * Convenience constructor + * + * @param executor The executor to be tested by the harness + * + * @throws SecurityException If a security manager, s, is present and any of the following conditions is met: + * <ul> + * <li>invocation of s.checkMemberAccess(this, Member.DECLARED) denies access to the declared field</li> + * <li>the caller's class loader is not the same as or an ancestor of the class loader for the current + * class and invocation of s.checkPackageAccess() denies access to the package of this class</li> + * </ul> + * @throws NoSuchFieldException if a field with the specified name is not found. + * @throws IllegalAccessException if this Field object is enforcing Java language access control and the underlying field is either + * inaccessible or final. + * @throws IllegalArgumentException if the specified object is not an instance of the class or interface declaring the underlying field + * (or a subclass or implementor thereof), or if an unwrapping conversion fails. + */ + public ExecutorHarness(SvcLogicJavaPlugin executor) throws NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException { + this(); + setExecutor(executor); + } + + /** + * @return The java plugin class to be executed + */ + public SvcLogicJavaPlugin getExecutor() { + return executor; + } + + /** + * @param executor The java plugin class to be executed + */ + public void setExecutor(SvcLogicJavaPlugin executor) { + this.executor = executor; + scanExecutor(); + } + + /** + * @return The set of all methods that meet the signature requirements + */ + public List<String> getExecMethodNames() { + List<String> names = new ArrayList<>(); + names.addAll(methods.keySet()); + return names; + } + + /** + * Returns an indication if the named method is a valid executor method that could be called from a DG execute node + * + * @param methodName The method name to be validated + * + * @return True if the method name meets the signature requirements, false if the method either does not exist or + * does not meet the requirements. + */ + public boolean isExecMethod(String methodName) { + return methods.containsKey(methodName); + } + + /** + * This method scans the executor class hierarchy to locate all methods that match the required signature of the + * executor and records these methods in a map. + */ + private void scanExecutor() { + methods.clear(); + Class<?> executorClass = executor.getClass(); + Method[] publicMethods = executorClass.getMethods(); + for (Method method : publicMethods) { + if (method.getReturnType().equals(Void.class)) { + Class<?>[] paramTypes = method.getParameterTypes(); + if (paramTypes.length == 2) { + if (Map.class.isAssignableFrom(paramTypes[0]) + && SvcLogicContext.class.isAssignableFrom(paramTypes[1])) { + methods.put(method.getName(), method); + } + } + } + } + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/test/InterceptLogger.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/test/InterceptLogger.java new file mode 100644 index 000000000..9f80f6880 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/test/java/org/onap/ccsdk/test/InterceptLogger.java @@ -0,0 +1,447 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.test; + +import ch.qos.logback.classic.Level; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Marker; + +/** + * This class is used as an intercept logger that can be used in testing to intercept and record all messages that are + * logged, thus allowing a junit test case to examine the log output and make assertions. + */ +public class InterceptLogger implements org.slf4j.Logger { + + /** + * The list of all intercepted log events + */ + private List<LogRecord> events; + + /** + * Create the intercept logger + */ + public InterceptLogger() { + events = new ArrayList<LogRecord>(1000); + } + + /** + * @return Returns all intercepted log events + */ + public List<LogRecord> getLogRecords() { + return events; + } + + /** + * Clears all log events + */ + public void clear() { + events.clear(); + } + + @Override + public void debug(Marker marker, String msg) { + debug(msg); + } + + @Override + public void debug(Marker marker, String format, Object arg) { + debug(MessageFormat.format(format, arg)); + } + + @Override + public void debug(Marker marker, String format, Object... arguments) { + debug(MessageFormat.format(format, arguments)); + } + + @Override + public void debug(Marker marker, String format, Object arg1, Object arg2) { + debug(MessageFormat.format(format, arg1, arg2)); + } + + @Override + public void debug(Marker marker, String msg, Throwable t) { + debug(msg, t); + } + + @Override + public void debug(String msg) { + events.add(new LogRecord(Level.DEBUG, msg)); + } + + @Override + public void debug(String format, Object arg) { + events.add(new LogRecord(Level.DEBUG, MessageFormat.format(format, arg))); + } + + @Override + public void debug(String format, Object... arguments) { + events.add(new LogRecord(Level.DEBUG, MessageFormat.format(format, arguments))); + } + + @Override + public void debug(String format, Object arg1, Object arg2) { + events.add(new LogRecord(Level.DEBUG, MessageFormat.format(format, arg1, arg2))); + } + + @Override + public void debug(String msg, Throwable t) { + events.add(new LogRecord(Level.DEBUG, msg, t)); + } + + @Override + public void error(Marker marker, String msg) { + error(msg); + } + + @Override + public void error(Marker marker, String format, Object arg) { + error(format, arg); + } + + @Override + public void error(Marker marker, String format, Object... arguments) { + error(format, arguments); + } + + @Override + public void error(Marker marker, String format, Object arg1, Object arg2) { + error(format, arg1, arg2); + } + + @Override + public void error(Marker marker, String msg, Throwable t) { + events.add(new LogRecord(Level.ERROR, msg, t)); + } + + @Override + public void error(String msg) { + events.add(new LogRecord(Level.ERROR, msg)); + } + + @Override + public void error(String format, Object arg) { + events.add(new LogRecord(Level.ERROR, MessageFormat.format(format, arg))); + } + + @Override + public void error(String format, Object... arguments) { + events.add(new LogRecord(Level.ERROR, MessageFormat.format(format, arguments))); + } + + @Override + public void error(String format, Object arg1, Object arg2) { + events.add(new LogRecord(Level.ERROR, MessageFormat.format(format, arg1, arg2))); + } + + @Override + public void error(String msg, Throwable t) { + events.add(new LogRecord(Level.ERROR, msg, t)); + } + + @Override + public String getName() { + return null; + } + + @Override + public void info(Marker marker, String msg) { + info(msg); + } + + @Override + public void info(Marker marker, String format, Object arg) { + info(format, arg); + } + + @Override + public void info(Marker marker, String format, Object... arguments) { + info(format, arguments); + } + + @Override + public void info(Marker marker, String format, Object arg1, Object arg2) { + info(format, arg1, arg2); + } + + @Override + public void info(Marker marker, String msg, Throwable t) { + events.add(new LogRecord(Level.INFO, msg, t)); + } + + @Override + public void info(String msg) { + events.add(new LogRecord(Level.INFO, msg)); + } + + @Override + public void info(String format, Object arg) { + events.add(new LogRecord(Level.INFO, MessageFormat.format(format, arg))); + } + + @Override + public void info(String format, Object... arguments) { + events.add(new LogRecord(Level.INFO, MessageFormat.format(format, arguments))); + } + + @Override + public void info(String format, Object arg1, Object arg2) { + events.add(new LogRecord(Level.INFO, MessageFormat.format(format, arg1, arg2))); + } + + @Override + public void info(String msg, Throwable t) { + events.add(new LogRecord(Level.INFO, msg, t)); + } + + @Override + public boolean isDebugEnabled() { + return true; + } + + @Override + public boolean isDebugEnabled(Marker marker) { + return true; + } + + @Override + public boolean isErrorEnabled() { + return true; + } + + @Override + public boolean isErrorEnabled(Marker marker) { + return true; + } + + @Override + public boolean isInfoEnabled() { + return true; + } + + @Override + public boolean isInfoEnabled(Marker marker) { + return true; + } + + @Override + public boolean isTraceEnabled() { + return true; + } + + @Override + public boolean isTraceEnabled(Marker marker) { + return true; + } + + @Override + public boolean isWarnEnabled() { + return true; + } + + @Override + public boolean isWarnEnabled(Marker marker) { + return true; + } + + @Override + public void trace(Marker marker, String msg) { + trace(msg); + } + + @Override + public void trace(Marker marker, String format, Object arg) { + trace(format, arg); + } + + @Override + public void trace(Marker marker, String format, Object... argArray) { + trace(format, argArray); + } + + @Override + public void trace(Marker marker, String format, Object arg1, Object arg2) { + trace(format, arg1, arg2); + } + + @Override + public void trace(Marker marker, String msg, Throwable t) { + trace(msg, t); + } + + @Override + public void trace(String msg) { + events.add(new LogRecord(Level.TRACE, msg)); + } + + @Override + public void trace(String format, Object arg) { + events.add(new LogRecord(Level.TRACE, MessageFormat.format(format, arg))); + } + + @Override + public void trace(String format, Object... arguments) { + events.add(new LogRecord(Level.TRACE, MessageFormat.format(format, arguments))); + } + + @Override + public void trace(String format, Object arg1, Object arg2) { + events.add(new LogRecord(Level.TRACE, MessageFormat.format(format, arg1, arg2))); + } + + @Override + public void trace(String msg, Throwable t) { + events.add(new LogRecord(Level.TRACE, msg, t)); + } + + @Override + public void warn(Marker marker, String msg) { + warn(msg); + } + + @Override + public void warn(Marker marker, String format, Object arg) { + warn(format, arg); + } + + @Override + public void warn(Marker marker, String format, Object... arguments) { + warn(format, arguments); + } + + @Override + public void warn(Marker marker, String format, Object arg1, Object arg2) { + warn(format, arg1, arg2); + } + + @Override + public void warn(Marker marker, String msg, Throwable t) { + events.add(new LogRecord(Level.WARN, msg, t)); + } + + @Override + public void warn(String msg) { + events.add(new LogRecord(Level.WARN, msg)); + } + + @Override + public void warn(String format, Object arg) { + events.add(new LogRecord(Level.WARN, MessageFormat.format(format, arg))); + } + + @Override + public void warn(String format, Object... arguments) { + events.add(new LogRecord(Level.WARN, MessageFormat.format(format, arguments))); + } + + @Override + public void warn(String format, Object arg1, Object arg2) { + events.add(new LogRecord(Level.WARN, MessageFormat.format(format, arg1, arg2))); + } + + @Override + public void warn(String msg, Throwable t) { + events.add(new LogRecord(Level.WARN, msg, t)); + } + + /** + * This inner class represents an intercepted log event + */ + public class LogRecord { + private Level level; + private String message; + private long timestamp; + private Throwable t; + + public LogRecord(Level level, String message) { + setLevel(level); + setTimestamp(System.currentTimeMillis()); + setMessage(message); + } + + public LogRecord(Level level, String message, Throwable t) { + this(level, message); + setThrowable(t); + } + + /** + * @return the value of level + */ + public Level getLevel() { + return level; + } + + /** + * @param level the value for level + */ + public void setLevel(Level level) { + this.level = level; + } + + /** + * @return the value of message + */ + public String getMessage() { + return message; + } + + /** + * @param message the value for message + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * @return the value of timestamp + */ + public long getTimestamp() { + return timestamp; + } + + /** + * @param timestamp the value for timestamp + */ + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + /** + * @return the value of t + */ + public Throwable getThrowable() { + return t; + } + + /** + * @param t the value for t + */ + public void setThrowable(Throwable t) { + this.t = t; + } + + } + +} |