]> source.dussan.org Git - sonarqube.git/blob
8d33cc5ddf5e56400ff29b726ed965202030bd75
[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.qualitymodel;
21
22 import javax.annotation.Nullable;
23 import org.junit.jupiter.api.Test;
24 import org.junit.jupiter.api.extension.RegisterExtension;
25 import org.sonar.api.rules.RuleType;
26 import org.sonar.ce.task.projectanalysis.component.Component;
27 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
28 import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler;
29 import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepositoryRule;
30 import org.sonar.ce.task.projectanalysis.issue.FillComponentIssuesVisitorRule;
31 import org.sonar.ce.task.projectanalysis.measure.Measure;
32 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
33 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
34 import org.sonar.core.issue.DefaultIssue;
35 import org.sonar.core.util.Uuids;
36 import org.sonar.server.measure.Rating;
37
38 import static java.util.Arrays.asList;
39 import static org.assertj.core.api.Assertions.assertThat;
40 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
41 import static org.sonar.api.issue.Issue.RESOLUTION_SAFE;
42 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED;
43 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_KEY;
44 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS;
45 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
46 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
47 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
48 import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING;
49 import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY;
50 import static org.sonar.api.rule.Severity.MAJOR;
51 import static org.sonar.api.rule.Severity.MINOR;
52 import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
53 import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
54 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
55 import static org.sonar.core.issue.DefaultIssue.STATUS_REVIEWED;
56 import static org.sonar.core.issue.DefaultIssue.STATUS_TO_REVIEW;
57 import static org.sonar.server.measure.Rating.A;
58 import static org.sonar.server.measure.Rating.B;
59 import static org.sonar.server.measure.Rating.C;
60 import static org.sonar.server.measure.Rating.D;
61 import static org.sonar.server.measure.Rating.E;
62 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING;
63 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
64
65 class SecurityReviewMeasuresVisitorTest {
66
67   private static final int PROJECT_REF = 1;
68   private static final int ROOT_DIR_REF = 12;
69   private static final int DIRECTORY_REF = 123;
70   private static final int FILE_1_REF = 1231;
71   private static final int FILE_2_REF = 1232;
72
73   private static final Component ROOT_PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project")
74     .addChildren(
75       builder(DIRECTORY, ROOT_DIR_REF).setKey("dir")
76         .addChildren(
77           builder(DIRECTORY, DIRECTORY_REF).setKey("directory")
78             .addChildren(
79               builder(FILE, FILE_1_REF).setKey("file1").build(),
80               builder(FILE, FILE_2_REF).setKey("file2").build())
81             .build())
82         .build())
83     .build();
84
85   @RegisterExtension
86   private final TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
87   @RegisterExtension
88   private final MetricRepositoryRule metricRepository = new MetricRepositoryRule()
89     .add(SECURITY_REVIEW_RATING)
90     .add(SOFTWARE_QUALITY_SECURITY_REVIEW_RATING)
91     .add(SECURITY_HOTSPOTS_REVIEWED)
92     .add(SECURITY_HOTSPOTS_REVIEWED_STATUS)
93     .add(SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
94   private final ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder);
95   @RegisterExtension
96   private final FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule =
97     new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder);
98   @RegisterExtension
99   private final MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
100
101   private final VisitorsCrawler underTest = new VisitorsCrawler(asList(fillComponentIssuesVisitorRule,
102     new SecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, metricRepository)));
103
104   @Test
105   void compute_rating_and_reviewed_measures_when_100_percent_hotspots_reviewed() {
106     treeRootHolder.setRoot(ROOT_PROJECT);
107     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
108       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
109       // Should not be taken into account
110       newIssue());
111     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
112       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
113       newHotspot(STATUS_REVIEWED, RESOLUTION_SAFE),
114       newIssue());
115
116     underTest.visit(ROOT_PROJECT);
117
118     verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
119     verifyRatingAndReviewedMeasures(FILE_2_REF, A, A, 100.0);
120     verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, A, 100.0);
121     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, A, 100.0);
122     verifyRatingAndReviewedMeasures(PROJECT_REF, A, A, 100.0);
123   }
124
125   @Test
126   void compute_rating_and_reviewed__measures_when_more_than_80_percent_hotspots_reviewed() {
127     treeRootHolder.setRoot(ROOT_PROJECT);
128     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
129       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
130       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
131       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
132       // Should not be taken into account
133       newIssue());
134     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
135       newHotspot(STATUS_TO_REVIEW, null),
136       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
137       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
138       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
139       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
140       newIssue());
141
142     underTest.visit(ROOT_PROJECT);
143
144     verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
145     verifyRatingAndReviewedMeasures(FILE_2_REF, A, B, 80.0);
146     verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, B, 87.5);
147     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, B, 87.5);
148     verifyRatingAndReviewedMeasures(PROJECT_REF, A, B, 87.5);
149   }
150
151   @Test
152   void compute_rating_and_reviewed__measures_when_more_than_70_percent_hotspots_reviewed() {
153     treeRootHolder.setRoot(ROOT_PROJECT);
154     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
155       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
156       // Should not be taken into account
157       newIssue());
158     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
159       newHotspot(STATUS_TO_REVIEW, null),
160       newHotspot(STATUS_TO_REVIEW, null),
161       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
162       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
163       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
164       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
165       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
166       newIssue());
167
168     underTest.visit(ROOT_PROJECT);
169
170     verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
171     verifyRatingAndReviewedMeasures(FILE_2_REF, B, B, 71.4);
172     verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, B, 75.0);
173     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, B, 75.0);
174     verifyRatingAndReviewedMeasures(PROJECT_REF, B, B, 75.0);
175   }
176
177   @Test
178   void compute_rating_and_reviewed__measures_when_more_than_50_percent_hotspots_reviewed() {
179     treeRootHolder.setRoot(ROOT_PROJECT);
180     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
181       newHotspot(STATUS_TO_REVIEW, null),
182       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
183       // Should not be taken into account
184       newIssue());
185     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
186       newHotspot(STATUS_TO_REVIEW, null),
187       newHotspot(STATUS_TO_REVIEW, null),
188       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
189       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
190       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
191       newIssue());
192
193     underTest.visit(ROOT_PROJECT);
194
195     verifyRatingAndReviewedMeasures(FILE_1_REF, C, C,50.0);
196     verifyRatingAndReviewedMeasures(FILE_2_REF, C, C,60.0);
197     verifyRatingAndReviewedMeasures(DIRECTORY_REF, C,C, 57.1);
198     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, C,57.1);
199     verifyRatingAndReviewedMeasures(PROJECT_REF, C, C,57.1);
200   }
201
202   @Test
203   void compute_rating_and_reviewed__measures_when_more_30_than_percent_hotspots_reviewed() {
204     treeRootHolder.setRoot(ROOT_PROJECT);
205     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
206       newHotspot(STATUS_TO_REVIEW, null),
207       newHotspot(STATUS_TO_REVIEW, null),
208       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
209       // Should not be taken into account
210       newIssue());
211     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
212       newHotspot(STATUS_TO_REVIEW, null),
213       newHotspot(STATUS_TO_REVIEW, null),
214       newHotspot(STATUS_TO_REVIEW, null),
215       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
216       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
217       newIssue());
218
219     underTest.visit(ROOT_PROJECT);
220
221     verifyRatingAndReviewedMeasures(FILE_1_REF, D, D,33.3);
222     verifyRatingAndReviewedMeasures(FILE_2_REF, D, D,40.0);
223     verifyRatingAndReviewedMeasures(DIRECTORY_REF, D,D, 37.5);
224     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, D,37.5);
225     verifyRatingAndReviewedMeasures(PROJECT_REF, D, D,37.5);
226   }
227
228   @Test
229   void compute_rating_and_reviewed__measures_when_less_than_30_percent_hotspots_reviewed() {
230     treeRootHolder.setRoot(ROOT_PROJECT);
231     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
232       newHotspot(STATUS_TO_REVIEW, null),
233       newHotspot(STATUS_TO_REVIEW, null),
234       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
235       // Should not be taken into account
236       newIssue());
237     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
238       newHotspot(STATUS_TO_REVIEW, null),
239       newHotspot(STATUS_TO_REVIEW, null),
240       newHotspot(STATUS_TO_REVIEW, null),
241       newIssue());
242
243     underTest.visit(ROOT_PROJECT);
244
245     verifyRatingAndReviewedMeasures(FILE_1_REF, D, D,33.3);
246     verifyRatingAndReviewedMeasures(FILE_2_REF, E, D,0.0);
247     verifyRatingAndReviewedMeasures(DIRECTORY_REF, E,D, 16.7);
248     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, D,16.7);
249     verifyRatingAndReviewedMeasures(PROJECT_REF, E, D,16.7);
250   }
251
252   @Test
253   void compute_A_rating_and_no_reviewed_when_no_hotspot() {
254     treeRootHolder.setRoot(ROOT_PROJECT);
255
256     underTest.visit(ROOT_PROJECT);
257
258     verifyRatingAndReviewedMeasures(PROJECT_REF, A, A,null);
259   }
260
261   @Test
262   void compute_status_related_measures() {
263     treeRootHolder.setRoot(ROOT_PROJECT);
264     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
265       newHotspot(STATUS_TO_REVIEW, null),
266       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
267       // Should not be taken into account
268       newIssue());
269     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
270       newHotspot(STATUS_TO_REVIEW, null),
271       newHotspot(STATUS_TO_REVIEW, null),
272       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
273       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
274       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
275       newIssue());
276
277     underTest.visit(ROOT_PROJECT);
278
279     verifyHotspotStatusMeasures(FILE_1_REF, 1, 1);
280     verifyHotspotStatusMeasures(FILE_2_REF, 3, 2);
281     verifyHotspotStatusMeasures(DIRECTORY_REF, 4, 3);
282     verifyHotspotStatusMeasures(ROOT_DIR_REF, 4, 3);
283     verifyHotspotStatusMeasures(PROJECT_REF, 4, 3);
284   }
285
286   @Test
287   void compute_0_status_related_measures_when_no_hotspot() {
288     treeRootHolder.setRoot(ROOT_PROJECT);
289
290     underTest.visit(ROOT_PROJECT);
291
292     verifyHotspotStatusMeasures(PROJECT_REF, 0, 0);
293   }
294
295   private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, Rating expectedSoftwareQualityReviewRating,
296     @Nullable Double expectedHotspotsReviewed) {
297     verifySecurityReviewRating(componentRef, expectedReviewRating, expectedSoftwareQualityReviewRating);
298     if (expectedHotspotsReviewed != null) {
299       verifySecurityHotspotsReviewed(componentRef, expectedHotspotsReviewed);
300     } else {
301       assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_KEY)).isEmpty();
302     }
303   }
304
305   private void verifySecurityReviewRating(int componentRef, Rating rating, Rating softwareQualityRating) {
306     Measure measure = measureRepository.getAddedRawMeasure(componentRef, SECURITY_REVIEW_RATING_KEY).get();
307     Measure softwareQualityMeasure = measureRepository.getAddedRawMeasure(componentRef, SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY).get();
308     assertThat(measure.getIntValue()).isEqualTo(rating.getIndex());
309     assertThat(measure.getData()).isEqualTo(rating.name());
310     assertThat(softwareQualityMeasure.getIntValue()).isEqualTo(softwareQualityRating.getIndex());
311     assertThat(softwareQualityMeasure.getData()).isEqualTo(softwareQualityRating.name());
312   }
313
314   private void verifySecurityHotspotsReviewed(int componentRef, double percent) {
315     assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_KEY).get().getDoubleValue()).isEqualTo(percent);
316   }
317
318   private void verifyHotspotStatusMeasures(int componentRef, @Nullable Integer hotspotsReviewed, @Nullable Integer hotspotsToReview) {
319     if (hotspotsReviewed == null){
320       assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).isEmpty();
321     } else {
322       assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsReviewed);
323     }
324     if (hotspotsReviewed == null){
325       assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).isEmpty();
326     } else {
327       assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsToReview);
328     }
329   }
330
331   private static DefaultIssue newHotspot(String status, @Nullable String resolution) {
332     return new DefaultIssue()
333       .setKey(Uuids.create())
334       .setSeverity(MINOR)
335       .setStatus(status)
336       .setResolution(resolution)
337       .setType(RuleType.SECURITY_HOTSPOT);
338   }
339
340   private static DefaultIssue newIssue() {
341     return new DefaultIssue()
342       .setKey(Uuids.create())
343       .setSeverity(MAJOR)
344       .setType(RuleType.BUG);
345   }
346
347 }