3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.issue.ws;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Date;
25 import java.util.List;
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 import org.mockito.Mock;
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.component.ComponentDto;
36 import org.sonar.db.issue.IssueChangeDto;
37 import org.sonar.db.issue.IssueDto;
38 import org.sonar.db.rule.RuleDto;
39 import org.sonar.db.user.UserDto;
40 import org.sonar.server.issue.TextRangeResponseFormatter;
41 import org.sonar.server.issue.workflow.Transition;
42 import org.sonarqube.ws.Common;
43 import org.sonarqube.ws.Issues.Issue;
44 import org.sonarqube.ws.Issues.Operation;
46 import static java.lang.System.currentTimeMillis;
47 import static java.util.stream.Collectors.toList;
48 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
49 import static org.assertj.core.api.Assertions.assertThat;
50 import static org.mockito.ArgumentMatchers.any;
51 import static org.mockito.ArgumentMatchers.eq;
52 import static org.mockito.Mockito.when;
53 import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
54 import static org.sonar.api.rule.RuleKey.EXTERNAL_RULE_REPO_PREFIX;
55 import static org.sonar.api.rules.RuleType.CODE_SMELL;
56 import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
57 import static org.sonar.api.utils.DateUtils.formatDateTime;
58 import static org.sonar.db.component.ComponentDto.BRANCH_KEY_SEPARATOR;
59 import static org.sonar.db.component.ComponentDto.PULL_REQUEST_SEPARATOR;
60 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
61 import static org.sonar.db.issue.IssueTesting.newIssue;
62 import static org.sonar.db.issue.IssueTesting.newIssuechangeDto;
63 import static org.sonar.db.rule.RuleTesting.newRule;
64 import static org.sonar.db.user.UserTesting.newUserDto;
65 import static org.sonar.server.issue.index.IssueScope.MAIN;
66 import static org.sonar.server.issue.index.IssueScope.TEST;
68 @RunWith(MockitoJUnitRunner.class)
69 public class SearchResponseFormatFormatOperationTest {
71 private SearchResponseFormat searchResponseFormat;
73 private final Durations durations = new Durations();
75 private Languages languages;
77 private TextRangeResponseFormatter textRangeResponseFormatter;
79 private UserResponseFormatter userResponseFormatter;
81 private Common.User user;
83 private SearchResponseData searchResponseData;
84 private IssueDto issueDto;
85 private ComponentDto componentDto;
86 private UserDto userDto;
91 searchResponseFormat = new SearchResponseFormat(durations, languages, textRangeResponseFormatter, userResponseFormatter);
92 searchResponseData = newSearchResponseData();
93 issueDto = searchResponseData.getIssues().get(0);
94 componentDto = searchResponseData.getComponents().iterator().next();
95 userDto = searchResponseData.getUsers().get(0);
96 when(userResponseFormatter.formatUser(any(Common.User.Builder.class), eq(userDto))).thenReturn(user);
100 public void formatOperation_should_add_components_to_response() {
101 Operation result = searchResponseFormat.formatOperation(searchResponseData);
103 assertThat(result.getComponentsList()).hasSize(1);
104 assertThat(result.getComponentsList().get(0).getKey()).isEqualTo(issueDto.getComponentKey());
108 public void formatOperation_should_add_rules_to_response() {
109 Operation result = searchResponseFormat.formatOperation(searchResponseData);
111 assertThat(result.getRulesList()).hasSize(1);
112 assertThat(result.getRulesList().get(0).getKey()).isEqualTo(issueDto.getRuleKey().toString());
116 public void formatOperation_should_add_users_to_response() {
117 Operation result = searchResponseFormat.formatOperation(searchResponseData);
119 assertThat(result.getUsersList()).hasSize(1);
120 assertThat(result.getUsers(0)).isSameAs(user);
124 public void formatOperation_should_add_issue_to_response() {
125 Operation result = searchResponseFormat.formatOperation(searchResponseData);
127 assertIssueEqualsIssueDto(result.getIssue(), issueDto);
130 private void assertIssueEqualsIssueDto(Issue issue, IssueDto issueDto) {
131 assertThat(issue.getKey()).isEqualTo(issueDto.getKey());
132 assertThat(issue.getType().getNumber()).isEqualTo(issueDto.getType());
133 assertThat(issue.getComponent()).isEqualTo(issueDto.getComponentKey());
134 assertThat(issue.getRule()).isEqualTo(issueDto.getRuleKey().toString());
135 assertThat(issue.getSeverity()).hasToString(issueDto.getSeverity());
136 assertThat(issue.getAssignee()).isEqualTo(userDto.getLogin());
137 assertThat(issue.getResolution()).isEqualTo(issueDto.getResolution());
138 assertThat(issue.getStatus()).isEqualTo(issueDto.getStatus());
139 assertThat(issue.getMessage()).isEqualTo(issueDto.getMessage());
140 assertThat(new ArrayList<>(issue.getTagsList())).containsExactlyInAnyOrderElementsOf(issueDto.getTags());
141 assertThat(issue.getLine()).isEqualTo(issueDto.getLine());
142 assertThat(issue.getHash()).isEqualTo(issueDto.getChecksum());
143 assertThat(issue.getAuthor()).isEqualTo(issueDto.getAuthorLogin());
144 assertThat(issue.getCreationDate()).isEqualTo(formatDateTime(issueDto.getIssueCreationDate()));
145 assertThat(issue.getUpdateDate()).isEqualTo(formatDateTime(issueDto.getIssueUpdateDate()));
146 assertThat(issue.getCloseDate()).isEqualTo(formatDateTime(issueDto.getIssueCloseDate()));
147 assertThat(issue.getQuickFixAvailable()).isEqualTo(issueDto.isQuickFixAvailable());
148 assertThat(issue.getRuleDescriptionContextKey()).isEqualTo(issueDto.getOptionalRuleDescriptionContextKey().orElse(null));
152 public void formatOperation_should_not_add_issue_when_several_issue() {
153 searchResponseData = new SearchResponseData(List.of(createIssue(), createIssue()));
155 Operation result = searchResponseFormat.formatOperation(searchResponseData);
157 assertThat(result.getIssue()).isEqualTo(Issue.getDefaultInstance());
160 private static IssueDto createIssue() {
161 RuleDto ruleDto = newRule();
162 String projectUuid = "project_uuid_" + randomAlphanumeric(5);
163 ComponentDto projectDto = newPrivateProjectDto();
164 projectDto.setBranchUuid(projectUuid);
165 return newIssue(ruleDto, projectUuid, "project_key_" + randomAlphanumeric(5), projectDto);
169 public void formatOperation_should_add_branch_on_issue() {
170 componentDto.setKey(randomAlphanumeric(5) + BRANCH_KEY_SEPARATOR + randomAlphanumeric(5));
172 Operation result = searchResponseFormat.formatOperation(searchResponseData);
174 assertThat(result.getIssue().getBranch()).isEqualTo(componentDto.getBranch());
178 public void formatOperation_should_add_pullrequest_on_issue() {
179 componentDto.setKey(randomAlphanumeric(5) + PULL_REQUEST_SEPARATOR + randomAlphanumeric(5));
181 Operation result = searchResponseFormat.formatOperation(searchResponseData);
183 assertThat(result.getIssue().getPullRequest()).isEqualTo(componentDto.getPullRequest());
187 public void formatOperation_should_add_project_on_issue() {
188 issueDto.setProjectUuid(componentDto.uuid());
190 Operation result = searchResponseFormat.formatOperation(searchResponseData);
192 assertThat(result.getIssue().getProject()).isEqualTo(componentDto.getKey());
196 public void formatOperation_should_add_external_rule_engine_on_issue() {
197 issueDto.setExternal(true);
198 String expected = randomAlphanumeric(5);
199 issueDto.setRuleKey(EXTERNAL_RULE_REPO_PREFIX + expected, randomAlphanumeric(5));
201 Operation result = searchResponseFormat.formatOperation(searchResponseData);
203 assertThat(result.getIssue().getExternalRuleEngine()).isEqualTo(expected);
207 public void formatOperation_should_add_effort_and_debt_on_issue() {
209 issueDto.setEffort(effort);
210 String expected = durations.encode(Duration.create(effort));
212 Operation result = searchResponseFormat.formatOperation(searchResponseData);
214 assertThat(result.getIssue().getEffort()).isEqualTo(expected);
215 assertThat(result.getIssue().getDebt()).isEqualTo(expected);
219 public void formatOperation_should_add_scope_test_on_issue_when_unit_test_file() {
220 componentDto.setQualifier(UNIT_TEST_FILE);
222 Operation result = searchResponseFormat.formatOperation(searchResponseData);
224 assertThat(result.getIssue().getScope()).isEqualTo(TEST.name());
228 public void formatOperation_should_add_scope_main_on_issue_when_not_unit_test_file() {
229 componentDto.setQualifier(randomAlphanumeric(5));
231 Operation result = searchResponseFormat.formatOperation(searchResponseData);
233 assertThat(result.getIssue().getScope()).isEqualTo(MAIN.name());
237 public void formatOperation_should_add_actions_on_issues() {
238 Set<String> expectedActions = Set.of("actionA", "actionB");
239 searchResponseData.addActions(issueDto.getKey(), expectedActions);
241 Operation result = searchResponseFormat.formatOperation(searchResponseData);
243 assertThat(result.getIssue().getActions().getActionsList()).containsExactlyInAnyOrderElementsOf(expectedActions);
247 public void formatOperation_should_add_transitions_on_issues() {
248 Set<String> expectedTransitions = Set.of("transitionone", "transitiontwo");
249 searchResponseData.addTransitions(issueDto.getKey(), createFakeTransitions(expectedTransitions));
251 Operation result = searchResponseFormat.formatOperation(searchResponseData);
253 assertThat(result.getIssue().getTransitions().getTransitionsList()).containsExactlyInAnyOrderElementsOf(expectedTransitions);
256 private static List<Transition> createFakeTransitions(Collection<String> transitions) {
257 return transitions.stream()
258 .map(transition -> Transition.builder(transition).from("OPEN").to("RESOLVED").build())
263 public void formatOperation_should_add_comments_on_issues() {
264 IssueChangeDto issueChangeDto = newIssuechangeDto(issueDto);
265 searchResponseData.setComments(List.of(issueChangeDto));
267 Operation result = searchResponseFormat.formatOperation(searchResponseData);
269 assertThat(result.getIssue().getComments().getCommentsList()).hasSize(1).extracting(Common.Comment::getKey).containsExactly(issueChangeDto.getKey());
273 public void formatOperation_should_not_set_severity_for_security_hotspot_issue() {
274 issueDto.setType(SECURITY_HOTSPOT);
276 Operation result = searchResponseFormat.formatOperation(searchResponseData);
278 assertThat(result.getIssue().hasSeverity()).isFalse();
281 private static SearchResponseData newSearchResponseData() {
282 RuleDto ruleDto = newRule();
284 String projectUuid = "project_uuid_" + randomAlphanumeric(5);
285 ComponentDto projectDto = newPrivateProjectDto();
286 projectDto.setBranchUuid(projectUuid);
288 UserDto userDto = newUserDto();
290 IssueDto issueDto = newIssue(ruleDto, projectUuid, "project_key_" + randomAlphanumeric(5), projectDto)
292 .setRuleDescriptionContextKey("context_key_" + randomAlphanumeric(5))
293 .setAssigneeUuid(userDto.getUuid())
294 .setResolution("resolution_" + randomAlphanumeric(5))
295 .setIssueCreationDate(new Date(currentTimeMillis() - 2_000))
296 .setIssueUpdateDate(new Date(currentTimeMillis() - 1_000))
297 .setIssueCloseDate(new Date(currentTimeMillis()));
299 SearchResponseData searchResponseData = new SearchResponseData(issueDto);
300 searchResponseData.addComponents(List.of(projectDto));
301 searchResponseData.addRules(List.of(ruleDto));
302 searchResponseData.addUsers(List.of(userDto));
303 return searchResponseData;