]> source.dussan.org Git - sonarqube.git/commitdiff
Move IssueUpdater from sonar-core to sonar-server
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Wed, 24 Feb 2016 15:31:10 +0000 (16:31 +0100)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 25 Feb 2016 09:18:41 +0000 (10:18 +0100)
34 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java
server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueLifecycle.java
server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java
server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java
server/sonar-server/src/main/java/org/sonar/server/issue/AddTagsAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java
server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java
server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
server/sonar-server/src/main/java/org/sonar/server/issue/IssueUpdater.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/RemoveTagsAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java
server/sonar-server/src/main/java/org/sonar/server/issue/workflow/FunctionExecutor.java
server/sonar-server/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueLifecycleTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueUpdaterTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java
sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java [deleted file]
sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java [deleted file]

index be57b30ca563466a32f73bedcd5e12a3f05d8569..293c650dafef5330581edb1d29ad1eadd332cc99 100644 (file)
@@ -28,7 +28,7 @@ import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 import org.sonar.server.computation.analysis.AnalysisMetadataHolder;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.scm.ScmInfo;
index cc01fdf86112590398add1898622304b96d30f08..33ab0b5f1d2d18d9f962c0ab67b0cfa2b4228c85 100644 (file)
@@ -25,7 +25,7 @@ import javax.annotation.Nullable;
 import org.sonar.api.issue.Issue;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 import org.sonar.server.issue.workflow.IssueWorkflow;
 import org.sonar.core.util.Uuids;
 import org.sonar.server.computation.analysis.AnalysisMetadataHolder;
index adae0a498a99d66fd20b708534fc992683f90185..3c9d7845be26fe3f993f10aef9875e0e463d0517 100644 (file)
@@ -36,7 +36,7 @@ import javax.annotation.Nullable;
 import org.apache.commons.lang.time.DateUtils;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.FieldDiffs;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 import org.sonar.db.issue.IssueChangeDto;
 import org.sonar.server.computation.period.Period;
 
index 28be9a3128dc7d570ddfa263fc4322d1058453be..a54f6ffc3026d718eef6433047cb751fda74d3b2 100644 (file)
@@ -27,7 +27,6 @@ import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.condition.IsUnResolved;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.api.server.rule.RuleTagFormat;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.user.UserSession;
 
 import java.util.Collection;
index 5f3d41c134c9045fcef459c713f50aadb83f8085..b517e01fbe1ca0bab318ad84dd762e391893470e 100644 (file)
@@ -33,7 +33,6 @@ import org.sonar.api.issue.action.Function;
 import org.sonar.api.server.ServerSide;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.server.properties.ProjectSettingsFactory;
index ff93b503799411cc12d8df133833ada196655246..4e27a00de11ea262dc5c5c9a647d47e12fd26d13 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.server.issue;
 
 import com.google.common.collect.Sets;
 import org.sonar.api.server.ServerSide;
-import org.sonar.core.issue.IssueUpdater;
 
 import java.util.Collection;
 import java.util.Set;
index 38b9ab1eca59545540dda3ad9963d61af3f8d325..19a44bb3d6f2d842196bb033247f4dc62fb2bb63 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.api.server.ServerSide;
 import org.sonar.api.user.User;
 import org.sonar.api.user.UserFinder;
 import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.user.UserSession;
 
 @ServerSide
index d7075a443be5880388c87164647ea38f057cde30..2c56f2332202008fb6fc032ea0848a1fde490289 100644 (file)
@@ -25,7 +25,6 @@ import java.util.Map;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.server.ServerSide;
 import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.user.UserSession;
 
 @ServerSide
index 8f868cac42a4d572b1e77425378c899c969a16fe..e9e7e55faae5bee387b259739849884aa1941bf7 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.api.i18n.I18n;
 import org.sonar.core.issue.FieldDiffs;
 import org.sonar.api.utils.Duration;
 import org.sonar.api.utils.Durations;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.user.UserSession;
 
 import static com.google.common.collect.Lists.newArrayList;
index fc83784aaa77592a3348524a1223a4d1dbb1bf0e..6fb50b2a36f1ad82c80a254b11bdf03dca8570b5 100644 (file)
@@ -22,13 +22,11 @@ package org.sonar.server.issue;
 import com.google.common.base.Objects;
 import com.google.common.base.Strings;
 import org.apache.commons.lang.StringUtils;
-import org.sonar.api.server.ServerSide;
 import org.sonar.api.issue.IssueComment;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.DefaultIssueComment;
 import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.api.utils.System2;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.db.issue.IssueChangeDto;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbClient;
index c9bee6d5f740bb9e720fecfed27f45d0394490ea..09c2c041d2cb3add384e6753fd4e2b20988595b9 100644 (file)
@@ -44,7 +44,6 @@ import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.DefaultIssueBuilder;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.issue.workflow.IssueWorkflow;
 import org.sonar.server.issue.workflow.Transition;
 import org.sonar.db.DbClient;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueUpdater.java
