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 String pullRequestKey = randomAlphanumeric(5);
180 componentDto.setKey(randomAlphanumeric(5) + PULL_REQUEST_SEPARATOR + pullRequestKey);
182 Operation result = searchResponseFormat.formatOperation(searchResponseData);
184 assertThat(result.getIssue().getPullRequest()).isEqualTo(pullRequestKey);
188 public void formatOperation_should_add_project_on_issue() {
189 issueDto.setProjectUuid(componentDto.uuid());
191 Operation result = searchResponseFormat.formatOperation(searchResponseData);
193 assertThat(result.getIssue().getProject()).isEqualTo(componentDto.getKey());
197 public void formatOperation_should_add_external_rule_engine_on_issue() {
198 issueDto.setExternal(true);
199 String expected = randomAlphanumeric(5);
200 issueDto.setRuleKey(EXTERNAL_RULE_REPO_PREFIX + expected, randomAlphanumeric(5));
202 Operation result = searchResponseFormat.formatOperation(searchResponseData);
204 assertThat(result.getIssue().getExternalRuleEngine()).isEqualTo(expected);
208 public void formatOperation_should_add_effort_and_debt_on_issue() {
210 issueDto.setEffort(effort);
211 String expected = durations.encode(Duration.create(effort));
213 Operation result = searchResponseFormat.formatOperation(searchResponseData);
215 assertThat(result.getIssue().getEffort()).isEqualTo(expected);
216 assertThat(result.getIssue().getDebt()).isEqualTo(expected);
220 public void formatOperation_should_add_scope_test_on_issue_when_unit_test_file() {
221 componentDto.setQualifier(UNIT_TEST_FILE);
223 Operation result = searchResponseFormat.formatOperation(searchResponseData);
225 assertThat(result.getIssue().getScope()).isEqualTo(TEST.name());
229 public void formatOperation_should_add_scope_main_on_issue_when_not_unit_test_file() {
230 componentDto.setQualifier(randomAlphanumeric(5));
232 Operation result = searchResponseFormat.formatOperation(searchResponseData);
234 assertThat(result.getIssue().getScope()).isEqualTo(MAIN.name());
238 public void formatOperation_should_add_actions_on_issues() {
239 Set<String> expectedActions = Set.of("actionA", "actionB");
240 searchResponseData.addActions(issueDto.getKey(), expectedActions);
242 Operation result = searchResponseFormat.formatOperation(searchResponseData);
244 assertThat(result.getIssue().getActions().getActionsList()).containsExactlyInAnyOrderElementsOf(expectedActions);
248 public void formatOperation_should_add_transitions_on_issues() {
249 Set<String> expectedTransitions = Set.of("transitionone", "transitiontwo");
250 searchResponseData.addTransitions(issueDto.getKey(), createFakeTransitions(expectedTransitions));
252 Operation result = searchResponseFormat.formatOperation(searchResponseData);
254 assertThat(result.getIssue().getTransitions().getTransitionsList()).containsExactlyInAnyOrderElementsOf(expectedTransitions);
257 private static List<Transition> createFakeTransitions(Collection<String> transitions) {
258 return transitions.stream()
259 .map(transition -> Transition.builder(transition).from("OPEN").to("RESOLVED").build())
264 public void formatOperation_should_add_comments_on_issues() {
265 IssueChangeDto issueChangeDto = newIssuechangeDto(issueDto);
266 searchResponseData.setComments(List.of(issueChangeDto));
268 Operation result = searchResponseFormat.formatOperation(searchResponseData);
270 assertThat(result.getIssue().getComments().getCommentsList()).hasSize(1).extracting(Common.Comment::getKey).containsExactly(issueChangeDto.getKey());
274 public void formatOperation_should_not_set_severity_for_security_hotspot_issue() {
275 issueDto.setType(SECURITY_HOTSPOT);
277 Operation result = searchResponseFormat.formatOperation(searchResponseData);
279 assertThat(result.getIssue().hasSeverity()).isFalse();
282 private static SearchResponseData newSearchResponseData() {
283 RuleDto ruleDto = newRule();
285 String projectUuid = "project_uuid_" + randomAlphanumeric(5);
286 ComponentDto projectDto = newPrivateProjectDto();
287 projectDto.setBranchUuid(projectUuid);
289 UserDto userDto = newUserDto();
291 IssueDto issueDto = newIssue(ruleDto, projectUuid, "project_key_" + randomAlphanumeric(5), projectDto)
293 .setRuleDescriptionContextKey("context_key_" + randomAlphanumeric(5))
294 .setAssigneeUuid(userDto.getUuid())
295 .setResolution("resolution_" + randomAlphanumeric(5))
296 .setIssueCreationDate(new Date(currentTimeMillis() - 2_000))
297 .setIssueUpdateDate(new Date(currentTimeMillis() - 1_000))
298 .setIssueCloseDate(new Date(currentTimeMillis()));
300 SearchResponseData searchResponseData = new SearchResponseData(issueDto);
301 searchResponseData.addComponents(List.of(projectDto));
302 searchResponseData.addRules(List.of(ruleDto));
303 searchResponseData.addUsers(List.of(userDto));
304 return searchResponseData;