]> source.dussan.org Git - sonarqube.git/blob
8f91b8578f0b67b387cf8b53a87126fc8867d018
[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.projectanalysis.issue;
21
22 import com.google.common.collect.Iterators;
23 import com.tngtech.java.junit.dataprovider.DataProvider;
24 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
25 import com.tngtech.java.junit.dataprovider.UseDataProvider;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Map;
29 import java.util.Optional;
30 import java.util.function.Consumer;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33 import org.junit.Before;
34 import org.junit.Rule;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 import org.sonar.api.rule.RuleKey;
38 import org.sonar.api.rule.Severity;
39 import org.sonar.api.rules.RuleType;
40 import org.sonar.api.utils.Duration;
41 import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
42 import org.sonar.ce.task.projectanalysis.component.Component;
43 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
44 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
45 import org.sonar.ce.task.projectanalysis.issue.filter.IssueFilter;
46 import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRule;
47 import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderRule;
48 import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
49 import org.sonar.core.issue.DefaultIssue;
50 import org.sonar.core.issue.tracking.Input;
51 import org.sonar.db.protobuf.DbIssues;
52 import org.sonar.scanner.protocol.Constants;
53 import org.sonar.scanner.protocol.output.ScannerReport;
54 import org.sonar.scanner.protocol.output.ScannerReport.FlowType;
55 import org.sonar.scanner.protocol.output.ScannerReport.IssueType;
56 import org.sonar.server.rule.CommonRuleKeys;
57
58 import static java.util.Arrays.asList;
59 import static java.util.Collections.emptyMap;
60 import static java.util.Collections.singletonList;
61 import static org.assertj.core.api.Assertions.assertThat;
62 import static org.mockito.ArgumentMatchers.any;
63 import static org.mockito.ArgumentMatchers.eq;
64 import static org.mockito.Mockito.mock;
65 import static org.mockito.Mockito.when;
66 import static org.sonar.api.issue.Issue.STATUS_OPEN;
67 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
68 import static org.sonar.api.issue.impact.Severity.LOW;
69 import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
70 import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY;
71 import static org.sonar.scanner.protocol.output.ScannerReport.MessageFormattingType.CODE;
72
73 @RunWith(DataProviderRunner.class)
74 public class TrackerRawInputFactoryTest {
75
76   private static final String FILE_UUID = "fake_uuid";
77   private static final String ANOTHER_FILE_UUID = "another_fake_uuid";
78   private static final String EXAMPLE_LINE_OF_CODE_FORMAT = "int example = line + of + code + %d; ";
79
80   private static final int FILE_REF = 2;
81   private static final int NOT_IN_REPORT_FILE_REF = 3;
82   private static final int ANOTHER_FILE_REF = 4;
83   private static final String TEST_CONTEXT_KEY = "test_context_key";
84
85   @Rule
86   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(PROJECT);
87   @Rule
88   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
89   @Rule
90   public ActiveRulesHolderRule activeRulesHolder = new ActiveRulesHolderRule();
91   @Rule
92   public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
93
94   private static final ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, FILE_REF).setUuid(FILE_UUID).build();
95   private static final ReportComponent ANOTHER_FILE = ReportComponent.builder(Component.Type.FILE, ANOTHER_FILE_REF).setUuid(ANOTHER_FILE_UUID).build();
96   private static final ReportComponent PROJECT = ReportComponent.builder(Component.Type.PROJECT, 1).addChildren(FILE, ANOTHER_FILE).build();
97
98   private final SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
99   private final IssueFilter issueFilter = mock(IssueFilter.class);
100   private final TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, sourceLinesHash,
101     issueFilter, ruleRepository, activeRulesHolder);
102
103   @Before
104   public void before() {
105     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
106     when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
107   }
108
109   @Test
110   public void load_source_hash_sequences() {
111     Input<DefaultIssue> input = underTest.create(FILE);
112
113     assertThat(input.getLineHashSequence()).isNotNull();
114     assertThat(input.getLineHashSequence().getHashForLine(1)).isEqualTo("line");
115     assertThat(input.getLineHashSequence().getHashForLine(2)).isEmpty();
116     assertThat(input.getLineHashSequence().getHashForLine(3)).isEmpty();
117
118     assertThat(input.getBlockHashSequence()).isNotNull();
119   }
120
121   @Test
122   public void load_source_hash_sequences_only_on_files() {
123     Input<DefaultIssue> input = underTest.create(PROJECT);
124
125     assertThat(input.getLineHashSequence()).isNotNull();
126     assertThat(input.getBlockHashSequence()).isNotNull();
127   }
128
129   @Test
130   public void load_issues_from_report() {
131     RuleKey ruleKey = RuleKey.of("java", "S001");
132     markRuleAsActive(ruleKey);
133     registerRule(ruleKey, "name", r -> r.addDefaultImpact(MAINTAINABILITY, LOW));
134     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
135       .setTextRange(newTextRange(2))
136       .setMsg("the message")
137       .addMsgFormatting(ScannerReport.MessageFormatting.newBuilder().setStart(0).setEnd(3).setType(CODE).build())
138       .addOverridenImpacts(ScannerReport.Impact.newBuilder()
139         .setSoftwareQuality(MAINTAINABILITY.name())
140         .setSeverity(org.sonar.api.issue.impact.Severity.HIGH.name())
141         .build())
142       .setRuleRepository(ruleKey.repository())
143       .setRuleKey(ruleKey.rule())
144       .setSeverity(Constants.Severity.BLOCKER)
145       .setGap(3.14)
146       .setQuickFixAvailable(true)
147       .build();
148     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
149     Input<DefaultIssue> input = underTest.create(FILE);
150
151     Collection<DefaultIssue> issues = input.getIssues();
152     assertThat(issues).hasSize(1);
153     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
154
155     // fields set by analysis report
156     assertThat(issue.ruleKey()).isEqualTo(ruleKey);
157     assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
158     assertThat(issue.line()).isEqualTo(2);
159     assertThat(issue.gap()).isEqualTo(3.14);
160     assertThat(issue.message()).isEqualTo("the message");
161     assertThat(issue.isQuickFixAvailable()).isTrue();
162
163     // Check message formatting
164     DbIssues.MessageFormattings messageFormattings = Iterators.getOnlyElement(issues.iterator()).getMessageFormattings();
165     assertThat(messageFormattings.getMessageFormattingCount()).isEqualTo(1);
166     assertThat(messageFormattings.getMessageFormatting(0).getStart()).isZero();
167     assertThat(messageFormattings.getMessageFormatting(0).getEnd()).isEqualTo(3);
168     assertThat(messageFormattings.getMessageFormatting(0).getType()).isEqualTo(DbIssues.MessageFormattingType.CODE);
169
170     // fields set by compute engine
171     assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
172     assertThat(issue.tags()).isEmpty();
173     assertInitializedIssue(issue);
174     assertThat(issue.effort()).isNull();
175     assertThat(issue.getRuleDescriptionContextKey()).isEmpty();
176     assertThat(issue.impacts()).containsExactlyEntriesOf(Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
177   }
178
179   @Test
180   public void load_issues_from_report_with_locations() {
181     RuleKey ruleKey = RuleKey.of("java", "S001");
182     markRuleAsActive(ruleKey);
183     registerRule(ruleKey, "name");
184
185     ScannerReport.MessageFormatting messageFormatting = ScannerReport.MessageFormatting.newBuilder().setStart(0).setEnd(4).setType(CODE).build();
186     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
187       .setMsg("the message")
188       .setRuleRepository(ruleKey.repository())
189       .setRuleKey(ruleKey.rule())
190       .addFlow(ScannerReport.Flow.newBuilder()
191         .setType(FlowType.DATA)
192         .setDescription("flow1")
193         .addLocation(ScannerReport.IssueLocation.newBuilder().setMsg("loc1").addMsgFormatting(messageFormatting).setComponentRef(1).build())
194         .addLocation(ScannerReport.IssueLocation.newBuilder().setMsg("loc2").setComponentRef(1).build()))
195       .addFlow(ScannerReport.Flow.newBuilder()
196         .setType(FlowType.EXECUTION)
197         .addLocation(ScannerReport.IssueLocation.newBuilder().setTextRange(newTextRange(2)).setComponentRef(1).build()))
198       .addFlow(ScannerReport.Flow.newBuilder()
199         .addLocation(ScannerReport.IssueLocation.newBuilder().setTextRange(newTextRange(2)).setComponentRef(1).build()))
200       .build();
201     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
202     Input<DefaultIssue> input = underTest.create(FILE);
203
204     Collection<DefaultIssue> issues = input.getIssues();
205     DbIssues.Locations locations = Iterators.getOnlyElement(issues.iterator()).getLocations();
206     assertThat(locations.getFlowCount()).isEqualTo(3);
207     assertThat(locations.getFlow(0).getDescription()).isEqualTo("flow1");
208     assertThat(locations.getFlow(0).getType()).isEqualTo(DbIssues.FlowType.DATA);
209     assertThat(locations.getFlow(0).getLocationList()).hasSize(2);
210
211     assertThat(locations.getFlow(0).getLocation(0).getMsg()).isEqualTo("loc1");
212     assertThat(locations.getFlow(0).getLocation(0).getMsgFormattingCount()).isEqualTo(1);
213     assertThat(locations.getFlow(0).getLocation(0).getMsgFormatting(0)).extracting(m -> m.getStart(), m -> m.getEnd(), m -> m.getType())
214       .containsExactly(0, 4, DbIssues.MessageFormattingType.CODE);
215
216     assertThat(locations.getFlow(1).hasDescription()).isFalse();
217     assertThat(locations.getFlow(1).getType()).isEqualTo(DbIssues.FlowType.EXECUTION);
218     assertThat(locations.getFlow(1).getLocationList()).hasSize(1);
219
220     assertThat(locations.getFlow(2).hasDescription()).isFalse();
221     assertThat(locations.getFlow(2).hasType()).isFalse();
222     assertThat(locations.getFlow(2).getLocationList()).hasSize(1);
223   }
224
225   @Test
226   public void load_issues_from_report_with_rule_description_context_key() {
227     RuleKey ruleKey = RuleKey.of("java", "S001");
228     markRuleAsActive(ruleKey);
229     registerRule(ruleKey, "name");
230
231     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
232       .setTextRange(newTextRange(2))
233       .setMsg("the message")
234       .setRuleRepository(ruleKey.repository())
235       .setRuleKey(ruleKey.rule())
236       .setRuleDescriptionContextKey(TEST_CONTEXT_KEY)
237       .build();
238     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
239     Input<DefaultIssue> input = underTest.create(FILE);
240
241     Collection<DefaultIssue> issues = input.getIssues();
242     assertThat(issues)
243       .hasSize(1)
244       .extracting(DefaultIssue::getRuleDescriptionContextKey)
245       .containsOnly(Optional.of(TEST_CONTEXT_KEY));
246   }
247
248   @Test
249   public void create_whenImpactIsNotDefinedAtRuleLevel_shouldDiscardImpacts() {
250     RuleKey ruleKey = RuleKey.of("java", "S001");
251     markRuleAsActive(ruleKey);
252     registerRule(ruleKey, "name", r -> r.addDefaultImpact(MAINTAINABILITY, LOW));
253     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
254       .setTextRange(newTextRange(2))
255       .setMsg("the message")
256       .setRuleRepository(ruleKey.repository())
257       .setRuleKey(ruleKey.rule())
258       .addOverridenImpacts(ScannerReport.Impact.newBuilder()
259         .setSoftwareQuality(SECURITY.name())
260         .setSeverity(LOW.name()).build())
261       .build();
262     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
263
264     Input<DefaultIssue> input = underTest.create(FILE);
265
266     Collection<DefaultIssue> issues = input.getIssues();
267     assertThat(issues).hasSize(1);
268     assertThat(issues.iterator().next().impacts()).hasSize(1).containsEntry(MAINTAINABILITY, LOW);
269   }
270
271   @Test
272   public void set_rule_name_as_message_when_issue_message_from_report_is_empty() {
273     RuleKey ruleKey = RuleKey.of("java", "S001");
274     markRuleAsActive(ruleKey);
275     registerRule(ruleKey, "Rule 1");
276     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
277       .setRuleRepository(ruleKey.repository())
278       .setRuleKey(ruleKey.rule())
279       .setMsg("")
280       .build();
281     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
282     Input<DefaultIssue> input = underTest.create(FILE);
283
284     Collection<DefaultIssue> issues = input.getIssues();
285     assertThat(issues).hasSize(1);
286     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
287
288     // fields set by analysis report
289     assertThat(issue.ruleKey()).isEqualTo(ruleKey);
290
291     // fields set by compute engine
292     assertInitializedIssue(issue);
293     assertThat(issue.message()).isEqualTo("Rule 1");
294   }
295
296   // SONAR-10781
297   @Test
298   public void load_issues_from_report_missing_secondary_location_component() {
299     RuleKey ruleKey = RuleKey.of("java", "S001");
300     markRuleAsActive(ruleKey);
301     registerRule(ruleKey, "name");
302
303     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
304       .setTextRange(newTextRange(2))
305       .setMsg("the message")
306       .setRuleRepository(ruleKey.repository())
307       .setRuleKey(ruleKey.rule())
308       .setSeverity(Constants.Severity.BLOCKER)
309       .setGap(3.14)
310       .addFlow(ScannerReport.Flow.newBuilder()
311         .addLocation(ScannerReport.IssueLocation.newBuilder()
312           .setComponentRef(FILE_REF)
313           .setMsg("Secondary location in same file")
314           .setTextRange(newTextRange(2)))
315         .addLocation(ScannerReport.IssueLocation.newBuilder()
316           .setComponentRef(NOT_IN_REPORT_FILE_REF)
317           .setMsg("Secondary location in a missing file")
318           .setTextRange(newTextRange(3)))
319         .addLocation(ScannerReport.IssueLocation.newBuilder()
320           .setComponentRef(ANOTHER_FILE_REF)
321           .setMsg("Secondary location in another file")
322           .setTextRange(newTextRange(3)))
323         .build())
324       .build();
325     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
326     Input<DefaultIssue> input = underTest.create(FILE);
327
328     Collection<DefaultIssue> issues = input.getIssues();
329     assertThat(issues).hasSize(1);
330     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
331
332     DbIssues.Locations locations = issue.getLocations();
333     // fields set by analysis report
334     assertThat(locations.getFlowList()).hasSize(1);
335     assertThat(locations.getFlow(0).getLocationList()).hasSize(2);
336     // Not component id if location is in the same file
337     assertThat(locations.getFlow(0).getLocation(0).getComponentId()).isEmpty();
338     assertThat(locations.getFlow(0).getLocation(1).getComponentId()).isEqualTo(ANOTHER_FILE_UUID);
339   }
340
341   @Test
342   @UseDataProvider("ruleTypeAndStatusByIssueType")
343   public void load_external_issues_from_report(IssueType issueType, RuleType expectedRuleType, String expectedStatus) {
344     registerRule(RuleKey.of("external_eslint", "S001"), "rule", r -> r.addDefaultImpact(MAINTAINABILITY, LOW));
345     ScannerReport.ExternalIssue reportIssue = ScannerReport.ExternalIssue.newBuilder()
346       .setTextRange(newTextRange(2))
347       .setMsg("the message")
348       .addMsgFormatting(ScannerReport.MessageFormatting.newBuilder().setStart(0).setEnd(3).build())
349       .setEngineId("eslint")
350       .setRuleId("S001")
351       .setSeverity(Constants.Severity.BLOCKER)
352       .setEffort(20L)
353       .setType(issueType)
354       .addFlow(ScannerReport.Flow.newBuilder().setType(FlowType.DATA).addLocation(ScannerReport.IssueLocation.newBuilder().build()).build())
355       .addImpacts(ScannerReport.Impact.newBuilder().setSoftwareQuality(MAINTAINABILITY.name())
356         .setSeverity(org.sonar.api.issue.impact.Severity.MEDIUM.name()).build())
357       .build();
358     reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
359     Input<DefaultIssue> input = underTest.create(FILE);
360
361     Collection<DefaultIssue> issues = input.getIssues();
362     assertThat(issues).hasSize(1);
363     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
364
365     // fields set by analysis report
366     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("external_eslint", "S001"));
367     assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
368     assertThat(issue.line()).isEqualTo(2);
369     assertThat(issue.effort()).isEqualTo(Duration.create(20L));
370     assertThat(issue.message()).isEqualTo("the message");
371
372     // Check message formatting
373     DbIssues.MessageFormattings messageFormattings = Iterators.getOnlyElement(issues.iterator()).getMessageFormattings();
374     assertThat(messageFormattings.getMessageFormattingCount()).isEqualTo(1);
375     assertThat(messageFormattings.getMessageFormatting(0).getStart()).isZero();
376     assertThat(messageFormattings.getMessageFormatting(0).getEnd()).isEqualTo(3);
377     assertThat(messageFormattings.getMessageFormatting(0).getType()).isEqualTo(DbIssues.MessageFormattingType.CODE);
378
379     assertThat(issue.type()).isEqualTo(expectedRuleType);
380
381     DbIssues.Locations locations = Iterators.getOnlyElement(issues.iterator()).getLocations();
382     assertThat(locations.getFlowCount()).isEqualTo(1);
383     assertThat(locations.getFlow(0).getType()).isEqualTo(DbIssues.FlowType.DATA);
384     assertThat(locations.getFlow(0).getLocationList()).hasSize(1);
385
386     // fields set by compute engine
387     assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
388     assertThat(issue.tags()).isEmpty();
389     assertInitializedExternalIssue(issue, expectedStatus);
390
391     assertThat(issue.impacts()).containsExactlyEntriesOf(Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
392   }
393
394   @DataProvider
395   public static Object[][] ruleTypeAndStatusByIssueType() {
396     return new Object[][]{
397       {IssueType.CODE_SMELL, RuleType.CODE_SMELL, STATUS_OPEN},
398       {IssueType.BUG, RuleType.BUG, STATUS_OPEN},
399       {IssueType.VULNERABILITY, RuleType.VULNERABILITY, STATUS_OPEN},
400       {IssueType.SECURITY_HOTSPOT, RuleType.SECURITY_HOTSPOT, STATUS_TO_REVIEW}
401     };
402   }
403
404   @Test
405   @UseDataProvider("ruleTypeAndStatusByIssueType")
406   public void load_external_issues_from_report_with_default_effort(IssueType issueType, RuleType expectedRuleType, String expectedStatus) {
407     registerRule(RuleKey.of("external_eslint", "S001"), "rule");
408     ScannerReport.ExternalIssue reportIssue = ScannerReport.ExternalIssue.newBuilder()
409       .setTextRange(newTextRange(2))
410       .setMsg("the message")
411       .setEngineId("eslint")
412       .setRuleId("S001")
413       .setSeverity(Constants.Severity.BLOCKER)
414       .setType(issueType)
415       .build();
416     reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
417     Input<DefaultIssue> input = underTest.create(FILE);
418
419     Collection<DefaultIssue> issues = input.getIssues();
420     assertThat(issues).hasSize(1);
421     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
422
423     // fields set by analysis report
424     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("external_eslint", "S001"));
425     assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
426     assertThat(issue.line()).isEqualTo(2);
427     assertThat(issue.effort()).isEqualTo(Duration.create(0L));
428     assertThat(issue.message()).isEqualTo("the message");
429     assertThat(issue.type()).isEqualTo(expectedRuleType);
430
431     // fields set by compute engine
432     assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
433     assertThat(issue.tags()).isEmpty();
434     assertInitializedExternalIssue(issue, expectedStatus);
435   }
436
437   @Test
438   public void create_whenSeverityAndTypeNotProvided_shouldTakeFromTheRule() {
439     registerRule(RuleKey.of("external_eslint", "S001"), "rule", r -> {
440       r.setType(RuleType.BUG);
441       r.setSeverity(Severity.MAJOR);
442     });
443     ScannerReport.ExternalIssue reportIssue = createIssue(null, null);
444     reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
445     Input<DefaultIssue> input = underTest.create(FILE);
446
447     Collection<DefaultIssue> issues = input.getIssues();
448     assertThat(issues).hasSize(1);
449     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
450
451     assertThat(issue.type()).isEqualTo(RuleType.BUG);
452     assertThat(issue.severity()).isEqualTo(Severity.MAJOR);
453   }
454
455   @Test
456   public void create_whenSeverityAndTypeNotProvidedByIssueAndRule_shouldTakeFromTheRuleImpact() {
457     registerRule(RuleKey.of("external_eslint", "S001"), "rule",
458       r -> r.addDefaultImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
459     ScannerReport.ExternalIssue reportIssue = createIssue(null, null);
460     reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
461     Input<DefaultIssue> input = underTest.create(FILE);
462
463     Collection<DefaultIssue> issues = input.getIssues();
464     assertThat(issues).hasSize(1);
465     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
466
467     assertThat(issue.type()).isEqualTo(RuleType.CODE_SMELL);
468     assertThat(issue.severity()).isEqualTo(Severity.MAJOR);
469   }
470
471   @NotNull
472   private ScannerReport.ExternalIssue createIssue(@Nullable RuleType ruleType, @Nullable String severity) {
473     ScannerReport.ExternalIssue.Builder builder = ScannerReport.ExternalIssue.newBuilder()
474       .setTextRange(newTextRange(2))
475       .setMsg("the message")
476       .setEngineId("eslint")
477       .setRuleId("S001");
478     if (ruleType != null) {
479       builder.setType(IssueType.valueOf(ruleType.name()));
480     }
481
482     if (severity != null) {
483       builder.setSeverity(Constants.Severity.valueOf(severity));
484     }
485
486     return builder.build();
487   }
488
489   @Test
490   public void excludes_issues_on_inactive_rules() {
491     RuleKey ruleKey = RuleKey.of("java", "S001");
492     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
493       .setTextRange(newTextRange(2))
494       .setMsg("the message")
495       .setRuleRepository(ruleKey.repository())
496       .setRuleKey(ruleKey.rule())
497       .setSeverity(Constants.Severity.BLOCKER)
498       .setGap(3.14)
499       .build();
500     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
501     Input<DefaultIssue> input = underTest.create(FILE);
502
503     Collection<DefaultIssue> issues = input.getIssues();
504     assertThat(issues).isEmpty();
505   }
506
507   @Test
508   public void filter_excludes_issues_from_report() {
509     RuleKey ruleKey = RuleKey.of("java", "S001");
510     markRuleAsActive(ruleKey);
511     registerRule(ruleKey, "name");
512     when(issueFilter.accept(any(), eq(FILE))).thenReturn(false);
513     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
514       .setTextRange(newTextRange(2))
515       .setMsg("the message")
516       .setRuleRepository(ruleKey.repository())
517       .setRuleKey(ruleKey.rule())
518       .setSeverity(Constants.Severity.BLOCKER)
519       .setGap(3.14)
520       .build();
521     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
522     Input<DefaultIssue> input = underTest.create(FILE);
523
524     Collection<DefaultIssue> issues = input.getIssues();
525     assertThat(issues).isEmpty();
526   }
527
528   @Test
529   public void exclude_issues_on_common_rules() {
530     RuleKey ruleKey = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), "S001");
531     markRuleAsActive(ruleKey);
532     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
533       .setMsg("the message")
534       .setRuleRepository(ruleKey.repository())
535       .setRuleKey(ruleKey.rule())
536       .setSeverity(Constants.Severity.BLOCKER)
537       .build();
538     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
539
540     Input<DefaultIssue> input = underTest.create(FILE);
541
542     assertThat(input.getIssues()).isEmpty();
543   }
544
545   private ScannerReport.TextRange newTextRange(int issueOnLine) {
546     return ScannerReport.TextRange.newBuilder()
547       .setStartLine(issueOnLine)
548       .setEndLine(issueOnLine)
549       .setStartOffset(0)
550       .setEndOffset(EXAMPLE_LINE_OF_CODE_FORMAT.length() - 1)
551       .build();
552   }
553
554   private void assertInitializedIssue(DefaultIssue issue) {
555     assertInitializedExternalIssue(issue, STATUS_OPEN);
556     assertThat(issue.effort()).isNull();
557     assertThat(issue.effortInMinutes()).isNull();
558   }
559
560   private void assertInitializedExternalIssue(DefaultIssue issue, String expectedStatus) {
561     assertThat(issue.projectKey()).isEqualTo(PROJECT.getKey());
562     assertThat(issue.componentKey()).isEqualTo(FILE.getKey());
563     assertThat(issue.componentUuid()).isEqualTo(FILE.getUuid());
564     assertThat(issue.resolution()).isNull();
565     assertThat(issue.status()).isEqualTo(expectedStatus);
566     assertThat(issue.key()).isNull();
567     assertThat(issue.authorLogin()).isNull();
568   }
569
570   private void markRuleAsActive(RuleKey ruleKey) {
571     activeRulesHolder.put(new ActiveRule(ruleKey, Severity.CRITICAL, emptyMap(), 1_000L, null, "qp1"));
572   }
573
574   private void registerRule(RuleKey ruleKey, String name) {
575     registerRule(ruleKey, name, r -> {});
576   }
577
578   private void registerRule(RuleKey ruleKey, String name, Consumer<DumbRule> dumbRulePopulator) {
579     DumbRule dumbRule = new DumbRule(ruleKey);
580     dumbRule.setName(name);
581     dumbRulePopulator.accept(dumbRule);
582     ruleRepository.add(dumbRule);
583   }
584 }