3 * Copyright (C) 2009-2019 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.
21 package org.sonar.server.platform.db.migration.version.v78;
23 import java.sql.SQLException;
24 import javax.annotation.Nullable;
25 import org.apache.commons.lang.math.RandomUtils;
26 import org.assertj.core.groups.Tuple;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.sonar.api.config.internal.MapSettings;
30 import org.sonar.api.utils.internal.TestSystem2;
31 import org.sonar.core.util.UuidFactoryFast;
32 import org.sonar.db.CoreDbTester;
33 import org.sonar.server.platform.db.migration.es.MigrationEsClient;
34 import org.sonar.server.platform.db.migration.step.DataChange;
36 import static java.util.stream.Collectors.toList;
37 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
38 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
39 import static org.assertj.core.api.Assertions.assertThat;
40 import static org.assertj.core.api.Assertions.tuple;
41 import static org.mockito.Mockito.mock;
42 import static org.mockito.Mockito.verify;
44 public class UpdateSecurityHotspotsStatusesTest {
46 private static final long PAST = 5_000_000_000L;
47 private static final long NOW = 10_000_000_000L;
50 public CoreDbTester db = CoreDbTester.createForSchema(UpdateSecurityHotspotsStatusesTest.class, "schema.sql");
52 private MapSettings settings = new MapSettings();
53 private TestSystem2 system2 = new TestSystem2().setNow(NOW);
54 private MigrationEsClient esClient = mock(MigrationEsClient.class);
56 private DataChange underTest = new UpdateSecurityHotspotsStatuses(db.database(), settings.asConfig(), system2, esClient, UuidFactoryFast.getInstance());
59 public void migrate_open_and_reopen_hotspots() throws SQLException {
60 int rule = insertRule(4);
61 String issue1 = insertIssue("OPEN", null, 4, rule);
62 String issue2 = insertIssue("REOPENED", null, 4, rule);
63 // Other type of issues should not be updated
64 String issue3 = insertIssue("OPEN", null, 1, rule);
65 String issue4 = insertIssue("REOPENED", null, 2, rule);
66 String issue5 = insertIssue("OPEN", null, 3, rule);
71 tuple(issue1, "TOREVIEW", null, 4, NOW),
72 tuple(issue2, "TOREVIEW", null, 4, NOW),
74 tuple(issue3, "OPEN", null, 1, PAST),
75 tuple(issue4, "REOPENED", null, 2, PAST),
76 tuple(issue5, "OPEN", null, 3, PAST));
80 public void migrate_resolved_as_fixed_and_wont_fix_hotspots() throws SQLException {
81 int rule = insertRule(4);
82 String issue1 = insertIssue("RESOLVED", "FIXED", 4, rule);
83 String issue2 = insertIssue("RESOLVED", "WONTFIX", 4, rule);
84 // Other type of issues should not be updated
85 String issue3 = insertIssue("RESOLVED", "FIXED", 1, rule);
86 String issue4 = insertIssue("RESOLVED", "WONTFIX", 2, rule);
87 String issue5 = insertIssue("RESOLVED", "WONTFIX", 3, rule);
92 tuple(issue1, "INREVIEW", null, 4, NOW),
93 tuple(issue2, "REVIEWED", "FIXED", 4, NOW),
95 tuple(issue3, "RESOLVED", "FIXED", 1, PAST),
96 tuple(issue4, "RESOLVED", "WONTFIX", 2, PAST),
97 tuple(issue5, "RESOLVED", "WONTFIX", 3, PAST));
101 public void insert_issue_changes() throws SQLException {
102 int rule = insertRule(4);
103 String issue1 = insertIssue("REOPENED", null, 4, rule);
104 // No changelog on OPEN issue as there was no previous state
105 String issue2 = insertIssue("OPEN", null, 4, rule);
106 String issue3 = insertIssue("RESOLVED", "FIXED", 4, rule);
107 String issue4 = insertIssue("RESOLVED", "WONTFIX", 4, rule);
112 tuple(issue1, "diff", "status=REOPENED|TOREVIEW,resolution=", NOW, NOW, NOW),
113 tuple(issue3, "diff", "status=RESOLVED|INREVIEW,resolution=FIXED|", NOW, NOW, NOW),
114 tuple(issue4, "diff", "status=RESOLVED|REVIEWED,resolution=WONTFIX|FIXED", NOW, NOW, NOW));
118 public void do_not_update_vulnerabilities_coming_from_hotspot() throws SQLException {
119 int rule = insertRule(4);
120 String issue1 = insertIssue("OPEN", null, 3, rule);
124 assertIssues(tuple(issue1, "OPEN", null, 3, PAST));
125 assertNoIssueChanges();
129 public void do_not_update_closed_hotspots() throws SQLException {
130 int rule = insertRule(4);
131 String issue1 = insertIssue("CLOSED", "FIXED", 4, rule);
132 String issue2 = insertIssue("CLOSED", "REMOVED", 4, rule);
137 tuple(issue1, "CLOSED", "FIXED", 4, PAST),
138 tuple(issue2, "CLOSED", "REMOVED", 4, PAST));
139 assertNoIssueChanges();
143 public void do_nothing_on_sonarcloud() throws SQLException {
144 settings.setProperty("sonar.sonarcloud.enabled", "true");
145 int rule = insertRule(4);
146 String issue1 = insertIssue("OPEN", null, 4, rule);
150 assertIssues(tuple(issue1, "OPEN", null, 4, PAST));
151 assertNoIssueChanges();
155 public void migration_is_reentrant() throws SQLException {
156 int rule = insertRule(4);
157 String issue1 = insertIssue("OPEN", null, 4, rule);
158 String issue2 = insertIssue("REOPENED", null, 4, rule);
162 tuple(issue1, "TOREVIEW", null, 4, NOW),
163 tuple(issue2, "TOREVIEW", null, 4, NOW));
165 // Set a new date for NOW in order to check that issues has not been updated again
166 system2.setNow(NOW + 1_000_000_000L);
169 tuple(issue1, "TOREVIEW", null, 4, NOW),
170 tuple(issue2, "TOREVIEW", null, 4, NOW));
174 public void issues_index_is_removed() throws SQLException {
177 verify(esClient).deleteIndexes("issues");
180 private void assertIssues(Tuple... expectedTuples) {
181 assertThat(db.select("SELECT kee, status, resolution, issue_type, updated_at FROM issues")
183 .map(map -> new Tuple(map.get("KEE"), map.get("STATUS"), map.get("RESOLUTION"), map.get("ISSUE_TYPE"), map.get("UPDATED_AT")))
185 .containsExactlyInAnyOrder(expectedTuples);
188 private void assertNoIssueChanges() {
189 assertThat(db.countRowsOfTable("issue_changes")).isZero();
192 private void assertIssueChanges(Tuple... expectedTuples) {
193 assertThat(db.select("SELECT issue_key, change_type, change_data, created_at, updated_at, issue_change_creation_date FROM issue_changes")
195 .map(map -> new Tuple(map.get("ISSUE_KEY"), map.get("CHANGE_TYPE"), map.get("CHANGE_DATA"), map.get("CREATED_AT"), map.get("UPDATED_AT"),
196 map.get("ISSUE_CHANGE_CREATION_DATE")))
198 .containsExactlyInAnyOrder(expectedTuples);
201 private String insertIssue(String status, @Nullable String resolution, int issueType, int ruleId) {
202 String issueKey = randomAlphabetic(3);
207 "RESOLUTION", resolution,
209 "ISSUE_TYPE", issueType,
210 "COMPONENT_UUID", randomAlphanumeric(10),
211 "PROJECT_UUID", randomAlphanumeric(10),
212 "MANUAL_SEVERITY", false,
218 private int insertRule(int ruleType) {
219 int id = RandomUtils.nextInt();
220 db.executeInsert("RULES",
222 "RULE_TYPE", ruleType,
223 "IS_EXTERNAL", false,
224 "PLUGIN_RULE_KEY", randomAlphanumeric(3),
225 "PLUGIN_NAME", randomAlphanumeric(3),