Topology REST API modifications and unit tests

- added a Serializer for Topology operations
- added a REST test harness class for Topology tests
- added REST API tests for topology get of all
  topology, links, switches, and devices.

Change-Id: I13e3654aad7c0f5af41f07be66da82330933d3a3
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java b/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java
new file mode 100644
index 0000000..e87d232
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java
@@ -0,0 +1,109 @@
+package net.onrc.onos.api.rest;
+
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.onrc.onos.core.intent.runtime.IntentTestMocks;
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import net.onrc.onos.core.intent.runtime.web.IntentWebRoutable;
+import net.onrc.onos.core.topology.ITopologyService;
+import net.onrc.onos.core.topology.web.TopologyWebRoutable;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.restlet.resource.ClientResource;
+
+/**
+ * Test harness for Topology based REST API tests.  This class maintains the
+ * web server and mocks required for testing topology APIs.  REST API tests
+ * for topology should inherit from this class.
+ */
+public class TestRestTopology extends TestRest {
+
+    private IntentTestMocks mocks;
+
+    /**
+     * Fetch the Intent mocking object.
+     *
+     * @return intent mocking object
+     */
+    IntentTestMocks getMocks() {
+        return mocks;
+    }
+
+    /**
+     * Create the web server and mocks required for the topology tests.
+     */
+    @Override
+    public void setUp() {
+        mocks = new IntentTestMocks();
+        mocks.setUpIntentMocks();
+
+        addRestlet(new TopologyWebRoutable());
+        super.setUp();
+
+        final PathCalcRuntimeModule runtime = new PathCalcRuntimeModule();
+        final FloodlightModuleContext moduleContext = getMocks().getModuleContext();
+        try {
+            runtime.init(moduleContext);
+        } catch (FloodlightModuleException floodlightEx) {
+            throw new IllegalArgumentException(floodlightEx);
+        }
+        runtime.startUp(moduleContext);
+
+        getRestApiServer().addAttribute(ITopologyService.class.getCanonicalName(),
+                                        mocks.getTopologyService());
+    }
+
+    /**
+     * Remove anything that will interfere with the next test running correctly.
+     * Shuts down the test REST web server and removes the mocks.
+     */
+    @Override
+    public void tearDown() {
+        getMocks().tearDownIntentMocks();
+        super.tearDown();
+    }
+
+    /**
+     * Fetch the base URL for Topology REST APIs.
+     *
+     * @return base URL
+     */
+    String getBaseRestTopologyUrl() {
+        return getBaseRestUrl() + "/topology";
+    }
+
+    /**
+     * Get the JSON object representation for the top level object referred
+     * to by the given client.
+     *
+     * @param client the ClientResource that references the JSON object
+     * @return JSONObject that represents the object, null if it can't be
+     *         fetched
+     */
+    JSONObject getJSONObject(final ClientResource client) {
+        try {
+            final String topologyJSONString = client.get(String.class);
+            return new JSONObject(topologyJSONString);
+        } catch (JSONException jsonException) {
+            return null;
+        }
+    }
+
+    /**
+     * Get the JSON array representation for the array referred to by
+     * the given client.
+     *
+     * @param client the ClientResource that references the JSON array
+     * @return JSONArray that represents the array, null if it can't be
+     *         fetched.
+     */
+    JSONArray getJSONArray(final ClientResource client) {
+        try {
+            final String topologyJSONString = client.get(String.class);
+            return new JSONArray(topologyJSONString);
+        } catch (JSONException jsonException) {
+            return null;
+        }
+    }
+}
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestTopologyGet.java b/src/test/java/net/onrc/onos/api/rest/TestRestTopologyGet.java
new file mode 100644
index 0000000..eb8ee2e
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestTopologyGet.java
@@ -0,0 +1,211 @@
+package net.onrc.onos.api.rest;
+
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.restlet.data.Status;
+import org.restlet.resource.ClientResource;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static net.onrc.onos.api.rest.ClientResourceStatusMatcher.hasStatusOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Tests for topology REST get operations.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(PathCalcRuntimeModule.class)
+public class TestRestTopologyGet extends TestRestTopology {
+
+    /**
+     * Create the web server and mocks required for
+     * all of the tests.
+     */
+    @Before
+    @SuppressWarnings("ununsed")
+    public void beforeTest() {
+        setRestPort(generateRandomPort());
+        setUp();
+    }
+
+
+    /**
+     * Remove anything that will interfere with the next test running correctly.
+     * Shuts down the test REST web server and removes the mocks.
+     */
+    @After
+    @SuppressWarnings("unused")
+    public void afterTest() {
+        tearDown();
+    }
+
+    /**
+     * Check that the JSON array returned for the switches element matches
+     * the data in the mocked topology.
+     *
+     * @param switches JSON array of switches
+     * @throws JSONException if the JSON is not properly specified
+     */
+    private void checkSwitches(final JSONArray switches) throws JSONException {
+        assertThat(switches.length(), is(equalTo(4)));
+
+        // Check that the first switch has the proper data
+        final JSONObject switch0 = switches.getJSONObject(0);
+        assertThat(switch0.length(), is(equalTo(3)));
+        assertThat(switch0.getString("dpid"), is(equalTo("00:00:00:00:00:00:00:02")));
+        assertThat(switch0.getString("state"), is(equalTo("ACTIVE")));
+
+        // Check that the ports array for the switch is correct
+        final JSONArray switch0Ports = switch0.getJSONArray("ports");
+
+        // check the length of the port array
+        assertThat(switch0Ports.length(), equalTo(4));
+
+        // check the contents of the ports array.  All of the ports should be
+        // active and refer to this switch.
+        for (int portIndex = 0; portIndex < switch0Ports.length(); portIndex++) {
+            final JSONObject switchPort = switch0Ports.getJSONObject(portIndex);
+            assertThat(switchPort.getString("dpid"), is(equalTo("00:00:00:00:00:00:00:02")));
+            assertThat(switchPort.getString("state"), is(equalTo("ACTIVE")));
+        }
+    }
+
+    /**
+     * Check that the JSON array returned for the links element matches
+     * the data in the mocked topology.
+     *
+     * @param links JSON array of links
+     * @throws JSONException if the JSON is not properly specified
+     */
+    private void checkLinks(final JSONArray links) throws JSONException {
+        // Check the length of the links array
+        assertThat(links.length(), is(equalTo(10)));
+
+        final Set<String> fromLinks = new HashSet<>();
+        final Set<String> toLinks = new HashSet<>();
+
+        // Check that the source and destination of links to switch 0 are
+        // correct
+        for (int linkIndex = 0; linkIndex < links.length(); linkIndex++) {
+            final JSONObject link = links.getJSONObject(linkIndex);
+            final String src = link.getString("src-switch");
+            final String dst = link.getString("dst-switch");
+            assertThat(src, is(notNullValue()));
+            assertThat(dst, is(notNullValue()));
+
+            if (src.equals("00:00:00:00:00:00:00:02")) {
+                toLinks.add(dst);
+            }
+
+            if (dst.equals("00:00:00:00:00:00:00:02")) {
+                fromLinks.add(src);
+            }
+        }
+
+        assertThat(toLinks, hasItems("00:00:00:00:00:00:00:01",
+                                     "00:00:00:00:00:00:00:03",
+                                     "00:00:00:00:00:00:00:04"));
+
+        assertThat(fromLinks, hasItems("00:00:00:00:00:00:00:01",
+                                       "00:00:00:00:00:00:00:03",
+                                       "00:00:00:00:00:00:00:04"));
+    }
+
+    /**
+     * Check that the JSON array returned for the devices element matches
+     * the data in the mocked topology.
+     *
+     * @param devices JSON array of switches
+     */
+    private void checkDevices(final JSONArray devices) {
+        // devices array should be empty
+        assertThat(devices.length(), is(equalTo(0)));
+    }
+
+    /**
+     * Test that the GET of all Topology REST call returns the proper result.
+     * The call to get all Topology should return 3 items (switches, links,
+     * and devices), an HTTP status of OK, and the proper topology data.
+     */
+    @Test
+    public void testFetchOfAllTopology() throws Exception {
+        final ClientResource client = new ClientResource(getBaseRestTopologyUrl());
+        final JSONObject topology = getJSONObject(client);
+
+        // HTTP status should be OK
+        assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+        // Check the number of top level members in the topology object
+        assertThat(topology.length(), is(equalTo(3)));
+
+        //  Check the switches element
+        final JSONArray switches = topology.getJSONArray("switches");
+        checkSwitches(switches);
+
+        // Check the values in the links array
+        final JSONArray links = topology.getJSONArray("links");
+        checkLinks(links);
+
+        // Check the devices array
+        final JSONArray devices = topology.getJSONArray("devices");
+        checkDevices(devices);
+    }
+
+    /**
+     * Test that the GET of all switches REST call returns the proper result.
+     * The call to get all switches should return the correct switch data.
+     */
+    @Test
+    public void testFetchOfAllSwitches() throws Exception {
+        final ClientResource client = new ClientResource(getBaseRestTopologyUrl() + "/switches");
+        final JSONArray switches = getJSONArray(client);
+
+        // HTTP status should be OK
+        assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+        checkSwitches(switches);
+    }
+
+    /**
+     * Test that the GET of all links REST call returns the proper result.
+     * The call to get all links should return the proper link data.
+     */
+    @Test
+    public void testFetchOfAllLinks() throws Exception {
+        final ClientResource client = new ClientResource(getBaseRestTopologyUrl() + "/links");
+        final JSONArray links = getJSONArray(client);
+
+        // HTTP status should be OK
+        assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+        checkLinks(links);
+    }
+
+    /**
+     * Test that the GET of all devices REST call returns the proper result.
+     * The call to get all devices should return no devices.
+     */
+    @Test
+    public void testFetchOfAllDevices() throws Exception {
+        final ClientResource client = new ClientResource(getBaseRestTopologyUrl() + "/switches");
+        final JSONArray devices = getJSONArray(client);
+
+        // HTTP status should be OK
+        assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+        checkSwitches(devices);
+    }
+}