]> source.dussan.org Git - sonarqube.git/blob
5ee116be146652ec329b6ad275f82b9eb6b8b82b
[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.server.issue.ws;
21
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Date;
25 import java.util.List;
26 import java.util.Set;
27 import org.junit.Before;
28 import org.junit.Rule;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.mockito.junit.MockitoJUnitRunner;
32 import org.sonar.api.resources.Languages;
33 import org.sonar.api.utils.Duration;
34 import org.sonar.api.utils.Durations;
35 import org.sonar.db.DbTester;
36 import org.sonar.db.component.BranchDto;
37 import org.sonar.db.component.BranchType;
38 import org.sonar.db.component.ComponentDto;
39 import org.sonar.db.issue.IssueChangeDto;
40 import org.sonar.db.issue.IssueDto;
41 import org.sonar.db.project.ProjectDto;
42 import org.sonar.db.rule.RuleDto;
43 import org.sonar.db.user.UserDto;
44 import org.sonar.server.issue.TextRangeResponseFormatter;
45 import org.sonar.server.issue.workflow.Transition;
46 import org.sonarqube.ws.Common;
47 import org.sonarqube.ws.Issues.Issue;
48 import org.sonarqube.ws.Issues.Operation;
49
50 import static java.lang.System.currentTimeMillis;
51 import static java.util.stream.Collectors.toList;
52 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
53 import static org.assertj.core.api.Assertions.assertThat;
54 import static org.mockito.ArgumentMatchers.any;
55 import static org.mockito.ArgumentMatchers.eq;
56 import static org.mockito.Mockito.mock;
57 import static org.mockito.Mockito.when;
58 import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
59 import static org.sonar.api.rule.RuleKey.EXTERNAL_RULE_REPO_PREFIX;
60 import static org.sonar.api.rules.RuleType.CODE_SMELL;
61 import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
62 import static org.sonar.api.utils.DateUtils.formatDateTime;
63 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
64 import static org.sonar.db.issue.IssueTesting.newIssue;
65 import static org.sonar.db.issue.IssueTesting.newIssuechangeDto;
66 import static org.sonar.db.rule.RuleTesting.newRule;
67 import static org.sonar.db.user.UserTesting.newUserDto;
68 import static org.sonar.server.issue.index.IssueScope.MAIN;
69 import static org.sonar.server.issue.index.IssueScope.TEST;
70
71 @RunWith(MockitoJUnitRunner.class)
72 public class SearchResponseFormatFormatOperationTest {
73   @Rule
74   public DbTester db = DbTester.create();
75   private final Durations durations = new Durations();
76   private final Languages languages = mock(Languages.class);
77   private final TextRangeResponseFormatter textRangeResponseFormatter = mock(TextRangeResponseFormatter.class);
78   private final UserResponseFormatter userResponseFormatter = mock(UserResponseFormatter.class);
79   private final Common.User user = mock(Common.User.class);
80   private final SearchResponseFormat searchResponseFormat = new SearchResponseFormat(durations, languages, textRangeResponseFormatter, userResponseFormatter);
81
82   private SearchResponseData searchResponseData;
83   private IssueDto issueDto;
84   private ComponentDto componentDto;
85   private UserDto userDto;
86
87   @Before
88   public void setUp() {
89     searchResponseData = newSearchResponseDataMainBranch();
90   }
91
92   @Test
93   public void formatOperation_should_add_components_to_response() {
94     Operation result = searchResponseFormat.formatOperation(searchResponseData);
95
96     assertThat(result.getComponentsList()).hasSize(1);
97     assertThat(result.getComponentsList().get(0).getKey()).isEqualTo(issueDto.getComponentKey());
98   }
99
100   @Test
101   public void formatOperation_should_add_rules_to_response() {
102     Operation result = searchResponseFormat.formatOperation(searchResponseData);
103
104     assertThat(result.getRulesList()).hasSize(1);
105     assertThat(result.getRulesList().get(0).getKey()).isEqualTo(issueDto.getRuleKey().toString());
106   }
107
108   @Test
109   public void formatOperation_should_add_users_to_response() {
110     Operation result = searchResponseFormat.formatOperation(searchResponseData);
111
112     assertThat(result.getUsersList()).hasSize(1);
113     assertThat(result.getUsers(0)).isSameAs(user);
114   }
115
116   @Test
117   public void formatOperation_should_add_issue_to_response() {
118     Operation result = searchResponseFormat.formatOperation(searchResponseData);
119
120     assertIssueEqualsIssueDto(result.getIssue(), issueDto);
121   }
122
123   private void assertIssueEqualsIssueDto(Issue issue, IssueDto issueDto) {
124     assertThat(issue.getKey()).isEqualTo(issueDto.getKey());
125     assertThat(issue.getType().getNumber()).isEqualTo(issueDto.getType());
126     assertThat(issue.getComponent()).isEqualTo(issueDto.getComponentKey());
127     assertThat(issue.getRule()).isEqualTo(issueDto.getRuleKey().toString());
128     assertThat(issue.getSeverity()).hasToString(issueDto.getSeverity());
129     assertThat(issue.getAssignee()).isEqualTo(userDto.getLogin());
130     assertThat(issue.getResolution()).isEqualTo(issueDto.getResolution());
131     assertThat(issue.getStatus()).isEqualTo(issueDto.getStatus());
132     assertThat(issue.getMessage()).isEqualTo(issueDto.getMessage());
133     assertThat(new ArrayList<>(issue.getTagsList())).containsExactlyInAnyOrderElementsOf(issueDto.getTags());
134     assertThat(issue.getLine()).isEqualTo(issueDto.getLine());
135     assertThat(issue.getHash()).isEqualTo(issueDto.getChecksum());
136     assertThat(issue.getAuthor()).isEqualTo(issueDto.getAuthorLogin());
137     assertThat(issue.getCreationDate()).isEqualTo(formatDateTime(issueDto.getIssueCreationDate()));
138     assertThat(issue.getUpdateDate()).isEqualTo(formatDateTime(issueDto.getIssueUpdateDate()));
139     assertThat(issue.getCloseDate()).isEqualTo(formatDateTime(issueDto.getIssueCloseDate()));
140     assertThat(issue.getQuickFixAvailable()).isEqualTo(issueDto.isQuickFixAvailable());
141     assertThat(issue.getRuleDescriptionContextKey()).isEqualTo(issueDto.getOptionalRuleDescriptionContextKey().orElse(null));
142   }
143
144   @Test
145   public void formatOperation_should_not_add_issue_when_several_issue() {
146     searchResponseData = new SearchResponseData(List.of(createIssue(), createIssue()));
147
148     Operation result = searchResponseFormat.formatOperation(searchResponseData);
149
150     assertThat(result.getIssue()).isEqualTo(Issue.getDefaultInstance());
151   }
152
153   private static IssueDto createIssue() {
154     RuleDto ruleDto = newRule();
155     String projectUuid = "project_uuid_" + randomAlphanumeric(5);
156     ComponentDto projectDto = newPrivateProjectDto();
157     projectDto.setBranchUuid(projectUuid);
158     return newIssue(ruleDto, projectUuid, "project_key_" + randomAlphanumeric(5), projectDto);
159   }
160
161   @Test
162   public void formatOperation_should_add_branch_on_issue() {
163     String branchName = randomAlphanumeric(5);
164     searchResponseData = newSearchResponseDataBranch(branchName);
165     Operation result = searchResponseFormat.formatOperation(searchResponseData);
166     assertThat(result.getIssue().getBranch()).isEqualTo(branchName);
167   }
168
169   @Test
170   public void formatOperation_should_add_pullrequest_on_issue() {
171     searchResponseData = newSearchResponseDataPr("pr1");
172     Operation result = searchResponseFormat.formatOperation(searchResponseData);
173     assertThat(result.getIssue().getPullRequest()).isEqualTo("pr1");
174   }
175
176   @Test
177   public void formatOperation_should_add_project_on_issue() {
178     issueDto.setProjectUuid(componentDto.uuid());
179
180     Operation result = searchResponseFormat.formatOperation(searchResponseData);
181
182     assertThat(result.getIssue().getProject()).isEqualTo(componentDto.getKey());
183   }
184
185   @Test
186   public void formatOperation_should_add_external_rule_engine_on_issue() {
187     issueDto.setExternal(true);
188     String expected = randomAlphanumeric(5);
189     issueDto.setRuleKey(EXTERNAL_RULE_REPO_PREFIX + expected, randomAlphanumeric(5));
190
191     Operation result = searchResponseFormat.formatOperation(searchResponseData);
192
193     assertThat(result.getIssue().getExternalRuleEngine()).isEqualTo(expected);
194   }
195
196   @Test
197   public void formatOperation_should_add_effort_and_debt_on_issue() {
198     long effort = 60L;
199     issueDto.setEffort(effort);
200     String expected = durations.encode(Duration.create(effort));
201
202     Operation result = searchResponseFormat.formatOperation(searchResponseData);
203
204     assertThat(result.getIssue().getEffort()).isEqualTo(expected);
205     assertThat(result.getIssue().getDebt()).isEqualTo(expected);
206   }
207
208   @Test
209   public void formatOperation_should_add_scope_test_on_issue_when_unit_test_file() {
210     componentDto.setQualifier(UNIT_TEST_FILE);
211
212     Operation result = searchResponseFormat.formatOperation(searchResponseData);
213
214     assertThat(result.getIssue().getScope()).isEqualTo(TEST.name());
215   }
216
217   @Test
218   public void formatOperation_should_add_scope_main_on_issue_when_not_unit_test_file() {
219     componentDto.setQualifier(randomAlphanumeric(5));
220
221     Operation result = searchResponseFormat.formatOperation(searchResponseData);
222
223     assertThat(result.getIssue().getScope()).isEqualTo(MAIN.name());
224   }
225
226   @Test
227   public void formatOperation_should_add_actions_on_issues() {
228     Set<String> expectedActions = Set.of("actionA", "actionB");
229     searchResponseData.addActions(issueDto.getKey(), expectedActions);
230
231     Operation result = searchResponseFormat.formatOperation(searchResponseData);
232
233     assertThat(result.getIssue().getActions().getActionsList()).containsExactlyInAnyOrderElementsOf(expectedActions);
234   }
235
236   @Test
237   public void formatOperation_should_add_transitions_on_issues() {
238     Set<String> expectedTransitions = Set.of("transitionone", "transitiontwo");
239     searchResponseData.addTransitions(issueDto.getKey(), createFakeTransitions(expectedTransitions));
240
241     Operation result = searchResponseFormat.formatOperation(searchResponseData);
242
243     assertThat(result.getIssue().getTransitions().getTransitionsList()).containsExactlyInAnyOrderElementsOf(expectedTransitions);
244   }
245
246   private static List<Transition> createFakeTransitions(Collection<String> transitions) {
247     return transitions.stream()
248       .map(transition -> Transition.builder(transition).from("OPEN").to("RESOLVED").build())
249       .collect(toList());
250   }
251
252   @Test
253   public void formatOperation_should_add_comments_on_issues() {
254     IssueChangeDto issueChangeDto = newIssuechangeDto(issueDto);
255     searchResponseData.setComments(List.of(issueChangeDto));
256
257     Operation result = searchResponseFormat.formatOperation(searchResponseData);
258
259     assertThat(result.getIssue().getComments().getCommentsList()).hasSize(1).extracting(Common.Comment::getKey).containsExactly(issueChangeDto.getKey());
260   }
261
262   @Test
263   public void formatOperation_should_not_set_severity_for_security_hotspot_issue() {
264     issueDto.setType(SECURITY_HOTSPOT);
265
266     Operation result = searchResponseFormat.formatOperation(searchResponseData);
267
268     assertThat(result.getIssue().hasSeverity()).isFalse();
269   }
270
271   private SearchResponseData newSearchResponseDataMainBranch() {
272     ComponentDto projectDto = db.components().insertPublicProject();
273     BranchDto branchDto = db.getDbClient().branchDao().selectByUuid(db.getSession(), projectDto.uuid()).get();
274     return newSearchResponseData(projectDto, branchDto);
275   }
276
277   private SearchResponseData newSearchResponseDataBranch(String name) {
278     ProjectDto projectDto = db.components().insertPublicProjectDto();
279     BranchDto branch = db.components().insertProjectBranch(projectDto, b -> b.setKey(name));
280     ComponentDto branchComponent = db.components().getComponentDto(branch);
281     return newSearchResponseData(branchComponent, branch);
282   }
283
284   private SearchResponseData newSearchResponseDataPr(String name) {
285     ProjectDto projectDto = db.components().insertPublicProjectDto();
286     BranchDto branch = db.components().insertProjectBranch(projectDto, b -> b.setKey(name).setBranchType(BranchType.PULL_REQUEST));
287     ComponentDto branchComponent = db.components().getComponentDto(branch);
288     return newSearchResponseData(branchComponent, branch);
289   }
290
291   private SearchResponseData newSearchResponseData(ComponentDto component, BranchDto branch) {
292     RuleDto ruleDto = newRule();
293     userDto = newUserDto();
294     componentDto = component;
295     issueDto = newIssue(ruleDto, component.branchUuid(), component.getKey(), component)
296       .setType(CODE_SMELL)
297       .setRuleDescriptionContextKey("context_key_" + randomAlphanumeric(5))
298       .setAssigneeUuid(userDto.getUuid())
299       .setResolution("resolution_" + randomAlphanumeric(5))
300       .setIssueCreationDate(new Date(currentTimeMillis() - 2_000))
301       .setIssueUpdateDate(new Date(currentTimeMillis() - 1_000))
302       .setIssueCloseDate(new Date(currentTimeMillis()));
303
304     SearchResponseData searchResponseData = new SearchResponseData(issueDto);
305     searchResponseData.addComponents(List.of(component));
306     searchResponseData.addRules(List.of(ruleDto));
307     searchResponseData.addUsers(List.of(userDto));
308     searchResponseData.addBranches(List.of(branch));
309
310     when(userResponseFormatter.formatUser(any(Common.User.Builder.class), eq(userDto))).thenReturn(user);
311
312     return searchResponseData;
313   }
314 }