new file mode 100644 (file)
index 0000000..303a430
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Sets;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.time.DateUtils;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.server.rule.RuleTagFormat;
+import org.sonar.api.user.User;
+import org.sonar.api.utils.Duration;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.DefaultIssueComment;
+import org.sonar.core.issue.IssueChangeContext;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+/**
+ * Updates issue fields and chooses if changes must be kept in history.
+ */
+@ServerSide
+public class IssueUpdater {
+
+  public static final String UNUSED = "";
+  public static final String SEVERITY = "severity";
+  public static final String ASSIGNEE = "assignee";
+  public static final String RESOLUTION = "resolution";
+  public static final String STATUS = "status";
+  public static final String AUTHOR = "author";
+  public static final String ACTION_PLAN = "actionPlan";
+  public static final String TECHNICAL_DEBT = "technicalDebt";
+  public static final String TAGS = "tags";
+
+  private static final Joiner CHANGELOG_TAG_JOINER = Joiner.on(" ").skipNulls();
+
+  public boolean setSeverity(DefaultIssue issue, String severity, IssueChangeContext context) {
+    if (issue.manualSeverity()) {
+      throw new IllegalStateException("Severity can't be changed");
+    }
+    if (!Objects.equal(severity, issue.severity())) {
+      issue.setFieldChange(context, SEVERITY, issue.severity(), severity);
+      issue.setSeverity(severity);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setPastSeverity(DefaultIssue issue, @Nullable String previousSeverity, IssueChangeContext context) {
+    String currentSeverity = issue.severity();
+    issue.setSeverity(previousSeverity);
+    return setSeverity(issue, currentSeverity, context);
+  }
+
+  public boolean setManualSeverity(DefaultIssue issue, String severity, IssueChangeContext context) {
+    if (!issue.manualSeverity() || !Objects.equal(severity, issue.severity())) {
+      issue.setFieldChange(context, SEVERITY, issue.severity(), severity);
+      issue.setSeverity(severity);
+      issue.setManualSeverity(true);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      issue.setSendNotifications(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean assign(DefaultIssue issue, @Nullable User user, IssueChangeContext context) {
+    String sanitizedAssignee = null;
+    if (user != null) {
+      sanitizedAssignee = StringUtils.defaultIfBlank(user.login(), null);
+    }
+    if (!Objects.equal(sanitizedAssignee, issue.assignee())) {
+      String newAssigneeName = user != null ? user.name() : null;
+      issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssigneeName);
+      issue.setAssignee(sanitizedAssignee);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      issue.setSendNotifications(true);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Used to set the assignee when it was null
+   */
+  public boolean setNewAssignee(DefaultIssue issue, @Nullable String newAssignee, IssueChangeContext context) {
+    if (newAssignee == null) {
+      return false;
+    }
+    checkState(issue.assignee() == null, "It's not possible to update the assignee with this method, please use assign()");
+    issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssignee);
+    issue.setAssignee(newAssignee);
+    issue.setUpdateDate(context.date());
+    issue.setChanged(true);
+    issue.setSendNotifications(true);
+    return true;
+  }
+
+  public boolean setLine(DefaultIssue issue, @Nullable Integer line) {
+    if (!Objects.equal(line, issue.line())) {
+      issue.setLine(line);
+      issue.setChanged(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setPastLine(DefaultIssue issue, @Nullable Integer previousLine) {
+    Integer currentLine = issue.line();
+    issue.setLine(previousLine);
+    return setLine(issue, currentLine);
+  }
+
+  public boolean setLocations(DefaultIssue issue, @Nullable Object locations) {
+    if (!Objects.equal(locations, issue.getLocations())) {
+      issue.setLocations(locations);
+      issue.setChanged(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setPastLocations(DefaultIssue issue, @Nullable Object previousLocations) {
+    Object currentLocations = issue.getLocations();
+    issue.setLocations(previousLocations);
+    return setLocations(issue, currentLocations);
+
+  }
+
+  public boolean setResolution(DefaultIssue issue, @Nullable String resolution, IssueChangeContext context) {
+    if (!Objects.equal(resolution, issue.resolution())) {
+      issue.setFieldChange(context, RESOLUTION, issue.resolution(), resolution);
+      issue.setResolution(resolution);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      issue.setSendNotifications(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setStatus(DefaultIssue issue, String status, IssueChangeContext context) {
+    if (!Objects.equal(status, issue.status())) {
+      issue.setFieldChange(context, STATUS, issue.status(), status);
+      issue.setStatus(status);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      issue.setSendNotifications(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setAuthorLogin(DefaultIssue issue, @Nullable String authorLogin, IssueChangeContext context) {
+    if (!Objects.equal(authorLogin, issue.authorLogin())) {
+      issue.setFieldChange(context, AUTHOR, issue.authorLogin(), authorLogin);
+      issue.setAuthorLogin(authorLogin);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      // do not send notifications to prevent spam when installing the developer cockpit plugin
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Used to set the author when it was null
+   */
+  public boolean setNewAuthor(DefaultIssue issue, @Nullable String newAuthorLogin, IssueChangeContext context) {
+    if (isNullOrEmpty(newAuthorLogin)) {
+      return false;
+    }
+    checkState(issue.authorLogin() == null, "It's not possible to update the author with this method, please use setAuthorLogin()");
+    issue.setFieldChange(context, AUTHOR, null, newAuthorLogin);
+    issue.setAuthorLogin(newAuthorLogin);
+    issue.setUpdateDate(context.date());
+    issue.setChanged(true);
+    // do not send notifications to prevent spam when installing the developer cockpit plugin
+    return true;
+  }
+
+  public boolean setMessage(DefaultIssue issue, @Nullable String s, IssueChangeContext context) {
+    if (!Objects.equal(s, issue.message())) {
+      issue.setMessage(s);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setPastMessage(DefaultIssue issue, @Nullable String previousMessage, IssueChangeContext context) {
+    String currentMessage = issue.message();
+    issue.setMessage(previousMessage);
+    return setMessage(issue, currentMessage, context);
+  }
+
+  public void addComment(DefaultIssue issue, String text, IssueChangeContext context) {
+    issue.addComment(DefaultIssueComment.create(issue.key(), context.login(), text));
+    issue.setUpdateDate(context.date());
+    issue.setChanged(true);
+  }
+
+  public void setCloseDate(DefaultIssue issue, @Nullable Date d, IssueChangeContext context) {
+    Date dateWithoutMilliseconds = d == null ? null : DateUtils.truncate(d, Calendar.SECOND);
+    if (!Objects.equal(dateWithoutMilliseconds, issue.closeDate())) {
+      issue.setCloseDate(d);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+    }
+  }
+
+  public boolean setEffortToFix(DefaultIssue issue, @Nullable Double d, IssueChangeContext context) {
+    if (!Objects.equal(d, issue.effortToFix())) {
+      issue.setEffortToFix(d);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      // Do not send notifications to prevent spam when installing the SQALE plugin,
+      // and do not complete the changelog (for the moment)
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setPastEffortToFix(DefaultIssue issue, @Nullable Double previousEffort, IssueChangeContext context) {
+    Double currentEffort = issue.effortToFix();
+    issue.setEffortToFix(previousEffort);
+    return setEffortToFix(issue, currentEffort, context);
+  }
+
+  public boolean setTechnicalDebt(DefaultIssue issue, @Nullable Duration value, IssueChangeContext context) {
+    Duration oldValue = issue.debt();
+    if (!Objects.equal(value, oldValue)) {
+      issue.setDebt(value != null ? value : null);
+      issue.setFieldChange(context, TECHNICAL_DEBT, oldValue != null ? oldValue.toMinutes() : null, value != null ? value.toMinutes() : null);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable Duration previousTechnicalDebt, IssueChangeContext context) {
+    Duration currentTechnicalDebt = issue.debt();
+    issue.setDebt(previousTechnicalDebt);
+    return setTechnicalDebt(issue, currentTechnicalDebt, context);
+  }
+
+  public boolean setAttribute(DefaultIssue issue, String key, @Nullable String value, IssueChangeContext context) {
+    String oldValue = issue.attribute(key);
+    if (!Objects.equal(oldValue, value)) {
+      issue.setFieldChange(context, key, oldValue, value);
+      issue.setAttribute(key, value);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean plan(DefaultIssue issue, @Nullable ActionPlan actionPlan, IssueChangeContext context) {
+    String sanitizedActionPlanKey = null;
+    if (actionPlan != null) {
+      sanitizedActionPlanKey = StringUtils.defaultIfBlank(actionPlan.key(), null);
+    }
+    if (!Objects.equal(sanitizedActionPlanKey, issue.actionPlanKey())) {
+      String newActionPlanName = actionPlan != null ? actionPlan.name() : null;
+      issue.setFieldChange(context, ACTION_PLAN, UNUSED, newActionPlanName);
+      issue.setActionPlanKey(sanitizedActionPlanKey);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      issue.setSendNotifications(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setProject(DefaultIssue issue, String projectKey, IssueChangeContext context) {
+    if (!Objects.equal(projectKey, issue.projectKey())) {
+      issue.setProjectKey(projectKey);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      return true;
+    }
+    return false;
+  }
+
+  public boolean setPastProject(DefaultIssue issue, String previousKey, IssueChangeContext context) {
+    String currentProjectKey = issue.projectKey();
+    issue.setProjectKey(previousKey);
+    return setProject(issue, currentProjectKey, context);
+  }
+
+  public boolean setTags(DefaultIssue issue, Collection<String> tags, IssueChangeContext context) {
+    Set<String> newTags = Sets.newHashSet(Collections2.transform(
+      Collections2.filter(tags, new Predicate<String>() {
+        @Override
+        public boolean apply(@Nullable String tag) {
+          return tag != null && !tag.isEmpty();
+        }
+      }), new Function<String, String>() {
+        @Override
+        public String apply(String tag) {
+          String lowerCaseTag = tag.toLowerCase();
+          RuleTagFormat.validate(lowerCaseTag);
+          return lowerCaseTag;
+        }
+      }));
+
+    Set<String> oldTags = Sets.newHashSet(issue.tags());
+
+    if (!oldTags.equals(newTags)) {
+      issue.setFieldChange(context, TAGS,
+        oldTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(oldTags),
+        newTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(newTags));
+      issue.setTags(newTags);
+      issue.setUpdateDate(context.date());
+      issue.setChanged(true);
+      issue.setSendNotifications(true);
+      return true;
+    }
+    return false;
+  }
+
+}
index e86e77d1fd23d85116fdc031731edcd35464cf0e..7d82e9be1510b3b641f9f55aad69e11b086a90d1 100644 (file)
@@ -27,7 +27,6 @@ import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.condition.IsUnResolved;
 import org.sonar.api.server.ServerSide;
 import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.user.UserSession;
 
index 5798428b90fb64a7eb129a6c81e311d2ba01a481..c9cb09c465b2ed076b35c04fa6a8e4016e341765 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.server.issue;
 
 import com.google.common.collect.Sets;
 import org.sonar.api.server.ServerSide;
-import org.sonar.core.issue.IssueUpdater;
 
 import java.util.Collection;
 import java.util.Set;
index 9bc86b568eb2c6178fd1d79c1d749d67ae7a537a..75656f961adc6f51ff8e1eeac4dca1ec9dc087d6 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.api.issue.condition.IsUnResolved;
 import org.sonar.api.server.ServerSide;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.user.UserSession;
 
 @ServerSide
index 4ee93f6ded3ed32360f790a3b819f294f2da4bb2..f425dd87295d145a71f7d18281db67364d7a968b 100644 (file)
@@ -33,7 +33,7 @@ import org.sonar.core.issue.ActionPlanStats;
 import org.sonar.core.issue.DefaultActionPlan;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ResourceDao;
index 9b9c88b303ace9a09f457e11e9fe9ca0d876c90b..392bb204fd7bc3a6ca5f06460c78327976193486 100644 (file)
@@ -25,7 +25,7 @@ import org.sonar.api.server.ServerSide;
 import org.sonar.api.user.User;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 
 @ServerSide
 public class FunctionExecutor {
index bb65c579d524b975396ea5a5dda8cc103a60918e..b46048b6d66d626849eb566cb2f55a07fe9f42a0 100644 (file)
@@ -29,7 +29,7 @@ import org.sonar.api.server.ServerSide;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 
 @ServerSide
 public class IssueWorkflow implements Startable {
index 9236429f5babf26a3c3e9a04da5654bda8cc6644..a6f46242d5f69d6ba2ccdd42225bc8c8e7244c90 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.api.rules.AnnotationRuleParser;
 import org.sonar.api.rules.XMLRuleParser;
 import org.sonar.api.server.rule.RulesDefinitionXmlLoader;
 import org.sonar.core.component.DefaultResourceTypes;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 import org.sonar.server.issue.workflow.FunctionExecutor;
 import org.sonar.server.issue.workflow.IssueWorkflow;
 import org.sonar.core.timemachine.Periods;
index 1abcbc0d81b4f433b4e9814816ed52755bb20d8e..5a929c0d1492c6bab7191712eab0b7b32e16dcdf 100644 (file)
@@ -23,7 +23,7 @@ import org.junit.Test;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 import org.sonar.server.computation.analysis.AnalysisMetadataHolderRule;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.scm.Changeset;
index 844d0ff8bbf1795847f134d3c7979dc10236581d..f9957a41dce2e9bca408de1452aa4e0366680f32 100644 (file)
@@ -25,7 +25,7 @@ import org.junit.Test;
 import org.sonar.api.utils.Duration;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 import org.sonar.server.issue.workflow.IssueWorkflow;
 import org.sonar.db.protobuf.DbCommons;
 import org.sonar.db.protobuf.DbIssues;
index dac1cf20a820a1c824b14c0a9f2c7ce80dc5d9d8..ed6f5695b32450cb542160c196c692f0da9a2681 100644 (file)
@@ -30,7 +30,6 @@ import org.sonar.api.issue.action.Function;
 import org.sonar.api.issue.condition.Condition;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
index 764afc96b86b5e207e34b07909dfd8a5260b11a5..ec27342bf6ca97bb224e353cb3854fbdf94e1214 100644 (file)
@@ -27,7 +27,6 @@ import org.junit.rules.ExpectedException;
 import org.mockito.Matchers;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
 
 import java.util.Collection;
 import java.util.Map;
index b5a3c05a4c298cb3d829fe455fd4800c732d93da..e9462422ecd7a9c5d8d27d689880811b438793b0 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.api.user.User;
 import org.sonar.api.user.UserFinder;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.core.user.DefaultUser;
 import org.sonar.server.user.ThreadLocalUserSession;
 
index 95a93d5d25fddde06fe63f4136a68a17a017b612..dea9d87c82c8a6f882e1dca17d7c8d6c2abe00fc 100644 (file)
@@ -26,7 +26,6 @@ import org.junit.Test;
 import org.sonar.api.issue.Issue;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.tester.AnonymousMockUserSession;
 
 import static com.google.common.collect.Maps.newHashMap;
index c754980f12a6e436451863fbc71756f5d836daf9..c02a79c08eae7784a4f166c9b27d94bd19b79dc8 100644 (file)
@@ -30,7 +30,6 @@ import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.DefaultIssueComment;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueUpdaterTest.java
new file mode 100644 (file)
index 0000000..b536a6d
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.Date;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.api.user.User;
+import org.sonar.api.utils.Duration;
+import org.sonar.core.issue.DefaultActionPlan;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.core.user.DefaultUser;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.issue.IssueUpdater.ACTION_PLAN;
+import static org.sonar.server.issue.IssueUpdater.ASSIGNEE;
+import static org.sonar.server.issue.IssueUpdater.RESOLUTION;
+import static org.sonar.server.issue.IssueUpdater.SEVERITY;
+import static org.sonar.server.issue.IssueUpdater.STATUS;
+import static org.sonar.server.issue.IssueUpdater.TECHNICAL_DEBT;
+import static org.sonar.server.issue.IssueUpdater.UNUSED;
+
+public class IssueUpdaterTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  DefaultIssue issue = new DefaultIssue();
+  IssueChangeContext context = IssueChangeContext.createUser(new Date(), "emmerik");
+
+  IssueUpdater updater;
+
+  @Before
+  public void setUp() {
+    updater = new IssueUpdater();
+  }
+
+  @Test
+  public void assign() {
+    User user = new DefaultUser().setLogin("emmerik").setName("Emmerik");
+
+    boolean updated = updater.assign(issue, user, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.assignee()).isEqualTo("emmerik");
+    assertThat(issue.mustSendNotifications()).isTrue();
+    FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
+    assertThat(diff.oldValue()).isEqualTo(UNUSED);
+    assertThat(diff.newValue()).isEqualTo("Emmerik");
+  }
+
+  @Test
+  public void unassign() {
+    issue.setAssignee("morgan");
+    boolean updated = updater.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() {
+    User user = new DefaultUser().setLogin("emmerik").setName("Emmerik");
+
+    issue.setAssignee("morgan");
+    boolean updated = updater.assign(issue, user, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.assignee()).isEqualTo("emmerik");
+    assertThat(issue.mustSendNotifications()).isTrue();
+    FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
+    assertThat(diff.oldValue()).isEqualTo(UNUSED);
+    assertThat(diff.newValue()).isEqualTo("Emmerik");
+  }
+
+  @Test
+  public void not_change_assignee() {
+    User user = new DefaultUser().setLogin("morgan").setName("Morgan");
+
+    issue.setAssignee("morgan");
+    boolean updated = updater.assign(issue, user, context);
+    assertThat(updated).isFalse();
+    assertThat(issue.currentChange()).isNull();
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void set_new_assignee() throws Exception {
+    boolean updated = updater.setNewAssignee(issue, "simon", context);
+    assertThat(updated).isTrue();
+    assertThat(issue.assignee()).isEqualTo("simon");
+    assertThat(issue.mustSendNotifications()).isTrue();
+    FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
+    assertThat(diff.oldValue()).isEqualTo(UNUSED);
+    assertThat(diff.newValue()).isEqualTo("simon");
+  }
+
+  @Test
+  public void not_set_new_assignee_if_new_assignee_is_null() throws Exception {
+    boolean updated = updater.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() throws Exception {
+    issue.setAssignee("simon");
+
+    thrown.expect(IllegalStateException.class);
+    thrown.expectMessage("It's not possible to update the assignee with this method, please use assign()");
+    updater.setNewAssignee(issue, "julien", context);
+  }
+
+  @Test
+  public void set_severity() {
+    boolean updated = updater.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 = updater.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 = updater.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 = updater.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 {
+      updater.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 = updater.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 = updater.setManualSeverity(issue, "MINOR", context);
+    assertThat(updated).isFalse();
+    assertThat(issue.currentChange()).isNull();
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void set_line() {
+    boolean updated = updater.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 = updater.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 = updater.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 = updater.setLocations(issue, "[1-4]");
+    assertThat(updated).isTrue();
+    assertThat(issue.getLocations()).isEqualTo("[1-4]");
+    assertThat(issue.currentChange()).isNull();
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void do_not_change_locations() {
+    issue.setLocations("[1-3]");
+    boolean updated = updater.setLocations(issue, "[1-3]");
+    assertThat(updated).isFalse();
+    assertThat(issue.getLocations()).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 = updater.setLocations(issue, "[1-4]");
+    assertThat(updated).isTrue();
+    assertThat(issue.getLocations()).isEqualTo("[1-4]");
+    assertThat(issue.currentChange()).isNull();
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void set_resolution() {
+    boolean updated = updater.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 = updater.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 = updater.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 = updater.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 = updater.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 = updater.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 = updater.setAttribute(issue, "JIRA", "FOO-123", context);
+    assertThat(updated).isFalse();
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void plan_with_no_existing_plan() {
+    ActionPlan newActionPlan = DefaultActionPlan.create("newName");
+
+    boolean updated = updater.plan(issue, newActionPlan, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.actionPlanKey()).isEqualTo(newActionPlan.key());
+
+    FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN);
+    assertThat(diff.oldValue()).isEqualTo(UNUSED);
+    assertThat(diff.newValue()).isEqualTo("newName");
+    assertThat(issue.mustSendNotifications()).isTrue();
+  }
+
+  @Test
+  public void plan_with_existing_plan() {
+    issue.setActionPlanKey("formerActionPlan");
+
+    ActionPlan newActionPlan = DefaultActionPlan.create("newName").setKey("newKey");
+
+    boolean updated = updater.plan(issue, newActionPlan, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.actionPlanKey()).isEqualTo(newActionPlan.key());
+
+    FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN);
+    assertThat(diff.oldValue()).isEqualTo(UNUSED);
+    assertThat(diff.newValue()).isEqualTo("newName");
+    assertThat(issue.mustSendNotifications()).isTrue();
+  }
+
+  @Test
+  public void unplan() {
+    issue.setActionPlanKey("formerActionPlan");
+
+    boolean updated = updater.plan(issue, null, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.actionPlanKey()).isNull();
+
+    FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN);
+    assertThat(diff.oldValue()).isEqualTo(UNUSED);
+    assertThat(diff.newValue()).isNull();
+    assertThat(issue.mustSendNotifications()).isTrue();
+  }
+
+  @Test
+  public void not_plan_again() {
+    issue.setActionPlanKey("existingActionPlan");
+
+    ActionPlan newActionPlan = DefaultActionPlan.create("existingActionPlan").setKey("existingActionPlan");
+
+    boolean updated = updater.plan(issue, newActionPlan, context);
+    assertThat(updated).isFalse();
+    assertThat(issue.currentChange()).isNull();
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void set_effort_to_fix() {
+    boolean updated = updater.setEffortToFix(issue, 3.14, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.isChanged()).isTrue();
+    assertThat(issue.effortToFix()).isEqualTo(3.14);
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void not_set_effort_to_fix_if_unchanged() {
+    issue.setEffortToFix(3.14);
+    boolean updated = updater.setEffortToFix(issue, 3.14, context);
+    assertThat(updated).isFalse();
+    assertThat(issue.isChanged()).isFalse();
+    assertThat(issue.effortToFix()).isEqualTo(3.14);
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void set_past_effort() {
+    issue.setEffortToFix(3.14);
+    boolean updated = updater.setPastEffortToFix(issue, 1.0, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.effortToFix()).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.setDebt(newDebt);
+    boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.debt()).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.setDebt(newDebt);
+    boolean updated = updater.setPastTechnicalDebt(issue, null, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.debt()).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.setDebt(null);
+    Duration previousDebt = Duration.create(10 * 8 * 60);
+    boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context);
+    assertThat(updated).isTrue();
+    assertThat(issue.debt()).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 = updater.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 = updater.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 = updater.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() throws Exception {
+    boolean updated = updater.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() throws Exception {
+    boolean updated = updater.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() throws Exception {
+    issue.setAuthorLogin("simon");
+
+    thrown.expect(IllegalStateException.class);
+    thrown.expectMessage("It's not possible to update the author with this method, please use setAuthorLogin()");
+    updater.setNewAuthor(issue, "julien", context);
+  }
+
+  @Test
+  public void set_project() {
+    boolean updated = updater.setProject(issue, "sample", context);
+    assertThat(updated).isTrue();
+    assertThat(issue.projectKey()).isEqualTo("sample");
+
+    // do not save change
+    assertThat(issue.currentChange()).isNull();
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void set_past_project() {
+    issue.setProjectKey("new project key");
+    boolean updated = updater.setPastProject(issue, "past project key", context);
+    assertThat(updated).isTrue();
+    assertThat(issue.projectKey()).isEqualTo("new project key");
+
+    // do not save change
+    assertThat(issue.currentChange()).isNull();
+    assertThat(issue.mustSendNotifications()).isFalse();
+  }
+
+  @Test
+  public void not_set_past_project_if_no_change() {
+    issue.setProjectKey("key");
+    boolean updated = updater.setPastProject(issue, "key", context);
+    assertThat(updated).isFalse();
+    assertThat(issue.projectKey()).isEqualTo("key");
+  }
+
+}
index df4974f8567746f1c14bd06190531b1e553d16f7..05e28447b309d33ce993b18d7673ee864c2596d1 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.api.issue.Issue;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.core.issue.DefaultActionPlan;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.user.ThreadLocalUserSession;
 
index 60c8ca20742c90abd074ae2b73d7241fea932a98..efe5914e89c34303f218d2a3100da9c8bf9f0ae8 100644 (file)
@@ -27,7 +27,6 @@ import org.junit.rules.ExpectedException;
 import org.mockito.Matchers;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
 
 import java.util.Collection;
 import java.util.Map;
index 6e5154626de1e92f66e5bb02d4387c5d9eacc819..2a6d4613c2ef5c0b694acfea38e4aae867aa8044 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.api.issue.Issue;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
 import org.sonar.server.tester.AnonymousMockUserSession;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.user.UserSession;
index 7ecda866583762ca4f7349a5782a619422159cba..3076cf36686fd49dc2fd81870b4e3c3a88fa15d1 100644 (file)
@@ -34,7 +34,7 @@ import org.sonar.core.issue.ActionPlanStats;
 import org.sonar.core.issue.DefaultActionPlan;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ResourceDao;
index 1900c2c0ce167f54dd51ddc209fb31014f90863b..abc253ed0797e566e4b5a310a52c0de8ef9305e4 100644 (file)
@@ -32,7 +32,7 @@ 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.core.issue.IssueUpdater;
+import org.sonar.server.issue.IssueUpdater;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java
deleted file mode 100644 (file)
index c691976..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.core.issue;
-
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Sets;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Set;
-import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.time.DateUtils;
-import org.sonar.api.issue.ActionPlan;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.server.rule.RuleTagFormat;
-import org.sonar.api.user.User;
-import org.sonar.api.utils.Duration;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Strings.isNullOrEmpty;
-
-/**
- * Updates issue fields and chooses if changes must be kept in history.
- */
-@ServerSide
-public class IssueUpdater {
-
-  public static final String UNUSED = "";
-  public static final String SEVERITY = "severity";
-  public static final String ASSIGNEE = "assignee";
-  public static final String RESOLUTION = "resolution";
-  public static final String STATUS = "status";
-  public static final String AUTHOR = "author";
-  public static final String ACTION_PLAN = "actionPlan";
-  public static final String TECHNICAL_DEBT = "technicalDebt";
-  public static final String TAGS = "tags";
-
-  private static final Joiner CHANGELOG_TAG_JOINER = Joiner.on(" ").skipNulls();
-
-  public boolean setSeverity(DefaultIssue issue, String severity, IssueChangeContext context) {
-    if (issue.manualSeverity()) {
-      throw new IllegalStateException("Severity can't be changed");
-    }
-    if (!Objects.equal(severity, issue.severity())) {
-      issue.setFieldChange(context, SEVERITY, issue.severity(), severity);
-      issue.setSeverity(severity);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setPastSeverity(DefaultIssue issue, @Nullable String previousSeverity, IssueChangeContext context) {
-    String currentSeverity = issue.severity();
-    issue.setSeverity(previousSeverity);
-    return setSeverity(issue, currentSeverity, context);
-  }
-
-  public boolean setManualSeverity(DefaultIssue issue, String severity, IssueChangeContext context) {
-    if (!issue.manualSeverity() || !Objects.equal(severity, issue.severity())) {
-      issue.setFieldChange(context, SEVERITY, issue.severity(), severity);
-      issue.setSeverity(severity);
-      issue.setManualSeverity(true);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      issue.setSendNotifications(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean assign(DefaultIssue issue, @Nullable User user, IssueChangeContext context) {
-    String sanitizedAssignee = null;
-    if (user != null) {
-      sanitizedAssignee = StringUtils.defaultIfBlank(user.login(), null);
-    }
-    if (!Objects.equal(sanitizedAssignee, issue.assignee())) {
-      String newAssigneeName = user != null ? user.name() : null;
-      issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssigneeName);
-      issue.setAssignee(sanitizedAssignee);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      issue.setSendNotifications(true);
-      return true;
-    }
-    return false;
-  }
-
-  /**
-   * Used to set the assignee when it was null
-   */
-  public boolean setNewAssignee(DefaultIssue issue, @Nullable String newAssignee, IssueChangeContext context) {
-    if (newAssignee == null) {
-      return false;
-    }
-    checkState(issue.assignee() == null, "It's not possible to update the assignee with this method, please use assign()");
-    issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssignee);
-    issue.setAssignee(newAssignee);
-    issue.setUpdateDate(context.date());
-    issue.setChanged(true);
-    issue.setSendNotifications(true);
-    return true;
-  }
-
-  public boolean setLine(DefaultIssue issue, @Nullable Integer line) {
-    if (!Objects.equal(line, issue.line())) {
-      issue.setLine(line);
-      issue.setChanged(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setPastLine(DefaultIssue issue, @Nullable Integer previousLine) {
-    Integer currentLine = issue.line();
-    issue.setLine(previousLine);
-    return setLine(issue, currentLine);
-  }
-
-  public boolean setLocations(DefaultIssue issue, @Nullable Object locations) {
-    if (!Objects.equal(locations, issue.getLocations())) {
-      issue.setLocations(locations);
-      issue.setChanged(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setPastLocations(DefaultIssue issue, @Nullable Object previousLocations) {
-    Object currentLocations = issue.getLocations();
-    issue.setLocations(previousLocations);
-    return setLocations(issue, currentLocations);
-
-  }
-
-  public boolean setResolution(DefaultIssue issue, @Nullable String resolution, IssueChangeContext context) {
-    if (!Objects.equal(resolution, issue.resolution())) {
-      issue.setFieldChange(context, RESOLUTION, issue.resolution(), resolution);
-      issue.setResolution(resolution);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      issue.setSendNotifications(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setStatus(DefaultIssue issue, String status, IssueChangeContext context) {
-    if (!Objects.equal(status, issue.status())) {
-      issue.setFieldChange(context, STATUS, issue.status(), status);
-      issue.setStatus(status);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      issue.setSendNotifications(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setAuthorLogin(DefaultIssue issue, @Nullable String authorLogin, IssueChangeContext context) {
-    if (!Objects.equal(authorLogin, issue.authorLogin())) {
-      issue.setFieldChange(context, AUTHOR, issue.authorLogin(), authorLogin);
-      issue.setAuthorLogin(authorLogin);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      // do not send notifications to prevent spam when installing the developer cockpit plugin
-      return true;
-    }
-    return false;
-  }
-
-  /**
-   * Used to set the author when it was null
-   */
-  public boolean setNewAuthor(DefaultIssue issue, @Nullable String newAuthorLogin, IssueChangeContext context) {
-    if (isNullOrEmpty(newAuthorLogin)) {
-      return false;
-    }
-    checkState(issue.authorLogin() == null, "It's not possible to update the author with this method, please use setAuthorLogin()");
-    issue.setFieldChange(context, AUTHOR, null, newAuthorLogin);
-    issue.setAuthorLogin(newAuthorLogin);
-    issue.setUpdateDate(context.date());
-    issue.setChanged(true);
-    // do not send notifications to prevent spam when installing the developer cockpit plugin
-    return true;
-  }
-
-  public boolean setMessage(DefaultIssue issue, @Nullable String s, IssueChangeContext context) {
-    if (!Objects.equal(s, issue.message())) {
-      issue.setMessage(s);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setPastMessage(DefaultIssue issue, @Nullable String previousMessage, IssueChangeContext context) {
-    String currentMessage = issue.message();
-    issue.setMessage(previousMessage);
-    return setMessage(issue, currentMessage, context);
-  }
-
-  public void addComment(DefaultIssue issue, String text, IssueChangeContext context) {
-    issue.addComment(DefaultIssueComment.create(issue.key(), context.login(), text));
-    issue.setUpdateDate(context.date());
-    issue.setChanged(true);
-  }
-
-  public void setCloseDate(DefaultIssue issue, @Nullable Date d, IssueChangeContext context) {
-    Date dateWithoutMilliseconds = d == null ? null : DateUtils.truncate(d, Calendar.SECOND);
-    if (!Objects.equal(dateWithoutMilliseconds, issue.closeDate())) {
-      issue.setCloseDate(d);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-    }
-  }
-
-  public boolean setEffortToFix(DefaultIssue issue, @Nullable Double d, IssueChangeContext context) {
-    if (!Objects.equal(d, issue.effortToFix())) {
-      issue.setEffortToFix(d);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      // Do not send notifications to prevent spam when installing the SQALE plugin,
-      // and do not complete the changelog (for the moment)
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setPastEffortToFix(DefaultIssue issue, @Nullable Double previousEffort, IssueChangeContext context) {
-    Double currentEffort = issue.effortToFix();
-    issue.setEffortToFix(previousEffort);
-    return setEffortToFix(issue, currentEffort, context);
-  }
-
-  public boolean setTechnicalDebt(DefaultIssue issue, @Nullable Duration value, IssueChangeContext context) {
-    Duration oldValue = issue.debt();
-    if (!Objects.equal(value, oldValue)) {
-      issue.setDebt(value != null ? value : null);
-      issue.setFieldChange(context, TECHNICAL_DEBT, oldValue != null ? oldValue.toMinutes() : null, value != null ? value.toMinutes() : null);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable Duration previousTechnicalDebt, IssueChangeContext context) {
-    Duration currentTechnicalDebt = issue.debt();
-    issue.setDebt(previousTechnicalDebt);
-    return setTechnicalDebt(issue, currentTechnicalDebt, context);
-  }
-
-  public boolean setAttribute(DefaultIssue issue, String key, @Nullable String value, IssueChangeContext context) {
-    String oldValue = issue.attribute(key);
-    if (!Objects.equal(oldValue, value)) {
-      issue.setFieldChange(context, key, oldValue, value);
-      issue.setAttribute(key, value);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean plan(DefaultIssue issue, @Nullable ActionPlan actionPlan, IssueChangeContext context) {
-    String sanitizedActionPlanKey = null;
-    if (actionPlan != null) {
-      sanitizedActionPlanKey = StringUtils.defaultIfBlank(actionPlan.key(), null);
-    }
-    if (!Objects.equal(sanitizedActionPlanKey, issue.actionPlanKey())) {
-      String newActionPlanName = actionPlan != null ? actionPlan.name() : null;
-      issue.setFieldChange(context, ACTION_PLAN, UNUSED, newActionPlanName);
-      issue.setActionPlanKey(sanitizedActionPlanKey);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      issue.setSendNotifications(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setProject(DefaultIssue issue, String projectKey, IssueChangeContext context) {
-    if (!Objects.equal(projectKey, issue.projectKey())) {
-      issue.setProjectKey(projectKey);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      return true;
-    }
-    return false;
-  }
-
-  public boolean setPastProject(DefaultIssue issue, String previousKey, IssueChangeContext context) {
-    String currentProjectKey = issue.projectKey();
-    issue.setProjectKey(previousKey);
-    return setProject(issue, currentProjectKey, context);
-  }
-
-  public boolean setTags(DefaultIssue issue, Collection<String> tags, IssueChangeContext context) {
-    Set<String> newTags = Sets.newHashSet(Collections2.transform(
-      Collections2.filter(tags, new Predicate<String>() {
-        @Override
-        public boolean apply(@Nullable String tag) {
-          return tag != null && !tag.isEmpty();
-        }
-      }), new Function<String, String>() {
-        @Override
-        public String apply(String tag) {
-          String lowerCaseTag = tag.toLowerCase();
-          RuleTagFormat.validate(lowerCaseTag);
-          return lowerCaseTag;
-        }
-      }));
-
-    Set<String> oldTags = Sets.newHashSet(issue.tags());
-
-    if (!oldTags.equals(newTags)) {
-      issue.setFieldChange(context, TAGS,
-        oldTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(oldTags),
-        newTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(newTags));
-      issue.setTags(newTags);
-      issue.setUpdateDate(context.date());
-      issue.setChanged(true);
-      issue.setSendNotifications(true);
-      return true;
-    }
-    return false;
-  }
-
-}
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java
deleted file mode 100644 (file)
index 247e551..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.core.issue;
-
-import java.util.Date;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.issue.ActionPlan;
-import org.sonar.api.user.User;
-import org.sonar.api.utils.Duration;
-import org.sonar.core.user.DefaultUser;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.core.issue.IssueUpdater.ACTION_PLAN;
-import static org.sonar.core.issue.IssueUpdater.ASSIGNEE;
-import static org.sonar.core.issue.IssueUpdater.RESOLUTION;
-import static org.sonar.core.issue.IssueUpdater.SEVERITY;
-import static org.sonar.core.issue.IssueUpdater.STATUS;
-import static org.sonar.core.issue.IssueUpdater.TECHNICAL_DEBT;
-import static org.sonar.core.issue.IssueUpdater.UNUSED;
-
-public class IssueUpdaterTest {
-
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
-  DefaultIssue issue = new DefaultIssue();
-  IssueChangeContext context = IssueChangeContext.createUser(new Date(), "emmerik");
-
-  IssueUpdater updater;
-
-  @Before
-  public void setUp() {
-    updater = new IssueUpdater();
-  }
-
-  @Test
-  public void assign() {
-    User user = new DefaultUser().setLogin("emmerik").setName("Emmerik");
-
-    boolean updated = updater.assign(issue, user, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.assignee()).isEqualTo("emmerik");
-    assertThat(issue.mustSendNotifications()).isTrue();
-    FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
-    assertThat(diff.oldValue()).isEqualTo(UNUSED);
-    assertThat(diff.newValue()).isEqualTo("Emmerik");
-  }
-
-  @Test
-  public void unassign() {
-    issue.setAssignee("morgan");
-    boolean updated = updater.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() {
-    User user = new DefaultUser().setLogin("emmerik").setName("Emmerik");
-
-    issue.setAssignee("morgan");
-    boolean updated = updater.assign(issue, user, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.assignee()).isEqualTo("emmerik");
-    assertThat(issue.mustSendNotifications()).isTrue();
-    FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
-    assertThat(diff.oldValue()).isEqualTo(UNUSED);
-    assertThat(diff.newValue()).isEqualTo("Emmerik");
-  }
-
-  @Test
-  public void not_change_assignee() {
-    User user = new DefaultUser().setLogin("morgan").setName("Morgan");
-
-    issue.setAssignee("morgan");
-    boolean updated = updater.assign(issue, user, context);
-    assertThat(updated).isFalse();
-    assertThat(issue.currentChange()).isNull();
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void set_new_assignee() throws Exception {
-    boolean updated = updater.setNewAssignee(issue, "simon", context);
-    assertThat(updated).isTrue();
-    assertThat(issue.assignee()).isEqualTo("simon");
-    assertThat(issue.mustSendNotifications()).isTrue();
-    FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
-    assertThat(diff.oldValue()).isEqualTo(UNUSED);
-    assertThat(diff.newValue()).isEqualTo("simon");
-  }
-
-  @Test
-  public void not_set_new_assignee_if_new_assignee_is_null() throws Exception {
-    boolean updated = updater.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() throws Exception {
-    issue.setAssignee("simon");
-
-    thrown.expect(IllegalStateException.class);
-    thrown.expectMessage("It's not possible to update the assignee with this method, please use assign()");
-    updater.setNewAssignee(issue, "julien", context);
-  }
-
-  @Test
-  public void set_severity() {
-    boolean updated = updater.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 = updater.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 = updater.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 = updater.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 {
-      updater.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 = updater.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 = updater.setManualSeverity(issue, "MINOR", context);
-    assertThat(updated).isFalse();
-    assertThat(issue.currentChange()).isNull();
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void set_line() {
-    boolean updated = updater.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 = updater.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 = updater.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 = updater.setLocations(issue, "[1-4]");
-    assertThat(updated).isTrue();
-    assertThat(issue.getLocations()).isEqualTo("[1-4]");
-    assertThat(issue.currentChange()).isNull();
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void do_not_change_locations() {
-    issue.setLocations("[1-3]");
-    boolean updated = updater.setLocations(issue, "[1-3]");
-    assertThat(updated).isFalse();
-    assertThat(issue.getLocations()).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 = updater.setLocations(issue, "[1-4]");
-    assertThat(updated).isTrue();
-    assertThat(issue.getLocations()).isEqualTo("[1-4]");
-    assertThat(issue.currentChange()).isNull();
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void set_resolution() {
-    boolean updated = updater.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 = updater.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 = updater.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 = updater.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 = updater.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 = updater.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 = updater.setAttribute(issue, "JIRA", "FOO-123", context);
-    assertThat(updated).isFalse();
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void plan_with_no_existing_plan() {
-    ActionPlan newActionPlan = DefaultActionPlan.create("newName");
-
-    boolean updated = updater.plan(issue, newActionPlan, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.actionPlanKey()).isEqualTo(newActionPlan.key());
-
-    FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN);
-    assertThat(diff.oldValue()).isEqualTo(UNUSED);
-    assertThat(diff.newValue()).isEqualTo("newName");
-    assertThat(issue.mustSendNotifications()).isTrue();
-  }
-
-  @Test
-  public void plan_with_existing_plan() {
-    issue.setActionPlanKey("formerActionPlan");
-
-    ActionPlan newActionPlan = DefaultActionPlan.create("newName").setKey("newKey");
-
-    boolean updated = updater.plan(issue, newActionPlan, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.actionPlanKey()).isEqualTo(newActionPlan.key());
-
-    FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN);
-    assertThat(diff.oldValue()).isEqualTo(UNUSED);
-    assertThat(diff.newValue()).isEqualTo("newName");
-    assertThat(issue.mustSendNotifications()).isTrue();
-  }
-
-  @Test
-  public void unplan() {
-    issue.setActionPlanKey("formerActionPlan");
-
-    boolean updated = updater.plan(issue, null, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.actionPlanKey()).isNull();
-
-    FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN);
-    assertThat(diff.oldValue()).isEqualTo(UNUSED);
-    assertThat(diff.newValue()).isNull();
-    assertThat(issue.mustSendNotifications()).isTrue();
-  }
-
-  @Test
-  public void not_plan_again() {
-    issue.setActionPlanKey("existingActionPlan");
-
-    ActionPlan newActionPlan = DefaultActionPlan.create("existingActionPlan").setKey("existingActionPlan");
-
-    boolean updated = updater.plan(issue, newActionPlan, context);
-    assertThat(updated).isFalse();
-    assertThat(issue.currentChange()).isNull();
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void set_effort_to_fix() {
-    boolean updated = updater.setEffortToFix(issue, 3.14, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.isChanged()).isTrue();
-    assertThat(issue.effortToFix()).isEqualTo(3.14);
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void not_set_effort_to_fix_if_unchanged() {
-    issue.setEffortToFix(3.14);
-    boolean updated = updater.setEffortToFix(issue, 3.14, context);
-    assertThat(updated).isFalse();
-    assertThat(issue.isChanged()).isFalse();
-    assertThat(issue.effortToFix()).isEqualTo(3.14);
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void set_past_effort() {
-    issue.setEffortToFix(3.14);
-    boolean updated = updater.setPastEffortToFix(issue, 1.0, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.effortToFix()).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.setDebt(newDebt);
-    boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.debt()).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.setDebt(newDebt);
-    boolean updated = updater.setPastTechnicalDebt(issue, null, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.debt()).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.setDebt(null);
-    Duration previousDebt = Duration.create(10 * 8 * 60);
-    boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context);
-    assertThat(updated).isTrue();
-    assertThat(issue.debt()).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 = updater.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 = updater.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 = updater.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() throws Exception {
-    boolean updated = updater.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() throws Exception {
-    boolean updated = updater.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() throws Exception {
-    issue.setAuthorLogin("simon");
-
-    thrown.expect(IllegalStateException.class);
-    thrown.expectMessage("It's not possible to update the author with this method, please use setAuthorLogin()");
-    updater.setNewAuthor(issue, "julien", context);
-  }
-
-  @Test
-  public void set_project() {
-    boolean updated = updater.setProject(issue, "sample", context);
-    assertThat(updated).isTrue();
-    assertThat(issue.projectKey()).isEqualTo("sample");
-
-    // do not save change
-    assertThat(issue.currentChange()).isNull();
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void set_past_project() {
-    issue.setProjectKey("new project key");
-    boolean updated = updater.setPastProject(issue, "past project key", context);
-    assertThat(updated).isTrue();
-    assertThat(issue.projectKey()).isEqualTo("new project key");
-
-    // do not save change
-    assertThat(issue.currentChange()).isNull();
-    assertThat(issue.mustSendNotifications()).isFalse();
-  }
-
-  @Test
-  public void not_set_past_project_if_no_change() {
-    issue.setProjectKey("key");
-    boolean updated = updater.setPastProject(issue, "key", context);
-    assertThat(updated).isFalse();
-    assertThat(issue.projectKey()).isEqualTo("key");
-  }
-
-}