You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ComponentIssuesLoaderIT.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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.ce.task.projectanalysis.issue;
  21. import com.tngtech.java.junit.dataprovider.DataProvider;
  22. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  23. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  24. import java.util.ArrayList;
  25. import java.util.Arrays;
  26. import java.util.Collections;
  27. import java.util.Date;
  28. import java.util.List;
  29. import java.util.Random;
  30. import java.util.function.Consumer;
  31. import java.util.stream.IntStream;
  32. import java.util.stream.LongStream;
  33. import javax.annotation.Nullable;
  34. import org.junit.Rule;
  35. import org.junit.Test;
  36. import org.junit.runner.RunWith;
  37. import org.sonar.api.config.Configuration;
  38. import org.sonar.api.config.internal.MapSettings;
  39. import org.sonar.api.issue.Issue;
  40. import org.sonar.api.utils.System2;
  41. import org.sonar.core.issue.DefaultIssue;
  42. import org.sonar.core.issue.DefaultIssueComment;
  43. import org.sonar.core.issue.FieldDiffs;
  44. import org.sonar.db.DbClient;
  45. import org.sonar.db.DbTester;
  46. import org.sonar.db.component.ComponentDto;
  47. import org.sonar.db.component.ComponentTesting;
  48. import org.sonar.db.issue.IssueChangeDto;
  49. import org.sonar.db.issue.IssueDto;
  50. import org.sonar.db.rule.RuleDto;
  51. import static java.util.Collections.emptyList;
  52. import static java.util.Collections.singleton;
  53. import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
  54. import static org.assertj.core.api.Assertions.assertThat;
  55. import static org.mockito.Mockito.mock;
  56. import static org.mockito.Mockito.verifyNoInteractions;
  57. import static org.mockito.Mockito.when;
  58. import static org.sonar.api.issue.Issue.STATUS_CLOSED;
  59. import static org.sonar.api.rules.RuleType.CODE_SMELL;
  60. import static org.sonar.api.utils.DateUtils.addDays;
  61. import static org.sonar.api.utils.DateUtils.parseDateTime;
  62. import static org.sonar.ce.task.projectanalysis.issue.ComponentIssuesLoader.NUMBER_STATUS_AND_BRANCH_CHANGES_TO_KEEP;
  63. @RunWith(DataProviderRunner.class)
  64. public class ComponentIssuesLoaderIT {
  65. private static final Date NOW = parseDateTime("2018-08-17T13:44:53+0000");
  66. private static final Date DATE_LIMIT_30_DAYS_BACK_MIDNIGHT = parseDateTime("2018-07-18T00:00:00+0000");
  67. @Rule
  68. public DbTester db = DbTester.create(System2.INSTANCE);
  69. private final DbClient dbClient = db.getDbClient();
  70. private final System2 system2 = mock(System2.class);
  71. private final IssueChangesToDeleteRepository issueChangesToDeleteRepository = new IssueChangesToDeleteRepository();
  72. @Test
  73. public void loadClosedIssues_returns_single_DefaultIssue_by_issue_based_on_first_row() {
  74. ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
  75. ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
  76. RuleDto rule = db.rules().insert(t -> t.setType(CODE_SMELL));
  77. Date issueDate = addDays(NOW, -10);
  78. IssueDto issue = db.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIssueCloseDate(issueDate).setType(CODE_SMELL));
  79. db.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(issueDate, 10));
  80. db.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(issueDate, 3), 20));
  81. db.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(issueDate, 1), 30));
  82. when(system2.now()).thenReturn(NOW.getTime());
  83. ComponentIssuesLoader underTest = newComponentIssuesLoader(newEmptySettings());
  84. List<DefaultIssue> defaultIssues = underTest.loadClosedIssues(file.uuid());
  85. assertThat(defaultIssues).hasSize(1);
  86. assertThat(defaultIssues.iterator().next().getLine()).isEqualTo(20);
  87. }
  88. @Test
  89. public void loadClosedIssues_returns_single_DefaultIssue_with_null_line_if_first_row_has_no_line_diff() {
  90. ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
  91. ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
  92. RuleDto rule = db.rules().insert(t -> t.setType(CODE_SMELL));
  93. Date issueDate = addDays(NOW, -10);
  94. IssueDto issue = db.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIssueCloseDate(issueDate).setType(CODE_SMELL));
  95. db.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(issueDate, 10));
  96. db.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(issueDate, 2), null));
  97. db.issues().insertFieldDiffs(issue, newToClosedDiffsWithLine(addDays(issueDate, 1), 30));
  98. when(system2.now()).thenReturn(NOW.getTime());
  99. ComponentIssuesLoader underTest = newComponentIssuesLoader(newEmptySettings());
  100. List<DefaultIssue> defaultIssues = underTest.loadClosedIssues(file.uuid());
  101. assertThat(defaultIssues).hasSize(1);
  102. assertThat(defaultIssues.iterator().next().getLine()).isNull();
  103. }
  104. @Test
  105. public void loadClosedIssues_returns_only_closed_issues_with_close_date() {
  106. ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
  107. ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
  108. RuleDto rule = db.rules().insert(t -> t.setType(CODE_SMELL));
  109. Date issueDate = addDays(NOW, -10);
  110. IssueDto closedIssue = db.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIssueCloseDate(issueDate).setType(CODE_SMELL));
  111. db.issues().insertFieldDiffs(closedIssue, newToClosedDiffsWithLine(issueDate, 10));
  112. IssueDto issueNoCloseDate = db.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED));
  113. db.issues().insertFieldDiffs(issueNoCloseDate, newToClosedDiffsWithLine(issueDate, 10));
  114. when(system2.now()).thenReturn(NOW.getTime());
  115. ComponentIssuesLoader underTest = newComponentIssuesLoader(newEmptySettings());
  116. List<DefaultIssue> defaultIssues = underTest.loadClosedIssues(file.uuid());
  117. assertThat(defaultIssues)
  118. .extracting(DefaultIssue::key)
  119. .containsOnly(closedIssue.getKey());
  120. }
  121. @Test
  122. public void loadClosedIssues_returns_only_closed_issues_which_close_date_is_from_day_30_days_ago() {
  123. ComponentIssuesLoader underTest = newComponentIssuesLoader(newEmptySettings());
  124. loadClosedIssues_returns_only_closed_issues_with_close_date_is_from_30_days_ago(underTest);
  125. }
  126. @Test
  127. public void loadClosedIssues_returns_only_closed_issues_with_close_date_is_from_30_days_ago_if_property_is_empty() {
  128. Configuration configuration = newConfiguration(null);
  129. ComponentIssuesLoader underTest = newComponentIssuesLoader(configuration);
  130. loadClosedIssues_returns_only_closed_issues_with_close_date_is_from_30_days_ago(underTest);
  131. }
  132. @Test
  133. public void loadClosedIssues_returns_only_closed_with_close_date_is_from_30_days_ago_if_property_is_less_than_0() {
  134. Configuration configuration = newConfiguration(String.valueOf(-(1 + new Random().nextInt(10))));
  135. ComponentIssuesLoader underTest = newComponentIssuesLoader(configuration);
  136. loadClosedIssues_returns_only_closed_issues_with_close_date_is_from_30_days_ago(underTest);
  137. }
  138. @Test
  139. public void loadClosedIssues_returns_only_closed_with_close_date_is_from_30_days_ago_if_property_is_30() {
  140. Configuration configuration = newConfiguration("30");
  141. ComponentIssuesLoader underTest = newComponentIssuesLoader(configuration);
  142. loadClosedIssues_returns_only_closed_issues_with_close_date_is_from_30_days_ago(underTest);
  143. }
  144. @Test
  145. @UseDataProvider("notAnIntegerPropertyValues")
  146. public void loadClosedIssues_returns_only_closed_with_close_date_is_from_30_days_ago_if_property_is_not_an_integer(String notAnInteger) {
  147. Configuration configuration = newConfiguration(notAnInteger);
  148. ComponentIssuesLoader underTest = newComponentIssuesLoader(configuration);
  149. loadClosedIssues_returns_only_closed_issues_with_close_date_is_from_30_days_ago(underTest);
  150. }
  151. @DataProvider
  152. public static Object[][] notAnIntegerPropertyValues() {
  153. return new Object[][] {
  154. {"foo"},
  155. {"1,3"},
  156. {"1.3"},
  157. {"-3.14"}
  158. };
  159. }
  160. private void loadClosedIssues_returns_only_closed_issues_with_close_date_is_from_30_days_ago(ComponentIssuesLoader underTest) {
  161. ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
  162. ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
  163. RuleDto rule = db.rules().insert(t -> t.setType(CODE_SMELL));
  164. Date[] issueDates = new Date[] {
  165. addDays(NOW, -10),
  166. addDays(NOW, -31),
  167. addDays(NOW, -30),
  168. DATE_LIMIT_30_DAYS_BACK_MIDNIGHT,
  169. addDays(NOW, -29),
  170. addDays(NOW, -60),
  171. };
  172. IssueDto[] issues = Arrays.stream(issueDates)
  173. .map(issueDate -> {
  174. IssueDto closedIssue = db.issues().insert(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setIssueCloseDate(issueDate).setType(CODE_SMELL));
  175. db.issues().insertFieldDiffs(closedIssue, newToClosedDiffsWithLine(issueDate, 10));
  176. return closedIssue;
  177. })
  178. .toArray(IssueDto[]::new);
  179. when(system2.now()).thenReturn(NOW.getTime());
  180. List<DefaultIssue> defaultIssues = underTest.loadClosedIssues(file.uuid());
  181. assertThat(defaultIssues)
  182. .extracting(DefaultIssue::key)
  183. .containsOnly(issues[0].getKey(), issues[2].getKey(), issues[3].getKey(), issues[4].getKey());
  184. }
  185. @Test
  186. public void loadClosedIssues_returns_empty_without_querying_DB_if_property_is_0() {
  187. System2 system2 = mock(System2.class);
  188. DbClient dbClient = mock(DbClient.class);
  189. Configuration configuration = newConfiguration("0");
  190. String componentUuid = randomAlphabetic(15);
  191. ComponentIssuesLoader underTest = new ComponentIssuesLoader(dbClient, null, null, configuration, system2, issueChangesToDeleteRepository);
  192. assertThat(underTest.loadClosedIssues(componentUuid)).isEmpty();
  193. verifyNoInteractions(dbClient, system2);
  194. }
  195. @Test
  196. public void loadLatestDiffChangesForReopeningOfClosedIssues_collects_issue_changes_to_delete() {
  197. IssueDto issue = db.issues().insert();
  198. for (long i = 0; i < NUMBER_STATUS_AND_BRANCH_CHANGES_TO_KEEP + 5; i++) {
  199. db.issues().insertChange(issue, diffIssueChangeModifier(i, "status"));
  200. }
  201. // should not be deleted
  202. db.issues().insertChange(issue, diffIssueChangeModifier(-1, "other"));
  203. ComponentIssuesLoader underTest = new ComponentIssuesLoader(dbClient, null, null, newConfiguration("0"), null, issueChangesToDeleteRepository);
  204. underTest.loadLatestDiffChangesForReopeningOfClosedIssues(singleton(new DefaultIssue().setKey(issue.getKey())));
  205. assertThat(issueChangesToDeleteRepository.getUuids()).containsOnly("0", "1", "2", "3", "4");
  206. }
  207. @Test
  208. public void loadLatestDiffChangesForReopeningOfClosedIssues_does_not_query_DB_if_issue_list_is_empty() {
  209. DbClient dbClient = mock(DbClient.class);
  210. ComponentIssuesLoader underTest = new ComponentIssuesLoader(dbClient, null, null, newConfiguration("0"), null, issueChangesToDeleteRepository);
  211. underTest.loadLatestDiffChangesForReopeningOfClosedIssues(emptyList());
  212. verifyNoInteractions(dbClient, system2);
  213. }
  214. @Test
  215. @UseDataProvider("statusOrResolutionFieldName")
  216. public void loadLatestDiffChangesForReopeningOfClosedIssues_add_diff_change_with_most_recent_status_or_resolution(String statusOrResolutionFieldName) {
  217. IssueDto issue = db.issues().insert();
  218. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith(statusOrResolutionFieldName, "val1")).setIssueChangeCreationDate(5));
  219. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith(statusOrResolutionFieldName, "val2")).setIssueChangeCreationDate(20));
  220. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith(statusOrResolutionFieldName, "val3")).setIssueChangeCreationDate(13));
  221. ComponentIssuesLoader underTest = new ComponentIssuesLoader(dbClient, null, null, newConfiguration("0"), null, issueChangesToDeleteRepository);
  222. DefaultIssue defaultIssue = new DefaultIssue().setKey(issue.getKey());
  223. underTest.loadLatestDiffChangesForReopeningOfClosedIssues(singleton(defaultIssue));
  224. assertThat(defaultIssue.changes())
  225. .hasSize(1);
  226. assertThat(defaultIssue.changes())
  227. .extracting(t -> t.get(statusOrResolutionFieldName))
  228. .filteredOn(t -> hasValue(t, "val2"))
  229. .hasSize(1);
  230. }
  231. @Test
  232. public void loadLatestDiffChangesForReopeningOfClosedIssues_add_single_diff_change_when_most_recent_status_and_resolution_is_the_same_diff() {
  233. IssueDto issue = db.issues().insert();
  234. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith("status", "valStatus1")).setIssueChangeCreationDate(5));
  235. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith("status", "valStatus2")).setIssueChangeCreationDate(19));
  236. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith("status", "valStatus3", "resolution", "valRes3")).setIssueChangeCreationDate(20));
  237. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith("resolution", "valRes4")).setIssueChangeCreationDate(13));
  238. ComponentIssuesLoader underTest = new ComponentIssuesLoader(dbClient, null, null, newConfiguration("0"), null, issueChangesToDeleteRepository);
  239. DefaultIssue defaultIssue = new DefaultIssue().setKey(issue.getKey());
  240. underTest.loadLatestDiffChangesForReopeningOfClosedIssues(singleton(defaultIssue));
  241. assertThat(defaultIssue.changes())
  242. .hasSize(1);
  243. assertThat(defaultIssue.changes())
  244. .extracting(t -> t.get("status"))
  245. .filteredOn(t -> hasValue(t, "valStatus3"))
  246. .hasSize(1);
  247. assertThat(defaultIssue.changes())
  248. .extracting(t -> t.get("resolution"))
  249. .filteredOn(t -> hasValue(t, "valRes3"))
  250. .hasSize(1);
  251. }
  252. @Test
  253. public void loadLatestDiffChangesForReopeningOfClosedIssues_adds_2_diff_changes_if_most_recent_status_and_resolution_are_not_the_same_diff() {
  254. IssueDto issue = db.issues().insert();
  255. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith("status", "valStatus1")).setIssueChangeCreationDate(5));
  256. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith("status", "valStatus2", "resolution", "valRes2")).setIssueChangeCreationDate(19));
  257. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith("status", "valStatus3")).setIssueChangeCreationDate(20));
  258. db.issues().insertChange(issue, t -> t.setChangeData(randomDiffWith("resolution", "valRes4")).setIssueChangeCreationDate(13));
  259. ComponentIssuesLoader underTest = new ComponentIssuesLoader(dbClient, null /* not used in method */, null /* not used in method */,
  260. newConfiguration("0"), null /* not used by method */, issueChangesToDeleteRepository);
  261. DefaultIssue defaultIssue = new DefaultIssue().setKey(issue.getKey());
  262. underTest.loadLatestDiffChangesForReopeningOfClosedIssues(singleton(defaultIssue));
  263. assertThat(defaultIssue.changes())
  264. .hasSize(2);
  265. assertThat(defaultIssue.changes())
  266. .extracting(t -> t.get("status"))
  267. .filteredOn(t -> hasValue(t, "valStatus3"))
  268. .hasSize(1);
  269. assertThat(defaultIssue.changes())
  270. .extracting(t -> t.get("resolution"))
  271. .filteredOn(t -> hasValue(t, "valRes2"))
  272. .hasSize(1);
  273. }
  274. @Test
  275. public void loadChanges_should_filter_out_old_status_changes() {
  276. IssueDto issue = db.issues().insert();
  277. for (int i = 0; i < NUMBER_STATUS_AND_BRANCH_CHANGES_TO_KEEP + 1; i++) {
  278. db.issues().insertChange(issue, diffIssueChangeModifier(i, "status"));
  279. }
  280. // these are kept
  281. db.issues().insertChange(issue, diffIssueChangeModifier(NUMBER_STATUS_AND_BRANCH_CHANGES_TO_KEEP + 1, "other"));
  282. db.issues().insertChange(issue, t -> t
  283. .setChangeType(IssueChangeDto.TYPE_COMMENT)
  284. .setKey("comment1"));
  285. ComponentIssuesLoader underTest = new ComponentIssuesLoader(dbClient, null, null, newConfiguration("0"), null, issueChangesToDeleteRepository);
  286. DefaultIssue defaultIssue = new DefaultIssue().setKey(issue.getKey());
  287. underTest.loadChanges(db.getSession(), singleton(defaultIssue));
  288. assertThat(defaultIssue.changes())
  289. .extracting(d -> d.creationDate().getTime())
  290. .containsOnly(LongStream.rangeClosed(1, NUMBER_STATUS_AND_BRANCH_CHANGES_TO_KEEP + 1).boxed().toArray(Long[]::new));
  291. assertThat(defaultIssue.defaultIssueComments()).extracting(DefaultIssueComment::key).containsOnly("comment1");
  292. assertThat(issueChangesToDeleteRepository.getUuids()).containsOnly("0");
  293. }
  294. @Test
  295. public void loadChanges_should_filter_out_old_from_branch_changes() {
  296. IssueDto issue = db.issues().insert();
  297. for (int i = 0; i < NUMBER_STATUS_AND_BRANCH_CHANGES_TO_KEEP + 1; i++) {
  298. db.issues().insertChange(issue, diffIssueChangeModifier(i, "from_branch"));
  299. }
  300. ComponentIssuesLoader underTest = new ComponentIssuesLoader(dbClient, null, null, newConfiguration("0"), null, issueChangesToDeleteRepository);
  301. DefaultIssue defaultIssue = new DefaultIssue().setKey(issue.getKey());
  302. underTest.loadChanges(db.getSession(), singleton(defaultIssue));
  303. assertThat(defaultIssue.changes())
  304. .extracting(d -> d.creationDate().getTime())
  305. .containsOnly(LongStream.rangeClosed(1, NUMBER_STATUS_AND_BRANCH_CHANGES_TO_KEEP).boxed().toArray(Long[]::new));
  306. assertThat(issueChangesToDeleteRepository.getUuids()).containsOnly("0");
  307. }
  308. private Consumer<IssueChangeDto> diffIssueChangeModifier(long created, String field) {
  309. return issueChangeDto -> issueChangeDto
  310. .setChangeData(new FieldDiffs().setDiff(field, "A", "B").toEncodedString())
  311. .setIssueChangeCreationDate(created)
  312. .setUuid(String.valueOf(created));
  313. }
  314. private static boolean hasValue(@Nullable FieldDiffs.Diff t, String value) {
  315. if (t == null) {
  316. return false;
  317. }
  318. return (t.oldValue() == null || value.equals(t.oldValue())) && (t.newValue() == null || value.equals(t.newValue()));
  319. }
  320. @DataProvider
  321. public static Object[][] statusOrResolutionFieldName() {
  322. return new Object[][] {
  323. {"status"},
  324. {"resolution"},
  325. };
  326. }
  327. private static String randomDiffWith(String... fieldsAndValues) {
  328. Random random = new Random();
  329. List<Diff> diffs = new ArrayList<>();
  330. for (int i = 0; i < fieldsAndValues.length; i++) {
  331. int oldOrNew = random.nextInt(3);
  332. String value = fieldsAndValues[i + 1];
  333. diffs.add(new Diff(fieldsAndValues[i], oldOrNew <= 2 ? value : null, oldOrNew >= 2 ? value : null));
  334. i++;
  335. }
  336. IntStream.range(0, random.nextInt(5))
  337. .forEach(i -> diffs.add(new Diff(randomAlphabetic(10), random.nextBoolean() ? null : randomAlphabetic(11), random.nextBoolean() ? null : randomAlphabetic(12))));
  338. Collections.shuffle(diffs);
  339. FieldDiffs res = new FieldDiffs();
  340. diffs.forEach(diff -> res.setDiff(diff.field, diff.oldValue, diff.newValue));
  341. return res.toEncodedString();
  342. }
  343. private static final class Diff {
  344. private final String field;
  345. private final String oldValue;
  346. private final String newValue;
  347. private Diff(String field, @Nullable String oldValue, @Nullable String newValue) {
  348. this.field = field;
  349. this.oldValue = oldValue;
  350. this.newValue = newValue;
  351. }
  352. }
  353. private static FieldDiffs newToClosedDiffsWithLine(Date creationDate, @Nullable Integer oldLineValue) {
  354. FieldDiffs fieldDiffs = new FieldDiffs().setCreationDate(addDays(creationDate, -5))
  355. .setDiff("status", randomNonCloseStatus(), STATUS_CLOSED);
  356. if (oldLineValue != null) {
  357. fieldDiffs.setDiff("line", oldLineValue, "");
  358. }
  359. return fieldDiffs;
  360. }
  361. private static String randomNonCloseStatus() {
  362. String[] nonCloseStatuses = Issue.STATUSES.stream()
  363. .filter(t -> !STATUS_CLOSED.equals(t))
  364. .toArray(String[]::new);
  365. return nonCloseStatuses[new Random().nextInt(nonCloseStatuses.length)];
  366. }
  367. private ComponentIssuesLoader newComponentIssuesLoader(Configuration configuration) {
  368. return new ComponentIssuesLoader(dbClient, null /* not used in loadClosedIssues */, null /* not used in loadClosedIssues */,
  369. configuration, system2, issueChangesToDeleteRepository);
  370. }
  371. private static Configuration newEmptySettings() {
  372. return new MapSettings().asConfig();
  373. }
  374. private static Configuration newConfiguration(@Nullable String maxAge) {
  375. MapSettings settings = new MapSettings();
  376. settings.setProperty("sonar.issuetracking.closedissues.maxage", maxAge);
  377. return settings.asConfig();
  378. }
  379. }