From: Julien HENRY Date: Fri, 13 Oct 2017 09:11:45 +0000 (+0200) Subject: SONAR-9970 Copy issue changes when creating an long living branch X-Git-Tag: 6.7-RC1~152 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=43e50a260031cd9a0eb7638b6136e6208a8783ca;p=sonarqube.git SONAR-9970 Copy issue changes when creating an long living branch --- diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java index 8fc59751ee0..5201db84197 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java @@ -28,6 +28,7 @@ import org.apache.ibatis.session.ResultHandler; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.sonar.api.issue.Issue; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.System2; import org.sonar.db.DbTester; @@ -186,12 +187,12 @@ public class IssueDaoTest { RuleDefinitionDto rule = db.rules().insert(); ComponentDto project = db.components().insertPrivateProject(); ComponentDto file = db.components().insertComponent(newFileDto(project)); - IssueDto openIssue = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null)); - IssueDto closedIssue = db.issues().insert(rule, project, file, i -> i.setStatus("CLOSED").setResolution("FIXED")); - IssueDto reopenedIssue = db.issues().insert(rule, project, file, i -> i.setStatus("REOPENED").setResolution(null)); - IssueDto confirmedIssue = db.issues().insert(rule, project, file, i -> i.setStatus("CONFIRMED").setResolution(null)); - IssueDto wontfixIssue = db.issues().insert(rule, project, file, i -> i.setStatus("RESOLVED").setResolution("WONTFIX")); - IssueDto fpIssue = db.issues().insert(rule, project, file, i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE")); + IssueDto openIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_OPEN).setResolution(null)); + IssueDto closedIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)); + IssueDto reopenedIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_REOPENED).setResolution(null)); + IssueDto confirmedIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_CONFIRMED).setResolution(null)); + IssueDto wontfixIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)); + IssueDto fpIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)); assertThat(underTest.selectResolvedOrConfirmedByComponentUuids(db.getSession(), Collections.singletonList(file.uuid()))) .extracting("kee") diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java index 2e5be262543..a68aa9e5340 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java @@ -64,7 +64,7 @@ import org.sonar.server.computation.task.projectanalysis.issue.IssueCache; import org.sonar.server.computation.task.projectanalysis.issue.IssueCounter; import org.sonar.server.computation.task.projectanalysis.issue.IssueCreationDateCalculator; import org.sonar.server.computation.task.projectanalysis.issue.IssueLifecycle; -import org.sonar.server.computation.task.projectanalysis.issue.ShortBranchIssueStatusCopier; +import org.sonar.server.computation.task.projectanalysis.issue.ShortBranchIssueMerger; import org.sonar.server.computation.task.projectanalysis.issue.IssueTrackingDelegator; import org.sonar.server.computation.task.projectanalysis.issue.IssueVisitors; import org.sonar.server.computation.task.projectanalysis.issue.IssuesRepositoryVisitor; @@ -253,7 +253,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop IssueTrackingDelegator.class, BranchPersister.class, ShortBranchIssuesLoader.class, - ShortBranchIssueStatusCopier.class, + ShortBranchIssueMerger.class, // filemove SourceSimilarityImpl.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ComponentIssuesLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ComponentIssuesLoader.java index 2ae00cdd153..91a45d7bc54 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ComponentIssuesLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ComponentIssuesLoader.java @@ -21,15 +21,20 @@ package org.sonar.server.computation.task.projectanalysis.issue; import java.util.ArrayList; import java.util.List; - +import java.util.Map; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; import org.sonar.core.issue.DefaultIssue; import org.sonar.db.DbClient; import org.sonar.db.DbSession; +import org.sonar.db.issue.IssueChangeDto; import org.sonar.db.issue.IssueMapper; import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRulesHolder; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toList; + public class ComponentIssuesLoader { private final DbClient dbClient; private final RuleRepository ruleRepository; @@ -43,24 +48,59 @@ public class ComponentIssuesLoader { public List loadForComponentUuid(String componentUuid) { try (DbSession dbSession = dbClient.openSession(false)) { - List result = new ArrayList<>(); - dbSession.getMapper(IssueMapper.class).scrollNonClosedByComponentUuid(componentUuid, resultContext -> { - DefaultIssue issue = (resultContext.getResultObject()).toDefaultIssue(); + return loadForComponentUuid(componentUuid, dbSession); + } + } + + public List loadForComponentUuidWithChanges(String componentUuid) { + try (DbSession dbSession = dbClient.openSession(false)) { + List result = loadForComponentUuid(componentUuid, dbSession); - // TODO this field should be set outside this class - if (!isActive(issue.ruleKey()) || ruleRepository.getByKey(issue.ruleKey()).getStatus() == RuleStatus.REMOVED) { - issue.setOnDisabledRule(true); - // TODO to be improved, why setOnDisabledRule(true) is not enough ? - issue.setBeingClosed(true); - } - // FIXME - issue.setSelectedAt(System.currentTimeMillis()); - result.add(issue); - }); - return result; + Map> changeDtoByIssueKey = dbClient.issueChangeDao() + .selectByIssueKeys(dbSession, result.stream().map(DefaultIssue::key).collect(toList())) + .stream() + .collect(groupingBy(IssueChangeDto::getIssueKey)); + + return result + .stream() + .peek(i -> setChanges(changeDtoByIssueKey, i)) + .collect(toList()); } } + private List loadForComponentUuid(String componentUuid, DbSession dbSession) { + List result = new ArrayList<>(); + dbSession.getMapper(IssueMapper.class).scrollNonClosedByComponentUuid(componentUuid, resultContext -> { + DefaultIssue issue = (resultContext.getResultObject()).toDefaultIssue(); + + // TODO this field should be set outside this class + if (!isActive(issue.ruleKey()) || ruleRepository.getByKey(issue.ruleKey()).getStatus() == RuleStatus.REMOVED) { + issue.setOnDisabledRule(true); + // TODO to be improved, why setOnDisabledRule(true) is not enough ? + issue.setBeingClosed(true); + } + // FIXME + issue.setSelectedAt(System.currentTimeMillis()); + result.add(issue); + }); + return result; + } + + public static void setChanges(Map> changeDtoByIssueKey, DefaultIssue i) { + changeDtoByIssueKey.computeIfAbsent(i.key(), k -> emptyList()).forEach(c -> { + switch (c.getChangeType()) { + case IssueChangeDto.TYPE_FIELD_CHANGE: + i.addChange(c.toFieldDiffs()); + break; + case IssueChangeDto.TYPE_COMMENT: + i.addComment(c.toComment()); + break; + default: + throw new IllegalStateException("Unknow change type: " + c.getChangeType()); + } + }); + } + private boolean isActive(RuleKey ruleKey) { return activeRulesHolder.get(ruleKey).isPresent(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java index a96d8f2d7e9..e0c9bd92fc1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java @@ -37,11 +37,11 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { private final IssueLifecycle issueLifecycle; private final IssueVisitors issueVisitors; private final IssueTrackingDelegator issueTracking; - private final ShortBranchIssueStatusCopier issueStatusCopier; + private final ShortBranchIssueMerger issueStatusCopier; private final AnalysisMetadataHolder analysisMetadataHolder; public IntegrateIssuesVisitor(IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors, - AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, ShortBranchIssueStatusCopier issueStatusCopier) { + AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, ShortBranchIssueMerger issueStatusCopier) { super(CrawlerDepthLimit.FILE, POST_ORDER); this.issueCache = issueCache; this.issueLifecycle = issueLifecycle; @@ -80,7 +80,7 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { } if (analysisMetadataHolder.isLongLivingBranch()) { - issueStatusCopier.updateStatus(component, list); + issueStatusCopier.tryMerge(component, list); } for (DefaultIssue issue : list) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java index 7f88d3404e0..6b40c04feb4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java @@ -69,30 +69,22 @@ public class IssueLifecycle { public void copyExistingOpenIssueFromLongLivingBranch(DefaultIssue raw, DefaultIssue base) { raw.setKey(Uuids.create()); raw.setNew(false); - raw.setCopied(true); - copyFields(raw, base); + copyIssueAttributes(raw, base); + } - if (base.manualSeverity()) { - raw.setManualSeverity(true); - raw.setSeverity(base.severity()); + public void copyIssueAttributes(DefaultIssue to, DefaultIssue from) { + to.setCopied(true); + copyFields(to, from); + if (from.manualSeverity()) { + to.setManualSeverity(true); + to.setSeverity(from.severity()); } + copyChanges(to, from); } - public void mergeIssueFromShortLivingBranch(DefaultIssue raw, DefaultIssue fromShortLiving) { - raw.setCopied(true); - raw.setType(fromShortLiving.type()); - raw.setResolution(fromShortLiving.resolution()); - raw.setStatus(fromShortLiving.status()); - raw.setAssignee(fromShortLiving.assignee()); - raw.setAuthorLogin(fromShortLiving.authorLogin()); - raw.setTags(fromShortLiving.tags()); - raw.setAttributes(fromShortLiving.attributes()); - if (fromShortLiving.manualSeverity()) { - raw.setManualSeverity(true); - raw.setSeverity(fromShortLiving.severity()); - } - fromShortLiving.comments().forEach(c -> raw.addComment(DefaultIssueComment.copy(raw.key(), c))); - fromShortLiving.changes().forEach(c -> raw.addChange(FieldDiffs.copy(raw.key(), c))); + private static void copyChanges(DefaultIssue raw, DefaultIssue base) { + base.comments().forEach(c -> raw.addComment(DefaultIssueComment.copy(raw.key(), c))); + base.changes().forEach(c -> raw.addChange(FieldDiffs.copy(raw.key(), c))); } public void mergeExistingOpenIssue(DefaultIssue raw, DefaultIssue base) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMerger.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMerger.java new file mode 100644 index 00000000000..79d5e5c2a77 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMerger.java @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.computation.task.projectanalysis.issue; + +import java.util.Collection; +import java.util.Map; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.ShortBranchIssue; +import org.sonar.core.issue.tracking.SimpleTracker; +import org.sonar.core.issue.tracking.Tracking; +import org.sonar.server.computation.task.projectanalysis.component.Component; + +public class ShortBranchIssueMerger { + private final ShortBranchIssuesLoader shortBranchIssuesLoader; + private final SimpleTracker tracker; + private final IssueLifecycle issueLifecycle; + + public ShortBranchIssueMerger(ShortBranchIssuesLoader resolvedShortBranchIssuesLoader, IssueLifecycle issueLifecycle) { + this(resolvedShortBranchIssuesLoader, new SimpleTracker<>(), issueLifecycle); + } + + public ShortBranchIssueMerger(ShortBranchIssuesLoader shortBranchIssuesLoader, SimpleTracker tracker, IssueLifecycle issueLifecycle) { + this.shortBranchIssuesLoader = shortBranchIssuesLoader; + this.tracker = tracker; + this.issueLifecycle = issueLifecycle; + } + + /** + * Look for all resolved/confirmed issues in short living branches targeting the current long living branch, and run + * a light issue tracking to find matches. Then merge issue attributes in the new issues. + */ + public void tryMerge(Component component, Collection newIssues) { + Collection shortBranchIssues = shortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component); + Tracking tracking = tracker.track(newIssues, shortBranchIssues); + + Map matchedRaws = tracking.getMatchedRaws(); + + Map defaultIssues = shortBranchIssuesLoader.loadDefaultIssuesWithChanges(matchedRaws.values()); + + for (Map.Entry e : matchedRaws.entrySet()) { + ShortBranchIssue issue = e.getValue(); + issueLifecycle.copyIssueAttributes(e.getKey(), defaultIssues.get(issue)); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopier.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopier.java deleted file mode 100644 index 3944af7c101..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopier.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.computation.task.projectanalysis.issue; - -import java.util.Collection; -import java.util.Map; -import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.ShortBranchIssue; -import org.sonar.core.issue.tracking.SimpleTracker; -import org.sonar.core.issue.tracking.Tracking; -import org.sonar.server.computation.task.projectanalysis.component.Component; - -public class ShortBranchIssueStatusCopier { - private final ShortBranchIssuesLoader shortBranchIssuesLoader; - private final SimpleTracker tracker; - private final IssueLifecycle issueLifecycle; - - public ShortBranchIssueStatusCopier(ShortBranchIssuesLoader resolvedShortBranchIssuesLoader, IssueLifecycle issueLifecycle) { - this(resolvedShortBranchIssuesLoader, new SimpleTracker<>(), issueLifecycle); - } - - public ShortBranchIssueStatusCopier(ShortBranchIssuesLoader shortBranchIssuesLoader, SimpleTracker tracker, IssueLifecycle issueLifecycle) { - this.shortBranchIssuesLoader = shortBranchIssuesLoader; - this.tracker = tracker; - this.issueLifecycle = issueLifecycle; - } - - public void updateStatus(Component component, Collection newIssues) { - Collection shortBranchIssues = shortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component); - Tracking tracking = tracker.track(newIssues, shortBranchIssues); - - Map matchedRaws = tracking.getMatchedRaws(); - - Map defaultIssues = shortBranchIssuesLoader.loadDefaultIssuesWithChanges(matchedRaws.values()); - - for (Map.Entry e : matchedRaws.entrySet()) { - ShortBranchIssue issue = e.getValue(); - issueLifecycle.mergeIssueFromShortLivingBranch(e.getKey(), defaultIssues.get(issue)); - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java index acc598059a6..1beca5a1b64 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java @@ -76,23 +76,9 @@ public class ShortBranchIssuesLoader { return dbClient.issueDao().selectByKeys(session, issuesByKey.keySet()) .stream() .map(IssueDto::toDefaultIssue) - .peek(i -> setChanges(changeDtoByIssueKey, i)) + .peek(i -> ComponentIssuesLoader.setChanges(changeDtoByIssueKey, i)) .collect(toMap(i -> issuesByKey.get(i.key()), i -> i)); } } - private static void setChanges(Map> changeDtoByIssueKey, DefaultIssue i) { - changeDtoByIssueKey.get(i.key()).forEach(c -> { - switch (c.getChangeType()) { - case IssueChangeDto.TYPE_FIELD_CHANGE: - i.addChange(c.toFieldDiffs()); - break; - case IssueChangeDto.TYPE_COMMENT: - i.addComment(c.toComment()); - break; - default: - throw new IllegalStateException("Unknow change type: " + c.getChangeType()); - } - }); - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerMergeBranchInputFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerMergeBranchInputFactory.java index 85717967e56..8a3746fb3f4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerMergeBranchInputFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerMergeBranchInputFactory.java @@ -79,7 +79,7 @@ public class TrackerMergeBranchInputFactory { if (mergeBranchComponentUuid == null) { return Collections.emptyList(); } - return mergeIssuesLoader.loadForComponentUuid(mergeBranchComponentUuid); + return mergeIssuesLoader.loadForComponentUuidWithChanges(mergeBranchComponentUuid); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStorage.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStorage.java index 6b186a72411..ddb9f58af79 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStorage.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStorage.java @@ -161,14 +161,14 @@ public abstract class IssueStorage { } } FieldDiffs diffs = issue.currentChange(); - if (!issue.isNew() && diffs != null) { - IssueChangeDto changeDto = IssueChangeDto.of(issue.key(), diffs); - mapper.insert(changeDto); - } else if (issue.isCopied()) { + if (issue.isCopied()) { for (FieldDiffs d : issue.changes()) { IssueChangeDto changeDto = IssueChangeDto.of(issue.key(), d); mapper.insert(changeDto); } + } else if (!issue.isNew() && diffs != null) { + IssueChangeDto changeDto = IssueChangeDto.of(issue.key(), diffs); + mapper.insert(changeDto); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitorTest.java index 3adcf83d6ca..52d282523af 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitorTest.java @@ -57,7 +57,8 @@ public class CloseIssuesOnRemovedComponentsVisitorTest { @Before public void setUp() throws Exception { issueCache = new IssueCache(temp.newFile(), System2.INSTANCE); - underTest = new VisitorsCrawler(Arrays.asList(new CloseIssuesOnRemovedComponentsVisitor(issuesLoader, componentsWithUnprocessedIssues, issueCache, issueLifecycle))); + underTest = new VisitorsCrawler( + Arrays.asList(new CloseIssuesOnRemovedComponentsVisitor(issuesLoader, componentsWithUnprocessedIssues, issueCache, issueLifecycle))); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java index 8f30a438333..1427302612c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java @@ -36,6 +36,7 @@ import org.sonar.api.utils.System2; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.tracking.Tracker; import org.sonar.db.DbTester; +import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.db.issue.IssueDto; @@ -45,6 +46,7 @@ import org.sonar.db.rule.RuleTesting; import org.sonar.scanner.protocol.Constants; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; +import org.sonar.server.computation.task.projectanalysis.analysis.Branch; import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderRule; import org.sonar.server.computation.task.projectanalysis.component.Component; import org.sonar.server.computation.task.projectanalysis.component.MergeBranchComponentUuids; @@ -61,6 +63,7 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder; @@ -68,6 +71,7 @@ import static org.sonar.server.computation.task.projectanalysis.component.Report public class IntegrateIssuesVisitorTest { static final String FILE_UUID = "FILE_UUID"; + static final String FILE_UUID_ON_BRANCH = "FILE_UUID_BRANCH"; static final String FILE_KEY = "FILE_KEY"; static final int FILE_REF = 2; @@ -78,6 +82,7 @@ public class IntegrateIssuesVisitorTest { static final String PROJECT_KEY = "PROJECT_KEY"; static final String PROJECT_UUID = "PROJECT_UUID"; + static final String PROJECT_UUID_ON_BRANCH = "PROJECT_UUID_BRANCH"; static final int PROJECT_REF = 1; static final Component PROJECT = builder(Component.Type.PROJECT, PROJECT_REF) .setKey(PROJECT_KEY) @@ -113,7 +118,7 @@ public class IntegrateIssuesVisitorTest { @Mock private MergeBranchComponentUuids mergeBranchComponentsUuids; @Mock - private ShortBranchIssueStatusCopier issueStatusCopier; + private ShortBranchIssueMerger issueStatusCopier; ArgumentCaptor defaultIssueCaptor; @@ -166,7 +171,7 @@ public class IntegrateIssuesVisitorTest { DefaultIssue capturedIssue = defaultIssueCaptor.getValue(); assertThat(capturedIssue.ruleKey().rule()).isEqualTo("S001"); - verify(issueStatusCopier).updateStatus(FILE, Collections.singletonList(capturedIssue)); + verify(issueStatusCopier).tryMerge(FILE, Collections.singletonList(capturedIssue)); verify(issueLifecycle).doAutomaticTransition(capturedIssue); @@ -249,6 +254,47 @@ public class IntegrateIssuesVisitorTest { underTest.visitAny(FILE); } + @Test + public void copy_issues_when_creating_new_long_living_branch() throws Exception { + + when(mergeBranchComponentsUuids.getUuid(FILE_KEY)).thenReturn(FILE_UUID_ON_BRANCH); + + when(analysisMetadataHolder.isLongLivingBranch()).thenReturn(true); + when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(true); + Branch branch = mock(Branch.class); + when(branch.isMain()).thenReturn(false); + when(branch.getType()).thenReturn(BranchType.LONG); + when(analysisMetadataHolder.getBranch()).thenReturn(java.util.Optional.of(branch)); + + RuleKey ruleKey = RuleTesting.XOO_X1; + // Issue from main branch has severity major + addBaseIssueOnBranch(ruleKey); + + // Issue from report has severity blocker + ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder() + .setMsg("the message") + .setRuleRepository(ruleKey.repository()) + .setRuleKey(ruleKey.rule()) + .setSeverity(Constants.Severity.BLOCKER) + .build(); + reportReader.putIssues(FILE_REF, asList(reportIssue)); + fileSourceRepository.addLine(FILE_REF, "line1"); + + underTest.visitAny(FILE); + + ArgumentCaptor rawIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class); + ArgumentCaptor baseIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class); + verify(issueLifecycle).copyExistingOpenIssueFromLongLivingBranch(rawIssueCaptor.capture(), baseIssueCaptor.capture()); + assertThat(rawIssueCaptor.getValue().severity()).isEqualTo(Severity.BLOCKER); + assertThat(baseIssueCaptor.getValue().severity()).isEqualTo(Severity.MAJOR); + + verify(issueLifecycle).doAutomaticTransition(defaultIssueCaptor.capture()); + assertThat(defaultIssueCaptor.getValue().ruleKey()).isEqualTo(ruleKey); + List issues = newArrayList(issueCache.traverse()); + assertThat(issues).hasSize(1); + assertThat(issues.get(0).severity()).isEqualTo(Severity.BLOCKER); + } + private void addBaseIssue(RuleKey ruleKey) { ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), PROJECT_UUID).setDbKey(PROJECT_KEY); ComponentDto file = ComponentTesting.newFileDto(project, null, FILE_UUID).setDbKey(FILE_KEY); @@ -265,4 +311,21 @@ public class IntegrateIssuesVisitorTest { dbTester.getDbClient().issueDao().insert(dbTester.getSession(), issue); dbTester.getSession().commit(); } + + private void addBaseIssueOnBranch(RuleKey ruleKey) { + ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), PROJECT_UUID_ON_BRANCH).setDbKey(PROJECT_KEY); + ComponentDto file = ComponentTesting.newFileDto(project, null, FILE_UUID_ON_BRANCH).setDbKey(FILE_KEY); + dbTester.getDbClient().componentDao().insert(dbTester.getSession(), project, file); + + RuleDto ruleDto = RuleTesting.newDto(ruleKey); + dbTester.rules().insertRule(ruleDto); + ruleRepositoryRule.add(ruleKey); + + IssueDto issue = IssueTesting.newDto(ruleDto, file, project) + .setKee("ISSUE") + .setStatus(Issue.STATUS_OPEN) + .setSeverity(Severity.MAJOR); + dbTester.getDbClient().issueDao().insert(dbTester.getSession(), issue); + dbTester.getSession().commit(); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java index a588db622b5..ecd79be5617 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java @@ -81,7 +81,7 @@ public class IssueLifecycleTest { DefaultIssue fromShort = new DefaultIssue(); fromShort.setResolution("resolution"); fromShort.setStatus("status"); - underTest.mergeIssueFromShortLivingBranch(raw, fromShort); + underTest.copyIssueAttributes(raw, fromShort); assertThat(raw.resolution()).isEqualTo("resolution"); assertThat(raw.status()).isEqualTo("status"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMergerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMergerTest.java new file mode 100644 index 00000000000..fcda03e282c --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMergerTest.java @@ -0,0 +1,125 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.computation.task.projectanalysis.issue; + +import com.google.common.collect.ImmutableMap; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import javax.annotation.Nullable; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.api.issue.Issue; +import org.sonar.api.rule.RuleKey; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.ShortBranchIssue; +import org.sonar.core.issue.tracking.SimpleTracker; +import org.sonar.server.computation.task.projectanalysis.component.Component; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyListOf; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class ShortBranchIssueMergerTest { + @Mock + private ShortBranchIssuesLoader resolvedShortBranchIssuesLoader; + @Mock + private IssueLifecycle issueLifecycle; + @Mock + private Component component; + + private SimpleTracker tracker = new SimpleTracker<>(); + private ShortBranchIssueMerger copier; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + copier = new ShortBranchIssueMerger(resolvedShortBranchIssuesLoader, tracker, issueLifecycle); + } + + @Test + public void do_nothing_if_no_match() { + when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.emptyList()); + DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null); + copier.tryMerge(component, Collections.singleton(i)); + + verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component); + verifyZeroInteractions(issueLifecycle); + } + + @Test + public void do_nothing_if_no_new_issue() { + DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null); + when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(newShortBranchIssue(i))); + copier.tryMerge(component, Collections.emptyList()); + + verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component); + verifyZeroInteractions(issueLifecycle); + } + + @Test + public void update_status_on_matches() { + DefaultIssue issue1 = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null); + ShortBranchIssue shortBranchIssue = newShortBranchIssue(issue1); + DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null); + + when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(shortBranchIssue)); + when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue, issue1)); + copier.tryMerge(component, Collections.singleton(newIssue)); + ArgumentCaptor captor = ArgumentCaptor.forClass(Collection.class); + verify(resolvedShortBranchIssuesLoader).loadDefaultIssuesWithChanges(captor.capture()); + assertThat(captor.getValue()).containsOnly(shortBranchIssue); + verify(issueLifecycle).copyIssueAttributes(newIssue, issue1); + } + + @Test + public void prefer_resolved_issues() { + ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null)); + ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_CONFIRMED, null)); + DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE); + ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3); + DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null); + + when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3)); + when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3)); + copier.tryMerge(component, Collections.singleton(newIssue)); + verify(issueLifecycle).copyIssueAttributes(newIssue, issue3); + } + + private static DefaultIssue createIssue(String key, String ruleKey, String status, @Nullable String resolution) { + DefaultIssue issue = new DefaultIssue(); + issue.setKey(key); + issue.setRuleKey(RuleKey.of("repo", ruleKey)); + issue.setMessage("msg"); + issue.setLine(1); + issue.setStatus(status); + issue.setResolution(resolution); + return issue; + } + + private ShortBranchIssue newShortBranchIssue(DefaultIssue i) { + return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status()); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopierTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopierTest.java deleted file mode 100644 index d244d44cb6a..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopierTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.computation.task.projectanalysis.issue; - -import com.google.common.collect.ImmutableMap; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import javax.annotation.Nullable; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.sonar.api.issue.Issue; -import org.sonar.api.rule.RuleKey; -import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.ShortBranchIssue; -import org.sonar.core.issue.tracking.SimpleTracker; -import org.sonar.server.computation.task.projectanalysis.component.Component; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.anyListOf; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -public class ShortBranchIssueStatusCopierTest { - @Mock - private ShortBranchIssuesLoader resolvedShortBranchIssuesLoader; - @Mock - private IssueLifecycle issueLifecycle; - @Mock - private Component component; - - private SimpleTracker tracker = new SimpleTracker<>(); - private ShortBranchIssueStatusCopier copier; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - copier = new ShortBranchIssueStatusCopier(resolvedShortBranchIssuesLoader, tracker, issueLifecycle); - } - - @Test - public void do_nothing_if_no_match() { - when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.emptyList()); - DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null); - copier.updateStatus(component, Collections.singleton(i)); - - verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component); - verifyZeroInteractions(issueLifecycle); - } - - @Test - public void do_nothing_if_no_new_issue() { - DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null); - when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(newShortBranchIssue(i))); - copier.updateStatus(component, Collections.emptyList()); - - verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component); - verifyZeroInteractions(issueLifecycle); - } - - @Test - public void update_status_on_matches() { - DefaultIssue issue1 = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null); - ShortBranchIssue shortBranchIssue = newShortBranchIssue(issue1); - DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null); - - when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(shortBranchIssue)); - when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue, issue1)); - copier.updateStatus(component, Collections.singleton(newIssue)); - ArgumentCaptor captor = ArgumentCaptor.forClass(Collection.class); - verify(resolvedShortBranchIssuesLoader).loadDefaultIssuesWithChanges(captor.capture()); - assertThat(captor.getValue()).containsOnly(shortBranchIssue); - verify(issueLifecycle).mergeIssueFromShortLivingBranch(newIssue, issue1); - } - - @Test - public void prefer_resolved_issues() { - ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null)); - ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_CONFIRMED, null)); - DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE); - ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3); - DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null); - - when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3)); - when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3)); - copier.updateStatus(component, Collections.singleton(newIssue)); - verify(issueLifecycle).mergeIssueFromShortLivingBranch(newIssue, issue3); - } - - private static DefaultIssue createIssue(String key, String ruleKey, String status, @Nullable String resolution) { - DefaultIssue issue = new DefaultIssue(); - issue.setKey(key); - issue.setRuleKey(RuleKey.of("repo", ruleKey)); - issue.setMessage("msg"); - issue.setLine(1); - issue.setStatus(status); - issue.setResolution(resolution); - return issue; - } - - private ShortBranchIssue newShortBranchIssue(DefaultIssue i) { - return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status()); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStepTest.java index bbb198d92ab..1fbc1c9d1a2 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStepTest.java @@ -19,6 +19,8 @@ */ package org.sonar.server.computation.task.projectanalysis.step; +import java.util.Arrays; +import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -37,6 +39,7 @@ import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; +import org.sonar.db.issue.IssueChangeDto; import org.sonar.db.issue.IssueDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.rule.RuleDefinitionDto; @@ -114,7 +117,67 @@ public class PersistIssuesStepTest extends BaseStepTest { .setStatus(Issue.STATUS_OPEN) .setNew(false) .setCopied(true) - .setType(RuleType.BUG)).close(); + .setType(RuleType.BUG) + .addComment(new DefaultIssueComment() + .setKey("COMMENT") + .setIssueKey("ISSUE") + .setUserLogin("john") + .setMarkdownText("Some text") + .setNew(true)) + .setCurrentChange(new FieldDiffs() + .setIssueKey("ISSUE") + .setUserLogin("john") + .setDiff("technicalDebt", null, 1L))) + .close(); + + step.execute(); + + IssueDto result = dbClient.issueDao().selectOrFailByKey(session, "ISSUE"); + assertThat(result.getKey()).isEqualTo("ISSUE"); + assertThat(result.getRuleKey()).isEqualTo(rule.getKey()); + assertThat(result.getComponentUuid()).isEqualTo(file.uuid()); + assertThat(result.getProjectUuid()).isEqualTo(project.uuid()); + assertThat(result.getSeverity()).isEqualTo(Severity.BLOCKER); + assertThat(result.getStatus()).isEqualTo(Issue.STATUS_OPEN); + assertThat(result.getType()).isEqualTo(RuleType.BUG.getDbConstant()); + + List changes = dbClient.issueChangeDao().selectByIssueKeys(session, Arrays.asList("ISSUE")); + assertThat(changes).extracting(IssueChangeDto::getChangeType).containsExactly(IssueChangeDto.TYPE_COMMENT, IssueChangeDto.TYPE_FIELD_CHANGE); + } + + @Test + public void insert_merged_issue() { + RuleDefinitionDto rule = RuleTesting.newRule(RuleKey.of("xoo", "S01")); + dbTester.rules().insert(rule); + OrganizationDto organizationDto = dbTester.organizations().insert(); + ComponentDto project = ComponentTesting.newPrivateProjectDto(organizationDto); + dbClient.componentDao().insert(session, project); + ComponentDto file = ComponentTesting.newFileDto(project, null); + dbClient.componentDao().insert(session, file); + session.commit(); + + issueCache.newAppender().append(new DefaultIssue() + .setKey("ISSUE") + .setType(RuleType.CODE_SMELL) + .setRuleKey(rule.getKey()) + .setComponentUuid(file.uuid()) + .setProjectUuid(project.uuid()) + .setSeverity(Severity.BLOCKER) + .setStatus(Issue.STATUS_OPEN) + .setNew(true) + .setCopied(true) + .setType(RuleType.BUG) + .addComment(new DefaultIssueComment() + .setKey("COMMENT") + .setIssueKey("ISSUE") + .setUserLogin("john") + .setMarkdownText("Some text") + .setNew(true)) + .setCurrentChange(new FieldDiffs() + .setIssueKey("ISSUE") + .setUserLogin("john") + .setDiff("technicalDebt", null, 1L))) + .close(); step.execute(); @@ -126,6 +189,9 @@ public class PersistIssuesStepTest extends BaseStepTest { assertThat(result.getSeverity()).isEqualTo(Severity.BLOCKER); assertThat(result.getStatus()).isEqualTo(Issue.STATUS_OPEN); assertThat(result.getType()).isEqualTo(RuleType.BUG.getDbConstant()); + + List changes = dbClient.issueChangeDao().selectByIssueKeys(session, Arrays.asList("ISSUE")); + assertThat(changes).extracting(IssueChangeDto::getChangeType).containsExactly(IssueChangeDto.TYPE_COMMENT, IssueChangeDto.TYPE_FIELD_CHANGE); } @Test