/* * Copyright 2016-present Open Networking Laboratory * * 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. */ package org.onosproject.rest.resources; import com.eclipsesource.json.Json; import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonObject; import org.hamcrest.Description; import org.hamcrest.Matchers; import org.hamcrest.TypeSafeMatcher; import org.junit.Before; import org.junit.Test; import org.onlab.osgi.ServiceDirectory; import org.onlab.osgi.TestServiceDirectory; import org.onlab.rest.BaseResource; import org.onosproject.codec.CodecService; import org.onosproject.codec.impl.CodecManager; import org.onosproject.net.key.DeviceKey; import org.onosproject.net.key.DeviceKeyAdminService; import org.onosproject.net.key.DeviceKeyId; import org.onosproject.net.key.DeviceKeyService; import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.InputStream; import java.net.HttpURLConnection; import java.util.HashSet; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** * Unit tests for device key REST APIs. */ public class DeviceKeyWebResourceTest extends ResourceTest { final DeviceKeyService mockDeviceKeyService = createMock(DeviceKeyService.class); final DeviceKeyAdminService mockDeviceKeyAdminService = createMock(DeviceKeyAdminService.class); final HashSet<DeviceKey> deviceKeySet = new HashSet<>(); private static final String ID = "id"; private static final String TYPE = "type"; private static final String LABEL = "label"; private static final String COMMUNITY_NAME = "community_name"; private static final String USERNAME = "username"; private static final String PASSWORD = "password"; private final String deviceKeyId1 = "DeviceKeyId1"; private final String deviceKeyId2 = "DeviceKeyId2"; private final String deviceKeyId3 = "DeviceKeyId3"; private final String deviceKeyId4 = "DeviceKeyId4"; private final String deviceKeyLabel = "DeviceKeyLabel"; private final String deviceKeyCommunityName = "DeviceKeyCommunityName"; private final String deviceKeyUsername = "DeviceKeyUsername"; private final String deviceKeyPassword = "DeviceKeyPassword"; private final DeviceKey deviceKey1 = DeviceKey.createDeviceKeyUsingCommunityName( DeviceKeyId.deviceKeyId(deviceKeyId1), deviceKeyLabel, deviceKeyCommunityName); private final DeviceKey deviceKey2 = DeviceKey.createDeviceKeyUsingUsernamePassword( DeviceKeyId.deviceKeyId(deviceKeyId2), null, deviceKeyUsername, deviceKeyPassword); private final DeviceKey deviceKey3 = DeviceKey.createDeviceKeyUsingUsernamePassword( DeviceKeyId.deviceKeyId(deviceKeyId3), null, null, null); private final DeviceKey deviceKey4 = DeviceKey.createDeviceKeyUsingCommunityName( DeviceKeyId.deviceKeyId(deviceKeyId4), null, null); /** * Initializes test mocks and environment. */ @Before public void setUpMocks() { expect(mockDeviceKeyService.getDeviceKeys()).andReturn(deviceKeySet).anyTimes(); // Register the services needed for the test CodecManager codecService = new CodecManager(); codecService.activate(); ServiceDirectory testDirectory = new TestServiceDirectory() .add(DeviceKeyService.class, mockDeviceKeyService) .add(DeviceKeyAdminService.class, mockDeviceKeyAdminService) .add(CodecService.class, codecService); BaseResource.setServiceDirectory(testDirectory); } /** * Hamcrest matcher to check that a device key representation in JSON matches * the actual device key. */ public static class DeviceKeyJsonMatcher extends TypeSafeMatcher<JsonObject> { private final DeviceKey deviceKey; private String reason = ""; public DeviceKeyJsonMatcher(DeviceKey deviceKeyValue) { deviceKey = deviceKeyValue; } @Override public boolean matchesSafely(JsonObject jsonHost) { // Check the device key id final String jsonId = jsonHost.get(ID).asString(); if (!jsonId.equals(deviceKey.deviceKeyId().id().toString())) { reason = ID + " " + deviceKey.deviceKeyId().id().toString(); return false; } // Check the device key label final String jsonLabel = (jsonHost.get(LABEL).isNull()) ? null : jsonHost.get(LABEL).asString(); if (deviceKey.label() != null) { if ((jsonLabel == null) || !jsonLabel.equals(deviceKey.label())) { reason = LABEL + " " + deviceKey.label(); return false; } } // Check the device key type final String jsonType = jsonHost.get(TYPE).asString(); if (!jsonType.equals(deviceKey.type().toString())) { reason = TYPE + " " + deviceKey.type().toString(); return false; } if (jsonType.equals(DeviceKey.Type.COMMUNITY_NAME.toString())) { // Check the device key community name final String jsonCommunityName = jsonHost.get(COMMUNITY_NAME).isNull() ? null : jsonHost.get(COMMUNITY_NAME).asString(); if (deviceKey.asCommunityName().name() != null) { if (!jsonCommunityName.equals(deviceKey.asCommunityName().name().toString())) { reason = COMMUNITY_NAME + " " + deviceKey.asCommunityName().name().toString(); return false; } } } else if (jsonType.equals(DeviceKey.Type.USERNAME_PASSWORD.toString())) { // Check the device key username final String jsonUsername = jsonHost.get(USERNAME).isNull() ? null : jsonHost.get(USERNAME).asString(); if (deviceKey.asUsernamePassword().username() != null) { if (!jsonUsername.equals(deviceKey.asUsernamePassword().username().toString())) { reason = USERNAME + " " + deviceKey.asUsernamePassword().username().toString(); return false; } } // Check the device key password final String jsonPassword = jsonHost.get(PASSWORD).isNull() ? null : jsonHost.get(PASSWORD).asString(); if (deviceKey.asUsernamePassword().password() != null) { if (!jsonPassword.equals(deviceKey.asUsernamePassword().password().toString())) { reason = PASSWORD + " " + deviceKey.asUsernamePassword().password().toString(); return false; } } } else { reason = "Unknown " + TYPE + " " + deviceKey.type().toString(); return false; } return true; } @Override public void describeTo(Description description) { description.appendText(reason); } } /** * Factory to allocate a device key array matcher. * * @param deviceKey device key object we are looking for * @return matcher */ private static DeviceKeyJsonMatcher matchesDeviceKey(DeviceKey deviceKey) { return new DeviceKeyJsonMatcher(deviceKey); } /** * Hamcrest matcher to check that a device key is represented properly in a JSON * array of device keys. */ public static class DeviceKeyJsonArrayMatcher extends TypeSafeMatcher<JsonArray> { private final DeviceKey deviceKey; private String reason = ""; public DeviceKeyJsonArrayMatcher(DeviceKey deviceKeyValue) { deviceKey = deviceKeyValue; } @Override public boolean matchesSafely(JsonArray json) { boolean deviceKeyFound = false; final int expectedAttributes = 5; for (int jsonDeviceKeyIndex = 0; jsonDeviceKeyIndex < json.size(); jsonDeviceKeyIndex++) { final JsonObject jsonHost = json.get(jsonDeviceKeyIndex).asObject(); // Device keys can have a variable number of attribute so we check // that there is a minimum number. if (jsonHost.names().size() < expectedAttributes) { reason = "Found a device key with the wrong number of attributes"; return false; } final String jsonDeviceKeyId = jsonHost.get(ID).asString(); if (jsonDeviceKeyId.equals(deviceKey.deviceKeyId().id().toString())) { deviceKeyFound = true; // We found the correct device key, check the device key attribute values assertThat(jsonHost, matchesDeviceKey(deviceKey)); } } if (!deviceKeyFound) { reason = "Device key with id " + deviceKey.deviceKeyId().id().toString() + " was not found"; return false; } else { return true; } } @Override public void describeTo(Description description) { description.appendText(reason); } } /** * Factory to allocate a device key array matcher. * * @param deviceKey device key object we are looking for * @return matcher */ private static DeviceKeyJsonArrayMatcher hasDeviceKey(DeviceKey deviceKey) { return new DeviceKeyJsonArrayMatcher(deviceKey); } /** * Tests the result of the REST API GET when there are no device keys. */ @Test public void testGetDeviceKeysEmptyArray() { replay(mockDeviceKeyService); WebTarget wt = target(); String response = wt.path("keys").request().get(String.class); assertThat(response, is("{\"keys\":[]}")); verify(mockDeviceKeyService); } /** * Tests the result of the REST API GET when device keys are defined. */ @Test public void testGetDeviceKeysArray() { replay(mockDeviceKeyService); deviceKeySet.add(deviceKey1); deviceKeySet.add(deviceKey2); deviceKeySet.add(deviceKey3); deviceKeySet.add(deviceKey4); WebTarget wt = target(); String response = wt.path("keys").request().get(String.class); assertThat(response, containsString("{\"keys\":[")); final JsonObject result = Json.parse(response).asObject(); assertThat(result, notNullValue()); assertThat(result.names(), hasSize(1)); assertThat(result.names().get(0), is("keys")); final JsonArray deviceKeys = result.get("keys").asArray(); assertThat(deviceKeys, notNullValue()); assertEquals("Device keys array is not the correct size.", 4, deviceKeys.size()); assertThat(deviceKeys, hasDeviceKey(deviceKey1)); assertThat(deviceKeys, hasDeviceKey(deviceKey2)); assertThat(deviceKeys, hasDeviceKey(deviceKey3)); assertThat(deviceKeys, hasDeviceKey(deviceKey4)); verify(mockDeviceKeyService); } /** * Tests the result of the REST API GET using a device key identifier. */ @Test public void testGetDeviceKeyById() { deviceKeySet.add(deviceKey1); expect(mockDeviceKeyService.getDeviceKey(DeviceKeyId.deviceKeyId(deviceKeyId1))) .andReturn(deviceKey1) .anyTimes(); replay(mockDeviceKeyService); WebTarget wt = target(); String response = wt.path("keys/" + deviceKeyId1).request().get(String.class); final JsonObject result = Json.parse(response).asObject(); assertThat(result, notNullValue()); assertThat(result, matchesDeviceKey(deviceKey1)); verify(mockDeviceKeyService); } /** * Tests that a GET of a non-existent object throws an exception. */ @Test public void testGetNonExistentDeviceKey() { expect(mockDeviceKeyService.getDeviceKey(DeviceKeyId.deviceKeyId(deviceKeyId1))) .andReturn(null) .anyTimes(); replay(mockDeviceKeyService); WebTarget wt = target(); try { wt.path("keys/" + deviceKeyId1).request().get(String.class); fail("GET of a non-existent device key did not throw an exception"); } catch (NotFoundException ex) { assertThat(ex.getMessage(), containsString("HTTP 404 Not Found")); } verify(mockDeviceKeyService); } /** * Tests adding of new device key using POST via JSON stream. */ @Test public void testPost() { mockDeviceKeyAdminService.addKey(anyObject()); expectLastCall(); replay(mockDeviceKeyAdminService); WebTarget wt = target(); InputStream jsonStream = DeviceKeyWebResourceTest.class .getResourceAsStream("post-device-key.json"); Response response = wt.path("keys").request(MediaType.APPLICATION_JSON_TYPE) .post(Entity.json(jsonStream)); assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED)); String location = response.getLocation().getPath(); assertThat(location, Matchers.startsWith("/keys/" + deviceKeyId3)); verify(mockDeviceKeyAdminService); } /** * Tests adding of a null device key using POST via JSON stream. */ @Test public void testPostNullDeviceKey() { replay(mockDeviceKeyAdminService); WebTarget wt = target(); try { wt.path("keys").request(MediaType.APPLICATION_JSON_TYPE) .post(Entity.json(null), String.class); fail("POST of null device key did not throw an exception"); } catch (BadRequestException ex) { assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request")); } verify(mockDeviceKeyAdminService); } /** * Tests removing a device key with DELETE request. */ @Test public void testDelete() { expect(mockDeviceKeyService.getDeviceKey(DeviceKeyId.deviceKeyId(deviceKeyId2))) .andReturn(deviceKey2) .anyTimes(); mockDeviceKeyAdminService.removeKey(anyObject()); expectLastCall(); replay(mockDeviceKeyService); replay(mockDeviceKeyAdminService); WebTarget wt = target(); Response response = wt.path("keys/" + deviceKeyId2) .request(MediaType.APPLICATION_JSON_TYPE) .delete(); assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK)); verify(mockDeviceKeyService); verify(mockDeviceKeyAdminService); } /** * Tests that a DELETE of a non-existent device key throws an exception. */ @Test public void testDeleteNonExistentDeviceKey() { expect(mockDeviceKeyService.getDeviceKey(anyObject())) .andReturn(null) .anyTimes(); expectLastCall(); replay(mockDeviceKeyService); replay(mockDeviceKeyAdminService); WebTarget wt = target(); try { wt.path("keys/" + "NON_EXISTENT_DEVICE_KEY").request() .delete(String.class); fail("Delete of a non-existent device key did not throw an exception"); } catch (NotFoundException ex) { assertThat(ex.getMessage(), containsString("HTTP 404 Not Found")); } verify(mockDeviceKeyService); verify(mockDeviceKeyAdminService); } }