import static com.google.common.base.Preconditions.checkState;
import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import static org.sonar.api.issue.Issue.STATUS_CLOSED;
/**
* Load closed issues for the specified Component, which have at least one line diff in changelog AND are
- * neither hotspots nor manual vulnerabilities.
+ * not manual vulnerabilities.
* <p>
* Closed issues do not have a line number in DB (it is unset when the issue is closed), this method
* returns {@link DefaultIssue} objects which line number is populated from the most recent diff logging
private static List<DefaultIssue> loadClosedIssues(DbSession dbSession, String componentUuid, long closeDateAfter) {
ClosedIssuesResultHandler handler = new ClosedIssuesResultHandler();
dbSession.getMapper(IssueMapper.class).scrollClosedByComponentUuid(componentUuid, closeDateAfter, handler);
- return ImmutableList.copyOf(handler.issues);
+ return unmodifiableList(handler.issues);
}
private static class ClosedIssuesResultHandler implements ResultHandler<IssueDto> {
}
@Test
- public void scrollClosedByComponentUuid_does_not_return_closed_issues_of_type_SECURITY_HOTSPOT() {
+ public void scrollClosedByComponentUuid_returns_closed_issues_of_type_SECURITY_HOTSPOT() {
RuleType ruleType = randomSupportedRuleType();
ComponentDto component = randomComponent();
IssueDto securityHotspotIssue = insertNewClosedIssue(component, RuleType.SECURITY_HOTSPOT);
- insertToClosedDiff(securityHotspotIssue);
+ IssueChangeDto hotspotChangedData = insertToClosedDiff(securityHotspotIssue);
IssueDto issue = insertNewClosedIssue(component, ruleType);
IssueChangeDto issueChange = insertToClosedDiff(issue);
assertThat(resultHandler.issues)
.extracting(IssueDto::getKey, t -> t.getClosedChangeData().get())
- .containsOnly(tuple(issue.getKey(), issueChange.getChangeData()));
+ .containsOnly(
+ tuple(issue.getKey(), issueChange.getChangeData()),
+ tuple(securityHotspotIssue.getKey(), hotspotChangedData.getChangeData()));
}
@Test
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.issue.workflow;
+
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rules.RuleType;
+import org.sonar.core.issue.DefaultIssue;
+
+enum IsHotspot implements Condition {
+ INSTANCE;
+
+ @Override
+ public boolean matches(Issue issue) {
+ return ((DefaultIssue) issue).type() == RuleType.SECURITY_HOTSPOT;
+ }
+}
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
-import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
import static org.sonar.db.rule.RuleTesting.XOO_X1;
+import static org.sonar.server.issue.workflow.IssueWorkflowTest.emptyIfNull;
@RunWith(DataProviderRunner.class)
public class IssueWorkflowForSecurityHotspotsTest {
-
private static final IssueChangeContext SOME_CHANGE_CONTEXT = IssueChangeContext.createUser(new Date(), "USER1");
- private IssueFieldsSetter updater = new IssueFieldsSetter();
-
- private IssueWorkflow underTest = new IssueWorkflow(new FunctionExecutor(updater), updater);
+ private final IssueFieldsSetter updater = new IssueFieldsSetter();
+ private final IssueWorkflow underTest = new IssueWorkflow(new FunctionExecutor(updater), updater);
@Test
@UseDataProvider("anyResolutionIncludingNone")
}
@Test
- @UseDataProvider("allStatusesLeadingToClosed")
- public void do_not_automatically_reopen_closed_issues_of_security_hotspots(String previousStatus) {
- DefaultIssue[] hotspots = Stream.of(RESOLUTION_FIXED, RESOLUTION_REMOVED)
- .map(resolution -> {
- DefaultIssue issue = newClosedHotspot(resolution);
- setStatusPreviousToClosed(issue, previousStatus);
- return issue;
- })
- .toArray(DefaultIssue[]::new);
+ public void automatically_reopen_closed_security_hotspots() {
+ DefaultIssue hotspot1 = newClosedHotspot(RESOLUTION_REMOVED);
+ setStatusPreviousToClosed(hotspot1, STATUS_REVIEWED, RESOLUTION_SAFE, RESOLUTION_REMOVED);
+
+ DefaultIssue hotspot2 = newClosedHotspot(RESOLUTION_FIXED);
+ setStatusPreviousToClosed(hotspot2, STATUS_TO_REVIEW, null, RESOLUTION_FIXED);
+
Date now = new Date();
underTest.start();
- Arrays.stream(hotspots).forEach(hotspot -> {
- underTest.doAutomaticTransition(hotspot, IssueChangeContext.createScan(now));
+ underTest.doAutomaticTransition(hotspot1, IssueChangeContext.createScan(now));
+ underTest.doAutomaticTransition(hotspot2, IssueChangeContext.createScan(now));
- assertThat(hotspot.status()).isEqualTo(STATUS_CLOSED);
- assertThat(hotspot.updateDate()).isNull();
- });
- }
+ assertThat(hotspot1.updateDate()).isNotNull();
+ assertThat(hotspot1.status()).isEqualTo(STATUS_REVIEWED);
+ assertThat(hotspot1.resolution()).isEqualTo(RESOLUTION_SAFE);
- @DataProvider
- public static Object[][] allStatusesLeadingToClosed() {
- return Stream.of(STATUS_TO_REVIEW, STATUS_REVIEWED)
- .map(t -> new Object[] {t})
- .toArray(Object[][]::new);
+ assertThat(hotspot2.updateDate()).isNotNull();
+ assertThat(hotspot2.status()).isEqualTo(STATUS_TO_REVIEW);
+ assertThat(hotspot2.resolution()).isNull();
}
@Test
return transitions.stream().map(Transition::key).collect(MoreCollectors.toList());
}
- private static void setStatusPreviousToClosed(DefaultIssue hotspot, String previousStatus) {
- addStatusChange(hotspot, new Date(), previousStatus, STATUS_CLOSED);
+ private static void setStatusPreviousToClosed(DefaultIssue hotspot, String previousStatus, @Nullable String previousResolution, @Nullable String newResolution) {
+ addStatusChange(hotspot, new Date(), previousStatus, STATUS_CLOSED, previousResolution, newResolution);
}
- private static void addStatusChange(DefaultIssue issue, Date date, String previousStatus, String newStatus) {
- issue.addChange(new FieldDiffs().setCreationDate(date).setDiff("status", previousStatus, newStatus));
+ private static void addStatusChange(DefaultIssue issue, Date date, String previousStatus, String newStatus, @Nullable String previousResolution, @Nullable String newResolution) {
+ issue.addChange(new FieldDiffs()
+ .setCreationDate(date)
+ .setDiff("status", previousStatus, newStatus)
+ .setDiff("resolution", emptyIfNull(previousResolution), emptyIfNull(newResolution)));
}
private static DefaultIssue newClosedHotspot(String resolution) {