Add REST unit tests for Low Level Intents
- GET of all low level intents
- GET of a single low level intent that exists
- GET of a single low level intent that does not exist

Change-Id: I881777629799c0b075d64bf22007eeabe5c1929b
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRest.java b/src/test/java/net/onrc/onos/api/rest/TestRest.java
index 21bc4be..f91e8dc 100644
--- a/src/test/java/net/onrc/onos/api/rest/TestRest.java
+++ b/src/test/java/net/onrc/onos/api/rest/TestRest.java
@@ -1,6 +1,10 @@
 package net.onrc.onos.api.rest;
 
 import net.floodlightcontroller.restserver.RestletRoutable;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.restlet.resource.ClientResource;
 
 import java.util.LinkedList;
 import java.util.List;
@@ -91,4 +95,38 @@
 
         return portStartRange + (random.nextInt(portEndRange - portStartRange));
     }
+
+    /**
+     * 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
+     */
+    protected static JSONObject getJSONObject(final ClientResource client) {
+        try {
+            final String responseJSONString = client.get(String.class);
+            return new JSONObject(responseJSONString);
+        } 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.
+     */
+    protected static JSONArray getJSONArray(final ClientResource client) {
+        try {
+            final String responseJSONString = client.get(String.class);
+            return new JSONArray(responseJSONString);
+        } catch (JSONException jsonException) {
+            return null;
+        }
+    }
 }
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestIntent.java b/src/test/java/net/onrc/onos/api/rest/TestRestIntent.java
index 630000e..b229121 100644
--- a/src/test/java/net/onrc/onos/api/rest/TestRestIntent.java
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestIntent.java
@@ -94,6 +94,15 @@
     }
 
     /**
+     * Fetch the URL to use for Low Level Intents REST API calls.
+     *
+     * @return low level intents REST API URL
+     */
+    String getLowRestIntentUrl() {
+        return getBaseRestIntentUrl() + "/low";
+    }
+
+    /**
      * Utility function to locate an intent in a JSON collection
      * that has the given id.
      * The JSON collection of intents looks like:
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestIntentLowGet.java b/src/test/java/net/onrc/onos/api/rest/TestRestIntentLowGet.java
new file mode 100644
index 0000000..86beb1b
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestIntentLowGet.java
@@ -0,0 +1,253 @@
+package net.onrc.onos.api.rest;
+
+
+import com.google.common.net.InetAddresses;
+import net.onrc.onos.core.intent.IntentOperation;
+import net.onrc.onos.core.intent.IntentOperationList;
+import net.onrc.onos.core.intent.ShortestPathIntent;
+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.Assert;
+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 org.restlet.resource.ResourceException;
+
+import static net.onrc.onos.api.rest.ClientResourceStatusMatcher.hasStatusOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Unit tests to test the Intents GET REST APIs.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(PathCalcRuntimeModule.class)
+public class TestRestIntentLowGet extends TestRestIntent {
+    private static final Long LOCAL_PORT = 0xFFFEL;
+    private static final String BAD_SWITCH_INTENT_NAME = "No Such Switch Intent";
+    private static final String IP_ADDRESS_1 = "127.0.0.1";
+    private static final String IP_ADDRESS_2 = "127.0.0.2";
+    private static final String IP_ADDRESS_3 = "127.0.0.3";
+
+    /**
+     * Create the web server, PathCalcRuntime, and mocks required for
+     * all of the tests.
+     */
+    @Before
+    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
+    public void afterTest() {
+        tearDown();
+    }
+
+
+    /**
+     * Make a set of Intents that can be used as test data.
+     */
+    private void makeDefaultIntents() {
+        final int ipAddress1AsInt = InetAddresses.coerceToInteger(
+                InetAddresses.forString(IP_ADDRESS_1));
+        final int ipAddress2AsInt = InetAddresses.coerceToInteger(
+                InetAddresses.forString(IP_ADDRESS_2));
+        final int ipAddress3AsInt = InetAddresses.coerceToInteger(
+                InetAddresses.forString(IP_ADDRESS_3));
+
+        // create shortest path intents
+        final IntentOperationList opList = new IntentOperationList();
+        opList.add(IntentOperation.Operator.ADD,
+                new ShortestPathIntent(BAD_SWITCH_INTENT_NAME, 111L, 12L,
+                        LOCAL_PORT, 2L, 21L, LOCAL_PORT));
+        opList.add(IntentOperation.Operator.ADD,
+                new ShortestPathIntent("1:2", 1L, 14L, LOCAL_PORT, ipAddress1AsInt,
+                        4L, 41L, LOCAL_PORT, ipAddress2AsInt));
+        opList.add(IntentOperation.Operator.ADD,
+                new ShortestPathIntent("1:3", 2L, 23L, LOCAL_PORT, ipAddress2AsInt,
+                        3L, 32L, LOCAL_PORT, ipAddress3AsInt));
+
+        // compile high-level intent operations into low-level intent
+        // operations (calculate paths)
+
+        final IntentOperationList pathIntentOpList =
+                getRuntime().executeIntentOperations(opList);
+        assertThat(pathIntentOpList, notNullValue());
+
+    }
+
+    /**
+     * Find the intent with the given ID in the intent array.
+     *
+     * @param intents array of intents
+     * @param id this is the id too look up
+     * @return JSONObject for the intent if found, null otherwise
+     * @throws JSONException if the intent object marshalling fails
+     */
+    private JSONObject findIntent(final JSONArray intents, final String id)
+                       throws JSONException {
+
+        if (id == null) {
+            return null;
+        }
+
+        JSONObject result = null;
+        for (int intentIndex = 0; intentIndex < intents.length(); intentIndex++) {
+            final JSONObject thisIntent = intents.getJSONObject(intentIndex);
+            if (id.equals(thisIntent.getString("id"))) {
+                result = thisIntent;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Test that the GET of all Intents REST call returns the proper result.
+     * The call to get all Intents should return 3 items, an HTTP status of OK,
+     * and the proper Intent data.
+     *
+     * @throws Exception to fail the test if any unhandled errors occur
+     */
+    @Test
+    public void testFetchOfAllLowLevelIntents() throws Exception {
+
+        makeDefaultIntents();
+
+        final ClientResource client = new ClientResource(getLowRestIntentUrl());
+        final JSONArray intents = getJSONArray(client);
+        assertThat(intents, is(notNullValue()));
+
+        // HTTP status should be OK
+        assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+        // 2 low level intents should have been fetched - one of the
+        // high level intents is in error
+        assertThat(intents.length(), is(equalTo(2)));
+
+        //  Check that intent 0 is correct
+        final JSONObject intent0 = findIntent(intents, "1:2___0");
+        assertThat(intent0, is(notNullValue()));
+
+        //  Check the values of the fields in low level intent 0
+        assertThat(intent0.getString("id"), is(equalTo("1:2___0")));
+        assertThat(intent0.getString("state"), is(equalTo("INST_REQ")));
+        assertThat(intent0.getBoolean("pathFrozen"), is(equalTo(false)));
+
+        // Check the path on intent 0
+        final JSONArray path0 = intent0.getJSONArray("path");
+        assertThat(path0, is(notNullValue()));
+        final JSONObject ports0 = path0.getJSONObject(0);
+        assertThat(ports0, is(notNullValue()));
+        final JSONObject dst0 = ports0.getJSONObject("dst");
+        assertThat(dst0, is(notNullValue()));
+        final String dstPortNumber0 = dst0.getString("portNumber");
+        assertThat(dstPortNumber0, is(equalTo("41")));
+        final JSONObject src0 = ports0.getJSONObject("src");
+        assertThat(src0, is(notNullValue()));
+        final String srcPortNumber0 = src0.getString("portNumber");
+        assertThat(srcPortNumber0, is(equalTo("14")));
+
+        //  Check that intent 1 is correct
+        final JSONObject intent1 = findIntent(intents, "1:3___0");
+        assertThat(intent1, is(notNullValue()));
+
+        //  Check the values of the fields in low level intent 1
+        assertThat(intent1.getString("id"), is(equalTo("1:3___0")));
+        assertThat(intent1.getString("state"), is(equalTo("INST_REQ")));
+        assertThat(intent1.getBoolean("pathFrozen"), is(equalTo(false)));
+
+        // Check the path on intent 1
+        final JSONArray path1 = intent1.getJSONArray("path");
+        assertThat(path1, is(notNullValue()));
+        final JSONObject ports1 = path1.getJSONObject(0);
+        assertThat(ports1, is(notNullValue()));
+        final JSONObject dst1 = ports1.getJSONObject("dst");
+        assertThat(dst1, is(notNullValue()));
+        final String dstPortNumber1 = dst1.getString("portNumber");
+        assertThat(dstPortNumber1, is(equalTo("32")));
+        final JSONObject src1 = ports1.getJSONObject("src");
+        assertThat(src1, is(notNullValue()));
+        final String srcPortNumber1 = src1.getString("portNumber");
+        assertThat(srcPortNumber1, is(equalTo("23")));
+    }
+
+    /**
+     * Test that the GET of a single Intent REST call returns the proper result
+     * when given a bad Intent id. The call to get the Intent should return a
+     * status of NOT_FOUND.
+     *
+     * @throws JSONException if a bad JSON object is returned for the error
+     */
+    @Test
+    public void testFetchOfBadLowLevelIntent() throws JSONException {
+
+        makeDefaultIntents();
+
+        final ClientResource client = new ClientResource(getLowRestIntentUrl() + "/2334");
+
+        try {
+            client.get();
+            // The get operation should have thrown a ResourceException.
+            // Fail because the Exception was not seen.
+            Assert.fail("Invalid intent fetch did not cause an exception");
+        } catch (ResourceException resourceError) {
+            // The HTTP status should be NOT FOUND
+            assertThat(client, hasStatusOf(Status.CLIENT_ERROR_NOT_FOUND));
+
+            //  Check that the error entity is correct
+            final String responseErrorString = client.getResponse().getEntityAsText();
+            final JSONObject responseError = new JSONObject(responseErrorString);
+            assertThat(responseError.getString("code"),
+                       is(equalTo("INTENT_NOT_FOUND")));
+            assertThat(responseError.getString("summary"),
+                       is(equalTo("Intent not found")));
+            assertThat(responseError.getString("formattedDescription"),
+                       containsString("An intent with the identifier"));
+        }
+    }
+
+    /**
+     * Test that the GET of a single Low Level Intent REST call returns the
+     * proper result for an existing Intent. The call to get the Low Level
+     * Intent should return a status of OK, and the data for the
+     * Low Level Intent should be correct.
+     *
+     * @throws JSONException if the JSON cannot be marshalled into an object.
+     */
+    @Test
+    public void testFetchOfGoodLowLevelIntent() throws JSONException {
+
+        makeDefaultIntents();
+
+        final ClientResource client = new ClientResource(getLowRestIntentUrl() + "/3___0");
+        final JSONObject intent = getJSONObject(client);
+
+        // HTTP status should be OK
+        assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+        //  Intent data should be correct
+        assertThat(intent, is(notNullValue()));
+
+        assertThat(intent.getString("id"), is(equalTo("1:3___0")));
+        assertThat(intent.getString("state"), is(equalTo("INST_REQ")));
+        assertThat(intent.getBoolean("pathFrozen"), is(false));
+    }
+}
+
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java b/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java
index 3ef89eb..aef7262 100644
--- a/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java
@@ -6,10 +6,6 @@
 import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
 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
@@ -72,37 +68,4 @@
         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;
-        }
-    }
 }