]> source.dussan.org Git - sonarqube.git/blob
feca4587a1aeb84e433c6432994885821ff8f79c
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 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.projectexport.rule;
21
22 import com.google.common.collect.ImmutableList;
23 import com.sonarsource.governance.projectdump.protobuf.ProjectDump;
24 import java.util.Date;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.stream.Collectors;
29 import org.apache.commons.lang.RandomStringUtils;
30 import org.junit.Before;
31 import org.junit.Rule;
32 import org.junit.Test;
33 import org.slf4j.event.Level;
34 import org.sonar.api.issue.impact.SoftwareQuality;
35 import org.sonar.api.rule.RuleKey;
36 import org.sonar.api.rule.RuleStatus;
37 import org.sonar.api.rule.Severity;
38 import org.sonar.api.rules.CleanCodeAttribute;
39 import org.sonar.api.rules.RuleType;
40 import org.sonar.api.testfixtures.log.LogTester;
41 import org.sonar.api.utils.System2;
42 import org.sonar.ce.task.projectexport.steps.DumpElement;
43 import org.sonar.ce.task.projectexport.steps.FakeDumpWriter;
44 import org.sonar.ce.task.projectexport.steps.ProjectHolder;
45 import org.sonar.ce.task.step.TestComputationStepContext;
46 import org.sonar.core.util.Uuids;
47 import org.sonar.db.DbTester;
48 import org.sonar.db.component.BranchDto;
49 import org.sonar.db.component.BranchType;
50 import org.sonar.db.component.ComponentDto;
51 import org.sonar.db.component.ProjectData;
52 import org.sonar.db.issue.ImpactDto;
53 import org.sonar.db.issue.IssueDto;
54 import org.sonar.db.project.ProjectDto;
55 import org.sonar.db.rule.RuleDto;
56
57 import static org.assertj.core.api.Assertions.assertThat;
58 import static org.assertj.core.api.Assertions.assertThatThrownBy;
59 import static org.mockito.Mockito.mock;
60 import static org.mockito.Mockito.when;
61
62 public class ExportAdHocRulesStepIT {
63   private static final String PROJECT_UUID = "some-uuid";
64   private static final List<BranchDto> BRANCHES = ImmutableList.of(
65     new BranchDto().setBranchType(BranchType.PULL_REQUEST).setProjectUuid(PROJECT_UUID).setKey("pr-1").setUuid("pr-1-uuid").setMergeBranchUuid("master").setIsMain(false),
66     new BranchDto().setBranchType(BranchType.BRANCH).setProjectUuid(PROJECT_UUID).setKey("branch-2").setUuid("branch-2-uuid").setMergeBranchUuid("master")
67       .setExcludeFromPurge(true).setIsMain(false),
68     new BranchDto().setBranchType(BranchType.BRANCH).setProjectUuid(PROJECT_UUID).setKey("branch-3").setUuid("branch-3-uuid").setMergeBranchUuid("master")
69       .setExcludeFromPurge(false).setIsMain(false));
70
71   @Rule
72   public LogTester logTester = new LogTester();
73   @Rule
74   public DbTester dbTester = DbTester.create(System2.INSTANCE);
75
76   private int issueUuidGenerator = 1;
77   private ComponentDto mainBranch;
78   private final FakeDumpWriter dumpWriter = new FakeDumpWriter();
79   private final ProjectHolder projectHolder = mock(ProjectHolder.class);
80   private final ExportAdHocRulesStep underTest = new ExportAdHocRulesStep(dbTester.getDbClient(), projectHolder, dumpWriter);
81
82   @Before
83   public void setup() {
84     logTester.setLevel(Level.DEBUG);
85     ProjectDto project = createProject();
86     when(projectHolder.projectDto()).thenReturn(project);
87   }
88
89   @Test
90   public void export_zero_ad_hoc_rules() {
91     underTest.execute(new TestComputationStepContext());
92
93     List<ProjectDump.AdHocRule> exportedRules = dumpWriter.getWrittenMessagesOf(DumpElement.AD_HOC_RULES);
94     assertThat(exportedRules).isEmpty();
95     assertThat(logTester.logs(Level.DEBUG)).contains("0 ad-hoc rules exported");
96   }
97
98   @Test
99   public void execute_only_exports_ad_hoc_rules_that_reference_project_issue() {
100     String differentProject = "diff-proj-uuid";
101     RuleDto rule1 = insertAddHocRule("rule-1");
102     RuleDto rule2 = insertAddHocRule("rule-2");
103     insertAddHocRule("rule-3");
104     insertIssue(rule1, differentProject, differentProject);
105     insertIssue(rule2, mainBranch.uuid(), mainBranch.uuid());
106
107     underTest.execute(new TestComputationStepContext());
108
109     List<ProjectDump.AdHocRule> exportedRules = dumpWriter.getWrittenMessagesOf(DumpElement.AD_HOC_RULES);
110     assertThat(exportedRules).hasSize(1);
111     assertProtobufAdHocRuleIsCorrectlyBuilt(exportedRules.iterator().next(), rule2);
112     assertThat(logTester.logs(Level.DEBUG)).contains("1 ad-hoc rules exported");
113   }
114
115   @Test
116   public void execute_only_exports_rules_that_are_ad_hoc() {
117     RuleDto rule1 = insertStandardRule("rule-1");
118     RuleDto rule2 = insertExternalRule("rule-2");
119     RuleDto rule3 = insertAddHocRule("rule-3");
120     insertIssue(rule1, mainBranch.uuid(), mainBranch.uuid());
121     insertIssue(rule2, mainBranch.uuid(), mainBranch.uuid());
122     insertIssue(rule3, mainBranch.uuid(), mainBranch.uuid());
123
124     underTest.execute(new TestComputationStepContext());
125
126     List<ProjectDump.AdHocRule> exportedRules = dumpWriter.getWrittenMessagesOf(DumpElement.AD_HOC_RULES);
127     assertThat(exportedRules).hasSize(1);
128     assertProtobufAdHocRuleIsCorrectlyBuilt(exportedRules.iterator().next(), rule3);
129     assertThat(logTester.logs(Level.DEBUG)).contains("1 ad-hoc rules exported");
130   }
131
132   @Test
133   public void execute_exports_ad_hoc_rules_that_are_referenced_by_issues_on_branches_excluded_from_purge() {
134     when(projectHolder.branches()).thenReturn(BRANCHES);
135     RuleDto rule1 = insertAddHocRule("rule-1");
136     RuleDto rule2 = insertAddHocRule("rule-2");
137     RuleDto rule3 = insertAddHocRule("rule-3");
138     insertIssue(rule1, "branch-1-uuid", "branch-1-uuid");
139     insertIssue(rule2, "branch-2-uuid", "branch-2-uuid");
140     insertIssue(rule3, "branch-3-uuid", "branch-3-uuid");
141
142     underTest.execute(new TestComputationStepContext());
143
144     List<ProjectDump.AdHocRule> exportedRules = dumpWriter.getWrittenMessagesOf(DumpElement.AD_HOC_RULES);
145     assertThat(exportedRules).hasSize(1);
146     assertProtobufAdHocRuleIsCorrectlyBuilt(exportedRules.iterator().next(), rule2);
147     assertThat(logTester.logs(Level.DEBUG)).contains("1 ad-hoc rules exported");
148   }
149
150   @Test
151   public void execute_throws_ISE_with_number_of_successful_exports_before_failure() {
152     RuleDto rule1 = insertAddHocRule("rule-1");
153     RuleDto rule2 = insertAddHocRule("rule-2");
154     RuleDto rule3 = insertAddHocRule("rule-3");
155     insertIssue(rule1, mainBranch.uuid(), mainBranch.uuid());
156     insertIssue(rule2, mainBranch.uuid(), mainBranch.uuid());
157     insertIssue(rule3, mainBranch.uuid(), mainBranch.uuid());
158     dumpWriter.failIfMoreThan(2, DumpElement.AD_HOC_RULES);
159
160     assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
161       .isInstanceOf(IllegalStateException.class)
162       .hasMessage("Ad-hoc rules export failed after processing 2 rules successfully");
163   }
164
165   @Test
166   public void execute_shouldReturnCorrectAdhocRules_whenMultipleIssuesForSameRule() {
167     RuleDto rule1 = insertAddHocRule("rule-1");
168     insertIssue(rule1, mainBranch.uuid(), mainBranch.uuid());
169     insertIssue(rule1, mainBranch.uuid(), mainBranch.uuid());
170     insertIssue(rule1, mainBranch.uuid(), mainBranch.uuid());
171
172     underTest.execute(new TestComputationStepContext());
173
174     List<ProjectDump.AdHocRule> exportedRules = dumpWriter.getWrittenMessagesOf(DumpElement.AD_HOC_RULES);
175     assertThat(exportedRules).hasSize(1);
176     assertProtobufAdHocRuleIsCorrectlyBuilt(exportedRules.iterator().next(), rule1);
177   }
178
179   @Test
180   public void getDescription() {
181     assertThat(underTest.getDescription()).isEqualTo("Export ad-hoc rules");
182   }
183
184   private ProjectDto createProject() {
185     Date createdAt = new Date();
186     ProjectData projectData = dbTester.components().insertPublicProject(PROJECT_UUID);
187     mainBranch = projectData.getMainBranchComponent();
188     BRANCHES.forEach(branch -> dbTester.components().insertProjectBranch(projectData.getProjectDto(), branch).setCreatedAt(createdAt));
189     dbTester.commit();
190     return projectData.getProjectDto();
191   }
192
193   private void insertIssue(RuleDto ruleDto, String branchUuid, String componentUuid) {
194     IssueDto dto = createBaseIssueDto(ruleDto, branchUuid, componentUuid);
195     insertIssue(dto);
196   }
197
198   private void insertIssue(IssueDto dto) {
199     dbTester.getDbClient().issueDao().insert(dbTester.getSession(), dto);
200     dbTester.commit();
201   }
202
203   private IssueDto createBaseIssueDto(RuleDto ruleDto, String branchUuid, String componentUuid) {
204     return new IssueDto()
205       .setKee("issue_uuid_" + issueUuidGenerator++)
206       .setComponentUuid(componentUuid)
207       .setProjectUuid(branchUuid)
208       .setRuleUuid(ruleDto.getUuid())
209       .setStatus("OPEN");
210   }
211
212   private RuleDto insertExternalRule(String ruleName) {
213     RuleDto ruleDto = new RuleDto()
214       .setIsExternal(true)
215       .setIsAdHoc(false);
216     return insertRule(ruleName, ruleDto);
217   }
218
219   private RuleDto insertAddHocRule(String ruleName) {
220     RuleDto ruleDto = new RuleDto()
221       .setIsExternal(false)
222       .setIsAdHoc(true)
223       .setCleanCodeAttribute(CleanCodeAttribute.CONVENTIONAL)
224       .addDefaultImpact(new ImpactDto().setUuid(Uuids.createFast()).setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(org.sonar.api.issue.impact.Severity.MEDIUM))
225       .addDefaultImpact(new ImpactDto().setUuid(Uuids.createFast()).setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(org.sonar.api.issue.impact.Severity.HIGH))
226       .setAdHocName("ad_hoc_rule" + RandomStringUtils.randomAlphabetic(10))
227       .setAdHocType(RuleType.VULNERABILITY)
228       .setAdHocSeverity(Severity.CRITICAL)
229       .setAdHocDescription("ad hoc description: " + RandomStringUtils.randomAlphanumeric(100));
230     return insertRule(ruleName, ruleDto);
231   }
232
233   private RuleDto insertStandardRule(String ruleName) {
234     RuleDto ruleDto = new RuleDto()
235       .setIsExternal(false)
236       .setIsAdHoc(false);
237     return insertRule(ruleName, ruleDto);
238   }
239
240   private RuleDto insertRule(String ruleName, RuleDto partiallyInitRuleDto) {
241     RuleKey ruleKey = RuleKey.of("plugin1", ruleName);
242     partiallyInitRuleDto
243       .setName("ruleName" + RandomStringUtils.randomAlphanumeric(10))
244       .setRuleKey(ruleKey)
245       .setPluginKey("pluginKey" + RandomStringUtils.randomAlphanumeric(10))
246       .setStatus(RuleStatus.READY)
247       .setScope(RuleDto.Scope.ALL);
248
249     dbTester.rules().insert(partiallyInitRuleDto);
250     dbTester.commit();
251     return dbTester.getDbClient().ruleDao().selectByKey(dbTester.getSession(), ruleKey)
252       .orElseThrow(() -> new RuntimeException("insertAdHocRule failed"));
253   }
254
255   private static void assertProtobufAdHocRuleIsCorrectlyBuilt(ProjectDump.AdHocRule protobufAdHocRule, RuleDto source) {
256     assertThat(protobufAdHocRule.getName()).isEqualTo(source.getName());
257     assertThat(protobufAdHocRule.getRef()).isEqualTo(source.getUuid());
258     assertThat(protobufAdHocRule.getPluginKey()).isEqualTo(source.getPluginKey());
259     assertThat(protobufAdHocRule.getPluginRuleKey()).isEqualTo(source.getRuleKey());
260     assertThat(protobufAdHocRule.getPluginName()).isEqualTo(source.getRepositoryKey());
261     assertThat(protobufAdHocRule.getName()).isEqualTo(source.getName());
262     assertThat(protobufAdHocRule.getStatus()).isEqualTo(source.getStatus().name());
263     assertThat(protobufAdHocRule.getType()).isEqualTo(source.getType());
264     assertThat(protobufAdHocRule.getScope()).isEqualTo(source.getScope().name());
265     assertThat(protobufAdHocRule.getCleanCodeAttribute()).isEqualTo(source.getCleanCodeAttribute().name());
266     assertThat(toImpactMap(protobufAdHocRule.getImpactsList())).isEqualTo(toImpactMap(source.getDefaultImpacts()));
267     assertProtobufAdHocRuleIsCorrectlyBuilt(protobufAdHocRule.getMetadata(), source);
268   }
269
270   private static Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> toImpactMap(Set<ImpactDto> defaultImpacts) {
271     return defaultImpacts
272       .stream().collect(Collectors.toMap(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity));
273   }
274
275   private static Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> toImpactMap(List<ProjectDump.Impact> impactsList) {
276     return impactsList.stream()
277       .collect(Collectors.toMap(i -> SoftwareQuality.valueOf(i.getSoftwareQuality().name()),
278         i -> org.sonar.api.issue.impact.Severity.valueOf(i.getSeverity().name())));
279   }
280
281
282   private static void assertProtobufAdHocRuleIsCorrectlyBuilt(ProjectDump.AdHocRule.RuleMetadata metadata, RuleDto expected) {
283     assertThat(metadata.getAdHocName()).isEqualTo(expected.getAdHocName());
284     assertThat(metadata.getAdHocDescription()).isEqualTo(expected.getAdHocDescription());
285     assertThat(metadata.getAdHocSeverity()).isEqualTo(expected.getAdHocSeverity());
286     assertThat(metadata.getAdHocType()).isEqualTo(expected.getAdHocType());
287   }
288
289 }