]> source.dussan.org Git - sonarqube.git/blob
3886de337640e205472d59fcda6ed7d6a8c3156b
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2020 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.server.issue.workflow;
21
22 import com.tngtech.java.junit.dataprovider.DataProvider;
23 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
24 import com.tngtech.java.junit.dataprovider.UseDataProvider;
25 import java.util.Arrays;
26 import java.util.Calendar;
27 import java.util.Collection;
28 import java.util.Date;
29 import java.util.List;
30 import org.apache.commons.lang.time.DateUtils;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.sonar.api.issue.DefaultTransitions;
34 import org.sonar.api.issue.Issue;
35 import org.sonar.api.rule.RuleKey;
36 import org.sonar.api.rules.RuleType;
37 import org.sonar.core.issue.DefaultIssue;
38 import org.sonar.core.issue.FieldDiffs;
39 import org.sonar.core.issue.IssueChangeContext;
40 import org.sonar.core.util.stream.MoreCollectors;
41 import org.sonar.server.issue.IssueFieldsSetter;
42
43 import static org.assertj.core.api.Assertions.assertThat;
44 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
45 import static org.sonar.api.issue.Issue.RESOLUTION_REMOVED;
46 import static org.sonar.api.issue.Issue.STATUS_CLOSED;
47 import static org.sonar.api.issue.Issue.STATUS_IN_REVIEW;
48 import static org.sonar.api.issue.Issue.STATUS_OPEN;
49 import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
50 import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
51 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
52 import static org.sonar.db.rule.RuleTesting.XOO_X1;
53
54 @RunWith(DataProviderRunner.class)
55 public class IssueWorkflowForSecurityHotspotsTest {
56
57   private static final String[] ALL_STATUSES_LEADING_TO_CLOSED = new String[] {STATUS_TO_REVIEW, STATUS_IN_REVIEW, STATUS_RESOLVED};
58
59   private static final String[] SUPPORTED_RESOLUTIONS_FOR_UNCLOSING = new String[] {RESOLUTION_FIXED, RESOLUTION_REMOVED};
60
61   private IssueFieldsSetter updater = new IssueFieldsSetter();
62
63   private IssueWorkflow underTest = new IssueWorkflow(new FunctionExecutor(updater), updater);
64
65   @DataProvider
66   public static Object[][] allStatusesLeadingToClosed() {
67     return Arrays.stream(ALL_STATUSES_LEADING_TO_CLOSED)
68       .map(t -> new Object[] {t})
69       .toArray(Object[][]::new);
70   }
71
72   private static DefaultIssue newClosedIssue(String resolution) {
73     return new DefaultIssue()
74       .setKey("ABCDE")
75       .setRuleKey(RuleKey.of("js", "S001"))
76       .setResolution(resolution)
77       .setStatus(STATUS_CLOSED)
78       .setNew(false)
79       .setCloseDate(new Date(5_999_999L));
80   }
81
82   private static void setStatusPreviousToClosed(DefaultIssue issue, String previousStatus) {
83     addStatusChange(issue, new Date(), previousStatus, STATUS_CLOSED);
84   }
85
86   private static void addStatusChange(DefaultIssue issue, Date date, String previousStatus, String newStatus) {
87     issue.addChange(new FieldDiffs().setCreationDate(date).setDiff("status", previousStatus, newStatus));
88   }
89
90   @Test
91   public void list_out_transitions_in_status_to_review() {
92     underTest.start();
93     DefaultIssue issue = new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(STATUS_TO_REVIEW);
94
95     List<Transition> transitions = underTest.outTransitions(issue);
96
97     assertThat(keys(transitions)).containsExactlyInAnyOrder("setinreview", "resolveasreviewed", "openasvulnerability");
98   }
99
100   @Test
101   public void list_out_transitions_in_status_in_review() {
102     underTest.start();
103     DefaultIssue issue = new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(STATUS_IN_REVIEW);
104
105     List<Transition> transitions = underTest.outTransitions(issue);
106
107     assertThat(keys(transitions)).containsExactlyInAnyOrder("resolveasreviewed", "openasvulnerability", "resetastoreview");
108   }
109
110   @Test
111   public void list_out_transitions_in_status_reviewed() {
112     underTest.start();
113     DefaultIssue issue = new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(STATUS_REVIEWED);
114
115     List<Transition> transitions = underTest.outTransitions(issue);
116
117     assertThat(keys(transitions)).containsExactlyInAnyOrder("openasvulnerability", "resetastoreview");
118   }
119
120   @Test
121   public void list_out_vulnerability_transitions_in_status_open() {
122     underTest.start();
123     DefaultIssue issue = new DefaultIssue().setType(RuleType.VULNERABILITY).setResolution(RESOLUTION_FIXED).setStatus(STATUS_OPEN).setIsFromHotspot(true);
124
125     List<Transition> transitions = underTest.outTransitions(issue);
126
127     assertThat(keys(transitions)).containsExactlyInAnyOrder("resolveasreviewed", "resetastoreview");
128   }
129
130   @Test
131   public void set_as_in_review() {
132     underTest.start();
133     DefaultIssue issue = new DefaultIssue()
134       .setType(RuleType.SECURITY_HOTSPOT)
135       .setIsFromHotspot(true)
136       .setStatus(STATUS_TO_REVIEW);
137
138     boolean result = underTest.doManualTransition(issue, DefaultTransitions.SET_AS_IN_REVIEW, IssueChangeContext.createUser(new Date(), "USER1"));
139
140     assertThat(result).isTrue();
141     assertThat(issue.getStatus()).isEqualTo(STATUS_IN_REVIEW);
142     assertThat(issue.resolution()).isNull();
143   }
144
145   @Test
146   public void resolve_as_reviewed_from_to_review() {
147     underTest.start();
148     DefaultIssue issue = new DefaultIssue()
149       .setType(RuleType.SECURITY_HOTSPOT)
150       .setIsFromHotspot(true)
151       .setStatus(STATUS_TO_REVIEW);
152
153     boolean result = underTest.doManualTransition(issue, DefaultTransitions.RESOLVE_AS_REVIEWED, IssueChangeContext.createUser(new Date(), "USER1"));
154
155     assertThat(result).isTrue();
156     assertThat(issue.getStatus()).isEqualTo(STATUS_REVIEWED);
157     assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
158   }
159
160   @Test
161   public void resolve_as_reviewed_from_in_review() {
162     underTest.start();
163     DefaultIssue issue = new DefaultIssue()
164       .setType(RuleType.SECURITY_HOTSPOT)
165       .setIsFromHotspot(true)
166       .setStatus(STATUS_IN_REVIEW)
167       .setResolution(null);
168
169     boolean result = underTest.doManualTransition(issue, DefaultTransitions.RESOLVE_AS_REVIEWED, IssueChangeContext.createUser(new Date(), "USER1"));
170
171     assertThat(result).isTrue();
172     assertThat(issue.getStatus()).isEqualTo(STATUS_REVIEWED);
173     assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
174   }
175
176   @Test
177   public void open_as_vulnerability_from_in_review() {
178     underTest.start();
179     DefaultIssue issue = new DefaultIssue()
180       .setType(RuleType.SECURITY_HOTSPOT)
181       .setIsFromHotspot(true)
182       .setStatus(STATUS_IN_REVIEW)
183       .setResolution(null);
184
185     boolean result = underTest.doManualTransition(issue, DefaultTransitions.OPEN_AS_VULNERABILITY, IssueChangeContext.createUser(new Date(), "USER1"));
186
187     assertThat(result).isTrue();
188     assertThat(issue.type()).isEqualTo(RuleType.VULNERABILITY);
189     assertThat(issue.getStatus()).isEqualTo(Issue.STATUS_OPEN);
190     assertThat(issue.resolution()).isNull();
191   }
192
193   @Test
194   public void open_as_vulnerability_from_to_review() {
195     underTest.start();
196     DefaultIssue issue = new DefaultIssue()
197       .setType(RuleType.SECURITY_HOTSPOT)
198       .setIsFromHotspot(true)
199       .setStatus(STATUS_TO_REVIEW)
200       .setResolution(null);
201
202     boolean result = underTest.doManualTransition(issue, DefaultTransitions.OPEN_AS_VULNERABILITY, IssueChangeContext.createUser(new Date(), "USER1"));
203
204     assertThat(result).isTrue();
205     assertThat(issue.type()).isEqualTo(RuleType.VULNERABILITY);
206     assertThat(issue.getStatus()).isEqualTo(Issue.STATUS_OPEN);
207     assertThat(issue.resolution()).isNull();
208   }
209
210   @Test
211   public void open_as_vulnerability_from_reviewed() {
212     underTest.start();
213     DefaultIssue issue = new DefaultIssue()
214       .setType(RuleType.SECURITY_HOTSPOT)
215       .setIsFromHotspot(true)
216       .setResolution(RESOLUTION_FIXED)
217       .setStatus(STATUS_REVIEWED);
218
219     boolean result = underTest.doManualTransition(issue, DefaultTransitions.OPEN_AS_VULNERABILITY, IssueChangeContext.createUser(new Date(), "USER1"));
220
221     assertThat(result).isTrue();
222     assertThat(issue.type()).isEqualTo(RuleType.VULNERABILITY);
223     assertThat(issue.getStatus()).isEqualTo(Issue.STATUS_OPEN);
224     assertThat(issue.resolution()).isNull();
225   }
226
227   @Test
228   public void reset_as_to_review_from_reviewed() {
229     underTest.start();
230     DefaultIssue issue = new DefaultIssue()
231       .setType(RuleType.SECURITY_HOTSPOT)
232       .setIsFromHotspot(true)
233       .setStatus(STATUS_REVIEWED)
234       .setResolution(RESOLUTION_FIXED);
235
236     boolean result = underTest.doManualTransition(issue, DefaultTransitions.RESET_AS_TO_REVIEW, IssueChangeContext.createUser(new Date(), "USER1"));
237     assertThat(result).isTrue();
238     assertThat(issue.type()).isEqualTo(RuleType.SECURITY_HOTSPOT);
239     assertThat(issue.getStatus()).isEqualTo(STATUS_TO_REVIEW);
240     assertThat(issue.resolution()).isNull();
241   }
242
243   @Test
244   public void reset_as_to_review_from_in_review() {
245     underTest.start();
246     DefaultIssue issue = new DefaultIssue()
247       .setType(RuleType.SECURITY_HOTSPOT)
248       .setIsFromHotspot(true)
249       .setStatus(STATUS_IN_REVIEW)
250       .setResolution(null);
251
252     boolean result = underTest.doManualTransition(issue, DefaultTransitions.RESET_AS_TO_REVIEW, IssueChangeContext.createUser(new Date(), "USER1"));
253     assertThat(result).isTrue();
254     assertThat(issue.type()).isEqualTo(RuleType.SECURITY_HOTSPOT);
255     assertThat(issue.getStatus()).isEqualTo(STATUS_TO_REVIEW);
256     assertThat(issue.resolution()).isNull();
257   }
258
259   @Test
260   public void reset_as_to_review_from_opened_as_vulnerability() {
261     underTest.start();
262     DefaultIssue issue = new DefaultIssue()
263       .setType(RuleType.VULNERABILITY)
264       .setIsFromHotspot(true)
265       .setStatus(STATUS_OPEN)
266       .setResolution(null);
267
268     boolean result = underTest.doManualTransition(issue, DefaultTransitions.RESET_AS_TO_REVIEW, IssueChangeContext.createUser(new Date(), "USER1"));
269     assertThat(result).isTrue();
270     assertThat(issue.type()).isEqualTo(RuleType.SECURITY_HOTSPOT);
271     assertThat(issue.getStatus()).isEqualTo(STATUS_TO_REVIEW);
272     assertThat(issue.resolution()).isNull();
273   }
274
275   @Test
276   public void automatically_close_resolved_security_hotspots_in_status_to_review() {
277     underTest.start();
278     DefaultIssue issue = new DefaultIssue()
279       .setType(RuleType.SECURITY_HOTSPOT)
280       .setResolution(null)
281       .setStatus(STATUS_TO_REVIEW)
282       .setNew(false)
283       .setBeingClosed(true);
284     Date now = new Date();
285
286     underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
287
288     assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
289     assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
290     assertThat(issue.closeDate()).isNotNull();
291     assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
292   }
293
294   @Test
295   public void automatically_close_resolved_security_hotspots_in_status_in_review() {
296     underTest.start();
297     DefaultIssue issue = new DefaultIssue()
298       .setType(RuleType.SECURITY_HOTSPOT)
299       .setResolution(null)
300       .setStatus(STATUS_IN_REVIEW)
301       .setNew(false)
302       .setBeingClosed(true);
303     Date now = new Date();
304
305     underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
306
307     assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
308     assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
309     assertThat(issue.closeDate()).isNotNull();
310     assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
311   }
312
313   @Test
314   public void automatically_close_resolved_security_hotspots_in_status_reviewed() {
315     underTest.start();
316     DefaultIssue issue = new DefaultIssue()
317       .setType(RuleType.SECURITY_HOTSPOT)
318       .setResolution(RESOLUTION_FIXED)
319       .setStatus(STATUS_REVIEWED)
320       .setNew(false)
321       .setBeingClosed(true);
322     Date now = new Date();
323
324     underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
325
326     assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
327     assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
328     assertThat(issue.closeDate()).isNotNull();
329     assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
330   }
331
332   @Test
333   public void automatically_close_hotspots_opened_as_vulnerability() {
334     underTest.start();
335     DefaultIssue issue = new DefaultIssue()
336       .setType(RuleType.VULNERABILITY)
337       .setResolution(null)
338       .setStatus(STATUS_OPEN)
339       .setIsFromHotspot(true)
340       .setNew(false)
341       .setBeingClosed(true);
342     Date now = new Date();
343
344     underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
345
346     assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
347     assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
348     assertThat(issue.closeDate()).isNotNull();
349     assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
350   }
351
352   @Test
353   @UseDataProvider("allStatusesLeadingToClosed")
354   public void do_not_automatically_reopen_closed_issues_of_security_hotspots(String previousStatus) {
355     DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING)
356       .map(resolution -> {
357         DefaultIssue issue = newClosedIssue(resolution);
358         setStatusPreviousToClosed(issue, previousStatus);
359         issue.setType(RuleType.SECURITY_HOTSPOT);
360         return issue;
361       })
362       .toArray(DefaultIssue[]::new);
363     Date now = new Date();
364     underTest.start();
365
366     Arrays.stream(issues).forEach(issue -> {
367       underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
368
369       assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
370       assertThat(issue.updateDate()).isNull();
371     });
372   }
373
374   @Test
375   public void doAutomaticTransition_does_nothing_on_security_hotspots_in_to_review_status() {
376     DefaultIssue issue = new DefaultIssue()
377       .setKey("ABCDE")
378       .setRuleKey(XOO_X1)
379       .setResolution(null)
380       .setStatus(STATUS_TO_REVIEW);
381
382     underTest.start();
383     underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(new Date()));
384
385     assertThat(issue.status()).isEqualTo(STATUS_TO_REVIEW);
386     assertThat(issue.resolution()).isNull();
387   }
388
389   @Test
390   @UseDataProvider("allStatusesLeadingToClosed")
391   public void do_not_automatically_reopen_closed_issues_of_manual_vulnerability(String previousStatus) {
392     DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING)
393       .map(resolution -> {
394         DefaultIssue issue = newClosedIssue(resolution);
395         setStatusPreviousToClosed(issue, previousStatus);
396         issue.setIsFromHotspot(true);
397         return issue;
398       })
399       .toArray(DefaultIssue[]::new);
400     Date now = new Date();
401     underTest.start();
402
403     Arrays.stream(issues).forEach(issue -> {
404       underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
405
406       assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
407       assertThat(issue.updateDate()).isNull();
408     });
409   }
410
411   @Test
412   public void do_not_allow_to_doManualTransition_when_condition_fails() {
413     underTest.start();
414     DefaultIssue issue = new DefaultIssue()
415       .setKey("ABCDE")
416       // Detect is only available on hotspot
417       .setType(RuleType.VULNERABILITY)
418       .setIsFromHotspot(false)
419       .setStatus(STATUS_OPEN)
420       .setResolution(null)
421       .setRuleKey(XOO_X1);
422
423     assertThat(underTest.doManualTransition(issue, DefaultTransitions.RESET_AS_TO_REVIEW, IssueChangeContext.createScan(new Date()))).isFalse();
424   }
425
426   private Collection<String> keys(List<Transition> transitions) {
427     return transitions.stream().map(Transition::key).collect(MoreCollectors.toList());
428   }
429 }