From afaa0ecdd013e75d9ffb67bfceba8335f16ba6c4 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 17 Sep 2014 11:24:27 +0200 Subject: SONAR-5531 Provide an implementation based on ES of all "issues relating" web services --- .../sonar/server/issue/DefaultIssueService.java | 339 +++++++++++ .../server/issue/InternalRubyIssueService.java | 14 +- .../java/org/sonar/server/issue/IssueService.java | 312 +--------- .../org/sonar/server/issue/OldIssueService.java | 301 +++++++++ .../sonar/server/issue/ws/IssueActionsWriter.java | 2 +- .../org/sonar/server/issue/ws/SearchAction.java | 17 +- .../sonar/server/platform/ServerComponents.java | 162 +---- .../org/sonar/server/search/IndexSynchronizer.java | 14 +- .../component/ws/ComponentAppActionTest.java | 13 +- .../issue/DefaultIssueServiceMediumTest.java | 391 ++++++++++++ .../issue/InternalRubyDefaultIssueServiceTest.java | 670 +++++++++++++++++++++ .../server/issue/InternalRubyIssueServiceTest.java | 670 --------------------- .../sonar/server/issue/IssueServiceMediumTest.java | 165 ----- .../org/sonar/server/issue/IssueServiceTest.java | 534 ---------------- .../server/issue/OldDefaultIssueServiceTest.java | 538 +++++++++++++++++ .../issue/PublicRubyDefaultIssueServiceTest.java | 132 ++++ .../server/issue/PublicRubyIssueServiceTest.java | 132 ---- .../server/issue/db/IssueBackendMediumTest.java | 3 +- .../org/sonar/server/issue/db/IssueDaoTest.java | 112 +++- .../index/IssueAuthorizationIndexMediumTest.java | 3 +- .../server/issue/index/IssueIndexMediumTest.java | 3 +- .../server/issue/ws/IssueActionsWriterTest.java | 25 +- .../server/issue/ws/IssueSearchActionTest.java | 3 +- .../sonar/server/issue/ws/IssueShowActionTest.java | 3 +- .../java/org/sonar/server/rule/RuleTesting.java | 1 + .../server/search/IndexSynchronizerMediumTest.java | 6 +- .../server/issue/db/IssueDaoTest/get_by_key.xml | 28 + .../server/issue/db/IssueDaoTest/insert-result.xml | 28 + .../issue/db/IssueDaoTest/should_select_all.xml | 80 --- .../server/issue/db/IssueDaoTest/some_issues.xml | 80 +++ .../server/issue/db/IssueDaoTest/update-result.xml | 28 + .../sonar/server/issue/db/IssueDaoTest/update.xml | 28 + .../org/sonar/core/issue/IssueNotifications.java | 11 +- .../java/org/sonar/core/issue/db/IssueDto.java | 14 +- .../sonar/core/permission/PermissionFacade.java | 2 + .../java/org/sonar/api/issue/IssueQueryResult.java | 2 + 36 files changed, 2783 insertions(+), 2083 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/issue/OldIssueService.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueServiceMediumTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyDefaultIssueServiceTest.java delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/OldDefaultIssueServiceTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyDefaultIssueServiceTest.java delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyIssueServiceTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/should_select_all.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/some_issues.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update.xml diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java new file mode 100644 index 00000000000..c19387a2a42 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java @@ -0,0 +1,339 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.issue; + +import com.google.common.base.Objects; +import com.google.common.base.Strings; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.issue.ActionPlan; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueQuery; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.IssueChangeContext; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; +import org.sonar.api.user.User; +import org.sonar.api.user.UserFinder; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.issue.DefaultIssueBuilder; +import org.sonar.core.issue.IssueNotifications; +import org.sonar.core.issue.IssueUpdater; +import org.sonar.core.issue.db.IssueDao; +import org.sonar.core.issue.db.IssueDto; +import org.sonar.core.issue.db.IssueStorage; +import org.sonar.core.issue.workflow.IssueWorkflow; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.preview.PreviewCache; +import org.sonar.core.rule.RuleDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.issue.actionplan.ActionPlanService; +import org.sonar.server.issue.index.IssueIndex; +import org.sonar.server.search.IndexClient; +import org.sonar.server.search.QueryContext; +import org.sonar.server.user.UserSession; + +import javax.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +public class DefaultIssueService implements IssueService { + + private final DbClient dbClient; + private final IndexClient indexClient; + + private final IssueWorkflow workflow; + private final IssueUpdater issueUpdater; + private final IssueStorage issueStorage; + private final IssueNotifications issueNotifications; + private final ActionPlanService actionPlanService; + private final RuleFinder ruleFinder; + private final IssueDao issueDao; + private final UserFinder userFinder; + private final PreviewCache dryRunCache; + + public DefaultIssueService(DbClient dbClient, IndexClient indexClient, + IssueWorkflow workflow, + IssueStorage issueStorage, + IssueUpdater issueUpdater, + IssueNotifications issueNotifications, + ActionPlanService actionPlanService, + RuleFinder ruleFinder, + IssueDao issueDao, + UserFinder userFinder, + PreviewCache dryRunCache) { + this.dbClient = dbClient; + this.indexClient = indexClient; + this.workflow = workflow; + this.issueStorage = issueStorage; + this.issueUpdater = issueUpdater; + this.actionPlanService = actionPlanService; + this.ruleFinder = ruleFinder; + this.issueNotifications = issueNotifications; + this.issueDao = issueDao; + this.userFinder = userFinder; + this.dryRunCache = dryRunCache; + } + + @Override + public List listStatus() { + return workflow.statusKeys(); + } + + /** + * List of available transitions. + *

+ * Never return null, but return an empty list if the issue does not exist. + */ + @Override + public List listTransitions(String issueKey) { + DbSession session = dbClient.openSession(false); + try { + IssueDto issueDto = getByKey(session, issueKey); + return listTransitions(issueDto.toDefaultIssue()); + } finally { + session.close(); + } + } + + /** + * Never return null, but an empty list if the issue does not exist. + * No security check is done since it should already have been done to get the issue + */ + @Override + public List listTransitions(@Nullable Issue issue) { + if (issue == null) { + return Collections.emptyList(); + } + List outTransitions = workflow.outTransitions(issue); + List allowedTransitions = new ArrayList(); + for (Transition transition : outTransitions) { + DefaultIssue defaultIssue = (DefaultIssue) issue; + String projectKey = defaultIssue.projectKey(); + if (StringUtils.isBlank(transition.requiredProjectPermission()) || + (projectKey != null && UserSession.get().hasProjectPermission(transition.requiredProjectPermission(), projectKey))) { + allowedTransitions.add(transition); + } + } + return allowedTransitions; + } + + @Override + public Issue doTransition(String issueKey, String transitionKey) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + IssueDto issueDto = getByKey(session, issueKey); + DefaultIssue defaultIssue = issueDto.toDefaultIssue(); + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + checkTransitionPermission(transitionKey, UserSession.get(), defaultIssue); + if (workflow.doTransition(defaultIssue, transitionKey, context)) { + saveIssue(session, defaultIssue, context); + } + return defaultIssue; + + } finally { + session.close(); + } + } + + private void checkTransitionPermission(String transitionKey, UserSession userSession, DefaultIssue defaultIssue) { + List outTransitions = workflow.outTransitions(defaultIssue); + for (Transition transition : outTransitions) { + String projectKey = defaultIssue.projectKey(); + if (transition.key().equals(transitionKey) && StringUtils.isNotBlank(transition.requiredProjectPermission()) && projectKey != null) { + userSession.checkProjectPermission(transition.requiredProjectPermission(), projectKey); + } + } + } + + @Override + public Issue assign(String issueKey, @Nullable String assignee) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + IssueDto issueDto = getByKey(session, issueKey); + DefaultIssue issue = issueDto.toDefaultIssue(); + User user = null; + if (!Strings.isNullOrEmpty(assignee)) { + user = userFinder.findByLogin(assignee); + if (user == null) { + throw new NotFoundException("Unknown user: " + assignee); + } + } + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.assign(issue, user, context)) { + saveIssue(session, issue, context); + } + return issue; + + } finally { + session.close(); + } + } + + @Override + public Issue plan(String issueKey, @Nullable String actionPlanKey) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + ActionPlan actionPlan = null; + if (!Strings.isNullOrEmpty(actionPlanKey)) { + actionPlan = actionPlanService.findByKey(actionPlanKey, UserSession.get()); + if (actionPlan == null) { + throw new NotFoundException("Unknown action plan: " + actionPlanKey); + } + } + IssueDto issueDto = getByKey(session, issueKey); + DefaultIssue issue = issueDto.toDefaultIssue(); + + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.plan(issue, actionPlan, context)) { + saveIssue(session, issue, context); + } + return issue; + + } finally { + session.close(); + } + } + + @Override + public Issue setSeverity(String issueKey, String severity) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + IssueDto issueDto = getByKey(session, issueKey); + DefaultIssue issue = issueDto.toDefaultIssue(); + UserSession.get().checkProjectPermission(UserRole.ISSUE_ADMIN, issue.projectKey()); + + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.setManualSeverity(issue, severity, context)) { + saveIssue(session, issue, context); + } + return issue; + } finally { + session.close(); + } + } + + @Override + public DefaultIssue createManualIssue(String componentKey, RuleKey ruleKey, @Nullable Integer line, @Nullable String message, @Nullable String severity, + @Nullable Double effortToFix) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + ComponentDto component = dbClient.componentDao().getByKey(session, componentKey); + ComponentDto project = dbClient.componentDao().getRootProjectByKey(componentKey, session); + + UserSession.get().checkProjectPermission(UserRole.USER, project.getKey()); + if (!ruleKey.isManual()) { + throw new IllegalArgumentException("Issues can be created only on rules marked as 'manual': " + ruleKey); + } + Rule rule = getRuleByKey(ruleKey); + + DefaultIssue issue = new DefaultIssueBuilder() + .componentKey(component.getKey()) + .projectKey(project.getKey()) + .line(line) + .message(!Strings.isNullOrEmpty(message) ? message : rule.getName()) + .severity(Objects.firstNonNull(severity, Severity.MAJOR)) + .effortToFix(effortToFix) + .ruleKey(ruleKey) + .reporter(UserSession.get().login()) + .build(); + + Date now = new Date(); + issue.setCreationDate(now); + issue.setUpdateDate(now); + issueStorage.save(issue); + dryRunCache.reportResourceModification(component.getKey()); + return issue; + } finally { + session.close(); + } + } + + // TODO result should be replaced by an aggregation object in IssueIndex + @Override + public RulesAggregation findRulesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { + RulesAggregation rulesAggregation = new RulesAggregation(); + for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey, periodDate, session)) { + rulesAggregation.add(ruleDto); + } + return rulesAggregation; + } + + // TODO result should be replaced by an aggregation object in IssueIndex + @Override + public Multiset findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { + Multiset aggregation = HashMultiset.create(); + for (String severity : issueDao.findSeveritiesByComponent(componentKey, periodDate, session)) { + aggregation.add(severity); + } + return aggregation; + } + + public IssueDto getByKey(DbSession session, String key) { + // Load with index to check permission + indexClient.get(IssueIndex.class).getByKey(key); + return dbClient.issueDao().getByKey(session, key); + } + + private void saveIssue(DbSession session, DefaultIssue issue, IssueChangeContext context) { + issueStorage.save(issue); + issueNotifications.sendChanges(issue, context, + getRuleByKey(issue.ruleKey()), + dbClient.componentDao().getByKey(session, issue.projectKey()), + dbClient.componentDao().getNullableByKey(session, issue.componentKey())); + dryRunCache.reportResourceModification(issue.componentKey()); + } + + private Rule getRuleByKey(RuleKey ruleKey) { + Rule rule = ruleFinder.findByKey(ruleKey); + if (rule == null) { + throw new IllegalArgumentException("Unknown rule: " + ruleKey); + } + return rule; + } + + public org.sonar.server.search.Result search(IssueQuery query, QueryContext options) { + IssueIndex issueIndex = indexClient.get(IssueIndex.class); + return issueIndex.search(query, options); + } + + private void verifyLoggedIn() { + UserSession.get().checkLoggedIn(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java index 1fa162a5dd7..e919ba8f6dc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java @@ -110,11 +110,11 @@ public class InternalRubyIssueService implements ServerComponent { } public List listTransitions(String issueKey) { - return issueService.listTransitions(issueKey, UserSession.get()); + return issueService.listTransitions(issueKey); } public List listTransitions(Issue issue) { - return issueService.listTransitions(issue, UserSession.get()); + return issueService.listTransitions(issue); } public List listStatus() { @@ -149,7 +149,7 @@ public class InternalRubyIssueService implements ServerComponent { public Result doTransition(String issueKey, String transitionKey) { Result result = Result.of(); try { - result.set(issueService.doTransition(issueKey, transitionKey, UserSession.get())); + result.set(issueService.doTransition(issueKey, transitionKey)); } catch (Exception e) { result.addError(e.getMessage()); } @@ -159,7 +159,7 @@ public class InternalRubyIssueService implements ServerComponent { public Result assign(String issueKey, @Nullable String assignee) { Result result = Result.of(); try { - result.set(issueService.assign(issueKey, StringUtils.defaultIfBlank(assignee, null), UserSession.get())); + result.set(issueService.assign(issueKey, StringUtils.defaultIfBlank(assignee, null))); } catch (Exception e) { result.addError(e.getMessage()); } @@ -169,7 +169,7 @@ public class InternalRubyIssueService implements ServerComponent { public Result setSeverity(String issueKey, String severity) { Result result = Result.of(); try { - result.set(issueService.setSeverity(issueKey, severity, UserSession.get())); + result.set(issueService.setSeverity(issueKey, severity)); } catch (Exception e) { result.addError(e.getMessage()); } @@ -179,7 +179,7 @@ public class InternalRubyIssueService implements ServerComponent { public Result plan(String issueKey, @Nullable String actionPlanKey) { Result result = Result.of(); try { - result.set(issueService.plan(issueKey, actionPlanKey, UserSession.get())); + result.set(issueService.plan(issueKey, actionPlanKey)); } catch (Exception e) { result.addError(e.getMessage()); } @@ -235,7 +235,7 @@ public class InternalRubyIssueService implements ServerComponent { if (result.ok()) { DefaultIssue issue = issueService.createManualIssue(componentKey, ruleKey, RubyUtils.toInteger(params.get("line")), params.get("message"), params.get("severity"), - RubyUtils.toDouble(params.get("effortToFix")), UserSession.get()); + RubyUtils.toDouble(params.get("effortToFix"))); result.set(issue); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java index 6ef2e3b252f..c60d205fe8a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java @@ -17,326 +17,44 @@ * 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.annotations.VisibleForTesting; -import com.google.common.base.Objects; -import com.google.common.base.Strings; -import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.api.ServerComponent; -import org.sonar.api.issue.ActionPlan; 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.rule.RuleKey; -import org.sonar.api.rule.Severity; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RuleFinder; -import org.sonar.api.user.User; -import org.sonar.api.user.UserFinder; -import org.sonar.api.web.UserRole; -import org.sonar.core.issue.DefaultIssueBuilder; -import org.sonar.core.issue.IssueNotifications; -import org.sonar.core.issue.IssueUpdater; -import org.sonar.core.issue.db.IssueDao; -import org.sonar.core.issue.db.IssueStorage; -import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.issue.workflow.Transition; import org.sonar.core.persistence.DbSession; -import org.sonar.core.preview.PreviewCache; -import org.sonar.core.resource.ResourceDao; -import org.sonar.core.resource.ResourceDto; -import org.sonar.core.resource.ResourceQuery; -import org.sonar.core.rule.RuleDto; -import org.sonar.core.user.AuthorizationDao; -import org.sonar.server.db.DbClient; -import org.sonar.server.issue.actionplan.ActionPlanService; -import org.sonar.server.issue.index.IssueIndex; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.QueryContext; -import org.sonar.server.user.UserSession; import javax.annotation.Nullable; -import java.util.*; - -/** - * @since 3.6 - */ -public class IssueService implements ServerComponent { - - private static final Logger LOGGER = LoggerFactory.getLogger(IssueService.class); - - private final DbClient dbClient; - private final IndexClient indexClient; - - private final DefaultIssueFinder finder; - private final IssueWorkflow workflow; - private final IssueUpdater issueUpdater; - private final IssueStorage issueStorage; - private final IssueNotifications issueNotifications; - private final ActionPlanService actionPlanService; - private final RuleFinder ruleFinder; - private final ResourceDao resourceDao; - private final IssueDao issueDao; - private final AuthorizationDao authorizationDao; - private final UserFinder userFinder; - private final PreviewCache dryRunCache; - - @Deprecated - @VisibleForTesting - public IssueService(DefaultIssueFinder finder, - IssueWorkflow workflow, - IssueStorage issueStorage, - IssueUpdater issueUpdater, - IssueNotifications issueNotifications, - ActionPlanService actionPlanService, - RuleFinder ruleFinder, - ResourceDao resourceDao, - IssueDao issueDao, - AuthorizationDao authorizationDao, - UserFinder userFinder, - PreviewCache dryRunCache) { - this(null, null, finder, workflow, issueStorage, issueUpdater, issueNotifications, actionPlanService, - ruleFinder, resourceDao, issueDao, authorizationDao, userFinder, dryRunCache); - } - - public IssueService(DbClient dbClient, IndexClient indexClient, - DefaultIssueFinder finder, - IssueWorkflow workflow, - IssueStorage issueStorage, - IssueUpdater issueUpdater, - IssueNotifications issueNotifications, - ActionPlanService actionPlanService, - RuleFinder ruleFinder, - ResourceDao resourceDao, - IssueDao issueDao, - AuthorizationDao authorizationDao, - UserFinder userFinder, - PreviewCache dryRunCache) { - this.dbClient = dbClient; - this.indexClient = indexClient; - this.finder = finder; - this.workflow = workflow; - this.issueStorage = issueStorage; - this.issueUpdater = issueUpdater; - this.actionPlanService = actionPlanService; - this.ruleFinder = ruleFinder; - this.issueNotifications = issueNotifications; - this.resourceDao = resourceDao; - this.issueDao = issueDao; - this.authorizationDao = authorizationDao; - this.userFinder = userFinder; - this.dryRunCache = dryRunCache; - } - - /** - * List of available transitions. - *

