add unit tests of basic server config
diff --git a/pom.xml b/pom.xml
index 0f066bc..720cf15 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,13 @@
             <version>9.4.31.v20200723</version>
         </dependency>
 
+        <!-- Annotation -->
+        <dependency>
+            <groupId>org.checkerframework</groupId>
+            <artifactId>checker-qual</artifactId>
+            <version>3.10.0</version>
+        </dependency>
+
         <!--Testing-->
         <dependency>
             <groupId>org.testng</groupId>
diff --git a/src/main/java/org/apache/datasketches/server/SketchConstants.java b/src/main/java/org/apache/datasketches/server/SketchConstants.java
index 6ef32c8..742dfc9 100644
--- a/src/main/java/org/apache/datasketches/server/SketchConstants.java
+++ b/src/main/java/org/apache/datasketches/server/SketchConstants.java
@@ -76,6 +76,7 @@
   public static final String RESPONSE_RESULT_MASS = "mass";
   public static final String RESPONSE_QUANTILE_LIST = "estimatedQuantiles";
   public static final String RESPONSE_RESULT_QUANTILE = "quantile";
+  public static final String RESPONSE_SKETCH_COUNT_FIELD = "count";
 
   // JSON Config Field Names
   public static final String CONFIG_PORT_FIELD = "port";
diff --git a/src/main/java/org/apache/datasketches/server/SketchServer.java b/src/main/java/org/apache/datasketches/server/SketchServer.java
index 4fefb9a..b70dca6 100644
--- a/src/main/java/org/apache/datasketches/server/SketchServer.java
+++ b/src/main/java/org/apache/datasketches/server/SketchServer.java
@@ -21,6 +21,7 @@
 
 import java.io.IOException;
 
+import org.checkerframework.checker.nullness.qual.NonNull;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.handler.ContextHandler;
@@ -43,7 +44,7 @@
    * @param configFile Path to a configuration file following the <tt>SketchServerConfig</tt> format
    * @throws IOException on parse errors
    */
