Unit tests for applications REST APIs

Change-Id: I4e8360ca9b0ca9eafe1b226b648acfe2b0676caa
diff --git a/web/api/src/main/java/org/onosproject/rest/ApplicationsWebResource.java b/web/api/src/main/java/org/onosproject/rest/ApplicationsWebResource.java
index 6177bab..9d32090 100644
--- a/web/api/src/main/java/org/onosproject/rest/ApplicationsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/ApplicationsWebResource.java
@@ -46,7 +46,7 @@
 
     @GET
     @Path("{name}")
-    public Response getApplication(@PathParam("id") String name) {
+    public Response getApplication(@PathParam("name") String name) {
         ApplicationAdminService service = get(ApplicationAdminService.class);
         ApplicationId appId = service.getId(name);
         return response(service, appId);
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/MockCodecContext.java b/web/api/src/test/java/org/onosproject/codec/impl/MockCodecContext.java
index f49e829..25a4fbb 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/MockCodecContext.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/MockCodecContext.java
@@ -23,7 +23,7 @@
 /**
  * Mock codec context for use in codec unit tests.
  */
-public final class MockCodecContext implements CodecContext {
+public class MockCodecContext implements CodecContext {
 
     private ObjectMapper mapper = new ObjectMapper();
     private CodecManager manager = new CodecManager();
diff --git a/web/api/src/test/java/org/onosproject/rest/ApplicationsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/ApplicationsResourceTest.java
new file mode 100644
index 0000000..132a739
--- /dev/null
+++ b/web/api/src/test/java/org/onosproject/rest/ApplicationsResourceTest.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2015 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;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Optional;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.After;
+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.app.ApplicationAdminService;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.app.ApplicationState;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.impl.ApplicationCodec;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.codec.impl.MockCodecContext;
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplication;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.core.Version;
+
+import com.eclipsesource.json.JsonArray;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.ImmutableSet;
+import com.sun.jersey.api.client.WebResource;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Unit tests for applications REST APIs.
+ */
+
+public class ApplicationsResourceTest extends ResourceTest {
+
+    private static class MockCodecContextWithService extends MockCodecContext {
+        private ApplicationAdminService service;
+
+        MockCodecContextWithService(ApplicationAdminService service) {
+            this.service = service;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> T get(Class<T> serviceClass) {
+            return (T) service;
+        }
+    }
+
+    private ApplicationAdminService service;
+    private ApplicationId id1 = new DefaultApplicationId(1, "app1");
+    private ApplicationId id2 = new DefaultApplicationId(2, "app2");
+    private ApplicationId id3 = new DefaultApplicationId(3, "app3");
+    private ApplicationId id4 = new DefaultApplicationId(4, "app4");
+
+    private static final URI FURL = URI.create("mvn:org.foo-features/1.2a/xml/features");
+    private static final Version VER = Version.version(1, 2, "a", null);
+
+    private Application app1 =
+            new DefaultApplication(id1, VER,
+                    "app1", "origin1", ImmutableSet.of(), Optional.of(FURL),
+                    ImmutableSet.of("My Feature"));
+    private Application app2 =
+            new DefaultApplication(id2, VER,
+                    "app2", "origin2", ImmutableSet.of(), Optional.of(FURL),
+                    ImmutableSet.of("My Feature"));
+    private Application app3 =
+            new DefaultApplication(id3, VER,
+                    "app3", "origin3", ImmutableSet.of(), Optional.of(FURL),
+                    ImmutableSet.of("My Feature"));
+    private Application app4 =
+            new DefaultApplication(id4, VER,
+                    "app4", "origin4", ImmutableSet.of(), Optional.of(FURL),
+                    ImmutableSet.of("My Feature"));
+
+    /**
+     * Hamcrest matcher to check that an device representation in JSON matches
+     * the actual device.
+     */
+    private static class AppJsonMatcher extends TypeSafeMatcher<JsonObject> {
+        private final Application app;
+        private String reason = "";
+
+        public AppJsonMatcher(Application appValue) {
+            app = appValue;
+        }
+
+        @Override
+        public boolean matchesSafely(JsonObject jsonApp) {
+            // check id
+            short jsonId = (short) jsonApp.get("id").asInt();
+            if (jsonId != app.id().id()) {
+                reason = "id " + app.id().id();
+                return false;
+            }
+
+            // check name
+            String jsonName = jsonApp.get("name").asString();
+            if (!jsonName.equals(app.id().name())) {
+                reason = "name " + app.id().name();
+                return false;
+            }
+
+            // check origin
+            String jsonOrigin = jsonApp.get("origin").asString();
+            if (!jsonOrigin.equals(app.origin())) {
+                reason = "manufacturer " + app.origin();
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText(reason);
+        }
+    }
+
+    /**
+     * Factory to allocate an device matcher.
+     *
+     * @param app application object we are looking for
+     * @return matcher
+     */
+    private static AppJsonMatcher matchesApp(Application app) {
+        return new AppJsonMatcher(app);
+    }
+
+
+    @Before
+    public void setUp() {
+        service = createMock(ApplicationAdminService.class);
+
+        expect(service.getId("one"))
+                .andReturn(id1)
+                .anyTimes();
+        expect(service.getId("two"))
+                .andReturn(id2)
+                .anyTimes();
+        expect(service.getId("three"))
+                .andReturn(id3)
+                .anyTimes();
+        expect(service.getId("four"))
+                .andReturn(id4)
+                .anyTimes();
+
+        expect(service.getApplication(id3))
+                .andReturn(app3)
+                .anyTimes();
+        expect(service.getState(isA(ApplicationId.class)))
+                .andReturn(ApplicationState.ACTIVE)
+                .anyTimes();
+
+        // Register the services needed for the test
+        CodecManager codecService = new CodecManager();
+        codecService.activate();
+        ServiceDirectory testDirectory =
+                new TestServiceDirectory()
+                        .add(ApplicationAdminService.class, service)
+                        .add(ApplicationService.class, service)
+                        .add(CodecService.class, codecService);
+
+        BaseResource.setServiceDirectory(testDirectory);
+    }
+
+    @After
+    public void shutDown() {
+        verify(service);
+    }
+
+    /**
+     * Tests a GET of all applications when no applications are present.
+     */
+    @Test
+    public void getAllApplicationsEmpty() {
+        expect(service.getApplications())
+                .andReturn(ImmutableSet.of());
+        replay(service);
+
+        WebResource rs = resource();
+        String response = rs.path("applications").get(String.class);
+        assertThat(response, is("{\"applications\":[]}"));
+    }
+
+    /**
+     * Tests a GET of all applications with data.
+     */
+    @Test
+    public void getAllApplicationsPopulated() {
+        expect(service.getApplications())
+                .andReturn(ImmutableSet.of(app1, app2, app3, app4));
+        replay(service);
+
+        WebResource rs = resource();
+        String response = rs.path("applications").get(String.class);
+        assertThat(response, containsString("{\"applications\":["));
+
+        JsonObject result = JsonObject.readFrom(response);
+        assertThat(result, notNullValue());
+
+        assertThat(result.names(), hasSize(1));
+        assertThat(result.names().get(0), is("applications"));
+
+        JsonArray jsonApps = result.get("applications").asArray();
+        assertThat(jsonApps, notNullValue());
+        assertThat(jsonApps.size(), is(4));
+
+        assertThat(jsonApps.get(0).asObject(), matchesApp(app1));
+        assertThat(jsonApps.get(1).asObject(), matchesApp(app2));
+        assertThat(jsonApps.get(2).asObject(), matchesApp(app3));
+        assertThat(jsonApps.get(3).asObject(), matchesApp(app4));
+    }
+
+    /**
+     * Tests a GET of a single application.
+     */
+    @Test
+    public void getSingleApplication() {
+        replay(service);
+
+        WebResource rs = resource();
+        String response = rs.path("applications/three").get(String.class);
+
+        JsonObject result = JsonObject.readFrom(response);
+        assertThat(result, notNullValue());
+
+        assertThat(result, matchesApp(app3));
+    }
+
+    /**
+     * Tests a DELETE of a single application - this should
+     * attempt to uninstall it.
+     */
+    @Test
+    public void deleteApplication() {
+        service.uninstall(id3);
+        expectLastCall();
+
+        replay(service);
+
+        WebResource rs = resource();
+        rs.path("applications/three").delete();
+    }
+
+    /**
+     * Tests a DELETE of a single active application - this should
+     * attempt to uninstall it.
+     */
+    @Test
+    public void deleteActiveApplication() {
+        service.deactivate(id3);
+        expectLastCall();
+
+        replay(service);
+
+        WebResource rs = resource();
+        rs.path("applications/three/active").delete();
+    }
+
+    /**
+     * Tests a POST operation to the "active" URL.  This should attempt to
+     * activate the application.
+     */
+    @Test
+    public void postActiveApplication() {
+        service.activate(id3);
+        expectLastCall();
+
+        replay(service);
+
+        WebResource rs = resource();
+        rs.path("applications/three/active").post();
+    }
+
+    /**
+     * Tests a POST operation.  This should attempt to
+     * install the application.
+     */
+    @Test
+    public void postApplication() {
+        expect(service.install(isA(InputStream.class)))
+                .andReturn(app4)
+                .once();
+
+        replay(service);
+
+        ApplicationCodec codec = new ApplicationCodec();
+        String app4Json = codec.encode(app4,
+                                       new MockCodecContextWithService(service))
+                .asText();
+
+        WebResource rs = resource();
+        String response = rs.path("applications").post(String.class, app4Json);
+
+        JsonObject result = JsonObject.readFrom(response);
+        assertThat(result, notNullValue());
+
+        assertThat(result, matchesApp(app4));
+    }
+}