Add unittests for NiciraNvpApi

Added some unittests for the NiciraNvpApi. These tests mainly validate
the logic of the execute methods, which are the main thing in this
class. Other methods are basically wrappers around these functions.

Changed NiciraNvpApi to have a factory method for obtaining the
HttpMethod. This makes it easier to mock it.

Changed the executeMethods in NiciraNvpApi to protected so the unittests
have access.

Fixed a bug in NiciraNvpApi where releaseconnection was not called in
some cases.
This commit is contained in:
Hugo Trippaers 2012-12-07 15:54:59 +01:00
parent 3ab8a65a39
commit fa207d2b4d
2 changed files with 373 additions and 36 deletions

View File

@ -41,7 +41,9 @@ import javax.net.ssl.X509TrustManager;
import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
@ -71,9 +73,42 @@ public class NiciraNvpApi {
private String _adminpass;
private HttpClient _client;
/* This factory method is protected so we can extend this
* in the unittests.
*/
protected HttpClient createHttpClient() {
return new HttpClient(s_httpClientManager);
}
protected HttpMethod createMethod(String type, String uri) throws NiciraNvpApiException {
String url;
try {
url = new URL(_protocol, _host, "/ws.v1/login").toString();
} catch (MalformedURLException e) {
s_logger.error("Unable to build Nicira API URL", e);
throw new NiciraNvpApiException("Unable to build Nicira API URL", e);
}
if ("post".equalsIgnoreCase(type)) {
return new PostMethod(url);
}
else if ("get".equalsIgnoreCase(type)) {
return new GetMethod(url);
}
else if ("delete".equalsIgnoreCase(type)) {
return new DeleteMethod(url);
}
else if ("put".equalsIgnoreCase(type)) {
return new PutMethod(url);
}
else {
throw new NiciraNvpApiException("Requesting unknown method type");
}
}
public NiciraNvpApi() {
_client = new HttpClient(s_httpClientManager);
_client = createHttpClient();
_client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
try {
@ -100,9 +135,15 @@ public class NiciraNvpApi {
* The method returns false if the login failed or the connection could not be made.
*
*/
private void login() throws NiciraNvpApiException {
protected void login() throws NiciraNvpApiException {
String url;
if (_host == null || _host.isEmpty() ||
_adminuser == null || _adminuser.isEmpty() ||
_adminpass == null || _adminpass.isEmpty()) {
throw new NiciraNvpApiException("Hostname/credentials are null or empty");
}
try {
url = new URL(_protocol, _host, "/ws.v1/login").toString();
} catch (MalformedURLException e) {
@ -294,18 +335,16 @@ public class NiciraNvpApi {
return executeRetrieveObject(new TypeToken<NiciraNvpList<LogicalRouterPort>>(){}.getType(), uri, params);
}
private <T> void executeUpdateObject(T newObject, String uri, Map<String,String> parameters) throws NiciraNvpApiException {
String url;
try {
url = new URL(_protocol, _host, uri).toString();
} catch (MalformedURLException e) {
s_logger.error("Unable to build Nicira API URL", e);
throw new NiciraNvpApiException("Connection to NVP Failed");
protected <T> void executeUpdateObject(T newObject, String uri, Map<String,String> parameters) throws NiciraNvpApiException {
if (_host == null || _host.isEmpty() ||
_adminuser == null || _adminuser.isEmpty() ||
_adminpass == null || _adminpass.isEmpty()) {
throw new NiciraNvpApiException("Hostname/credentials are null or empty");
}
Gson gson = new Gson();
PutMethod pm = new PutMethod(url);
PutMethod pm = (PutMethod) createMethod("put", uri);
pm.setRequestHeader("Content-Type", "application/json");
try {
pm.setRequestEntity(new StringRequestEntity(
@ -325,18 +364,16 @@ public class NiciraNvpApi {
pm.releaseConnection();
}
private <T> T executeCreateObject(T newObject, Type returnObjectType, String uri, Map<String,String> parameters) throws NiciraNvpApiException {
String url;
try {
url = new URL(_protocol, _host, uri).toString();
} catch (MalformedURLException e) {
s_logger.error("Unable to build Nicira API URL", e);
throw new NiciraNvpApiException("Unable to build Nicira API URL", e);
protected <T> T executeCreateObject(T newObject, Type returnObjectType, String uri, Map<String,String> parameters) throws NiciraNvpApiException {
if (_host == null || _host.isEmpty() ||
_adminuser == null || _adminuser.isEmpty() ||
_adminpass == null || _adminpass.isEmpty()) {
throw new NiciraNvpApiException("Hostname/credentials are null or empty");
}
Gson gson = new Gson();
PostMethod pm = new PostMethod(url);
PostMethod pm = (PostMethod) createMethod("post", uri);
pm.setRequestHeader("Content-Type", "application/json");
try {
pm.setRequestEntity(new StringRequestEntity(
@ -366,16 +403,14 @@ public class NiciraNvpApi {
return result;
}
private void executeDeleteObject(String uri) throws NiciraNvpApiException {
String url;
try {
url = new URL(_protocol, _host, uri).toString();
} catch (MalformedURLException e) {
s_logger.error("Unable to build Nicira API URL", e);
throw new NiciraNvpApiException("Unable to build Nicira API URL", e);
protected void executeDeleteObject(String uri) throws NiciraNvpApiException {
if (_host == null || _host.isEmpty() ||
_adminuser == null || _adminuser.isEmpty() ||
_adminpass == null || _adminpass.isEmpty()) {
throw new NiciraNvpApiException("Hostname/credentials are null or empty");
}
DeleteMethod dm = new DeleteMethod(url);
DeleteMethod dm = (DeleteMethod) createMethod("delete", uri);
dm.setRequestHeader("Content-Type", "application/json");
executeMethod(dm);
@ -389,16 +424,14 @@ public class NiciraNvpApi {
dm.releaseConnection();
}
private <T> T executeRetrieveObject(Type returnObjectType, String uri, Map<String,String> parameters) throws NiciraNvpApiException {
String url;
try {
url = new URL(_protocol, _host, uri).toString();
} catch (MalformedURLException e) {
s_logger.error("Unable to build Nicira API URL", e);
throw new NiciraNvpApiException("Unable to build Nicira API URL", e);
protected <T> T executeRetrieveObject(Type returnObjectType, String uri, Map<String,String> parameters) throws NiciraNvpApiException {
if (_host == null || _host.isEmpty() ||
_adminuser == null || _adminuser.isEmpty() ||
_adminpass == null || _adminpass.isEmpty()) {
throw new NiciraNvpApiException("Hostname/credentials are null or empty");
}
GetMethod gm = new GetMethod(url);
GetMethod gm = (GetMethod) createMethod("get", uri);
gm.setRequestHeader("Content-Type", "application/json");
if (parameters != null && !parameters.isEmpty()) {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(parameters.size());
@ -430,7 +463,7 @@ public class NiciraNvpApi {
return returnValue;
}
private void executeMethod(HttpMethodBase method) throws NiciraNvpApiException {
protected void executeMethod(HttpMethodBase method) throws NiciraNvpApiException {
try {
_client.executeMethod(method);
if (method.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
@ -441,9 +474,11 @@ public class NiciraNvpApi {
}
} catch (HttpException e) {
s_logger.error("HttpException caught while trying to connect to the Nicira NVP Controller", e);
method.releaseConnection();
throw new NiciraNvpApiException("API call to Nicira NVP Controller Failed", e);
} catch (IOException e) {
s_logger.error("IOException caught while trying to connect to the Nicira NVP Controller", e);
method.releaseConnection();
throw new NiciraNvpApiException("API call to Nicira NVP Controller Failed", e);
}
}

View File

@ -0,0 +1,302 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
package com.cloud.network.nicira;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.Collections;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.http.HttpStatus;
import org.junit.Before;
import org.junit.Test;
public class NiciraNvpApiTest {
NiciraNvpApi _api;
HttpClient _client = mock(HttpClient.class);
HttpMethod _method;
@Before
public void setUp() {
HttpClientParams hmp = mock(HttpClientParams.class);
when (_client.getParams()).thenReturn(hmp);
_api = new NiciraNvpApi() {
@Override
protected HttpClient createHttpClient() {
return _client;
}
@Override
protected HttpMethod createMethod(String type, String uri) {
return _method;
}
};
_api.setAdminCredentials("admin", "adminpass");
_api.setControllerAddress("localhost");
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteLoginWithoutHostname() throws NiciraNvpApiException {
_api.setControllerAddress(null);
_api.login();
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteLoginWithoutCredentials() throws NiciraNvpApiException {
_api.setAdminCredentials(null, null);
_api.login();
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteUpdateObjectWithoutHostname() throws NiciraNvpApiException {
_api.setControllerAddress(null);
_api.executeUpdateObject(new String(), "/", Collections.<String, String> emptyMap());
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteUpdateObjectWithoutCredentials() throws NiciraNvpApiException {
_api.setAdminCredentials(null, null);
_api.executeUpdateObject(new String(), "/", Collections.<String, String> emptyMap());
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteCreateObjectWithoutHostname() throws NiciraNvpApiException {
_api.setControllerAddress(null);
_api.executeCreateObject(new String(), String.class, "/", Collections.<String, String> emptyMap());
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteCreateObjectWithoutCredentials() throws NiciraNvpApiException {
_api.setAdminCredentials(null, null);
_api.executeCreateObject(new String(), String.class, "/", Collections.<String, String> emptyMap());
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteDeleteObjectWithoutHostname() throws NiciraNvpApiException {
_api.setControllerAddress(null);
_api.executeDeleteObject("/");
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteDeleteObjectWithoutCredentials() throws NiciraNvpApiException {
_api.setAdminCredentials(null, null);
_api.executeDeleteObject("/");
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteRetrieveObjectWithoutHostname() throws NiciraNvpApiException {
_api.setControllerAddress(null);
_api.executeRetrieveObject(String.class, "/", Collections.<String, String> emptyMap());
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteRetrieveObjectWithoutCredentials() throws NiciraNvpApiException {
_api.setAdminCredentials(null, null);
_api.executeDeleteObject("/");
}
@Test
public void executeMethodTest() throws NiciraNvpApiException {
GetMethod gm = mock(GetMethod.class);
when(gm.getStatusCode()).thenReturn(HttpStatus.SC_OK);
_api.executeMethod(gm);
verify(gm, times(1)).getStatusCode();
}
/* Bit of a roundabout way to ensure that login is called after an un authorized result
* It not possible to properly mock login()
*/
@Test (expected=NiciraNvpApiException.class)
public void executeMethodTestWithLogin() throws NiciraNvpApiException, HttpException, IOException {
GetMethod gm = mock(GetMethod.class);
when(_client.executeMethod((HttpMethod)any())).thenThrow(new HttpException());
when(gm.getStatusCode()).thenReturn(HttpStatus.SC_UNAUTHORIZED).thenReturn(HttpStatus.SC_UNAUTHORIZED);
_api.executeMethod(gm);
verify(gm, times(1)).getStatusCode();
}
@Test
public void testExecuteCreateObject() throws NiciraNvpApiException, IOException {
LogicalSwitch ls = new LogicalSwitch();
_method = mock(PostMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_CREATED);
when(_method.getResponseBodyAsString()).thenReturn("{ \"uuid\" : \"aaaa\" }");
ls = _api.executeCreateObject(ls, LogicalSwitch.class, "/", Collections.<String, String> emptyMap());
assertTrue("aaaa".equals(ls.getUuid()));
verify(_method, times(1)).releaseConnection();
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteCreateObjectFailure() throws NiciraNvpApiException, IOException {
LogicalSwitch ls = new LogicalSwitch();
_method = mock(PostMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
Header header = mock(Header.class);
when(header.getValue()).thenReturn("text/html");
when(_method.getResponseHeader("Content-Type")).thenReturn(header);
when(_method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
try {
ls = _api.executeCreateObject(ls, LogicalSwitch.class, "/", Collections.<String, String> emptyMap());
} finally {
verify(_method, times(1)).releaseConnection();
}
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteCreateObjectException() throws NiciraNvpApiException, IOException {
LogicalSwitch ls = new LogicalSwitch();
when(_client.executeMethod((HttpMethod) any())).thenThrow(new HttpException());
_method = mock(PostMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
Header header = mock(Header.class);
when(header.getValue()).thenReturn("text/html");
when(_method.getResponseHeader("Content-Type")).thenReturn(header);
when(_method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
try {
ls = _api.executeCreateObject(ls, LogicalSwitch.class, "/", Collections.<String, String> emptyMap());
} finally {
verify(_method, times(1)).releaseConnection();
}
}
@Test
public void testExecuteUpdateObject() throws NiciraNvpApiException, IOException {
LogicalSwitch ls = new LogicalSwitch();
_method = mock(PutMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_OK);
_api.executeUpdateObject(ls, "/", Collections.<String, String> emptyMap());
verify(_method, times(1)).releaseConnection();
verify(_client, times(1)).executeMethod(_method);
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteUpdateObjectFailure() throws NiciraNvpApiException, IOException {
LogicalSwitch ls = new LogicalSwitch();
_method = mock(PutMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
Header header = mock(Header.class);
when(header.getValue()).thenReturn("text/html");
when(_method.getResponseHeader("Content-Type")).thenReturn(header);
when(_method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
try {
_api.executeUpdateObject(ls, "/", Collections.<String, String> emptyMap());
} finally {
verify(_method, times(1)).releaseConnection();
}
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteUpdateObjectException() throws NiciraNvpApiException, IOException {
LogicalSwitch ls = new LogicalSwitch();
_method = mock(PutMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_OK);
when(_client.executeMethod((HttpMethod) any())).thenThrow(new IOException());
try {
_api.executeUpdateObject(ls, "/", Collections.<String, String> emptyMap());
} finally {
verify(_method, times(1)).releaseConnection();
}
}
@Test
public void testExecuteDeleteObject() throws NiciraNvpApiException, IOException {
_method = mock(DeleteMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_NO_CONTENT);
_api.executeDeleteObject("/");
verify(_method, times(1)).releaseConnection();
verify(_client, times(1)).executeMethod(_method);
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteDeleteObjectFailure() throws NiciraNvpApiException, IOException {
_method = mock(DeleteMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
Header header = mock(Header.class);
when(header.getValue()).thenReturn("text/html");
when(_method.getResponseHeader("Content-Type")).thenReturn(header);
when(_method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
try {
_api.executeDeleteObject("/");
} finally {
verify(_method, times(1)).releaseConnection();
}
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteDeleteObjectException() throws NiciraNvpApiException, IOException {
_method = mock(DeleteMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_NO_CONTENT);
when(_client.executeMethod((HttpMethod) any())).thenThrow(new HttpException());
try {
_api.executeDeleteObject("/");
} finally {
verify(_method, times(1)).releaseConnection();
}
}
@Test
public void testExecuteRetrieveObject() throws NiciraNvpApiException, IOException {
_method = mock(GetMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_OK);
when(_method.getResponseBodyAsString()).thenReturn("{ \"uuid\" : \"aaaa\" }");
_api.executeRetrieveObject(LogicalSwitch.class, "/", Collections.<String, String> emptyMap());
verify(_method, times(1)).releaseConnection();
verify(_client, times(1)).executeMethod(_method);
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteRetrieveObjectFailure() throws NiciraNvpApiException, IOException {
_method = mock(GetMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
when(_method.getResponseBodyAsString()).thenReturn("{ \"uuid\" : \"aaaa\" }");
Header header = mock(Header.class);
when(header.getValue()).thenReturn("text/html");
when(_method.getResponseHeader("Content-Type")).thenReturn(header);
when(_method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
try {
_api.executeRetrieveObject(LogicalSwitch.class, "/", Collections.<String, String> emptyMap());
} finally {
verify(_method, times(1)).releaseConnection();
}
}
@Test (expected=NiciraNvpApiException.class)
public void testExecuteRetrieveObjectException() throws NiciraNvpApiException, IOException {
_method = mock(GetMethod.class);
when(_method.getStatusCode()).thenReturn(HttpStatus.SC_OK);
when(_method.getResponseBodyAsString()).thenReturn("{ \"uuid\" : \"aaaa\" }");
when(_client.executeMethod((HttpMethod) any())).thenThrow(new HttpException());
try {
_api.executeRetrieveObject(LogicalSwitch.class, "/", Collections.<String, String> emptyMap());
} finally {
verify(_method, times(1)).releaseConnection();
}
}
}