]> source.dussan.org Git - sonarqube.git/blob
abf09be52aed81d3094a5940fcfe9631197a9c2c
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 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.qualityprofile.ws;
21
22 import java.util.List;
23 import org.junit.Rule;
24 import org.junit.Test;
25 import org.sonar.api.resources.Language;
26 import org.sonar.api.resources.Languages;
27 import org.sonar.api.server.ws.Change;
28 import org.sonar.api.server.ws.WebService;
29 import org.sonar.api.utils.DateUtils;
30 import org.sonar.api.utils.System2;
31 import org.sonar.core.util.stream.MoreCollectors;
32 import org.sonar.db.DbClient;
33 import org.sonar.db.DbTester;
34 import org.sonar.db.permission.GlobalPermission;
35 import org.sonar.db.project.ProjectDto;
36 import org.sonar.db.qualityprofile.QProfileDto;
37 import org.sonar.db.qualityprofile.QualityProfileDbTester;
38 import org.sonar.db.rule.RuleDefinitionDto;
39 import org.sonar.db.user.GroupDto;
40 import org.sonar.db.user.UserDto;
41 import org.sonar.server.component.ComponentFinder;
42 import org.sonar.server.exceptions.NotFoundException;
43 import org.sonar.server.tester.UserSessionRule;
44 import org.sonar.server.ws.TestRequest;
45 import org.sonar.server.ws.WsActionTester;
46 import org.sonarqube.ws.MediaTypes;
47 import org.sonarqube.ws.Qualityprofiles.SearchWsResponse;
48 import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile;
49
50 import static java.util.stream.IntStream.range;
51 import static org.assertj.core.api.Assertions.assertThat;
52 import static org.assertj.core.api.Assertions.assertThatThrownBy;
53 import static org.assertj.core.api.Assertions.tuple;
54 import static org.sonar.api.rule.RuleStatus.DEPRECATED;
55 import static org.sonar.api.utils.DateUtils.parseDateTime;
56 import static org.sonar.db.qualityprofile.QualityProfileTesting.newQualityProfileDto;
57 import static org.sonar.server.language.LanguageTesting.newLanguage;
58 import static org.sonar.test.JsonAssert.assertJson;
59 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_DEFAULTS;
60 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
61 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT;
62 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY;
63 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
64
65 public class SearchActionTest {
66
67   private static final Language XOO1 = newLanguage("xoo1");
68   private static final Language XOO2 = newLanguage("xoo2");
69   private static final Languages LANGUAGES = new Languages(XOO1, XOO2);
70
71   @Rule
72   public DbTester db = DbTester.create(System2.INSTANCE);
73   @Rule
74   public UserSessionRule userSession = UserSessionRule.standalone();
75   private final QualityProfileDbTester qualityProfileDb = db.qualityProfiles();
76   private final DbClient dbClient = db.getDbClient();
77
78   private SearchAction underTest = new SearchAction(userSession, LANGUAGES, dbClient, new ComponentFinder(dbClient, null));
79   private WsActionTester ws = new WsActionTester(underTest);
80
81   @Test
82   public void no_profile() {
83     SearchWsResponse result = call(ws.newRequest());
84
85     assertThat(result.getProfilesList()).isEmpty();
86   }
87
88   @Test
89   public void empty_when_no_language_installed() {
90     WsActionTester ws = new WsActionTester(new SearchAction(userSession, new Languages(), dbClient, new ComponentFinder(dbClient, null)));
91     db.qualityProfiles().insert();
92
93     SearchWsResponse result = call(ws.newRequest());
94
95     assertThat(result.getProfilesList()).isEmpty();
96   }
97
98   @Test
99   public void filter_on_default_profile() {
100     QProfileDto defaultProfile1 = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
101     QProfileDto defaultProfile2 = db.qualityProfiles().insert(p -> p.setLanguage(XOO2.getKey()));
102     QProfileDto nonDefaultProfile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
103     db.qualityProfiles().setAsDefault(defaultProfile1, defaultProfile2);
104
105     SearchWsResponse result = call(ws.newRequest().setParam(PARAM_DEFAULTS, "true"));
106
107     assertThat(result.getProfilesList()).extracting(QualityProfile::getKey)
108       .containsExactlyInAnyOrder(defaultProfile1.getKee(), defaultProfile2.getKee())
109       .doesNotContain(nonDefaultProfile.getKee());
110   }
111
112   @Test
113   public void does_not_filter_when_defaults_is_false() {
114     QProfileDto defaultProfile1 = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
115     QProfileDto defaultProfile2 = db.qualityProfiles().insert(p -> p.setLanguage(XOO2.getKey()));
116     QProfileDto nonDefaultProfile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
117     db.qualityProfiles().setAsDefault(defaultProfile1, defaultProfile2);
118
119     SearchWsResponse result = call(ws.newRequest().setParam(PARAM_DEFAULTS, "false"));
120
121     assertThat(result.getProfilesList()).extracting(QualityProfile::getKey)
122       .containsExactlyInAnyOrder(defaultProfile1.getKee(), defaultProfile2.getKee(), nonDefaultProfile.getKee());
123   }
124
125   @Test
126   public void filter_on_language() {
127     QProfileDto profile1OnXoo1 = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
128     QProfileDto profile2OnXoo1 = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
129     QProfileDto profileOnXoo2 = db.qualityProfiles().insert(p -> p.setLanguage(XOO2.getKey()));
130
131     SearchWsResponse result = call(ws.newRequest().setParam(PARAM_LANGUAGE, XOO1.getKey()));
132
133     assertThat(result.getProfilesList()).extracting(QualityProfile::getKey)
134       .containsExactlyInAnyOrder(profile1OnXoo1.getKee(), profile2OnXoo1.getKee())
135       .doesNotContain(profileOnXoo2.getKee());
136   }
137
138   @Test
139   public void ignore_profiles_on_unknown_language() {
140     QProfileDto profile1OnXoo1 = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
141     QProfileDto profile2OnXoo1 = db.qualityProfiles().insert(p -> p.setLanguage(XOO2.getKey()));
142     QProfileDto profileOnUnknownLanguage = db.qualityProfiles().insert(p -> p.setLanguage("unknown"));
143
144     SearchWsResponse result = call(ws.newRequest());
145
146     assertThat(result.getProfilesList()).extracting(QualityProfile::getKey)
147       .containsExactlyInAnyOrder(profile1OnXoo1.getKee(), profile2OnXoo1.getKee())
148       .doesNotContain(profileOnUnknownLanguage.getKee());
149   }
150
151   @Test
152   public void filter_on_profile_name() {
153     QProfileDto sonarWayOnXoo1 = db.qualityProfiles().insert(p -> p.setName("Sonar way").setLanguage(XOO1.getKey()));
154     QProfileDto sonarWayOnXoo2 = db.qualityProfiles().insert(p -> p.setName("Sonar way").setLanguage(XOO1.getKey()));
155     QProfileDto sonarWayInCamelCase = db.qualityProfiles().insert(p -> p.setName("Sonar Way").setLanguage(XOO2.getKey()));
156     QProfileDto anotherProfile = db.qualityProfiles().insert(p -> p.setName("Another").setLanguage(XOO2.getKey()));
157
158     SearchWsResponse result = call(ws.newRequest().setParam(PARAM_QUALITY_PROFILE, "Sonar way"));
159
160     assertThat(result.getProfilesList()).extracting(QualityProfile::getKey)
161       .containsExactlyInAnyOrder(sonarWayOnXoo1.getKee(), sonarWayOnXoo2.getKee())
162       .doesNotContain(anotherProfile.getKee(), sonarWayInCamelCase.getKee());
163   }
164
165   @Test
166   public void filter_on_defaults_and_name() {
167     QProfileDto sonarWayOnXoo1 = db.qualityProfiles().insert(p -> p.setName("Sonar way").setLanguage(XOO1.getKey()));
168     QProfileDto sonarWayOnXoo2 = db.qualityProfiles().insert(p -> p.setName("Sonar way").setLanguage(XOO2.getKey()));
169     QProfileDto anotherProfile = db.qualityProfiles().insert(p -> p.setName("Another").setLanguage(XOO2.getKey()));
170     db.qualityProfiles().setAsDefault(sonarWayOnXoo1, anotherProfile);
171
172     SearchWsResponse result = call(ws.newRequest()
173       .setParam(PARAM_DEFAULTS, "true")
174       .setParam(PARAM_QUALITY_PROFILE, "Sonar way"));
175
176     assertThat(result.getProfilesList()).extracting(QualityProfile::getKey)
177       .containsExactlyInAnyOrder(sonarWayOnXoo1.getKee())
178       .doesNotContain(sonarWayOnXoo2.getKee(), anotherProfile.getKee());
179   }
180
181   @Test
182   public void filter_on_project_key() {
183     ProjectDto project = db.components().insertPrivateProjectDto();
184     QProfileDto profileOnXoo1 = db.qualityProfiles().insert(q -> q.setLanguage(XOO1.getKey()));
185     QProfileDto defaultProfileOnXoo1 = db.qualityProfiles().insert(q -> q.setLanguage(XOO1.getKey()));
186     QProfileDto defaultProfileOnXoo2 = db.qualityProfiles().insert(q -> q.setLanguage(XOO2.getKey()));
187     db.qualityProfiles().associateWithProject(project, profileOnXoo1);
188     db.qualityProfiles().setAsDefault(defaultProfileOnXoo1, defaultProfileOnXoo2);
189
190     SearchWsResponse result = call(ws.newRequest().setParam(PARAM_PROJECT, project.getKey()));
191
192     assertThat(result.getProfilesList())
193       .extracting(QualityProfile::getKey)
194       .containsExactlyInAnyOrder(profileOnXoo1.getKee(), defaultProfileOnXoo2.getKee())
195       .doesNotContain(defaultProfileOnXoo1.getKee());
196   }
197
198   @Test
199   public void filter_on_project_key_and_default() {
200     ProjectDto project = db.components().insertPrivateProjectDto();
201     QProfileDto profileOnXoo1 = db.qualityProfiles().insert(q -> q.setLanguage(XOO1.getKey()));
202     QProfileDto defaultProfileOnXoo1 = db.qualityProfiles().insert(q -> q.setLanguage(XOO1.getKey()));
203     QProfileDto defaultProfileOnXoo2 = db.qualityProfiles().insert(q -> q.setLanguage(XOO2.getKey()));
204     db.qualityProfiles().associateWithProject(project, profileOnXoo1);
205     db.qualityProfiles().setAsDefault(defaultProfileOnXoo1, defaultProfileOnXoo2);
206
207     SearchWsResponse result = call(ws.newRequest()
208       .setParam(PARAM_PROJECT, project.getKey())
209       .setParam(PARAM_DEFAULTS, "true"));
210
211     assertThat(result.getProfilesList())
212       .extracting(QualityProfile::getKey)
213       .containsExactlyInAnyOrder(defaultProfileOnXoo2.getKee())
214       .doesNotContain(defaultProfileOnXoo1.getKee(), profileOnXoo1.getKee());
215   }
216
217   @Test
218   public void filter_on_deprecated_project_key_and_default() {
219     ProjectDto project = db.components().insertPrivateProjectDto();
220     QProfileDto profileOnXoo1 = db.qualityProfiles().insert(q -> q.setLanguage(XOO1.getKey()));
221     QProfileDto defaultProfileOnXoo1 = db.qualityProfiles().insert(q -> q.setLanguage(XOO1.getKey()));
222     QProfileDto defaultProfileOnXoo2 = db.qualityProfiles().insert(q -> q.setLanguage(XOO2.getKey()));
223     db.qualityProfiles().associateWithProject(project, profileOnXoo1);
224     db.qualityProfiles().setAsDefault(defaultProfileOnXoo1, defaultProfileOnXoo2);
225
226     SearchWsResponse result = call(ws.newRequest()
227       .setParam(PARAM_PROJECT_KEY, project.getKey())
228       .setParam(PARAM_DEFAULTS, "true"));
229
230     assertThat(result.getProfilesList())
231       .extracting(QualityProfile::getKey)
232       .containsExactlyInAnyOrder(defaultProfileOnXoo2.getKee())
233       .doesNotContain(defaultProfileOnXoo1.getKee(), profileOnXoo1.getKee());
234   }
235
236   @Test
237   public void empty_when_filtering_on_project_and_no_language_installed() {
238     WsActionTester ws = new WsActionTester(new SearchAction(userSession, new Languages(), dbClient, new ComponentFinder(dbClient, null)));
239     db.qualityProfiles().insert();
240     ProjectDto project = db.components().insertPrivateProjectDto();
241     QProfileDto profileOnXoo1 = db.qualityProfiles().insert(q -> q.setLanguage(XOO1.getKey()));
242     db.qualityProfiles().associateWithProject(project, profileOnXoo1);
243
244     SearchWsResponse result = call(ws.newRequest()
245       .setParam(PARAM_PROJECT, project.getKey())
246       .setParam(PARAM_DEFAULTS, "true"));
247
248     assertThat(result.getProfilesList()).isEmpty();
249   }
250
251   @Test
252   public void actions_when_user_is_global_qprofile_administer() {
253     QProfileDto customProfile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
254     QProfileDto builtInProfile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()).setIsBuiltIn(true));
255     QProfileDto defaultProfile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
256     db.qualityProfiles().setAsDefault(defaultProfile);
257     UserDto user = db.users().insertUser();
258     userSession.logIn(user).addPermission(GlobalPermission.ADMINISTER_QUALITY_PROFILES);
259
260     SearchWsResponse result = call(ws.newRequest());
261
262     assertThat(result.getProfilesList())
263       .extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(),
264         qp -> qp.getActions().getDelete(), qp -> qp.getActions().getAssociateProjects())
265       .containsExactlyInAnyOrder(
266         tuple(customProfile.getKee(), true, true, true, true, true),
267         tuple(builtInProfile.getKee(), false, true, true, false, true),
268         tuple(defaultProfile.getKee(), true, true, false, false, false));
269     assertThat(result.getActions().getCreate()).isTrue();
270   }
271
272   @Test
273   public void actions_when_user_can_edit_profile() {
274     QProfileDto profile1 = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
275     QProfileDto profile2 = db.qualityProfiles().insert(p -> p.setLanguage(XOO2.getKey()));
276     QProfileDto profile3 = db.qualityProfiles().insert(p -> p.setLanguage(XOO2.getKey()));
277     QProfileDto builtInProfile = db.qualityProfiles().insert(p -> p.setLanguage(XOO2.getKey()).setIsBuiltIn(true));
278     UserDto user = db.users().insertUser();
279     GroupDto group = db.users().insertGroup();
280     db.qualityProfiles().addUserPermission(profile1, user);
281     db.qualityProfiles().addGroupPermission(profile3, group);
282     userSession.logIn(user).setGroups(group);
283
284     SearchWsResponse result = call(ws.newRequest());
285
286     assertThat(result.getProfilesList())
287       .extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(),
288         qp -> qp.getActions().getDelete(), qp -> qp.getActions().getAssociateProjects())
289       .containsExactlyInAnyOrder(
290         tuple(profile1.getKee(), true, false, false, true, true),
291         tuple(profile2.getKee(), false, false, false, false, false),
292         tuple(profile3.getKee(), true, false, false, true, true),
293         tuple(builtInProfile.getKee(), false, false, false, false, false));
294     assertThat(result.getActions().getCreate()).isFalse();
295   }
296
297   @Test
298   public void actions_when_not_logged_in() {
299     QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
300     userSession.anonymous();
301
302     SearchWsResponse result = call(ws.newRequest());
303
304     assertThat(result.getProfilesList())
305       .extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(),
306         qp -> qp.getActions().getDelete(), qp -> qp.getActions().getAssociateProjects())
307       .containsExactlyInAnyOrder(tuple(profile.getKee(), false, false, false, false, false));
308     assertThat(result.getActions().getCreate()).isFalse();
309   }
310
311   @Test
312   public void statistics_on_active_rules() {
313     QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
314     RuleDefinitionDto rule = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition();
315     RuleDefinitionDto deprecatedRule1 = db.rules().insertRule(r -> r.setStatus(DEPRECATED)).getDefinition();
316     RuleDefinitionDto deprecatedRule2 = db.rules().insertRule(r -> r.setStatus(DEPRECATED)).getDefinition();
317     RuleDefinitionDto inactiveRule = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition();
318     db.qualityProfiles().activateRule(profile, rule);
319     db.qualityProfiles().activateRule(profile, deprecatedRule1);
320     db.qualityProfiles().activateRule(profile, deprecatedRule2);
321
322     SearchWsResponse result = call(ws.newRequest());
323
324     assertThat(result.getProfilesList())
325       .extracting(QualityProfile::getActiveRuleCount, QualityProfile::getActiveDeprecatedRuleCount)
326       .containsExactlyInAnyOrder(tuple(3L, 2L));
327   }
328
329   @Test
330   public void statistics_on_projects() {
331     ProjectDto project1 = db.components().insertPrivateProjectDto();
332     ProjectDto project2 = db.components().insertPrivateProjectDto();
333     QProfileDto profileOnXoo1 = db.qualityProfiles().insert(q -> q.setLanguage(XOO1.getKey()));
334     QProfileDto defaultProfileOnXoo1 = db.qualityProfiles().insert(q -> q.setLanguage(XOO1.getKey()));
335     db.qualityProfiles().associateWithProject(project1, profileOnXoo1);
336     db.qualityProfiles().associateWithProject(project2, profileOnXoo1);
337     db.qualityProfiles().setAsDefault(defaultProfileOnXoo1);
338
339     SearchWsResponse result = call(ws.newRequest());
340
341     assertThat(result.getProfilesList())
342       .extracting(QualityProfile::hasProjectCount, QualityProfile::getProjectCount)
343       .containsExactlyInAnyOrder(tuple(true, 2L), tuple(false, 0L));
344   }
345
346   @Test
347   public void map_dates() {
348     long time = DateUtils.parseDateTime("2016-12-22T19:10:03+0100").getTime();
349     qualityProfileDb.insert(newQualityProfileDto()
350       .setLanguage(XOO1.getKey())
351       .setRulesUpdatedAt("2016-12-21T19:10:03+0100")
352       .setLastUsed(time)
353       .setUserUpdatedAt(time));
354
355     SearchWsResponse result = call(ws.newRequest());
356
357     assertThat(result.getProfilesCount()).isOne();
358     assertThat(result.getProfiles(0).getRulesUpdatedAt()).isEqualTo("2016-12-21T19:10:03+0100");
359     assertThat(parseDateTime(result.getProfiles(0).getLastUsed()).getTime()).isEqualTo(time);
360     assertThat(parseDateTime(result.getProfiles(0).getUserUpdatedAt()).getTime()).isEqualTo(time);
361   }
362
363   @Test
364   public void fail_if_project_does_not_exist() {
365     assertThatThrownBy(() -> {
366       call(ws.newRequest().setParam(PARAM_PROJECT, "unknown-project"));
367     })
368       .isInstanceOf(NotFoundException.class)
369       .hasMessage("Project 'unknown-project' not found");
370   }
371
372   @Test
373   public void json_example() {
374     // languages
375     Language cs = newLanguage("cs", "C#");
376     Language java = newLanguage("java", "Java");
377     Language python = newLanguage("py", "Python");
378     // profiles
379     QProfileDto sonarWayCs = db.qualityProfiles().insert(
380       p -> p.setName("Sonar way").setKee("AU-TpxcA-iU5OvuD2FL3").setIsBuiltIn(true).setLanguage(cs.getKey()));
381     QProfileDto myCompanyProfile = db.qualityProfiles().insert(p -> p.setName("My Company Profile").setKee("iU5OvuD2FLz").setLanguage(java.getKey()));
382     QProfileDto myBuProfile = db.qualityProfiles().insert(
383       p -> p.setName("My BU Profile").setKee("AU-TpxcA-iU5OvuD2FL1").setParentKee(myCompanyProfile.getKee()).setLanguage(java.getKey()));
384     QProfileDto sonarWayPython = db.qualityProfiles().insert(
385       p -> p.setName("Sonar way").setKee("AU-TpxcB-iU5OvuD2FL7").setIsBuiltIn(true).setLanguage(python.getKey()));
386     db.qualityProfiles().setAsDefault(sonarWayCs, myCompanyProfile, sonarWayPython);
387     // rules
388     List<RuleDefinitionDto> javaRules = range(0, 10).mapToObj(i -> db.rules().insertRule(r -> r.setLanguage(java.getKey())).getDefinition())
389       .collect(MoreCollectors.toList());
390     List<RuleDefinitionDto> deprecatedJavaRules = range(0, 5)
391       .mapToObj(i -> db.rules().insertRule(r -> r.setLanguage(java.getKey()).setStatus(DEPRECATED)).getDefinition())
392       .collect(MoreCollectors.toList());
393     range(0, 7).forEach(i -> db.qualityProfiles().activateRule(myCompanyProfile, javaRules.get(i)));
394     range(0, 2).forEach(i -> db.qualityProfiles().activateRule(myCompanyProfile, deprecatedJavaRules.get(i)));
395     range(0, 10).forEach(i -> db.qualityProfiles().activateRule(myBuProfile, javaRules.get(i)));
396     range(0, 5).forEach(i -> db.qualityProfiles().activateRule(myBuProfile, deprecatedJavaRules.get(i)));
397     range(0, 2)
398       .mapToObj(i -> db.rules().insertRule(r -> r.setLanguage(python.getKey())).getDefinition())
399       .forEach(rule -> db.qualityProfiles().activateRule(sonarWayPython, rule));
400     range(0, 3)
401       .mapToObj(i -> db.rules().insertRule(r -> r.setLanguage(cs.getKey())).getDefinition())
402       .forEach(rule -> db.qualityProfiles().activateRule(sonarWayCs, rule));
403     // project
404     range(0, 7)
405       .mapToObj(i -> db.components().insertPrivateProjectDto())
406       .forEach(project -> db.qualityProfiles().associateWithProject(project, myBuProfile));
407     // User
408     UserDto user = db.users().insertUser();
409     db.qualityProfiles().addUserPermission(myCompanyProfile, user);
410     db.qualityProfiles().addUserPermission(myBuProfile, user);
411     userSession.logIn(user);
412
413     underTest = new SearchAction(userSession, new Languages(cs, java, python), dbClient, new ComponentFinder(dbClient, null));
414     ws = new WsActionTester(underTest);
415     String result = ws.newRequest().execute().getInput();
416     assertJson(result).ignoreFields("ruleUpdatedAt", "lastUsed", "userUpdatedAt")
417       .isSimilarTo(ws.getDef().responseExampleAsString());
418   }
419
420   @Test
421   public void definition() {
422     WebService.Action definition = ws.getDef();
423
424     assertThat(definition.key()).isEqualTo("search");
425     assertThat(definition.responseExampleAsString()).isNotEmpty();
426     assertThat(definition.isPost()).isFalse();
427
428     assertThat(definition.changelog())
429       .extracting(Change::getVersion, Change::getDescription)
430       .containsExactlyInAnyOrder(
431         tuple("6.5", "The parameters 'defaults', 'project' and 'language' can be combined without any constraint"),
432         tuple("6.6", "Add available actions 'edit', 'copy' and 'setAsDefault' and global action 'create'"),
433         tuple("7.0", "Add available actions 'delete' and 'associateProjects'"));
434
435     WebService.Param defaults = definition.param("defaults");
436     assertThat(defaults.defaultValue()).isEqualTo("false");
437     assertThat(defaults.description()).isEqualTo("If set to true, return only the quality profiles marked as default for each language");
438
439     WebService.Param projectKey = definition.param("project");
440     assertThat(projectKey.description()).isEqualTo("Project key");
441     assertThat(projectKey.deprecatedKey()).isEqualTo("projectKey");
442
443     WebService.Param language = definition.param("language");
444     assertThat(language.possibleValues()).containsExactly("xoo1", "xoo2");
445     assertThat(language.deprecatedSince()).isNull();
446     assertThat(language.description()).isEqualTo("Language key. If provided, only profiles for the given language are returned.");
447
448     WebService.Param profileName = definition.param("qualityProfile");
449     assertThat(profileName.deprecatedSince()).isNull();
450     assertThat(profileName.description()).isEqualTo("Quality profile name");
451   }
452
453   private SearchWsResponse call(TestRequest request) {
454     TestRequest wsRequest = request.setMediaType(MediaTypes.PROTOBUF);
455
456     return wsRequest.executeProtobuf(SearchWsResponse.class);
457   }
458 }