-  public SketchServer(final String configFile) throws IOException {
+  public SketchServer(@NonNull final String configFile) throws IOException {
     config = new SketchServerConfig(configFile);
   }
 
diff --git a/src/main/java/org/apache/datasketches/server/SketchServerConfig.java b/src/main/java/org/apache/datasketches/server/SketchServerConfig.java
index 31a2daf..447e36e 100644
--- a/src/main/java/org/apache/datasketches/server/SketchServerConfig.java
+++ b/src/main/java/org/apache/datasketches/server/SketchServerConfig.java
@@ -24,6 +24,7 @@
 import java.io.IOException;
 import java.io.Reader;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -36,6 +37,8 @@
 
 import static org.apache.datasketches.server.SketchConstants.*;
 
+import org.checkerframework.checker.nullness.qual.NonNull;
+
 /**
  * A class to hold the server configuration, along with a supporting subclass and file-parsing methods.
  *
@@ -54,25 +57,16 @@
       this.family = family;
       this.type = type;
     }
-
-    SketchInfo(final String name, final int k, final String family) {
-      this(name, k, family, null);
-    }
-
   }
 
   private int port = DEFAULT_PORT;
   private ArrayList<SketchInfo> sketchList;
 
-  SketchServerConfig(final String configFile) throws IOException {
+  SketchServerConfig(@NonNull final String configFile) throws IOException {
     final JsonElement config = readJsonFromFile(configFile);
     parseConfig(config);
   }
 
-  SketchServerConfig(final JsonElement config) throws IOException {
-    parseConfig(config);
-  }
-
   int getPort() {
     return port;
   }
@@ -83,16 +77,10 @@
 
   // output should have a list with full info per sketch, even if input allows a
   // more condensed format
-  private static JsonElement readJsonFromFile(final String configFile) {
-    JsonElement config = null;
-
-    try (final Reader reader = Files.newBufferedReader(Paths.get(configFile))) {
-      config = JsonParser.parseReader(reader);
-    } catch (final IOException e) {
-      e.printStackTrace();
-    }
-
-    return config;
+  private static JsonElement readJsonFromFile(final String configFile) throws IOException {
+    final Path configPath = Paths.get(configFile);
+    final Reader reader = Files.newBufferedReader(configPath);
+    return JsonParser.parseReader(reader);
   }
 
   private void parseConfig(final JsonElement config) throws IOException {
diff --git a/src/main/java/org/apache/datasketches/server/SketchStorage.java b/src/main/java/org/apache/datasketches/server/SketchStorage.java
index c6c8c89..b6af0c5 100644
--- a/src/main/java/org/apache/datasketches/server/SketchStorage.java
+++ b/src/main/java/org/apache/datasketches/server/SketchStorage.java
@@ -31,6 +31,8 @@
 import org.apache.datasketches.sampling.ReservoirItemsSketch;
 import org.apache.datasketches.sampling.VarOptItemsSketch;
 import org.apache.datasketches.theta.SetOperationBuilder;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
@@ -43,7 +45,8 @@
  * order to ensure that data is presented in a consistent way.
  */
 public class SketchStorage {
-  private static final String SKETCH_COUNT_NAME = "count";
+  // the set of SketchEntries held by this object
+  HashMap<String, SketchEntry> sketchMap;
 
   /**
    * Returns true if the sketch family is for distinct counting.
@@ -81,12 +84,8 @@
     }
   }
 
-  HashMap<String, SketchEntry> sketchMap;
-
-  SketchStorage(final List<SketchServerConfig.SketchInfo> sketchList) {
-    if (sketchList != null) {
-      createSketches(sketchList);
-    }
+  SketchStorage(@NonNull final List<SketchServerConfig.SketchInfo> sketchList) {
+    createSketches(sketchList);
   }
 
   JsonObject listSketches() {
@@ -125,7 +124,7 @@
       sketchList.add(item);
     }
 
-    summary.addProperty(SKETCH_COUNT_NAME, sketchMap.size());
+    summary.addProperty(RESPONSE_SKETCH_COUNT_FIELD, sketchMap.size());
     summary.add(SketchConstants.CONFIG_SKETCHES_PREFIX, sketchList); // bare prefix, sketches fully qualified
 
     return summary;
diff --git a/src/test/java/org/apache/datasketches/server/SketchServerConfigTest.java b/src/test/java/org/apache/datasketches/server/SketchServerConfigTest.java
new file mode 100644
index 0000000..23dac17
--- /dev/null
+++ b/src/test/java/org/apache/datasketches/server/SketchServerConfigTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.datasketches.server;
+
+import static org.apache.datasketches.server.SketchConstants.DEFAULT_PORT;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import org.testng.annotations.Test;
+
+public class SketchServerConfigTest {
+
+  @Test
+  public void invalidFile() {
+    try {
+      new SketchServerConfig("fileDoesNotExist");
+      fail();
+    } catch (final IOException e) {
+      // expected
+    }
+  }
+
+  @Test
+  public void parseTestConfig() {
+    final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+    try {
+      final SketchServerConfig serverConf =
+          new SketchServerConfig(Objects.requireNonNull(classLoader.getResource("test_config.json")).getFile());
+      assertEquals(serverConf.getSketchList().size(), 15);
+      assertEquals(serverConf.getPort(), 8080);
+    } catch (final IOException e) {
+      fail();
+    }
+  }
+
+  @Test
+  public void parseJsonArrayConfig() {
+    final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+    try {
+      final SketchServerConfig serverConf =
+          new SketchServerConfig(Objects.requireNonNull(classLoader.getResource("array_config.json")).getFile());
+      assertEquals(serverConf.getSketchList().size(), 2);
+      assertEquals(serverConf.getPort(), DEFAULT_PORT);
+    } catch (final IOException e) {
+      fail();
+    }
+  }
+}
diff --git a/src/test/java/org/apache/datasketches/server/SketchServerTest.java b/src/test/java/org/apache/datasketches/server/SketchServerTest.java
new file mode 100644
index 0000000..757c855
--- /dev/null
+++ b/src/test/java/org/apache/datasketches/server/SketchServerTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.datasketches.server;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import org.testng.annotations.Test;
+
+public class SketchServerTest {
+  @Test
+  public void createServer() {
+    final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+    SketchServer server = null;
+    try {
+      server = new SketchServer(Objects.requireNonNull(classLoader.getResource("test_config.json")).getFile());
+    } catch (final IOException e) {
+      fail();
+    }
+
+    // check that port and URI are invalid before starting the server
+    assertNotNull(server);
+    assertNull(server.getURI());
+    assertEquals(server.getPort(), -1);
+    try {
+      server.start();
+    } catch (final Exception e) {
+      fail();
+    }
+
+    assertEquals(server.getPort(), 8080);
+    // initial testing suggests it's just using the host's IP address so just checking that the port
+    // is working correctly
+    assertTrue(server.getURI().endsWith(":" + server.getPort() + "/"));
+  }
+}
diff --git a/src/test/java/org/apache/datasketches/server/SketchStorageTest.java b/src/test/java/org/apache/datasketches/server/SketchStorageTest.java
new file mode 100644
index 0000000..39076b2
--- /dev/null
+++ b/src/test/java/org/apache/datasketches/server/SketchStorageTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.datasketches.server;
+
+import static org.apache.datasketches.server.SketchConstants.RESPONSE_SKETCH_COUNT_FIELD;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import org.apache.datasketches.Family;
+import org.apache.datasketches.cpc.CpcSketch;
+import org.apache.datasketches.hll.HllSketch;
+import org.testng.annotations.Test;
+
+
+import com.google.gson.JsonObject;
+
+public class SketchStorageTest {
+
+  @Test
+  public void invalidSketchEntry() {
+    try {
+      new SketchStorage.SketchEntry(Family.CPC, null, new CpcSketch(12), 12);
+      fail();
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    try {
+      new SketchStorage.SketchEntry(Family.HLL, new HllSketch(10), 10);
+      fail();
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+  }
+
+  @Test
+  public void loadSketches() {
+    final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+    SketchServerConfig serverConfig = null;
+    try {
+      serverConfig = new SketchServerConfig(Objects.requireNonNull(classLoader.getResource("test_config.json")).getFile());
+    } catch (final IOException e) {
+      fail();
+    }
+    assertNotNull(serverConfig);
+
+    final SketchStorage storage = new SketchStorage(serverConfig.getSketchList());
+    final JsonObject sketches = storage.listSketches();
+    assertTrue(sketches.has(RESPONSE_SKETCH_COUNT_FIELD));
+    assertEquals(sketches.get(RESPONSE_SKETCH_COUNT_FIELD).getAsInt(), 15);
+    assertTrue(storage.contains("cpcOfNumbers"));
+  }
+}
diff --git a/src/test/resources/array_config.json b/src/test/resources/array_config.json
new file mode 100644
index 0000000..7c718ee
--- /dev/null
+++ b/src/test/resources/array_config.json
@@ -0,0 +1,12 @@
+[
+  { "name": "cpcOfNumbers",
+    "k": 12,
+    "type": "long",
+    "family": "cpc"
+  },
+  { "name": "cpcOfStrings",
+    "k": 14,
+    "type": "string",
+    "family": "cpc"
+  }
+]
\ No newline at end of file
diff --git a/src/test/resources/test_config.json b/src/test/resources/test_config.json
new file mode 100644
index 0000000..172086e
--- /dev/null
+++ b/src/test/resources/test_config.json
@@ -0,0 +1,56 @@
+{
+    "port": 8080,
+    "sketches_A": [
+	{ "name": "cpcOfNumbers",
+	  "k": 12,
+	  "type": "long",
+	  "family": "cpc"
+	},
+	{ "name": "cpcOfStrings",
+	  "k": 14,
+	  "type": "string",
+	  "family": "cpc"
+	}	
+    ],
+    "set1": {
+	"family": "hll",
+	"type": "string",
+	"k": 14,
+	"names": [
+	    "hll1",
+	    "hll2",
+	    "hll3",
+	    "hll4"
+	]
+    },
+    "set2": {
+	"k": 12,
+	"family": "theta",
+	"type": "int",
+	"names": [
+	    "theta0",
+	    "theta1",
+	    "theta2",
+	    "theta3",
+	    "theta4"
+	]
+    },
+    "sketches_B": [
+	{ "name": "duration",
+	  "k": "160",
+	  "family": "kll"
+	},
+	{ "name": "topItems",
+	  "k": "128",
+	  "family": "frequency"
+	},
+	{ "name": "rs",
+	  "k": "20",
+	  "family": "reservoir"
+	},
+	{ "name": "vo",
+	  "k": "20",
+	  "family": "varopt"
+	}	
+    ]
+}