diff options
author | Julien Lancelot <julien.lancelot@gmail.com> | 2013-06-24 19:15:33 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@gmail.com> | 2013-06-24 19:15:33 +0200 |
commit | 1296af33c362310fde9d7e6e4357d5a758a323c4 (patch) | |
tree | 77df3e37c40e9b7e2271af757174454873844342 | |
parent | 10ad98a015b17abbb97dfd672958adc348d36e0a (diff) | |
download | sonarqube-1296af33c362310fde9d7e6e4357d5a758a323c4.tar.gz sonarqube-1296af33c362310fde9d7e6e4357d5a758a323c4.zip |
SONAR-3714 Refactor the bulk change service in order to inject Actions instead of having it hardcoded
14 files changed, 779 insertions, 275 deletions
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/Action.java b/sonar-server/src/main/java/org/sonar/server/issue/Action.java new file mode 100644 index 00000000000..eddfba173f8 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/issue/Action.java @@ -0,0 +1,112 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.issue; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import org.sonar.api.ServerComponent; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.condition.Condition; +import org.sonar.api.issue.internal.IssueChangeContext; +import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.user.UserSession; + +import java.util.List; +import java.util.Map; + +import static com.google.common.collect.Lists.newArrayList; + +/** + * @since 3.7 + */ +public abstract class Action implements ServerComponent { + + private final String key; + private final List<Condition> conditions; + + public Action(String key) { + Preconditions.checkArgument(!Strings.isNullOrEmpty(key), "Action key must be set"); + this.key = key; + this.conditions = newArrayList(); + } + + public String key() { + return key; + } + + public Action setConditions(Condition... conditions) { + this.conditions.addAll(ImmutableList.copyOf(conditions)); + return this; + } + + public List<Condition> conditions() { + return conditions; + } + + public boolean supports(Issue issue) { + for (Condition condition : conditions) { + if (!condition.matches(issue)) { + return false; + } + } + return true; + } + + abstract boolean verify(Map<String, Object> properties, UserSession userSession); + + abstract boolean execute(Map<String, Object> properties, Context context); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Action that = (Action) o; + if (!key.equals(that.key)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public String toString() { + return key; + } + + interface Context { + Issue issue(); + + IssueUpdater issueUpdater(); + + IssueChangeContext issueChangeContext(); + } + +} + diff --git a/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java b/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java new file mode 100644 index 00000000000..04a0f82ecd9 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java @@ -0,0 +1,62 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.issue; + +import org.sonar.api.ServerComponent; +import org.sonar.api.issue.condition.IsUnResolved; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.user.UserFinder; +import org.sonar.server.user.UserSession; + +import java.util.Map; + + +public class AssignAction extends Action implements ServerComponent { + + public static final String ASSIGN_ACTION_KEY = "assign"; + + private final UserFinder userFinder; + + public AssignAction(UserFinder userFinder) { + super(ASSIGN_ACTION_KEY); + this.userFinder = userFinder; + super.setConditions(new IsUnResolved()); + } + + @Override + public boolean verify(Map<String, Object> properties, UserSession userSession){ + String assignee = assignee(properties); + if (assignee != null && userFinder.findByLogin(assignee) == null) { + throw new IllegalArgumentException("Unknown user: " + assignee); + } + return true; + } + + @Override + public boolean execute(Map<String, Object> properties, Context context) { + context.issueUpdater().assign((DefaultIssue) context.issue(), assignee(properties), context.issueChangeContext()); + return true; + } + + private String assignee(Map<String, Object> properties){ + return (String) properties.get("assignee"); + } +}
\ No newline at end of file diff --git a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java index 11ebef8736f..a3edbcc346a 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java @@ -591,7 +591,7 @@ public class InternalRubyIssueService implements ServerComponent { public Result<List<Issue>> executeBulkChange(Map<String, Object> props) { Result<List<Issue>> result = Result.of(); try { - IssueBulkChangeQuery issueBulkChangeQuery = toIssueBulkChangeQuery(props); + IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(props); result.set(issueBulkChangeService.execute(issueBulkChangeQuery, UserSession.get())); } catch (Exception e) { result.addError(e.getMessage()); @@ -599,15 +599,4 @@ public class InternalRubyIssueService implements ServerComponent { return result; } - private IssueBulkChangeQuery toIssueBulkChangeQuery(Map<String, Object> props) { - return IssueBulkChangeQuery.builder() - .issueKeys(RubyUtils.toStrings(props.get("issues"))) - .assignee((String) props.get("assignee")) - .plan((String) props.get("plan")) - .severity((String) props.get("severity")) - .transition((String) props.get("transition")) - .comment((String) props.get("comment")) - .build(); - } - }
\ No newline at end of file diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeQuery.java b/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeQuery.java index 6d1a0e4f9fd..42324b1315d 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeQuery.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeQuery.java @@ -17,164 +17,80 @@ * 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.Preconditions; -import org.apache.commons.lang.builder.ReflectionToStringBuilder; +package org.sonar.server.issue; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.server.util.RubyUtils; -import java.util.Collection; -import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.newHashMap; /** * @since 3.7 */ public class IssueBulkChangeQuery { - private static final String ASSIGNEE = "assign"; - private static final String PLAN = "plan"; - private static final String SEVERITY = "set_severity"; - private static final String TRANSITION = "do_transition"; - private static final String COMMENT = "comment"; - - private final Collection<String> actions; - private final Collection<String> issueKeys; - private final String assignee; - private final String plan; - private final String severity; - private final String transition; - private final String comment; - - private IssueBulkChangeQuery(Builder builder) { - this.actions = defaultCollection(builder.actions); - this.issueKeys = defaultCollection(builder.issueKeys); - this.assignee = builder.assignee; - this.plan = builder.plan; - this.severity = builder.severity; - this.transition = builder.transition; - this.comment = builder.comment; - } - - public Collection<String> issueKeys() { - return issueKeys; - } - - @CheckForNull - public String assignee() { - return assignee; - } + private List<String> issues; + private List<String> actions; + private String comment; - @CheckForNull - public String plan() { - return plan; - } + Map<String, Map<String, Object>> propertiesByActions = new HashMap<String, Map<String, Object>>(); - @CheckForNull - public String severity() { - return severity; + public IssueBulkChangeQuery(Map<String, Object> props) { + parse(props, null); } - @CheckForNull - public String transition() { - return transition; + public IssueBulkChangeQuery(Map<String, Object> props, String comment) { + parse(props, comment); } - @CheckForNull - public String comment() { - return comment; - } - - public boolean isOnAssignee() { - return actions.contains(ASSIGNEE); - } - - public boolean isOnActionPlan() { - return actions.contains(PLAN); - } - - public boolean isOnSeverity() { - return actions.contains(SEVERITY); + private void parse(Map<String, Object> props, String comment) { + this.comment = comment; + this.issues = RubyUtils.toStrings(props.get("issues")); + if (issues == null || issues.isEmpty()) { + throw new IllegalArgumentException("Issues must not be empty"); + } + actions = RubyUtils.toStrings(props.get("actions")); + if (actions == null || actions.isEmpty()) { + throw new IllegalArgumentException("At least one action must be provided"); + } + for (String action : actions) { + propertiesByActions.put(action, getActionProps(action, props)); + } } - public boolean isOnTransition() { - return actions.contains(TRANSITION); + public List<String> issues() { + return issues; } - public boolean isOnComment() { - return actions.contains(COMMENT); + public List<String> actions() { + return actions; } - @Override - public String toString() { - return ReflectionToStringBuilder.toString(this); + public Map<String, Object> properties(String action) { + return propertiesByActions.get(action); } - public static Builder builder() { - return new Builder(); + public String getComment() { + return comment; } - public static class Builder { - - private Collection<String> actions = newArrayList(); - private Collection<String> issueKeys; - private String assignee; - private String plan; - private String severity; - private String transition; - private String comment; - - private Builder() { - } - - public Builder issueKeys(@Nullable Collection<String> l) { - this.issueKeys = l; - return this; - } - - public Builder assignee(@Nullable String assignee) { - actions.add(IssueBulkChangeQuery.ASSIGNEE); - this.assignee = assignee; - return this; - } - - public Builder plan(@Nullable String plan) { - actions.add(IssueBulkChangeQuery.PLAN); - this.plan = plan; - return this; - } - - public Builder severity(@Nullable String severity) { - actions.add(IssueBulkChangeQuery.SEVERITY); - this.severity = severity; - return this; + private static Map<String, Object> getActionProps(String action, Map<String, Object> props) { + Map<String, Object> actionProps = newHashMap(); + for (Map.Entry<String, Object> propsEntry : props.entrySet()) { + String key = propsEntry.getKey(); + String actionPrefix = action + "."; + String property = StringUtils.substringAfter(key, actionPrefix); + if (!property.isEmpty()) { + actionProps.put(property, propsEntry.getValue()); + } } - - public Builder transition(@Nullable String transition) { - actions.add(IssueBulkChangeQuery.TRANSITION); - this.transition = transition; - return this; - } - - public Builder comment(@Nullable String comment) { - actions.add(IssueBulkChangeQuery.COMMENT); - this.comment = comment; - return this; - } - - public IssueBulkChangeQuery build() { - Preconditions.checkArgument(issueKeys != null && !issueKeys.isEmpty(), "Issues must not be empty"); - Preconditions.checkArgument(!actions.isEmpty(), "At least one action must be provided"); - - return new IssueBulkChangeQuery(this); - } - } - - private static <T> Collection<T> defaultCollection(@Nullable Collection<T> c) { - return c == null ? Collections.<T>emptyList() : Collections.unmodifiableCollection(c); + props.get(action); + return actionProps; } } diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java b/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java index 8480c44eff1..836eb4774dc 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java @@ -20,20 +20,23 @@ package org.sonar.server.issue; -import com.google.common.base.Strings; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueQuery; import org.sonar.api.issue.IssueQueryResult; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.IssueChangeContext; -import org.sonar.api.user.UserFinder; import org.sonar.api.web.UserRole; import org.sonar.core.issue.IssueNotifications; import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.db.IssueStorage; -import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.server.user.UserSession; +import javax.annotation.CheckForNull; + import java.util.Date; import java.util.List; @@ -41,77 +44,93 @@ import static com.google.common.collect.Lists.newArrayList; public class IssueBulkChangeService { + private static final Logger LOG = LoggerFactory.getLogger(IssueBulkChangeService.class); + private final DefaultIssueFinder issueFinder; - private final IssueWorkflow workflow; private final IssueUpdater issueUpdater; private final IssueStorage issueStorage; private final IssueNotifications issueNotifications; - private final ActionPlanService actionPlanService; - private final UserFinder userFinder; + private final List<Action> actions; - public IssueBulkChangeService(DefaultIssueFinder issueFinder, IssueWorkflow workflow, ActionPlanService actionPlanService, UserFinder userFinder, - IssueUpdater issueUpdater, IssueStorage issueStorage, IssueNotifications issueNotifications) { + public IssueBulkChangeService(DefaultIssueFinder issueFinder, IssueUpdater issueUpdater, IssueStorage issueStorage, IssueNotifications issueNotifications, List<Action> actions) { this.issueFinder = issueFinder; - this.workflow = workflow; this.issueUpdater = issueUpdater; this.issueStorage = issueStorage; this.issueNotifications = issueNotifications; - this.actionPlanService = actionPlanService; - this.userFinder = userFinder; + this.actions = actions; } public List<Issue> execute(IssueBulkChangeQuery issueBulkChangeQuery, UserSession userSession) { List<Issue> issues = newArrayList(); verifyLoggedIn(userSession); - IssueQueryResult issueQueryResult = issueFinder.find(IssueQuery.builder().issueKeys(issueBulkChangeQuery.issueKeys()).requiredRole(UserRole.USER).build()); - - String assignee = issueBulkChangeQuery.assignee(); - if (assignee != null && userFinder.findByLogin(assignee) == null) { - throw new IllegalArgumentException("Unknown user: " + assignee); + for (String actionName : issueBulkChangeQuery.actions()) { + Action action = getAction(actionName); + action.verify(issueBulkChangeQuery.properties(actionName), userSession); } - String actionPlanKey = issueBulkChangeQuery.plan(); - if (!Strings.isNullOrEmpty(actionPlanKey) && actionPlanService.findByKey(actionPlanKey, userSession) == null) { - throw new IllegalArgumentException("Unknown action plan: " + actionPlanKey); - } - String severity = issueBulkChangeQuery.severity(); - String transition = issueBulkChangeQuery.transition(); - String comment = issueBulkChangeQuery.comment(); - - IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); + IssueQueryResult issueQueryResult = issueFinder.find(IssueQuery.builder().issueKeys(issueBulkChangeQuery.issues()).requiredRole(UserRole.USER).build()); + IssueChangeContext issueChangeContext = IssueChangeContext.createUser(new Date(), userSession.login()); for (Issue issue : issueQueryResult.issues()) { - DefaultIssue defaultIssue = (DefaultIssue) issue; - try { - if (issueBulkChangeQuery.isOnAssignee()) { - issueUpdater.assign(defaultIssue, assignee, context); - } - if (issueBulkChangeQuery.isOnActionPlan()) { - issueUpdater.plan(defaultIssue, actionPlanKey, context); + for (String actionName : issueBulkChangeQuery.actions()) { + try { + Action action = getAction(actionName); + ActionContext actionContext = new ActionContext(issue, issueUpdater, issueChangeContext); + if (action.supports(issue) && action.execute(issueBulkChangeQuery.properties(actionName), actionContext)) { + issueStorage.save((DefaultIssue) issue); + issueNotifications.sendChanges((DefaultIssue) issue, issueChangeContext, issueQueryResult); + issues.add(issue); + } + } catch (Exception e) { + // Do nothing, just go to the next issue + LOG.info("An error occur when trying to apply the action : "+ actionName + " on issue : "+ issue.key() + ". This issue has been ignored.", e); } - if (issueBulkChangeQuery.isOnSeverity()) { - issueUpdater.setManualSeverity(defaultIssue, severity, context); - } - if (issueBulkChangeQuery.isOnTransition()) { - workflow.doTransition(defaultIssue, transition, context); - } - if (issueBulkChangeQuery.isOnComment()) { - issueUpdater.addComment(defaultIssue, comment, context); - } - issueStorage.save(defaultIssue); - issueNotifications.sendChanges(defaultIssue, context, issueQueryResult); - issues.add(defaultIssue); - } catch (Exception e) { - // Do nothing, just go to the next issue } } return issues; } + @CheckForNull + private Action getAction(final String actionKey) { + return Iterables.find(actions, new Predicate<Action>() { + @Override + public boolean apply(Action action) { + return action.key().equals(actionKey); + } + }, null); + } + private void verifyLoggedIn(UserSession userSession) { if (!userSession.isLoggedIn()) { // must be logged throw new IllegalStateException("User is not logged in"); } } + + static class ActionContext implements Action.Context { + private final Issue issue; + private final IssueUpdater updater; + private final IssueChangeContext changeContext; + + ActionContext(Issue issue, IssueUpdater updater, IssueChangeContext changeContext) { + this.updater = updater; + this.issue = issue; + this.changeContext = changeContext; + } + + @Override + public Issue issue() { + return issue; + } + + @Override + public IssueUpdater issueUpdater() { + return updater; + } + + @Override + public IssueChangeContext issueChangeContext() { + return changeContext; + } + } } diff --git a/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java b/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java new file mode 100644 index 00000000000..c7d9433c199 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java @@ -0,0 +1,63 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.issue; + +import com.google.common.base.Strings; +import org.sonar.api.ServerComponent; +import org.sonar.api.issue.condition.IsUnResolved; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.server.user.UserSession; + +import java.util.Map; + + +public class PlanAction extends Action implements ServerComponent { + + public static final String PLAN_ACTION_KEY = "plan"; + + private final ActionPlanService actionPlanService; + + public PlanAction(ActionPlanService actionPlanService) { + super(PLAN_ACTION_KEY); + this.actionPlanService = actionPlanService; + super.setConditions(new IsUnResolved()); + } + + @Override + public boolean verify(Map<String, Object> properties, UserSession userSession){ + String actionPlanKey = planKey(properties); + if (!Strings.isNullOrEmpty(actionPlanKey) && actionPlanService.findByKey(actionPlanKey, userSession) == null) { + throw new IllegalArgumentException("Unknown action plan: " + actionPlanKey); + } + return true; + } + + @Override + public boolean execute(Map<String, Object> properties, Context context) { + context.issueUpdater().plan((DefaultIssue) context.issue(), planKey(properties), context.issueChangeContext()); + return true; + } + + private String planKey(Map<String, Object> properties){ + return (String) properties.get("plan"); + } + +}
\ No newline at end of file diff --git a/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java b/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java new file mode 100644 index 00000000000..8f6ccb6c78a --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java @@ -0,0 +1,54 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.issue; + +import org.sonar.api.ServerComponent; +import org.sonar.api.issue.condition.IsUnResolved; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.server.user.UserSession; + +import java.util.Map; + + +public class SetSeverityAction extends Action implements ServerComponent { + + public static final String SET_SEVERITY_ACTION_KEY = "set_severity"; + + public SetSeverityAction() { + super(SET_SEVERITY_ACTION_KEY); + super.setConditions(new IsUnResolved()); + } + + @Override + public boolean verify(Map<String, Object> properties, UserSession userSession) { + return true; + } + + @Override + public boolean execute(Map<String, Object> properties, Context context) { + context.issueUpdater().setSeverity((DefaultIssue) context.issue(), severity(properties), context.issueChangeContext()); + return true; + } + + private String severity(Map<String, Object> properties) { + return (String) properties.get("severity"); + } +}
\ No newline at end of file diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 7d7c5d873c6..8968b3d06e0 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -277,6 +277,10 @@ public final class Platform { servicesContainer.addSingleton(IssueFilterSerializer.class); servicesContainer.addSingleton(IssueFilterService.class); servicesContainer.addSingleton(IssueBulkChangeService.class); + // issues actions + servicesContainer.addSingleton(AssignAction.class); + servicesContainer.addSingleton(PlanAction.class); + servicesContainer.addSingleton(SetSeverityAction.class); // rules servicesContainer.addSingleton(RubyRuleService.class); diff --git a/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java new file mode 100644 index 00000000000..0d7b2af26e6 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java @@ -0,0 +1,91 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.issue; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.IssueChangeContext; +import org.sonar.api.user.UserFinder; +import org.sonar.core.issue.IssueUpdater; +import org.sonar.core.user.DefaultUser; +import org.sonar.server.user.UserSession; + +import java.util.Map; + +import static com.google.common.collect.Maps.newHashMap; +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +public class AssignActionTest { + + private AssignAction action; + + private final UserFinder userFinder = mock(UserFinder.class); + + @Before + public void before(){ + action = new AssignAction(userFinder); + } + + @Test + public void should_execute(){ + String assignee = "arthur"; + + Map<String, Object> properties = newHashMap(); + properties.put("assignee", assignee); + DefaultIssue issue = mock(DefaultIssue.class); + IssueUpdater issueUpdater = mock(IssueUpdater.class); + + Action.Context context = mock(Action.Context.class); + when(context.issueUpdater()).thenReturn(issueUpdater); + when(context.issue()).thenReturn(issue); + + action.execute(properties, context); + verify(issueUpdater).assign(eq(issue), eq(assignee), any(IssueChangeContext.class)); + } + + @Test + public void should_verify_assignee_exists(){ + String assignee = "arthur"; + Map<String, Object> properties = newHashMap(); + properties.put("assignee", assignee); + + when(userFinder.findByLogin(assignee)).thenReturn(new DefaultUser()); + assertThat(action.verify(properties, mock(UserSession.class))).isTrue(); + + when(userFinder.findByLogin(assignee)).thenReturn(null); + try { + action.verify(properties, mock(UserSession.class)); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown user: arthur"); + } + } + + @Test + public void should_support_only_unresolved_issues(){ + assertThat(action.supports(new DefaultIssue().setStatus(Issue.STATUS_OPEN))).isTrue(); + assertThat(action.supports(new DefaultIssue().setStatus(Issue.STATUS_CLOSED))).isTrue(); + } +}
\ No newline at end of file diff --git a/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java index 2363f0c0162..badffa93a4e 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java @@ -537,7 +537,11 @@ public class InternalRubyIssueServiceTest { public void should_execute_bulk_change() { Map<String, Object> params = newHashMap(); params.put("issues", newArrayList("ABCD", "EFGH")); - params.put("assignee", "arthur"); + params.put("actions", newArrayList("do_transition", "assign", "set_severity", "plan")); + params.put("do_transition.transition", "confirm"); + params.put("assign.assignee", "arthur"); + params.put("set_severity.severity", "MINOR"); + params.put("plan.plan", "3.7"); service.executeBulkChange(params); verify(issueBulkChangeService).execute(any(IssueBulkChangeQuery.class), any(UserSession.class)); } @@ -548,7 +552,8 @@ public class InternalRubyIssueServiceTest { Map<String, Object> params = newHashMap(); params.put("issues", newArrayList("ABCD", "EFGH")); - params.put("assignee", "arthur"); + params.put("actions", newArrayList("assign")); + params.put("assign.assignee", "arthur"); Result result = service.executeBulkChange(params); assertThat(result.ok()).isFalse(); assertThat(((Result.Message) result.errors().get(0)).text()).contains("Error"); diff --git a/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeQueryTest.java b/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeQueryTest.java index 55de437d240..3c9a94bb49f 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeQueryTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeQueryTest.java @@ -23,32 +23,69 @@ package org.sonar.server.issue; import org.junit.Test; import java.util.Collections; +import java.util.Map; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.newHashMap; import static org.fest.assertions.Assertions.assertThat; public class IssueBulkChangeQueryTest { @Test - public void test_build(){ - IssueBulkChangeQuery issueBulkChangeQuery = IssueBulkChangeQuery.builder().issueKeys(newArrayList("ABCD", "EFGH")).assignee("perceval").build(); - assertThat(issueBulkChangeQuery.issueKeys()).isNotNull(); - assertThat(issueBulkChangeQuery.assignee()).isNotNull(); + public void should_create_query(){ + Map<String, Object> params = newHashMap(); + params.put("issues", newArrayList("ABCD", "EFGH")); + params.put("actions", newArrayList("do_transition", "assign", "set_severity", "plan")); + params.put("do_transition.transition", "confirm"); + params.put("assign.assignee", "arthur"); + params.put("set_severity.severity", "MINOR"); + params.put("plan.plan", "3.7"); + + IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(params); + assertThat(issueBulkChangeQuery.actions()).containsOnly("do_transition", "assign", "set_severity", "plan"); + assertThat(issueBulkChangeQuery.issues()).containsOnly("ABCD", "EFGH"); + } + + @Test + public void should_get_properties_action(){ + Map<String, Object> params = newHashMap(); + params.put("issues", newArrayList("ABCD", "EFGH")); + params.put("actions", newArrayList("assign")); + params.put("assign.assignee", "arthur"); + + IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(params); + assertThat(issueBulkChangeQuery.properties("assign")).hasSize(1); + assertThat(issueBulkChangeQuery.properties("assign").get("assignee")).isEqualTo("arthur"); } @Test(expected = IllegalArgumentException.class) public void fail_to_build_if_no_issue(){ - IssueBulkChangeQuery.builder().assignee("perceval").build(); + Map<String, Object> params = newHashMap(); + params.put("actions", newArrayList("do_transition", "assign", "set_severity", "plan")); + new IssueBulkChangeQuery(params); } @Test(expected = IllegalArgumentException.class) public void fail_to_build_if_issues_are_empty(){ - IssueBulkChangeQuery.builder().issueKeys(Collections.<String>emptyList()).assignee("perceval").build(); + Map<String, Object> params = newHashMap(); + params.put("issues", Collections.emptyList()); + params.put("actions", newArrayList("do_transition", "assign", "set_severity", "plan")); + new IssueBulkChangeQuery(params); } @Test(expected = IllegalArgumentException.class) public void fail_to_build_if_no_action(){ - IssueBulkChangeQuery.builder().issueKeys(newArrayList("ABCD", "EFGH")).build(); + Map<String, Object> params = newHashMap(); + params.put("issues", Collections.emptyList()); + new IssueBulkChangeQuery(params); + } + + @Test(expected = IllegalArgumentException.class) + public void fail_to_build_if_actions_are_empty(){ + Map<String, Object> params = newHashMap(); + params.put("issues", newArrayList("ABCD", "EFGH")); + params.put("actions", Collections.emptyList()); + new IssueBulkChangeQuery(params); } } diff --git a/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeServiceTest.java index 5237982fcd1..85e04a983a3 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeServiceTest.java @@ -27,19 +27,18 @@ import org.sonar.api.issue.IssueQuery; import org.sonar.api.issue.IssueQueryResult; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.IssueChangeContext; -import org.sonar.api.user.UserFinder; -import org.sonar.core.issue.DefaultActionPlan; import org.sonar.core.issue.IssueNotifications; import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.db.IssueStorage; -import org.sonar.core.issue.workflow.IssueWorkflow; -import org.sonar.core.user.DefaultUser; import org.sonar.server.user.UserSession; import java.util.List; +import java.util.Map; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.newHashMap; import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; @@ -47,12 +46,9 @@ import static org.mockito.Mockito.*; public class IssueBulkChangeServiceTest { private DefaultIssueFinder finder = mock(DefaultIssueFinder.class); - private IssueWorkflow workflow = mock(IssueWorkflow.class); private IssueUpdater issueUpdater = mock(IssueUpdater.class); private IssueStorage issueStorage = mock(IssueStorage.class); private IssueNotifications issueNotifications = mock(IssueNotifications.class); - private ActionPlanService actionPlanService = mock(ActionPlanService.class); - private UserFinder userFinder = mock(UserFinder.class); private IssueQueryResult issueQueryResult = mock(IssueQueryResult.class); private UserSession userSession = mock(UserSession.class); @@ -60,8 +56,11 @@ public class IssueBulkChangeServiceTest { private IssueBulkChangeService service; + private Action action = mock(Action.class); + private List<Action> actions; + @Before - public void before(){ + public void before() { when(userSession.isLoggedIn()).thenReturn(true); when(userSession.userId()).thenReturn(10); when(userSession.login()).thenReturn("fred"); @@ -69,36 +68,28 @@ public class IssueBulkChangeServiceTest { when(finder.find(any(IssueQuery.class))).thenReturn(issueQueryResult); when(issueQueryResult.issues()).thenReturn(newArrayList((Issue) issue)); - service = new IssueBulkChangeService(finder, workflow, actionPlanService, userFinder, issueUpdater, issueStorage, issueNotifications); - } - - @Test - public void should_do_bulk_assign(){ - String assignee = "perceval"; - when(userFinder.findByLogin(assignee)).thenReturn(new DefaultUser()); + when(action.key()).thenReturn("assign"); - IssueBulkChangeQuery issueBulkChangeQuery = IssueBulkChangeQuery.builder().issueKeys(newArrayList(issue.key())).assignee(assignee).build(); - List<Issue> result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result).hasSize(1); + actions = newArrayList(); + actions.add(action); - verify(issueUpdater).assign(eq(issue), eq(assignee), any(IssueChangeContext.class)); - verifyNoMoreInteractions(issueUpdater); - verify(issueStorage).save(eq(issue)); - verifyNoMoreInteractions(issueStorage); - verify(issueNotifications).sendChanges(eq(issue), any(IssueChangeContext.class), eq(issueQueryResult)); - verifyNoMoreInteractions(issueNotifications); + service = new IssueBulkChangeService(finder, issueUpdater, issueStorage, issueNotifications, actions); } @Test - public void should_do_bulk_plan(){ - String actionPlanKey = "EFGH"; - when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(new DefaultActionPlan()); + public void should_execute_bulk_change() { + Map<String, Object> properties = newHashMap(); + properties.put("issues", "ABCD"); + properties.put("actions", "assign"); - IssueBulkChangeQuery issueBulkChangeQuery = IssueBulkChangeQuery.builder().issueKeys(newArrayList(issue.key())).plan(actionPlanKey).build(); + when(action.supports(any(Issue.class))).thenReturn(true); + when(action.execute(anyMap(), any(IssueBulkChangeService.ActionContext.class))).thenReturn(true); + when(action.execute(eq(properties), any(IssueBulkChangeService.ActionContext.class))).thenReturn(true); + + IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties); List<Issue> result = service.execute(issueBulkChangeQuery, userSession); assertThat(result).hasSize(1); - verify(issueUpdater).plan(eq(issue), eq(actionPlanKey), any(IssueChangeContext.class)); verifyNoMoreInteractions(issueUpdater); verify(issueStorage).save(eq(issue)); verifyNoMoreInteractions(issueStorage); @@ -107,73 +98,76 @@ public class IssueBulkChangeServiceTest { } @Test - public void should_do_bulk_change_severity(){ - String severity = "MINOR"; + public void should_not_execute_bulk_if_issue_does_not_support_action() { + Map<String, Object> properties = newHashMap(); + properties.put("issues", "ABCD"); + properties.put("actions", "assign"); + + when(action.supports(any(Issue.class))).thenReturn(false); - IssueBulkChangeQuery issueBulkChangeQuery = IssueBulkChangeQuery.builder().issueKeys(newArrayList(issue.key())).severity(severity).build(); + IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties); List<Issue> result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result).hasSize(1); + assertThat(result).isEmpty(); - verify(issueUpdater).setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class)); - verifyNoMoreInteractions(issueUpdater); - verify(issueStorage).save(eq(issue)); - verifyNoMoreInteractions(issueStorage); - verify(issueNotifications).sendChanges(eq(issue), any(IssueChangeContext.class), eq(issueQueryResult)); - verifyNoMoreInteractions(issueNotifications); + verifyZeroInteractions(issueUpdater); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); } @Test - public void should_do_bulk_transition(){ - String transition = "reopen"; + public void should_not_execute_bulk_if_action_could_not_be_executed_on_issue() { + Map<String, Object> properties = newHashMap(); + properties.put("issues", "ABCD"); + properties.put("actions", "assign"); - when(workflow.doTransition(eq(issue), eq(transition), any(IssueChangeContext.class))).thenReturn(true); + when(action.supports(any(Issue.class))).thenReturn(true); + when(action.execute(anyMap(), any(IssueBulkChangeService.ActionContext.class))).thenReturn(false); - IssueBulkChangeQuery issueBulkChangeQuery = IssueBulkChangeQuery.builder().issueKeys(newArrayList(issue.key())).transition(transition).build(); + IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties); List<Issue> result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result).hasSize(1); + assertThat(result).isEmpty(); - verify(workflow).doTransition(eq(issue), eq(transition), any(IssueChangeContext.class)); - verifyNoMoreInteractions(issueUpdater); - verify(issueStorage).save(eq(issue)); - verifyNoMoreInteractions(issueStorage); - verify(issueNotifications).sendChanges(eq(issue), any(IssueChangeContext.class), eq(issueQueryResult)); - verifyNoMoreInteractions(issueNotifications); + verifyZeroInteractions(issueUpdater); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); } @Test - public void should_do_bulk_comment(){ - String comment = "Bulk change comment"; + public void should_not_execute_bulk_on_unexpected_error() { + Map<String, Object> properties = newHashMap(); + properties.put("issues", "ABCD"); + properties.put("actions", "assign"); + + when(action.supports(any(Issue.class))).thenReturn(true); + doThrow(new RuntimeException("Error")).when(action).execute(anyMap(), any(IssueBulkChangeService.ActionContext.class)); - IssueBulkChangeQuery issueBulkChangeQuery = IssueBulkChangeQuery.builder().issueKeys(newArrayList(issue.key())).comment(comment).build(); + IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties); List<Issue> result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result).hasSize(1); + assertThat(result).isEmpty(); - verify(issueUpdater).addComment(eq(issue), eq(comment), any(IssueChangeContext.class)); - verifyNoMoreInteractions(issueUpdater); - verify(issueStorage).save(eq(issue)); - verifyNoMoreInteractions(issueStorage); - verify(issueNotifications).sendChanges(eq(issue), any(IssueChangeContext.class), eq(issueQueryResult)); - verifyNoMoreInteractions(issueNotifications); + verifyZeroInteractions(issueUpdater); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); } @Test - public void should_ignore_an_issue_if_an_action_fail(){ - when(issueQueryResult.issues()).thenReturn(newArrayList((Issue) issue, new DefaultIssue().setKey("EFGH"))); - - // Bulk change with 2 actions : severity and comment - IssueBulkChangeQuery issueBulkChangeQuery = IssueBulkChangeQuery.builder().issueKeys(newArrayList("ABCD", "EFGH")).severity("MAJOR").comment("Bulk change comment").build(); - - // The first call the change severity is ok, the second will fail - when(issueUpdater.setManualSeverity(any(DefaultIssue.class), eq("MAJOR"),any(IssueChangeContext.class))).thenReturn(true).thenThrow(new RuntimeException("Cant change severity")); - - List<Issue> result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result).hasSize(1); - assertThat(result.get(0).key()).isEqualTo("ABCD"); - - verify(issueStorage).save(eq(issue)); - verifyNoMoreInteractions(issueStorage); - verify(issueNotifications).sendChanges(eq(issue), any(IssueChangeContext.class), eq(issueQueryResult)); - verifyNoMoreInteractions(issueNotifications); + public void should_fail_if_user_not_loggued() { + when(userSession.isLoggedIn()).thenReturn(false); + + Map<String, Object> properties = newHashMap(); + properties.put("issues", "ABCD"); + properties.put("actions", "assign"); + IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties); + try { + service.execute(issueBulkChangeQuery, userSession); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("User is not logged in"); + } + verifyZeroInteractions(issueUpdater); + verifyZeroInteractions(issueUpdater); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); } } diff --git a/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java new file mode 100644 index 00000000000..31c6392136d --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java @@ -0,0 +1,89 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.issue; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.IssueChangeContext; +import org.sonar.core.issue.DefaultActionPlan; +import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.user.UserSession; + +import java.util.Map; + +import static com.google.common.collect.Maps.newHashMap; +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +public class PlanActionTest { + + private PlanAction action; + + private ActionPlanService actionPlanService = mock(ActionPlanService.class); + + @Before + public void before(){ + action = new PlanAction(actionPlanService); + } + + @Test + public void should_execute(){ + String planKey = "ABCD"; + Map<String, Object> properties = newHashMap(); + properties.put("plan", planKey); + DefaultIssue issue = mock(DefaultIssue.class); + IssueUpdater issueUpdater = mock(IssueUpdater.class); + + Action.Context context = mock(Action.Context.class); + when(context.issueUpdater()).thenReturn(issueUpdater); + when(context.issue()).thenReturn(issue); + + action.execute(properties, context); + verify(issueUpdater).plan(eq(issue), eq(planKey), any(IssueChangeContext.class)); + } + + @Test + public void should_verify_action_plan_exists(){ + String planKey = "ABCD"; + Map<String, Object> properties = newHashMap(); + properties.put("plan", planKey); + + when(actionPlanService.findByKey(eq(planKey), any(UserSession.class))).thenReturn(new DefaultActionPlan()); + assertThat(action.verify(properties, mock(UserSession.class))).isTrue(); + + when(actionPlanService.findByKey(eq(planKey), any(UserSession.class))).thenReturn(null); + try { + action.verify(properties, mock(UserSession.class)); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown action plan: ABCD"); + } + } + + @Test + public void should_support_only_unresolved_issues(){ + assertThat(action.supports(new DefaultIssue().setStatus(Issue.STATUS_OPEN))).isTrue(); + assertThat(action.supports(new DefaultIssue().setStatus(Issue.STATUS_CLOSED))).isTrue(); + } +}
\ No newline at end of file diff --git a/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java b/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java new file mode 100644 index 00000000000..82aeb4fd48e --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java @@ -0,0 +1,69 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.issue; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.IssueChangeContext; +import org.sonar.core.issue.IssueUpdater; + +import java.util.Map; + +import static com.google.common.collect.Maps.newHashMap; +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +public class SetSeverityActionTest { + + private SetSeverityAction action; + + @Before + public void before(){ + action = new SetSeverityAction(); + } + + @Test + public void should_execute(){ + String severity = "MINOR"; + Map<String, Object> properties = newHashMap(); + properties.put("severity", severity); + DefaultIssue issue = mock(DefaultIssue.class); + IssueUpdater issueUpdater = mock(IssueUpdater.class); + + Action.Context context = mock(Action.Context.class); + when(context.issueUpdater()).thenReturn(issueUpdater); + when(context.issue()).thenReturn(issue); + + action.execute(properties, context); + verify(issueUpdater).setSeverity(eq(issue), eq(severity), any(IssueChangeContext.class)); + } + + @Test + public void should_support_only_unresolved_issues(){ + assertThat(action.supports(new DefaultIssue().setStatus(Issue.STATUS_OPEN))).isTrue(); + assertThat(action.supports(new DefaultIssue().setStatus(Issue.STATUS_CLOSED))).isTrue(); + } + +} |