]> source.dussan.org Git - sonarqube.git/blob
ba4c77f28311cf1a966db5d455a198759306d03d
[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 java.util.Arrays;
23 import javax.annotation.Nullable;
24 import org.junit.Before;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.slf4j.event.Level;
28 import org.sonar.api.testfixtures.log.LogTester;
29 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
30 import org.sonar.ce.task.projectanalysis.component.Component;
31 import org.sonar.ce.task.projectanalysis.scm.Changeset;
32 import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepositoryRule;
33 import org.sonar.core.issue.DefaultIssue;
34 import org.sonar.db.protobuf.DbCommons;
35 import org.sonar.db.protobuf.DbIssues;
36 import org.sonar.db.user.UserIdDto;
37 import org.sonar.server.issue.IssueFieldsSetter;
38
39 import static java.util.stream.Collectors.joining;
40 import static java.util.stream.IntStream.range;
41 import static org.assertj.core.api.Assertions.assertThat;
42 import static org.mockito.Mockito.mock;
43 import static org.mockito.Mockito.when;
44 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
45
46 public class IssueAssignerTest {
47
48   private static final int FILE_REF = 1;
49   private static final Component FILE = builder(Component.Type.FILE, FILE_REF).setKey("FILE_KEY").setUuid("FILE_UUID").build();
50
51   @Rule
52   public LogTester logTester = new LogTester();
53
54   @Rule
55   public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule();
56
57   @Rule
58   public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule().setAnalysisDate(123456789L);
59
60   private final ScmAccountToUser scmAccountToUser = mock(ScmAccountToUser.class);
61   private final DefaultAssignee defaultAssignee = mock(DefaultAssignee.class);
62   private final IssueAssigner underTest = new IssueAssigner(analysisMetadataHolder, scmInfoRepository, scmAccountToUser, defaultAssignee, new IssueFieldsSetter());
63
64   @Before
65   public void before() {
66     logTester.setLevel(Level.DEBUG);
67   }
68
69   @Test
70   public void do_not_set_author_if_no_changeset() {
71     DefaultIssue issue = newIssueOnLines(1);
72
73     underTest.onIssue(FILE, issue);
74
75     assertThat(issue.authorLogin()).isNull();
76   }
77
78   @Test
79   public void set_author_of_new_issue_if_changeset() {
80     setSingleChangeset("john", 123456789L, "rev-1");
81     DefaultIssue issue = newIssueOnLines(1);
82
83     underTest.onIssue(FILE, issue);
84
85     assertThat(issue.authorLogin()).isEqualTo("john");
86   }
87
88   @Test
89   public void do_not_reset_author_if_already_set() {
90     setSingleChangeset("john", 123456789L, "rev-1");
91     DefaultIssue issue = newIssueOnLines(1)
92       .setAuthorLogin("jane");
93
94     underTest.onIssue(FILE, issue);
95
96     assertThat(issue.authorLogin()).isEqualTo("jane");
97   }
98
99   @Test
100   public void assign_but_do_not_set_author_if_too_long() {
101     String scmAuthor = range(0, 256).mapToObj(i -> "s").collect(joining());
102     addScmUser(scmAuthor, buildUserId("u123", "John C"));
103     setSingleChangeset(scmAuthor, 123456789L, "rev-1");
104     DefaultIssue issue = newIssueOnLines(1);
105
106     underTest.onIssue(FILE, issue);
107
108     assertThat(issue.authorLogin()).isNull();
109     assertThat(issue.assignee()).isEqualTo("u123");
110     assertThat(issue.assigneeLogin()).isEqualTo("John C");
111
112     assertThat(logTester.logs(Level.DEBUG)).contains("SCM account '" + scmAuthor + "' is too long to be stored as issue author");
113   }
114
115   @Test
116   public void assign_new_issue_to_author_of_change() {
117     addScmUser("john", buildUserId("u123", "john"));
118     setSingleChangeset("john", 123456789L, "rev-1");
119     DefaultIssue issue = newIssueOnLines(1);
120
121     underTest.onIssue(FILE, issue);
122
123     assertThat(issue.assignee()).isEqualTo("u123");
124     assertThat(issue.assigneeLogin()).isEqualTo("john");
125   }
126
127   @Test
128   public void assign_new_issue_to_default_assignee_if_author_not_found() {
129     setSingleChangeset("john", 123456789L, "rev-1");
130     when(defaultAssignee.loadDefaultAssigneeUserId()).thenReturn(new UserIdDto("u1234", "john"));
131     DefaultIssue issue = newIssueOnLines(1);
132
133     underTest.onIssue(FILE, issue);
134
135     assertThat(issue.assignee()).isEqualTo("u1234");
136     assertThat(issue.assigneeLogin()).isEqualTo("john");
137   }
138
139   @Test
140   public void do_not_assign_new_issue_if_no_author_in_changeset() {
141     setSingleChangeset(null, 123456789L, "rev-1");
142     DefaultIssue issue = newIssueOnLines(1);
143
144     underTest.onIssue(FILE, issue);
145
146     assertThat(issue.authorLogin()).isNull();
147     assertThat(issue.assignee()).isNull();
148   }
149
150   @Test
151   public void do_not_assign_issue_if_unassigned_but_already_authored() {
152     addScmUser("john", buildUserId("u1234", "john"));
153     setSingleChangeset("john", 123456789L, "rev-1");
154     DefaultIssue issue = newIssueOnLines(1)
155       .setAuthorLogin("john")
156       .setAssigneeUuid(null);
157
158     underTest.onIssue(FILE, issue);
159
160     assertThat(issue.authorLogin()).isEqualTo("john");
161     assertThat(issue.assignee()).isNull();
162   }
163
164   @Test
165   public void assign_to_last_committer_of_file_if_issue_is_global_to_file() {
166     addScmUser("henry", buildUserId("u123", "Henry V"));
167     Changeset changeset1 = Changeset.newChangesetBuilder()
168       .setAuthor("john")
169       .setDate(1_000L)
170       .setRevision("rev-1")
171       .build();
172     // Latest changeset
173     Changeset changeset2 = Changeset.newChangesetBuilder()
174       .setAuthor("henry")
175       .setDate(2_000L)
176       .setRevision("rev-2")
177       .build();
178     scmInfoRepository.setScmInfo(FILE_REF, changeset1, changeset2, changeset1);
179
180     DefaultIssue issue = newIssueOnLines();
181
182     underTest.onIssue(FILE, issue);
183
184     assertThat(issue.assignee()).isEqualTo("u123");
185     assertThat(issue.assigneeLogin()).isEqualTo("Henry V");
186   }
187
188   @Test
189   public void assign_to_default_assignee_if_no_author() {
190     DefaultIssue issue = newIssueOnLines();
191
192     when(defaultAssignee.loadDefaultAssigneeUserId()).thenReturn(new UserIdDto("u123", "john"));
193     underTest.onIssue(FILE, issue);
194
195     assertThat(issue.assignee()).isEqualTo("u123");
196     assertThat(issue.assigneeLogin()).isEqualTo("john");
197   }
198
199   @Test
200   public void assign_to_default_assignee_if_no_scm_on_issue_locations() {
201     addScmUser("john", buildUserId("u123", "John C"));
202     Changeset changeset = Changeset.newChangesetBuilder()
203       .setAuthor("john")
204       .setDate(123456789L)
205       .setRevision("rev-1")
206       .build();
207     scmInfoRepository.setScmInfo(FILE_REF, changeset, changeset);
208     DefaultIssue issue = newIssueOnLines(3);
209
210     underTest.onIssue(FILE, issue);
211
212     assertThat(issue.assignee()).isEqualTo("u123");
213     assertThat(issue.assigneeLogin()).isEqualTo("John C");
214   }
215
216   @Test
217   public void assign_to_author_of_the_most_recent_change_in_all_issue_locations() {
218     addScmUser("john", buildUserId("u1", "John"));
219     addScmUser("jane", buildUserId("u2", "Jane"));
220     Changeset commit1 = Changeset.newChangesetBuilder()
221       .setAuthor("john")
222       .setDate(1_000L)
223       .setRevision("rev-1")
224       .build();
225     Changeset commit2 = Changeset.newChangesetBuilder()
226       .setAuthor("jane")
227       .setDate(2_000L)
228       .setRevision("rev-2")
229       .build();
230     scmInfoRepository.setScmInfo(FILE_REF, commit1, commit1, commit2, commit1);
231     DefaultIssue issue = newIssueOnLines(2, 3, 4);
232
233     underTest.onIssue(FILE, issue);
234
235     assertThat(issue.authorLogin()).isEqualTo("jane");
236     assertThat(issue.assignee()).isEqualTo("u2");
237     assertThat(issue.assigneeLogin()).isEqualTo("Jane");
238   }
239
240   private void setSingleChangeset(@Nullable String author, Long date, String revision) {
241     scmInfoRepository.setScmInfo(FILE_REF,
242       Changeset.newChangesetBuilder()
243         .setAuthor(author)
244         .setDate(date)
245         .setRevision(revision)
246         .build());
247   }
248
249   private void addScmUser(String scmAccount, UserIdDto userId) {
250     when(scmAccountToUser.getNullable(scmAccount)).thenReturn(userId);
251   }
252
253   private static DefaultIssue newIssueOnLines(int... lines) {
254     DefaultIssue issue = new DefaultIssue();
255     issue.setComponentUuid(FILE.getUuid());
256     DbIssues.Locations.Builder locations = DbIssues.Locations.newBuilder();
257     DbIssues.Flow.Builder flow = DbIssues.Flow.newBuilder();
258     Arrays.stream(lines).forEach(line -> flow.addLocation(newLocation(line)));
259     locations.addFlow(flow.build());
260     issue.setLocations(locations.build());
261     return issue;
262   }
263
264   private static DbIssues.Location newLocation(int line) {
265     return DbIssues.Location.newBuilder()
266       .setComponentId(FILE.getUuid())
267       .setTextRange(DbCommons.TextRange.newBuilder().setStartLine(line).setEndLine(line).build()).build();
268   }
269
270   private UserIdDto buildUserId(String uuid, String login) {
271     return new UserIdDto(uuid, login);
272   }
273 }