Add IT test for Jira

Change-Id: I5f512c778ce5cfd1985d382f83da20fffc0e19ec
diff --git a/BUILD b/BUILD
index c22e753..eca48a1 100644
--- a/BUILD
+++ b/BUILD
@@ -43,5 +43,6 @@
         ":its-jira__plugin",
         "//plugins/its-base",
         "@mockito//jar",
+        "@wiremock//jar",
     ],
 )
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index 2709df3..f04d8fb 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -23,3 +23,8 @@
     sha1 = "639033469776fd37c08358c6b92a4761feb2af4b",
   )
 
+  maven_jar(
+    name = "wiremock",
+    artifact = "com.github.tomakehurst:wiremock-standalone:2.12.0",
+    sha1 = "25f45d45091627a3bae5510495c99a561b2633c4",
+  )
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraITTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraITTest.java
new file mode 100644
index 0000000..1edce30
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraITTest.java
@@ -0,0 +1,312 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.its.jira;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.noContent;
+import static com.github.tomakehurst.wiremock.client.WireMock.ok;
+import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
+import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static java.lang.String.format;
+import static java.net.HttpURLConnection.HTTP_CREATED;
+import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.testing.ConfigSuite;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+
+@RunWith(ConfigSuite.class)
+@UseLocalDisk
+@NoHttpd
+@TestPlugin(name = "its-jira", sysModule = "com.googlesource.gerrit.plugins.its.jira.JiraModule")
+public class JiraITTest extends LightweightPluginDaemonTest {
+  private static final String PLUGIN_NAME = "its-jira";
+  private static final String BASE_PREFIX = "/rest/api/2";
+  private static final String SERVER_INFO_PREFIX = "/serverinfo";
+  private static final String COMMENT_CLASS_PREFIX = "/comment";
+  private static final String TRANSITION = "transition";
+  private static final String TRANSITION_RESPONSE_BODY =
+      format("{\"transitions\":[{\"name\":\"%s\",\"id\":\"1\"}]}", TRANSITION);
+  private static final String ISSUE_CLASS_PREFIX = "/issue/";
+  private static final String TRANSITIONS_CLASS_PREFIX = "/transitions";
+  private static final String JIRA_ISSUE = "JIRA-1000";
+  private static final int PORT = 19888;
+  private static final String URL = "http://localhost:" + PORT;
+  private static final String COMMENT_SECTION = "commentLink." + PLUGIN_NAME;
+
+  private Path its_dir;
+
+  @Rule public WireMockRule wireMockRule = new WireMockRule(PORT);
+
+  @Override
+  public void beforeTest(Description description) throws Exception {
+    super.beforeTest(description);
+    createItsDir();
+  }
+
+  @Before
+  public void enablePluginInProjectConfig() throws Exception {
+    projectCache
+        .checkedGet(project)
+        .getConfig()
+        .getPluginConfig(PLUGIN_NAME)
+        .setString("enabled", "true");
+  }
+
+  @Test
+  @GerritConfig(name = COMMENT_SECTION + ".match", value = "([A-Z]+-[0-9]+)")
+  @GerritConfig(
+    name = COMMENT_SECTION + ".html",
+    value = "<a href=\"" + URL + "/browse/$1\">$1</a>"
+  )
+  @GerritConfig(name = COMMENT_SECTION + ".association", value = "SUGGESTED")
+  @GerritConfig(name = PLUGIN_NAME + ".url", value = URL)
+  @GerritConfig(name = PLUGIN_NAME + ".username", value = "user")
+  @GerritConfig(name = PLUGIN_NAME + ".password", value = "pass")
+  public void testIssueExists() throws Exception {
+    createItsRulesConfigWithoutActions();
+    mockServerCall();
+    wireMockRule.givenThat(
+        WireMock.get(urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE)).willReturn(ok()));
+
+    createChangeWithIssue();
+    verifyIssueCall();
+  }
+
+  @Test
+  @GerritConfig(name = COMMENT_SECTION + ".match", value = "([A-Z]+-[0-9]+)")
+  @GerritConfig(
+    name = COMMENT_SECTION + ".html",
+    value = "<a href=\"" + URL + "/browse/$1\">$1</a>"
+  )
+  @GerritConfig(name = COMMENT_SECTION + ".association", value = "SUGGESTED")
+  @GerritConfig(name = PLUGIN_NAME + ".url", value = URL)
+  @GerritConfig(name = PLUGIN_NAME + ".username", value = "user")
+  @GerritConfig(name = PLUGIN_NAME + ".password", value = "pass")
+  public void testIssueExistsWithTransitionSuccessful() throws Exception {
+    createItsRulesConfigWithTransitions();
+    mockServerCall();
+    mockTransitionCalls();
+    wireMockRule.givenThat(
+        WireMock.get(urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE)).willReturn(ok()));
+
+    createChangeWithIssue();
+
+    verifyIssueCall();
+    verifyTransitionCalls();
+  }
+
+  @Test
+  @GerritConfig(name = COMMENT_SECTION + ".match", value = "([A-Z]+-[0-9]+)")
+  @GerritConfig(
+    name = COMMENT_SECTION + ".html",
+    value = "<a href=\"" + URL + "/browse/$1\">$1</a>"
+  )
+  @GerritConfig(name = COMMENT_SECTION + ".association", value = "SUGGESTED")
+  @GerritConfig(name = PLUGIN_NAME + ".url", value = URL)
+  @GerritConfig(name = PLUGIN_NAME + ".username", value = "user")
+  @GerritConfig(name = PLUGIN_NAME + ".password", value = "pass")
+  public void testIssueExistsWithCommentSuccessful() throws Exception {
+    createItsRulesConfigWithComment();
+    mockServerCall();
+    mockCommentCall();
+    wireMockRule.givenThat(
+        WireMock.get(urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE)).willReturn(ok()));
+
+    createChangeWithIssue();
+
+    verifyIssueCall();
+    verifyCommentCall();
+  }
+
+  @Test
+  @GerritConfig(name = COMMENT_SECTION + ".match", value = "([A-Z]+-[0-9]+)")
+  @GerritConfig(
+    name = COMMENT_SECTION + ".html",
+    value = "<a href=\"" + URL + "/browse/$1\">$1</a>"
+  )
+  @GerritConfig(name = COMMENT_SECTION + ".association", value = "SUGGESTED")
+  @GerritConfig(name = PLUGIN_NAME + ".url", value = URL)
+  @GerritConfig(name = PLUGIN_NAME + ".username", value = "user")
+  @GerritConfig(name = PLUGIN_NAME + ".password", value = "pass")
+  public void testIssueExistsWithAllActionsSuccessful() throws Exception {
+    createItsRulesConfigWithAllActions();
+    mockServerCall();
+    mockTransitionCalls();
+    mockCommentCall();
+    wireMockRule.givenThat(
+        WireMock.get(urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE)).willReturn(ok()));
+
+    createChangeWithIssue();
+
+    verifyIssueCall();
+    verifyTransitionCalls();
+    verifyCommentCall();
+  }
+
+  @Test
+  @GerritConfig(name = COMMENT_SECTION + ".match", value = "([A-Z]+-[0-9]+)")
+  @GerritConfig(
+    name = COMMENT_SECTION + ".html",
+    value = "<a href=\"" + URL + "/browse/$1\">$1</a>"
+  )
+  @GerritConfig(name = COMMENT_SECTION + ".association", value = "SUGGESTED")
+  @GerritConfig(name = PLUGIN_NAME + ".url", value = URL)
+  @GerritConfig(name = PLUGIN_NAME + ".username", value = "user")
+  @GerritConfig(name = PLUGIN_NAME + ".password", value = "pass")
+  public void testIssueNotExists() throws Exception {
+    createItsRulesConfigWithoutActions();
+    mockServerCall();
+    wireMockRule.givenThat(
+        WireMock.get(urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE))
+            .willReturn(aResponse().withStatus(HTTP_NOT_FOUND)));
+    createChangeWithIssue();
+    verifyIssueCall();
+  }
+
+  @Test
+  @GerritConfig(name = COMMENT_SECTION + ".match", value = "([A-Z]+-[0-9]+)")
+  @GerritConfig(
+    name = COMMENT_SECTION + ".html",
+    value = "<a href=\"" + URL + "/browse/$1\">$1</a>"
+  )
+  @GerritConfig(name = COMMENT_SECTION + ".association", value = "SUGGESTED")
+  @GerritConfig(name = PLUGIN_NAME + ".url", value = URL)
+  @GerritConfig(name = PLUGIN_NAME + ".username", value = "user")
+  @GerritConfig(name = PLUGIN_NAME + ".password", value = "pass")
+  public void testIssueForbidden() throws Exception {
+    createItsRulesConfigWithoutActions();
+    mockServerCall();
+    wireMockRule.givenThat(
+        WireMock.get(urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE))
+            .willReturn(aResponse().withStatus(HTTP_FORBIDDEN)));
+    createChangeWithIssue();
+    verifyIssueCall();
+  }
+
+  private void mockServerCall() {
+    wireMockRule.resetRequests();
+    wireMockRule.givenThat(
+        WireMock.get(urlEqualTo(BASE_PREFIX + SERVER_INFO_PREFIX)).willReturn(ok()));
+  }
+
+  private void mockTransitionCalls() {
+    wireMockRule.givenThat(
+        WireMock.get(
+                urlEqualTo(
+                    BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE + TRANSITIONS_CLASS_PREFIX))
+            .willReturn(okJson(TRANSITION_RESPONSE_BODY)));
+    wireMockRule.givenThat(
+        WireMock.post(
+                urlEqualTo(
+                    BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE + TRANSITIONS_CLASS_PREFIX))
+            .willReturn(noContent()));
+  }
+
+  private void mockCommentCall() {
+    wireMockRule.givenThat(
+        WireMock.post(
+                urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE + COMMENT_CLASS_PREFIX))
+            .willReturn(aResponse().withStatus(HTTP_CREATED)));
+  }
+
+  private void verifyIssueCall() {
+    wireMockRule.verify(getRequestedFor(urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE)));
+  }
+
+  private void verifyTransitionCalls() {
+    wireMockRule.verify(
+        getRequestedFor(
+            urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE + TRANSITIONS_CLASS_PREFIX)));
+    wireMockRule.verify(
+        postRequestedFor(
+            urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE + TRANSITIONS_CLASS_PREFIX)));
+  }
+
+  private void verifyCommentCall() {
+    wireMockRule.verify(
+        postRequestedFor(
+            urlEqualTo(BASE_PREFIX + ISSUE_CLASS_PREFIX + JIRA_ISSUE + COMMENT_CLASS_PREFIX)));
+  }
+
+  private void createChangeWithIssue() throws Exception {
+    pushFactory
+        .create(db, admin.getIdent(), testRepo, JIRA_ISSUE, "a.txt", "test")
+        .to("refs/for/master");
+  }
+
+  private void createItsDir() throws IOException {
+    its_dir = server.getSitePath().resolve("etc").resolve("its");
+    Files.createDirectories(its_dir);
+  }
+
+  private void createItsRulesConfigWithoutActions() throws IOException {
+    FileBasedConfig cfg =
+        new FileBasedConfig(its_dir.resolve("actions-its-jira.config").toFile(), FS.DETECTED);
+    cfg.setString("rule", "open", "event-type", "patchset-created");
+    cfg.save();
+  }
+
+  private void createItsRulesConfigWithTransitions() throws IOException {
+    FileBasedConfig cfg =
+        new FileBasedConfig(its_dir.resolve("actions-its-jira.config").toFile(), FS.DETECTED);
+    cfg.setString("rule", "open", "event-type", "patchset-created");
+    List<String> actions = new ArrayList<>();
+    actions.add(TRANSITION);
+    cfg.setStringList("rule", "open", "action", actions);
+    cfg.save();
+  }
+
+  private void createItsRulesConfigWithComment() throws IOException {
+    FileBasedConfig cfg =
+        new FileBasedConfig(its_dir.resolve("actions-its-jira.config").toFile(), FS.DETECTED);
+    cfg.setString("rule", "open", "event-type", "patchset-created");
+    List<String> actions = new ArrayList<>();
+    actions.add("add-comment Change created");
+    cfg.setStringList("rule", "open", "action", actions);
+    cfg.save();
+  }
+
+  private void createItsRulesConfigWithAllActions() throws IOException {
+    FileBasedConfig cfg =
+        new FileBasedConfig(its_dir.resolve("actions-its-jira.config").toFile(), FS.DETECTED);
+    cfg.setString("rule", "open", "event-type", "patchset-created");
+    List<String> actions = new ArrayList<>();
+    actions.add(TRANSITION);
+    actions.add("add-comment Change created");
+    cfg.setStringList("rule", "open", "action", actions);
+    cfg.save();
+  }
+}