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