@@ -20,8 +20,10 @@ | |||
package org.sonar.ce.task.projectanalysis.issue; | |||
import com.google.common.collect.ImmutableList; | |||
import java.time.temporal.ChronoUnit; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
@@ -29,6 +31,7 @@ import org.apache.ibatis.session.ResultContext; | |||
import org.apache.ibatis.session.ResultHandler; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.FieldDiffs; | |||
@@ -45,14 +48,18 @@ import static java.util.stream.Collectors.toList; | |||
import static org.sonar.api.issue.Issue.STATUS_CLOSED; | |||
public class ComponentIssuesLoader { | |||
private static final int DEFAULT_CLOSED_ISSUES_MAX_AGE = 30; | |||
private final DbClient dbClient; | |||
private final RuleRepository ruleRepository; | |||
private final ActiveRulesHolder activeRulesHolder; | |||
private final System2 system2; | |||
public ComponentIssuesLoader(DbClient dbClient, RuleRepository ruleRepository, ActiveRulesHolder activeRulesHolder) { | |||
public ComponentIssuesLoader(DbClient dbClient, RuleRepository ruleRepository, ActiveRulesHolder activeRulesHolder, System2 system2) { | |||
this.dbClient = dbClient; | |||
this.activeRulesHolder = activeRulesHolder; | |||
this.ruleRepository = ruleRepository; | |||
this.system2 = system2; | |||
} | |||
public List<DefaultIssue> loadOpenIssues(String componentUuid) { | |||
@@ -136,16 +143,24 @@ public class ComponentIssuesLoader { | |||
* 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 | |||
* the removal of the line. Closed issues which do not have such diff are not loaded. | |||
* <p> | |||
* To not depend on purge configuration of closed issues, only issues which close date is less than 30 days ago at | |||
* 00H00 are returned. | |||
*/ | |||
public List<DefaultIssue> loadClosedIssues(String componentUuid) { | |||
Date date = new Date(system2.now()); | |||
long closeDateAfter = date.toInstant() | |||
.minus(DEFAULT_CLOSED_ISSUES_MAX_AGE, ChronoUnit.DAYS) | |||
.truncatedTo(ChronoUnit.DAYS) | |||
.toEpochMilli(); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
return loadClosedIssues(componentUuid, dbSession); | |||
return loadClosedIssues(dbSession, componentUuid, closeDateAfter); | |||
} | |||
} | |||
private static List<DefaultIssue> loadClosedIssues(String componentUuid, DbSession dbSession) { | |||
private static List<DefaultIssue> loadClosedIssues(DbSession dbSession, String componentUuid, long closeDateAfter) { | |||
ClosedIssuesResultHandler handler = new ClosedIssuesResultHandler(); | |||
dbSession.getMapper(IssueMapper.class).scrollClosedByComponentUuid(componentUuid, handler); | |||
dbSession.getMapper(IssueMapper.class).scrollClosedByComponentUuid(componentUuid, closeDateAfter, handler); | |||
return ImmutableList.copyOf(handler.issues); | |||
} | |||
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.ce.task.projectanalysis.issue; | |||
import java.util.Arrays; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Random; | |||
@@ -39,16 +40,23 @@ import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.rule.RuleDefinitionDto; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.api.issue.Issue.STATUS_CLOSED; | |||
import static org.sonar.api.utils.DateUtils.addDays; | |||
import static org.sonar.api.utils.DateUtils.parseDateTime; | |||
public class ComponentIssuesLoaderTest { | |||
private static final Date NOW = parseDateTime("2018-08-17T13:44:53+0000"); | |||
private static final Date DATE_LIMIT_30_DAYS_BACK_MIDNIGHT = parseDateTime("2018-07-18T00:00:00+0000"); | |||
@Rule | |||
public DbTester dbTester = DbTester.create(System2.INSTANCE); | |||
private DbClient dbClient = dbTester.getDbClient(); | |||
private System2 system2 = mock(System2.class); | |||
private ComponentIssuesLoader underTest = new ComponentIssuesLoader(dbClient, | |||
null /* not used in loadClosedIssues */, null /* not used in loadClosedIssues */); | |||
null /* not used in loadClosedIssues */, null /* not used in loadClosedIssues */, system2); | |||
@Test | |||
public void loadClosedIssues_returns_single_DefaultIssue_by_issue_based_on_first_row() { | |||
@@ -56,11 +64,12 @@ public class ComponentIssuesLoaderTest { | |||
ComponentDto project = dbTester.components().insertPublicProject(organization); | |||
ComponentDto file = dbTester.components().insertComponent(ComponentTesting.newFileDto(project)); | |||
RuleDefinitionDto rule = dbTester.rules().insert(t -> t.setType(RuleType.CODE_SMELL)); | |||
IssueDto issue = dbTester.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIsFromHotspot(false)); | |||
Date creationDate = new Date(); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(creationDate, -5), 10)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(creationDate, 20)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(creationDate, -10), 30)); | |||
Date issueDate = addDays(NOW, -10); | |||
IssueDto issue = dbTester.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIssueCloseDate(issueDate).setIsFromHotspot(false)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(issueDate, 10)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(issueDate, 3), 20)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(issueDate, 1), 30)); | |||
when(system2.now()).thenReturn(NOW.getTime()); | |||
List<DefaultIssue> defaultIssues = underTest.loadClosedIssues(file.uuid()); | |||
@@ -74,11 +83,12 @@ public class ComponentIssuesLoaderTest { | |||
ComponentDto project = dbTester.components().insertPublicProject(organization); | |||
ComponentDto file = dbTester.components().insertComponent(ComponentTesting.newFileDto(project)); | |||
RuleDefinitionDto rule = dbTester.rules().insert(t -> t.setType(RuleType.CODE_SMELL)); | |||
IssueDto issue = dbTester.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIsFromHotspot(false)); | |||
Date creationDate = new Date(); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(creationDate, -5), 10)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(creationDate, null)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(creationDate, -10), 30)); | |||
Date issueDate = addDays(NOW, -10); | |||
IssueDto issue = dbTester.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIssueCloseDate(issueDate).setIsFromHotspot(false)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(issueDate, 10)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(issueDate, 2), null)); | |||
dbTester.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(issueDate, 1), 30)); | |||
when(system2.now()).thenReturn(NOW.getTime()); | |||
List<DefaultIssue> defaultIssues = underTest.loadClosedIssues(file.uuid()); | |||
@@ -86,6 +96,56 @@ public class ComponentIssuesLoaderTest { | |||
assertThat(defaultIssues.iterator().next().getLine()).isNull(); | |||
} | |||
@Test | |||
public void loadClosedIssues_returns_only_closed_issues_with_close_date() { | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
ComponentDto project = dbTester.components().insertPublicProject(organization); | |||
ComponentDto file = dbTester.components().insertComponent(ComponentTesting.newFileDto(project)); | |||
RuleDefinitionDto rule = dbTester.rules().insert(t -> t.setType(RuleType.CODE_SMELL)); | |||
Date issueDate = addDays(NOW, -10); | |||
IssueDto closedIssue = dbTester.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIssueCloseDate(issueDate).setIsFromHotspot(false)); | |||
dbTester.issues().insertFieldDiffs(closedIssue, newToClosedDiffsWithLine(issueDate, 10)); | |||
IssueDto issueNoCloseDate = dbTester.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIsFromHotspot(false)); | |||
dbTester.issues().insertFieldDiffs(issueNoCloseDate, newToClosedDiffsWithLine(issueDate, 10)); | |||
when(system2.now()).thenReturn(NOW.getTime()); | |||
List<DefaultIssue> defaultIssues = underTest.loadClosedIssues(file.uuid()); | |||
assertThat(defaultIssues) | |||
.extracting(DefaultIssue::key) | |||
.containsOnly(closedIssue.getKey()); | |||
} | |||
@Test | |||
public void loadClosedIssues_returns_only_closed_issues_which_close_date_is_from_day_30_days_ago() { | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
ComponentDto project = dbTester.components().insertPublicProject(organization); | |||
ComponentDto file = dbTester.components().insertComponent(ComponentTesting.newFileDto(project)); | |||
RuleDefinitionDto rule = dbTester.rules().insert(t -> t.setType(RuleType.CODE_SMELL)); | |||
Date[] issueDates = new Date[] { | |||
addDays(NOW, -10), | |||
addDays(NOW, -31), | |||
addDays(NOW, -30), | |||
DATE_LIMIT_30_DAYS_BACK_MIDNIGHT, | |||
addDays(NOW, -29), | |||
addDays(NOW, -60), | |||
}; | |||
IssueDto[] issues = Arrays.stream(issueDates) | |||
.map(issueDate -> { | |||
IssueDto closedIssue = dbTester.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIssueCloseDate(issueDate).setIsFromHotspot(false)); | |||
dbTester.issues().insertFieldDiffs(closedIssue, newToClosedDiffsWithLine(issueDate, 10)); | |||
return closedIssue; | |||
}) | |||
.toArray(IssueDto[]::new); | |||
when(system2.now()).thenReturn(NOW.getTime()); | |||
List<DefaultIssue> defaultIssues = underTest.loadClosedIssues(file.uuid()); | |||
assertThat(defaultIssues) | |||
.extracting(DefaultIssue::key) | |||
.containsOnly(issues[0].getKey(), issues[2].getKey(), issues[3].getKey(), issues[4].getKey()); | |||
} | |||
private static FieldDiffs newToClosedDiffsWithLine(Date creationDate, @Nullable Integer oldLineValue) { | |||
FieldDiffs fieldDiffs = new FieldDiffs().setCreationDate(addDays(creationDate, -5)) | |||
.setDiff("status", randomNonCloseStatus(), STATUS_CLOSED); |
@@ -119,7 +119,7 @@ public class IntegrateIssuesVisitorTest { | |||
private ArgumentCaptor<DefaultIssue> defaultIssueCaptor; | |||
private ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule); | |||
private ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule, System2.INSTANCE); | |||
private IssueTrackingDelegator trackingDelegator; | |||
private TrackerExecution tracker; | |||
private ShortBranchTrackerExecution shortBranchTracker; | |||
@@ -142,7 +142,7 @@ public class IntegrateIssuesVisitorTest { | |||
TrackerBaseInputFactory baseInputFactory = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository); | |||
TrackerMergeBranchInputFactory mergeInputFactory = new TrackerMergeBranchInputFactory(issuesLoader, mergeBranchComponentsUuids, dbClient); | |||
ClosedIssuesInputFactory closedIssuesInputFactory = new ClosedIssuesInputFactory(issuesLoader, dbClient, movedFilesRepository); | |||
tracker = new TrackerExecution(baseInputFactory, rawInputFactory, closedIssuesInputFactory, new Tracker<>(), new ComponentIssuesLoader(dbClient, ruleRepositoryRule, activeRulesHolder)); | |||
tracker = new TrackerExecution(baseInputFactory, rawInputFactory, closedIssuesInputFactory, new Tracker<>(), issuesLoader); | |||
shortBranchTracker = new ShortBranchTrackerExecution(baseInputFactory, rawInputFactory, mergeInputFactory, new Tracker<>()); | |||
mergeBranchTracker = new MergeBranchTrackerExecution(rawInputFactory, mergeInputFactory, new Tracker<>()); | |||
@@ -32,6 +32,7 @@ import org.mockito.Mock; | |||
import org.mockito.MockitoAnnotations; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.ce.task.projectanalysis.component.ShortBranchComponentsWithIssues; | |||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; | |||
import org.sonar.core.issue.DefaultIssue; | |||
@@ -94,7 +95,7 @@ public class ShortBranchIssueMergerTest { | |||
public void setUp() { | |||
MockitoAnnotations.initMocks(this); | |||
DbClient dbClient = db.getDbClient(); | |||
copier = new ShortBranchIssueMerger(new ShortBranchIssuesLoader(new ShortBranchComponentsWithIssues(treeRootHolder, dbClient), dbClient, new ComponentIssuesLoader(dbClient, null, null)), tracker, | |||
copier = new ShortBranchIssueMerger(new ShortBranchIssuesLoader(new ShortBranchComponentsWithIssues(treeRootHolder, dbClient), dbClient, new ComponentIssuesLoader(dbClient, null, null, System2.INSTANCE)), tracker, | |||
issueLifecycle); | |||
projectDto = db.components().insertMainBranch(p -> p.setDbKey(PROJECT_KEY).setUuid(PROJECT_UUID)); | |||
branch1Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch1") |
@@ -44,7 +44,7 @@ public interface IssueMapper { | |||
void scrollNonClosedByComponentUuid(@Param("componentUuid") String componentUuid, ResultHandler<IssueDto> handler); | |||
void scrollClosedByComponentUuid(@Param("componentUuid") String componentUuid, ResultHandler<IssueDto> handler); | |||
void scrollClosedByComponentUuid(@Param("componentUuid") String componentUuid, @Param("closeDateAfter") long closeDateAfter, ResultHandler<IssueDto> handler); | |||
List<IssueDto> selectNonClosedByComponentUuidExcludingExternals(@Param("componentUuid") String componentUuid); | |||
@@ -216,7 +216,7 @@ | |||
i.issue_type <> 4 and (i.from_hotspot is NULL or i.from_hotspot = ${_false}) | |||
</select> | |||
<select id="scrollClosedByComponentUuid" parameterType="String" resultType="Issue" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY"> | |||
<select id="scrollClosedByComponentUuid" resultType="Issue" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY"> | |||
select | |||
<include refid="issueColumns"/>, | |||
ic.change_data as closedChangeData | |||
@@ -234,8 +234,10 @@ | |||
where | |||
i.component_uuid = #{componentUuid,jdbcType=VARCHAR} | |||
and i.status = 'CLOSED' | |||
and i.issue_close_date is not null | |||
and i.issue_close_date >= #{closeDateAfter,jdbcType=BIGINT} | |||
and i.issue_type <> 4 | |||
and (i.from_hotspot is NULL or i.from_hotspot = ${_false}) | |||
and (i.from_hotspot is null or i.from_hotspot = ${_false}) | |||
order by | |||
i.kee, ic.issue_change_creation_date desc | |||
</select> |
@@ -39,6 +39,7 @@ import org.sonar.api.issue.Issue; | |||
import org.sonar.api.rules.RuleType; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.api.utils.internal.AlwaysIncreasingSystem2; | |||
import org.sonar.core.issue.FieldDiffs; | |||
import org.sonar.core.util.UuidFactoryFast; | |||
import org.sonar.db.DbSession; | |||
@@ -57,6 +58,8 @@ import static org.assertj.core.api.Assertions.tuple; | |||
@RunWith(DataProviderRunner.class) | |||
public class IssueMapperTest { | |||
private static final long NO_FILTERING_ON_CLOSE_DATE = 1L; | |||
@Rule | |||
public DbTester dbTester = DbTester.create(System2.INSTANCE); | |||
@@ -67,6 +70,7 @@ public class IssueMapperTest { | |||
private ComponentDto project, file, file2; | |||
private RuleDto rule; | |||
private Random random = new Random(); | |||
private System2 system2 = new AlwaysIncreasingSystem2(); | |||
@Before | |||
public void setUp() throws Exception { | |||
@@ -241,7 +245,7 @@ public class IssueMapperTest { | |||
String componentUuid = randomAlphabetic(10); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(componentUuid, resultHandler); | |||
underTest.scrollClosedByComponentUuid(componentUuid, new Date().getTime(), resultHandler); | |||
assertThat(resultHandler.issues).isEmpty(); | |||
} | |||
@@ -255,7 +259,7 @@ public class IssueMapperTest { | |||
IssueChangeDto changeDto = insertToClosedDiff(expected); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), resultHandler); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues).hasSize(1); | |||
IssueDto issue = resultHandler.issues.iterator().next(); | |||
@@ -274,7 +278,7 @@ public class IssueMapperTest { | |||
insertToClosedDiff(issueWithoutRule); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), resultHandler); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey, t -> t.getClosedChangeData().get()) | |||
@@ -294,7 +298,7 @@ public class IssueMapperTest { | |||
insertToClosedDiff(issueMissingProject); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), resultHandler); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey, t -> t.getClosedChangeData().get()) | |||
@@ -311,7 +315,7 @@ public class IssueMapperTest { | |||
insertNewClosedIssue(component, ruleType); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), resultHandler); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey, t -> t.getClosedChangeData().get()) | |||
@@ -319,8 +323,8 @@ public class IssueMapperTest { | |||
} | |||
@Test | |||
@UseDataProvider("closedIssuesSupportedRuleTypes") | |||
public void scrollClosedByComponentUuid_does_not_return_closed_issues_of_type_SECURITY_HOTSPOT(RuleType ruleType) { | |||
public void scrollClosedByComponentUuid_does_not_return_closed_issues_of_type_SECURITY_HOTSPOT() { | |||
RuleType ruleType = randomSupportedRuleType(); | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
ComponentDto component = randomComponent(organization); | |||
IssueDto securityHotspotIssue = insertNewClosedIssue(component, RuleType.SECURITY_HOTSPOT); | |||
@@ -329,7 +333,7 @@ public class IssueMapperTest { | |||
IssueChangeDto issueChange = insertToClosedDiff(issue); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), resultHandler); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey, t -> t.getClosedChangeData().get()) | |||
@@ -337,8 +341,8 @@ public class IssueMapperTest { | |||
} | |||
@Test | |||
@UseDataProvider("closedIssuesSupportedRuleTypes") | |||
public void scrollClosedByComponentUuid_return_closed_issues_without_isHotspot_flag(RuleType ruleType) { | |||
public void scrollClosedByComponentUuid_returns_closed_issues_without_isHotspot_flag() { | |||
RuleType ruleType = randomSupportedRuleType(); | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
ComponentDto component = randomComponent(organization); | |||
IssueDto noHotspotFlagIssue = insertNewClosedIssue(component, ruleType); | |||
@@ -348,7 +352,7 @@ public class IssueMapperTest { | |||
IssueChangeDto issueChange = insertToClosedDiff(issue); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), resultHandler); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey, t -> t.getClosedChangeData().get()) | |||
@@ -357,6 +361,73 @@ public class IssueMapperTest { | |||
tuple(noHotspotFlagIssue.getKey(), noFlagIssueChange.getChangeData())); | |||
} | |||
@Test | |||
public void scrollClosedByComponentUuid_does_not_return_closed_issues_without_close_date() { | |||
RuleType ruleType = randomSupportedRuleType(); | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
ComponentDto component = randomComponent(organization); | |||
IssueDto issueWithoutCloseDate = insertNewClosedIssue(component, ruleType, t -> t.setIssueCloseDate(null)); | |||
insertToClosedDiff(issueWithoutCloseDate); | |||
IssueDto issueCloseDate = insertNewClosedIssue(component, ruleType); | |||
IssueChangeDto changeDto = insertToClosedDiff(issueCloseDate); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues).hasSize(1); | |||
IssueDto issue = resultHandler.issues.iterator().next(); | |||
assertThat(issue.getKey()).isEqualTo(issue.getKey()); | |||
assertThat(issue.getClosedChangeData()).contains(changeDto.getChangeData()); | |||
} | |||
@Test | |||
public void scrollClosedByComponentUuid_returns_closed_issues_which_close_date_is_greater_or_equal_to_requested() { | |||
RuleType ruleType = randomSupportedRuleType(); | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
ComponentDto component = randomComponent(organization); | |||
RuleDefinitionDto rule1 = dbTester.rules().insert(t -> t.setType(ruleType)); | |||
IssueDto[] issues = new IssueDto[] { | |||
insertNewClosedIssue(component, rule1, 1_999_999L), | |||
insertNewClosedIssue(component, rule1, 3_999_999L), | |||
insertNewClosedIssue(component, rule1, 2_999_999L), | |||
insertNewClosedIssue(component, rule1, 10_999_999L) | |||
}; | |||
Arrays.stream(issues).forEach(this::insertToClosedDiff); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), 4_000_000L, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey) | |||
.containsOnly(issues[3].getKey()); | |||
resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), 11_999_999L, resultHandler); | |||
assertThat(resultHandler.issues).isEmpty(); | |||
resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), 3_999_999L, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey) | |||
.containsOnly(issues[3].getKey(), issues[1].getKey()); | |||
resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), 2_999_999L, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey) | |||
.containsOnly(issues[3].getKey(), issues[1].getKey(), issues[2].getKey()); | |||
resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), 1L, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey) | |||
.containsOnly(issues[3].getKey(), issues[1].getKey(), issues[2].getKey(), issues[0].getKey()); | |||
} | |||
private void manuallySetToNullFromHotpotsColumn(IssueDto fromHostSpotIssue) { | |||
dbTester.executeUpdateSql("update issues set from_hotspot = null where kee = '" + fromHostSpotIssue.getKey() + "'"); | |||
dbTester.commit(); | |||
@@ -373,7 +444,7 @@ public class IssueMapperTest { | |||
IssueChangeDto issueChange = insertToClosedDiff(issue); | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), resultHandler); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.extracting(IssueDto::getKey, t -> t.getClosedChangeData().get()) | |||
@@ -395,7 +466,7 @@ public class IssueMapperTest { | |||
}; | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), resultHandler); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.hasSize(4) | |||
@@ -422,7 +493,7 @@ public class IssueMapperTest { | |||
}; | |||
RecorderResultHandler resultHandler = new RecorderResultHandler(); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), resultHandler); | |||
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler); | |||
assertThat(resultHandler.issues) | |||
.hasSize(3) | |||
@@ -460,32 +531,45 @@ public class IssueMapperTest { | |||
@SafeVarargs | |||
private final IssueDto insertNewClosedIssue(ComponentDto component, RuleType ruleType, Consumer<IssueDto>... consumers) { | |||
RuleDefinitionDto rule = dbTester.rules().insert(t -> t.setType(ruleType)); | |||
return insertNewClosedIssue(component, rule, consumers); | |||
return insertNewClosedIssue(component, rule, system2.now(), consumers); | |||
} | |||
@SafeVarargs | |||
private final IssueDto insertNewClosedIssue(ComponentDto component, RuleDefinitionDto rule, Consumer<IssueDto>... consumers) { | |||
return insertNewClosedIssue(component, rule, system2.now(), consumers); | |||
} | |||
@SafeVarargs | |||
private final IssueDto insertNewClosedIssue(ComponentDto component, RuleDefinitionDto rule, long issueCloseTime, Consumer<IssueDto>... consumers) { | |||
IssueDto res = new IssueDto() | |||
.setKee(UuidFactoryFast.getInstance().create()) | |||
.setRuleId(rule.getId()) | |||
.setType(rule.getType()) | |||
.setComponentUuid(component.uuid()) | |||
.setProjectUuid(component.projectUuid()) | |||
.setStatus(Issue.STATUS_CLOSED); | |||
.setStatus(Issue.STATUS_CLOSED) | |||
.setIssueCloseTime(issueCloseTime); | |||
Arrays.asList(consumers).forEach(c -> c.accept(res)); | |||
underTest.insert(res); | |||
dbSession.commit(); | |||
return res; | |||
} | |||
private static final RuleType[] SUPPORTED_RULE_TYPES = Arrays.stream(RuleType.values()) | |||
.filter(t -> t != RuleType.SECURITY_HOTSPOT) | |||
.toArray(RuleType[]::new); | |||
@DataProvider | |||
public static Object[][] closedIssuesSupportedRuleTypes() { | |||
return Arrays.stream(RuleType.values()) | |||
.filter(t -> t != RuleType.SECURITY_HOTSPOT) | |||
return Arrays.stream(SUPPORTED_RULE_TYPES) | |||
.map(t -> new Object[] {t}) | |||
.toArray(Object[][]::new); | |||
} | |||
private static RuleType randomSupportedRuleType() { | |||
return SUPPORTED_RULE_TYPES[new Random().nextInt(SUPPORTED_RULE_TYPES.length)]; | |||
} | |||
private ComponentDto randomComponent(OrganizationDto organization) { | |||
ComponentDto project = dbTester.components().insertPublicProject(organization); | |||
ComponentDto module = dbTester.components().insertComponent(ComponentTesting.newModuleDto(project)); |