3 * Copyright (C) 2009-2020 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.issue.workflow;
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.rule.RuleKey;
35 import org.sonar.api.rules.RuleType;
36 import org.sonar.core.issue.DefaultIssue;
37 import org.sonar.core.issue.FieldDiffs;
38 import org.sonar.core.issue.IssueChangeContext;
39 import org.sonar.core.util.stream.MoreCollectors;
40 import org.sonar.server.issue.IssueFieldsSetter;
42 import static org.assertj.core.api.Assertions.assertThat;
43 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
44 import static org.sonar.api.issue.Issue.RESOLUTION_REMOVED;
45 import static org.sonar.api.issue.Issue.STATUS_CLOSED;
46 import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
47 import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
48 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
49 import static org.sonar.db.rule.RuleTesting.XOO_X1;
51 @RunWith(DataProviderRunner.class)
52 public class IssueWorkflowForSecurityHotspotsTest {
54 private static final String[] ALL_STATUSES_LEADING_TO_CLOSED = new String[] {STATUS_TO_REVIEW, STATUS_RESOLVED};
56 private static final String[] SUPPORTED_RESOLUTIONS_FOR_UNCLOSING = new String[] {RESOLUTION_FIXED, RESOLUTION_REMOVED};
58 private IssueFieldsSetter updater = new IssueFieldsSetter();
60 private IssueWorkflow underTest = new IssueWorkflow(new FunctionExecutor(updater), updater);
63 public static Object[][] allStatusesLeadingToClosed() {
64 return Arrays.stream(ALL_STATUSES_LEADING_TO_CLOSED)
65 .map(t -> new Object[] {t})
66 .toArray(Object[][]::new);
69 private static DefaultIssue newClosedIssue(String resolution) {
70 return new DefaultIssue()
72 .setRuleKey(RuleKey.of("js", "S001"))
73 .setResolution(resolution)
74 .setStatus(STATUS_CLOSED)
76 .setCloseDate(new Date(5_999_999L));
79 private static void setStatusPreviousToClosed(DefaultIssue issue, String previousStatus) {
80 addStatusChange(issue, new Date(), previousStatus, STATUS_CLOSED);
83 private static void addStatusChange(DefaultIssue issue, Date date, String previousStatus, String newStatus) {
84 issue.addChange(new FieldDiffs().setCreationDate(date).setDiff("status", previousStatus, newStatus));
88 public void list_out_transitions_in_status_to_review() {
90 DefaultIssue issue = new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(STATUS_TO_REVIEW);
92 List<Transition> transitions = underTest.outTransitions(issue);
94 assertThat(keys(transitions)).containsExactlyInAnyOrder("resolveasreviewed");
98 public void list_out_transitions_in_status_reviewed() {
100 DefaultIssue issue = new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(STATUS_REVIEWED);
102 List<Transition> transitions = underTest.outTransitions(issue);
104 assertThat(keys(transitions)).containsExactlyInAnyOrder("resetastoreview");
108 public void resolve_as_reviewed_from_to_review() {
110 DefaultIssue issue = new DefaultIssue()
111 .setType(RuleType.SECURITY_HOTSPOT)
112 .setStatus(STATUS_TO_REVIEW);
114 boolean result = underTest.doManualTransition(issue, DefaultTransitions.RESOLVE_AS_REVIEWED, IssueChangeContext.createUser(new Date(), "USER1"));
116 assertThat(result).isTrue();
117 assertThat(issue.getStatus()).isEqualTo(STATUS_REVIEWED);
118 assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
122 public void reset_as_to_review_from_reviewed() {
124 DefaultIssue issue = new DefaultIssue()
125 .setType(RuleType.SECURITY_HOTSPOT)
126 .setStatus(STATUS_REVIEWED)
127 .setResolution(RESOLUTION_FIXED);
129 boolean result = underTest.doManualTransition(issue, DefaultTransitions.RESET_AS_TO_REVIEW, IssueChangeContext.createUser(new Date(), "USER1"));
130 assertThat(result).isTrue();
131 assertThat(issue.type()).isEqualTo(RuleType.SECURITY_HOTSPOT);
132 assertThat(issue.getStatus()).isEqualTo(STATUS_TO_REVIEW);
133 assertThat(issue.resolution()).isNull();
137 public void automatically_close_resolved_security_hotspots_in_status_to_review() {
139 DefaultIssue issue = new DefaultIssue()
140 .setType(RuleType.SECURITY_HOTSPOT)
142 .setStatus(STATUS_TO_REVIEW)
144 .setBeingClosed(true);
145 Date now = new Date();
147 underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
149 assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
150 assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
151 assertThat(issue.closeDate()).isNotNull();
152 assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
156 public void automatically_close_resolved_security_hotspots_in_status_reviewed() {
158 DefaultIssue issue = new DefaultIssue()
159 .setType(RuleType.SECURITY_HOTSPOT)
160 .setResolution(RESOLUTION_FIXED)
161 .setStatus(STATUS_REVIEWED)
163 .setBeingClosed(true);
164 Date now = new Date();
166 underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
168 assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
169 assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
170 assertThat(issue.closeDate()).isNotNull();
171 assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
175 @UseDataProvider("allStatusesLeadingToClosed")
176 public void do_not_automatically_reopen_closed_issues_of_security_hotspots(String previousStatus) {
177 DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING)
179 DefaultIssue issue = newClosedIssue(resolution);
180 setStatusPreviousToClosed(issue, previousStatus);
181 issue.setType(RuleType.SECURITY_HOTSPOT);
184 .toArray(DefaultIssue[]::new);
185 Date now = new Date();
188 Arrays.stream(issues).forEach(issue -> {
189 underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
191 assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
192 assertThat(issue.updateDate()).isNull();
197 public void doAutomaticTransition_does_nothing_on_security_hotspots_in_to_review_status() {
198 DefaultIssue issue = new DefaultIssue()
202 .setStatus(STATUS_TO_REVIEW);
205 underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(new Date()));
207 assertThat(issue.status()).isEqualTo(STATUS_TO_REVIEW);
208 assertThat(issue.resolution()).isNull();
211 private Collection<String> keys(List<Transition> transitions) {
212 return transitions.stream().map(Transition::key).collect(MoreCollectors.toList());