diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2015-10-07 14:12:10 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2015-10-09 16:48:24 +0200 |
commit | f59052c8e1daed111d931893b2a08f9f5fa2a74a (patch) | |
tree | 247e34ce3bb90f2652a3fc0c4602990c66c76369 /it | |
parent | 74af3d8161ca9a767d9b6a7a2bdc3f5d29bd5003 (diff) | |
download | sonarqube-f59052c8e1daed111d931893b2a08f9f5fa2a74a.tar.gz sonarqube-f59052c8e1daed111d931893b2a08f9f5fa2a74a.zip |
SONAR-6717 Restore issues actions API
Diffstat (limited to 'it')
9 files changed, 449 insertions, 8 deletions
diff --git a/it/it-plugins/issue-action-plugin/pom.xml b/it/it-plugins/issue-action-plugin/pom.xml new file mode 100644 index 00000000000..6b176b8e5bd --- /dev/null +++ b/it/it-plugins/issue-action-plugin/pom.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.sonarsource.it</groupId> + <artifactId>it-plugins</artifactId> + <version>5.2-SNAPSHOT</version> + </parent> + + <artifactId>issue-action-plugin</artifactId> + <packaging>sonar-plugin</packaging> + <name>Plugins :: Issue Action</name> + <version>1.0-SNAPSHOT</version> + + <dependencies> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-plugin-api</artifactId> + <version>${apiVersion}</version> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-packaging-maven-plugin</artifactId> + <version>1.1</version> + <extensions>true</extensions> + <configuration> + <pluginClass>IssueActionPlugin</pluginClass> + <pluginKey>issueaction</pluginKey> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/it/it-plugins/issue-action-plugin/src/main/java/ActionDefinition.java b/it/it-plugins/issue-action-plugin/src/main/java/ActionDefinition.java new file mode 100644 index 00000000000..84f5e6c5a6a --- /dev/null +++ b/it/it-plugins/issue-action-plugin/src/main/java/ActionDefinition.java @@ -0,0 +1,46 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import org.sonar.api.ServerExtension; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.action.Actions; +import org.sonar.api.issue.action.Function; +import org.sonar.api.issue.condition.HasResolution; + +public class ActionDefinition implements ServerExtension { + + private final Actions actions; + + public ActionDefinition(Actions actions) { + this.actions = actions; + } + + public void start() { + actions.add("fake") + .setConditions(new HasResolution(Issue.RESOLUTION_FIXED)) + .setFunctions(new Function() { + @Override + public void execute(Context context) { + context.setAttribute("fake", "fake action"); + context.addComment("New Comment from fake action"); + } + }); + } +} diff --git a/it/it-plugins/issue-action-plugin/src/main/java/IssueActionPlugin.java b/it/it-plugins/issue-action-plugin/src/main/java/IssueActionPlugin.java new file mode 100644 index 00000000000..d322d9f4d90 --- /dev/null +++ b/it/it-plugins/issue-action-plugin/src/main/java/IssueActionPlugin.java @@ -0,0 +1,34 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import org.sonar.api.SonarPlugin; + +import java.util.Arrays; +import java.util.List; + +public class IssueActionPlugin extends SonarPlugin { + + public List getExtensions() { + return Arrays.asList( + ActionDefinition.class + ); + } + +} diff --git a/it/it-plugins/issue-action-plugin/src/main/resources/org/sonar/l10n/issueaction.properties b/it/it-plugins/issue-action-plugin/src/main/resources/org/sonar/l10n/issueaction.properties new file mode 100644 index 00000000000..f507ceed575 --- /dev/null +++ b/it/it-plugins/issue-action-plugin/src/main/resources/org/sonar/l10n/issueaction.properties @@ -0,0 +1,21 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2014 SonarSource +# mailto:contact AT sonarsource DOT com +# +# SonarQube is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# SonarQube is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +issue.action.fake.formlink=Fake diff --git a/it/it-plugins/pom.xml b/it/it-plugins/pom.xml index b0d7c374cef..984e022859d 100644 --- a/it/it-plugins/pom.xml +++ b/it/it-plugins/pom.xml @@ -35,6 +35,7 @@ <module>batch-plugin</module> <module>extension-lifecycle-plugin</module> <module>global-property-change-plugin</module> + <module>issue-action-plugin</module> <module>l10n-fr-pack</module> <module>license-plugin</module> <module>project-builder-plugin</module> diff --git a/it/it-tests/src/test/java/issue/suite/IssueActionTest.java b/it/it-tests/src/test/java/issue/suite/IssueActionTest.java new file mode 100644 index 00000000000..ab23ce589fe --- /dev/null +++ b/it/it-tests/src/test/java/issue/suite/IssueActionTest.java @@ -0,0 +1,257 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package issue.suite; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.build.SonarRunner; +import com.sonar.orchestrator.locator.FileLocation; +import java.util.List; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Test; +import org.sonar.wsclient.base.HttpException; +import org.sonar.wsclient.issue.ActionPlan; +import org.sonar.wsclient.issue.ActionPlanClient; +import org.sonar.wsclient.issue.Issue; +import org.sonar.wsclient.issue.IssueComment; +import org.sonar.wsclient.issue.IssueQuery; +import org.sonar.wsclient.issue.Issues; +import org.sonar.wsclient.issue.NewActionPlan; + +import static issue.suite.IssueTestSuite.ORCHESTRATOR; +import static issue.suite.IssueTestSuite.adminIssueClient; +import static issue.suite.IssueTestSuite.issueClient; +import static issue.suite.IssueTestSuite.search; +import static issue.suite.IssueTestSuite.searchIssueByKey; +import static issue.suite.IssueTestSuite.searchIssues; +import static issue.suite.IssueTestSuite.searchRandomIssue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static util.ItUtils.projectDir; +import static util.ItUtils.toDate; +import static util.ItUtils.verifyHttpException; + +public class IssueActionTest { + + @ClassRule + public static Orchestrator orchestrator = ORCHESTRATOR; + + Issue issue; + SonarRunner scan; + + private static List<Issue> searchIssuesBySeverities(String componentKey, String... severities) { + return searchIssues(IssueQuery.create().componentRoots(componentKey).severities(severities)); + } + + private static ActionPlanClient adminActionPlanClient() { + return orchestrator.getServer().adminWsClient().actionPlanClient(); + } + + @Before + public void resetData() { + orchestrator.resetData(); + orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/issue/suite/IssueActionTest/xoo-one-issue-per-line-profile.xml")); + orchestrator.getServer().provisionProject("sample", "Sample"); + orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "xoo-one-issue-per-line-profile"); + + scan = SonarRunner.create(projectDir("shared/xoo-sample")); + orchestrator.executeBuild(scan); + issue = searchRandomIssue(); + } + + @Test + public void no_comments_by_default() throws Exception { + assertThat(issue.comments()).isEmpty(); + } + + @Test + public void add_comment() throws Exception { + IssueComment comment = adminIssueClient().addComment(issue.key(), "this is my *comment*"); + assertThat(comment.key()).isNotNull(); + assertThat(comment.htmlText()).isEqualTo("this is my <em>comment</em>"); + assertThat(comment.login()).isEqualTo("admin"); + assertThat(comment.createdAt()).isNotNull(); + + // reload issue + Issue reloaded = searchIssueByKey(issue.key()); + + assertThat(reloaded.comments()).hasSize(1); + assertThat(reloaded.comments().get(0).key()).isEqualTo(comment.key()); + assertThat(reloaded.comments().get(0).htmlText()).isEqualTo("this is my <em>comment</em>"); + assertThat(reloaded.updateDate().before(issue.creationDate())).isFalse(); + } + + /** + * SONAR-4450 + */ + @Test + public void should_reject_blank_comment() throws Exception { + try { + adminIssueClient().addComment(issue.key(), " "); + fail(); + } catch (HttpException ex) { + assertThat(ex.status()).isEqualTo(400); + } + + Issue reloaded = searchIssueByKey(issue.key()); + assertThat(reloaded.comments()).hasSize(0); + } + + /** + * SONAR-4352 + */ + @Test + public void change_severity() { + String componentKey = "sample"; + + // there are no blocker issues + assertThat(searchIssuesBySeverities(componentKey, "BLOCKER")).isEmpty(); + + // increase the severity of an issue + adminIssueClient().setSeverity(issue.key(), "BLOCKER"); + + assertThat(searchIssuesBySeverities(componentKey, "BLOCKER")).hasSize(1); + + orchestrator.executeBuild(scan); + Issue reloaded = searchIssueByKey(issue.key()); + assertThat(reloaded.severity()).isEqualTo("BLOCKER"); + assertThat(reloaded.status()).isEqualTo("OPEN"); + assertThat(reloaded.resolution()).isNull(); + assertThat(reloaded.creationDate()).isEqualTo(issue.creationDate()); + assertThat(reloaded.creationDate().before(reloaded.updateDate())).isTrue(); + } + + /** + * SONAR-4287 + */ + @Test + public void assign() { + assertThat(issue.assignee()).isNull(); + Issues issues = search(IssueQuery.create().issues(issue.key())); + assertThat(issues.users()).isEmpty(); + + adminIssueClient().assign(issue.key(), "admin"); + assertThat(search(IssueQuery.create().assignees("admin")).list()).hasSize(1); + + orchestrator.executeBuild(scan); + Issue reloaded = searchIssueByKey(issue.key()); + assertThat(reloaded.assignee()).isEqualTo("admin"); + assertThat(reloaded.creationDate()).isEqualTo(issue.creationDate()); + + issues = search(IssueQuery.create().issues(issue.key())); + assertThat(issues.user("admin")).isNotNull(); + assertThat(issues.user("admin").name()).isEqualTo("Administrator"); + + // unassign + adminIssueClient().assign(issue.key(), null); + reloaded = searchIssueByKey(issue.key()); + assertThat(reloaded.assignee()).isNull(); + assertThat(issueClient().find(IssueQuery.create().assignees("admin")).list()).isEmpty(); + } + + /** + * SONAR-4287 + */ + @Test + public void fail_assign_if_assignee_does_not_exist() { + assertThat(issue.assignee()).isNull(); + try { + adminIssueClient().assign(issue.key(), "unknown"); + fail(); + } catch (Exception e) { + verifyHttpException(e, 400); + } + } + + /** + * SONAR-4290 + */ + @Test + public void plan() { + assertThat(issue.actionPlan()).isNull(); + + // Set action plan to issue + ActionPlan newActionPlan = adminActionPlanClient().create(NewActionPlan.create().name("Short term").project("sample") + .description("Short term issues").deadLine(toDate("2113-01-31"))); + assertThat(newActionPlan.key()).isNotNull(); + adminIssueClient().plan(issue.key(), newActionPlan.key()); + assertThat(search(IssueQuery.create().actionPlans(newActionPlan.key())).list()).hasSize(1); + + orchestrator.executeBuild(scan); + Issue reloaded = searchIssueByKey(issue.key()); + assertThat(reloaded.actionPlan()).isEqualTo(newActionPlan.key()); + assertThat(reloaded.creationDate()).isEqualTo(issue.creationDate()); + ActionPlan actionPlan = search(IssueQuery.create().actionPlans(newActionPlan.key())).actionPlans(reloaded); + assertThat(actionPlan.name()).isEqualTo(newActionPlan.name()); + assertThat(actionPlan.deadLine()).isEqualTo(newActionPlan.deadLine()); + } + + @Test + public void fail_plan_if_action_plan_does_not_exist() { + assertThat(issue.actionPlan()).isNull(); + try { + adminIssueClient().plan(issue.key(), "unknown"); + fail(); + } catch (Exception e) { + verifyHttpException(e, 400); + } + } + + @Test + public void unplan() { + assertThat(issue.actionPlan()).isNull(); + + // Set action plan to issue + ActionPlan newActionPlan = adminActionPlanClient().create(NewActionPlan.create().name("Short term").project("sample") + .description("Short term issues").deadLine(toDate("2113-01-31"))); + assertThat(newActionPlan.key()).isNotNull(); + adminIssueClient().plan(issue.key(), newActionPlan.key()); + assertThat(search(IssueQuery.create().actionPlans(newActionPlan.key())).list()).hasSize(1); + + // Unplan + adminIssueClient().plan(issue.key(), null); + assertThat(search(IssueQuery.create().actionPlans(newActionPlan.key())).list()).hasSize(0); + + orchestrator.executeBuild(scan); + Issue reloaded = searchIssueByKey(issue.key()); + assertThat(reloaded.actionPlan()).isNull(); + assertThat(reloaded.creationDate()).isEqualTo(issue.creationDate()); + } + + /** + * SONAR-4315 + */ + @Test + public void apply_action_from_plugin() { + // The condition on the action defined by the plugin is that the status must be resolved + adminIssueClient().doTransition(issue.key(), "resolve"); + assertThat(adminIssueClient().actions(issue.key())).contains("fake"); + + adminIssueClient().doAction(issue.key(), "fake"); + + // reload issue + Issue reloaded = searchIssueByKey(issue.key()); + + assertThat(reloaded.comments()).hasSize(1); + assertThat(reloaded.comments().get(0).htmlText()).isEqualTo("New Comment from fake action"); + } + +} diff --git a/it/it-tests/src/test/java/issue/suite/IssueTestSuite.java b/it/it-tests/src/test/java/issue/suite/IssueTestSuite.java index 6757552276e..3e6a1e4ce02 100644 --- a/it/it-tests/src/test/java/issue/suite/IssueTestSuite.java +++ b/it/it-tests/src/test/java/issue/suite/IssueTestSuite.java @@ -13,42 +13,52 @@ import org.junit.runners.Suite; import org.sonar.wsclient.issue.Issue; import org.sonar.wsclient.issue.IssueClient; import org.sonar.wsclient.issue.IssueQuery; -import util.ItUtils; +import org.sonar.wsclient.issue.Issues; import static org.assertj.core.api.Assertions.assertThat; +import static util.ItUtils.pluginArtifact; +import static util.ItUtils.xooPlugin; @RunWith(Suite.class) @Suite.SuiteClasses({ - CommonRulesTest.class, IssueWorkflowTest.class, ManualRulesTest.class, CustomRulesTest.class + CommonRulesTest.class, IssueWorkflowTest.class, ManualRulesTest.class, CustomRulesTest.class, IssueActionTest.class }) public class IssueTestSuite { @ClassRule public static final Orchestrator ORCHESTRATOR = Orchestrator.builderEnv() .setSonarVersion("DEV") - .addPlugin(ItUtils.xooPlugin()) + .addPlugin(xooPlugin()) + .addPlugin(pluginArtifact("issue-action-plugin")) .build(); static IssueClient adminIssueClient() { return ORCHESTRATOR.getServer().adminWsClient().issueClient(); } + static IssueClient issueClient() { + return ORCHESTRATOR.getServer().wsClient().issueClient(); + } + static Issue searchRandomIssue() { List<Issue> issues = searchIssues(IssueQuery.create()); assertThat(issues).isNotEmpty(); return issues.get(0); } - static List<Issue> searchIssues(IssueQuery issueQuery) { + static Issues search(IssueQuery issueQuery) { issueQuery.urlParams().put("additionalFields", "_all"); - return adminIssueClient().find(issueQuery).list(); + return issueClient().find(issueQuery); + } + + static List<Issue> searchIssues(IssueQuery issueQuery) { + return search(issueQuery).list(); } static Issue searchIssueByKey(String issueKey) { - IssueQuery query = IssueQuery.create().issues(issueKey); - query.urlParams().put("additionalFields", "_all"); - List<Issue> issues = searchIssues(query); + List<Issue> issues = searchIssues(IssueQuery.create().issues(issueKey)); assertThat(issues).hasSize(1); return issues.get(0); } + } diff --git a/it/it-tests/src/test/java/util/ItUtils.java b/it/it-tests/src/test/java/util/ItUtils.java index 1d8e3406ea8..4cb4df2c045 100644 --- a/it/it-tests/src/test/java/util/ItUtils.java +++ b/it/it-tests/src/test/java/util/ItUtils.java @@ -9,6 +9,9 @@ import com.google.common.collect.Iterables; import com.sonar.orchestrator.Orchestrator; import com.sonar.orchestrator.build.BuildResult; import com.sonar.orchestrator.build.SonarRunner; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; @@ -27,6 +30,7 @@ import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.api.Assertions.assertThat; import org.apache.commons.io.FileUtils; +import org.sonar.wsclient.base.HttpException; public class ItUtils { @@ -157,4 +161,20 @@ public class ItUtils { } return from(Iterables.concat(asList(properties), asList(str))).toArray(String.class); } + + public static void verifyHttpException(Exception e, int expectedCode) { + assertThat(e).isInstanceOf(HttpException.class); + HttpException exception = (HttpException) e; + assertThat(exception.status()).isEqualTo(expectedCode); + } + + public static Date toDate(String sDate) { + try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + return sdf.parse(sDate); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + } diff --git a/it/it-tests/src/test/resources/issue/suite/IssueActionTest/xoo-one-issue-per-line-profile.xml b/it/it-tests/src/test/resources/issue/suite/IssueActionTest/xoo-one-issue-per-line-profile.xml new file mode 100644 index 00000000000..608f80cae96 --- /dev/null +++ b/it/it-tests/src/test/resources/issue/suite/IssueActionTest/xoo-one-issue-per-line-profile.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?><!-- Generated by Sonar --> +<profile> + <name>xoo-one-issue-per-line-profile</name> + <language>xoo</language> + <rules> + <rule> + <repositoryKey>xoo</repositoryKey> + <key>OneIssuePerLine</key> + <priority>CRITICAL</priority> + </rule> + </rules> +</profile> |