]> source.dussan.org Git - sonarqube.git/blob
384ea59093be6214d911f01d64741d0efc86d33d
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2021 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 com.google.common.collect.ImmutableMap;
23 import java.util.Date;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.sonar.api.rules.RuleType;
27 import org.sonar.api.utils.Duration;
28 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
29 import org.sonar.ce.task.projectanalysis.analysis.Branch;
30 import org.sonar.core.issue.DefaultIssue;
31 import org.sonar.core.issue.DefaultIssueComment;
32 import org.sonar.core.issue.FieldDiffs;
33 import org.sonar.core.issue.IssueChangeContext;
34 import org.sonar.db.component.BranchType;
35 import org.sonar.db.protobuf.DbCommons;
36 import org.sonar.db.protobuf.DbIssues;
37 import org.sonar.server.issue.IssueFieldsSetter;
38 import org.sonar.server.issue.workflow.IssueWorkflow;
39
40 import static com.google.common.collect.Lists.newArrayList;
41 import static org.assertj.core.api.Assertions.assertThat;
42 import static org.assertj.core.api.Assertions.assertThatThrownBy;
43 import static org.assertj.core.api.Assertions.entry;
44 import static org.assertj.core.groups.Tuple.tuple;
45 import static org.mockito.Mockito.mock;
46 import static org.mockito.Mockito.never;
47 import static org.mockito.Mockito.verify;
48 import static org.mockito.Mockito.verifyNoInteractions;
49 import static org.mockito.Mockito.when;
50 import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
51 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
52 import static org.sonar.api.issue.Issue.STATUS_CLOSED;
53 import static org.sonar.api.issue.Issue.STATUS_OPEN;
54 import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
55 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
56 import static org.sonar.api.rule.Severity.BLOCKER;
57 import static org.sonar.api.utils.DateUtils.parseDate;
58 import static org.sonar.db.rule.RuleTesting.XOO_X1;
59
60 public class IssueLifecycleTest {
61   private static final Date DEFAULT_DATE = new Date();
62   private static final Duration DEFAULT_DURATION = Duration.create(10);
63
64   private final DumbRule rule = new DumbRule(XOO_X1);
65
66   @Rule
67   public RuleRepositoryRule ruleRepository = new RuleRepositoryRule().add(rule);
68   @Rule
69   public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
70
71   private final IssueChangeContext issueChangeContext = IssueChangeContext.createUser(DEFAULT_DATE, "default_user_uuid");
72   private final IssueWorkflow workflow = mock(IssueWorkflow.class);
73   private final IssueFieldsSetter updater = mock(IssueFieldsSetter.class);
74   private final DebtCalculator debtCalculator = mock(DebtCalculator.class);
75   private final IssueLifecycle underTest = new IssueLifecycle(analysisMetadataHolder, issueChangeContext, workflow, updater, debtCalculator, ruleRepository);
76
77   @Test
78   public void initNewOpenIssue() {
79     DefaultIssue issue = new DefaultIssue()
80       .setRuleKey(XOO_X1);
81     when(debtCalculator.calculate(issue)).thenReturn(DEFAULT_DURATION);
82
83     underTest.initNewOpenIssue(issue);
84
85     assertThat(issue.key()).isNotNull();
86     assertThat(issue.creationDate()).isNotNull();
87     assertThat(issue.updateDate()).isNotNull();
88     assertThat(issue.status()).isEqualTo(STATUS_OPEN);
89     assertThat(issue.effort()).isEqualTo(DEFAULT_DURATION);
90     assertThat(issue.isNew()).isTrue();
91     assertThat(issue.isCopied()).isFalse();
92   }
93
94   @Test
95   public void initNewOpenHotspot() {
96     rule.setType(RuleType.SECURITY_HOTSPOT);
97     DefaultIssue issue = new DefaultIssue()
98       .setRuleKey(XOO_X1);
99     when(debtCalculator.calculate(issue)).thenReturn(DEFAULT_DURATION);
100
101     underTest.initNewOpenIssue(issue);
102
103     assertThat(issue.key()).isNotNull();
104     assertThat(issue.creationDate()).isNotNull();
105     assertThat(issue.updateDate()).isNotNull();
106     assertThat(issue.status()).isEqualTo(STATUS_TO_REVIEW);
107     assertThat(issue.resolution()).isNull();
108     assertThat(issue.effort()).isEqualTo(DEFAULT_DURATION);
109     assertThat(issue.isNew()).isTrue();
110     assertThat(issue.isCopied()).isFalse();
111   }
112
113   @Test
114   public void mergeIssueFromPRIntoBranch() {
115     DefaultIssue raw = new DefaultIssue()
116       .setKey("raw");
117     DefaultIssue fromShort = new DefaultIssue()
118       .setKey("short");
119     fromShort.setResolution("resolution");
120     fromShort.setStatus("status");
121
122     Date commentDate = new Date();
123     fromShort.addComment(new DefaultIssueComment()
124       .setIssueKey("short")
125       .setCreatedAt(commentDate)
126       .setUserUuid("user_uuid")
127       .setMarkdownText("A comment"));
128
129     Date diffDate = new Date();
130     // file diff alone
131     fromShort.addChange(new FieldDiffs()
132       .setCreationDate(diffDate)
133       .setIssueKey("short")
134       .setUserUuid("user_uuid")
135       .setDiff("file", "uuidA1", "uuidB1"));
136     // file diff with another field
137     fromShort.addChange(new FieldDiffs()
138       .setCreationDate(diffDate)
139       .setIssueKey("short")
140       .setUserUuid("user_uuid")
141       .setDiff("severity", "MINOR", "MAJOR")
142       .setDiff("file", "uuidA2", "uuidB2"));
143
144     Branch branch = mock(Branch.class);
145     when(branch.getName()).thenReturn("master");
146     analysisMetadataHolder.setBranch(branch);
147
148     underTest.mergeConfirmedOrResolvedFromPr(raw, fromShort, "2");
149
150     assertThat(raw.resolution()).isEqualTo("resolution");
151     assertThat(raw.status()).isEqualTo("status");
152     assertThat(raw.defaultIssueComments())
153       .extracting(DefaultIssueComment::issueKey, DefaultIssueComment::createdAt, DefaultIssueComment::userUuid, DefaultIssueComment::markdownText)
154       .containsOnly(tuple("raw", commentDate, "user_uuid", "A comment"));
155     assertThat(raw.changes()).hasSize(2);
156     assertThat(raw.changes().get(0).creationDate()).isEqualTo(diffDate);
157     assertThat(raw.changes().get(0).userUuid()).isEqualTo("user_uuid");
158     assertThat(raw.changes().get(0).issueKey()).isEqualTo("raw");
159     assertThat(raw.changes().get(0).diffs()).containsOnlyKeys("severity");
160     assertThat(raw.changes().get(1).userUuid()).isEqualTo("default_user_uuid");
161     assertThat(raw.changes().get(1).diffs()).containsOnlyKeys(IssueFieldsSetter.FROM_BRANCH);
162     assertThat(raw.changes().get(1).get(IssueFieldsSetter.FROM_BRANCH).oldValue()).isEqualTo("#2");
163     assertThat(raw.changes().get(1).get(IssueFieldsSetter.FROM_BRANCH).newValue()).isEqualTo("master");
164   }
165
166   @Test
167   public void copyExistingIssuesFromSourceBranchOfPullRequest() {
168     String pullRequestKey = "1";
169     Branch branch = mock(Branch.class);
170     when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
171     when(branch.getName()).thenReturn("sourceBranch-1");
172     when(branch.getPullRequestKey()).thenReturn(pullRequestKey);
173     analysisMetadataHolder.setBranch(branch);
174     analysisMetadataHolder.setPullRequestKey(pullRequestKey);
175     DefaultIssue raw = new DefaultIssue()
176       .setKey("raw");
177     DefaultIssue fromShort = new DefaultIssue()
178       .setKey("short");
179     fromShort.setResolution("resolution");
180     fromShort.setStatus("status");
181
182     Date commentDate = new Date();
183     fromShort.addComment(new DefaultIssueComment()
184       .setIssueKey("short")
185       .setCreatedAt(commentDate)
186       .setUserUuid("user_uuid")
187       .setMarkdownText("A comment"));
188
189     Date diffDate = new Date();
190     // file diff alone
191     fromShort.addChange(new FieldDiffs()
192       .setCreationDate(diffDate)
193       .setIssueKey("short")
194       .setUserUuid("user_uuid")
195       .setDiff("file", "uuidA1", "uuidB1"));
196     // file diff with another field
197     fromShort.addChange(new FieldDiffs()
198       .setCreationDate(diffDate)
199       .setIssueKey("short")
200       .setUserUuid("user_uuid")
201       .setDiff("severity", "MINOR", "MAJOR")
202       .setDiff("file", "uuidA2", "uuidB2"));
203
204     underTest.copyExistingIssueFromSourceBranchToPullRequest(raw, fromShort);
205
206     assertThat(raw.resolution()).isEqualTo("resolution");
207     assertThat(raw.status()).isEqualTo("status");
208     assertThat(raw.defaultIssueComments())
209       .extracting(DefaultIssueComment::issueKey, DefaultIssueComment::createdAt, DefaultIssueComment::userUuid, DefaultIssueComment::markdownText)
210       .containsOnly(tuple("raw", commentDate, "user_uuid", "A comment"));
211     assertThat(raw.changes()).hasSize(2);
212     assertThat(raw.changes().get(0).creationDate()).isEqualTo(diffDate);
213     assertThat(raw.changes().get(0).userUuid()).isEqualTo("user_uuid");
214     assertThat(raw.changes().get(0).issueKey()).isEqualTo("raw");
215     assertThat(raw.changes().get(0).diffs()).containsOnlyKeys("severity");
216     assertThat(raw.changes().get(1).userUuid()).isEqualTo("default_user_uuid");
217     assertThat(raw.changes().get(1).diffs()).containsOnlyKeys(IssueFieldsSetter.FROM_BRANCH);
218     assertThat(raw.changes().get(1).get(IssueFieldsSetter.FROM_BRANCH).oldValue()).isEqualTo("sourceBranch-1");
219     assertThat(raw.changes().get(1).get(IssueFieldsSetter.FROM_BRANCH).newValue()).isEqualTo("#1");
220   }
221
222   @Test
223   public void copyExistingIssuesFromSourceBranchOfPullRequest_only_works_for_pull_requests() {
224     DefaultIssue raw = new DefaultIssue()
225       .setKey("raw");
226     DefaultIssue from = new DefaultIssue()
227       .setKey("short");
228     from.setResolution("resolution");
229     from.setStatus("status");
230     Branch branch = mock(Branch.class);
231     when(branch.getType()).thenReturn(BranchType.BRANCH);
232     analysisMetadataHolder.setBranch(branch);
233
234     assertThatThrownBy(() -> underTest.copyExistingIssueFromSourceBranchToPullRequest(raw, from))
235       .isInstanceOf(IllegalStateException.class)
236       .hasMessage("This operation should be done only on pull request analysis");
237   }
238
239   @Test
240   public void copiedIssue() {
241     DefaultIssue raw = new DefaultIssue()
242       .setNew(true)
243       .setKey("RAW_KEY")
244       .setCreationDate(parseDate("2015-10-01"))
245       .setUpdateDate(parseDate("2015-10-02"))
246       .setCloseDate(parseDate("2015-10-03"));
247
248     DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
249       .setTextRange(DbCommons.TextRange.newBuilder()
250         .setStartLine(10)
251         .setEndLine(12)
252         .build())
253       .build();
254     DefaultIssue base = new DefaultIssue()
255       .setKey("BASE_KEY")
256       .setCreationDate(parseDate("2015-01-01"))
257       .setUpdateDate(parseDate("2015-01-02"))
258       .setCloseDate(parseDate("2015-01-03"))
259       .setResolution(RESOLUTION_FIXED)
260       .setStatus(STATUS_CLOSED)
261       .setSeverity(BLOCKER)
262       .setAssigneeUuid("base assignee uuid")
263       .setAuthorLogin("base author")
264       .setTags(newArrayList("base tag"))
265       .setOnDisabledRule(true)
266       .setSelectedAt(1000L)
267       .setLine(10)
268       .setMessage("message")
269       .setGap(15d)
270       .setEffort(Duration.create(15L))
271       .setManualSeverity(false)
272       .setLocations(issueLocations);
273
274     when(debtCalculator.calculate(raw)).thenReturn(DEFAULT_DURATION);
275
276     Branch branch = mock(Branch.class);
277     when(branch.getName()).thenReturn("release-2.x");
278     analysisMetadataHolder.setBranch(branch);
279
280     underTest.copyExistingOpenIssueFromBranch(raw, base, "master");
281
282     assertThat(raw.isNew()).isFalse();
283     assertThat(raw.isCopied()).isTrue();
284     assertThat(raw.key()).isNotNull();
285     assertThat(raw.key()).isNotEqualTo(base.key());
286     assertThat(raw.creationDate()).isEqualTo(base.creationDate());
287     assertThat(raw.updateDate()).isEqualTo(base.updateDate());
288     assertThat(raw.closeDate()).isEqualTo(base.closeDate());
289     assertThat(raw.resolution()).isEqualTo(RESOLUTION_FIXED);
290     assertThat(raw.status()).isEqualTo(STATUS_CLOSED);
291     assertThat(raw.assignee()).isEqualTo("base assignee uuid");
292     assertThat(raw.authorLogin()).isEqualTo("base author");
293     assertThat(raw.tags()).containsOnly("base tag");
294     assertThat(raw.effort()).isEqualTo(DEFAULT_DURATION);
295     assertThat(raw.isOnDisabledRule()).isTrue();
296     assertThat(raw.selectedAt()).isEqualTo(1000L);
297     assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_BRANCH).oldValue()).isEqualTo("master");
298     assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_BRANCH).newValue()).isEqualTo("release-2.x");
299
300     verifyNoInteractions(updater);
301   }
302
303   @Test
304   public void doAutomaticTransition() {
305     DefaultIssue issue = new DefaultIssue();
306
307     underTest.doAutomaticTransition(issue);
308
309     verify(workflow).doAutomaticTransition(issue, issueChangeContext);
310   }
311
312   @Test
313   public void mergeExistingOpenIssue() {
314     DefaultIssue raw = new DefaultIssue()
315       .setNew(true)
316       .setKey("RAW_KEY")
317       .setRuleKey(XOO_X1)
318       .setCreationDate(parseDate("2015-10-01"))
319       .setUpdateDate(parseDate("2015-10-02"))
320       .setCloseDate(parseDate("2015-10-03"));
321
322     DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
323       .setTextRange(DbCommons.TextRange.newBuilder()
324         .setStartLine(10)
325         .setEndLine(12)
326         .build())
327       .build();
328     DefaultIssue base = new DefaultIssue()
329       .setKey("BASE_KEY")
330       .setCreationDate(parseDate("2015-01-01"))
331       .setUpdateDate(parseDate("2015-01-02"))
332       .setResolution(RESOLUTION_FALSE_POSITIVE)
333       .setStatus(STATUS_RESOLVED)
334       .setSeverity(BLOCKER)
335       .setAssigneeUuid("base assignee uuid")
336       .setAuthorLogin("base author")
337       .setTags(newArrayList("base tag"))
338       .setOnDisabledRule(true)
339       .setSelectedAt(1000L)
340       .setLine(10)
341       .setMessage("message")
342       .setGap(15d)
343       .setEffort(Duration.create(15L))
344       .setManualSeverity(false)
345       .setLocations(issueLocations)
346       .addChange(new FieldDiffs().setDiff("foo", "bar", "donut"))
347       .addChange(new FieldDiffs().setDiff("file", "A", "B"));
348
349     when(debtCalculator.calculate(raw)).thenReturn(DEFAULT_DURATION);
350
351     underTest.mergeExistingOpenIssue(raw, base);
352
353     assertThat(raw.isNew()).isFalse();
354     assertThat(raw.key()).isEqualTo("BASE_KEY");
355     assertThat(raw.creationDate()).isEqualTo(base.creationDate());
356     assertThat(raw.updateDate()).isEqualTo(base.updateDate());
357     assertThat(raw.resolution()).isEqualTo(RESOLUTION_FALSE_POSITIVE);
358     assertThat(raw.status()).isEqualTo(STATUS_RESOLVED);
359     assertThat(raw.assignee()).isEqualTo("base assignee uuid");
360     assertThat(raw.authorLogin()).isEqualTo("base author");
361     assertThat(raw.tags()).containsOnly("base tag");
362     assertThat(raw.effort()).isEqualTo(DEFAULT_DURATION);
363     assertThat(raw.isOnDisabledRule()).isTrue();
364     assertThat(raw.selectedAt()).isEqualTo(1000L);
365     assertThat(raw.isChanged()).isFalse();
366     assertThat(raw.changes()).hasSize(2);
367     assertThat(raw.changes().get(0).diffs())
368       .containsOnly(entry("foo", new FieldDiffs.Diff<>("bar", "donut")));
369     assertThat(raw.changes().get(1).diffs())
370       .containsOnly(entry("file", new FieldDiffs.Diff<>("A", "B")));
371
372     verify(updater).setPastSeverity(raw, BLOCKER, issueChangeContext);
373     verify(updater).setPastLine(raw, 10);
374     verify(updater).setPastMessage(raw, "message", issueChangeContext);
375     verify(updater).setPastEffort(raw, Duration.create(15L), issueChangeContext);
376     verify(updater).setPastLocations(raw, issueLocations);
377   }
378
379   @Test
380   public void mergeExistingOpenIssue_with_manual_severity() {
381     DefaultIssue raw = new DefaultIssue()
382       .setNew(true)
383       .setKey("RAW_KEY")
384       .setRuleKey(XOO_X1);
385     DefaultIssue base = new DefaultIssue()
386       .setKey("BASE_KEY")
387       .setResolution(RESOLUTION_FIXED)
388       .setStatus(STATUS_CLOSED)
389       .setSeverity(BLOCKER)
390       .setManualSeverity(true);
391
392     underTest.mergeExistingOpenIssue(raw, base);
393
394     assertThat(raw.manualSeverity()).isTrue();
395     assertThat(raw.severity()).isEqualTo(BLOCKER);
396
397     verify(updater, never()).setPastSeverity(raw, BLOCKER, issueChangeContext);
398   }
399
400   @Test
401   public void mergeExistingOpenIssue_with_base_changed() {
402     DefaultIssue raw = new DefaultIssue()
403       .setNew(true)
404       .setKey("RAW_KEY")
405       .setRuleKey(XOO_X1);
406     DefaultIssue base = new DefaultIssue()
407       .setChanged(true)
408       .setKey("BASE_KEY")
409       .setResolution(RESOLUTION_FALSE_POSITIVE)
410       .setStatus(STATUS_RESOLVED);
411
412     underTest.mergeExistingOpenIssue(raw, base);
413
414     assertThat(raw.isChanged()).isTrue();
415   }
416
417   @Test
418   public void mergeExistingOpenIssue_with_attributes() {
419     DefaultIssue raw = new DefaultIssue()
420       .setNew(true)
421       .setKey("RAW_KEY")
422       .setRuleKey(XOO_X1);
423     DefaultIssue base = new DefaultIssue()
424       .setKey("BASE_KEY")
425       .setResolution(RESOLUTION_FIXED)
426       .setStatus(STATUS_CLOSED)
427       .setSeverity(BLOCKER)
428       .setAttributes(ImmutableMap.of("JIRA", "SONAR-01"));
429
430     underTest.mergeExistingOpenIssue(raw, base);
431
432     assertThat(raw.attributes()).containsEntry("JIRA", "SONAR-01");
433   }
434 }