3 * Copyright (C) 2009-2022 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.
20 package org.sonar.ce.task.projectexport.rule;
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 org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.sonar.api.resources.Qualifiers;
30 import org.sonar.api.resources.Scopes;
31 import org.sonar.api.rule.RuleKey;
32 import org.sonar.api.rule.RuleStatus;
33 import org.sonar.api.utils.System2;
34 import org.sonar.api.utils.log.LogTester;
35 import org.sonar.api.utils.log.LoggerLevel;
36 import org.sonar.ce.task.projectexport.steps.DumpElement;
37 import org.sonar.ce.task.projectexport.steps.FakeDumpWriter;
38 import org.sonar.ce.task.projectexport.steps.ProjectHolder;
39 import org.sonar.ce.task.step.TestComputationStepContext;
40 import org.sonar.db.DbTester;
41 import org.sonar.db.component.BranchDto;
42 import org.sonar.db.component.BranchType;
43 import org.sonar.db.component.ComponentDto;
44 import org.sonar.db.issue.IssueDto;
45 import org.sonar.db.project.ProjectDto;
46 import org.sonar.db.rule.RuleDto;
47 import org.sonar.db.rule.RuleMetadataDto;
49 import static org.assertj.core.api.Assertions.assertThat;
50 import static org.assertj.core.api.Assertions.assertThatThrownBy;
51 import static org.assertj.core.api.Assertions.tuple;
52 import static org.mockito.Mockito.mock;
53 import static org.mockito.Mockito.when;
54 import static org.sonar.db.component.ComponentDto.UUID_PATH_OF_ROOT;
56 public class ExportAdHocRulesStepTest {
57 private static final String PROJECT_UUID = "some-uuid";
59 private static final ComponentDto PROJECT = new ComponentDto()
61 .setScope(Scopes.PROJECT)
62 .setQualifier(Qualifiers.PROJECT)
63 .setDbKey("the_project")
64 .setName("The Project")
65 .setDescription("The project description")
67 .setUuid(PROJECT_UUID)
68 .setUuidPath(UUID_PATH_OF_ROOT)
69 .setRootUuid(PROJECT_UUID)
71 .setModuleUuidPath("." + PROJECT_UUID + ".")
72 .setProjectUuid(PROJECT_UUID);
74 private static final List<BranchDto> BRANCHES = ImmutableList.of(
75 new BranchDto().setBranchType(BranchType.PULL_REQUEST).setProjectUuid(PROJECT_UUID).setKey("pr-1").setUuid("pr-1-uuid").setMergeBranchUuid("master"),
76 new BranchDto().setBranchType(BranchType.BRANCH).setProjectUuid(PROJECT_UUID).setKey("branch-2").setUuid("branch-2-uuid").setMergeBranchUuid("master")
77 .setExcludeFromPurge(true),
78 new BranchDto().setBranchType(BranchType.BRANCH).setProjectUuid(PROJECT_UUID).setKey("branch-3").setUuid("branch-3-uuid").setMergeBranchUuid("master")
79 .setExcludeFromPurge(false));
83 public LogTester logTester = new LogTester();
86 public DbTester dbTester = DbTester.create(System2.INSTANCE);
88 private int issueUuidGenerator = 1;
89 private FakeDumpWriter dumpWriter = new FakeDumpWriter();
90 private ProjectHolder projectHolder = mock(ProjectHolder.class);
91 private ExportAdHocRulesStep underTest = new ExportAdHocRulesStep(dbTester.getDbClient(), projectHolder, dumpWriter);
95 ProjectDto project = createProject();
96 when(projectHolder.projectDto()).thenReturn(project);
100 public void export_zero_ad_hoc_rules() {
101 underTest.execute(new TestComputationStepContext());
103 List<ProjectDump.AdHocRule> exportedRules = dumpWriter.getWrittenMessagesOf(DumpElement.AD_HOC_RULES);
104 assertThat(exportedRules).isEmpty();
105 assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("0 ad-hoc rules exported");
109 public void execute_only_exports_ad_hoc_rules_that_reference_project_issue() {
110 String differentProject = "diff-proj-uuid";
111 RuleDto rule1 = insertRule(RuleKey.of("plugin-1", "rule-1"), true, true);
112 RuleDto rule2 = insertRule(RuleKey.of("plugin-1", "rule-2"), true, true);
113 insertRule(RuleKey.of("plugin-1", "rule-3"), true, true);
114 insertIssue(rule1, differentProject, differentProject);
115 insertIssue(rule2, PROJECT_UUID, PROJECT_UUID);
117 underTest.execute(new TestComputationStepContext());
119 List<ProjectDump.AdHocRule> exportedRules = dumpWriter.getWrittenMessagesOf(DumpElement.AD_HOC_RULES);
120 assertThat(exportedRules).hasSize(1);
121 assertThat(exportedRules).extracting(ProjectDump.AdHocRule::getPluginName, ProjectDump.AdHocRule::getPluginRuleKey)
122 .containsOnly(tuple("plugin-1", "rule-2"));
123 assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("1 ad-hoc rules exported");
127 public void execute_only_exports_rules_that_are_ad_hoc() {
128 RuleDto rule1 = insertRule(RuleKey.of("plugin-1", "rule-1"), false, false);
129 RuleDto rule2 = insertRule(RuleKey.of("plugin-1", "rule-2"), true, false);
130 RuleDto rule3 = insertRule(RuleKey.of("plugin-1", "rule-3"), true, true);
131 insertIssue(rule1, PROJECT_UUID, PROJECT_UUID);
132 insertIssue(rule2, PROJECT_UUID, PROJECT_UUID);
133 insertIssue(rule3, PROJECT_UUID, PROJECT_UUID);
135 underTest.execute(new TestComputationStepContext());
137 List<ProjectDump.AdHocRule> exportedRules = dumpWriter.getWrittenMessagesOf(DumpElement.AD_HOC_RULES);
138 assertThat(exportedRules).hasSize(1);
139 assertThat(exportedRules).extracting(ProjectDump.AdHocRule::getPluginName, ProjectDump.AdHocRule::getPluginRuleKey)
140 .containsOnly(tuple("plugin-1", "rule-3"));
141 assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("1 ad-hoc rules exported");
145 public void execute_exports_ad_hoc_rules_that_are_referenced_by_issues_on_branches_excluded_from_purge() {
146 when(projectHolder.branches()).thenReturn(BRANCHES);
147 RuleDto rule1 = insertRule(RuleKey.of("plugin-1", "rule-1"), true, true);
148 RuleDto rule2 = insertRule(RuleKey.of("plugin-1", "rule-2"), true, true);
149 RuleDto rule3 = insertRule(RuleKey.of("plugin-1", "rule-3"), true, true);
150 insertIssue(rule1, "branch-1-uuid", "branch-1-uuid");
151 insertIssue(rule2, "branch-2-uuid", "branch-2-uuid");
152 insertIssue(rule3, "branch-3-uuid", "branch-3-uuid");
154 underTest.execute(new TestComputationStepContext());
156 List<ProjectDump.AdHocRule> exportedRules = dumpWriter.getWrittenMessagesOf(DumpElement.AD_HOC_RULES);
157 assertThat(exportedRules).hasSize(1);
158 assertThat(exportedRules).extracting(ProjectDump.AdHocRule::getPluginName, ProjectDump.AdHocRule::getPluginRuleKey)
159 .containsOnly(tuple("plugin-1", "rule-2"));
160 assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("1 ad-hoc rules exported");
164 public void execute_throws_ISE_with_number_of_successful_exports_before_failure() {
165 RuleDto rule1 = insertRule(RuleKey.of("plugin-1", "rule-1"), true, true);
166 RuleDto rule2 = insertRule(RuleKey.of("plugin-1", "rule-2"), true, true);
167 RuleDto rule3 = insertRule(RuleKey.of("plugin-1", "rule-3"), true, true);
168 insertIssue(rule1, PROJECT_UUID, PROJECT_UUID);
169 insertIssue(rule2, PROJECT_UUID, PROJECT_UUID);
170 insertIssue(rule3, PROJECT_UUID, PROJECT_UUID);
171 dumpWriter.failIfMoreThan(2, DumpElement.AD_HOC_RULES);
173 assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
174 .isInstanceOf(IllegalStateException.class)
175 .hasMessage("Ad-hoc rules export failed after processing 2 rules successfully");
179 public void getDescription() {
180 assertThat(underTest.getDescription()).isEqualTo("Export ad-hoc rules");
183 private ProjectDto createProject() {
184 Date createdAt = new Date();
185 ComponentDto projectDto = dbTester.components().insertPublicProject(PROJECT);
186 BRANCHES.forEach(branch -> dbTester.components().insertProjectBranch(projectDto, branch).setCreatedAt(createdAt));
188 return dbTester.components().getProjectDto(projectDto);
191 private void insertIssue(RuleDto ruleDto, String projectUuid, String componentUuid) {
192 IssueDto dto = createBaseIssueDto(ruleDto, projectUuid, componentUuid);
196 private void insertIssue(IssueDto dto) {
197 dbTester.getDbClient().issueDao().insert(dbTester.getSession(), dto);
201 private IssueDto createBaseIssueDto(RuleDto ruleDto, String projectUuid, String componentUuid) {
202 return new IssueDto()
203 .setKee("issue_uuid_" + issueUuidGenerator++)
204 .setComponentUuid(componentUuid)
205 .setProjectUuid(projectUuid)
206 .setRuleUuid(ruleDto.getUuid())
210 private RuleDto insertRule(RuleKey ruleKey, boolean isExternal, boolean isAdHoc) {
211 dbTester.rules().insert(
214 .setIsExternal(isExternal)
216 .setStatus(RuleStatus.READY)
217 .setScope(RuleDto.Scope.ALL));
219 return dbTester.getDbClient().ruleDao().selectByKey(dbTester.getSession(), ruleKey)
220 .orElseThrow(() -> new RuntimeException("insertAdHocRule failed"));
223 private void insertRuleMetadata(String ruleUuid, String adHocName) {
224 dbTester.rules().insertOrUpdateMetadata(new RuleMetadataDto()
225 .setRuleUuid(ruleUuid)
226 .setAdHocName(adHocName));