3 * Copyright (C) 2009-2019 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.qualityprofile.ws;
22 import com.google.common.collect.ImmutableMap;
23 import java.util.Arrays;
25 import java.util.function.Consumer;
26 import javax.annotation.Nullable;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.junit.rules.ExpectedException;
30 import org.sonar.api.resources.Languages;
31 import org.sonar.api.rule.RuleKey;
32 import org.sonar.api.server.ws.WebService;
33 import org.sonar.api.utils.DateUtils;
34 import org.sonar.api.impl.utils.TestSystem2;
35 import org.sonar.db.DbTester;
36 import org.sonar.db.organization.OrganizationDto;
37 import org.sonar.db.qualityprofile.QProfileChangeDto;
38 import org.sonar.db.qualityprofile.QProfileDto;
39 import org.sonar.db.rule.RuleDefinitionDto;
40 import org.sonar.db.user.UserDto;
41 import org.sonar.server.exceptions.ForbiddenException;
42 import org.sonar.server.exceptions.NotFoundException;
43 import org.sonar.server.organization.TestDefaultOrganizationProvider;
44 import org.sonar.server.qualityprofile.ActiveRuleChange;
45 import org.sonar.server.qualityprofile.ActiveRuleInheritance;
46 import org.sonar.server.tester.UserSessionRule;
47 import org.sonar.server.ws.WsActionTester;
49 import static java.lang.String.format;
50 import static java.lang.String.valueOf;
51 import static org.assertj.core.api.Assertions.assertThat;
52 import static org.sonar.db.organization.OrganizationDto.Subscription.PAID;
53 import static org.sonar.test.JsonAssert.assertJson;
54 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_KEY;
55 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
56 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION;
57 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
58 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SINCE;
60 public class ChangelogActionTest {
62 private static final String DATE = "2011-04-25T01:15:42+0100";
64 private TestSystem2 system2 = new TestSystem2().setNow(DateUtils.parseDateTime(DATE).getTime());
67 public DbTester db = DbTester.create(system2);
69 public UserSessionRule userSession = UserSessionRule.standalone();
71 public ExpectedException expectedException = ExpectedException.none();
73 private QProfileWsSupport wsSupport = new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db));
74 private WsActionTester ws = new WsActionTester(
75 new ChangelogAction(wsSupport, new Languages(), db.getDbClient()));
78 public void return_change_with_all_fields() {
79 OrganizationDto organization = db.organizations().insert();
80 QProfileDto profile = db.qualityProfiles().insert(organization);
81 UserDto user = db.users().insertUser();
82 RuleDefinitionDto rule = db.rules().insert(RuleKey.of("java", "S001"));
83 insertChange(profile, ActiveRuleChange.Type.ACTIVATED, user, ImmutableMap.of(
84 "ruleId", valueOf(rule.getId()),
86 "inheritance", ActiveRuleInheritance.INHERITED.name(),
87 "param_foo", "foo_value",
88 "param_bar", "bar_value"));
90 String response = ws.newRequest()
91 .setParam(PARAM_KEY, profile.getKee())
95 assertJson(response).isSimilarTo("{\n" +
101 " \"date\": \"" + DATE + "\",\n" +
102 " \"authorLogin\": \"" + user.getLogin() + "\",\n" +
103 " \"authorName\": \"" + user.getName() + "\",\n" +
104 " \"action\": \"ACTIVATED\",\n" +
105 " \"ruleKey\": \"" + rule.getKey() + "\",\n" +
106 " \"ruleName\": \"" + rule.getName() + "\",\n" +
108 " \"severity\": \"MINOR\",\n" +
109 " \"bar\": \"bar_value\",\n" +
110 " \"foo\": \"foo_value\"\n" +
118 public void find_changelog_by_profile_key() {
119 OrganizationDto organization = db.organizations().insert();
120 QProfileDto profile = db.qualityProfiles().insert(organization);
121 RuleDefinitionDto rule = db.rules().insert();
122 UserDto user = db.users().insertUser();
123 insertChange(profile, ActiveRuleChange.Type.ACTIVATED, user,
125 "ruleId", valueOf(rule.getId()),
126 "severity", "MINOR"));
128 String response = ws.newRequest()
129 .setParam(PARAM_KEY, profile.getKee())
133 assertJson(response).isSimilarTo("{\n" +
136 " \"date\": \"" + DATE + "\",\n" +
137 " \"authorLogin\": \"" + user.getLogin() + "\",\n" +
138 " \"action\": \"ACTIVATED\",\n" +
139 " \"ruleKey\": \"" + rule.getKey() + "\",\n" +
140 " \"ruleName\": \"" + rule.getName() + "\",\n" +
142 " \"severity\": \"MINOR\"\n" +
150 public void find_changelog_by_language_and_name() {
151 QProfileDto qualityProfile = db.qualityProfiles().insert(db.getDefaultOrganization());
152 RuleDefinitionDto rule = db.rules().insert();
153 UserDto user = db.users().insertUser();
154 insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
156 "ruleId", valueOf(rule.getId()),
157 "severity", "MINOR"));
159 String response = ws.newRequest()
160 .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
161 .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
165 assertJson(response).isSimilarTo("{\n" +
168 " \"date\": \"" + DATE + "\",\n" +
169 " \"authorLogin\": \"" + user.getLogin() + "\",\n" +
170 " \"action\": \"ACTIVATED\",\n" +
171 " \"ruleKey\": \"" + rule.getKey() + "\",\n" +
172 " \"ruleName\": \"" + rule.getName() + "\",\n" +
174 " \"severity\": \"MINOR\"\n" +
182 public void find_changelog_by_organization_and_language_and_name() {
183 OrganizationDto organization = db.organizations().insert();
184 QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
185 RuleDefinitionDto rule = db.rules().insert();
186 UserDto user = db.users().insertUser();
187 insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
189 "ruleId", valueOf(rule.getId()),
190 "severity", "MINOR"));
192 String response = ws.newRequest()
193 .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
194 .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
195 .setParam(PARAM_ORGANIZATION, organization.getKey())
199 assertJson(response).isSimilarTo("{\n" +
202 " \"date\": \"" + DATE + "\",\n" +
203 " \"authorLogin\": \"" + user.getLogin() + "\",\n" +
204 " \"action\": \"ACTIVATED\",\n" +
205 " \"ruleKey\": \"" + rule.getKey() + "\",\n" +
206 " \"ruleName\": \"" + rule.getName() + "\",\n" +
208 " \"severity\": \"MINOR\"\n" +
216 public void changelog_empty() {
217 OrganizationDto organization = db.organizations().insert();
218 QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
220 String response = ws.newRequest()
221 .setParam(PARAM_KEY, qualityProfile.getKee())
225 assertJson(response).isSimilarTo("{\"total\":0,\"p\":1,\"ps\":50,\"events\":[]}");
229 public void changelog_filter_by_since() {
230 OrganizationDto organization = db.organizations().insert();
231 QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
232 system2.setNow(DateUtils.parseDateTime("2011-04-25T01:15:42+0100").getTime());
233 RuleDefinitionDto rule = db.rules().insert();
234 UserDto user = db.users().insertUser();
235 insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
237 "ruleId", valueOf(rule.getId()),
238 "severity", "MINOR"));
240 assertJson(ws.newRequest()
241 .setParam(PARAM_KEY, qualityProfile.getKee())
242 .setParam(PARAM_SINCE, "2011-04-25T01:15:42+0100")
244 .getInput()).isSimilarTo("{\n" +
247 " \"date\": \"2011-04-25T01:15:42+0100\",\n" +
248 " \"authorLogin\": \"" + user.getLogin() + "\",\n" +
249 " \"action\": \"ACTIVATED\",\n" +
250 " \"ruleKey\": \"" + rule.getKey() + "\",\n" +
251 " \"ruleName\": \"" + rule.getName() + "\",\n" +
256 assertJson(ws.newRequest()
257 .setParam(PARAM_KEY, qualityProfile.getKee())
258 .setParam(PARAM_SINCE, "2011-04-25T01:15:43+0100")
260 .getInput()).isSimilarTo("{\n" +
261 " \"events\": []\n" +
266 public void sort_changelog_events_in_reverse_chronological_order() {
267 OrganizationDto organization = db.organizations().insert();
268 QProfileDto profile = db.qualityProfiles().insert(organization);
269 system2.setNow(DateUtils.parseDateTime("2011-04-25T01:15:42+0100").getTime());
270 RuleDefinitionDto rule1 = db.rules().insert();
271 insertChange(profile, ActiveRuleChange.Type.ACTIVATED, null,
273 "ruleId", valueOf(rule1.getId()),
274 "severity", "MINOR"));
275 system2.setNow(DateUtils.parseDateTime("2011-04-25T01:15:43+0100").getTime());
276 UserDto user = db.users().insertUser();
277 RuleDefinitionDto rule2 = db.rules().insert();
278 insertChange(profile, ActiveRuleChange.Type.DEACTIVATED, user,
279 ImmutableMap.of("ruleId", valueOf(rule2.getId())));
281 String response = ws.newRequest()
282 .setParam(PARAM_KEY, profile.getKee())
286 assertJson(response).isSimilarTo("{\n" +
289 " \"date\": \"2011-04-25T02:15:43+0200\",\n" +
290 " \"action\": \"DEACTIVATED\",\n" +
291 " \"authorLogin\": \"" + user.getLogin() + "\",\n" +
292 " \"ruleKey\": \"" + rule2.getKey() + "\",\n" +
293 " \"ruleName\": \"" + rule2.getName() + "\",\n" +
294 " \"params\": {}\n" +
297 " \"date\": \"2011-04-25T02:15:42+0200\",\n" +
298 " \"action\": \"ACTIVATED\",\n" +
299 " \"ruleKey\": \"" + rule1.getKey() + "\",\n" +
300 " \"ruleName\": \"" + rule1.getName() + "\",\n" +
302 " \"severity\": \"MINOR\"\n" +
310 public void changelog_on_no_more_existing_rule() {
311 OrganizationDto organization = db.organizations().insert();
312 QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
313 UserDto user = db.users().insertUser();
314 insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
315 ImmutableMap.of("ruleId", "123"));
317 String response = ws.newRequest()
318 .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
319 .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
320 .setParam(PARAM_ORGANIZATION, organization.getKey())
324 assertJson(response).isSimilarTo("{\n" +
327 " \"date\": \"" + DATE + "\",\n" +
328 " \"action\": \"ACTIVATED\",\n" +
329 " \"params\": {}\n" +
333 assertThat(response).doesNotContain("ruleKey", "ruleName");
337 public void changelog_on_no_more_existing_user() {
338 OrganizationDto organization = db.organizations().insert();
339 QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
340 RuleDefinitionDto rule = db.rules().insert();
341 insertChange(c -> c.setRulesProfileUuid(qualityProfile.getRulesProfileUuid())
342 .setUserUuid("UNKNOWN")
343 .setChangeType(ActiveRuleChange.Type.ACTIVATED.name())
344 .setData(ImmutableMap.of("ruleId", rule.getId())));
346 String response = ws.newRequest()
347 .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
348 .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
349 .setParam(PARAM_ORGANIZATION, organization.getKey())
353 assertJson(response).isSimilarTo("{\n" +
356 " \"date\": \"" + DATE + "\",\n" +
357 " \"ruleKey\": \"" + rule.getKey() + "\",\n" +
358 " \"ruleName\": \"" + rule.getName() + "\",\n" +
359 " \"action\": \"ACTIVATED\",\n" +
360 " \"params\": {}\n" +
364 assertThat(response).doesNotContain("authorLogin", "authorName");
368 public void changelog_on_paid_organization() {
369 OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID));
370 UserDto user = db.users().insertUser();
371 userSession.logIn(user).addMembership(organization);
372 QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
373 RuleDefinitionDto rule = db.rules().insert();
374 insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, db.users().insertUser(),
376 "ruleId", valueOf(rule.getId()),
377 "severity", "MINOR"));
379 String response = ws.newRequest()
380 .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
381 .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
382 .setParam(PARAM_ORGANIZATION, organization.getKey())
386 assertJson(response).isSimilarTo("{\n" +
389 " \"ruleKey\": \"" + rule.getKey() + "\",\n" +
396 public void do_not_find_changelog_by_wrong_organization_and_language_and_name() {
397 OrganizationDto organization1 = db.organizations().insert();
398 OrganizationDto organization2 = db.organizations().insert();
399 QProfileDto qualityProfile = db.qualityProfiles().insert(organization1);
400 RuleDefinitionDto rule = db.rules().insert();
401 UserDto user = db.users().insertUser();
402 insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
404 "ruleId", valueOf(rule.getId()),
405 "severity", "MINOR"));
407 expectedException.expect(NotFoundException.class);
410 .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
411 .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
412 .setParam(PARAM_ORGANIZATION, organization2.getKey())
417 public void fail_on_paid_organization_when_not_member() {
418 OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID));
419 QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
421 expectedException.expect(ForbiddenException.class);
422 expectedException.expectMessage(format("You're not member of organization '%s'", organization.getKey()));
425 .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
426 .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
427 .setParam(PARAM_ORGANIZATION, organization.getKey())
432 public void example() {
433 OrganizationDto organization = db.organizations().insert();
434 QProfileDto profile = db.qualityProfiles().insert(organization);
435 String profileUuid = profile.getRulesProfileUuid();
437 system2.setNow(DateUtils.parseDateTime("2015-02-23T17:58:39+0100").getTime());
438 RuleDefinitionDto rule1 = db.rules().insert(RuleKey.of("squid", "S2438"), r -> r.setName("\"Threads\" should not be used where \"Runnables\" are expected"));
439 UserDto user1 = db.users().insertUser(u -> u.setLogin("anakin.skywalker").setName("Anakin Skywalker"));
440 insertChange(c -> c.setRulesProfileUuid(profileUuid)
441 .setUserUuid(user1.getUuid())
442 .setChangeType(ActiveRuleChange.Type.ACTIVATED.name())
443 .setData(ImmutableMap.of("severity", "CRITICAL", "ruleId", valueOf(rule1.getId()))));
445 system2.setNow(DateUtils.parseDateTime("2015-02-23T17:58:18+0100").getTime());
446 RuleDefinitionDto rule2 = db.rules().insert(RuleKey.of("squid", "S2162"), r -> r.setName("\"equals\" methods should be symmetric and work for subclasses"));
447 UserDto user2 = db.users().insertUser(u -> u.setLogin("padme.amidala").setName("Padme Amidala"));
448 QProfileChangeDto change2 = insertChange(c -> c.setRulesProfileUuid(profileUuid)
449 .setUserUuid(user2.getUuid())
450 .setChangeType(ActiveRuleChange.Type.DEACTIVATED.name())
451 .setData(ImmutableMap.of("ruleId", valueOf(rule2.getId()))));
453 system2.setNow(DateUtils.parseDateTime("2014-09-12T15:20:46+0200").getTime());
454 RuleDefinitionDto rule3 = db.rules().insert(RuleKey.of("squid", "S00101"), r -> r.setName("Class names should comply with a naming convention"));
455 UserDto user3 = db.users().insertUser(u -> u.setLogin("obiwan.kenobi").setName("Obiwan Kenobi"));
456 QProfileChangeDto change3 = insertChange(c -> c.setRulesProfileUuid(profileUuid)
457 .setUserUuid(user3.getUuid())
458 .setChangeType(ActiveRuleChange.Type.ACTIVATED.name())
459 .setData(ImmutableMap.of("severity", "MAJOR", "param_format", "^[A-Z][a-zA-Z0-9]*$", "ruleId", valueOf(rule3.getId()))));
461 String response = ws.newRequest()
463 .setParam(PARAM_KEY, profile.getKee())
464 .setParam("ps", "10")
468 assertJson(response).isSimilarTo(getClass().getResource("changelog-example.json"));
472 public void definition() {
473 WebService.Action definition = ws.getDef();
475 assertThat(definition.isPost()).isFalse();
476 assertThat(definition.responseExampleAsString()).isNotEmpty();
477 assertThat(definition.params()).extracting(WebService.Param::key)
478 .containsExactlyInAnyOrder("key", "qualityProfile", "language", "organization", "since", "to", "p", "ps");
479 WebService.Param profileName = definition.param("qualityProfile");
480 assertThat(profileName.deprecatedSince()).isNullOrEmpty();
481 WebService.Param language = definition.param("language");
482 assertThat(language.deprecatedSince()).isNullOrEmpty();
485 private QProfileChangeDto insertChange(QProfileDto profile, ActiveRuleChange.Type type, @Nullable UserDto user, @Nullable Map<String, Object> data) {
486 return insertChange(c -> c.setRulesProfileUuid(profile.getRulesProfileUuid())
487 .setUserUuid(user == null ? null : user.getUuid())
488 .setChangeType(type.name())
493 private final QProfileChangeDto insertChange(Consumer<QProfileChangeDto>... consumers) {
494 QProfileChangeDto dto = new QProfileChangeDto();
495 Arrays.stream(consumers).forEach(c -> c.accept(dto));
496 db.getDbClient().qProfileChangeDao().insert(db.getSession(), dto);