- * Never return null, but return an empty list if the issue does not exist. - */ - public List listTransitions(String issueKey, UserSession userSession) { - Issue issue = loadIssue(issueKey).first(); - return listTransitions(issue, userSession); - } +import java.util.Date; +import java.util.List; - /** - * Never return null, but an empty list if the issue does not exist. - * No security check is done since it should already have been done to get the issue - */ - public List listTransitions(@Nullable Issue issue, UserSession userSession) { - if (issue == null) { - return Collections.emptyList(); - } - List outTransitions = workflow.outTransitions(issue); - List allowedTransitions = new ArrayList(); - for (Transition transition : outTransitions) { - DefaultIssue defaultIssue = (DefaultIssue) issue; - String projectKey = defaultIssue.projectKey(); - if (StringUtils.isBlank(transition.requiredProjectPermission()) || - (projectKey != null && userSession.hasProjectPermission(transition.requiredProjectPermission(), projectKey))) { - allowedTransitions.add(transition); - } - } - return allowedTransitions; - } +public interface IssueService extends ServerComponent { + List listStatus(); - public Issue doTransition(String issueKey, String transitionKey, UserSession userSession) { - verifyLoggedIn(userSession); - IssueQueryResult queryResult = loadIssue(issueKey); - DefaultIssue defaultIssue = (DefaultIssue) queryResult.first(); - IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); - checkTransitionPermission(transitionKey, userSession, defaultIssue); - if (workflow.doTransition(defaultIssue, transitionKey, context)) { - issueStorage.save(defaultIssue); - issueNotifications.sendChanges(defaultIssue, context, queryResult); - dryRunCache.reportResourceModification(defaultIssue.componentKey()); - } - return defaultIssue; - } + List listTransitions(String issueKey); - private void checkTransitionPermission(String transitionKey, UserSession userSession, DefaultIssue defaultIssue) { - List outTransitions = workflow.outTransitions(defaultIssue); - for (Transition transition : outTransitions) { - String projectKey = defaultIssue.projectKey(); - if (transition.key().equals(transitionKey) && StringUtils.isNotBlank(transition.requiredProjectPermission()) && projectKey != null) { - userSession.checkProjectPermission(transition.requiredProjectPermission(), projectKey); - } - } - } + List listTransitions(@Nullable Issue issue); - public Issue assign(String issueKey, @Nullable String assignee, UserSession userSession) { - verifyLoggedIn(userSession); - IssueQueryResult queryResult = loadIssue(issueKey); - DefaultIssue issue = (DefaultIssue) queryResult.first(); - User user = null; - if (!Strings.isNullOrEmpty(assignee)) { - user = userFinder.findByLogin(assignee); - if (user == null) { - throw new IllegalArgumentException("Unknown user: " + assignee); - } - } - IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); - if (issueUpdater.assign(issue, user, context)) { - issueStorage.save(issue); - issueNotifications.sendChanges(issue, context, queryResult); - dryRunCache.reportResourceModification(issue.componentKey()); - } - return issue; - } + Issue doTransition(String issueKey, String transitionKey); - public Issue plan(String issueKey, @Nullable String actionPlanKey, UserSession userSession) { - verifyLoggedIn(userSession); - ActionPlan actionPlan = null; - if (!Strings.isNullOrEmpty(actionPlanKey)) { - actionPlan = actionPlanService.findByKey(actionPlanKey, userSession); - if (actionPlan == null) { - throw new IllegalArgumentException("Unknown action plan: " + actionPlanKey); - } - } - IssueQueryResult queryResult = loadIssue(issueKey); - DefaultIssue issue = (DefaultIssue) queryResult.first(); + Issue assign(String issueKey, @Nullable String assignee); - IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); - if (issueUpdater.plan(issue, actionPlan, context)) { - issueStorage.save(issue); - issueNotifications.sendChanges(issue, context, queryResult); - dryRunCache.reportResourceModification(issue.componentKey()); - } - return issue; - } + Issue plan(String issueKey, @Nullable String actionPlanKey); - public Issue setSeverity(String issueKey, String severity, UserSession userSession) { - verifyLoggedIn(userSession); - IssueQueryResult queryResult = loadIssue(issueKey); - DefaultIssue issue = (DefaultIssue) queryResult.first(); - userSession.checkProjectPermission(UserRole.ISSUE_ADMIN, issue.projectKey()); + Issue setSeverity(String issueKey, String severity); - IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); - if (issueUpdater.setManualSeverity(issue, severity, context)) { - issueStorage.save(issue); - issueNotifications.sendChanges(issue, context, queryResult); - dryRunCache.reportResourceModification(issue.componentKey()); - } - return issue; - } - - public DefaultIssue createManualIssue(String componentKey, RuleKey ruleKey, @Nullable Integer line, @Nullable String message, @Nullable String severity, - @Nullable Double effortToFix, UserSession userSession) { - verifyLoggedIn(userSession); - ResourceDto component = resourceDao.getResource(ResourceQuery.create().setKey(componentKey)); - ResourceDto project = resourceDao.getRootProjectByComponentKey(componentKey); - if (component == null || project == null) { - throw new IllegalArgumentException("Unknown component: " + componentKey); - } - if (!authorizationDao.isAuthorizedComponentKey(component.getKey(), userSession.userId(), UserRole.USER)) { - // TODO throw unauthorized - throw new IllegalStateException("User does not have the required role"); - } - if (!ruleKey.isManual()) { - throw new IllegalArgumentException("Issues can be created only on rules marked as 'manual': " + ruleKey); - } - Rule rule = findRule(ruleKey); - - DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder() - .componentKey(component.getKey()) - .projectKey(project.getKey()) - .line(line) - .message(!Strings.isNullOrEmpty(message) ? message : rule.getName()) - .severity(Objects.firstNonNull(severity, Severity.MAJOR)) - .effortToFix(effortToFix) - .ruleKey(ruleKey) - .reporter(UserSession.get().login()) - .build(); - - Date now = new Date(); - issue.setCreationDate(now); - issue.setUpdateDate(now); - issueStorage.save(issue); - dryRunCache.reportResourceModification(component.getKey()); - return issue; - } - - private Rule findRule(RuleKey ruleKey) { - Rule rule = ruleFinder.findByKey(ruleKey); - if (rule == null) { - throw new IllegalArgumentException("Unknown rule: " + ruleKey); - } - return rule; - } - - public IssueQueryResult loadIssue(String issueKey) { - // TODO use IssueIndex for ACL - // TODO load DTO - IssueQueryResult result = finder.find(IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).requiredRole(UserRole.USER).build()); - if (result.issues().size() != 1) { - // TODO throw 404 - throw new IllegalArgumentException("Issue not found: " + issueKey); - } - return result; - } - - public List listStatus() { - return workflow.statusKeys(); - } - - private void verifyLoggedIn(UserSession userSession) { - if (!userSession.isLoggedIn()) { - // must be logged - throw new IllegalStateException("User is not logged in"); - } - } + DefaultIssue createManualIssue(String componentKey, RuleKey ruleKey, @Nullable Integer line, @Nullable String message, @Nullable String severity, + @Nullable Double effortToFix); // TODO result should be replaced by an aggregation object in IssueIndex - public RulesAggregation findRulesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { - RulesAggregation rulesAggregation = new RulesAggregation(); - for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey, periodDate, session)) { - rulesAggregation.add(ruleDto); - } - return rulesAggregation; - } + RulesAggregation findRulesByComponent(String componentKey, @Nullable Date periodDate, DbSession session); // TODO result should be replaced by an aggregation object in IssueIndex - public Multiset findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { - Multiset aggregation = HashMultiset.create(); - for (String severity : issueDao.findSeveritiesByComponent(componentKey, periodDate, session)) { - aggregation.add(severity); - } - return aggregation; - } - - public Issue getByKey(String key) { - return indexClient.get(IssueIndex.class).getByKey(key); - } + Multiset findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session); - public org.sonar.server.search.Result search(IssueQuery query, QueryContext options) { - IssueIndex issueIndex = indexClient.get(IssueIndex.class); - return issueIndex.search(query, options); - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/OldIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/OldIssueService.java new file mode 100644 index 00000000000..107ac341c95 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/OldIssueService.java @@ -0,0 +1,301 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.issue; + +import com.google.common.base.Objects; +import com.google.common.base.Strings; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.issue.ActionPlan; +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.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; +import org.sonar.api.user.User; +import org.sonar.api.user.UserFinder; +import org.sonar.api.web.UserRole; +import org.sonar.core.issue.DefaultIssueBuilder; +import org.sonar.core.issue.IssueNotifications; +import org.sonar.core.issue.IssueUpdater; +import org.sonar.core.issue.db.IssueDao; +import org.sonar.core.issue.db.IssueStorage; +import org.sonar.core.issue.workflow.IssueWorkflow; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.preview.PreviewCache; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.resource.ResourceDto; +import org.sonar.core.resource.ResourceQuery; +import org.sonar.core.rule.RuleDto; +import org.sonar.core.user.AuthorizationDao; +import org.sonar.server.issue.actionplan.ActionPlanService; +import org.sonar.server.user.UserSession; + +import javax.annotation.Nullable; + +import java.util.*; + +/** + * @since 3.6 + */ +public class OldIssueService implements IssueService { + + private final DefaultIssueFinder finder; + private final IssueWorkflow workflow; + private final IssueUpdater issueUpdater; + private final IssueStorage issueStorage; + private final IssueNotifications issueNotifications; + private final ActionPlanService actionPlanService; + private final RuleFinder ruleFinder; + private final ResourceDao resourceDao; + private final IssueDao issueDao; + private final AuthorizationDao authorizationDao; + private final UserFinder userFinder; + private final PreviewCache dryRunCache; + + public OldIssueService( + DefaultIssueFinder finder, + IssueWorkflow workflow, + IssueStorage issueStorage, + IssueUpdater issueUpdater, + IssueNotifications issueNotifications, + ActionPlanService actionPlanService, + RuleFinder ruleFinder, + ResourceDao resourceDao, + IssueDao issueDao, + AuthorizationDao authorizationDao, + UserFinder userFinder, + PreviewCache dryRunCache) { + this.finder = finder; + this.workflow = workflow; + this.issueStorage = issueStorage; + this.issueUpdater = issueUpdater; + this.actionPlanService = actionPlanService; + this.ruleFinder = ruleFinder; + this.issueNotifications = issueNotifications; + this.resourceDao = resourceDao; + this.issueDao = issueDao; + this.authorizationDao = authorizationDao; + this.userFinder = userFinder; + this.dryRunCache = dryRunCache; + } + + /** + * List of available transitions. + *

+ * Never return null, but return an empty list if the issue does not exist. + */ + public List listTransitions(String issueKey) { + Issue issue = loadIssue(issueKey).first(); + return listTransitions(issue); + } + + /** + * Never return null, but an empty list if the issue does not exist. + * No security check is done since it should already have been done to get the issue + */ + public List listTransitions(@Nullable Issue issue) { + if (issue == null) { + return Collections.emptyList(); + } + List outTransitions = workflow.outTransitions(issue); + List allowedTransitions = new ArrayList(); + for (Transition transition : outTransitions) { + DefaultIssue defaultIssue = (DefaultIssue) issue; + String projectKey = defaultIssue.projectKey(); + if (StringUtils.isBlank(transition.requiredProjectPermission()) || + (projectKey != null && UserSession.get().hasProjectPermission(transition.requiredProjectPermission(), projectKey))) { + allowedTransitions.add(transition); + } + } + return allowedTransitions; + } + + public Issue doTransition(String issueKey, String transitionKey) { + verifyLoggedIn(); + IssueQueryResult queryResult = loadIssue(issueKey); + DefaultIssue defaultIssue = (DefaultIssue) queryResult.first(); + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + checkTransitionPermission(transitionKey, UserSession.get(), defaultIssue); + if (workflow.doTransition(defaultIssue, transitionKey, context)) { + issueStorage.save(defaultIssue); + issueNotifications.sendChanges(defaultIssue, context, queryResult); + dryRunCache.reportResourceModification(defaultIssue.componentKey()); + } + return defaultIssue; + } + + private void checkTransitionPermission(String transitionKey, UserSession userSession, DefaultIssue defaultIssue) { + List outTransitions = workflow.outTransitions(defaultIssue); + for (Transition transition : outTransitions) { + String projectKey = defaultIssue.projectKey(); + if (transition.key().equals(transitionKey) && StringUtils.isNotBlank(transition.requiredProjectPermission()) && projectKey != null) { + userSession.checkProjectPermission(transition.requiredProjectPermission(), projectKey); + } + } + } + + public Issue assign(String issueKey, @Nullable String assignee) { + verifyLoggedIn(); + IssueQueryResult queryResult = loadIssue(issueKey); + DefaultIssue issue = (DefaultIssue) queryResult.first(); + User user = null; + if (!Strings.isNullOrEmpty(assignee)) { + user = userFinder.findByLogin(assignee); + if (user == null) { + throw new IllegalArgumentException("Unknown user: " + assignee); + } + } + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.assign(issue, user, context)) { + issueStorage.save(issue); + issueNotifications.sendChanges(issue, context, queryResult); + dryRunCache.reportResourceModification(issue.componentKey()); + } + return issue; + } + + public Issue plan(String issueKey, @Nullable String actionPlanKey) { + verifyLoggedIn(); + ActionPlan actionPlan = null; + if (!Strings.isNullOrEmpty(actionPlanKey)) { + actionPlan = actionPlanService.findByKey(actionPlanKey, UserSession.get()); + if (actionPlan == null) { + throw new IllegalArgumentException("Unknown action plan: " + actionPlanKey); + } + } + IssueQueryResult queryResult = loadIssue(issueKey); + DefaultIssue issue = (DefaultIssue) queryResult.first(); + + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.plan(issue, actionPlan, context)) { + issueStorage.save(issue); + issueNotifications.sendChanges(issue, context, queryResult); + dryRunCache.reportResourceModification(issue.componentKey()); + } + return issue; + } + + public Issue setSeverity(String issueKey, String severity) { + verifyLoggedIn(); + IssueQueryResult queryResult = loadIssue(issueKey); + DefaultIssue issue = (DefaultIssue) queryResult.first(); + UserSession.get().checkProjectPermission(UserRole.ISSUE_ADMIN, issue.projectKey()); + + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.setManualSeverity(issue, severity, context)) { + issueStorage.save(issue); + issueNotifications.sendChanges(issue, context, queryResult); + dryRunCache.reportResourceModification(issue.componentKey()); + } + return issue; + } + + public DefaultIssue createManualIssue(String componentKey, RuleKey ruleKey, @Nullable Integer line, @Nullable String message, @Nullable String severity, + @Nullable Double effortToFix) { + verifyLoggedIn(); + ResourceDto component = resourceDao.getResource(ResourceQuery.create().setKey(componentKey)); + ResourceDto project = resourceDao.getRootProjectByComponentKey(componentKey); + if (component == null || project == null) { + throw new IllegalArgumentException("Unknown component: " + componentKey); + } + if (!authorizationDao.isAuthorizedComponentKey(component.getKey(), UserSession.get().userId(), UserRole.USER)) { + // TODO throw unauthorized + throw new IllegalStateException("User does not have the required role"); + } + if (!ruleKey.isManual()) { + throw new IllegalArgumentException("Issues can be created only on rules marked as 'manual': " + ruleKey); + } + Rule rule = findRule(ruleKey); + + DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder() + .componentKey(component.getKey()) + .projectKey(project.getKey()) + .line(line) + .message(!Strings.isNullOrEmpty(message) ? message : rule.getName()) + .severity(Objects.firstNonNull(severity, Severity.MAJOR)) + .effortToFix(effortToFix) + .ruleKey(ruleKey) + .reporter(UserSession.get().login()) + .build(); + + Date now = new Date(); + issue.setCreationDate(now); + issue.setUpdateDate(now); + issueStorage.save(issue); + dryRunCache.reportResourceModification(component.getKey()); + return issue; + } + + private Rule findRule(RuleKey ruleKey) { + Rule rule = ruleFinder.findByKey(ruleKey); + if (rule == null) { + throw new IllegalArgumentException("Unknown rule: " + ruleKey); + } + return rule; + } + + public IssueQueryResult loadIssue(String issueKey) { + // TODO use IssueIndex for ACL + // TODO load DTO + IssueQueryResult result = finder.find(IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).requiredRole(UserRole.USER).build()); + if (result.issues().size() != 1) { + // TODO throw 404 + throw new IllegalArgumentException("Issue not found: " + issueKey); + } + return result; + } + + public List listStatus() { + return workflow.statusKeys(); + } + + private void verifyLoggedIn() { + if (!UserSession.get().isLoggedIn()) { + // must be logged + throw new IllegalStateException("User is not logged in"); + } + } + + // TODO result should be replaced by an aggregation object in IssueIndex + public RulesAggregation findRulesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { + RulesAggregation rulesAggregation = new RulesAggregation(); + for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey, periodDate, session)) { + rulesAggregation.add(ruleDto); + } + return rulesAggregation; + } + + // TODO result should be replaced by an aggregation object in IssueIndex + public Multiset findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { + Multiset aggregation = HashMultiset.create(); + for (String severity : issueDao.findSeveritiesByComponent(componentKey, periodDate, session)) { + aggregation.add(severity); + } + return aggregation; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java index e4d7cfa04da..4057524f0f8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java @@ -48,7 +48,7 @@ public class IssueActionsWriter implements ServerComponent { public void writeTransitions(Issue issue, JsonWriter json) { json.name("transitions").beginArray(); if (UserSession.get().isLoggedIn()) { - List transitions = issueService.listTransitions(issue, UserSession.get()); + List transitions = issueService.listTransitions(issue); for (Transition transition : transitions) { json.value(transition.key()); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java index 20ceda385ea..30fd11a0cc2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -24,11 +24,7 @@ import com.google.common.base.Strings; import com.google.common.collect.Iterables; import com.google.common.io.Resources; import org.sonar.api.i18n.I18n; -import org.sonar.api.issue.ActionPlan; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.IssueComment; -import org.sonar.api.issue.IssueQuery; -import org.sonar.api.issue.IssueQueryResult; +import org.sonar.api.issue.*; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.server.ws.Request; @@ -45,6 +41,7 @@ import org.sonar.core.persistence.DbSession; import org.sonar.markdown.Markdown; import org.sonar.server.component.DefaultComponentFinder; import org.sonar.server.db.DbClient; +import org.sonar.server.issue.DefaultIssueService; import org.sonar.server.issue.IssueService; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.filter.IssueFilterParameters; @@ -58,13 +55,7 @@ import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import static com.google.common.collect.Lists.newArrayList; @@ -229,7 +220,7 @@ public class SearchAction extends SearchRequestHandler { @Override protected Result doSearch(IssueQuery query, QueryContext context) { - return service.search(query, context); + return ((DefaultIssueService)service).search(query, context); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 9ccca614c62..4e4caaae932 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -51,13 +51,7 @@ import org.sonar.core.measure.db.MeasureFilterDao; import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.permission.PermissionFacade; -import org.sonar.core.persistence.DaoUtils; -import org.sonar.core.persistence.DatabaseVersion; -import org.sonar.core.persistence.DefaultDatabase; -import org.sonar.core.persistence.MyBatis; -import org.sonar.core.persistence.PreviewDatabaseFactory; -import org.sonar.core.persistence.SemaphoreUpdater; -import org.sonar.core.persistence.SemaphoresImpl; +import org.sonar.core.persistence.*; import org.sonar.core.preview.PreviewCache; import org.sonar.core.profiling.Profiling; import org.sonar.core.purge.PurgeProfiler; @@ -83,57 +77,27 @@ import org.sonar.server.activity.index.ActivityNormalizer; import org.sonar.server.activity.ws.ActivitiesWebService; import org.sonar.server.activity.ws.ActivityMapping; import org.sonar.server.authentication.ws.AuthenticationWs; -import org.sonar.server.batch.BatchIndex; -import org.sonar.server.batch.BatchWs; -import org.sonar.server.batch.GlobalReferentialsAction; -import org.sonar.server.batch.ProjectReferentialsAction; -import org.sonar.server.batch.UploadReportAction; +import org.sonar.server.batch.*; import org.sonar.server.charts.ChartFactory; import org.sonar.server.component.ComponentCleanerService; import org.sonar.server.component.DefaultComponentFinder; import org.sonar.server.component.DefaultRubyComponentService; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.component.db.SnapshotDao; -import org.sonar.server.component.ws.ComponentAppAction; -import org.sonar.server.component.ws.ComponentsWs; -import org.sonar.server.component.ws.EventsWs; -import org.sonar.server.component.ws.ProjectsWs; -import org.sonar.server.component.ws.ResourcesWs; +import org.sonar.server.component.ws.*; import org.sonar.server.config.ws.PropertiesWs; import org.sonar.server.db.DatabaseChecker; import org.sonar.server.db.DbClient; import org.sonar.server.db.EmbeddedDatabaseFactory; import org.sonar.server.db.migrations.DatabaseMigrations; import org.sonar.server.db.migrations.DatabaseMigrator; -import org.sonar.server.debt.DebtCharacteristicsXMLImporter; -import org.sonar.server.debt.DebtModelBackup; -import org.sonar.server.debt.DebtModelLookup; -import org.sonar.server.debt.DebtModelOperations; -import org.sonar.server.debt.DebtModelPluginRepository; -import org.sonar.server.debt.DebtModelService; -import org.sonar.server.debt.DebtModelXMLExporter; -import org.sonar.server.debt.DebtRulesXMLImporter; +import org.sonar.server.debt.*; import org.sonar.server.design.FileDesignWidget; import org.sonar.server.design.PackageDesignWidget; import org.sonar.server.duplication.ws.DuplicationsJsonWriter; import org.sonar.server.duplication.ws.DuplicationsParser; import org.sonar.server.duplication.ws.DuplicationsWs; -import org.sonar.server.issue.ActionService; -import org.sonar.server.issue.AssignAction; -import org.sonar.server.issue.CommentAction; -import org.sonar.server.issue.DefaultIssueFinder; -import org.sonar.server.issue.InternalRubyIssueService; -import org.sonar.server.issue.IssueBulkChangeService; -import org.sonar.server.issue.IssueChangelogFormatter; -import org.sonar.server.issue.IssueChangelogService; -import org.sonar.server.issue.IssueCommentService; -import org.sonar.server.issue.IssueService; -import org.sonar.server.issue.IssueStatsFinder; -import org.sonar.server.issue.PlanAction; -import org.sonar.server.issue.PublicRubyIssueService; -import org.sonar.server.issue.ServerIssueStorage; -import org.sonar.server.issue.SetSeverityAction; -import org.sonar.server.issue.TransitionAction; +import org.sonar.server.issue.*; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.actionplan.ActionPlanWs; import org.sonar.server.issue.db.IssueAuthorizationDao; @@ -167,84 +131,22 @@ import org.sonar.server.platform.ws.L10nWs; import org.sonar.server.platform.ws.RestartHandler; import org.sonar.server.platform.ws.ServerWs; import org.sonar.server.platform.ws.SystemWs; -import org.sonar.server.plugins.InstalledPluginReferentialFactory; -import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerExtensionInstaller; -import org.sonar.server.plugins.ServerPluginJarInstaller; -import org.sonar.server.plugins.ServerPluginJarsInstaller; -import org.sonar.server.plugins.ServerPluginRepository; -import org.sonar.server.plugins.UpdateCenterClient; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.server.plugins.*; import org.sonar.server.qualitygate.QgateProjectFinder; import org.sonar.server.qualitygate.QualityGates; import org.sonar.server.qualitygate.RegisterQualityGates; -import org.sonar.server.qualitygate.ws.QGatesAppAction; -import org.sonar.server.qualitygate.ws.QGatesCopyAction; -import org.sonar.server.qualitygate.ws.QGatesCreateAction; -import org.sonar.server.qualitygate.ws.QGatesCreateConditionAction; -import org.sonar.server.qualitygate.ws.QGatesDeleteConditionAction; -import org.sonar.server.qualitygate.ws.QGatesDeselectAction; -import org.sonar.server.qualitygate.ws.QGatesDestroyAction; -import org.sonar.server.qualitygate.ws.QGatesListAction; -import org.sonar.server.qualitygate.ws.QGatesRenameAction; -import org.sonar.server.qualitygate.ws.QGatesSearchAction; -import org.sonar.server.qualitygate.ws.QGatesSelectAction; -import org.sonar.server.qualitygate.ws.QGatesSetAsDefaultAction; -import org.sonar.server.qualitygate.ws.QGatesShowAction; -import org.sonar.server.qualitygate.ws.QGatesUnsetDefaultAction; -import org.sonar.server.qualitygate.ws.QGatesUpdateConditionAction; -import org.sonar.server.qualitygate.ws.QGatesWs; -import org.sonar.server.qualityprofile.BuiltInProfiles; -import org.sonar.server.qualityprofile.QProfileBackuper; -import org.sonar.server.qualityprofile.QProfileCopier; -import org.sonar.server.qualityprofile.QProfileExporters; -import org.sonar.server.qualityprofile.QProfileFactory; -import org.sonar.server.qualityprofile.QProfileLoader; -import org.sonar.server.qualityprofile.QProfileLookup; -import org.sonar.server.qualityprofile.QProfileProjectLookup; -import org.sonar.server.qualityprofile.QProfileProjectOperations; -import org.sonar.server.qualityprofile.QProfileRepositoryExporter; -import org.sonar.server.qualityprofile.QProfileReset; -import org.sonar.server.qualityprofile.QProfileService; -import org.sonar.server.qualityprofile.QProfiles; -import org.sonar.server.qualityprofile.RegisterQualityProfiles; -import org.sonar.server.qualityprofile.RuleActivator; -import org.sonar.server.qualityprofile.RuleActivatorContextFactory; +import org.sonar.server.qualitygate.ws.*; +import org.sonar.server.qualityprofile.*; import org.sonar.server.qualityprofile.db.ActiveRuleDao; import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer; -import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions; -import org.sonar.server.qualityprofile.ws.ProfilesWs; -import org.sonar.server.qualityprofile.ws.QProfileRestoreBuiltInAction; -import org.sonar.server.qualityprofile.ws.QProfilesWs; -import org.sonar.server.qualityprofile.ws.RuleActivationActions; -import org.sonar.server.rule.DefaultRuleFinder; -import org.sonar.server.rule.DeprecatedRulesDefinition; -import org.sonar.server.rule.RegisterRules; -import org.sonar.server.rule.RubyRuleService; -import org.sonar.server.rule.RuleCreator; -import org.sonar.server.rule.RuleDefinitionsLoader; -import org.sonar.server.rule.RuleDeleter; -import org.sonar.server.rule.RuleOperations; -import org.sonar.server.rule.RuleRepositories; -import org.sonar.server.rule.RuleService; -import org.sonar.server.rule.RuleUpdater; +import org.sonar.server.qualityprofile.ws.*; +import org.sonar.server.rule.*; import org.sonar.server.rule.db.RuleDao; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.rule.index.RuleNormalizer; -import org.sonar.server.rule.ws.ActiveRuleCompleter; -import org.sonar.server.rule.ws.AppAction; -import org.sonar.server.rule.ws.DeleteAction; -import org.sonar.server.rule.ws.RuleMapping; -import org.sonar.server.rule.ws.RulesWebService; -import org.sonar.server.rule.ws.SearchAction; -import org.sonar.server.rule.ws.TagsAction; -import org.sonar.server.rule.ws.UpdateAction; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.IndexQueue; -import org.sonar.server.search.IndexSynchronizer; -import org.sonar.server.search.SearchClient; -import org.sonar.server.search.SearchHealth; +import org.sonar.server.rule.ws.*; +import org.sonar.server.search.*; import org.sonar.server.source.CodeColorizers; import org.sonar.server.source.DeprecatedSourceDecorator; import org.sonar.server.source.HtmlSourceDecorator; @@ -253,27 +155,9 @@ import org.sonar.server.source.ws.ScmAction; import org.sonar.server.source.ws.ScmWriter; import org.sonar.server.source.ws.ShowAction; import org.sonar.server.source.ws.SourcesWs; -import org.sonar.server.startup.CleanPreviewAnalysisCache; -import org.sonar.server.startup.CopyRequirementsFromCharacteristicsToRules; -import org.sonar.server.startup.GeneratePluginIndex; -import org.sonar.server.startup.GwtPublisher; -import org.sonar.server.startup.JdbcDriverDeployer; -import org.sonar.server.startup.LogServerId; -import org.sonar.server.startup.RegisterDashboards; -import org.sonar.server.startup.RegisterDebtModel; -import org.sonar.server.startup.RegisterMetrics; -import org.sonar.server.startup.RegisterNewMeasureFilters; -import org.sonar.server.startup.RegisterPermissionTemplates; -import org.sonar.server.startup.RegisterServletFilters; -import org.sonar.server.startup.RenameDeprecatedPropertyKeys; -import org.sonar.server.startup.ServerMetadataPersister; +import org.sonar.server.startup.*; import org.sonar.server.test.CoverageService; -import org.sonar.server.test.ws.CoverageShowAction; -import org.sonar.server.test.ws.CoverageWs; -import org.sonar.server.test.ws.TestsCoveredFilesAction; -import org.sonar.server.test.ws.TestsShowAction; -import org.sonar.server.test.ws.TestsTestCasesAction; -import org.sonar.server.test.ws.TestsWs; +import org.sonar.server.test.ws.*; import org.sonar.server.text.MacroInterpreter; import org.sonar.server.text.RubyTextService; import org.sonar.server.ui.JRubyI18n; @@ -281,23 +165,12 @@ import org.sonar.server.ui.JRubyProfiling; import org.sonar.server.ui.PageDecorations; import org.sonar.server.ui.Views; import org.sonar.server.updatecenter.ws.UpdateCenterWs; -import org.sonar.server.user.DefaultUserService; -import org.sonar.server.user.DoPrivileged; -import org.sonar.server.user.GroupMembershipFinder; -import org.sonar.server.user.GroupMembershipService; -import org.sonar.server.user.NewUserNotifier; -import org.sonar.server.user.SecurityRealmFactory; +import org.sonar.server.user.*; import org.sonar.server.user.db.GroupDao; import org.sonar.server.user.ws.FavoritesWs; import org.sonar.server.user.ws.UserPropertiesWs; import org.sonar.server.user.ws.UsersWs; -import org.sonar.server.util.BooleanTypeValidation; -import org.sonar.server.util.FloatTypeValidation; -import org.sonar.server.util.IntegerTypeValidation; -import org.sonar.server.util.StringListTypeValidation; -import org.sonar.server.util.StringTypeValidation; -import org.sonar.server.util.TextTypeValidation; -import org.sonar.server.util.TypeValidations; +import org.sonar.server.util.*; import org.sonar.server.ws.ListingWs; import org.sonar.server.ws.WebServiceEngine; @@ -595,7 +468,6 @@ class ServerComponents { pico.addSingleton(IssueUpdater.class); pico.addSingleton(FunctionExecutor.class); pico.addSingleton(IssueWorkflow.class); - pico.addSingleton(IssueService.class); pico.addSingleton(IssueCommentService.class); pico.addSingleton(DefaultIssueFinder.class); pico.addSingleton(IssueStatsFinder.class); @@ -613,8 +485,10 @@ class ServerComponents { // Switch Issue search if (properties.getProperty("sonar.issues.use_es_backend", null) != null) { pico.addSingleton(org.sonar.server.issue.ws.SearchAction.class); + pico.addSingleton(DefaultIssueService.class); } else { pico.addSingleton(IssueSearchAction.class); + pico.addSingleton(OldIssueService.class); } pico.addSingleton(IssueActionsWriter.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java index 8a847ef35ea..1ffcb84bf46 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java @@ -21,6 +21,7 @@ package org.sonar.server.search; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.config.Settings; import org.sonar.core.persistence.DbSession; import org.sonar.server.activity.index.ActivityIndex; import org.sonar.server.db.Dao; @@ -41,10 +42,12 @@ public class IndexSynchronizer { private final DbClient db; private final IndexClient index; + private final Settings settings; - public IndexSynchronizer(DbClient db, IndexClient index) { + public IndexSynchronizer(DbClient db, IndexClient index, Settings settings) { this.db = db; this.index = index; + this.settings = settings; } public void execute() { @@ -53,8 +56,13 @@ public class IndexSynchronizer { LOG.info("Starting DB to Index synchronization"); long start = System.currentTimeMillis(); synchronize(session, db.ruleDao(), index.get(RuleIndex.class)); - synchronize(session, db.issueDao(), index.get(IssueIndex.class)); - synchronize(session, db.issueAuthorizationDao(), index.get(IssueAuthorizationIndex.class)); + + // Switch Issue search + if (settings.getString("sonar.issues.use_es_backend") != null) { + synchronize(session, db.issueDao(), index.get(IssueIndex.class)); + synchronize(session, db.issueAuthorizationDao(), index.get(IssueAuthorizationIndex.class)); + } + synchronize(session, db.activeRuleDao(), index.get(ActiveRuleIndex.class)); synchronize(session, db.activityDao(), index.get(ActivityIndex.class)); session.commit(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java index 5de5a0bac76..34a0835706e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java @@ -350,7 +350,6 @@ public class ComponentAppActionTest { MockUserSession.set().addComponentPermission(UserRole.USER, SUB_PROJECT_KEY, COMPONENT_KEY); addComponent(); - addMeasure(CoreMetrics.IT_COVERAGE_KEY, 85.2); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); @@ -365,7 +364,7 @@ public class ComponentAppActionTest { when(resourceDao.getLastSnapshotByResourceId(eq(1L), eq(session))).thenReturn( new SnapshotDto().setPeriodMode(1, "previous_analysis").setPeriodDate(1, DateUtils.parseDate("2014-05-08")) - ); + ); when(periods.label(anyString(), anyString(), any(Date.class))).thenReturn("since previous analysis (May 08 2014)"); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); @@ -409,7 +408,7 @@ public class ComponentAppActionTest { addComponent(); when(issueService.findRulesByComponent(COMPONENT_KEY, null, session)).thenReturn( new RulesAggregation().add(new RuleDto().setRuleKey("AvoidCycle").setRepositoryKey("squid").setName("Avoid Cycle")) - ); + ); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); request.execute().assertJson(getClass(), "app_with_rules.json"); @@ -424,12 +423,12 @@ public class ComponentAppActionTest { Date periodDate = DateUtils.parseDate("2014-05-08"); when(resourceDao.getLastSnapshotByResourceId(eq(1L), eq(session))).thenReturn( new SnapshotDto().setPeriodMode(1, "previous_analysis").setPeriodDate(1, periodDate) - ); + ); when(periods.label(anyString(), anyString(), any(Date.class))).thenReturn("since previous analysis (May 08 2014)"); when(issueService.findRulesByComponent(COMPONENT_KEY, periodDate, session)).thenReturn( new RulesAggregation().add(new RuleDto().setRuleKey("AvoidCycle").setRepositoryKey("squid").setName("Avoid Cycle")) - ); + ); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY).setParam("period", "1"); request.execute().assertJson(getClass(), "app_with_rules_when_period_is_set.json"); @@ -485,11 +484,11 @@ public class ComponentAppActionTest { when(componentDao.getById(1L, session)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube").setKey("org.codehaus.sonar:sonar")); } - private void addPeriod(){ + private void addPeriod() { Date periodDate = DateUtils.parseDate("2014-05-08"); when(resourceDao.getLastSnapshotByResourceId(eq(1L), eq(session))).thenReturn( new SnapshotDto().setPeriodMode(1, "previous_analysis").setPeriodDate(1, periodDate) - ); + ); when(periods.label(anyString(), anyString(), any(Date.class))).thenReturn("since previous analysis (May 08 2014)"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueServiceMediumTest.java new file mode 100644 index 00000000000..708b5dc80cb --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueServiceMediumTest.java @@ -0,0 +1,391 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.issue; + +import com.google.common.collect.Multiset; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.issue.DefaultTransitions; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueQuery; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Scopes; +import org.sonar.api.rule.Severity; +import org.sonar.api.security.DefaultGroups; +import org.sonar.api.utils.DateUtils; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.issue.db.ActionPlanDto; +import org.sonar.core.issue.db.IssueDto; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.permission.PermissionFacade; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.rule.RuleDto; +import org.sonar.core.user.UserDto; +import org.sonar.server.component.SnapshotTesting; +import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.component.db.SnapshotDao; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.issue.db.IssueDao; +import org.sonar.server.issue.index.IssueIndex; +import org.sonar.server.rule.RuleTesting; +import org.sonar.server.rule.db.RuleDao; +import org.sonar.server.search.IndexClient; +import org.sonar.server.search.QueryContext; +import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; + +public class DefaultIssueServiceMediumTest { + + @ClassRule + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); + + DbClient db; + IndexClient indexClient; + DbSession session; + IssueService service; + + RuleDto rule; + ComponentDto project; + ComponentDto file; + UserDto connectedUser; + + @Before + public void setUp() throws Exception { + tester.clearDbAndIndexes(); + db = tester.get(DbClient.class); + indexClient = tester.get(IndexClient.class); + session = db.openSession(false); + service = tester.get(IssueService.class); + + rule = RuleTesting.newXooX1(); + tester.get(RuleDao.class).insert(session, rule); + + project = new ComponentDto() + .setKey("MyProject") + .setLongName("My Project") + .setQualifier(Qualifiers.PROJECT) + .setScope(Scopes.PROJECT); + tester.get(ComponentDao.class).insert(session, project); + tester.get(SnapshotDao.class).insert(session, SnapshotTesting.createForComponent(project)); + + file = new ComponentDto() + .setProjectId(project.getId()) + .setSubProjectId(project.getId()) + .setKey("MyComponent") + .setLongName("My Component"); + tester.get(ComponentDao.class).insert(session, file); + tester.get(SnapshotDao.class).insert(session, SnapshotTesting.createForComponent(file)); + + // project can be seen by anyone + tester.get(PermissionFacade.class).insertGroupPermission(project.getId(), DefaultGroups.ANYONE, UserRole.USER, session); + db.issueAuthorizationDao().synchronizeAfter(session, new Date(0)); + + connectedUser = new UserDto().setLogin("gandalf").setName("Gandalf"); + db.userDao().insert(session, connectedUser); + tester.get(PermissionFacade.class).insertUserPermission(project.getId(), connectedUser.getId(), UserRole.USER, session); + + MockUserSession.set() + .setLogin(connectedUser.getLogin()) + .setUserId(connectedUser.getId().intValue()) + .setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN) + .addProjectPermissions(UserRole.USER, project.key()) + .addProjectPermissions(UserRole.ISSUE_ADMIN, project.key()); + + session.commit(); + } + + @After + public void after() { + session.close(); + } + + @Test + public void can_facet() throws Exception { + IssueDto issue1 = newIssue().setActionPlanKey("P1"); + IssueDto issue2 = newIssue().setActionPlanKey("P2").setResolution("NONE"); + tester.get(IssueDao.class).insert(session, issue1, issue2); + session.commit(); + + org.sonar.server.search.Result result = ((DefaultIssueService) service).search(IssueQuery.builder().build(), new QueryContext()); + assertThat(result.getHits()).hasSize(2); + assertThat(result.getFacets()).isEmpty(); + + result = ((DefaultIssueService) service).search(IssueQuery.builder().build(), new QueryContext().setFacet(true)); + assertThat(result.getFacets().keySet()).hasSize(4); + assertThat(result.getFacetKeys("actionPlan")).hasSize(2); + } + + @Test + public void list_status() { + assertThat(service.listStatus()).containsExactly("OPEN", "CONFIRMED", "REOPENED", "RESOLVED", "CLOSED"); + } + + @Test + public void list_transitions() { + IssueDto issue = newIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + List result = service.listTransitions(issue.getKey()); + assertThat(result).hasSize(1); + assertThat(result.get(0).key()).isEqualTo("reopen"); + } + + @Test + public void do_transition() { + IssueDto issue = newIssue().setStatus(Issue.STATUS_OPEN); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + assertThat(db.issueDao().getByKey(session, issue.getKey())).isNotNull(); + IssueTesting.assertIsEquivalent(issue, indexClient.get(IssueIndex.class).getByKey(issue.getKey())); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).status()).isEqualTo(Issue.STATUS_OPEN); + + service.doTransition(issue.getKey(), DefaultTransitions.CONFIRM); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).status()).isEqualTo(Issue.STATUS_CONFIRMED); + } + + @Test + public void assign() { + IssueDto issue = newIssue(); + tester.get(IssueDao.class).insert(session, issue); + + UserDto user = new UserDto().setLogin("perceval").setName("Perceval"); + db.userDao().insert(session, user); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isNull(); + + service.assign(issue.getKey(), user.getLogin()); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isEqualTo("perceval"); + } + + @Test + public void un_assign() { + IssueDto issue = newIssue().setAssignee("perceval"); + tester.get(IssueDao.class).insert(session, issue); + + UserDto user = new UserDto().setLogin("perceval").setName("Perceval"); + db.userDao().insert(session, user); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isEqualTo("perceval"); + + service.assign(issue.getKey(), ""); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isNull(); + } + + @Test + public void fail_to_assign_on_unknown_user() { + IssueDto issue = newIssue(); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + try { + service.assign(issue.getKey(), "unknown"); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("Unknown user: unknown"); + } + } + + @Test + public void plan() { + IssueDto issue = newIssue(); + tester.get(IssueDao.class).insert(session, issue); + + String actionPlanKey = "EFGH"; + db.actionPlanDao().save(new ActionPlanDto().setKey(actionPlanKey).setProjectId(project.getId())); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isNull(); + + service.plan(issue.getKey(), actionPlanKey); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isEqualTo(actionPlanKey); + } + + @Test + public void un_plan() { + String actionPlanKey = "EFGH"; + db.actionPlanDao().save(new ActionPlanDto().setKey(actionPlanKey).setProjectId(project.getId())); + + IssueDto issue = newIssue().setActionPlanKey(actionPlanKey); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isEqualTo(actionPlanKey); + + service.plan(issue.getKey(), null); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isNull(); + } + + @Test + public void fail_plan_if_action_plan_not_found() { + tester.get(IssueDao.class).insert(session, newIssue()); + session.commit(); + + try { + service.plan("ABCD", "unknown"); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("Unknown action plan: unknown"); + } + } + + @Test + public void set_severity() { + IssueDto issue = newIssue().setSeverity(Severity.BLOCKER); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).severity()).isEqualTo(Severity.BLOCKER); + + service.setSeverity(issue.getKey(), Severity.MINOR); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).severity()).isEqualTo(Severity.MINOR); + } + + @Test + public void create_manual_issue() { + RuleDto manualRule = RuleTesting.newManualRule("manualRuleKey"); + tester.get(RuleDao.class).insert(session, manualRule); + session.commit(); + + Issue result = service.createManualIssue(file.key(), manualRule.getKey(), 10, "Fix it", Severity.MINOR, 2d); + + Issue manualIssue = indexClient.get(IssueIndex.class).getByKey(result.key()); + assertThat(manualIssue.componentKey()).isEqualTo(file.key()); + assertThat(manualIssue.projectKey()).isEqualTo(project.key()); + assertThat(manualIssue.ruleKey()).isEqualTo(manualRule.getKey()); + assertThat(manualIssue.message()).isEqualTo("Fix it"); + assertThat(manualIssue.line()).isEqualTo(10); + assertThat(manualIssue.severity()).isEqualTo(Severity.MINOR); + assertThat(manualIssue.effortToFix()).isEqualTo(2d); + assertThat(manualIssue.reporter()).isEqualTo(connectedUser.getLogin()); + } + + @Test + public void create_manual_issue_with_major_severity_when_no_severity() { + RuleDto manualRule = RuleTesting.newManualRule("manualRuleKey"); + tester.get(RuleDao.class).insert(session, manualRule); + session.commit(); + + Issue result = service.createManualIssue(file.key(), manualRule.getKey(), 10, "Fix it", null, 2d); + + Issue manualIssue = indexClient.get(IssueIndex.class).getByKey(result.key()); + assertThat(manualIssue.severity()).isEqualTo(Severity.MAJOR); + } + + @Test + public void create_manual_issue_with_rule_name_when_no_message() { + RuleDto manualRule = RuleTesting.newManualRule("manualRuleKey").setName("Manual rule name"); + tester.get(RuleDao.class).insert(session, manualRule); + session.commit(); + + Issue result = service.createManualIssue(file.key(), manualRule.getKey(), 10, null, null, 2d); + + Issue manualIssue = indexClient.get(IssueIndex.class).getByKey(result.key()); + assertThat(manualIssue.message()).isEqualTo("Manual rule name"); + } + + @Test + public void fail_create_manual_issue_on_not_manual_rule() { + try { + service.createManualIssue(file.key(), rule.getKey(), 10, "Fix it", null, 2d); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Issues can be created only on rules marked as 'manual': xoo:x1"); + } + } + + @Test(expected = ForbiddenException.class) + public void fail_create_manual_issue_if_not_having_required_role() { + // User has not the 'user' role on the project + MockUserSession.set() + .setLogin(connectedUser.getLogin()) + .setUserId(connectedUser.getId().intValue()) + .setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN) + .addProjectPermissions(UserRole.CODEVIEWER, project.key()); + + RuleDto manualRule = RuleTesting.newManualRule("manualRuleKey"); + tester.get(RuleDao.class).insert(session, manualRule); + session.commit(); + + service.createManualIssue(file.key(), rule.getKey(), 10, "Fix it", null, 2d); + } + + @Test + public void find_rules_by_component() throws Exception { + // 2 issues on the same rule + tester.get(IssueDao.class).insert(session, newIssue().setRule(rule)); + tester.get(IssueDao.class).insert(session, newIssue().setRule(rule)); + session.commit(); + + RulesAggregation result = service.findRulesByComponent(file.key(), null, session); + assertThat(result.rules()).hasSize(1); + } + + @Test + public void find_rules_by_severity() throws Exception { + tester.get(IssueDao.class).insert(session, newIssue().setSeverity(Severity.MAJOR)); + tester.get(IssueDao.class).insert(session, newIssue().setSeverity(Severity.MAJOR)); + tester.get(IssueDao.class).insert(session, newIssue().setSeverity(Severity.INFO)); + session.commit(); + + Multiset result = service.findSeveritiesByComponent(file.key(), null, session); + assertThat(result.count("MAJOR")).isEqualTo(2); + assertThat(result.count("INFO")).isEqualTo(1); + assertThat(result.count("UNKNOWN")).isEqualTo(0); + } + + private IssueDto newIssue() { + return new IssueDto() + .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) + .setIssueUpdateDate(DateUtils.parseDate("2014-12-04")) + .setRule(rule) + .setDebt(10L) + .setRootComponent(project) + .setComponent(file) + .setStatus(Issue.STATUS_OPEN) + .setResolution(null) + .setSeverity(Severity.MAJOR) + .setKee(UUID.randomUUID().toString()); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyDefaultIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyDefaultIssueServiceTest.java new file mode 100644 index 00000000000..b807c0bc5f2 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyDefaultIssueServiceTest.java @@ -0,0 +1,670 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.issue; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.issue.ActionPlan; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueQuery; +import org.sonar.api.issue.action.Action; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.FieldDiffs; +import org.sonar.api.user.User; +import org.sonar.core.issue.DefaultActionPlan; +import org.sonar.core.issue.DefaultIssueFilter; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.resource.ResourceDto; +import org.sonar.core.resource.ResourceQuery; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.Message; +import org.sonar.server.issue.actionplan.ActionPlanService; +import org.sonar.server.issue.filter.IssueFilterService; +import org.sonar.server.user.UserSession; + +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; +import static org.fest.assertions.Fail.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class InternalRubyDefaultIssueServiceTest { + + @Mock + IssueService issueService; + + @Mock + IssueCommentService commentService; + + @Mock + IssueChangelogService changelogService; + + @Mock + ActionPlanService actionPlanService; + + @Mock + ResourceDao resourceDao; + + @Mock + IssueStatsFinder issueStatsFinder; + + @Mock + ActionService actionService; + + @Mock + IssueFilterService issueFilterService; + + @Mock + IssueBulkChangeService issueBulkChangeService; + + InternalRubyIssueService service; + + @Before + public void setUp() { + ResourceDto project = new ResourceDto().setKey("org.sonar.Sample"); + when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(project); + service = new InternalRubyIssueService(issueService, commentService, changelogService, actionPlanService, issueStatsFinder, resourceDao, actionService, + issueFilterService, issueBulkChangeService); + } + + @Test + public void find_issue_assignees() throws Exception { + service.findIssueAssignees(ImmutableMap.of("issues", "ABCD")); + verify(issueStatsFinder).findIssueAssignees(any(IssueQuery.class)); + } + + @Test + public void list_transitions_by_issue_key() throws Exception { + service.listTransitions("ABCD"); + verify(issueService).listTransitions(eq("ABCD")); + } + + @Test + public void list_transitions_by_issue() throws Exception { + Issue issue = new DefaultIssue().setKey("ABCD"); + service.listTransitions(issue); + verify(issueService).listTransitions(eq(issue)); + } + + @Test + public void list_status() throws Exception { + service.listStatus(); + verify(issueService).listStatus(); + } + + @Test + public void list_resolutions() throws Exception { + assertThat(service.listResolutions()).isEqualTo(Issue.RESOLUTIONS); + } + + @Test + public void list_plugin_actions() { + Action action = mock(Action.class); + when(action.key()).thenReturn("link-to-jira"); + + when(actionService.listAllActions()).thenReturn(newArrayList(action)); + + assertThat(service.listPluginActions()).containsOnly("link-to-jira"); + } + + @Test + public void do_transition() throws Exception { + service.doTransition("ABCD", Issue.STATUS_RESOLVED); + verify(issueService).doTransition(eq("ABCD"), eq(Issue.STATUS_RESOLVED)); + } + + @Test + public void create_action_plan() { + Map parameters = newHashMap(); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + parameters.put("project", "org.sonar.Sample"); + parameters.put("deadLine", "2113-05-13"); + + Result result = service.createActionPlan(parameters); + assertThat(result.ok()).isTrue(); + + ArgumentCaptor actionPlanCaptor = ArgumentCaptor.forClass(ActionPlan.class); + verify(actionPlanService).create(actionPlanCaptor.capture(), any(UserSession.class)); + ActionPlan actionPlan = actionPlanCaptor.getValue(); + + assertThat(actionPlan).isNotNull(); + assertThat(actionPlan.key()).isNotNull(); + assertThat(actionPlan.name()).isEqualTo("Long term"); + assertThat(actionPlan.description()).isEqualTo("Long term issues"); + assertThat(actionPlan.deadLine()).isNotNull(); + } + + @Test + public void update_action_plan() { + when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term")); + + Map parameters = newHashMap(); + parameters.put("name", "New Long term"); + parameters.put("description", "New Long term issues"); + parameters.put("deadLine", "2113-05-13"); + parameters.put("project", "org.sonar.MultiSample"); + + Result result = service.updateActionPlan("ABCD", parameters); + assertThat(result.ok()).isTrue(); + + ArgumentCaptor actionPlanCaptor = ArgumentCaptor.forClass(ActionPlan.class); + verify(actionPlanService).update(actionPlanCaptor.capture(), any(UserSession.class)); + ActionPlan actionPlan = actionPlanCaptor.getValue(); + + assertThat(actionPlan).isNotNull(); + assertThat(actionPlan.key()).isNotNull(); + assertThat(actionPlan.name()).isEqualTo("New Long term"); + assertThat(actionPlan.description()).isEqualTo("New Long term issues"); + assertThat(actionPlan.deadLine()).isNotNull(); + } + + @Test + public void update_action_plan_with_new_project() { + when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term").setProjectKey("org.sonar.MultiSample")); + + Map parameters = newHashMap(); + parameters.put("name", "New Long term"); + parameters.put("description", "New Long term issues"); + parameters.put("deadLine", "2113-05-13"); + + ArgumentCaptor actionPlanCaptor = ArgumentCaptor.forClass(ActionPlan.class); + Result result = service.updateActionPlan("ABCD", parameters); + assertThat(result.ok()).isTrue(); + + verify(actionPlanService).update(actionPlanCaptor.capture(), any(UserSession.class)); + ActionPlan actionPlan = actionPlanCaptor.getValue(); + + assertThat(actionPlan).isNotNull(); + assertThat(actionPlan.key()).isNotNull(); + assertThat(actionPlan.name()).isEqualTo("New Long term"); + assertThat(actionPlan.description()).isEqualTo("New Long term issues"); + assertThat(actionPlan.deadLine()).isNotNull(); + assertThat(actionPlan.projectKey()).isEqualTo("org.sonar.MultiSample"); + } + + @Test + public void not_update_action_plan_when_action_plan_is_not_found() { + when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(null); + + Result result = service.updateActionPlan("ABCD", null); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.errors.action_plan_does_not_exist", "ABCD")); + } + + @Test + public void delete_action_plan() { + when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term")); + + Result result = service.deleteActionPlan("ABCD"); + verify(actionPlanService).delete(eq("ABCD"), any(UserSession.class)); + assertThat(result.ok()).isTrue(); + } + + @Test + public void not_delete_action_plan_if_action_plan_not_found() { + when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(null); + + Result result = service.deleteActionPlan("ABCD"); + verify(actionPlanService, never()).delete(eq("ABCD"), any(UserSession.class)); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.errors.action_plan_does_not_exist", "ABCD")); + } + + @Test + public void close_action_plan() { + when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term")); + + Result result = service.closeActionPlan("ABCD"); + verify(actionPlanService).setStatus(eq("ABCD"), eq("CLOSED"), any(UserSession.class)); + assertThat(result.ok()).isTrue(); + } + + @Test + public void open_action_plan() { + when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term")); + + Result result = service.openActionPlan("ABCD"); + verify(actionPlanService).setStatus(eq("ABCD"), eq("OPEN"), any(UserSession.class)); + assertThat(result.ok()).isTrue(); + } + + @Test + public void get_error_on_action_plan_result_when_no_project() { + Map parameters = newHashMap(); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + + Result result = service.createActionPlanResult(parameters); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("errors.cant_be_empty", "project")); + } + + @Test + public void get_error_on_action_plan_result_when_no_name() { + Map parameters = newHashMap(); + parameters.put("name", null); + parameters.put("description", "Long term issues"); + parameters.put("project", "org.sonar.Sample"); + + Result result = service.createActionPlanResult(parameters); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("errors.cant_be_empty", "name")); + } + + @Test + public void get_error_on_action_plan_result_when_name_is_too_long() { + Map parameters = newHashMap(); + parameters.put("name", createLongString(201)); + parameters.put("description", "Long term issues"); + parameters.put("project", "org.sonar.Sample"); + + Result result = service.createActionPlanResult(parameters); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("errors.is_too_long", "name", 200)); + } + + @Test + public void get_error_on_action_plan_result_when_description_is_too_long() { + Map parameters = newHashMap(); + parameters.put("name", "Long term"); + parameters.put("description", createLongString(1001)); + parameters.put("project", "org.sonar.Sample"); + + Result result = service.createActionPlanResult(parameters); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("errors.is_too_long", "description", 1000)); + } + + @Test + public void get_error_on_action_plan_result_when_dead_line_use_wrong_format() { + Map parameters = newHashMap(); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + parameters.put("project", "org.sonar.Sample"); + parameters.put("deadLine", "18/05/2013"); + + Result result = service.createActionPlanResult(parameters); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("errors.is_not_valid", "date")); + } + + @Test + public void get_error_on_action_plan_result_when_dead_line_is_in_the_past() { + Map parameters = newHashMap(); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + parameters.put("project", "org.sonar.Sample"); + parameters.put("deadLine", "2000-01-01"); + + Result result = service.createActionPlanResult(parameters); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.date_cant_be_in_past")); + } + + @Test + public void get_error_on_action_plan_result_when_name_is_already_used_for_project() { + Map parameters = newHashMap(); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + parameters.put("project", "org.sonar.Sample"); + + when(actionPlanService.isNameAlreadyUsedForProject(anyString(), anyString())).thenReturn(true); + + Result result = service.createActionPlanResult(parameters, DefaultActionPlan.create("Short term")); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.same_name_in_same_project")); + } + + @Test + public void get_error_on_action_plan_result_when_project_not_found() { + Map parameters = newHashMap(); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + parameters.put("project", "org.sonar.Sample"); + + when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null); + + Result result = service.createActionPlanResult(parameters); + assertThat(result.ok()).isFalse(); + assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.errors.project_does_not_exist", "org.sonar.Sample")); + } + + @Test + public void test_changelog_from_issue_key() throws Exception { + IssueChangelog changelog = new IssueChangelog(Collections.emptyList(), Collections.emptyList()); + when(changelogService.changelog(eq("ABCDE"))).thenReturn(changelog); + + IssueChangelog result = service.changelog("ABCDE"); + + assertThat(result).isSameAs(changelog); + } + + @Test + public void test_changelog_from_issue() throws Exception { + Issue issue = new DefaultIssue().setKey("ABCDE"); + + IssueChangelog changelog = new IssueChangelog(Collections.emptyList(), Collections.emptyList()); + when(changelogService.changelog(eq(issue))).thenReturn(changelog); + + IssueChangelog result = service.changelog(issue); + + assertThat(result).isSameAs(changelog); + } + + @Test + public void create_issue_filter() { + Map parameters = newHashMap(); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + + service.createIssueFilter(parameters); + + ArgumentCaptor issueFilterCaptor = ArgumentCaptor.forClass(DefaultIssueFilter.class); + verify(issueFilterService).save(issueFilterCaptor.capture(), any(UserSession.class)); + DefaultIssueFilter issueFilter = issueFilterCaptor.getValue(); + assertThat(issueFilter.name()).isEqualTo("Long term"); + assertThat(issueFilter.description()).isEqualTo("Long term issues"); + } + + @Test + public void update_issue_filter() { + Map parameters = newHashMap(); + parameters.put("id", "10"); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + parameters.put("user", "John"); + + service.updateIssueFilter(parameters); + + ArgumentCaptor issueFilterCaptor = ArgumentCaptor.forClass(DefaultIssueFilter.class); + verify(issueFilterService).update(issueFilterCaptor.capture(), any(UserSession.class)); + DefaultIssueFilter issueFilter = issueFilterCaptor.getValue(); + assertThat(issueFilter.id()).isEqualTo(10L); + assertThat(issueFilter.name()).isEqualTo("Long term"); + assertThat(issueFilter.description()).isEqualTo("Long term issues"); + } + + @Test + public void update_data() { + Map data = newHashMap(); + service.updateIssueFilterQuery(10L, data); + verify(issueFilterService).updateFilterQuery(eq(10L), eq(data), any(UserSession.class)); + } + + @Test + public void delete_issue_filter() { + service.deleteIssueFilter(1L); + verify(issueFilterService).delete(eq(1L), any(UserSession.class)); + } + + @Test + public void copy_issue_filter() { + Map parameters = newHashMap(); + parameters.put("name", "Copy of Long term"); + parameters.put("description", "Copy of Long term issues"); + + service.copyIssueFilter(1L, parameters); + + ArgumentCaptor issueFilterCaptor = ArgumentCaptor.forClass(DefaultIssueFilter.class); + verify(issueFilterService).copy(eq(1L), issueFilterCaptor.capture(), any(UserSession.class)); + DefaultIssueFilter issueFilter = issueFilterCaptor.getValue(); + assertThat(issueFilter.name()).isEqualTo("Copy of Long term"); + assertThat(issueFilter.description()).isEqualTo("Copy of Long term issues"); + } + + @Test + public void get_error_on_create_issue_filter_result_when_no_name() { + Map parameters = newHashMap(); + parameters.put("name", ""); + parameters.put("description", "Long term issues"); + parameters.put("user", "John"); + + try { + service.createIssueFilterResultForNew(parameters); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class); + checkBadRequestException(e, "errors.cant_be_empty", "name"); + } + } + + @Test + public void get_error_on_create_issue_filter_result_when_name_is_too_long() { + Map parameters = newHashMap(); + parameters.put("name", createLongString(101)); + parameters.put("description", "Long term issues"); + parameters.put("user", "John"); + + try { + service.createIssueFilterResultForNew(parameters); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class); + checkBadRequestException(e, "errors.is_too_long", "name", 100); + } + } + + @Test + public void get_error_on_create_issue_filter_result_when_description_is_too_long() { + Map parameters = newHashMap(); + parameters.put("name", "Long term"); + parameters.put("description", createLongString(4001)); + parameters.put("user", "John"); + + try { + service.createIssueFilterResultForNew(parameters); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class); + checkBadRequestException(e, "errors.is_too_long", "description", 4000); + } + } + + @Test + public void get_error_on_create_issue_filter_result_when_id_is_null_on_update() { + Map parameters = newHashMap(); + parameters.put("id", null); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + parameters.put("user", "John"); + + try { + service.createIssueFilterResultForUpdate(parameters); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class); + checkBadRequestException(e, "errors.cant_be_empty", "id"); + } + } + + @Test + public void get_error_on_create_issue_filter_result_when_user_is_null_on_update() { + Map parameters = newHashMap(); + parameters.put("id", "10"); + parameters.put("name", "All Open Issues"); + parameters.put("description", "Long term issues"); + parameters.put("user", null); + + try { + service.createIssueFilterResultForUpdate(parameters); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class); + checkBadRequestException(e, "errors.cant_be_empty", "user"); + } + } + + @Test + public void get_no_error_on_issue_filter_result_when_id_and_user_are_null_on_copy() { + Map parameters = newHashMap(); + parameters.put("id", null); + parameters.put("name", "Long term"); + parameters.put("description", "Long term issues"); + parameters.put("user", null); + + DefaultIssueFilter result = service.createIssueFilterResultForCopy(parameters); + assertThat(result).isNotNull(); + } + + @Test + public void execute_issue_filter_from_issue_query() { + service.execute(Maps.newHashMap()); + verify(issueFilterService).execute(any(IssueQuery.class)); + } + + @Test + public void execute_issue_filter_from_existing_filter() { + Map props = newHashMap(); + props.put("componentRoots", "struts"); + props.put("statuses", "OPEN"); + when(issueFilterService.deserializeIssueFilterQuery(any(DefaultIssueFilter.class))).thenReturn(props); + + Map overrideProps = newHashMap(); + overrideProps.put("statuses", "CLOSED"); + overrideProps.put("resolved", true); + overrideProps.put("pageSize", 20); + overrideProps.put("pageIndex", 2); + service.execute(10L, overrideProps); + ArgumentCaptor captor = ArgumentCaptor.forClass(IssueQuery.class); + verify(issueFilterService).execute(captor.capture()); + verify(issueFilterService).find(eq(10L), any(UserSession.class)); + + IssueQuery issueQuery = captor.getValue(); + assertThat(issueQuery.componentRoots()).contains("struts"); + assertThat(issueQuery.statuses()).contains("CLOSED"); + assertThat(issueQuery.resolved()).isTrue(); + assertThat(issueQuery.pageSize()).isEqualTo(20); + assertThat(issueQuery.pageIndex()).isEqualTo(2); + } + + @Test + public void serialize_filter_query() { + Map props = newHashMap(); + props.put("componentRoots", "struts"); + service.serializeFilterQuery(props); + verify(issueFilterService).serializeFilterQuery(props); + } + + @Test + public void deserialize_filter_query() { + DefaultIssueFilter issueFilter = new DefaultIssueFilter(); + service.deserializeFilterQuery(issueFilter); + verify(issueFilterService).deserializeIssueFilterQuery(issueFilter); + } + + @Test + public void sanitize_filter_query() { + Map query = newHashMap(); + query.put("statuses", "CLOSED"); + query.put("resolved", true); + query.put("unknown", "john"); + Map result = service.sanitizeFilterQuery(query); + assertThat(result.keySet()).containsOnly("statuses", "resolved"); + } + + @Test + public void find_user_issue_filters() { + service.findIssueFiltersForCurrentUser(); + verify(issueFilterService).findByUser(any(UserSession.class)); + } + + @Test + public void find_shared_issue_filters() { + service.findSharedFiltersForCurrentUser(); + verify(issueFilterService).findSharedFiltersWithoutUserFilters(any(UserSession.class)); + } + + @Test + public void find_favourite_issue_filters() { + service.findFavouriteIssueFiltersForCurrentUser(); + verify(issueFilterService).findFavoriteFilters(any(UserSession.class)); + } + + @Test + public void toggle_favourite_issue_filter() { + service.toggleFavouriteIssueFilter(10L); + verify(issueFilterService).toggleFavouriteIssueFilter(eq(10L), any(UserSession.class)); + } + + @Test + public void check_if_user_is_authorized_to_see_issue_filter() { + DefaultIssueFilter issueFilter = new DefaultIssueFilter(); + service.isUserAuthorized(issueFilter); + verify(issueFilterService).getLoggedLogin(any(UserSession.class)); + verify(issueFilterService).verifyCurrentUserCanReadFilter(eq(issueFilter), anyString()); + } + + @Test + public void check_if_user_can_share_issue_filter() { + service.canUserShareIssueFilter(); + verify(issueFilterService).canShareFilter(any(UserSession.class)); + } + + @Test + public void execute_bulk_change() { + Map 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"); + service.bulkChange(params, "My comment", true); + verify(issueBulkChangeService).execute(any(IssueBulkChangeQuery.class), any(UserSession.class)); + } + + @Test + public void format_changelog() { + FieldDiffs fieldDiffs = new FieldDiffs(); + service.formatChangelog(fieldDiffs); + verify(changelogService).formatDiffs(eq(fieldDiffs)); + } + + private void checkBadRequestException(Exception e, String key, Object... params) { + BadRequestException exception = (BadRequestException) e; + Message msg = exception.errors().messages().get(0); + assertThat(msg.getKey()).isEqualTo(key); + assertThat(msg.getParams()).containsOnly(params); + } + + private String createLongString(int size) { + String result = ""; + for (int i = 0; i < size; i++) { + result += "c"; + } + return result; + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java deleted file mode 100644 index 508890be52b..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java +++ /dev/null @@ -1,670 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.server.issue; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.sonar.api.issue.ActionPlan; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.IssueQuery; -import org.sonar.api.issue.action.Action; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.FieldDiffs; -import org.sonar.api.user.User; -import org.sonar.core.issue.DefaultActionPlan; -import org.sonar.core.issue.DefaultIssueFilter; -import org.sonar.core.resource.ResourceDao; -import org.sonar.core.resource.ResourceDto; -import org.sonar.core.resource.ResourceQuery; -import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.exceptions.Message; -import org.sonar.server.issue.actionplan.ActionPlanService; -import org.sonar.server.issue.filter.IssueFilterService; -import org.sonar.server.user.UserSession; - -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; -import static org.fest.assertions.Fail.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; - -@RunWith(MockitoJUnitRunner.class) -public class InternalRubyIssueServiceTest { - - @Mock - IssueService issueService; - - @Mock - IssueCommentService commentService = mock(IssueCommentService.class); - - @Mock - IssueChangelogService changelogService = mock(IssueChangelogService.class); - - @Mock - ActionPlanService actionPlanService = mock(ActionPlanService.class); - - @Mock - ResourceDao resourceDao = mock(ResourceDao.class); - - @Mock - IssueStatsFinder issueStatsFinder = mock(IssueStatsFinder.class); - - @Mock - ActionService actionService = mock(ActionService.class); - - @Mock - IssueFilterService issueFilterService = mock(IssueFilterService.class); - - @Mock - IssueBulkChangeService issueBulkChangeService = mock(IssueBulkChangeService.class); - - InternalRubyIssueService service; - - @Before - public void setUp() { - ResourceDto project = new ResourceDto().setKey("org.sonar.Sample"); - when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(project); - service = new InternalRubyIssueService(issueService, commentService, changelogService, actionPlanService, issueStatsFinder, resourceDao, actionService, - issueFilterService, issueBulkChangeService); - } - - @Test - public void find_issue_assignees() throws Exception { - service.findIssueAssignees(ImmutableMap.of("issues", "ABCD")); - verify(issueStatsFinder).findIssueAssignees(any(IssueQuery.class)); - } - - @Test - public void list_transitions_by_issue_key() throws Exception { - service.listTransitions("ABCD"); - verify(issueService).listTransitions(eq("ABCD"), any(UserSession.class)); - } - - @Test - public void list_transitions_by_issue() throws Exception { - Issue issue = new DefaultIssue().setKey("ABCD"); - service.listTransitions(issue); - verify(issueService).listTransitions(eq(issue), any(UserSession.class)); - } - - @Test - public void list_status() throws Exception { - service.listStatus(); - verify(issueService).listStatus(); - } - - @Test - public void list_resolutions() throws Exception { - assertThat(service.listResolutions()).isEqualTo(Issue.RESOLUTIONS); - } - - @Test - public void list_plugin_actions() { - Action action = mock(Action.class); - when(action.key()).thenReturn("link-to-jira"); - - when(actionService.listAllActions()).thenReturn(newArrayList(action)); - - assertThat(service.listPluginActions()).containsOnly("link-to-jira"); - } - - @Test - public void do_transition() throws Exception { - service.doTransition("ABCD", Issue.STATUS_RESOLVED); - verify(issueService).doTransition(eq("ABCD"), eq(Issue.STATUS_RESOLVED), any(UserSession.class)); - } - - @Test - public void create_action_plan() { - Map parameters = newHashMap(); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - parameters.put("project", "org.sonar.Sample"); - parameters.put("deadLine", "2113-05-13"); - - Result result = service.createActionPlan(parameters); - assertThat(result.ok()).isTrue(); - - ArgumentCaptor actionPlanCaptor = ArgumentCaptor.forClass(ActionPlan.class); - verify(actionPlanService).create(actionPlanCaptor.capture(), any(UserSession.class)); - ActionPlan actionPlan = actionPlanCaptor.getValue(); - - assertThat(actionPlan).isNotNull(); - assertThat(actionPlan.key()).isNotNull(); - assertThat(actionPlan.name()).isEqualTo("Long term"); - assertThat(actionPlan.description()).isEqualTo("Long term issues"); - assertThat(actionPlan.deadLine()).isNotNull(); - } - - @Test - public void update_action_plan() { - when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term")); - - Map parameters = newHashMap(); - parameters.put("name", "New Long term"); - parameters.put("description", "New Long term issues"); - parameters.put("deadLine", "2113-05-13"); - parameters.put("project", "org.sonar.MultiSample"); - - Result result = service.updateActionPlan("ABCD", parameters); - assertThat(result.ok()).isTrue(); - - ArgumentCaptor actionPlanCaptor = ArgumentCaptor.forClass(ActionPlan.class); - verify(actionPlanService).update(actionPlanCaptor.capture(), any(UserSession.class)); - ActionPlan actionPlan = actionPlanCaptor.getValue(); - - assertThat(actionPlan).isNotNull(); - assertThat(actionPlan.key()).isNotNull(); - assertThat(actionPlan.name()).isEqualTo("New Long term"); - assertThat(actionPlan.description()).isEqualTo("New Long term issues"); - assertThat(actionPlan.deadLine()).isNotNull(); - } - - @Test - public void update_action_plan_with_new_project() { - when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term").setProjectKey("org.sonar.MultiSample")); - - Map parameters = newHashMap(); - parameters.put("name", "New Long term"); - parameters.put("description", "New Long term issues"); - parameters.put("deadLine", "2113-05-13"); - - ArgumentCaptor actionPlanCaptor = ArgumentCaptor.forClass(ActionPlan.class); - Result result = service.updateActionPlan("ABCD", parameters); - assertThat(result.ok()).isTrue(); - - verify(actionPlanService).update(actionPlanCaptor.capture(), any(UserSession.class)); - ActionPlan actionPlan = actionPlanCaptor.getValue(); - - assertThat(actionPlan).isNotNull(); - assertThat(actionPlan.key()).isNotNull(); - assertThat(actionPlan.name()).isEqualTo("New Long term"); - assertThat(actionPlan.description()).isEqualTo("New Long term issues"); - assertThat(actionPlan.deadLine()).isNotNull(); - assertThat(actionPlan.projectKey()).isEqualTo("org.sonar.MultiSample"); - } - - @Test - public void not_update_action_plan_when_action_plan_is_not_found() { - when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(null); - - Result result = service.updateActionPlan("ABCD", null); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.errors.action_plan_does_not_exist", "ABCD")); - } - - @Test - public void delete_action_plan() { - when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term")); - - Result result = service.deleteActionPlan("ABCD"); - verify(actionPlanService).delete(eq("ABCD"), any(UserSession.class)); - assertThat(result.ok()).isTrue(); - } - - @Test - public void not_delete_action_plan_if_action_plan_not_found() { - when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(null); - - Result result = service.deleteActionPlan("ABCD"); - verify(actionPlanService, never()).delete(eq("ABCD"), any(UserSession.class)); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.errors.action_plan_does_not_exist", "ABCD")); - } - - @Test - public void close_action_plan() { - when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term")); - - Result result = service.closeActionPlan("ABCD"); - verify(actionPlanService).setStatus(eq("ABCD"), eq("CLOSED"), any(UserSession.class)); - assertThat(result.ok()).isTrue(); - } - - @Test - public void open_action_plan() { - when(actionPlanService.findByKey(eq("ABCD"), any(UserSession.class))).thenReturn(DefaultActionPlan.create("Long term")); - - Result result = service.openActionPlan("ABCD"); - verify(actionPlanService).setStatus(eq("ABCD"), eq("OPEN"), any(UserSession.class)); - assertThat(result.ok()).isTrue(); - } - - @Test - public void get_error_on_action_plan_result_when_no_project() { - Map parameters = newHashMap(); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - - Result result = service.createActionPlanResult(parameters); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("errors.cant_be_empty", "project")); - } - - @Test - public void get_error_on_action_plan_result_when_no_name() { - Map parameters = newHashMap(); - parameters.put("name", null); - parameters.put("description", "Long term issues"); - parameters.put("project", "org.sonar.Sample"); - - Result result = service.createActionPlanResult(parameters); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("errors.cant_be_empty", "name")); - } - - @Test - public void get_error_on_action_plan_result_when_name_is_too_long() { - Map parameters = newHashMap(); - parameters.put("name", createLongString(201)); - parameters.put("description", "Long term issues"); - parameters.put("project", "org.sonar.Sample"); - - Result result = service.createActionPlanResult(parameters); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("errors.is_too_long", "name", 200)); - } - - @Test - public void get_error_on_action_plan_result_when_description_is_too_long() { - Map parameters = newHashMap(); - parameters.put("name", "Long term"); - parameters.put("description", createLongString(1001)); - parameters.put("project", "org.sonar.Sample"); - - Result result = service.createActionPlanResult(parameters); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("errors.is_too_long", "description", 1000)); - } - - @Test - public void get_error_on_action_plan_result_when_dead_line_use_wrong_format() { - Map parameters = newHashMap(); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - parameters.put("project", "org.sonar.Sample"); - parameters.put("deadLine", "18/05/2013"); - - Result result = service.createActionPlanResult(parameters); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("errors.is_not_valid", "date")); - } - - @Test - public void get_error_on_action_plan_result_when_dead_line_is_in_the_past() { - Map parameters = newHashMap(); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - parameters.put("project", "org.sonar.Sample"); - parameters.put("deadLine", "2000-01-01"); - - Result result = service.createActionPlanResult(parameters); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.date_cant_be_in_past")); - } - - @Test - public void get_error_on_action_plan_result_when_name_is_already_used_for_project() { - Map parameters = newHashMap(); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - parameters.put("project", "org.sonar.Sample"); - - when(actionPlanService.isNameAlreadyUsedForProject(anyString(), anyString())).thenReturn(true); - - Result result = service.createActionPlanResult(parameters, DefaultActionPlan.create("Short term")); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.same_name_in_same_project")); - } - - @Test - public void get_error_on_action_plan_result_when_project_not_found() { - Map parameters = newHashMap(); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - parameters.put("project", "org.sonar.Sample"); - - when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null); - - Result result = service.createActionPlanResult(parameters); - assertThat(result.ok()).isFalse(); - assertThat(result.errors()).contains(Result.Message.ofL10n("action_plans.errors.project_does_not_exist", "org.sonar.Sample")); - } - - @Test - public void test_changelog_from_issue_key() throws Exception { - IssueChangelog changelog = new IssueChangelog(Collections.emptyList(), Collections.emptyList()); - when(changelogService.changelog(eq("ABCDE"))).thenReturn(changelog); - - IssueChangelog result = service.changelog("ABCDE"); - - assertThat(result).isSameAs(changelog); - } - - @Test - public void test_changelog_from_issue() throws Exception { - Issue issue = new DefaultIssue().setKey("ABCDE"); - - IssueChangelog changelog = new IssueChangelog(Collections.emptyList(), Collections.emptyList()); - when(changelogService.changelog(eq(issue))).thenReturn(changelog); - - IssueChangelog result = service.changelog(issue); - - assertThat(result).isSameAs(changelog); - } - - @Test - public void create_issue_filter() { - Map parameters = newHashMap(); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - - service.createIssueFilter(parameters); - - ArgumentCaptor issueFilterCaptor = ArgumentCaptor.forClass(DefaultIssueFilter.class); - verify(issueFilterService).save(issueFilterCaptor.capture(), any(UserSession.class)); - DefaultIssueFilter issueFilter = issueFilterCaptor.getValue(); - assertThat(issueFilter.name()).isEqualTo("Long term"); - assertThat(issueFilter.description()).isEqualTo("Long term issues"); - } - - @Test - public void update_issue_filter() { - Map parameters = newHashMap(); - parameters.put("id", "10"); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - parameters.put("user", "John"); - - service.updateIssueFilter(parameters); - - ArgumentCaptor issueFilterCaptor = ArgumentCaptor.forClass(DefaultIssueFilter.class); - verify(issueFilterService).update(issueFilterCaptor.capture(), any(UserSession.class)); - DefaultIssueFilter issueFilter = issueFilterCaptor.getValue(); - assertThat(issueFilter.id()).isEqualTo(10L); - assertThat(issueFilter.name()).isEqualTo("Long term"); - assertThat(issueFilter.description()).isEqualTo("Long term issues"); - } - - @Test - public void update_data() { - Map data = newHashMap(); - service.updateIssueFilterQuery(10L, data); - verify(issueFilterService).updateFilterQuery(eq(10L), eq(data), any(UserSession.class)); - } - - @Test - public void delete_issue_filter() { - service.deleteIssueFilter(1L); - verify(issueFilterService).delete(eq(1L), any(UserSession.class)); - } - - @Test - public void copy_issue_filter() { - Map parameters = newHashMap(); - parameters.put("name", "Copy of Long term"); - parameters.put("description", "Copy of Long term issues"); - - service.copyIssueFilter(1L, parameters); - - ArgumentCaptor issueFilterCaptor = ArgumentCaptor.forClass(DefaultIssueFilter.class); - verify(issueFilterService).copy(eq(1L), issueFilterCaptor.capture(), any(UserSession.class)); - DefaultIssueFilter issueFilter = issueFilterCaptor.getValue(); - assertThat(issueFilter.name()).isEqualTo("Copy of Long term"); - assertThat(issueFilter.description()).isEqualTo("Copy of Long term issues"); - } - - @Test - public void get_error_on_create_issue_filter_result_when_no_name() { - Map parameters = newHashMap(); - parameters.put("name", ""); - parameters.put("description", "Long term issues"); - parameters.put("user", "John"); - - try { - service.createIssueFilterResultForNew(parameters); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class); - checkBadRequestException(e, "errors.cant_be_empty", "name"); - } - } - - @Test - public void get_error_on_create_issue_filter_result_when_name_is_too_long() { - Map parameters = newHashMap(); - parameters.put("name", createLongString(101)); - parameters.put("description", "Long term issues"); - parameters.put("user", "John"); - - try { - service.createIssueFilterResultForNew(parameters); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class); - checkBadRequestException(e, "errors.is_too_long", "name", 100); - } - } - - @Test - public void get_error_on_create_issue_filter_result_when_description_is_too_long() { - Map parameters = newHashMap(); - parameters.put("name", "Long term"); - parameters.put("description", createLongString(4001)); - parameters.put("user", "John"); - - try { - service.createIssueFilterResultForNew(parameters); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class); - checkBadRequestException(e, "errors.is_too_long", "description", 4000); - } - } - - @Test - public void get_error_on_create_issue_filter_result_when_id_is_null_on_update() { - Map parameters = newHashMap(); - parameters.put("id", null); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - parameters.put("user", "John"); - - try { - service.createIssueFilterResultForUpdate(parameters); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class); - checkBadRequestException(e, "errors.cant_be_empty", "id"); - } - } - - @Test - public void get_error_on_create_issue_filter_result_when_user_is_null_on_update() { - Map parameters = newHashMap(); - parameters.put("id", "10"); - parameters.put("name", "All Open Issues"); - parameters.put("description", "Long term issues"); - parameters.put("user", null); - - try { - service.createIssueFilterResultForUpdate(parameters); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class); - checkBadRequestException(e, "errors.cant_be_empty", "user"); - } - } - - @Test - public void get_no_error_on_issue_filter_result_when_id_and_user_are_null_on_copy() { - Map parameters = newHashMap(); - parameters.put("id", null); - parameters.put("name", "Long term"); - parameters.put("description", "Long term issues"); - parameters.put("user", null); - - DefaultIssueFilter result = service.createIssueFilterResultForCopy(parameters); - assertThat(result).isNotNull(); - } - - @Test - public void execute_issue_filter_from_issue_query() { - service.execute(Maps.newHashMap()); - verify(issueFilterService).execute(any(IssueQuery.class)); - } - - @Test - public void execute_issue_filter_from_existing_filter() { - Map props = newHashMap(); - props.put("componentRoots", "struts"); - props.put("statuses", "OPEN"); - when(issueFilterService.deserializeIssueFilterQuery(any(DefaultIssueFilter.class))).thenReturn(props); - - Map overrideProps = newHashMap(); - overrideProps.put("statuses", "CLOSED"); - overrideProps.put("resolved", true); - overrideProps.put("pageSize", 20); - overrideProps.put("pageIndex", 2); - service.execute(10L, overrideProps); - ArgumentCaptor captor = ArgumentCaptor.forClass(IssueQuery.class); - verify(issueFilterService).execute(captor.capture()); - verify(issueFilterService).find(eq(10L), any(UserSession.class)); - - IssueQuery issueQuery = captor.getValue(); - assertThat(issueQuery.componentRoots()).contains("struts"); - assertThat(issueQuery.statuses()).contains("CLOSED"); - assertThat(issueQuery.resolved()).isTrue(); - assertThat(issueQuery.pageSize()).isEqualTo(20); - assertThat(issueQuery.pageIndex()).isEqualTo(2); - } - - @Test - public void serialize_filter_query() { - Map props = newHashMap(); - props.put("componentRoots", "struts"); - service.serializeFilterQuery(props); - verify(issueFilterService).serializeFilterQuery(props); - } - - @Test - public void deserialize_filter_query() { - DefaultIssueFilter issueFilter = new DefaultIssueFilter(); - service.deserializeFilterQuery(issueFilter); - verify(issueFilterService).deserializeIssueFilterQuery(issueFilter); - } - - @Test - public void sanitize_filter_query() { - Map query = newHashMap(); - query.put("statuses", "CLOSED"); - query.put("resolved", true); - query.put("unknown", "john"); - Map result = service.sanitizeFilterQuery(query); - assertThat(result.keySet()).containsOnly("statuses", "resolved"); - } - - @Test - public void find_user_issue_filters() { - service.findIssueFiltersForCurrentUser(); - verify(issueFilterService).findByUser(any(UserSession.class)); - } - - @Test - public void find_shared_issue_filters() { - service.findSharedFiltersForCurrentUser(); - verify(issueFilterService).findSharedFiltersWithoutUserFilters(any(UserSession.class)); - } - - @Test - public void find_favourite_issue_filters() { - service.findFavouriteIssueFiltersForCurrentUser(); - verify(issueFilterService).findFavoriteFilters(any(UserSession.class)); - } - - @Test - public void toggle_favourite_issue_filter() { - service.toggleFavouriteIssueFilter(10L); - verify(issueFilterService).toggleFavouriteIssueFilter(eq(10L), any(UserSession.class)); - } - - @Test - public void check_if_user_is_authorized_to_see_issue_filter() { - DefaultIssueFilter issueFilter = new DefaultIssueFilter(); - service.isUserAuthorized(issueFilter); - verify(issueFilterService).getLoggedLogin(any(UserSession.class)); - verify(issueFilterService).verifyCurrentUserCanReadFilter(eq(issueFilter), anyString()); - } - - @Test - public void check_if_user_can_share_issue_filter() { - service.canUserShareIssueFilter(); - verify(issueFilterService).canShareFilter(any(UserSession.class)); - } - - @Test - public void execute_bulk_change() { - Map 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"); - service.bulkChange(params, "My comment", true); - verify(issueBulkChangeService).execute(any(IssueBulkChangeQuery.class), any(UserSession.class)); - } - - @Test - public void format_changelog() { - FieldDiffs fieldDiffs = new FieldDiffs(); - service.formatChangelog(fieldDiffs); - verify(changelogService).formatDiffs(eq(fieldDiffs)); - } - - private void checkBadRequestException(Exception e, String key, Object... params) { - BadRequestException exception = (BadRequestException) e; - Message msg = exception.errors().messages().get(0); - assertThat(msg.getKey()).isEqualTo(key); - assertThat(msg.getParams()).containsOnly(params); - } - - private String createLongString(int size) { - String result = ""; - for (int i = 0; i < size; i++) { - result += "c"; - } - return result; - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java deleted file mode 100644 index ac4e5069a02..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.issue; - -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.sonar.api.issue.DefaultTransitions; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.IssueQuery; -import org.sonar.api.resources.Qualifiers; -import org.sonar.api.resources.Scopes; -import org.sonar.api.rule.Severity; -import org.sonar.api.security.DefaultGroups; -import org.sonar.api.utils.DateUtils; -import org.sonar.api.web.UserRole; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.issue.db.IssueDto; -import org.sonar.core.permission.GlobalPermissions; -import org.sonar.core.permission.PermissionFacade; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.rule.RuleDto; -import org.sonar.core.user.UserDto; -import org.sonar.server.component.SnapshotTesting; -import org.sonar.server.component.db.ComponentDao; -import org.sonar.server.component.db.SnapshotDao; -import org.sonar.server.db.DbClient; -import org.sonar.server.issue.db.IssueDao; -import org.sonar.server.issue.index.IssueIndex; -import org.sonar.server.rule.RuleTesting; -import org.sonar.server.rule.db.RuleDao; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.QueryContext; -import org.sonar.server.tester.ServerTester; -import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; - -import java.util.Date; -import java.util.UUID; - -import static org.fest.assertions.Assertions.assertThat; - -public class IssueServiceMediumTest { - - @ClassRule - public static ServerTester tester = new ServerTester(); - - DbClient db; - IndexClient indexClient; - DbSession session; - IssueService service; - UserSession userSession; - - RuleDto rule; - ComponentDto project; - ComponentDto resource; - - @Before - public void setUp() throws Exception { - tester.clearDbAndIndexes(); - db = tester.get(DbClient.class); - indexClient = tester.get(IndexClient.class); - session = db.openSession(false); - service = tester.get(IssueService.class); - - rule = RuleTesting.newXooX1(); - tester.get(RuleDao.class).insert(session, rule); - - project = new ComponentDto() - .setKey("MyProject") - .setLongName("My Project") - .setQualifier(Qualifiers.PROJECT) - .setScope(Scopes.PROJECT); - tester.get(ComponentDao.class).insert(session, project); - tester.get(SnapshotDao.class).insert(session, SnapshotTesting.createForComponent(project)); - - resource = new ComponentDto() - .setProjectId(project.getId()) - .setKey("MyComponent") - .setLongName("My Component"); - tester.get(ComponentDao.class).insert(session, resource); - tester.get(SnapshotDao.class).insert(session, SnapshotTesting.createForComponent(resource)); - - // project can be seen by anyone - tester.get(PermissionFacade.class).insertGroupPermission(project.getId(), DefaultGroups.ANYONE, UserRole.USER, session); - db.issueAuthorizationDao().synchronizeAfter(session, new Date(0)); - - UserDto user = new UserDto().setLogin("gandalf").setName("Gandalf"); - db.userDao().insert(session, user); - tester.get(PermissionFacade.class).insertUserPermission(project.getId(), user.getId(), UserRole.USER, session); - - userSession = MockUserSession.create().setLogin(user.getLogin()).setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN) - .addProjectPermissions(UserRole.USER, project.key()); - - session.commit(); - } - - @After - public void after() { - session.close(); - } - - @Test - public void can_facet() throws Exception { - IssueDto issue1 = getIssue().setActionPlanKey("P1"); - IssueDto issue2 = getIssue().setActionPlanKey("P2").setResolution("NONE"); - tester.get(IssueDao.class).insert(session, issue1, issue2); - session.commit(); - - org.sonar.server.search.Result result = service.search(IssueQuery.builder().build(), new QueryContext()); - assertThat(result.getHits()).hasSize(2); - assertThat(result.getFacets()).isEmpty(); - - result = service.search(IssueQuery.builder().build(), new QueryContext().setFacet(true)); - assertThat(result.getFacets().keySet()).hasSize(4); - assertThat(result.getFacetKeys("actionPlan")).hasSize(2); - } - - @Test - public void do_transition() { - IssueDto issue = getIssue(); - tester.get(IssueDao.class).insert(session, issue); - session.commit(); - - assertThat(db.issueDao().getByKey(session, issue.getKey())).isNotNull(); - - IssueTesting.assertIsEquivalent(issue, indexClient.get(IssueIndex.class).getByKey(issue.getKey())); - - service.doTransition(issue.getKey(), DefaultTransitions.CONFIRM, userSession); - Issue issueDoc = indexClient.get(IssueIndex.class).getByKey(issue.getKey()); - assertThat(issueDoc.status()).isEqualTo(Issue.STATUS_CONFIRMED); - } - - private IssueDto getIssue() { - return new IssueDto() - .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) - .setIssueUpdateDate(DateUtils.parseDate("2014-12-04")) - .setRule(rule) - .setDebt(10L) - .setRootComponent(project) - .setComponent(resource) - .setStatus(Issue.STATUS_OPEN) - .setResolution(null) - .setSeverity(Severity.MAJOR) - .setKee(UUID.randomUUID().toString()); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java deleted file mode 100644 index 4f555e56a9b..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java +++ /dev/null @@ -1,534 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.server.issue; - -import com.google.common.collect.Multiset; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.sonar.api.issue.ActionPlan; -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.rule.RuleKey; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RuleFinder; -import org.sonar.api.user.User; -import org.sonar.api.user.UserFinder; -import org.sonar.api.web.UserRole; -import org.sonar.core.issue.DefaultActionPlan; -import org.sonar.core.issue.IssueNotifications; -import org.sonar.core.issue.IssueUpdater; -import org.sonar.core.issue.db.IssueDao; -import org.sonar.core.issue.db.IssueStorage; -import org.sonar.core.issue.workflow.IssueWorkflow; -import org.sonar.core.issue.workflow.Transition; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.preview.PreviewCache; -import org.sonar.core.resource.ResourceDao; -import org.sonar.core.resource.ResourceDto; -import org.sonar.core.resource.ResourceQuery; -import org.sonar.core.rule.RuleDto; -import org.sonar.core.user.AuthorizationDao; -import org.sonar.core.user.DefaultUser; -import org.sonar.server.issue.actionplan.ActionPlanService; -import org.sonar.server.user.UserSession; - -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import static com.google.common.collect.Lists.newArrayList; -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.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; - -@RunWith(MockitoJUnitRunner.class) -public class IssueServiceTest { - - @Mock - DefaultIssueFinder finder; - - @Mock - IssueWorkflow workflow; - - @Mock - IssueUpdater issueUpdater; - - @Mock - IssueStorage issueStorage; - - @Mock - IssueNotifications issueNotifications; - - @Mock - ActionPlanService actionPlanService; - - @Mock - RuleFinder ruleFinder; - - @Mock - ResourceDao resourceDao; - - @Mock - IssueDao issueDao; - - @Mock - AuthorizationDao authorizationDao; - - @Mock - UserFinder userFinder; - - @Mock - UserSession userSession; - - @Mock - IssueQueryResult issueQueryResult; - - @Mock - ResourceDto resource; - - @Mock - ResourceDto project; - - Transition transition = Transition.create("reopen", Issue.STATUS_RESOLVED, Issue.STATUS_REOPENED); - - DefaultIssue issue = new DefaultIssue().setKey("ABCD"); - - IssueService issueService; - - @Before - public void before() { - when(userSession.isLoggedIn()).thenReturn(true); - when(userSession.userId()).thenReturn(10); - when(userSession.login()).thenReturn("arthur"); - - when(authorizationDao.isAuthorizedComponentKey(anyString(), eq(10), anyString())).thenReturn(true); - when(finder.find(any(IssueQuery.class))).thenReturn(issueQueryResult); - when(issueQueryResult.issues()).thenReturn(newArrayList((Issue) issue)); - when(issueQueryResult.first()).thenReturn(issue); - - when(resource.getKey()).thenReturn("org.sonar.Sample"); - when(project.getKey()).thenReturn("Sample"); - - issueService = new IssueService(finder, workflow, issueStorage, issueUpdater, issueNotifications, actionPlanService, ruleFinder, resourceDao, issueDao, - authorizationDao, userFinder, mock(PreviewCache.class)); - } - - @Test - public void load_issue() { - IssueQueryResult result = issueService.loadIssue("ABCD"); - assertThat(result).isEqualTo(issueQueryResult); - } - - @Test - public void fail_to_load_issue() { - when(issueQueryResult.issues()).thenReturn(Collections.emptyList()); - when(finder.find(any(IssueQuery.class))).thenReturn(issueQueryResult); - - try { - issueService.loadIssue("ABCD"); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Issue not found: ABCD"); - } - } - - @Test - public void list_status() { - issueService.listStatus(); - verify(workflow).statusKeys(); - } - - @Test - public void list_transitions() { - List transitions = newArrayList(transition); - when(workflow.outTransitions(issue)).thenReturn(transitions); - - List result = issueService.listTransitions("ABCD", userSession); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(transition); - } - - @Test - public void return_no_transition() { - when(issueQueryResult.first()).thenReturn(null); - when(issueQueryResult.issues()).thenReturn(newArrayList((Issue) new DefaultIssue())); - - assertThat(issueService.listTransitions("ABCD", userSession)).isEmpty(); - verifyZeroInteractions(workflow); - } - - @Test - public void do_transition() { - when(workflow.doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class))).thenReturn(true); - - Issue result = issueService.doTransition("ABCD", transition.key(), userSession); - assertThat(result).isNotNull(); - - ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); - verify(workflow).doTransition(eq(issue), eq(transition.key()), measureCaptor.capture()); - verify(issueStorage).save(issue); - - IssueChangeContext issueChangeContext = measureCaptor.getValue(); - assertThat(issueChangeContext.login()).isEqualTo("arthur"); - assertThat(issueChangeContext.date()).isNotNull(); - - verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); - } - - @Test - public void not_do_transition() { - when(workflow.doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class))).thenReturn(false); - - Issue result = issueService.doTransition("ABCD", transition.key(), userSession); - assertThat(result).isNotNull(); - verify(workflow).doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class)); - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(issueNotifications); - } - - @Test - public void fail_do_transition_if_not_logged() { - when(userSession.isLoggedIn()).thenReturn(false); - try { - issueService.doTransition("ABCD", transition.key(), userSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("User is not logged in"); - } - verifyZeroInteractions(authorizationDao); - } - - @Test - public void assign() { - String assignee = "perceval"; - User user = new DefaultUser(); - - when(userFinder.findByLogin(assignee)).thenReturn(user); - when(issueUpdater.assign(eq(issue), eq(user), any(IssueChangeContext.class))).thenReturn(true); - - Issue result = issueService.assign("ABCD", assignee, userSession); - assertThat(result).isNotNull(); - - ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); - verify(issueUpdater).assign(eq(issue), eq(user), measureCaptor.capture()); - verify(issueStorage).save(issue); - - IssueChangeContext issueChangeContext = measureCaptor.getValue(); - assertThat(issueChangeContext.login()).isEqualTo("arthur"); - assertThat(issueChangeContext.date()).isNotNull(); - - verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); - } - - @Test - public void unassign() { - when(issueUpdater.assign(eq(issue), eq((User) null), any(IssueChangeContext.class))).thenReturn(true); - - Issue result = issueService.assign("ABCD", null, userSession); - assertThat(result).isNotNull(); - - ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); - verify(issueUpdater).assign(eq(issue), eq((User) null), measureCaptor.capture()); - verify(issueStorage).save(issue); - - IssueChangeContext issueChangeContext = measureCaptor.getValue(); - assertThat(issueChangeContext.login()).isEqualTo("arthur"); - assertThat(issueChangeContext.date()).isNotNull(); - - verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); - verify(userFinder, never()).findByLogin(anyString()); - } - - @Test - public void not_assign() { - String assignee = "perceval"; - User user = new DefaultUser(); - - when(userFinder.findByLogin(assignee)).thenReturn(user); - when(issueUpdater.assign(eq(issue), eq(user), any(IssueChangeContext.class))).thenReturn(false); - - Issue result = issueService.assign("ABCD", assignee, userSession); - assertThat(result).isNotNull(); - - verify(issueUpdater).assign(eq(issue), eq(user), any(IssueChangeContext.class)); - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(issueNotifications); - } - - @Test - public void fail_assign_if_assignee_not_found() { - String assignee = "perceval"; - - when(userFinder.findByLogin(assignee)).thenReturn(null); - - try { - issueService.assign("ABCD", assignee, userSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown user: perceval"); - } - - verifyZeroInteractions(issueUpdater); - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(issueNotifications); - } - - @Test - public void plan() { - String actionPlanKey = "EFGH"; - - ActionPlan actionPlan = new DefaultActionPlan(); - - when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(actionPlan); - when(issueUpdater.plan(eq(issue), eq(actionPlan), any(IssueChangeContext.class))).thenReturn(true); - - Issue result = issueService.plan("ABCD", actionPlanKey, userSession); - assertThat(result).isNotNull(); - - ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); - verify(issueUpdater).plan(eq(issue), eq(actionPlan), measureCaptor.capture()); - verify(issueStorage).save(issue); - - IssueChangeContext issueChangeContext = measureCaptor.getValue(); - assertThat(issueChangeContext.login()).isEqualTo("arthur"); - assertThat(issueChangeContext.date()).isNotNull(); - - verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); - } - - @Test - public void unplan() { - when(issueUpdater.plan(eq(issue), eq((ActionPlan) null), any(IssueChangeContext.class))).thenReturn(true); - - Issue result = issueService.plan("ABCD", null, userSession); - assertThat(result).isNotNull(); - - ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); - verify(issueUpdater).plan(eq(issue), eq((ActionPlan) null), measureCaptor.capture()); - verify(issueStorage).save(issue); - - IssueChangeContext issueChangeContext = measureCaptor.getValue(); - assertThat(issueChangeContext.login()).isEqualTo("arthur"); - assertThat(issueChangeContext.date()).isNotNull(); - - verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); - verify(actionPlanService, never()).findByKey(anyString(), any(UserSession.class)); - } - - @Test - public void not_plan() { - String actionPlanKey = "EFGH"; - - ActionPlan actionPlan = new DefaultActionPlan(); - - when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(actionPlan); - when(issueUpdater.plan(eq(issue), eq(actionPlan), any(IssueChangeContext.class))).thenReturn(false); - - Issue result = issueService.plan("ABCD", actionPlanKey, userSession); - assertThat(result).isNotNull(); - verify(issueUpdater).plan(eq(issue), eq(actionPlan), any(IssueChangeContext.class)); - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(issueNotifications); - } - - @Test - public void fail_plan_if_action_plan_not_found() { - String actionPlanKey = "EFGH"; - - when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(null); - try { - issueService.plan("ABCD", actionPlanKey, userSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown action plan: EFGH"); - } - - verifyZeroInteractions(issueUpdater); - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(issueNotifications); - } - - @Test - public void set_severity() { - String severity = "MINOR"; - when(issueUpdater.setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class))).thenReturn(true); - - Issue result = issueService.setSeverity("ABCD", severity, userSession); - assertThat(result).isNotNull(); - - ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); - verify(issueUpdater).setManualSeverity(eq(issue), eq(severity), measureCaptor.capture()); - verify(issueStorage).save(issue); - - IssueChangeContext issueChangeContext = measureCaptor.getValue(); - assertThat(issueChangeContext.login()).isEqualTo("arthur"); - assertThat(issueChangeContext.date()).isNotNull(); - - verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); - } - - @Test - public void not_set_severity() { - String severity = "MINOR"; - when(issueUpdater.setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class))).thenReturn(false); - - Issue result = issueService.setSeverity("ABCD", severity, userSession); - assertThat(result).isNotNull(); - verify(issueUpdater).setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class)); - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(issueNotifications); - } - - @Test - public void create_manual_issue() { - RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); - when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey")); - when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); - when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); - - Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "Fix it", null, null, userSession); - assertThat(result).isNotNull(); - assertThat(result.message()).isEqualTo("Fix it"); - assertThat(result.creationDate()).isNotNull(); - assertThat(result.updateDate()).isNotNull(); - - verify(issueStorage).save(any(DefaultIssue.class)); - verify(authorizationDao).isAuthorizedComponentKey(anyString(), anyInt(), eq(UserRole.USER)); - } - - @Test - public void create_manual_issue_use_rule_name_if_no_message() { - RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); - when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey").setName("Manual Rule")); - when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); - when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); - - Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "", null, null, userSession); - assertThat(result).isNotNull(); - assertThat(result.message()).isEqualTo("Manual Rule"); - assertThat(result.creationDate()).isNotNull(); - assertThat(result.updateDate()).isNotNull(); - - verify(issueStorage).save(any(DefaultIssue.class)); - verify(authorizationDao).isAuthorizedComponentKey(anyString(), anyInt(), eq(UserRole.USER)); - } - - @Test - public void fail_create_manual_issue_if_not_having_required_role() { - RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); - when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); - when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); - when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey")); - when(authorizationDao.isAuthorizedComponentKey(anyString(), eq(10), anyString())).thenReturn(false); - - try { - issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null, userSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("User does not have the required role"); - } - } - - @Test - public void fail_create_manual_issue_if_not_manual_rule() { - when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); - when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); - - RuleKey ruleKey = RuleKey.of("squid", "s100"); - try { - issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null, userSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Issues can be created only on rules marked as 'manual': squid:s100"); - } - verifyZeroInteractions(issueStorage); - } - - @Test - public void fail_create_manual_issue_if_rule_not_found() { - when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); - when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); - - RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); - when(ruleFinder.findByKey(ruleKey)).thenReturn(null); - try { - issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null, userSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown rule: manual:manualRuleKey"); - } - verifyZeroInteractions(issueStorage); - } - - @Test - public void fail_create_manual_issue_if_component_not_found() { - RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); - when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey")); - when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null); - try { - issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null, userSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown component: org.sonar.Sample"); - } - verifyZeroInteractions(issueStorage); - } - - @Test - public void find_rules_by_component() throws Exception { - DbSession session = mock(DbSession.class); - String componentKey = "org.sonar.Sample"; - - Date date = new Date(); - when(issueDao.findRulesByComponent(componentKey, date, session)).thenReturn(newArrayList( - RuleDto.createFor(RuleKey.of("repo", "rule")).setName("Rule name"), - RuleDto.createFor(RuleKey.of("repo", "rule")).setName("Rule name") - )); - - RulesAggregation result = issueService.findRulesByComponent(componentKey, date, session); - assertThat(result.rules()).hasSize(1); - } - - @Test - public void find_rules_by_severity() throws Exception { - DbSession session = mock(DbSession.class); - String componentKey = "org.sonar.Sample"; - - Date date = new Date(); - when(issueDao.findSeveritiesByComponent(componentKey, date, session)).thenReturn(newArrayList("MAJOR", "MAJOR", "INFO")); - - Multiset result = issueService.findSeveritiesByComponent(componentKey, date, session); - assertThat(result.count("MAJOR")).isEqualTo(2); - assertThat(result.count("INFO")).isEqualTo(1); - assertThat(result.count("UNKNOWN")).isEqualTo(0); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/OldDefaultIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/OldDefaultIssueServiceTest.java new file mode 100644 index 00000000000..ceb1db14f4b --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/OldDefaultIssueServiceTest.java @@ -0,0 +1,538 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.issue; + +import com.google.common.collect.Multiset; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.issue.ActionPlan; +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.rule.RuleKey; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; +import org.sonar.api.user.User; +import org.sonar.api.user.UserFinder; +import org.sonar.api.web.UserRole; +import org.sonar.core.issue.DefaultActionPlan; +import org.sonar.core.issue.IssueNotifications; +import org.sonar.core.issue.IssueUpdater; +import org.sonar.core.issue.db.IssueDao; +import org.sonar.core.issue.db.IssueStorage; +import org.sonar.core.issue.workflow.IssueWorkflow; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.preview.PreviewCache; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.resource.ResourceDto; +import org.sonar.core.resource.ResourceQuery; +import org.sonar.core.rule.RuleDto; +import org.sonar.core.user.AuthorizationDao; +import org.sonar.core.user.DefaultUser; +import org.sonar.server.issue.actionplan.ActionPlanService; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.user.UserSession; + +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; +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.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class OldDefaultIssueServiceTest { + + @Mock + DefaultIssueFinder finder; + + @Mock + IssueWorkflow workflow; + + @Mock + IssueUpdater issueUpdater; + + @Mock + IssueStorage issueStorage; + + @Mock + IssueNotifications issueNotifications; + + @Mock + ActionPlanService actionPlanService; + + @Mock + RuleFinder ruleFinder; + + @Mock + ResourceDao resourceDao; + + @Mock + IssueDao issueDao; + + @Mock + AuthorizationDao authorizationDao; + + @Mock + UserFinder userFinder; + + UserSession userSession; + + @Mock + IssueQueryResult issueQueryResult; + + @Mock + ResourceDto resource; + + @Mock + ResourceDto project; + + Transition transition = Transition.create("reopen", Issue.STATUS_RESOLVED, Issue.STATUS_REOPENED); + + DefaultIssue issue = new DefaultIssue().setKey("ABCD"); + + OldIssueService issueService; + + @Before + public void before() { + userSession = MockUserSession.set() + .setLogin("arthur") + .setUserId(10) + .setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN) + .addProjectPermissions(UserRole.USER, project.getKey()) + .addProjectPermissions(UserRole.ISSUE_ADMIN, project.getKey()); + + when(authorizationDao.isAuthorizedComponentKey(anyString(), eq(10), anyString())).thenReturn(true); + when(finder.find(any(IssueQuery.class))).thenReturn(issueQueryResult); + when(issueQueryResult.issues()).thenReturn(newArrayList((Issue) issue)); + when(issueQueryResult.first()).thenReturn(issue); + + when(resource.getKey()).thenReturn("org.sonar.Sample"); + when(project.getKey()).thenReturn("Sample"); + + issueService = new OldIssueService(finder, workflow, issueStorage, issueUpdater, issueNotifications, actionPlanService, ruleFinder, resourceDao, issueDao, + authorizationDao, userFinder, mock(PreviewCache.class)); + } + + @Test + public void load_issue() { + IssueQueryResult result = issueService.loadIssue("ABCD"); + assertThat(result).isEqualTo(issueQueryResult); + } + + @Test + public void fail_to_load_issue() { + when(issueQueryResult.issues()).thenReturn(Collections.emptyList()); + when(finder.find(any(IssueQuery.class))).thenReturn(issueQueryResult); + + try { + issueService.loadIssue("ABCD"); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Issue not found: ABCD"); + } + } + + @Test + public void list_status() { + issueService.listStatus(); + verify(workflow).statusKeys(); + } + + @Test + public void list_transitions() { + List transitions = newArrayList(transition); + when(workflow.outTransitions(issue)).thenReturn(transitions); + + List result = issueService.listTransitions("ABCD"); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo(transition); + } + + @Test + public void return_no_transition() { + when(issueQueryResult.first()).thenReturn(null); + when(issueQueryResult.issues()).thenReturn(newArrayList((Issue) new DefaultIssue())); + + assertThat(issueService.listTransitions("ABCD")).isEmpty(); + verifyZeroInteractions(workflow); + } + + @Test + public void do_transition() { + when(workflow.doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class))).thenReturn(true); + + Issue result = issueService.doTransition("ABCD", transition.key()); + assertThat(result).isNotNull(); + + ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); + verify(workflow).doTransition(eq(issue), eq(transition.key()), measureCaptor.capture()); + verify(issueStorage).save(issue); + + IssueChangeContext issueChangeContext = measureCaptor.getValue(); + assertThat(issueChangeContext.login()).isEqualTo("arthur"); + assertThat(issueChangeContext.date()).isNotNull(); + + verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); + } + + @Test + public void not_do_transition() { + when(workflow.doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class))).thenReturn(false); + + Issue result = issueService.doTransition("ABCD", transition.key()); + assertThat(result).isNotNull(); + verify(workflow).doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class)); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); + } + + @Test + public void fail_do_transition_if_not_logged() { + MockUserSession.set(); + try { + issueService.doTransition("ABCD", transition.key()); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("User is not logged in"); + } + verifyZeroInteractions(authorizationDao); + } + + @Test + public void assign() { + String assignee = "perceval"; + User user = new DefaultUser(); + + when(userFinder.findByLogin(assignee)).thenReturn(user); + when(issueUpdater.assign(eq(issue), eq(user), any(IssueChangeContext.class))).thenReturn(true); + + Issue result = issueService.assign("ABCD", assignee); + assertThat(result).isNotNull(); + + ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); + verify(issueUpdater).assign(eq(issue), eq(user), measureCaptor.capture()); + verify(issueStorage).save(issue); + + IssueChangeContext issueChangeContext = measureCaptor.getValue(); + assertThat(issueChangeContext.login()).isEqualTo("arthur"); + assertThat(issueChangeContext.date()).isNotNull(); + + verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); + } + + @Test + public void unassign() { + when(issueUpdater.assign(eq(issue), eq((User) null), any(IssueChangeContext.class))).thenReturn(true); + + Issue result = issueService.assign("ABCD", null); + assertThat(result).isNotNull(); + + ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); + verify(issueUpdater).assign(eq(issue), eq((User) null), measureCaptor.capture()); + verify(issueStorage).save(issue); + + IssueChangeContext issueChangeContext = measureCaptor.getValue(); + assertThat(issueChangeContext.login()).isEqualTo("arthur"); + assertThat(issueChangeContext.date()).isNotNull(); + + verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); + verify(userFinder, never()).findByLogin(anyString()); + } + + @Test + public void not_assign() { + String assignee = "perceval"; + User user = new DefaultUser(); + + when(userFinder.findByLogin(assignee)).thenReturn(user); + when(issueUpdater.assign(eq(issue), eq(user), any(IssueChangeContext.class))).thenReturn(false); + + Issue result = issueService.assign("ABCD", assignee); + assertThat(result).isNotNull(); + + verify(issueUpdater).assign(eq(issue), eq(user), any(IssueChangeContext.class)); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); + } + + @Test + public void fail_assign_if_assignee_not_found() { + String assignee = "perceval"; + + when(userFinder.findByLogin(assignee)).thenReturn(null); + + try { + issueService.assign("ABCD", assignee); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown user: perceval"); + } + + verifyZeroInteractions(issueUpdater); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); + } + + @Test + public void plan() { + String actionPlanKey = "EFGH"; + + ActionPlan actionPlan = new DefaultActionPlan(); + + when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(actionPlan); + when(issueUpdater.plan(eq(issue), eq(actionPlan), any(IssueChangeContext.class))).thenReturn(true); + + Issue result = issueService.plan("ABCD", actionPlanKey); + assertThat(result).isNotNull(); + + ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); + verify(issueUpdater).plan(eq(issue), eq(actionPlan), measureCaptor.capture()); + verify(issueStorage).save(issue); + + IssueChangeContext issueChangeContext = measureCaptor.getValue(); + assertThat(issueChangeContext.login()).isEqualTo("arthur"); + assertThat(issueChangeContext.date()).isNotNull(); + + verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); + } + + @Test + public void unplan() { + when(issueUpdater.plan(eq(issue), eq((ActionPlan) null), any(IssueChangeContext.class))).thenReturn(true); + + Issue result = issueService.plan("ABCD", null); + assertThat(result).isNotNull(); + + ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); + verify(issueUpdater).plan(eq(issue), eq((ActionPlan) null), measureCaptor.capture()); + verify(issueStorage).save(issue); + + IssueChangeContext issueChangeContext = measureCaptor.getValue(); + assertThat(issueChangeContext.login()).isEqualTo("arthur"); + assertThat(issueChangeContext.date()).isNotNull(); + + verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); + verify(actionPlanService, never()).findByKey(anyString(), any(UserSession.class)); + } + + @Test + public void not_plan() { + String actionPlanKey = "EFGH"; + + ActionPlan actionPlan = new DefaultActionPlan(); + + when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(actionPlan); + when(issueUpdater.plan(eq(issue), eq(actionPlan), any(IssueChangeContext.class))).thenReturn(false); + + Issue result = issueService.plan("ABCD", actionPlanKey); + assertThat(result).isNotNull(); + verify(issueUpdater).plan(eq(issue), eq(actionPlan), any(IssueChangeContext.class)); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); + } + + @Test + public void fail_plan_if_action_plan_not_found() { + String actionPlanKey = "EFGH"; + + when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(null); + try { + issueService.plan("ABCD", actionPlanKey); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown action plan: EFGH"); + } + + verifyZeroInteractions(issueUpdater); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); + } + + @Test + public void set_severity() { + String severity = "MINOR"; + when(issueUpdater.setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class))).thenReturn(true); + + Issue result = issueService.setSeverity("ABCD", severity); + assertThat(result).isNotNull(); + + ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); + verify(issueUpdater).setManualSeverity(eq(issue), eq(severity), measureCaptor.capture()); + verify(issueStorage).save(issue); + + IssueChangeContext issueChangeContext = measureCaptor.getValue(); + assertThat(issueChangeContext.login()).isEqualTo("arthur"); + assertThat(issueChangeContext.date()).isNotNull(); + + verify(issueNotifications).sendChanges(eq(issue), eq(issueChangeContext), eq(issueQueryResult)); + } + + @Test + public void not_set_severity() { + String severity = "MINOR"; + when(issueUpdater.setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class))).thenReturn(false); + + Issue result = issueService.setSeverity("ABCD", severity); + assertThat(result).isNotNull(); + verify(issueUpdater).setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class)); + verifyZeroInteractions(issueStorage); + verifyZeroInteractions(issueNotifications); + } + + @Test + public void create_manual_issue() { + RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); + when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey")); + when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); + when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); + + Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "Fix it", null, null); + assertThat(result).isNotNull(); + assertThat(result.message()).isEqualTo("Fix it"); + assertThat(result.creationDate()).isNotNull(); + assertThat(result.updateDate()).isNotNull(); + + verify(issueStorage).save(any(DefaultIssue.class)); + verify(authorizationDao).isAuthorizedComponentKey(anyString(), anyInt(), eq(UserRole.USER)); + } + + @Test + public void create_manual_issue_use_rule_name_if_no_message() { + RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); + when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey").setName("Manual Rule")); + when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); + when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); + + Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "", null, null); + assertThat(result).isNotNull(); + assertThat(result.message()).isEqualTo("Manual Rule"); + assertThat(result.creationDate()).isNotNull(); + assertThat(result.updateDate()).isNotNull(); + + verify(issueStorage).save(any(DefaultIssue.class)); + verify(authorizationDao).isAuthorizedComponentKey(anyString(), anyInt(), eq(UserRole.USER)); + } + + @Test + public void fail_create_manual_issue_if_not_having_required_role() { + RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); + when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); + when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); + when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey")); + when(authorizationDao.isAuthorizedComponentKey(anyString(), eq(10), anyString())).thenReturn(false); + + try { + issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("User does not have the required role"); + } + } + + @Test + public void fail_create_manual_issue_if_not_manual_rule() { + when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); + when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); + + RuleKey ruleKey = RuleKey.of("squid", "s100"); + try { + issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Issues can be created only on rules marked as 'manual': squid:s100"); + } + verifyZeroInteractions(issueStorage); + } + + @Test + public void fail_create_manual_issue_if_rule_not_found() { + when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); + when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); + + RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); + when(ruleFinder.findByKey(ruleKey)).thenReturn(null); + try { + issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown rule: manual:manualRuleKey"); + } + verifyZeroInteractions(issueStorage); + } + + @Test + public void fail_create_manual_issue_if_component_not_found() { + RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); + when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey")); + when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null); + try { + issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown component: org.sonar.Sample"); + } + verifyZeroInteractions(issueStorage); + } + + @Test + public void find_rules_by_component() throws Exception { + DbSession session = mock(DbSession.class); + String componentKey = "org.sonar.Sample"; + + Date date = new Date(); + when(issueDao.findRulesByComponent(componentKey, date, session)).thenReturn(newArrayList( + RuleDto.createFor(RuleKey.of("repo", "rule")).setName("Rule name"), + RuleDto.createFor(RuleKey.of("repo", "rule")).setName("Rule name") + )); + + RulesAggregation result = issueService.findRulesByComponent(componentKey, date, session); + assertThat(result.rules()).hasSize(1); + } + + @Test + public void find_rules_by_severity() throws Exception { + DbSession session = mock(DbSession.class); + String componentKey = "org.sonar.Sample"; + + Date date = new Date(); + when(issueDao.findSeveritiesByComponent(componentKey, date, session)).thenReturn(newArrayList("MAJOR", "MAJOR", "INFO")); + + Multiset result = issueService.findSeveritiesByComponent(componentKey, date, session); + assertThat(result.count("MAJOR")).isEqualTo(2); + assertThat(result.count("INFO")).isEqualTo(1); + assertThat(result.count("UNKNOWN")).isEqualTo(0); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyDefaultIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyDefaultIssueServiceTest.java new file mode 100644 index 00000000000..27d10b67c22 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyDefaultIssueServiceTest.java @@ -0,0 +1,132 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.issue; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.sonar.api.issue.IssueFinder; +import org.sonar.api.issue.IssueQuery; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.DateUtils; +import org.sonar.api.web.UserRole; + +import java.util.Map; + +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.newHashMap; +import static java.util.Arrays.asList; +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.*; + +public class PublicRubyDefaultIssueServiceTest { + + IssueFinder finder = mock(IssueFinder.class); + PublicRubyIssueService facade = new PublicRubyIssueService(finder); + + @Test + public void find_by_issue_keys() throws Exception { + facade.find("ABCDE"); + verify(finder).find(argThat(new ArgumentMatcher() { + @Override + public boolean matches(Object o) { + IssueQuery query = (IssueQuery) o; + return query.issueKeys().contains("ABCDE") && UserRole.USER.equals(query.requiredRole()); + } + })); + } + + @Test + public void find_by_params() throws Exception { + facade.find(ImmutableMap.of("issues", Lists.newArrayList("ABCDE"))); + verify(finder).find(argThat(new ArgumentMatcher() { + @Override + public boolean matches(Object o) { + IssueQuery query = (IssueQuery) o; + return query.issueKeys().contains("ABCDE") && UserRole.USER.equals(query.requiredRole()); + } + })); + } + + @Test + public void create_query_from_parameters() { + Map map = newHashMap(); + map.put("issues", newArrayList("ABCDE1234")); + map.put("severities", newArrayList("MAJOR", "MINOR")); + map.put("statuses", newArrayList("CLOSED")); + map.put("resolutions", newArrayList("FALSE-POSITIVE")); + map.put("resolved", true); + map.put("components", newArrayList("org.apache")); + map.put("componentRoots", newArrayList("org.sonar")); + map.put("reporters", newArrayList("marilyn")); + map.put("assignees", newArrayList("joanna")); + map.put("languages", newArrayList("xoo")); + map.put("assigned", true); + map.put("planned", true); + map.put("hideRules", true); + map.put("createdAfter", "2013-04-16T09:08:24+0200"); + map.put("createdBefore", "2013-04-17T09:08:24+0200"); + map.put("rules", "squid:AvoidCycle,findbugs:NullReference"); + map.put("pageSize", 10l); + map.put("pageIndex", 50); + map.put("sort", "CREATION_DATE"); + map.put("asc", true); + + IssueQuery query = new PublicRubyIssueService(finder).toQuery(map); + assertThat(query.issueKeys()).containsOnly("ABCDE1234"); + assertThat(query.severities()).containsOnly("MAJOR", "MINOR"); + assertThat(query.statuses()).containsOnly("CLOSED"); + assertThat(query.resolutions()).containsOnly("FALSE-POSITIVE"); + assertThat(query.resolved()).isTrue(); + assertThat(query.components()).containsOnly("org.apache"); + assertThat(query.componentRoots()).containsOnly("org.sonar"); + assertThat(query.reporters()).containsOnly("marilyn"); + assertThat(query.assignees()).containsOnly("joanna"); + assertThat(query.languages()).containsOnly("xoo"); + assertThat(query.assigned()).isTrue(); + assertThat(query.planned()).isTrue(); + assertThat(query.hideRules()).isTrue(); + assertThat(query.rules()).hasSize(2); + assertThat(query.createdAfter()).isEqualTo(DateUtils.parseDateTime("2013-04-16T09:08:24+0200")); + assertThat(query.createdBefore()).isEqualTo(DateUtils.parseDateTime("2013-04-17T09:08:24+0200")); + assertThat(query.pageSize()).isEqualTo(10); + assertThat(query.pageIndex()).isEqualTo(50); + assertThat(query.sort()).isEqualTo(IssueQuery.SORT_BY_CREATION_DATE); + assertThat(query.asc()).isTrue(); + } + + @Test + public void parse_list_of_rules() { + assertThat(PublicRubyIssueService.toRules(null)).isNull(); + assertThat(PublicRubyIssueService.toRules("")).isEmpty(); + assertThat(PublicRubyIssueService.toRules("squid:AvoidCycle")).containsOnly(RuleKey.of("squid", "AvoidCycle")); + assertThat(PublicRubyIssueService.toRules("squid:AvoidCycle,findbugs:NullRef")).containsOnly(RuleKey.of("squid", "AvoidCycle"), RuleKey.of("findbugs", "NullRef")); + assertThat(PublicRubyIssueService.toRules(asList("squid:AvoidCycle", "findbugs:NullRef"))).containsOnly(RuleKey.of("squid", "AvoidCycle"), RuleKey.of("findbugs", "NullRef")); + } + + @Test + public void start() throws Exception { + facade.start(); + // nothing is done + verifyZeroInteractions(finder); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyIssueServiceTest.java deleted file mode 100644 index 95bc3a3fcf2..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyIssueServiceTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.issue; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import org.junit.Test; -import org.mockito.ArgumentMatcher; -import org.sonar.api.issue.IssueFinder; -import org.sonar.api.issue.IssueQuery; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.utils.DateUtils; -import org.sonar.api.web.UserRole; - -import java.util.Map; - -import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Maps.newHashMap; -import static java.util.Arrays.asList; -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.argThat; -import static org.mockito.Mockito.*; - -public class PublicRubyIssueServiceTest { - - IssueFinder finder = mock(IssueFinder.class); - PublicRubyIssueService facade = new PublicRubyIssueService(finder); - - @Test - public void find_by_issue_keys() throws Exception { - facade.find("ABCDE"); - verify(finder).find(argThat(new ArgumentMatcher() { - @Override - public boolean matches(Object o) { - IssueQuery query = (IssueQuery) o; - return query.issueKeys().contains("ABCDE") && UserRole.USER.equals(query.requiredRole()); - } - })); - } - - @Test - public void find_by_params() throws Exception { - facade.find(ImmutableMap.of("issues", Lists.newArrayList("ABCDE"))); - verify(finder).find(argThat(new ArgumentMatcher() { - @Override - public boolean matches(Object o) { - IssueQuery query = (IssueQuery) o; - return query.issueKeys().contains("ABCDE") && UserRole.USER.equals(query.requiredRole()); - } - })); - } - - @Test - public void create_query_from_parameters() { - Map map = newHashMap(); - map.put("issues", newArrayList("ABCDE1234")); - map.put("severities", newArrayList("MAJOR", "MINOR")); - map.put("statuses", newArrayList("CLOSED")); - map.put("resolutions", newArrayList("FALSE-POSITIVE")); - map.put("resolved", true); - map.put("components", newArrayList("org.apache")); - map.put("componentRoots", newArrayList("org.sonar")); - map.put("reporters", newArrayList("marilyn")); - map.put("assignees", newArrayList("joanna")); - map.put("languages", newArrayList("xoo")); - map.put("assigned", true); - map.put("planned", true); - map.put("hideRules", true); - map.put("createdAfter", "2013-04-16T09:08:24+0200"); - map.put("createdBefore", "2013-04-17T09:08:24+0200"); - map.put("rules", "squid:AvoidCycle,findbugs:NullReference"); - map.put("pageSize", 10l); - map.put("pageIndex", 50); - map.put("sort", "CREATION_DATE"); - map.put("asc", true); - - IssueQuery query = new PublicRubyIssueService(finder).toQuery(map); - assertThat(query.issueKeys()).containsOnly("ABCDE1234"); - assertThat(query.severities()).containsOnly("MAJOR", "MINOR"); - assertThat(query.statuses()).containsOnly("CLOSED"); - assertThat(query.resolutions()).containsOnly("FALSE-POSITIVE"); - assertThat(query.resolved()).isTrue(); - assertThat(query.components()).containsOnly("org.apache"); - assertThat(query.componentRoots()).containsOnly("org.sonar"); - assertThat(query.reporters()).containsOnly("marilyn"); - assertThat(query.assignees()).containsOnly("joanna"); - assertThat(query.languages()).containsOnly("xoo"); - assertThat(query.assigned()).isTrue(); - assertThat(query.planned()).isTrue(); - assertThat(query.hideRules()).isTrue(); - assertThat(query.rules()).hasSize(2); - assertThat(query.createdAfter()).isEqualTo(DateUtils.parseDateTime("2013-04-16T09:08:24+0200")); - assertThat(query.createdBefore()).isEqualTo(DateUtils.parseDateTime("2013-04-17T09:08:24+0200")); - assertThat(query.pageSize()).isEqualTo(10); - assertThat(query.pageIndex()).isEqualTo(50); - assertThat(query.sort()).isEqualTo(IssueQuery.SORT_BY_CREATION_DATE); - assertThat(query.asc()).isTrue(); - } - - @Test - public void parse_list_of_rules() { - assertThat(PublicRubyIssueService.toRules(null)).isNull(); - assertThat(PublicRubyIssueService.toRules("")).isEmpty(); - assertThat(PublicRubyIssueService.toRules("squid:AvoidCycle")).containsOnly(RuleKey.of("squid", "AvoidCycle")); - assertThat(PublicRubyIssueService.toRules("squid:AvoidCycle,findbugs:NullRef")).containsOnly(RuleKey.of("squid", "AvoidCycle"), RuleKey.of("findbugs", "NullRef")); - assertThat(PublicRubyIssueService.toRules(asList("squid:AvoidCycle", "findbugs:NullRef"))).containsOnly(RuleKey.of("squid", "AvoidCycle"), RuleKey.of("findbugs", "NullRef")); - } - - @Test - public void start() throws Exception { - facade.start(); - // nothing is done - verifyZeroInteractions(finder); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueBackendMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueBackendMediumTest.java index b2a231eb694..7cae13f4c4d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueBackendMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueBackendMediumTest.java @@ -50,7 +50,8 @@ import static org.fest.assertions.Assertions.assertThat; public class IssueBackendMediumTest { @ClassRule - public static ServerTester tester = new ServerTester(); + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); DbClient dbClient; IndexClient indexClient; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueDaoTest.java index 44d4efb6b31..5034e196290 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueDaoTest.java @@ -23,15 +23,20 @@ import com.google.common.collect.ImmutableMap; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.issue.db.IssueDto; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.persistence.DbSession; +import org.sonar.server.rule.RuleTesting; import java.util.Date; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class IssueDaoTest extends AbstractDaoTestCase { @@ -51,9 +56,42 @@ public class IssueDaoTest extends AbstractDaoTestCase { this.session.close(); } + @Test + public void get_by_key() { + setupData("shared", "get_by_key"); + + IssueDto issue = dao.getByKey(session, "ABCDE"); + assertThat(issue.getKee()).isEqualTo("ABCDE"); + assertThat(issue.getId()).isEqualTo(100L); + assertThat(issue.getComponentId()).isEqualTo(401); + assertThat(issue.getRootComponentId()).isEqualTo(399); + assertThat(issue.getRuleId()).isEqualTo(500); + assertThat(issue.getSeverity()).isEqualTo("BLOCKER"); + assertThat(issue.isManualSeverity()).isFalse(); + assertThat(issue.getMessage()).isNull(); + assertThat(issue.getLine()).isEqualTo(200); + assertThat(issue.getEffortToFix()).isEqualTo(4.2); + assertThat(issue.getStatus()).isEqualTo("OPEN"); + assertThat(issue.getResolution()).isEqualTo("FIXED"); + assertThat(issue.getChecksum()).isEqualTo("XXX"); + assertThat(issue.getAuthorLogin()).isEqualTo("karadoc"); + assertThat(issue.getReporter()).isEqualTo("arthur"); + assertThat(issue.getAssignee()).isEqualTo("perceval"); + assertThat(issue.getIssueAttributes()).isEqualTo("JIRA=FOO-1234"); + assertThat(issue.getIssueCreationDate()).isNotNull(); + assertThat(issue.getIssueUpdateDate()).isNotNull(); + assertThat(issue.getIssueCloseDate()).isNotNull(); + assertThat(issue.getCreatedAt()).isNotNull(); + assertThat(issue.getUpdatedAt()).isNotNull(); + assertThat(issue.getRuleRepo()).isEqualTo("squid"); + assertThat(issue.getRule()).isEqualTo("AvoidCycle"); + assertThat(issue.getComponentKey()).isEqualTo("Action.java"); + assertThat(issue.getRootComponentKey()).isEqualTo("struts"); + } + @Test public void find_after_dates() throws Exception { - setupData("shared", "should_select_all"); + setupData("shared", "some_issues"); Date t0 = new Date(0); assertThat(dao.findAfterDate(session, t0)).hasSize(3); @@ -68,4 +106,76 @@ public class IssueDaoTest extends AbstractDaoTestCase { assertThat(dao.findAfterDate(session, DateUtils.parseDate("2014-01-01"), ImmutableMap.of("project", "struts"))).hasSize(1); } + + @Test + public void insert() throws Exception { + when(system2.now()).thenReturn(DateUtils.parseDate("2013-05-22").getTime()); + + IssueDto dto = new IssueDto(); + dto.setComponent(new ComponentDto().setKey("struts:Action").setId(123L)); + dto.setRootComponent(new ComponentDto().setKey("struts").setId(100L)); + dto.setRule(RuleTesting.newDto(RuleKey.of("squid", "S001")).setId(200)); + dto.setKee("ABCDE"); + dto.setLine(500); + dto.setEffortToFix(3.14); + dto.setDebt(10L); + dto.setResolution("FIXED"); + dto.setStatus("RESOLVED"); + dto.setSeverity("BLOCKER"); + dto.setReporter("emmerik"); + dto.setAuthorLogin("morgan"); + dto.setAssignee("karadoc"); + dto.setActionPlanKey("current_sprint"); + dto.setIssueAttributes("JIRA=FOO-1234"); + dto.setChecksum("123456789"); + dto.setMessage("the message"); + + dto.setIssueCreationDate(DateUtils.parseDate("2013-05-18")); + dto.setIssueUpdateDate(DateUtils.parseDate("2013-05-19")); + dto.setIssueCloseDate(DateUtils.parseDate("2013-05-20")); + dto.setCreatedAt(DateUtils.parseDate("2013-05-21")); + dto.setUpdatedAt(DateUtils.parseDate("2013-05-22")); + + dao.insert(session, dto); + session.commit(); + + checkTables("insert", new String[]{"id"}, "issues"); + } + + @Test + public void update() throws Exception { + when(system2.now()).thenReturn(DateUtils.parseDate("2013-05-22").getTime()); + + setupData("update"); + + IssueDto dto = new IssueDto(); + dto.setComponent(new ComponentDto().setKey("struts:Action").setId(123L)); + dto.setRootComponent(new ComponentDto().setKey("struts").setId(101L)); + dto.setRule(RuleTesting.newDto(RuleKey.of("squid", "S001")).setId(200)); + dto.setKee("ABCDE"); + dto.setLine(500); + dto.setEffortToFix(3.14); + dto.setDebt(10L); + dto.setResolution("FIXED"); + dto.setStatus("RESOLVED"); + dto.setSeverity("BLOCKER"); + dto.setReporter("emmerik"); + dto.setAuthorLogin("morgan"); + dto.setAssignee("karadoc"); + dto.setActionPlanKey("current_sprint"); + dto.setIssueAttributes("JIRA=FOO-1234"); + dto.setChecksum("123456789"); + dto.setMessage("the message"); + + dto.setIssueCreationDate(DateUtils.parseDate("2013-05-18")); + dto.setIssueUpdateDate(DateUtils.parseDate("2013-05-19")); + dto.setIssueCloseDate(DateUtils.parseDate("2013-05-20")); + dto.setCreatedAt(DateUtils.parseDate("2013-05-21")); + dto.setUpdatedAt(DateUtils.parseDate("2013-05-22")); + + dao.update(session, dto); + session.commit(); + + checkTables("update", new String[]{"id"}, "issues"); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueAuthorizationIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueAuthorizationIndexMediumTest.java index 0895eedaa5b..6d9942bbbc7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueAuthorizationIndexMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueAuthorizationIndexMediumTest.java @@ -42,7 +42,8 @@ import static org.fest.assertions.Assertions.assertThat; public class IssueAuthorizationIndexMediumTest { @ClassRule - public static ServerTester tester = new ServerTester(); + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); DbClient db; DbSession session; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java index 0fdc78077e7..fb95a3f198c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java @@ -54,7 +54,8 @@ import static org.fest.assertions.Assertions.assertThat; public class IssueIndexMediumTest { @ClassRule - public static ServerTester tester = new ServerTester(); + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); DbClient db; DbSession session; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java index bb8d97e87b0..0970c222332 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java @@ -37,12 +37,10 @@ import org.sonar.core.issue.workflow.Transition; import org.sonar.server.issue.ActionService; import org.sonar.server.issue.IssueService; import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; import java.io.StringWriter; import static com.google.common.collect.Lists.newArrayList; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -77,8 +75,7 @@ public class IssueActionsWriterTest { "{\"actions\": " + "[" + "\"comment\", \"assign\", \"assign_to_me\", \"plan\", \"set_severity\"\n" + - "]}" - ); + "]}"); } @Test @@ -98,8 +95,7 @@ public class IssueActionsWriterTest { "{\"actions\": " + "[" + "\"comment\", \"assign\", \"assign_to_me\", \"plan\", \"link-to-jira\"\n" + - "]}" - ); + "]}"); } @Test @@ -117,8 +113,7 @@ public class IssueActionsWriterTest { "{\"actions\": " + "[" + "\"comment\"" + - "]}" - ); + "]}"); } @Test @@ -132,8 +127,7 @@ public class IssueActionsWriterTest { MockUserSession.set(); testActions(issue, - "{\"actions\": []}" - ); + "{\"actions\": []}"); } @Test @@ -151,8 +145,7 @@ public class IssueActionsWriterTest { "{\"actions\": " + "[" + "\"comment\", \"assign\", \"plan\"\n" + - "]}" - ); + "]}"); } @Test @@ -163,14 +156,13 @@ public class IssueActionsWriterTest { .setProjectKey("sample") .setRuleKey(RuleKey.of("squid", "AvoidCycle")); - when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); + when(issueService.listTransitions(eq(issue))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); MockUserSession.set().setLogin("john"); testTransitions(issue, "{\"transitions\": [\n" + " \"reopen\"\n" + - " ]}" - ); + " ]}"); } @Test @@ -184,8 +176,7 @@ public class IssueActionsWriterTest { MockUserSession.set().setLogin("john"); testTransitions(issue, - "{\"transitions\": []}" - ); + "{\"transitions\": []}"); } private void testActions(Issue issue, String expected) throws JSONException { diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java index 95c8ff3da4d..9325f7745c3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java @@ -52,7 +52,6 @@ import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.issue.ActionService; import org.sonar.server.issue.IssueService; import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; import org.sonar.server.ws.WsTester; import java.util.*; @@ -243,7 +242,7 @@ public class IssueSearchActionTest { issues.add(issue); MockUserSession.set().setLogin("john"); - when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); + when(issueService.listTransitions(eq(issue))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); result.addActionPlans(newArrayList((ActionPlan) new DefaultActionPlan().setKey("AP-ABCD").setName("1.0"))); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java index 2871fa8cd7c..66a73144b72 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java @@ -53,7 +53,6 @@ import org.sonar.server.issue.IssueChangelog; import org.sonar.server.issue.IssueChangelogService; import org.sonar.server.issue.IssueService; import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; import org.sonar.server.ws.WsTester; import java.util.ArrayList; @@ -431,7 +430,7 @@ public class IssueShowActionTest { .setResolution("FIXED"); issues.add(issue); - when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); + when(issueService.listTransitions(eq(issue))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); MockUserSession.set().setLogin("john"); WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleTesting.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleTesting.java index d86c17e1ff8..6cfd273bce4 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleTesting.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleTesting.java @@ -97,6 +97,7 @@ public class RuleTesting { public static RuleDto newManualRule(String manualKey){ return new RuleDto().setRuleKey(manualKey) + .setName("Name " + manualKey) .setRepositoryKey("manual") .setDescription("Description " + manualKey) .setStatus(RuleStatus.READY); diff --git a/server/sonar-server/src/test/java/org/sonar/server/search/IndexSynchronizerMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/search/IndexSynchronizerMediumTest.java index 8048b57beca..1e313236343 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/search/IndexSynchronizerMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/search/IndexSynchronizerMediumTest.java @@ -23,6 +23,7 @@ import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; +import org.sonar.api.config.Settings; import org.sonar.api.rule.RuleKey; import org.sonar.core.persistence.DbSession; import org.sonar.server.db.DbClient; @@ -36,7 +37,8 @@ import static org.fest.assertions.Assertions.assertThat; public class IndexSynchronizerMediumTest { @ClassRule - public static ServerTester tester = new ServerTester(); + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); IndexSynchronizer synchronizer; DbClient dbClient; @@ -50,7 +52,7 @@ public class IndexSynchronizerMediumTest { indexClient = tester.get(IndexClient.class); platform = tester.get(Platform.class); dbSession = dbClient.openSession(false); - synchronizer = new IndexSynchronizer(dbClient, indexClient); + synchronizer = new IndexSynchronizer(dbClient, indexClient, tester.get(Settings.class)); tester.clearDbAndIndexes(); } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml new file mode 100644 index 00000000000..9a5cc947917 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml new file mode 100644 index 00000000000..3d736381a9e --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml @@ -0,0 +1,28 @@ + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/should_select_all.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/should_select_all.xml deleted file mode 100644 index 96ada68ee82..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/should_select_all.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/some_issues.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/some_issues.xml new file mode 100644 index 00000000000..96ada68ee82 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/some_issues.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml new file mode 100644 index 00000000000..a923dfa54e0 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml @@ -0,0 +1,28 @@ + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update.xml new file mode 100644 index 00000000000..4c6f701dc2c --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update.xml @@ -0,0 +1,28 @@ + + + diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java index 39b56b1644d..44262e5dc5d 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java @@ -37,6 +37,7 @@ import org.sonar.api.utils.DateUtils; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.io.Serializable; import java.util.List; import java.util.Map; @@ -71,7 +72,14 @@ public class IssueNotifications implements BatchComponent, ServerComponent { public List sendChanges(DefaultIssue issue, IssueChangeContext context, IssueQueryResult queryResult) { Map issues = Maps.newHashMap(); issues.put(issue, queryResult.rule(issue)); - return sendChanges(issues, context, queryResult.project(issue), queryResult.component(issue)); + return sendChanges(issue, context, queryResult.rule(issue), queryResult.project(issue), queryResult.component(issue)); + } + + @CheckForNull + public List sendChanges(DefaultIssue issue, IssueChangeContext context, Rule rule, Component project, @Nullable Component component) { + Map issues = Maps.newHashMap(); + issues.put(issue, rule); + return sendChanges(issues, context, project, component); } @CheckForNull @@ -131,6 +139,7 @@ public class IssueNotifications implements BatchComponent, ServerComponent { return notification; } + @CheckForNull private String ruleName(@Nullable Rule rule) { // this code should definitely be shared in api if (rule == null) { diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java index 63278903bf2..fa67f6872a9 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java @@ -136,7 +136,7 @@ public final class IssueDto extends Dto implements Serializable { } /** - * @deprecated please use setRootComponent; + * @deprecated please use setRootComponent */ @Deprecated public IssueDto setRootComponentId(Long rootComponentId) { @@ -349,6 +349,10 @@ public final class IssueDto extends Dto implements Serializable { return ruleRepo; } + public RuleKey getRuleKey(){ + return RuleKey.of(ruleRepo, ruleKey); + } + public String getComponentKey() { return componentKey; } @@ -369,6 +373,8 @@ public final class IssueDto extends Dto implements Serializable { /** * Should only be used to persist in E/S + * + * Please use {@link #setRule(org.sonar.core.rule.RuleDto)} instead */ public IssueDto setRuleKey(String repo, String rule) { this.ruleRepo = repo; @@ -378,6 +384,8 @@ public final class IssueDto extends Dto implements Serializable { /** * Should only be used to persist in E/S + * + * Please use {@link #setComponent(org.sonar.core.component.ComponentDto)} instead */ public IssueDto setComponentKey(String componentKey) { this.componentKey = componentKey; @@ -386,6 +394,8 @@ public final class IssueDto extends Dto implements Serializable { /** * Should only be used to persist in E/S + * + * Please use {@link #setRootComponent(org.sonar.core.component.ComponentDto)} instead */ public IssueDto setRootComponentKey(String rootComponentKey) { this.rootComponentKey = rootComponentKey; @@ -474,7 +484,7 @@ public final class IssueDto extends Dto implements Serializable { issue.setComponentId(componentId); issue.setProjectKey(rootComponentKey); issue.setManualSeverity(manualSeverity); - issue.setRuleKey(RuleKey.of(ruleRepo, ruleKey)); + issue.setRuleKey(getRuleKey()); issue.setActionPlanKey(actionPlanKey); issue.setAuthorLogin(authorLogin); issue.setNew(false); diff --git a/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java b/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java index 6bf273c5176..7fb5c14598d 100644 --- a/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java +++ b/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java @@ -42,6 +42,8 @@ import java.util.List; * This facade wraps db operations related to permissions * * Should be removed when batch will no more create permission, and be replaced by a new PermissionService in module server (probably be a merge with InternalPermissionService) + * + * WARNING, this class is called by Views to apply default permission template on new views */ public class PermissionFacade implements TaskComponent, ServerComponent { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java index b327c37d4f3..37ea218345f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java @@ -31,7 +31,9 @@ import java.util.List; /** * @since 3.6 + * @deprecated since 5.0 */ +@Deprecated public interface IssueQueryResult { /** * Non-null paginated list of issues. -- cgit v1.2.3