Correctly throw error instead of NPE if topic is empty.

Change-Id: I0d7291bdd1dec690077026ee1498272f154a8b90
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/MergeValidator.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/MergeValidator.java
index 60a4818..6293fb0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/MergeValidator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/MergeValidator.java
@@ -109,6 +109,11 @@
     for (String downstreamBranch : downstreamBranches) {
       boolean dsExists = false;
       QueryBuilder queryBuilder = new QueryBuilder();
+      if (upstreamChange.topic == null || upstreamChange.topic == "") {
+        // If topic is null or empty, we immediately know that downstream is missing.
+        missingDownstreamBranches.add(downstreamBranch);
+        continue;
+      }
       queryBuilder.addParameter("topic", upstreamChange.topic);
       queryBuilder.addParameter("branch", downstreamBranch);
       queryBuilder.addParameter("status", "open");
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/QueryBuilder.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/QueryBuilder.java
index 2aa0ba9..9defd08 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/QueryBuilder.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/QueryBuilder.java
@@ -28,6 +28,9 @@
   }
 
   public void addParameter(String key, String value) throws InvalidQueryParameterException {
+    if (key == null || value == null) {
+      throw new InvalidQueryParameterException("Cannot use null value for key or value of query.");
+    }
     if (value.contains("\"") && (value.contains("{") || value.contains("}"))) {
       // Gerrit does not support search string escaping as of 5/16/2017
       // see https://bugs.chromium.org/p/gerrit/issues/detail?id=5617
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java
index cf42a56..b060b69 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.extensions.common.ApprovalInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.BinaryResult;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Project;
@@ -344,6 +345,54 @@
     merge(result);
   }
 
+  @Test
+  public void testTopicEditedListener_nullTopic() throws Exception {
+    Project.NameKey manifestNameKey = defaultSetup();
+    // Create initial change
+    PushOneCommit.Result result = createChange("subject", "filename", "content", "testtopic");
+    // Project name is scoped by test, so we need to get it from our initial change
+    String projectName = result.getChange().project().get();
+    createBranch(new Branch.NameKey(projectName, "ds_one"));
+    createBranch(new Branch.NameKey(projectName, "ds_two"));
+    pushConfig("automerger.config", manifestNameKey.get(), projectName, "ds_one", "ds_two");
+    // After we upload our config, we upload a new patchset to create the downstreams
+    amendChange(result.getChangeId());
+    result.assertOkStatus();
+    gApi.changes().id(result.getChangeId()).topic(null);
+    int changeNumber = result.getChange().getId().id;
+    exception.expect(ResourceConflictException.class);
+    exception.expectMessage(
+        "Failed to submit 1 change due to the following problems:\nChange "
+            + changeNumber
+            + ": Missing downstream branches ds_one, ds_two. Please recreate the automerges.");
+    // +2 and submit
+    merge(result);
+  }
+
+  @Test
+  public void testTopicEditedListener_emptyTopic() throws Exception {
+    Project.NameKey manifestNameKey = defaultSetup();
+    // Create initial change
+    PushOneCommit.Result result = createChange("subject", "filename", "content", "testtopic");
+    // Project name is scoped by test, so we need to get it from our initial change
+    String projectName = result.getChange().project().get();
+    createBranch(new Branch.NameKey(projectName, "ds_one"));
+    createBranch(new Branch.NameKey(projectName, "ds_two"));
+    pushConfig("automerger.config", manifestNameKey.get(), projectName, "ds_one", "ds_two");
+    // After we upload our config, we upload a new patchset to create the downstreams
+    amendChange(result.getChangeId());
+    result.assertOkStatus();
+    gApi.changes().id(result.getChangeId()).topic("");
+    int changeNumber = result.getChange().getId().id;
+    exception.expect(ResourceConflictException.class);
+    exception.expectMessage(
+        "Failed to submit 1 change due to the following problems:\nChange "
+            + changeNumber
+            + ": Missing downstream branches ds_one, ds_two. Please recreate the automerges.");
+    // +2 and submit
+    merge(result);
+  }
+
   private Project.NameKey defaultSetup() throws Exception {
     Project.NameKey manifestNameKey = createProject("platform/manifest");
     setupTestRepo("default.xml", manifestNameKey, "master", "default.xml");
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/MergeValidatorIT.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/MergeValidatorIT.java
index 54ff231..47b5492 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/automerger/MergeValidatorIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/MergeValidatorIT.java
@@ -69,6 +69,48 @@
   }
 
   @Test
+  public void testNoMissingDownstreamMerges_nullTopic() throws Exception {
+    // Create initial change
+    PushOneCommit.Result result = createChange("subject", "filename", "content");
+    // Project name is scoped by test, so we need to get it from our initial change
+    String projectName = result.getChange().change().getProject().get();
+    createBranch(new Branch.NameKey(projectName, "ds_one"));
+    pushConfig("automerger.config", projectName, "ds_one");
+    // After we upload our config, we upload a new patchset to create the downstreams
+    amendChange(result.getChangeId());
+    result.assertOkStatus();
+    gApi.changes().id(result.getChangeId()).topic(null);
+    int changeNumber = result.getChange().getId().id;
+    exception.expect(ResourceConflictException.class);
+    exception.expectMessage(
+        "Failed to submit 1 change due to the following problems:\nChange "
+            + changeNumber
+            + ": Missing downstream branches ds_one. Please recreate the automerges.");
+    merge(result);
+  }
+
+  @Test
+  public void testNoMissingDownstreamMerges_emptyTopic() throws Exception {
+    // Create initial change
+    PushOneCommit.Result result = createChange("subject", "filename", "content");
+    // Project name is scoped by test, so we need to get it from our initial change
+    String projectName = result.getChange().change().getProject().get();
+    createBranch(new Branch.NameKey(projectName, "ds_one"));
+    pushConfig("automerger.config", projectName, "ds_one");
+    // After we upload our config, we upload a new patchset to create the downstreams
+    amendChange(result.getChangeId());
+    result.assertOkStatus();
+    gApi.changes().id(result.getChangeId()).topic("");
+    int changeNumber = result.getChange().getId().id;
+    exception.expect(ResourceConflictException.class);
+    exception.expectMessage(
+        "Failed to submit 1 change due to the following problems:\nChange "
+            + changeNumber
+            + ": Missing downstream branches ds_one. Please recreate the automerges.");
+    merge(result);
+  }
+
+  @Test
   public void testNoMissingDownstreamMerges_branchWithQuotes() throws Exception {
     // Create initial change
     PushOneCommit.Result result = createChange("subject", "filename", "content", "testtopic");
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/QueryBuilderTest.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/QueryBuilderTest.java
index 2179602..dd99802 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/automerger/QueryBuilderTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/QueryBuilderTest.java
@@ -37,6 +37,21 @@
   }
 
   @Test
+  public void nullTest() throws Exception {
+    queryBuilder.addParameter("status", "open");
+    exception.expect(InvalidQueryParameterException.class);
+    exception.expectMessage("Cannot use null value for key or value of query.");
+    queryBuilder.addParameter("topic", null);
+  }
+
+  @Test
+  public void emptyStringTest() throws Exception {
+    queryBuilder.addParameter("status", "open");
+    queryBuilder.addParameter("topic", "");
+    assertThat(queryBuilder.get()).isEqualTo("topic:\"\" status:\"open\"");
+  }
+
+  @Test
   public void removeParameterTest() throws Exception {
     queryBuilder.addParameter("status", "open");
     queryBuilder.addParameter("branch", "master");