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