]> source.dussan.org Git - sonarqube.git/blob
776b24352f08b8a5f521ffa09858365836f69c6b
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.ce.task.projectanalysis.issue;
21
22 import java.time.Instant;
23 import java.time.temporal.ChronoUnit;
24 import java.util.Collections;
25 import java.util.Date;
26 import org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.mockito.ArgumentCaptor;
30 import org.sonar.api.config.internal.MapSettings;
31 import org.sonar.api.issue.Issue;
32 import org.sonar.api.rule.RuleKey;
33 import org.sonar.api.utils.System2;
34 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
35 import org.sonar.ce.task.projectanalysis.analysis.Branch;
36 import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues;
37 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
38 import org.sonar.core.issue.DefaultIssue;
39 import org.sonar.core.issue.FieldDiffs;
40 import org.sonar.core.issue.tracking.SimpleTracker;
41 import org.sonar.db.DbClient;
42 import org.sonar.db.DbTester;
43 import org.sonar.db.component.BranchType;
44 import org.sonar.db.component.ComponentDto;
45 import org.sonar.db.issue.IssueDto;
46 import org.sonar.db.issue.IssueTesting;
47 import org.sonar.db.rule.RuleDefinitionDto;
48 import org.sonar.db.user.UserDto;
49 import org.sonar.server.project.Project;
50
51 import static org.assertj.core.api.Assertions.assertThat;
52 import static org.mockito.ArgumentMatchers.eq;
53 import static org.mockito.Mockito.mock;
54 import static org.mockito.Mockito.verify;
55 import static org.mockito.Mockito.verifyNoInteractions;
56 import static org.mockito.Mockito.when;
57 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
58 import static org.sonar.db.component.ComponentTesting.newFileDto;
59
60 public class SiblingsIssueMergerTest {
61   private final IssueLifecycle issueLifecycle = mock(IssueLifecycle.class);
62   private final Branch branch = mock(Branch.class);
63
64   @Rule
65   public DbTester db = DbTester.create();
66
67   @Rule
68   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
69     .setRoot(builder(org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT, PROJECT_REF).setKey(PROJECT_KEY).setUuid(PROJECT_UUID)
70       .addChildren(FILE_1)
71       .build());
72
73   @Rule
74   public AnalysisMetadataHolderRule metadataHolder = new AnalysisMetadataHolderRule();
75
76   private static final String PROJECT_KEY = "project";
77   private static final int PROJECT_REF = 1;
78   private static final String PROJECT_UUID = "projectUuid";
79   private static final int FILE_1_REF = 12341;
80   private static final String FILE_1_KEY = "fileKey";
81   private static final String FILE_1_UUID = "fileUuid";
82
83   private static final org.sonar.ce.task.projectanalysis.component.Component FILE_1 = builder(
84     org.sonar.ce.task.projectanalysis.component.Component.Type.FILE, FILE_1_REF)
85     .setKey(FILE_1_KEY)
86     .setUuid(FILE_1_UUID)
87     .build();
88
89   private final SimpleTracker<DefaultIssue, SiblingIssue> tracker = new SimpleTracker<>();
90   private SiblingsIssueMerger copier;
91   private ComponentDto fileOnBranch1Dto;
92   private ComponentDto fileOnBranch2Dto;
93   private ComponentDto fileOnBranch3Dto;
94   private ComponentDto projectDto;
95   private ComponentDto branch1Dto;
96   private ComponentDto branch2Dto;
97   private ComponentDto branch3Dto;
98   private RuleDefinitionDto rule;
99
100   @Before
101   public void setUp() {
102     DbClient dbClient = db.getDbClient();
103     ComponentIssuesLoader componentIssuesLoader = new ComponentIssuesLoader(dbClient, null, null, new MapSettings().asConfig(), System2.INSTANCE,
104       mock(IssueChangesToDeleteRepository.class));
105     copier = new SiblingsIssueMerger(new SiblingsIssuesLoader(new SiblingComponentsWithOpenIssues(treeRootHolder, metadataHolder, dbClient), dbClient, componentIssuesLoader),
106       tracker,
107       issueLifecycle);
108     projectDto = db.components().insertPublicProject(p -> p.setDbKey(PROJECT_KEY).setUuid(PROJECT_UUID));
109     branch1Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch1")
110       .setBranchType(BranchType.PULL_REQUEST)
111       .setMergeBranchUuid(projectDto.uuid()));
112     branch2Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch2")
113       .setBranchType(BranchType.PULL_REQUEST)
114       .setMergeBranchUuid(projectDto.uuid()));
115     branch3Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch3")
116       .setBranchType(BranchType.PULL_REQUEST)
117       .setMergeBranchUuid(projectDto.uuid()));
118     fileOnBranch1Dto = db.components().insertComponent(newFileDto(branch1Dto).setDbKey(FILE_1_KEY + ":PULL_REQUEST:myBranch1"));
119     fileOnBranch2Dto = db.components().insertComponent(newFileDto(branch2Dto).setDbKey(FILE_1_KEY + ":PULL_REQUEST:myBranch2"));
120     fileOnBranch3Dto = db.components().insertComponent(newFileDto(branch3Dto).setDbKey(FILE_1_KEY + ":PULL_REQUEST:myBranch3"));
121     rule = db.rules().insert();
122     when(branch.getReferenceBranchUuid()).thenReturn(projectDto.uuid());
123     metadataHolder.setBranch(branch);
124     metadataHolder.setProject(new Project(projectDto.uuid(), projectDto.getKey(), projectDto.name(), projectDto.description(), Collections.emptyList()));
125   }
126
127   @Test
128   public void do_nothing_if_no_match() {
129     DefaultIssue i = createIssue("issue1", rule.getKey(), Issue.STATUS_CONFIRMED, new Date());
130     copier.tryMerge(FILE_1, Collections.singleton(i));
131
132     verifyNoInteractions(issueLifecycle);
133   }
134
135   @Test
136   public void do_nothing_if_no_new_issue() {
137     db.issues().insert(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
138     copier.tryMerge(FILE_1, Collections.emptyList());
139
140     verifyNoInteractions(issueLifecycle);
141   }
142
143   @Test
144   public void merge_confirmed_issues() {
145     db.issues().insert(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
146     DefaultIssue newIssue = createIssue("issue2", rule.getKey(), Issue.STATUS_OPEN, new Date());
147
148     copier.tryMerge(FILE_1, Collections.singleton(newIssue));
149
150     ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
151     verify(issueLifecycle).mergeConfirmedOrResolvedFromPrOrBranch(eq(newIssue), issueToMerge.capture(), eq(BranchType.PULL_REQUEST), eq("myBranch1"));
152
153     assertThat(issueToMerge.getValue().key()).isEqualTo("issue1");
154   }
155
156   @Test
157   public void prefer_more_recently_updated_issues() {
158     Instant now = Instant.now();
159     db.issues().insert(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_REOPENED).setLine(1).setChecksum("checksum")
160       .setIssueUpdateDate(Date.from(now.plus(2, ChronoUnit.SECONDS))));
161     db.issues().insert(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue2").setStatus(Issue.STATUS_OPEN).setLine(1).setChecksum("checksum")
162       .setIssueUpdateDate(Date.from(now.plus(1, ChronoUnit.SECONDS))));
163     db.issues().insert(IssueTesting.newIssue(rule, branch3Dto, fileOnBranch3Dto).setKee("issue3").setStatus(Issue.STATUS_OPEN).setLine(1).setChecksum("checksum")
164       .setIssueUpdateDate(Date.from(now)));
165     DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, new Date());
166
167     copier.tryMerge(FILE_1, Collections.singleton(newIssue));
168
169     ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
170     verify(issueLifecycle).mergeConfirmedOrResolvedFromPrOrBranch(eq(newIssue), issueToMerge.capture(), eq(BranchType.PULL_REQUEST), eq("myBranch1"));
171
172     assertThat(issueToMerge.getValue().key()).isEqualTo("issue1");
173   }
174
175   @Test
176   public void lazy_load_changes() {
177     UserDto user = db.users().insertUser();
178     IssueDto issue = db.issues()
179       .insert(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
180     db.issues().insertComment(issue, user, "A comment 2");
181     db.issues().insertFieldDiffs(issue, FieldDiffs.parse("severity=BLOCKER|MINOR,assignee=foo|bar").setCreationDate(new Date()));
182     DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, new Date());
183
184     copier.tryMerge(FILE_1, Collections.singleton(newIssue));
185
186     ArgumentCaptor<DefaultIssue> issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class);
187     verify(issueLifecycle).mergeConfirmedOrResolvedFromPrOrBranch(eq(newIssue), issueToMerge.capture(), eq(BranchType.PULL_REQUEST), eq("myBranch2"));
188
189     assertThat(issueToMerge.getValue().key()).isEqualTo("issue");
190     assertThat(issueToMerge.getValue().defaultIssueComments()).isNotEmpty();
191     assertThat(issueToMerge.getValue().changes()).isNotEmpty();
192   }
193
194   private static DefaultIssue createIssue(String key, RuleKey ruleKey, String status, Date creationDate) {
195     DefaultIssue issue = new DefaultIssue();
196     issue.setKey(key);
197     issue.setRuleKey(ruleKey);
198     issue.setMessage("msg");
199     issue.setLine(1);
200     issue.setStatus(status);
201     issue.setResolution(null);
202     issue.setCreationDate(creationDate);
203     issue.setChecksum("checksum");
204     return issue;
205   }
206
207 }