You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by ce...@apache.org on 2017/03/02 20:51:54 UTC

[08/10] incubator-metron git commit: METRON-503: Metron REST API this closes apache/incubator-metron#316

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/e6628499/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorEnrichmentConfigServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorEnrichmentConfigServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorEnrichmentConfigServiceImplTest.java
new file mode 100644
index 0000000..d292948
--- /dev/null
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorEnrichmentConfigServiceImplTest.java
@@ -0,0 +1,256 @@
+/**
+ * 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 org.apache.metron.rest.service.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.adrianwalker.multilinestring.Multiline;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.api.DeleteBuilder;
+import org.apache.curator.framework.api.GetChildrenBuilder;
+import org.apache.curator.framework.api.GetDataBuilder;
+import org.apache.curator.framework.api.SetDataBuilder;
+import org.apache.metron.common.configuration.ConfigurationType;
+import org.apache.metron.common.configuration.enrichment.EnrichmentConfig;
+import org.apache.metron.common.configuration.enrichment.SensorEnrichmentConfig;
+import org.apache.metron.common.configuration.enrichment.threatintel.ThreatIntelConfig;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.service.SensorEnrichmentConfigService;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.data.Stat;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings("ALL")
+public class SensorEnrichmentConfigServiceImplTest {
+  @Rule
+  public final ExpectedException exception = ExpectedException.none();
+
+  ObjectMapper objectMapper;
+  CuratorFramework curatorFramework;
+  SensorEnrichmentConfigService sensorEnrichmentConfigService;
+
+  /**
+   {
+   "enrichment" : {
+   "fieldMap": {
+   "geo": ["ip_dst_addr"]
+   }
+   },
+   "threatIntel": {
+   "fieldMap": {
+   "hbaseThreatIntel": ["ip_src_addr"]
+   },
+   "fieldToTypeMap": {
+   "ip_src_addr" : ["malicious_ip"]
+   }
+   }
+   }
+   */
+  @Multiline
+  public static String broJson;
+
+  @Before
+  public void setUp() throws Exception {
+    objectMapper = mock(ObjectMapper.class);
+    curatorFramework = mock(CuratorFramework.class);
+    sensorEnrichmentConfigService = new SensorEnrichmentConfigServiceImpl(objectMapper, curatorFramework);
+  }
+
+
+  @Test
+  public void deleteShouldProperlyCatchNoNodeExceptionAndReturnFalse() throws Exception {
+    DeleteBuilder builder = mock(DeleteBuilder.class);
+
+    when(curatorFramework.delete()).thenReturn(builder);
+    when(builder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro")).thenThrow(KeeperException.NoNodeException.class);
+
+    assertFalse(sensorEnrichmentConfigService.delete("bro"));
+  }
+
+  @Test
+  public void deleteShouldProperlyCatchNonNoNodeExceptionAndThrowRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    DeleteBuilder builder = mock(DeleteBuilder.class);
+
+    when(curatorFramework.delete()).thenReturn(builder);
+    when(builder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro")).thenThrow(Exception.class);
+
+    assertFalse(sensorEnrichmentConfigService.delete("bro"));
+  }
+
+  @Test
+  public void deleteShouldReturnTrueWhenClientSuccessfullyCallsDelete() throws Exception {
+    DeleteBuilder builder = mock(DeleteBuilder.class);
+
+    when(curatorFramework.delete()).thenReturn(builder);
+    when(builder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro")).thenReturn(null);
+
+    assertTrue(sensorEnrichmentConfigService.delete("bro"));
+
+    verify(curatorFramework).delete();
+  }
+
+  @Test
+  public void findOneShouldProperlyReturnSensorEnrichmentConfig() throws Exception {
+    final SensorEnrichmentConfig sensorEnrichmentConfig = getTestSensorEnrichmentConfig();
+
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro")).thenReturn(broJson.getBytes());
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    assertEquals(getTestSensorEnrichmentConfig(), sensorEnrichmentConfigService.findOne("bro"));
+  }
+
+  @Test
+  public void findOneShouldReturnNullWhenNoNodeExceptionIsThrown() throws Exception {
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro")).thenThrow(KeeperException.NoNodeException.class);
+
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    assertNull(sensorEnrichmentConfigService.findOne("bro"));
+  }
+
+  @Test
+  public void findOneShouldWrapNonNoNodeExceptionInRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro")).thenThrow(Exception.class);
+
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    sensorEnrichmentConfigService.findOne("bro");
+  }
+
+  @Test
+  public void getAllTypesShouldProperlyReturnTypes() throws Exception {
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot()))
+            .thenReturn(new ArrayList() {{
+              add("bro");
+              add("squid");
+            }});
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    assertEquals(new ArrayList() {{
+      add("bro");
+      add("squid");
+    }}, sensorEnrichmentConfigService.getAllTypes());
+  }
+
+  @Test
+  public void getAllTypesShouldReturnNullWhenNoNodeExceptionIsThrown() throws Exception {
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot())).thenThrow(KeeperException.NoNodeException.class);
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    assertEquals(new ArrayList<>(), sensorEnrichmentConfigService.getAllTypes());
+  }
+
+  @Test
+  public void getAllTypesShouldWrapNonNoNodeExceptionInRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot())).thenThrow(Exception.class);
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    sensorEnrichmentConfigService.getAllTypes();
+  }
+
+  @Test
+  public void getAllShouldProperlyReturnSensorEnrichmentConfigs() throws Exception {
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot()))
+            .thenReturn(new ArrayList() {{
+              add("bro");
+            }});
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    final SensorEnrichmentConfig sensorEnrichmentConfig = getTestSensorEnrichmentConfig();
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro")).thenReturn(broJson.getBytes());
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    assertEquals(new HashMap() {{ put("bro", sensorEnrichmentConfig);}}, sensorEnrichmentConfigService.getAll());
+  }
+
+  @Test
+  public void saveShouldWrapExceptionInRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    SetDataBuilder setDataBuilder = mock(SetDataBuilder.class);
+    when(setDataBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro", broJson.getBytes())).thenThrow(Exception.class);
+
+    when(curatorFramework.setData()).thenReturn(setDataBuilder);
+
+    sensorEnrichmentConfigService.save("bro", new SensorEnrichmentConfig());
+  }
+
+  @Test
+  public void saveShouldReturnSameConfigThatIsPassedOnSuccessfulSave() throws Exception {
+    final SensorEnrichmentConfig sensorEnrichmentConfig = getTestSensorEnrichmentConfig();
+
+    when(objectMapper.writeValueAsString(sensorEnrichmentConfig)).thenReturn(broJson);
+
+    SetDataBuilder setDataBuilder = mock(SetDataBuilder.class);
+    when(setDataBuilder.forPath(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro", broJson.getBytes())).thenReturn(new Stat());
+    when(curatorFramework.setData()).thenReturn(setDataBuilder);
+
+    assertEquals(sensorEnrichmentConfig, sensorEnrichmentConfigService.save("bro", sensorEnrichmentConfig));
+    verify(setDataBuilder).forPath(eq(ConfigurationType.ENRICHMENT.getZookeeperRoot() + "/bro"), eq(broJson.getBytes()));
+  }
+
+  @Test
+  public void getAvailableEnrichmentsShouldReturnEnrichments() throws Exception {
+    assertEquals(new ArrayList<String>() {{
+      add("geo");
+      add("host");
+      add("whois");
+    }}, sensorEnrichmentConfigService.getAvailableEnrichments());
+  }
+
+  private SensorEnrichmentConfig getTestSensorEnrichmentConfig() {
+    SensorEnrichmentConfig sensorEnrichmentConfig = new SensorEnrichmentConfig();
+    EnrichmentConfig enrichmentConfig = new EnrichmentConfig();
+    enrichmentConfig.setFieldMap(new HashMap() {{ put("geo", Arrays.asList("ip_dst_addr")); }});
+    sensorEnrichmentConfig.setEnrichment(enrichmentConfig);
+    ThreatIntelConfig threatIntelConfig = new ThreatIntelConfig();
+    threatIntelConfig.setFieldMap(new HashMap() {{ put("hbaseThreatIntel", Arrays.asList("ip_src_addr")); }});
+    threatIntelConfig.setFieldToTypeMap(new HashMap() {{ put("ip_src_addr", Arrays.asList("malicious_ip")); }});
+    sensorEnrichmentConfig.setThreatIntel(threatIntelConfig);
+    return sensorEnrichmentConfig;
+  }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/e6628499/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorIndexingConfigServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorIndexingConfigServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorIndexingConfigServiceImplTest.java
new file mode 100644
index 0000000..43ca0f7
--- /dev/null
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorIndexingConfigServiceImplTest.java
@@ -0,0 +1,234 @@
+/**
+ * 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 org.apache.metron.rest.service.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.adrianwalker.multilinestring.Multiline;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.api.DeleteBuilder;
+import org.apache.curator.framework.api.GetChildrenBuilder;
+import org.apache.curator.framework.api.GetDataBuilder;
+import org.apache.curator.framework.api.SetDataBuilder;
+import org.apache.metron.common.configuration.ConfigurationType;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.service.SensorIndexingConfigService;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.data.Stat;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings("ALL")
+public class SensorIndexingConfigServiceImplTest {
+  @Rule
+  public final ExpectedException exception = ExpectedException.none();
+
+  ObjectMapper objectMapper;
+  CuratorFramework curatorFramework;
+  SensorIndexingConfigService sensorIndexingConfigService;
+
+  /**
+   {
+   "hdfs" : {
+   "index": "bro",
+   "batchSize": 5,
+   "enabled" : true
+   }
+   }
+   */
+  @Multiline
+  public static String broJson;
+
+  @Before
+  public void setUp() throws Exception {
+    objectMapper = mock(ObjectMapper.class);
+    curatorFramework = mock(CuratorFramework.class);
+    sensorIndexingConfigService = new SensorIndexingConfigServiceImpl(objectMapper, curatorFramework);
+  }
+
+
+  @Test
+  public void deleteShouldProperlyCatchNoNodeExceptionAndReturnFalse() throws Exception {
+    DeleteBuilder builder = mock(DeleteBuilder.class);
+
+    when(curatorFramework.delete()).thenReturn(builder);
+    when(builder.forPath(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro")).thenThrow(KeeperException.NoNodeException.class);
+
+    assertFalse(sensorIndexingConfigService.delete("bro"));
+  }
+
+  @Test
+  public void deleteShouldProperlyCatchNonNoNodeExceptionAndThrowRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    DeleteBuilder builder = mock(DeleteBuilder.class);
+
+    when(curatorFramework.delete()).thenReturn(builder);
+    when(builder.forPath(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro")).thenThrow(Exception.class);
+
+    assertFalse(sensorIndexingConfigService.delete("bro"));
+  }
+
+  @Test
+  public void deleteShouldReturnTrueWhenClientSuccessfullyCallsDelete() throws Exception {
+    DeleteBuilder builder = mock(DeleteBuilder.class);
+
+    when(curatorFramework.delete()).thenReturn(builder);
+    when(builder.forPath(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro")).thenReturn(null);
+
+    assertTrue(sensorIndexingConfigService.delete("bro"));
+
+    verify(curatorFramework).delete();
+  }
+
+  @Test
+  public void findOneShouldProperlyReturnSensorEnrichmentConfig() throws Exception {
+    final Map<String, Object> sensorIndexingConfig = getTestSensorIndexingConfig();
+
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro")).thenReturn(broJson.getBytes());
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    assertEquals(getTestSensorIndexingConfig(), sensorIndexingConfigService.findOne("bro"));
+  }
+
+  @Test
+  public void findOneShouldReturnNullWhenNoNodeExceptionIsThrown() throws Exception {
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro")).thenThrow(KeeperException.NoNodeException.class);
+
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    assertNull(sensorIndexingConfigService.findOne("bro"));
+  }
+
+  @Test
+  public void findOneShouldWrapNonNoNodeExceptionInRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro")).thenThrow(Exception.class);
+
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    sensorIndexingConfigService.findOne("bro");
+  }
+
+  @Test
+  public void getAllTypesShouldProperlyReturnTypes() throws Exception {
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot()))
+            .thenReturn(new ArrayList() {{
+              add("bro");
+              add("squid");
+            }});
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    assertEquals(new ArrayList() {{
+      add("bro");
+      add("squid");
+    }}, sensorIndexingConfigService.getAllTypes());
+  }
+
+  @Test
+  public void getAllTypesShouldReturnNullWhenNoNodeExceptionIsThrown() throws Exception {
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot())).thenThrow(KeeperException.NoNodeException.class);
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    assertEquals(new ArrayList<>(), sensorIndexingConfigService.getAllTypes());
+  }
+
+  @Test
+  public void getAllTypesShouldWrapNonNoNodeExceptionInRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot())).thenThrow(Exception.class);
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    sensorIndexingConfigService.getAllTypes();
+  }
+
+  @Test
+  public void getAllShouldProperlyReturnSensorEnrichmentConfigs() throws Exception {
+    final Map<String, Object> sensorIndexingConfig = getTestSensorIndexingConfig();
+
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot()))
+            .thenReturn(new ArrayList() {{
+              add("bro");
+            }});
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro")).thenReturn(broJson.getBytes());
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    assertEquals(new HashMap() {{ put("bro", sensorIndexingConfig);}}, sensorIndexingConfigService.getAll());
+  }
+
+  @Test
+  public void saveShouldWrapExceptionInRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    SetDataBuilder setDataBuilder = mock(SetDataBuilder.class);
+    when(setDataBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro", broJson.getBytes())).thenThrow(Exception.class);
+
+    when(curatorFramework.setData()).thenReturn(setDataBuilder);
+
+    sensorIndexingConfigService.save("bro", new HashMap<>());
+  }
+
+  @Test
+  public void saveShouldReturnSameConfigThatIsPassedOnSuccessfulSave() throws Exception {
+    final Map<String, Object> sensorIndexingConfig = getTestSensorIndexingConfig();
+
+    when(objectMapper.writeValueAsString(sensorIndexingConfig)).thenReturn(broJson);
+
+    SetDataBuilder setDataBuilder = mock(SetDataBuilder.class);
+    when(setDataBuilder.forPath(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro", broJson.getBytes())).thenReturn(new Stat());
+    when(curatorFramework.setData()).thenReturn(setDataBuilder);
+
+    assertEquals(sensorIndexingConfig, sensorIndexingConfigService.save("bro", sensorIndexingConfig));
+    verify(setDataBuilder).forPath(eq(ConfigurationType.INDEXING.getZookeeperRoot() + "/bro"), eq(broJson.getBytes()));
+  }
+
+  private Map<String, Object> getTestSensorIndexingConfig() {
+    Map<String, Object> sensorIndexingConfig = new HashMap<>();
+    sensorIndexingConfig.put("hdfs", new HashMap(){{
+      put("index", "bro");
+      put("batchSize", 5);
+      put("enabled", true);
+    }});
+    return sensorIndexingConfig;
+  }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/e6628499/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorParserConfigServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorParserConfigServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorParserConfigServiceImplTest.java
new file mode 100644
index 0000000..d35a48c
--- /dev/null
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/SensorParserConfigServiceImplTest.java
@@ -0,0 +1,344 @@
+/**
+ * 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 org.apache.metron.rest.service.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.adrianwalker.multilinestring.Multiline;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.api.DeleteBuilder;
+import org.apache.curator.framework.api.GetChildrenBuilder;
+import org.apache.curator.framework.api.GetDataBuilder;
+import org.apache.curator.framework.api.SetDataBuilder;
+import org.apache.metron.common.configuration.ConfigurationType;
+import org.apache.metron.common.configuration.SensorParserConfig;
+import org.apache.metron.rest.RestException;
+import org.apache.metron.rest.model.ParseMessageRequest;
+import org.apache.metron.rest.service.GrokService;
+import org.apache.metron.rest.service.SensorParserConfigService;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.data.Stat;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.springframework.core.env.Environment;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings("ALL")
+public class SensorParserConfigServiceImplTest {
+  @Rule
+  public final ExpectedException exception = ExpectedException.none();
+
+  Environment environment;
+  ObjectMapper objectMapper;
+  CuratorFramework curatorFramework;
+  GrokService grokService;
+  SensorParserConfigService sensorParserConfigService;
+
+  /**
+   {
+   "parserClassName": "org.apache.metron.parsers.GrokParser",
+   "sensorTopic": "squid",
+   "parserConfig": {
+   "grokPath": "/patterns/squid",
+   "patternLabel": "SQUID_DELIMITED",
+   "timestampField": "timestamp"
+   }
+   }
+   */
+  @Multiline
+  public static String squidJson;
+
+  /**
+   {
+   "parserClassName":"org.apache.metron.parsers.bro.BasicBroParser",
+   "sensorTopic":"bro",
+   "parserConfig": {}
+   }
+   */
+  @Multiline
+  public static String broJson;
+
+  @Before
+  public void setUp() throws Exception {
+    objectMapper = mock(ObjectMapper.class);
+    curatorFramework = mock(CuratorFramework.class);
+    grokService = mock(GrokService.class);
+    sensorParserConfigService = new SensorParserConfigServiceImpl(objectMapper, curatorFramework, grokService);
+  }
+
+
+  @Test
+  public void deleteShouldProperlyCatchNoNodeExceptionAndReturnFalse() throws Exception {
+    DeleteBuilder builder = mock(DeleteBuilder.class);
+
+    when(curatorFramework.delete()).thenReturn(builder);
+    when(builder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenThrow(KeeperException.NoNodeException.class);
+
+    assertFalse(sensorParserConfigService.delete("bro"));
+  }
+
+  @Test
+  public void deleteShouldProperlyCatchNonNoNodeExceptionAndThrowRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    DeleteBuilder builder = mock(DeleteBuilder.class);
+
+    when(curatorFramework.delete()).thenReturn(builder);
+    when(builder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenThrow(Exception.class);
+
+    assertFalse(sensorParserConfigService.delete("bro"));
+  }
+
+  @Test
+  public void deleteShouldReturnTrueWhenClientSuccessfullyCallsDelete() throws Exception {
+    DeleteBuilder builder = mock(DeleteBuilder.class);
+
+    when(curatorFramework.delete()).thenReturn(builder);
+    when(builder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenReturn(null);
+
+    assertTrue(sensorParserConfigService.delete("bro"));
+
+    verify(curatorFramework).delete();
+  }
+
+  @Test
+  public void findOneShouldProperlyReturnSensorEnrichmentConfig() throws Exception {
+    final SensorParserConfig sensorParserConfig = getTestBroSensorParserConfig();
+
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenReturn(broJson.getBytes());
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    assertEquals(getTestBroSensorParserConfig(), sensorParserConfigService.findOne("bro"));
+  }
+
+  @Test
+  public void findOneShouldReturnNullWhenNoNodeExceptionIsThrown() throws Exception {
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenThrow(KeeperException.NoNodeException.class);
+
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    assertNull(sensorParserConfigService.findOne("bro"));
+  }
+
+  @Test
+  public void findOneShouldWrapNonNoNodeExceptionInRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenThrow(Exception.class);
+
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    sensorParserConfigService.findOne("bro");
+  }
+
+  @Test
+  public void getAllTypesShouldProperlyReturnTypes() throws Exception {
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot()))
+            .thenReturn(new ArrayList() {{
+              add("bro");
+              add("squid");
+            }});
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    assertEquals(new ArrayList() {{
+      add("bro");
+      add("squid");
+    }}, sensorParserConfigService.getAllTypes());
+  }
+
+  @Test
+  public void getAllTypesShouldReturnEmptyListWhenNoNodeExceptionIsThrown() throws Exception {
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot())).thenThrow(KeeperException.NoNodeException.class);
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    assertEquals(new ArrayList<>(), sensorParserConfigService.getAllTypes());
+  }
+
+  @Test
+  public void getAllTypesShouldWrapNonNoNodeExceptionInRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot())).thenThrow(Exception.class);
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    sensorParserConfigService.getAllTypes();
+  }
+
+  @Test
+  public void getAllShouldProperlyReturnSensorParserConfigs() throws Exception {
+    GetChildrenBuilder getChildrenBuilder = mock(GetChildrenBuilder.class);
+    when(getChildrenBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot()))
+            .thenReturn(new ArrayList() {{
+              add("bro");
+              add("squid");
+            }});
+    when(curatorFramework.getChildren()).thenReturn(getChildrenBuilder);
+
+    final SensorParserConfig broSensorParserConfig = getTestBroSensorParserConfig();
+    final SensorParserConfig squidSensorParserConfig = getTestSquidSensorParserConfig();
+    GetDataBuilder getDataBuilder = mock(GetDataBuilder.class);
+    when(getDataBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenReturn(broJson.getBytes());
+    when(getDataBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/squid")).thenReturn(squidJson.getBytes());
+    when(curatorFramework.getData()).thenReturn(getDataBuilder);
+
+    assertEquals(new ArrayList() {{
+      add(getTestBroSensorParserConfig());
+      add(getTestSquidSensorParserConfig());
+    }}, sensorParserConfigService.getAll());
+  }
+
+  @Test
+  public void saveShouldWrapExceptionInRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    SetDataBuilder setDataBuilder = mock(SetDataBuilder.class);
+    when(setDataBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro", broJson.getBytes())).thenThrow(Exception.class);
+
+    when(curatorFramework.setData()).thenReturn(setDataBuilder);
+
+    final SensorParserConfig sensorParserConfig = new SensorParserConfig();
+    sensorParserConfig.setSensorTopic("bro");
+    sensorParserConfigService.save(sensorParserConfig);
+  }
+
+  @Test
+  public void saveShouldReturnSameConfigThatIsPassedOnSuccessfulSave() throws Exception {
+    final SensorParserConfig sensorParserConfig = getTestBroSensorParserConfig();
+
+    when(objectMapper.writeValueAsString(sensorParserConfig)).thenReturn(broJson);
+
+    SetDataBuilder setDataBuilder = mock(SetDataBuilder.class);
+    when(setDataBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro", broJson.getBytes())).thenReturn(new Stat());
+    when(curatorFramework.setData()).thenReturn(setDataBuilder);
+
+    assertEquals(getTestBroSensorParserConfig(), sensorParserConfigService.save(sensorParserConfig));
+    verify(setDataBuilder).forPath(eq(ConfigurationType.PARSER.getZookeeperRoot() + "/bro"), eq(broJson.getBytes()));
+  }
+
+  @Test
+  public void reloadAvailableParsersShouldReturnParserClasses() throws Exception {
+    Map<String, String> availableParsers = sensorParserConfigService.reloadAvailableParsers();
+    assertTrue(availableParsers.size() > 0);
+    assertEquals("org.apache.metron.parsers.GrokParser", availableParsers.get("Grok"));
+    assertEquals("org.apache.metron.parsers.bro.BasicBroParser", availableParsers.get("Bro"));
+  }
+
+  @Test
+  public void parseMessageShouldProperlyReturnParsedResults() throws Exception {
+    final SensorParserConfig sensorParserConfig = getTestSquidSensorParserConfig();
+    String grokStatement = "SQUID_DELIMITED %{NUMBER:timestamp}[^0-9]*%{INT:elapsed} %{IP:ip_src_addr} %{WORD:action}/%{NUMBER:code} %{NUMBER:bytes} %{WORD:method} %{NOTSPACE:url}[^0-9]*(%{IP:ip_dst_addr})?";
+    String sampleData = "1461576382.642    161 127.0.0.1 TCP_MISS/200 103701 GET http://www.cnn.com/ - DIRECT/199.27.79.73 text/html";
+    ParseMessageRequest parseMessageRequest = new ParseMessageRequest();
+    parseMessageRequest.setSensorParserConfig(sensorParserConfig);
+    parseMessageRequest.setGrokStatement(grokStatement);
+    parseMessageRequest.setSampleData(sampleData);
+
+    File patternFile = new File("./target/squidTest");
+    FileWriter writer = new FileWriter(patternFile);
+    writer.write(grokStatement);
+    writer.close();
+
+    when(grokService.saveTemporary(grokStatement, "squid")).thenReturn(patternFile);
+
+    assertEquals(new HashMap() {{
+      put("elapsed", 161);
+      put("code", 200);
+      put("ip_dst_addr", "199.27.79.73");
+      put("ip_src_addr", "127.0.0.1");
+      put("action", "TCP_MISS");
+      put("bytes", 103701);
+      put("method", "GET");
+      put("url", "http://www.cnn.com/");
+      put("timestamp", 1461576382642L);
+      put("original_string", "1461576382.642    161 127.0.0.1 TCP_MISS/200 103701 GET http://www.cnn.com/ - DIRECT/199.27.79.73 text/html");
+    }}, sensorParserConfigService.parseMessage(parseMessageRequest));
+
+  }
+
+  @Test
+  public void missingSensorParserConfigShouldThrowRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    ParseMessageRequest parseMessageRequest = new ParseMessageRequest();
+    sensorParserConfigService.parseMessage(parseMessageRequest);
+  }
+
+  @Test
+  public void missingParserClassShouldThrowRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    final SensorParserConfig sensorParserConfig = new SensorParserConfig();
+    sensorParserConfig.setSensorTopic("squid");
+    ParseMessageRequest parseMessageRequest = new ParseMessageRequest();
+    parseMessageRequest.setSensorParserConfig(sensorParserConfig);
+    sensorParserConfigService.parseMessage(parseMessageRequest);
+  }
+
+  @Test
+  public void invalidParserClassShouldThrowRestException() throws Exception {
+    exception.expect(RestException.class);
+
+    final SensorParserConfig sensorParserConfig = new SensorParserConfig();
+    sensorParserConfig.setSensorTopic("squid");
+    sensorParserConfig.setParserClassName("bad.class.package.BadClassName");
+    ParseMessageRequest parseMessageRequest = new ParseMessageRequest();
+    parseMessageRequest.setSensorParserConfig(sensorParserConfig);
+    sensorParserConfigService.parseMessage(parseMessageRequest);
+  }
+
+  private SensorParserConfig getTestBroSensorParserConfig() {
+    SensorParserConfig sensorParserConfig = new SensorParserConfig();
+    sensorParserConfig.setSensorTopic("bro");
+    sensorParserConfig.setParserClassName("org.apache.metron.parsers.bro.BasicBroParser");
+    return sensorParserConfig;
+  }
+
+  private SensorParserConfig getTestSquidSensorParserConfig() {
+    SensorParserConfig sensorParserConfig = new SensorParserConfig();
+    sensorParserConfig.setSensorTopic("squid");
+    sensorParserConfig.setParserClassName("org.apache.metron.parsers.GrokParser");
+    sensorParserConfig.setParserConfig(new HashMap() {{
+      put("grokPath", "/patterns/squid");
+      put("patternLabel", "SQUID_DELIMITED");
+      put("timestampField", "timestamp");
+    }});
+    return sensorParserConfig;
+  }
+
+ }

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/e6628499/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StellarServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StellarServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StellarServiceImplTest.java
new file mode 100644
index 0000000..3ddbf56
--- /dev/null
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StellarServiceImplTest.java
@@ -0,0 +1,92 @@
+/**
+ * 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 org.apache.metron.rest.service.impl;
+
+import org.apache.metron.common.configuration.FieldTransformer;
+import org.apache.metron.common.configuration.SensorParserConfig;
+import org.apache.metron.rest.model.SensorParserContext;
+import org.apache.metron.rest.service.StellarService;
+import org.apache.storm.shade.com.google.common.collect.ImmutableList;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class StellarServiceImplTest {
+
+  private StellarService stellarService;
+
+  @Before
+  public void setUp() throws Exception {
+    stellarService = new StellarServiceImpl();
+  }
+
+  @Test
+  public void validateRulesShouldProperlyValidateRules() {
+    List<String> rules = Arrays.asList("TO_LOWER(test)", "BAD_FUNCTION(test)");
+    Map<String, Boolean> results = stellarService.validateRules(rules);
+    assertEquals(2, results.size());
+    assertEquals(true, results.get("TO_LOWER(test)"));
+    assertEquals(false, results.get("BAD_FUNCTION(test)"));
+  }
+
+  @Test
+  public void applyTransformationsShouldProperlyTransformData() {
+    SensorParserConfig sensorParserConfig = new SensorParserConfig();
+    FieldTransformer fieldTransformater = new FieldTransformer();
+    fieldTransformater.setOutput("url_host");
+    fieldTransformater.setTransformation("STELLAR");
+    fieldTransformater.setConfig(new LinkedHashMap<String, Object>() {{
+      put("url_host", "TO_LOWER(URL_TO_HOST(url))");
+    }});
+    sensorParserConfig.setFieldTransformations(ImmutableList.of(fieldTransformater));
+    SensorParserContext sensorParserContext = new SensorParserContext();
+    sensorParserContext.setSensorParserConfig(sensorParserConfig);
+    sensorParserContext.setSampleData(new HashMap<String, Object>() {{
+      put("url", "https://caseystella.com/blog");
+    }});
+    Map<String, Object> results = stellarService.applyTransformations(sensorParserContext);
+    assertEquals(2, results.size());
+    assertEquals("https://caseystella.com/blog", results.get("url"));
+    assertEquals("caseystella.com", results.get("url_host"));
+  }
+
+  @Test
+  public void getTransformationsShouldReturnTransformation() {
+    assertTrue(stellarService.getTransformations().length > 0);
+  }
+
+  @Test
+  public void getStellarFunctionsShouldReturnFunctions() {
+    assertTrue(stellarService.getStellarFunctions().size() > 0);
+  }
+
+  @Test
+  public void getSimpleStellarFunctionsShouldReturnFunctions() {
+    assertEquals(1, stellarService.getSimpleStellarFunctions().stream()
+            .filter(stellarFunctionDescription -> stellarFunctionDescription.getName().equals("TO_LOWER")).count());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/e6628499/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormAdminServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormAdminServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormAdminServiceImplTest.java
new file mode 100644
index 0000000..d83a74c
--- /dev/null
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormAdminServiceImplTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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 org.apache.metron.rest.service.impl;
+
+import org.apache.metron.common.configuration.SensorParserConfig;
+import org.apache.metron.rest.model.TopologyResponse;
+import org.apache.metron.rest.model.TopologyStatusCode;
+import org.apache.metron.rest.service.GlobalConfigService;
+import org.apache.metron.rest.service.SensorParserConfigService;
+import org.apache.metron.rest.service.StormAdminService;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings("ALL")
+public class StormAdminServiceImplTest {
+  @Rule
+  public final ExpectedException exception = ExpectedException.none();
+
+  StormCLIWrapper stormCLIClientWrapper;
+  StormAdminService stormAdminService;
+  GlobalConfigService globalConfigService;
+  SensorParserConfigService sensorParserConfigService;
+
+  @Before
+  public void setUp() throws Exception {
+    stormCLIClientWrapper = mock(StormCLIWrapper.class);
+    globalConfigService = mock(GlobalConfigService.class);
+    sensorParserConfigService = mock(SensorParserConfigService.class);
+    stormAdminService = new StormAdminServiceImpl(stormCLIClientWrapper, globalConfigService, sensorParserConfigService);
+  }
+
+  @Test
+  public void startParserTopologyShouldProperlyReturnSuccessTopologyResponse() throws Exception {
+    when(stormCLIClientWrapper.startParserTopology("bro")).thenReturn(0);
+    when(globalConfigService.get()).thenReturn(new HashMap<String, Object>());
+    when(sensorParserConfigService.findOne("bro")).thenReturn(new SensorParserConfig());
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setSuccessMessage(TopologyStatusCode.STARTED.toString());
+    TopologyResponse actual = stormAdminService.startParserTopology("bro");
+
+    assertEquals(expected, actual);
+    assertEquals(expected.hashCode(), actual.hashCode());
+  }
+
+  @Test
+  public void startParserTopologyShouldReturnGlobalConfigMissingError() throws Exception {
+    when(globalConfigService.get()).thenReturn(null);
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setErrorMessage(TopologyStatusCode.GLOBAL_CONFIG_MISSING.toString());
+
+    assertEquals(expected, stormAdminService.startParserTopology("bro"));
+  }
+
+  @Test
+  public void startParserTopologyShouldReturnSensorParserConfigMissingError() throws Exception {
+    when(globalConfigService.get()).thenReturn(new HashMap<String, Object>());
+    when(sensorParserConfigService.findOne("bro")).thenReturn(null);
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setErrorMessage(TopologyStatusCode.SENSOR_PARSER_CONFIG_MISSING.toString());
+
+    assertEquals(expected, stormAdminService.startParserTopology("bro"));
+  }
+
+  @Test
+  public void stopParserTopologyShouldProperlyReturnErrorTopologyResponse() throws Exception {
+    when(stormCLIClientWrapper.stopParserTopology("bro", false)).thenReturn(1);
+    when(globalConfigService.get()).thenReturn(new HashMap<String, Object>());
+    when(sensorParserConfigService.findOne("bro")).thenReturn(new SensorParserConfig());
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setErrorMessage(TopologyStatusCode.STOP_ERROR.toString());
+
+    assertEquals(expected, stormAdminService.stopParserTopology("bro", false));
+  }
+
+  @Test
+  public void startEnrichmentTopologyShouldProperlyReturnSuccessTopologyResponse() throws Exception {
+    when(stormCLIClientWrapper.startEnrichmentTopology()).thenReturn(0);
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setSuccessMessage(TopologyStatusCode.STARTED.toString());
+
+    assertEquals(expected, stormAdminService.startEnrichmentTopology());
+  }
+
+  @Test
+  public void stopEnrichmentTopologyShouldProperlyReturnSuccessTopologyResponse() throws Exception {
+    when(stormCLIClientWrapper.stopEnrichmentTopology(false)).thenReturn(0);
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setSuccessMessage(TopologyStatusCode.STOPPED.toString());
+
+    assertEquals(expected, stormAdminService.stopEnrichmentTopology(false));
+  }
+
+  @Test
+  public void startIndexingTopologyShouldProperlyReturnSuccessTopologyResponse() throws Exception {
+    when(stormCLIClientWrapper.startIndexingTopology()).thenReturn(0);
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setSuccessMessage(TopologyStatusCode.STARTED.toString());
+
+    assertEquals(expected, stormAdminService.startIndexingTopology());
+  }
+
+  @Test
+  public void stopIndexingTopologyShouldProperlyReturnSuccessTopologyResponse() throws Exception {
+    when(stormCLIClientWrapper.stopIndexingTopology(false)).thenReturn(0);
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setSuccessMessage(TopologyStatusCode.STOPPED.toString());
+
+    assertEquals(expected, stormAdminService.stopIndexingTopology(false));
+  }
+
+  @Test
+  public void getStormClientStatusShouldProperlyReturnStatus() throws Exception {
+    final Map<String, String> status = new HashMap() {{
+      put("status", "statusValue");
+    }};
+    when(stormCLIClientWrapper.getStormClientStatus()).thenReturn(status);
+
+    assertEquals(new HashMap() {{
+      put("status", "statusValue");
+    }}, stormAdminService.getStormClientStatus());
+  }
+
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/e6628499/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormCLIWrapperTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormCLIWrapperTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormCLIWrapperTest.java
new file mode 100644
index 0000000..cb26783
--- /dev/null
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormCLIWrapperTest.java
@@ -0,0 +1,216 @@
+/**
+ * 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 org.apache.metron.rest.service.impl;
+
+import org.apache.metron.rest.MetronRestConstants;
+import org.apache.metron.rest.RestException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.springframework.core.env.Environment;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyVararg;
+import static org.mockito.Mockito.verify;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.verifyNew;
+import static org.powermock.api.mockito.PowerMockito.when;
+import static org.powermock.api.mockito.PowerMockito.whenNew;
+
+@SuppressWarnings("unchecked")
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({DockerStormCLIWrapper.class, ProcessBuilder.class})
+public class StormCLIWrapperTest {
+
+  @Rule
+  public final ExpectedException exception = ExpectedException.none();
+
+  private ProcessBuilder processBuilder;
+  private Environment environment;
+  private Process process;
+  private StormCLIWrapper stormCLIWrapper;
+
+  @Before
+  public void setUp() throws Exception {
+    processBuilder = mock(ProcessBuilder.class);
+    environment = mock(Environment.class);
+    process = mock(Process.class);
+    stormCLIWrapper = new StormCLIWrapper();
+    stormCLIWrapper.setEnvironment(environment);
+  }
+
+  @Test
+  public void startParserTopologyShouldRunCommandProperly() throws Exception {
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+
+    when(processBuilder.start()).thenReturn(process);
+    when(environment.getProperty(MetronRestConstants.PARSER_SCRIPT_PATH_SPRING_PROPERTY)).thenReturn("/start_parser");
+    when(environment.getProperty(MetronRestConstants.KAFKA_BROKER_URL_SPRING_PROPERTY)).thenReturn("kafka_broker_url");
+    when(environment.getProperty(MetronRestConstants.ZK_URL_SPRING_PROPERTY)).thenReturn("zookeeper_url");
+    when(process.exitValue()).thenReturn(0);
+
+    assertEquals(0, stormCLIWrapper.startParserTopology("bro"));
+    verify(process).waitFor();
+    verifyNew(ProcessBuilder.class).withArguments("/start_parser", "-k", "kafka_broker_url", "-z", "zookeeper_url", "-s", "bro");
+  }
+
+  @Test
+  public void stopParserTopologyShouldRunCommandProperly() throws Exception {
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+
+    when(processBuilder.start()).thenReturn(process);
+    when(process.exitValue()).thenReturn(0);
+
+    assertEquals(0, stormCLIWrapper.stopParserTopology("bro", false));
+    verify(process).waitFor();
+    verifyNew(ProcessBuilder.class).withArguments("storm", "kill", "bro");
+  }
+
+  @Test
+  public void stopParserTopologyNowShouldRunCommandProperly() throws Exception {
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+
+    when(processBuilder.start()).thenReturn(process);
+    when(process.exitValue()).thenReturn(0);
+
+    assertEquals(0, stormCLIWrapper.stopParserTopology("bro", true));
+    verify(process).waitFor();
+    verifyNew(ProcessBuilder.class).withArguments("storm", "kill", "bro", "-w", "0");
+  }
+
+  @Test
+  public void startEnrichmentTopologyShouldRunCommandProperly() throws Exception {
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+
+    when(processBuilder.start()).thenReturn(process);
+    when(environment.getProperty(MetronRestConstants.ENRICHMENT_SCRIPT_PATH_SPRING_PROPERTY)).thenReturn("/start_enrichment");
+    when(process.exitValue()).thenReturn(0);
+
+    assertEquals(0, stormCLIWrapper.startEnrichmentTopology());
+    verify(process).waitFor();
+    verifyNew(ProcessBuilder.class).withArguments("/start_enrichment");
+
+  }
+
+  @Test
+  public void stopEnrichmentTopologyShouldRunCommandProperly() throws Exception {
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+
+    when(processBuilder.start()).thenReturn(process);
+    when(process.exitValue()).thenReturn(0);
+
+    assertEquals(0, stormCLIWrapper.stopEnrichmentTopology(false));
+    verify(process).waitFor();
+    verifyNew(ProcessBuilder.class).withArguments("storm", "kill", MetronRestConstants.ENRICHMENT_TOPOLOGY_NAME);
+  }
+
+  @Test
+  public void startIndexingTopologyShouldRunCommandProperly() throws Exception {
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+
+    when(processBuilder.start()).thenReturn(process);
+    when(environment.getProperty(MetronRestConstants.INDEXING_SCRIPT_PATH_SPRING_PROPERTY)).thenReturn("/start_indexing");
+    when(process.exitValue()).thenReturn(0);
+
+    assertEquals(0, stormCLIWrapper.startIndexingTopology());
+    verify(process).waitFor();
+    verifyNew(ProcessBuilder.class).withArguments("/start_indexing");
+
+  }
+
+  @Test
+  public void stopIndexingTopologyShouldRunCommandProperly() throws Exception {
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+
+    when(processBuilder.start()).thenReturn(process);
+    when(process.exitValue()).thenReturn(0);
+
+    assertEquals(0, stormCLIWrapper.stopIndexingTopology(false));
+    verify(process).waitFor();
+    verifyNew(ProcessBuilder.class).withArguments("storm", "kill", MetronRestConstants.INDEXING_TOPOLOGY_NAME);
+  }
+
+  @Test
+  public void getStormClientStatusShouldReturnCorrectStatus() throws Exception {
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+
+    Process process = mock(Process.class);
+    InputStream inputStream = new ByteArrayInputStream("\nStorm 1.1".getBytes(UTF_8));
+
+    when(processBuilder.start()).thenReturn(process);
+
+    when(process.getInputStream()).thenReturn(inputStream);
+    when(environment.getProperty(MetronRestConstants.PARSER_SCRIPT_PATH_SPRING_PROPERTY)).thenReturn("/start_parser");
+    when(environment.getProperty(MetronRestConstants.ENRICHMENT_SCRIPT_PATH_SPRING_PROPERTY)).thenReturn("/start_enrichment");
+    when(environment.getProperty(MetronRestConstants.INDEXING_SCRIPT_PATH_SPRING_PROPERTY)).thenReturn("/start_indexing");
+
+
+    Map<String, String> actual = stormCLIWrapper.getStormClientStatus();
+    assertEquals(new HashMap<String, String>() {{
+      put("parserScriptPath", "/start_parser");
+      put("enrichmentScriptPath", "/start_enrichment");
+      put("indexingScriptPath", "/start_indexing");
+      put("stormClientVersionInstalled", "1.1");
+
+    }}, actual);
+    verifyNew(ProcessBuilder.class).withArguments("storm", "version");
+  }
+
+  @Test
+  public void stormClientVersionInstalledShouldReturnDefault() throws Exception {
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+
+    Process process = mock(Process.class);
+    InputStream inputStream = new ByteArrayInputStream("".getBytes(UTF_8));
+
+    when(processBuilder.start()).thenReturn(process);
+    when(process.getInputStream()).thenReturn(inputStream);
+    assertEquals("Storm client is not installed", stormCLIWrapper.stormClientVersionInstalled());
+  }
+
+  @Test
+  public void runCommandShouldReturnRestExceptionOnError() throws Exception {
+    exception.expect(RestException.class);
+
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+    when(processBuilder.start()).thenThrow(new IOException());
+
+    stormCLIWrapper.runCommand(new String[]{"storm", "kill"});
+  }
+
+  @Test
+  public void stormClientVersionInstalledShouldReturnRestExceptionOnError() throws Exception {
+    exception.expect(RestException.class);
+
+    whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg()).thenReturn(processBuilder);
+    when(processBuilder.start()).thenThrow(new IOException());
+
+    stormCLIWrapper.stormClientVersionInstalled();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/e6628499/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormStatusServiceImplTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormStatusServiceImplTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormStatusServiceImplTest.java
new file mode 100644
index 0000000..db2cb7f
--- /dev/null
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/service/impl/StormStatusServiceImplTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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 org.apache.metron.rest.service.impl;
+
+import org.apache.metron.rest.model.TopologyResponse;
+import org.apache.metron.rest.model.TopologyStatus;
+import org.apache.metron.rest.model.TopologyStatusCode;
+import org.apache.metron.rest.model.TopologySummary;
+import org.apache.metron.rest.service.StormStatusService;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.springframework.core.env.Environment;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.metron.rest.MetronRestConstants.STORM_UI_SPRING_PROPERTY;
+import static org.apache.metron.rest.MetronRestConstants.TOPOLOGY_SUMMARY_URL;
+import static org.apache.metron.rest.MetronRestConstants.TOPOLOGY_URL;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings("ALL")
+public class StormStatusServiceImplTest {
+  @Rule
+  public final ExpectedException exception = ExpectedException.none();
+
+  Environment environment;
+  RestTemplate restTemplate;
+  StormStatusService stormStatusService;
+
+  @Before
+  public void setUp() throws Exception {
+    environment = mock(Environment.class);
+    restTemplate = mock(RestTemplate.class);
+    stormStatusService = new StormStatusServiceImpl(environment, restTemplate);
+  }
+
+  @Test
+  public void getTopologySummaryShouldReturnTopologySummary() throws Exception {
+    final TopologyStatus topologyStatus = new TopologyStatus();
+    topologyStatus.setStatus(TopologyStatusCode.STARTED);
+    topologyStatus.setName("bro");
+    topologyStatus.setId("bro_id");
+    final TopologySummary topologySummary = new TopologySummary();
+    topologySummary.setTopologies(new TopologyStatus[]{topologyStatus});
+
+    when(environment.getProperty(STORM_UI_SPRING_PROPERTY)).thenReturn("storm_ui");
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_SUMMARY_URL, TopologySummary.class)).thenReturn(topologySummary);
+
+    TopologyStatus expectedStatus = new TopologyStatus();
+    expectedStatus.setStatus(TopologyStatusCode.STARTED);
+    expectedStatus.setName("bro");
+    expectedStatus.setId("bro_id");
+    TopologySummary expected = new TopologySummary();
+    expected.setTopologies(new TopologyStatus[]{expectedStatus});
+
+    TopologySummary actual = stormStatusService.getTopologySummary();
+    assertEquals(expected, actual);
+    assertEquals(expected.hashCode(), actual.hashCode());
+  }
+
+  @Test
+  public void getTopologyStatusShouldReturnTopologyStatus() throws Exception {
+    final TopologyStatus topologyStatus = new TopologyStatus();
+    topologyStatus.setStatus(TopologyStatusCode.STARTED);
+    topologyStatus.setName("bro");
+    topologyStatus.setId("bro_id");
+    final TopologySummary topologySummary = new TopologySummary();
+    topologySummary.setTopologies(new TopologyStatus[]{topologyStatus});
+
+    when(environment.getProperty(STORM_UI_SPRING_PROPERTY)).thenReturn("storm_ui");
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_SUMMARY_URL, TopologySummary.class)).thenReturn(topologySummary);
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_URL + "/bro_id", TopologyStatus.class)).thenReturn(topologyStatus);
+
+    TopologyStatus expected = new TopologyStatus();
+    expected.setStatus(TopologyStatusCode.STARTED);
+    expected.setName("bro");
+    expected.setId("bro_id");
+
+    TopologyStatus actual = stormStatusService.getTopologyStatus("bro");
+    assertEquals(expected, actual);
+    assertEquals(expected.hashCode(), actual.hashCode());
+  }
+
+  @Test
+  public void getAllTopologyStatusShouldReturnAllTopologyStatus() {
+    final TopologyStatus topologyStatus = new TopologyStatus();
+    topologyStatus.setStatus(TopologyStatusCode.STARTED);
+    topologyStatus.setName("bro");
+    topologyStatus.setId("bro_id");
+    final TopologySummary topologySummary = new TopologySummary();
+    topologySummary.setTopologies(new TopologyStatus[]{topologyStatus});
+
+    when(environment.getProperty(STORM_UI_SPRING_PROPERTY)).thenReturn("storm_ui");
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_SUMMARY_URL, TopologySummary.class)).thenReturn(topologySummary);
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_URL + "/bro_id", TopologyStatus.class)).thenReturn(topologyStatus);
+
+    TopologyStatus expected = new TopologyStatus();
+    expected.setStatus(TopologyStatusCode.STARTED);
+    expected.setName("bro");
+    expected.setId("bro_id");
+
+    assertEquals(new ArrayList() {{ add(expected); }}, stormStatusService.getAllTopologyStatus());
+  }
+
+
+  @Test
+  public void activateTopologyShouldReturnActiveTopologyResponse() {
+    final TopologyStatus topologyStatus = new TopologyStatus();
+    topologyStatus.setName("bro");
+    topologyStatus.setId("bro_id");
+    final TopologySummary topologySummary = new TopologySummary();
+    topologySummary.setTopologies(new TopologyStatus[]{topologyStatus});
+
+    when(environment.getProperty(STORM_UI_SPRING_PROPERTY)).thenReturn("storm_ui");
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_SUMMARY_URL, TopologySummary.class)).thenReturn(topologySummary);
+    when(restTemplate.postForObject("http://storm_ui" + TOPOLOGY_URL + "/bro_id/activate", null, Map.class))
+            .thenReturn(new HashMap() {{ put("status", "success"); }});
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setSuccessMessage(TopologyStatusCode.ACTIVE.toString());
+    assertEquals(expected, stormStatusService.activateTopology("bro"));
+  }
+
+  @Test
+  public void activateTopologyShouldReturnErrorTopologyResponse() {
+    final TopologyStatus topologyStatus = new TopologyStatus();
+    topologyStatus.setName("bro");
+    topologyStatus.setId("bro_id");
+    final TopologySummary topologySummary = new TopologySummary();
+    topologySummary.setTopologies(new TopologyStatus[]{topologyStatus});
+
+    when(environment.getProperty(STORM_UI_SPRING_PROPERTY)).thenReturn("storm_ui");
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_SUMMARY_URL, TopologySummary.class)).thenReturn(topologySummary);
+    when(restTemplate.postForObject("http://storm_ui" + TOPOLOGY_URL + "/bro_id/activate", null, Map.class))
+            .thenReturn(new HashMap() {{ put("status", "error message"); }});
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setErrorMessage("error message");
+    assertEquals(expected, stormStatusService.activateTopology("bro"));
+  }
+
+  @Test
+  public void activateTopologyShouldReturnTopologyNotFoundTopologyResponse() {
+    when(environment.getProperty(STORM_UI_SPRING_PROPERTY)).thenReturn("storm_ui");
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_SUMMARY_URL, TopologySummary.class)).thenReturn(new TopologySummary());
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setErrorMessage(TopologyStatusCode.TOPOLOGY_NOT_FOUND.toString());
+    assertEquals(expected, stormStatusService.activateTopology("bro"));
+  }
+
+  @Test
+  public void deactivateTopologyShouldReturnActiveTopologyResponse() {
+    final TopologyStatus topologyStatus = new TopologyStatus();
+    topologyStatus.setName("bro");
+    topologyStatus.setId("bro_id");
+    final TopologySummary topologySummary = new TopologySummary();
+    topologySummary.setTopologies(new TopologyStatus[]{topologyStatus});
+
+    when(environment.getProperty(STORM_UI_SPRING_PROPERTY)).thenReturn("storm_ui");
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_SUMMARY_URL, TopologySummary.class)).thenReturn(topologySummary);
+    when(restTemplate.postForObject("http://storm_ui" + TOPOLOGY_URL + "/bro_id/deactivate", null, Map.class))
+            .thenReturn(new HashMap() {{ put("status", "success"); }});
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setSuccessMessage(TopologyStatusCode.INACTIVE.toString());
+    assertEquals(expected, stormStatusService.deactivateTopology("bro"));
+  }
+
+  @Test
+  public void deactivateTopologyShouldReturnErrorTopologyResponse() {
+    final TopologyStatus topologyStatus = new TopologyStatus();
+    topologyStatus.setName("bro");
+    topologyStatus.setId("bro_id");
+    final TopologySummary topologySummary = new TopologySummary();
+    topologySummary.setTopologies(new TopologyStatus[]{topologyStatus});
+
+    when(environment.getProperty(STORM_UI_SPRING_PROPERTY)).thenReturn("storm_ui");
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_SUMMARY_URL, TopologySummary.class)).thenReturn(topologySummary);
+    when(restTemplate.postForObject("http://storm_ui" + TOPOLOGY_URL + "/bro_id/deactivate", null, Map.class))
+            .thenReturn(new HashMap() {{ put("status", "error message"); }});
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setErrorMessage("error message");
+    assertEquals(expected, stormStatusService.deactivateTopology("bro"));
+  }
+
+  @Test
+  public void deactivateTopologyShouldReturnTopologyNotFoundTopologyResponse() {
+    when(environment.getProperty(STORM_UI_SPRING_PROPERTY)).thenReturn("storm_ui");
+    when(restTemplate.getForObject("http://storm_ui" + TOPOLOGY_SUMMARY_URL, TopologySummary.class)).thenReturn(new TopologySummary());
+
+    TopologyResponse expected = new TopologyResponse();
+    expected.setErrorMessage(TopologyStatusCode.TOPOLOGY_NOT_FOUND.toString());
+    assertEquals(expected, stormStatusService.deactivateTopology("bro"));
+  }
+
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/e6628499/metron-interface/metron-rest/src/test/resources/README.vm
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/resources/README.vm b/metron-interface/metron-rest/src/test/resources/README.vm
index f259a91..99f4286 100644
--- a/metron-interface/metron-rest/src/test/resources/README.vm
+++ b/metron-interface/metron-rest/src/test/resources/README.vm
@@ -1,66 +1,83 @@
-#[[#]]# Metron REST and Configuration UI
+#[[#]]# Metron REST
 
-This UI exposes and aids in sensor configuration.
+This module provides a RESTful API for interacting with Metron.
 
 #[[##]]# Prerequisites
 
 * A running Metron cluster
-* A running instance of MySQL
 * Java 8 installed
 * Storm CLI and Metron topology scripts (start_parser_topology.sh, start_enrichment_topology.sh, start_elasticsearch_topology.sh) installed
 
 #[[##]]# Installation
-1. Package the Application with Maven:
-    ```
-    mvn clean package
-    ```
+1. Package the application with Maven:
+```
+mvn clean package
+```
 
 1. Untar the archive in the target directory.  The directory structure will look like:
-    ```
-    bin
-      start.sh
-    lib
-      metron-rest-version.jar
-    ```
+```
+bin
+  start_metron_rest.sh
+lib
+  metron-rest-$METRON_VERSION.jar
+```
 
-1. Install Hibernate by downloading version 5.0.11.Final from (http://hibernate.org/orm/downloads/).  Unpack the archive and set the HIBERNATE_HOME environment variable to the absolute path of the top level directory.
-    ```
-    export HIBERNATE_HOME=/path/to/hibernate-release-5.0.11.Final
-    ```
+1. Create an `application.yml` file with the contents of [application-docker.yml](src/main/resources/application-docker.yml).  Substitute the appropriate Metron service urls (Kafka, Zookeeper, Storm, etc) in properties containing `${docker.host.address}` and update the `spring.datasource.*` properties as needed (see the [Security](#security) section for more details).
 
-1. Install the MySQL client by downloading version 5.1.40 from (https://dev.mysql.com/downloads/connector/j/).  Unpack the archive and set the MYSQL_CLIENT_HOME environment variable to the absolute path of the top level directory.
-    ```
-    export MYSQL_CLIENT_HOME=/path/to/mysql-connector-java-5.1.40
-    ```
+1. Start the application with this command:
+```
+./bin/start_metron_rest.sh /path/to/application.yml
+```
 
-1. Create a MySQL user for the Config UI (http://dev.mysql.com/doc/refman/5.7/en/adding-users.html).
+#[[##]]# Usage
 
-1. Create a Config UI database in MySQL with this command:
-    ```
-    CREATE DATABASE IF NOT EXISTS metronrest
-    ```
+The exposed REST endpoints can be accessed with the Swagger UI at http://host:port/swagger-ui.html#/.  The default port is 8080 but can be changed in application.yml by setting "server.port" to the desired port.
 
-1. Create an `application.yml` file with the contents of [application-docker.yml](src/main/resources/application-docker.yml).  Substitute the appropriate Metron service urls (Kafka, Zookeeper, Storm, etc) in properties containing `${docker.host.address}` and update the `spring.datasource.username` and `spring.datasource.password` properties using the MySQL credentials from step 4.
+#[[##]]# Security
 
-1. Start the UI with this command:
-    ```
-    ./bin/start.sh /path/to/application.yml
-    ```
+The metron-rest module uses [Spring Security](http://projects.spring.io/spring-security/) for authentication and stores user credentials in a relational database.  The H2 database is configured by default and is intended only for development purposes.  The "dev" profile can be used to automatically load test users:
+```
+./bin/start_metron_rest.sh /path/to/application.yml --spring.profiles.active=dev
+```
 
-#[[##]]# Usage
+For [production use](http://docs.spring.io/spring-boot/docs/1.4.1.RELEASE/reference/htmlsingle/#boot-features-connect-to-production-database), a relational database should be configured.  For example, configuring MySQL would be done as follows:
+
+1. Create a MySQL user for the Metron REST application (http://dev.mysql.com/doc/refman/5.7/en/adding-users.html).
+
+1. Connect to MySQL and create a Metron REST database:
+```
+CREATE DATABASE IF NOT EXISTS metronrest
+```
 
-The exposed REST endpoints can be accessed with the Swagger UI at http://host:port/swagger-ui.html#/.  The default port is 8080 but can be changed in application.yml by setting "server.port" to the desired port.  Users can be added with this SQL statement:
+1. Add users:
 ```
 use metronrest;
 insert into users (username, password, enabled) values ('your_username','your_password',1);
 insert into authorities (username, authority) values ('your_username', 'ROLE_USER');
 ```
-Users can be added to additional groups with this SQL statement:
+
+1. Replace the H2 connection information in the application.yml file with MySQL connection information:
+```
+spring:
+  datasource:
+        driverClassName: com.mysql.jdbc.Driver
+        url: jdbc:mysql://mysql_host:3306/metronrest
+        username: metron_rest_user
+        password: metron_rest_password
+        platform: mysql
 ```
-use metronrest;
-insert into authorities (username, authority) values ('your_username', 'your_group');
+
+1. Add a dependency for the MySQL JDBC connector in the metron-rest pom.xml:
+```
+<dependency>
+  <groupId>mysql</groupId>
+  <artifactId>mysql-connector-java</artifactId>
+  <version>${mysql.client.version}</version>
+</dependency>
 ```
 
+1. Follow the steps in the [Installation](#installation) section
+
 #[[##]]# API
 
 Request and Response objects are JSON formatted.  The JSON schemas are available in the Swagger UI.
@@ -86,6 +103,38 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
 #end
 
 #end
+#[[##]]# Testing
+
+Profiles are includes for both the metron-docker and Quick Dev environments.
+
+#[[###]]# metron-docker
+
+Start the [metron-docker](../../metron-docker) environment.  Build the metron-rest module and start it with the Spring Boot Maven plugin:
+```
+mvn clean package
+mvn spring-boot:run -Drun.profiles=docker,dev
+```
+The metron-rest application will be available at http://localhost:8080/swagger-ui.html#/.
+
+#[[###]]# Quick Dev
+
+Start the [Quick Dev](../../metron-deployment/vagrant/quick-dev-platform) environment.  Build the metron-rest module and start it with the Spring Boot Maven plugin:
+```
+mvn clean package
+mvn spring-boot:run -Drun.profiles=vagrant,dev
+```
+The metron-rest application will be available at http://localhost:8080/swagger-ui.html#/.
+
+To run the application locally on the Quick Dev host, package the application and scp the archive to node1:
+```
+mvn clean package
+scp ./target/metron-rest-$METRON_VERSION-archive.tar.gz root@node1:~/
+```
+Login to node1 and unarchive the metron-rest application.  Start the application on a different port to avoid conflicting with Ambari:
+```
+java -jar ./lib/metron-rest-$METRON_VERSION.jar --spring.profiles.active=vagrant,dev --server.port=8082
+```
+The metron-rest application will be available at http://node1:8082/swagger-ui.html#/.
 
 #[[##]]# License
 

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/e6628499/metron-interface/pom.xml
----------------------------------------------------------------------
diff --git a/metron-interface/pom.xml b/metron-interface/pom.xml
index 078da19..805c024 100644
--- a/metron-interface/pom.xml
+++ b/metron-interface/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.apache.metron</groupId>
         <artifactId>Metron</artifactId>
-        <version>0.3.0</version>
+        <version>0.3.1</version>
     </parent>
     <description>Interfaces for Metron</description>
     <url>https://metron.incubator.apache.org/</url>