aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server-common/src/test/java/org
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2018-07-02 15:07:45 +0200
committersonartech <sonartech@sonarsource.com>2018-07-09 10:39:31 +0200
commitecbd530ab653001d01a64734ad2f29889e511680 (patch)
tree84a024c27cb976fd44bda7e521904297e7de885f /server/sonar-server-common/src/test/java/org
parent69714b41c96eb99271a21735f09ca1a17ff4eaef (diff)
downloadsonarqube-ecbd530ab653001d01a64734ad2f29889e511680.tar.gz
sonarqube-ecbd530ab653001d01a64734ad2f29889e511680.zip
move issue workflow classes to server-common
Diffstat (limited to 'server/sonar-server-common/src/test/java/org')
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java519
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IsBeingClosedTest.java37
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java266
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetCloseDateTest.java36
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetClosedTest.java59
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetResolutionTest.java36
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/StateMachineTest.java44
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/StateTest.java66
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/TransitionTest.java153
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/UnsetAssigneeTest.java37
10 files changed, 1253 insertions, 0 deletions
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java
new file mode 100644
index 00000000000..0be2e028f14
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java
@@ -0,0 +1,519 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Map;
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.Duration;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.db.user.UserDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.rules.ExpectedException.none;
+import static org.sonar.db.user.UserTesting.newUserDto;
+import static org.sonar.server.issue.IssueFieldsSetter.ASSIGNEE;
+import static org.sonar.server.issue.IssueFieldsSetter.RESOLUTION;
+import static org.sonar.server.issue.IssueFieldsSetter.SEVERITY;
+import static org.sonar.server.issue.IssueFieldsSetter.STATUS;
+import static org.sonar.server.issue.IssueFieldsSetter.TECHNICAL_DEBT;
+import static org.sonar.server.issue.IssueFieldsSetter.UNUSED;
+
+public class IssueFieldsSetterTest {
+
+ @Rule
+ public ExpectedException thrown = none();
+
+ private DefaultIssue issue = new DefaultIssue();
+ private IssueChangeContext context = IssueChangeContext.createUser(new Date(), "user_uuid");
+ private IssueFieldsSetter underTest = new IssueFieldsSetter();
+
+ @Test
+ public void assign() {
+ UserDto user = newUserDto().setLogin("emmerik").setName("Emmerik");
+
+ boolean updated = underTest.assign(issue, user, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.assignee()).isEqualTo(user.getUuid());
+ assertThat(issue.mustSendNotifications()).isTrue();
+ FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
+ assertThat(diff.oldValue()).isEqualTo(UNUSED);
+ assertThat(diff.newValue()).isEqualTo(user.getName());
+ }
+
+ @Test
+ public void unassign() {
+ issue.setAssigneeUuid("user_uuid");
+ boolean updated = underTest.assign(issue, null, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.assignee()).isNull();
+ assertThat(issue.mustSendNotifications()).isTrue();
+ FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
+ assertThat(diff.oldValue()).isEqualTo(UNUSED);
+ assertThat(diff.newValue()).isNull();
+ }
+
+ @Test
+ public void change_assignee() {
+ UserDto user = newUserDto().setLogin("emmerik").setName("Emmerik");
+
+ issue.setAssigneeUuid("user_uuid");
+ boolean updated = underTest.assign(issue, user, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.assignee()).isEqualTo(user.getUuid());
+ assertThat(issue.mustSendNotifications()).isTrue();
+ FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
+ assertThat(diff.oldValue()).isEqualTo(UNUSED);
+ assertThat(diff.newValue()).isEqualTo(user.getName());
+ }
+
+ @Test
+ public void not_change_assignee() {
+ UserDto user = newUserDto().setLogin("morgan").setName("Morgan");
+
+ issue.setAssigneeUuid(user.getUuid());
+ boolean updated = underTest.assign(issue, user, context);
+ assertThat(updated).isFalse();
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_new_assignee() {
+ boolean updated = underTest.setNewAssignee(issue, "user_uuid", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.assignee()).isEqualTo("user_uuid");
+ assertThat(issue.mustSendNotifications()).isTrue();
+ FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
+ assertThat(diff.oldValue()).isEqualTo(UNUSED);
+ assertThat(diff.newValue()).isEqualTo("user_uuid");
+ }
+
+ @Test
+ public void not_set_new_assignee_if_new_assignee_is_null() {
+ boolean updated = underTest.setNewAssignee(issue, null, context);
+ assertThat(updated).isFalse();
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void fail_with_ISE_when_setting_new_assignee_on_already_assigned_issue() {
+ issue.setAssigneeUuid("user_uuid");
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("It's not possible to update the assignee with this method, please use assign()");
+ underTest.setNewAssignee(issue, "another_user_uuid", context);
+ }
+
+ @Test
+ public void set_severity() {
+ boolean updated = underTest.setSeverity(issue, "BLOCKER", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.severity()).isEqualTo("BLOCKER");
+ assertThat(issue.manualSeverity()).isFalse();
+ assertThat(issue.mustSendNotifications()).isFalse();
+
+ FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY);
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo("BLOCKER");
+ }
+
+ @Test
+ public void set_past_severity() {
+ issue.setSeverity("BLOCKER");
+ boolean updated = underTest.setPastSeverity(issue, "INFO", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.severity()).isEqualTo("BLOCKER");
+ assertThat(issue.mustSendNotifications()).isFalse();
+
+ FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY);
+ assertThat(diff.oldValue()).isEqualTo("INFO");
+ assertThat(diff.newValue()).isEqualTo("BLOCKER");
+ }
+
+ @Test
+ public void update_severity() {
+ issue.setSeverity("BLOCKER");
+ boolean updated = underTest.setSeverity(issue, "MINOR", context);
+
+ assertThat(updated).isTrue();
+ assertThat(issue.severity()).isEqualTo("MINOR");
+ assertThat(issue.mustSendNotifications()).isFalse();
+ FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY);
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("MINOR");
+ }
+
+ @Test
+ public void not_change_severity() {
+ issue.setSeverity("MINOR");
+ boolean updated = underTest.setSeverity(issue, "MINOR", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ assertThat(issue.currentChange()).isNull();
+ }
+
+ @Test
+ public void not_revert_manual_severity() {
+ issue.setSeverity("MINOR").setManualSeverity(true);
+ try {
+ underTest.setSeverity(issue, "MAJOR", context);
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("Severity can't be changed");
+ }
+ }
+
+ @Test
+ public void set_manual_severity() {
+ issue.setSeverity("BLOCKER");
+ boolean updated = underTest.setManualSeverity(issue, "MINOR", context);
+
+ assertThat(updated).isTrue();
+ assertThat(issue.severity()).isEqualTo("MINOR");
+ assertThat(issue.manualSeverity()).isTrue();
+ assertThat(issue.mustSendNotifications()).isTrue();
+ FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY);
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("MINOR");
+ }
+
+ @Test
+ public void not_change_manual_severity() {
+ issue.setSeverity("MINOR").setManualSeverity(true);
+ boolean updated = underTest.setManualSeverity(issue, "MINOR", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_line() {
+ boolean updated = underTest.setLine(issue, 123);
+ assertThat(updated).isTrue();
+ assertThat(issue.line()).isEqualTo(123);
+ assertThat(issue.mustSendNotifications()).isFalse();
+ // do not save change
+ assertThat(issue.currentChange()).isNull();
+ }
+
+ @Test
+ public void set_past_line() {
+ issue.setLine(42);
+ boolean updated = underTest.setPastLine(issue, 123);
+ assertThat(updated).isTrue();
+ assertThat(issue.line()).isEqualTo(42);
+ assertThat(issue.mustSendNotifications()).isFalse();
+
+ // do not save change
+ assertThat(issue.currentChange()).isNull();
+ }
+
+ @Test
+ public void line_is_not_changed() {
+ issue.setLine(123);
+ boolean updated = underTest.setLine(issue, 123);
+ assertThat(updated).isFalse();
+ assertThat(issue.line()).isEqualTo(123);
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void change_locations() {
+ issue.setLocations("[1-3]");
+ boolean updated = underTest.setLocations(issue, "[1-4]");
+ assertThat(updated).isTrue();
+ assertThat(issue.getLocations().toString()).isEqualTo("[1-4]");
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void do_not_change_locations() {
+ issue.setLocations("[1-3]");
+ boolean updated = underTest.setLocations(issue, "[1-3]");
+ assertThat(updated).isFalse();
+ assertThat(issue.getLocations().toString()).isEqualTo("[1-3]");
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_locations_for_the_first_time() {
+ issue.setLocations(null);
+ boolean updated = underTest.setLocations(issue, "[1-4]");
+ assertThat(updated).isTrue();
+ assertThat(issue.getLocations().toString()).isEqualTo("[1-4]");
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_resolution() {
+ boolean updated = underTest.setResolution(issue, "OPEN", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.resolution()).isEqualTo("OPEN");
+
+ FieldDiffs.Diff diff = issue.currentChange().get(RESOLUTION);
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo("OPEN");
+ assertThat(issue.mustSendNotifications()).isTrue();
+ }
+
+ @Test
+ public void not_change_resolution() {
+ issue.setResolution("FIXED");
+ boolean updated = underTest.setResolution(issue, "FIXED", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.resolution()).isEqualTo("FIXED");
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_status() {
+ boolean updated = underTest.setStatus(issue, "OPEN", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.status()).isEqualTo("OPEN");
+
+ FieldDiffs.Diff diff = issue.currentChange().get(STATUS);
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo("OPEN");
+ assertThat(issue.mustSendNotifications()).isTrue();
+ }
+
+ @Test
+ public void not_change_status() {
+ issue.setStatus("CLOSED");
+ boolean updated = underTest.setStatus(issue, "CLOSED", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.status()).isEqualTo("CLOSED");
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_new_attribute_value() {
+ boolean updated = underTest.setAttribute(issue, "JIRA", "FOO-123", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.attribute("JIRA")).isEqualTo("FOO-123");
+ assertThat(issue.currentChange().diffs()).hasSize(1);
+ assertThat(issue.currentChange().get("JIRA").oldValue()).isNull();
+ assertThat(issue.currentChange().get("JIRA").newValue()).isEqualTo("FOO-123");
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void unset_attribute() {
+ issue.setAttribute("JIRA", "FOO-123");
+ boolean updated = underTest.setAttribute(issue, "JIRA", null, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.attribute("JIRA")).isNull();
+ assertThat(issue.currentChange().diffs()).hasSize(1);
+ assertThat(issue.currentChange().get("JIRA").oldValue()).isEqualTo("FOO-123");
+ assertThat(issue.currentChange().get("JIRA").newValue()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void not_update_attribute() {
+ issue.setAttribute("JIRA", "FOO-123");
+ boolean updated = underTest.setAttribute(issue, "JIRA", "FOO-123", context);
+ assertThat(updated).isFalse();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_gap_to_fix() {
+ boolean updated = underTest.setGap(issue, 3.14, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.isChanged()).isTrue();
+ assertThat(issue.gap()).isEqualTo(3.14);
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void not_set_gap_to_fix_if_unchanged() {
+ issue.setGap(3.14);
+ boolean updated = underTest.setGap(issue, 3.14, context);
+ assertThat(updated).isFalse();
+ assertThat(issue.isChanged()).isFalse();
+ assertThat(issue.gap()).isEqualTo(3.14);
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_past_gap() {
+ issue.setGap(3.14);
+ boolean updated = underTest.setPastGap(issue, 1.0, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.gap()).isEqualTo(3.14);
+
+ // do not save change
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_past_technical_debt() {
+ Duration newDebt = Duration.create(15 * 8 * 60);
+ Duration previousDebt = Duration.create(10 * 8 * 60);
+ issue.setEffort(newDebt);
+ boolean updated = underTest.setPastEffort(issue, previousDebt, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.effort()).isEqualTo(newDebt);
+ assertThat(issue.mustSendNotifications()).isFalse();
+
+ FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT);
+ assertThat(diff.oldValue()).isEqualTo(10L * 8 * 60);
+ assertThat(diff.newValue()).isEqualTo(15L * 8 * 60);
+ }
+
+ @Test
+ public void set_past_technical_debt_without_previous_value() {
+ Duration newDebt = Duration.create(15 * 8 * 60);
+ issue.setEffort(newDebt);
+ boolean updated = underTest.setPastEffort(issue, null, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.effort()).isEqualTo(newDebt);
+ assertThat(issue.mustSendNotifications()).isFalse();
+
+ FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT);
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo(15L * 8 * 60);
+ }
+
+ @Test
+ public void set_past_technical_debt_with_null_new_value() {
+ issue.setEffort(null);
+ Duration previousDebt = Duration.create(10 * 8 * 60);
+ boolean updated = underTest.setPastEffort(issue, previousDebt, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.effort()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+
+ FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT);
+ assertThat(diff.oldValue()).isEqualTo(10L * 8 * 60);
+ assertThat(diff.newValue()).isNull();
+ }
+
+ @Test
+ public void set_message() {
+ boolean updated = underTest.setMessage(issue, "the message", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.isChanged()).isTrue();
+ assertThat(issue.message()).isEqualTo("the message");
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_past_message() {
+ issue.setMessage("new message");
+ boolean updated = underTest.setPastMessage(issue, "past message", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.message()).isEqualTo("new message");
+
+ // do not save change
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_author() {
+ boolean updated = underTest.setAuthorLogin(issue, "eric", context);
+ assertThat(updated).isTrue();
+ assertThat(issue.authorLogin()).isEqualTo("eric");
+
+ FieldDiffs.Diff diff = issue.currentChange().get("author");
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo("eric");
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void set_new_author() {
+ boolean updated = underTest.setNewAuthor(issue, "simon", context);
+ assertThat(updated).isTrue();
+
+ FieldDiffs.Diff diff = issue.currentChange().get("author");
+ assertThat(diff.oldValue()).isNull();
+ assertThat(diff.newValue()).isEqualTo("simon");
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void not_set_new_author_if_new_author_is_null() {
+ boolean updated = underTest.setNewAuthor(issue, null, context);
+ assertThat(updated).isFalse();
+ assertThat(issue.currentChange()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void fail_with_ISE_when_setting_new_author_on_issue() {
+ issue.setAuthorLogin("simon");
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("It's not possible to update the author with this method, please use setAuthorLogin()");
+ underTest.setNewAuthor(issue, "julien", context);
+ }
+
+ @Test
+ public void setIssueMoved_has_no_effect_if_component_uuid_is_not_changed() {
+ String componentUuid = "a";
+ issue.setComponentUuid(componentUuid);
+
+ underTest.setIssueMoved(issue, componentUuid, context);
+
+ assertThat(issue.changes()).isEmpty();
+ assertThat(issue.componentUuid()).isEqualTo(componentUuid);
+ assertThat(issue.isChanged()).isFalse();
+ assertThat(issue.updateDate()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void setIssueMoved_changes_componentUuid_adds_a_change() {
+ String oldComponentUuid = "a";
+ String newComponentUuid = "b";
+ issue.setComponentUuid(oldComponentUuid);
+
+ underTest.setIssueMoved(issue, newComponentUuid, context);
+
+ assertThat(issue.changes()).hasSize(1);
+ FieldDiffs fieldDiffs = issue.changes().get(0);
+ assertThat(fieldDiffs.creationDate()).isEqualTo(context.date());
+ assertThat(fieldDiffs.diffs()).hasSize(1);
+ Map.Entry<String, FieldDiffs.Diff> entry = fieldDiffs.diffs().entrySet().iterator().next();
+ assertThat(entry.getKey()).isEqualTo("file");
+ assertThat(entry.getValue().oldValue()).isEqualTo(oldComponentUuid);
+ assertThat(entry.getValue().newValue()).isEqualTo(newComponentUuid);
+ assertThat(issue.componentUuid()).isEqualTo(newComponentUuid);
+ assertThat(issue.isChanged()).isTrue();
+ assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(context.date(), Calendar.SECOND));
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IsBeingClosedTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IsBeingClosedTest.java
new file mode 100644
index 00000000000..f20e35aab69
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IsBeingClosedTest.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue.workflow;
+
+import org.junit.Test;
+import org.sonar.core.issue.DefaultIssue;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.issue.workflow.IsBeingClosed.INSTANCE;
+
+public class IsBeingClosedTest {
+
+ @Test
+ public void should_be_end_of_life() {
+ DefaultIssue issue = new DefaultIssue();
+ assertThat(INSTANCE.matches(issue.setBeingClosed(true))).isTrue();
+ assertThat(INSTANCE.matches(issue.setBeingClosed(false))).isFalse();
+ }
+
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java
new file mode 100644
index 00000000000..0580233ca31
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java
@@ -0,0 +1,266 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue.workflow;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.Test;
+import org.sonar.api.issue.DefaultTransitions;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.server.issue.IssueFieldsSetter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
+import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX;
+import static org.sonar.api.issue.Issue.STATUS_CLOSED;
+import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
+import static org.sonar.api.issue.Issue.STATUS_OPEN;
+import static org.sonar.api.issue.Issue.STATUS_REOPENED;
+import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
+
+public class IssueWorkflowTest {
+
+ IssueFieldsSetter updater = new IssueFieldsSetter();
+ IssueWorkflow workflow = new IssueWorkflow(new FunctionExecutor(updater), updater);
+
+ @Test
+ public void init_state_machine() {
+ assertThat(workflow.machine()).isNull();
+ workflow.start();
+ assertThat(workflow.machine()).isNotNull();
+ assertThat(workflow.machine().state(STATUS_OPEN)).isNotNull();
+ assertThat(workflow.machine().state(STATUS_CONFIRMED)).isNotNull();
+ assertThat(workflow.machine().state(STATUS_CLOSED)).isNotNull();
+ assertThat(workflow.machine().state(STATUS_REOPENED)).isNotNull();
+ assertThat(workflow.machine().state(STATUS_RESOLVED)).isNotNull();
+ workflow.stop();
+ }
+
+ @Test
+ public void list_statuses() {
+ workflow.start();
+ // order is important for UI
+ assertThat(workflow.statusKeys()).containsSubsequence(STATUS_OPEN, STATUS_CONFIRMED, STATUS_REOPENED, STATUS_RESOLVED, STATUS_CLOSED);
+ }
+
+ @Test
+ public void list_out_transitions_from_status_open() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue().setStatus(STATUS_OPEN);
+ List<Transition> transitions = workflow.outTransitions(issue);
+ assertThat(keys(transitions)).containsOnly("confirm", "falsepositive", "resolve", "wontfix");
+ }
+
+ @Test
+ public void list_out_transitions_from_status_confirmed() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue().setStatus(STATUS_CONFIRMED);
+ List<Transition> transitions = workflow.outTransitions(issue);
+ assertThat(keys(transitions)).containsOnly("unconfirm", "falsepositive", "resolve", "wontfix");
+ }
+
+ @Test
+ public void list_out_transitions_from_status_resolved() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue().setStatus(STATUS_RESOLVED);
+ List<Transition> transitions = workflow.outTransitions(issue);
+ assertThat(keys(transitions)).containsOnly("reopen");
+ }
+
+ @Test
+ public void list_out_transitions_from_status_reopen() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue().setStatus(STATUS_REOPENED);
+ List<Transition> transitions = workflow.outTransitions(issue);
+ assertThat(keys(transitions)).containsOnly("confirm", "resolve", "falsepositive", "wontfix");
+ }
+
+ @Test
+ public void list_no_out_transition_from_status_closed() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue().setStatus(STATUS_CLOSED).setRuleKey(RuleKey.of("java", "R1 "));
+ List<Transition> transitions = workflow.outTransitions(issue);
+ assertThat(transitions).isEmpty();
+ }
+
+ @Test
+ public void fail_if_unknown_status_when_listing_transitions() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue().setStatus("xxx");
+ try {
+ workflow.outTransitions(issue);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Unknown status: xxx");
+ }
+ }
+
+ @Test
+ public void automatically_close_resolved_issue() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue()
+ .setKey("ABCDE")
+ .setRuleKey(RuleKey.of("js", "S001"))
+ .setResolution(RESOLUTION_FIXED)
+ .setStatus(STATUS_RESOLVED)
+ .setNew(false)
+ .setBeingClosed(true);
+ Date now = new Date();
+ workflow.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
+ assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
+ assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
+ assertThat(issue.closeDate()).isNotNull();
+ assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
+ }
+
+ @Test
+ public void close_open_dead_issue() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue()
+ .setKey("ABCDE")
+ .setResolution(null)
+ .setStatus(STATUS_OPEN)
+ .setNew(false)
+ .setBeingClosed(true);
+ Date now = new Date();
+ workflow.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
+ assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
+ assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
+ assertThat(issue.closeDate()).isNotNull();
+ assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
+ }
+
+ @Test
+ public void close_reopened_dead_issue() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue()
+ .setKey("ABCDE")
+ .setResolution(null)
+ .setStatus(STATUS_REOPENED)
+ .setNew(false)
+ .setBeingClosed(true);
+ Date now = new Date();
+ workflow.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
+ assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
+ assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
+ assertThat(issue.closeDate()).isNotNull();
+ assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
+ }
+
+ @Test
+ public void close_confirmed_dead_issue() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue()
+ .setKey("ABCDE")
+ .setResolution(null)
+ .setStatus(STATUS_CONFIRMED)
+ .setNew(false)
+ .setBeingClosed(true);
+ Date now = new Date();
+ workflow.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
+ assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
+ assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
+ assertThat(issue.closeDate()).isNotNull();
+ assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
+ }
+
+ @Test
+ public void fail_if_unknown_status_on_automatic_trans() {
+ workflow.start();
+
+ DefaultIssue issue = new DefaultIssue()
+ .setKey("ABCDE")
+ .setResolution(RESOLUTION_FIXED)
+ .setStatus("xxx")
+ .setNew(false)
+ .setBeingClosed(true);
+ try {
+ workflow.doAutomaticTransition(issue, IssueChangeContext.createScan(new Date()));
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("Unknown status: xxx [issue=ABCDE]");
+ }
+ }
+
+ @Test
+ public void flag_as_false_positive() {
+ DefaultIssue issue = new DefaultIssue()
+ .setKey("ABCDE")
+ .setStatus(STATUS_OPEN)
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+ .setAssigneeUuid("morgan");
+
+ workflow.start();
+ workflow.doTransition(issue, DefaultTransitions.FALSE_POSITIVE, IssueChangeContext.createScan(new Date()));
+
+ assertThat(issue.resolution()).isEqualTo(RESOLUTION_FALSE_POSITIVE);
+ assertThat(issue.status()).isEqualTo(STATUS_RESOLVED);
+
+ // should remove assignee
+ assertThat(issue.assignee()).isNull();
+ }
+
+ @Test
+ public void wont_fix() {
+ DefaultIssue issue = new DefaultIssue()
+ .setKey("ABCDE")
+ .setStatus(STATUS_OPEN)
+ .setRuleKey(RuleKey.of("squid", "AvoidCycle"))
+ .setAssigneeUuid("morgan");
+
+ workflow.start();
+ workflow.doTransition(issue, DefaultTransitions.WONT_FIX, IssueChangeContext.createScan(new Date()));
+
+ assertThat(issue.resolution()).isEqualTo(RESOLUTION_WONT_FIX);
+ assertThat(issue.status()).isEqualTo(STATUS_RESOLVED);
+
+ // should remove assignee
+ assertThat(issue.assignee()).isNull();
+ }
+
+ private Collection<String> keys(List<Transition> transitions) {
+ return Collections2.transform(transitions, new Function<Transition, String>() {
+ @Override
+ public String apply(@Nullable Transition transition) {
+ return transition.key();
+ }
+ });
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetCloseDateTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetCloseDateTest.java
new file mode 100644
index 00000000000..ee139df8786
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetCloseDateTest.java
@@ -0,0 +1,36 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue.workflow;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class SetCloseDateTest {
+ @Test
+ public void should_set_close_date() {
+ SetCloseDate function = new SetCloseDate(true);
+ Function.Context context = mock(Function.Context.class);
+ function.execute(context);
+ verify(context, times(1)).setCloseDate(true);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetClosedTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetClosedTest.java
new file mode 100644
index 00000000000..8c526c4b16b
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetClosedTest.java
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue.workflow;
+
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.core.issue.DefaultIssue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.issue.workflow.SetClosed.INSTANCE;
+
+public class SetClosedTest {
+
+ Function.Context context = mock(Function.Context.class);
+
+ @Test
+ public void should_resolve_as_fixed() {
+ Issue issue = new DefaultIssue().setBeingClosed(true).setOnDisabledRule(false);
+ when(context.issue()).thenReturn(issue);
+ INSTANCE.execute(context);
+ verify(context, times(1)).setResolution(Issue.RESOLUTION_FIXED);
+ }
+
+ @Test
+ public void should_resolve_as_removed_when_rule_is_disabled() {
+ Issue issue = new DefaultIssue().setBeingClosed(true).setOnDisabledRule(true);
+ when(context.issue()).thenReturn(issue);
+ INSTANCE.execute(context);
+ verify(context, times(1)).setResolution(Issue.RESOLUTION_REMOVED);
+ }
+
+ @Test
+ public void line_number_must_be_unset() {
+ Issue issue = new DefaultIssue().setBeingClosed(true).setLine(10);
+ when(context.issue()).thenReturn(issue);
+ INSTANCE.execute(context);
+ verify(context).setLine(null);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetResolutionTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetResolutionTest.java
new file mode 100644
index 00000000000..d84e4d6f5d0
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/SetResolutionTest.java
@@ -0,0 +1,36 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue.workflow;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class SetResolutionTest {
+ @Test
+ public void execute() {
+ SetResolution function = new SetResolution("FIXED");
+ Function.Context context = mock(Function.Context.class);
+ function.execute(context);
+ verify(context, times(1)).setResolution("FIXED");
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/StateMachineTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/StateMachineTest.java
new file mode 100644
index 00000000000..92182ff6062
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/StateMachineTest.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue.workflow;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class StateMachineTest {
+ @Test
+ public void keep_order_of_state_keys() {
+ StateMachine machine = StateMachine.builder().states("OPEN", "RESOLVED", "CLOSED").build();
+
+ assertThat(machine.stateKeys()).containsSubsequence("OPEN", "RESOLVED", "CLOSED");
+ }
+
+ @Test
+ public void stateKey() {
+ StateMachine machine = StateMachine.builder()
+ .states("OPEN", "RESOLVED", "CLOSED")
+ .transition(Transition.builder("resolve").from("OPEN").to("RESOLVED").build())
+ .build();
+
+ assertThat(machine.state("OPEN")).isNotNull();
+ assertThat(machine.state("OPEN").transition("resolve")).isNotNull();
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/StateTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/StateTest.java
new file mode 100644
index 00000000000..c432d210aad
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/StateTest.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue.workflow;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class StateTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ Transition t1 = Transition.builder("close").from("OPEN").to("CLOSED").build();
+
+ @Test
+ public void key_should_be_set() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("State key must be set");
+
+ new State("", new Transition[0]);
+ }
+
+ @Test
+ public void key_should_be_upper_case() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("State key must be upper-case");
+
+ new State("close", new Transition[0]);
+ }
+
+ @Test
+ public void no_duplicated_out_transitions() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Transition 'close' is declared several times from the originating state 'CLOSE'");
+
+ new State("CLOSE", new Transition[] {t1, t1});
+ }
+
+ @Test
+ public void fail_when_transition_is_unknown() {
+ State state = new State("VALIDATED", new Transition[0]);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Transition from state VALIDATED does not exist: Unknown Transition");
+
+ state.transition("Unknown Transition");
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/TransitionTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/TransitionTest.java
new file mode 100644
index 00000000000..ede5b240413
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/TransitionTest.java
@@ -0,0 +1,153 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue.workflow;
+
+import org.junit.Test;
+import org.sonar.api.issue.condition.Condition;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.issue.DefaultIssue;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TransitionTest {
+
+ Condition condition1 = mock(Condition.class);
+ Condition condition2 = mock(Condition.class);
+ Function function1 = mock(Function.class);
+ Function function2 = mock(Function.class);
+
+ @Test
+ public void test_builder() {
+ Transition transition = Transition.builder("close")
+ .from("OPEN").to("CLOSED")
+ .conditions(condition1, condition2)
+ .functions(function1, function2)
+ .requiredProjectPermission(UserRole.ISSUE_ADMIN)
+ .build();
+ assertThat(transition.key()).isEqualTo("close");
+ assertThat(transition.from()).isEqualTo("OPEN");
+ assertThat(transition.to()).isEqualTo("CLOSED");
+ assertThat(transition.conditions()).containsOnly(condition1, condition2);
+ assertThat(transition.functions()).containsOnly(function1, function2);
+ assertThat(transition.automatic()).isFalse();
+ assertThat(transition.requiredProjectPermission()).isEqualTo(UserRole.ISSUE_ADMIN);
+ }
+
+ @Test
+ public void test_simplest_transition() {
+ Transition transition = Transition.builder("close")
+ .from("OPEN").to("CLOSED")
+ .build();
+ assertThat(transition.key()).isEqualTo("close");
+ assertThat(transition.from()).isEqualTo("OPEN");
+ assertThat(transition.to()).isEqualTo("CLOSED");
+ assertThat(transition.conditions()).isEmpty();
+ assertThat(transition.functions()).isEmpty();
+ assertThat(transition.requiredProjectPermission()).isNull();
+ }
+
+ @Test
+ public void key_should_be_set() {
+ try {
+ Transition.builder("").from("OPEN").to("CLOSED").build();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).hasMessage("Transition key must be set");
+ }
+ }
+
+ @Test
+ public void key_should_be_lower_case() {
+ try {
+ Transition.builder("CLOSE").from("OPEN").to("CLOSED").build();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).hasMessage("Transition key must be lower-case");
+ }
+ }
+
+ @Test
+ public void originating_status_should_be_set() {
+ try {
+ Transition.builder("close").from("").to("CLOSED").build();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).hasMessage("Originating status must be set");
+ }
+ }
+
+ @Test
+ public void destination_status_should_be_set() {
+ try {
+ Transition.builder("close").from("OPEN").to("").build();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).hasMessage("Destination status must be set");
+ }
+ }
+
+ @Test
+ public void should_verify_conditions() {
+ DefaultIssue issue = new DefaultIssue();
+ Transition transition = Transition.builder("close")
+ .from("OPEN").to("CLOSED")
+ .conditions(condition1, condition2)
+ .build();
+
+ when(condition1.matches(issue)).thenReturn(true);
+ when(condition2.matches(issue)).thenReturn(false);
+ assertThat(transition.supports(issue)).isFalse();
+
+ when(condition1.matches(issue)).thenReturn(true);
+ when(condition2.matches(issue)).thenReturn(true);
+ assertThat(transition.supports(issue)).isTrue();
+ }
+
+ @Test
+ public void test_equals_and_hashCode() {
+ Transition t1 = Transition.create("resolve", "OPEN", "RESOLVED");
+ Transition t2 = Transition.create("resolve", "REOPENED", "RESOLVED");
+ Transition t3 = Transition.create("confirm", "OPEN", "CONFIRMED");
+
+ assertThat(t1).isNotEqualTo(t2);
+ assertThat(t1).isNotEqualTo(t3);
+ assertThat(t1).isEqualTo(t1);
+
+ assertThat(t1.hashCode()).isEqualTo(t1.hashCode());
+ }
+
+ @Test
+ public void test_toString() {
+ Transition t1 = Transition.create("resolve", "OPEN", "RESOLVED");
+ assertThat(t1.toString()).isEqualTo("OPEN->resolve->RESOLVED");
+ }
+
+ @Test
+ public void test_automatic_transition() {
+ Transition transition = Transition.builder("close")
+ .from("OPEN").to("CLOSED")
+ .automatic()
+ .build();
+ assertThat(transition.automatic()).isTrue();
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/UnsetAssigneeTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/UnsetAssigneeTest.java
new file mode 100644
index 00000000000..c4c88aa3aba
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/UnsetAssigneeTest.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 org.sonar.server.issue.workflow;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.sonar.server.issue.workflow.UnsetAssignee.INSTANCE;
+
+public class UnsetAssigneeTest {
+
+ @Test
+ public void unassign() {
+ Function.Context context = mock(Function.Context.class);
+ INSTANCE.execute(context);
+ verify(context, times(1)).setAssignee(null);
+ }
+}