3 * Copyright (C) 2009-2017 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.computation.task.projectanalysis.issue;
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.utils.Duration;
27 import org.sonar.core.issue.DefaultIssue;
28 import org.sonar.core.issue.DefaultIssueComment;
29 import org.sonar.core.issue.IssueChangeContext;
30 import org.sonar.db.protobuf.DbCommons;
31 import org.sonar.db.protobuf.DbIssues;
32 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
33 import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
34 import org.sonar.server.issue.IssueFieldsSetter;
35 import org.sonar.server.issue.workflow.IssueWorkflow;
37 import static com.google.common.collect.Lists.newArrayList;
38 import static org.assertj.core.api.Assertions.assertThat;
39 import static org.mockito.Mockito.mock;
40 import static org.mockito.Mockito.never;
41 import static org.mockito.Mockito.verify;
42 import static org.mockito.Mockito.verifyZeroInteractions;
43 import static org.mockito.Mockito.when;
44 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
45 import static org.sonar.api.issue.Issue.STATUS_CLOSED;
46 import static org.sonar.api.issue.Issue.STATUS_OPEN;
47 import static org.sonar.api.rule.Severity.BLOCKER;
48 import static org.sonar.api.utils.DateUtils.parseDate;
50 public class IssueLifecycleTest {
52 static final Date DEFAULT_DATE = new Date();
54 static final Duration DEFAULT_DURATION = Duration.create(10);
56 IssueChangeContext issueChangeContext = IssueChangeContext.createUser(DEFAULT_DATE, "julien");
58 IssueWorkflow workflow = mock(IssueWorkflow.class);
60 IssueFieldsSetter updater = mock(IssueFieldsSetter.class);
62 DebtCalculator debtCalculator = mock(DebtCalculator.class);
65 public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
67 IssueLifecycle underTest = new IssueLifecycle(analysisMetadataHolder, issueChangeContext, workflow, updater, debtCalculator);
70 public void initNewOpenIssue() throws Exception {
71 DefaultIssue issue = new DefaultIssue();
72 when(debtCalculator.calculate(issue)).thenReturn(DEFAULT_DURATION);
74 underTest.initNewOpenIssue(issue);
76 assertThat(issue.key()).isNotNull();
77 assertThat(issue.creationDate()).isNotNull();
78 assertThat(issue.updateDate()).isNotNull();
79 assertThat(issue.status()).isEqualTo(STATUS_OPEN);
80 assertThat(issue.debt()).isEqualTo(DEFAULT_DURATION);
81 assertThat(issue.isNew()).isTrue();
82 assertThat(issue.isCopied()).isFalse();
86 public void mergeIssueFromShortLivingBranch() {
87 DefaultIssue raw = new DefaultIssue();
88 DefaultIssue fromShort = new DefaultIssue();
89 fromShort.setResolution("resolution");
90 fromShort.setStatus("status");
92 Date commentDate = new Date();
93 fromShort.addComment(new DefaultIssueComment()
95 .setCreatedAt(commentDate)
97 .setMarkdownText("A comment"));
99 Branch branch = mock(Branch.class);
100 when(branch.getName()).thenReturn("master");
101 analysisMetadataHolder.setBranch(branch);
103 underTest.mergeConfirmedOrResolvedFromShortLivingBranch(raw, fromShort, "feature/foo");
104 assertThat(raw.resolution()).isEqualTo("resolution");
105 assertThat(raw.status()).isEqualTo("status");
106 assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_SHORT_BRANCH).oldValue()).isEqualTo("feature/foo");
107 assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_SHORT_BRANCH).newValue()).isEqualTo("master");
111 public void copiedIssue() throws Exception {
112 DefaultIssue raw = new DefaultIssue()
115 .setCreationDate(parseDate("2015-10-01"))
116 .setUpdateDate(parseDate("2015-10-02"))
117 .setCloseDate(parseDate("2015-10-03"));
119 DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
120 .setTextRange(DbCommons.TextRange.newBuilder()
125 DefaultIssue base = new DefaultIssue()
127 .setCreationDate(parseDate("2015-01-01"))
128 .setUpdateDate(parseDate("2015-01-02"))
129 .setCloseDate(parseDate("2015-01-03"))
130 .setResolution(RESOLUTION_FIXED)
131 .setStatus(STATUS_CLOSED)
132 .setSeverity(BLOCKER)
133 .setAssignee("base assignee")
134 .setAuthorLogin("base author")
135 .setTags(newArrayList("base tag"))
136 .setOnDisabledRule(true)
137 .setSelectedAt(1000L)
139 .setMessage("message")
141 .setEffort(Duration.create(15L))
142 .setManualSeverity(false)
143 .setLocations(issueLocations);
145 when(debtCalculator.calculate(raw)).thenReturn(DEFAULT_DURATION);
147 Branch branch = mock(Branch.class);
148 when(branch.getName()).thenReturn("release-2.x");
149 analysisMetadataHolder.setBranch(branch);
151 underTest.copyExistingOpenIssueFromLongLivingBranch(raw, base, "master");
153 assertThat(raw.isNew()).isFalse();
154 assertThat(raw.isCopied()).isTrue();
155 assertThat(raw.key()).isNotNull();
156 assertThat(raw.key()).isNotEqualTo(base.key());
157 assertThat(raw.creationDate()).isEqualTo(base.creationDate());
158 assertThat(raw.updateDate()).isEqualTo(base.updateDate());
159 assertThat(raw.closeDate()).isEqualTo(base.closeDate());
160 assertThat(raw.resolution()).isEqualTo(RESOLUTION_FIXED);
161 assertThat(raw.status()).isEqualTo(STATUS_CLOSED);
162 assertThat(raw.assignee()).isEqualTo("base assignee");
163 assertThat(raw.authorLogin()).isEqualTo("base author");
164 assertThat(raw.tags()).containsOnly("base tag");
165 assertThat(raw.debt()).isEqualTo(DEFAULT_DURATION);
166 assertThat(raw.isOnDisabledRule()).isTrue();
167 assertThat(raw.selectedAt()).isEqualTo(1000L);
168 assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_LONG_BRANCH).oldValue()).isEqualTo("master");
169 assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_LONG_BRANCH).newValue()).isEqualTo("release-2.x");
171 verifyZeroInteractions(updater);
175 public void doAutomaticTransition() throws Exception {
176 DefaultIssue issue = new DefaultIssue();
178 underTest.doAutomaticTransition(issue);
180 verify(workflow).doAutomaticTransition(issue, issueChangeContext);
184 public void mergeExistingOpenIssue() throws Exception {
185 DefaultIssue raw = new DefaultIssue()
188 .setCreationDate(parseDate("2015-10-01"))
189 .setUpdateDate(parseDate("2015-10-02"))
190 .setCloseDate(parseDate("2015-10-03"));
192 DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder()
193 .setTextRange(DbCommons.TextRange.newBuilder()
198 DefaultIssue base = new DefaultIssue()
200 .setCreationDate(parseDate("2015-01-01"))
201 .setUpdateDate(parseDate("2015-01-02"))
202 .setCloseDate(parseDate("2015-01-03"))
203 .setResolution(RESOLUTION_FIXED)
204 .setStatus(STATUS_CLOSED)
205 .setSeverity(BLOCKER)
206 .setAssignee("base assignee")
207 .setAuthorLogin("base author")
208 .setTags(newArrayList("base tag"))
209 .setOnDisabledRule(true)
210 .setSelectedAt(1000L)
212 .setMessage("message")
214 .setEffort(Duration.create(15L))
215 .setManualSeverity(false)
216 .setLocations(issueLocations);
218 when(debtCalculator.calculate(raw)).thenReturn(DEFAULT_DURATION);
220 underTest.mergeExistingOpenIssue(raw, base);
222 assertThat(raw.isNew()).isFalse();
223 assertThat(raw.key()).isEqualTo("BASE_KEY");
224 assertThat(raw.creationDate()).isEqualTo(base.creationDate());
225 assertThat(raw.updateDate()).isEqualTo(base.updateDate());
226 assertThat(raw.closeDate()).isEqualTo(base.closeDate());
227 assertThat(raw.resolution()).isEqualTo(RESOLUTION_FIXED);
228 assertThat(raw.status()).isEqualTo(STATUS_CLOSED);
229 assertThat(raw.assignee()).isEqualTo("base assignee");
230 assertThat(raw.authorLogin()).isEqualTo("base author");
231 assertThat(raw.tags()).containsOnly("base tag");
232 assertThat(raw.debt()).isEqualTo(DEFAULT_DURATION);
233 assertThat(raw.isOnDisabledRule()).isTrue();
234 assertThat(raw.selectedAt()).isEqualTo(1000L);
236 verify(updater).setPastSeverity(raw, BLOCKER, issueChangeContext);
237 verify(updater).setPastLine(raw, 10);
238 verify(updater).setPastMessage(raw, "message", issueChangeContext);
239 verify(updater).setPastEffort(raw, Duration.create(15L), issueChangeContext);
240 verify(updater).setPastLocations(raw, issueLocations);
244 public void mergeExistingOpenIssue_with_manual_severity() throws Exception {
245 DefaultIssue raw = new DefaultIssue()
248 DefaultIssue base = new DefaultIssue()
250 .setResolution(RESOLUTION_FIXED)
251 .setStatus(STATUS_CLOSED)
252 .setSeverity(BLOCKER)
253 .setManualSeverity(true);
255 underTest.mergeExistingOpenIssue(raw, base);
257 assertThat(raw.manualSeverity()).isTrue();
258 assertThat(raw.severity()).isEqualTo(BLOCKER);
260 verify(updater, never()).setPastSeverity(raw, BLOCKER, issueChangeContext);
264 public void mergeExistingOpenIssue_with_attributes() throws Exception {
265 DefaultIssue raw = new DefaultIssue()
268 DefaultIssue base = new DefaultIssue()
270 .setResolution(RESOLUTION_FIXED)
271 .setStatus(STATUS_CLOSED)
272 .setSeverity(BLOCKER)
273 .setAttributes(ImmutableMap.of("JIRA", "SONAR-01"));
275 underTest.mergeExistingOpenIssue(raw, base);
277 assertThat(raw.attributes()).containsEntry("JIRA", "SONAR-01");