3 * Copyright (C) 2009-2021 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.projectanalysis.ws;
22 import com.tngtech.java.junit.dataprovider.DataProvider;
23 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
24 import com.tngtech.java.junit.dataprovider.UseDataProvider;
25 import java.util.Date;
26 import java.util.List;
27 import java.util.function.Function;
28 import java.util.stream.IntStream;
29 import javax.annotation.Nullable;
30 import org.junit.Rule;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.sonar.api.server.ws.WebService;
34 import org.sonar.api.server.ws.WebService.Param;
35 import org.sonar.api.utils.log.LogAndArguments;
36 import org.sonar.api.utils.log.LogTester;
37 import org.sonar.api.utils.log.LoggerLevel;
38 import org.sonar.api.web.UserRole;
39 import org.sonar.core.config.CorePropertyDefinitions;
40 import org.sonar.core.util.UuidFactoryFast;
41 import org.sonar.db.DbClient;
42 import org.sonar.db.DbTester;
43 import org.sonar.db.component.AnalysisPropertyDto;
44 import org.sonar.db.component.BranchDto;
45 import org.sonar.db.component.ComponentDto;
46 import org.sonar.db.component.ComponentTesting;
47 import org.sonar.db.component.SnapshotDto;
48 import org.sonar.db.event.EventComponentChangeDto;
49 import org.sonar.db.event.EventComponentChangeDto.ChangeCategory;
50 import org.sonar.db.event.EventDto;
51 import org.sonar.db.event.EventPurgeData;
52 import org.sonar.db.newcodeperiod.NewCodePeriodDto;
53 import org.sonar.db.newcodeperiod.NewCodePeriodType;
54 import org.sonar.server.component.TestComponentFinder;
55 import org.sonar.server.exceptions.ForbiddenException;
56 import org.sonar.server.exceptions.NotFoundException;
57 import org.sonar.server.tester.UserSessionRule;
58 import org.sonar.server.ws.TestRequest;
59 import org.sonar.server.ws.WsActionTester;
60 import org.sonarqube.ws.Common.Paging;
61 import org.sonarqube.ws.ProjectAnalyses.Analysis;
62 import org.sonarqube.ws.ProjectAnalyses.Event;
63 import org.sonarqube.ws.ProjectAnalyses.Project;
64 import org.sonarqube.ws.ProjectAnalyses.SearchResponse;
66 import static java.lang.String.format;
67 import static java.util.Optional.ofNullable;
68 import static org.assertj.core.api.Assertions.assertThat;
69 import static org.assertj.core.api.Assertions.assertThatThrownBy;
70 import static org.assertj.core.api.Assertions.tuple;
71 import static org.sonar.api.utils.DateUtils.formatDate;
72 import static org.sonar.api.utils.DateUtils.formatDateTime;
73 import static org.sonar.api.utils.DateUtils.parseDateTime;
74 import static org.sonar.db.component.BranchType.BRANCH;
75 import static org.sonar.db.component.ComponentTesting.newBranchDto;
76 import static org.sonar.db.component.ComponentTesting.newFileDto;
77 import static org.sonar.db.component.SnapshotTesting.newAnalysis;
78 import static org.sonar.db.event.EventComponentChangeDto.ChangeCategory.ADDED;
79 import static org.sonar.db.event.EventComponentChangeDto.ChangeCategory.FAILED_QUALITY_GATE;
80 import static org.sonar.db.event.EventComponentChangeDto.ChangeCategory.REMOVED;
81 import static org.sonar.db.event.EventDto.CATEGORY_ALERT;
82 import static org.sonar.db.event.EventTesting.newEvent;
83 import static org.sonar.server.projectanalysis.ws.EventCategory.DEFINITION_CHANGE;
84 import static org.sonar.server.projectanalysis.ws.EventCategory.OTHER;
85 import static org.sonar.server.projectanalysis.ws.EventCategory.QUALITY_GATE;
86 import static org.sonar.server.projectanalysis.ws.EventCategory.VERSION;
87 import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_BRANCH;
88 import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_CATEGORY;
89 import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_FROM;
90 import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_PROJECT;
91 import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_TO;
92 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
93 import static org.sonar.test.JsonAssert.assertJson;
94 import static org.sonarqube.ws.client.WsRequest.Method.POST;
96 @RunWith(DataProviderRunner.class)
97 public class SearchActionTest {
100 public UserSessionRule userSession = UserSessionRule.standalone();
102 public DbTester db = DbTester.create();
104 public LogTester logTester = new LogTester();
106 private final DbClient dbClient = db.getDbClient();
107 private final WsActionTester ws = new WsActionTester(new SearchAction(dbClient, TestComponentFinder.from(db), userSession));
108 private final UuidFactoryFast uuidFactoryFast = UuidFactoryFast.getInstance();
111 public static Object[][] changedBranches() {
112 return new Object[][] {
114 {"newbranch", "anotherbranch"},
120 public void json_example() {
121 ComponentDto project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto().setDbKey(KEY_PROJECT_EXAMPLE_001));
123 userSession.addProjectPermission(UserRole.USER, project);
124 SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project)
126 .setCreatedAt(parseDateTime("2016-12-11T17:12:45+0100").getTime())
127 .setProjectVersion("1.2")
128 .setBuildString("1.2.0.322")
129 .setRevision("bfe36592eb7f9f2708b5d358b5b5f33ed535c8cf"));
130 SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project)
132 .setCreatedAt(parseDateTime("2016-12-12T17:12:45+0100").getTime())
133 .setProjectVersion("1.2.1")
134 .setBuildString("1.2.1.423")
135 .setRevision("be6c75b85da526349c44e3978374c95e0b80a96d"));
136 SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project)
138 .setCreatedAt(parseDateTime("2015-11-11T10:00:00+0100").getTime())
139 .setProjectVersion("1.2")
140 .setBuildString("1.2.0.321"));
141 db.getDbClient().analysisPropertiesDao().insert(db.getSession(), new AnalysisPropertyDto()
142 .setUuid("P1-prop-uuid")
143 .setAnalysisUuid(a3.getUuid())
144 .setKey(CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDCI)
147 BranchDto branchDto = newBranchDto(project, BRANCH);
148 db.getDbClient().branchDao().insert(db.getSession(), branchDto);
149 db.newCodePeriods().insert(new NewCodePeriodDto()
150 .setProjectUuid(project.uuid())
151 .setBranchUuid(branchDto.getUuid())
152 .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
153 .setValue(a1.getUuid()));
155 db.events().insertEvent(newEvent(a1).setUuid("E11")
156 .setName("Quality Gate is Red (was Orange)")
157 .setCategory(QUALITY_GATE.getLabel())
158 .setDescription("Coverage is < 80%"));
159 db.events().insertEvent(newEvent(a1).setUuid("E12")
160 .setName("6.3").setCategory(VERSION.getLabel()));
161 db.events().insertEvent(newEvent(a2).setUuid("E21")
162 .setName("Quality Profile changed to Sonar Way")
163 .setCategory(EventCategory.QUALITY_PROFILE.getLabel()));
164 db.events().insertEvent(newEvent(a2).setUuid("E22")
165 .setName("6.3").setCategory(OTHER.getLabel()));
167 EventDto eventDto = db.events().insertEvent(newEvent(a3)
169 .setName("Quality Gate is Red")
170 .setData("{stillFailing: true, status: \"ERROR\"}")
171 .setCategory(CATEGORY_ALERT)
172 .setDescription(""));
173 EventComponentChangeDto changeDto1 = generateEventComponentChange(eventDto, FAILED_QUALITY_GATE, "My project", "app1", "master", project.uuid());
174 EventComponentChangeDto changeDto2 = generateEventComponentChange(eventDto, FAILED_QUALITY_GATE, "Another project", "app2", "master", uuidFactoryFast.create());
175 insertEventComponentChanges(project, a3, changeDto1, changeDto2);
177 String result = ws.newRequest()
178 .setParam(PARAM_PROJECT, KEY_PROJECT_EXAMPLE_001)
179 .execute().getInput();
181 assertJson(result).isSimilarTo(getClass().getResource("search-example.json"));
185 public void return_analyses_ordered_by_analysis_date() {
186 ComponentDto project = db.components().insertPrivateProject();
187 userSession.addProjectPermission(UserRole.USER, project);
188 db.components().insertSnapshot(newAnalysis(project).setUuid("A1").setCreatedAt(1_000_000L));
189 db.components().insertSnapshot(newAnalysis(project).setUuid("A2").setCreatedAt(2_000_000L));
190 db.components().insertSnapshot(newAnalysis(project).setUuid("A3").setCreatedAt(3_000_000L));
192 List<Analysis> result = call(project.getKey()).getAnalysesList();
194 assertThat(result).hasSize(3);
195 assertThat(result).extracting(Analysis::getKey, a -> parseDateTime(a.getDate()).getTime()).containsExactly(
196 tuple("A3", 3_000_000L),
197 tuple("A2", 2_000_000L),
198 tuple("A1", 1_000_000L));
202 public void return_only_processed_analyses() {
203 ComponentDto project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto().setDbKey("P1"));
204 userSession.addProjectPermission(UserRole.USER, project);
205 db.components().insertSnapshot(newAnalysis(project).setUuid("A1"));
206 db.components().insertSnapshot(newAnalysis(project).setUuid("A2").setStatus(SnapshotDto.STATUS_UNPROCESSED));
208 List<Analysis> result = call("P1").getAnalysesList();
210 assertThat(result).hasSize(1);
211 assertThat(result.get(0).getKey()).isEqualTo("A1");
215 public void return_events() {
216 ComponentDto project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto().setDbKey("P1"));
217 userSession.addProjectPermission(UserRole.USER, project);
218 SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("A1"));
219 SnapshotDto a42 = db.components().insertSnapshot(newAnalysis(ComponentTesting.newPrivateProjectDto()).setUuid("A42"));
220 EventDto e1 = db.events().insertEvent(newEvent(a1).setUuid("E1").setName("N1").setCategory(QUALITY_GATE.getLabel()).setDescription("D1"));
221 EventDto e2 = db.events().insertEvent(newEvent(a1).setUuid("E2").setName("N2").setCategory(VERSION.getLabel()).setDescription("D2"));
222 db.events().insertEvent(newEvent(a42));
224 List<Analysis> result = call("P1").getAnalysesList();
226 assertThat(result).hasSize(1);
227 List<Event> events = result.get(0).getEventsList();
228 assertThat(events).hasSize(2);
229 assertThat(events).extracting(Event::getKey, wsToDbCategory(), Event::getName, Event::getDescription).containsOnly(
230 tuple(e1.getUuid(), e1.getCategory(), e1.getName(), e1.getDescription()),
231 tuple(e2.getUuid(), e2.getCategory(), e2.getName(), e2.getDescription()));
235 public void return_analyses_of_application() {
236 ComponentDto application = db.components().insertPublicApplication();
237 userSession.registerComponents(application);
238 SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
239 SnapshotDto secondAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(2_000_000L));
240 SnapshotDto thirdAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(3_000_000L));
242 List<Analysis> result = call(application.getDbKey()).getAnalysesList();
246 .extracting(Analysis::getKey).containsExactly(thirdAnalysis.getUuid(), secondAnalysis.getUuid(), firstAnalysis.getUuid());
248 assertThat(result.get(0).getEventsList()).isEmpty();
249 assertThat(result.get(1).getEventsList()).isEmpty();
250 assertThat(result.get(2).getEventsList()).isEmpty();
254 public void return_definition_change_events_on_application_analyses() {
255 ComponentDto application = db.components().insertPublicApplication();
256 userSession.registerComponents(application);
257 SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
258 EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
259 EventComponentChangeDto changeDto1 = generateEventComponentChange(event, ADDED, "My project", "app1", "master", uuidFactoryFast.create());
260 EventComponentChangeDto changeDto2 = generateEventComponentChange(event, REMOVED, "Another project", "app2", "master", uuidFactoryFast.create());
261 insertEventComponentChanges(application, firstAnalysis, changeDto1, changeDto2);
263 List<Analysis> result = call(application.getDbKey()).getAnalysesList();
265 assertThat(result).hasSize(1);
266 List<Event> events = result.get(0).getEventsList();
268 .extracting(Event::getName, Event::getCategory, Event::getKey)
269 .containsExactly(tuple("", DEFINITION_CHANGE.name(), "E11"));
270 assertThat(events.get(0).getDefinitionChange().getProjectsList())
271 .extracting(Project::getChangeType, Project::getName, Project::getKey, Project::getNewBranch, Project::getOldBranch)
273 tuple("ADDED", "My project", "app1", "", ""),
274 tuple("REMOVED", "Another project", "app2", "", ""));
278 @UseDataProvider("changedBranches")
279 public void application_definition_change_with_branch(@Nullable String oldBranch, @Nullable String newBranch) {
280 ComponentDto application = db.components().insertPublicApplication();
281 userSession.registerComponents(application);
282 SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
283 EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
284 EventComponentChangeDto changeDto1 = generateEventComponentChange(event, REMOVED, "My project", "app1", oldBranch, uuidFactoryFast.create());
285 EventComponentChangeDto changeDto2 = generateEventComponentChange(event, ADDED, "My project", "app1", newBranch, changeDto1.getComponentUuid());
286 insertEventComponentChanges(application, firstAnalysis, changeDto1, changeDto2);
288 List<Analysis> result = call(application.getDbKey()).getAnalysesList();
290 assertThat(result).hasSize(1);
291 List<Event> events = result.get(0).getEventsList();
293 .extracting(Event::getName, Event::getCategory, Event::getKey)
294 .containsExactly(tuple("", DEFINITION_CHANGE.name(), "E11"));
295 assertThat(events.get(0).getDefinitionChange().getProjectsList())
296 .extracting(Project::getChangeType, Project::getKey, Project::getName, Project::getNewBranch, Project::getOldBranch)
297 .containsExactly(tuple("BRANCH_CHANGED", "app1", "My project", newBranch == null ? "" : newBranch, oldBranch == null ? "" : oldBranch));
301 public void incorrect_eventcomponentchange_two_identical_changes_added_on_same_project() {
302 ComponentDto application = db.components().insertPublicApplication();
303 userSession.registerComponents(application);
304 SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
305 EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
306 EventComponentChangeDto changeDto1 = generateEventComponentChange(event, ADDED, "My project", "app1", "master", uuidFactoryFast.create());
307 EventComponentChangeDto changeDto2 = generateEventComponentChange(event, ADDED, "My project", "app1", "master", uuidFactoryFast.create());
308 EventPurgeData eventPurgeData = new EventPurgeData(application.uuid(), firstAnalysis.getUuid());
309 db.getDbClient().eventComponentChangeDao().insert(db.getSession(), changeDto1, eventPurgeData);
310 db.getDbClient().eventComponentChangeDao().insert(db.getSession(), changeDto2, eventPurgeData);
311 db.getSession().commit();
313 List<Analysis> result = call(application.getDbKey()).getAnalysesList();
315 assertThat(result).hasSize(1);
316 List<Event> events = result.get(0).getEventsList();
318 .extracting(Event::getName, Event::getCategory, Event::getKey)
319 .containsExactly(tuple("", DEFINITION_CHANGE.name(), "E11"));
320 assertThat(events.get(0).getDefinitionChange().getProjectsList())
323 assertThat(logTester.getLogs(LoggerLevel.ERROR))
324 .extracting(LogAndArguments::getFormattedMsg)
326 format("Incorrect changes : [uuid=%s change=ADDED, branch=master] and [uuid=%s, change=ADDED, branch=master]", changeDto1.getUuid(), changeDto2.getUuid()));
330 public void incorrect_eventcomponentchange_incorrect_category() {
331 ComponentDto application = db.components().insertPublicApplication();
332 userSession.registerComponents(application);
333 SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
334 EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
335 EventComponentChangeDto changeDto1 = generateEventComponentChange(event, FAILED_QUALITY_GATE, "My project", "app1", "master", uuidFactoryFast.create());
336 EventPurgeData eventPurgeData = new EventPurgeData(application.uuid(), firstAnalysis.getUuid());
337 db.getDbClient().eventComponentChangeDao().insert(db.getSession(), changeDto1, eventPurgeData);
338 db.getSession().commit();
340 List<Analysis> result = call(application.getDbKey()).getAnalysesList();
342 assertThat(result).hasSize(1);
343 List<Event> events = result.get(0).getEventsList();
345 .extracting(Event::getName, Event::getCategory, Event::getKey)
346 .containsExactly(tuple("", DEFINITION_CHANGE.name(), "E11"));
347 assertThat(events.get(0).getDefinitionChange().getProjectsList())
350 assertThat(logTester.getLogs(LoggerLevel.ERROR))
351 .extracting(LogAndArguments::getFormattedMsg)
352 .containsExactly("Unknown change FAILED_QUALITY_GATE for eventComponentChange uuid: " + changeDto1.getUuid());
356 public void incorrect_eventcomponentchange_three_component_changes_on_same_project() {
357 ComponentDto application = db.components().insertPublicApplication();
358 userSession.registerComponents(application);
359 SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
360 EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
361 EventComponentChangeDto changeDto1 = generateEventComponentChange(event, ADDED, "My project", "app1", "master", uuidFactoryFast.create());
362 EventComponentChangeDto changeDto2 = generateEventComponentChange(event, REMOVED, "Another project", "app1", "", uuidFactoryFast.create());
363 EventComponentChangeDto changeDto3 = generateEventComponentChange(event, REMOVED, "Another project", "app1", "", uuidFactoryFast.create());
364 EventPurgeData eventPurgeData = new EventPurgeData(application.uuid(), firstAnalysis.getUuid());
365 db.getDbClient().eventComponentChangeDao().insert(db.getSession(), changeDto1, eventPurgeData);
366 db.getDbClient().eventComponentChangeDao().insert(db.getSession(), changeDto2, eventPurgeData);
367 db.getDbClient().eventComponentChangeDao().insert(db.getSession(), changeDto3, eventPurgeData);
368 db.getSession().commit();
370 List<Analysis> result = call(application.getDbKey()).getAnalysesList();
372 assertThat(result).hasSize(1);
373 List<Event> events = result.get(0).getEventsList();
375 .extracting(Event::getName, Event::getCategory, Event::getKey)
376 .containsExactly(tuple("", DEFINITION_CHANGE.name(), "E11"));
377 assertThat(events.get(0).getDefinitionChange().getProjectsList())
380 assertThat(logTester.getLogs(LoggerLevel.ERROR))
381 .extracting(LogAndArguments::getFormattedMsg)
383 format("Too many changes on same project (3) for eventComponentChange uuids : %s,%s,%s", changeDto1.getUuid(), changeDto2.getUuid(), changeDto3.getUuid()));
387 public void incorrect_quality_gate_information() {
388 ComponentDto application = db.components().insertPublicApplication();
389 userSession.registerComponents(application);
390 SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
391 EventDto event = db.events().insertEvent(
392 newEvent(firstAnalysis)
395 .setCategory(CATEGORY_ALERT)
396 .setData("UNPARSEABLE JSON")); // Error in Data
397 EventComponentChangeDto changeDto1 = generateEventComponentChange(event, FAILED_QUALITY_GATE, "My project", "app1", "master", uuidFactoryFast.create());
398 EventPurgeData eventPurgeData = new EventPurgeData(application.uuid(), firstAnalysis.getUuid());
399 db.getDbClient().eventComponentChangeDao().insert(db.getSession(), changeDto1, eventPurgeData);
400 db.getSession().commit();
402 List<Analysis> result = call(application.getDbKey()).getAnalysesList();
404 assertThat(result).hasSize(1);
405 List<Event> events = result.get(0).getEventsList();
407 .extracting(Event::getName, Event::getCategory, Event::getKey)
408 .containsExactly(tuple("", QUALITY_GATE.name(), "E11"));
410 // Verify that the values are not populated
411 assertThat(events.get(0).getQualityGate().hasStatus()).isFalse();
412 assertThat(events.get(0).getQualityGate().hasStillFailing()).isFalse();
414 assertThat(logTester.getLogs(LoggerLevel.ERROR))
415 .extracting(LogAndArguments::getFormattedMsg)
416 .containsExactly("Unable to retrieve data from event uuid=E11");
420 public void return_analyses_of_portfolio() {
421 ComponentDto view = db.components().insertPublicPortfolio();
422 userSession.registerComponents(view);
423 SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(view).setCreatedAt(1_000_000L));
424 SnapshotDto secondAnalysis = db.components().insertSnapshot(newAnalysis(view).setCreatedAt(2_000_000L));
425 SnapshotDto thirdAnalysis = db.components().insertSnapshot(newAnalysis(view).setCreatedAt(3_000_000L));
427 List<Analysis> result = call(view.getDbKey()).getAnalysesList();
431 .extracting(Analysis::getKey).containsExactly(thirdAnalysis.getUuid(), secondAnalysis.getUuid(), firstAnalysis.getUuid());
435 public void paginate_analyses() {
436 ComponentDto project = db.components().insertPrivateProject();
437 userSession.addProjectPermission(UserRole.USER, project);
438 IntStream.rangeClosed(1, 9).forEach(i -> db.components().insertSnapshot(newAnalysis(project).setCreatedAt(1_000_000L * i).setUuid("A" + i)));
440 SearchResponse result = call(SearchRequest.builder()
441 .setProject(project.getDbKey())
446 assertThat(result.getAnalysesList()).extracting(Analysis::getKey)
447 .containsExactly("A6", "A5", "A4");
451 public void filter_by_category() {
452 ComponentDto project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto().setDbKey("P1"));
453 userSession.addProjectPermission(UserRole.USER, project);
454 SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("A1"));
455 SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("A2"));
456 SnapshotDto a42 = db.components().insertSnapshot(newAnalysis(project).setUuid("A42"));
457 db.events().insertEvent(newEvent(a1).setUuid("E11").setCategory(VERSION.getLabel()));
458 db.events().insertEvent(newEvent(a1).setUuid("E12").setCategory(QUALITY_GATE.getLabel()));
459 db.events().insertEvent(newEvent(a2).setUuid("E21").setCategory(QUALITY_GATE.getLabel()));
460 // Analysis A42 doesn't have a quality gate event
461 db.events().insertEvent(newEvent(a42).setCategory(OTHER.getLabel()));
463 List<Analysis> result = call(SearchRequest.builder()
465 .setCategory(QUALITY_GATE)
466 .build()).getAnalysesList();
468 assertThat(result).extracting(Analysis::getKey).containsOnly("A1", "A2");
472 public void paginate_with_filter_on_category() {
473 ComponentDto project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto().setDbKey("P1"));
474 userSession.addProjectPermission(UserRole.USER, project);
475 SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("A1").setCreatedAt(1_000_000L));
476 SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("A2").setCreatedAt(2_000_000L));
477 SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project).setUuid("A3").setCreatedAt(3_000_000L));
478 SnapshotDto a42 = db.components().insertSnapshot(newAnalysis(project).setUuid("A42"));
479 db.events().insertEvent(newEvent(a1).setUuid("E11").setCategory(VERSION.getLabel()));
480 db.events().insertEvent(newEvent(a1).setUuid("E12").setCategory(QUALITY_GATE.getLabel()));
481 db.events().insertEvent(newEvent(a2).setUuid("E21").setCategory(QUALITY_GATE.getLabel()));
482 db.events().insertEvent(newEvent(a3).setUuid("E31").setCategory(QUALITY_GATE.getLabel()));
483 // Analysis A42 doesn't have a quality gate event
484 db.events().insertEvent(newEvent(a42).setCategory(OTHER.getLabel()));
486 SearchResponse result = call(SearchRequest.builder()
488 .setCategory(QUALITY_GATE)
493 assertThat(result.getAnalysesList()).extracting(Analysis::getKey).containsOnly("A2");
494 assertThat(result.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal)
495 .containsExactly(2, 1, 3);
499 public void filter_from_date() {
500 ComponentDto project = db.components().insertPrivateProject();
501 userSession.addProjectPermission(UserRole.USER, project);
502 SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("a1").setCreatedAt(1_000_000_000L));
503 SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("a2").setCreatedAt(2_000_000_000L));
504 SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project).setUuid("a3").setCreatedAt(3_000_000_000L));
505 SnapshotDto a4 = db.components().insertSnapshot(newAnalysis(project).setUuid("a4").setCreatedAt(4_000_000_000L));
507 SearchResponse result = call(SearchRequest.builder()
508 .setProject(project.getDbKey())
509 .setFrom(formatDateTime(2_000_000_000L))
512 assertThat(result.getAnalysesList())
513 .extracting(Analysis::getKey)
514 .containsOnly(a2.getUuid(), a3.getUuid(), a4.getUuid())
515 .doesNotContain(a1.getUuid());
519 public void filter_to_date() {
520 ComponentDto project = db.components().insertPrivateProject();
521 userSession.addProjectPermission(UserRole.USER, project);
522 SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("a1").setCreatedAt(1_000_000_000L));
523 SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("a2").setCreatedAt(2_000_000_000L));
524 SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project).setUuid("a3").setCreatedAt(3_000_000_000L));
525 SnapshotDto a4 = db.components().insertSnapshot(newAnalysis(project).setUuid("a4").setCreatedAt(4_000_000_000L));
527 SearchResponse result = call(SearchRequest.builder()
528 .setProject(project.getDbKey())
529 .setTo(formatDateTime(2_000_000_000L))
532 assertThat(result.getAnalysesList())
533 .extracting(Analysis::getKey)
534 .containsOnly(a1.getUuid(), a2.getUuid())
535 .doesNotContain(a3.getUuid(), a4.getUuid());
539 public void filter_by_dates_using_datetime_format() {
540 ComponentDto project = db.components().insertPrivateProject();
541 userSession.addProjectPermission(UserRole.USER, project);
542 SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("a1").setCreatedAt(1_000_000_000L));
543 SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("a2").setCreatedAt(2_000_000_000L));
544 SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project).setUuid("a3").setCreatedAt(3_000_000_000L));
545 SnapshotDto a4 = db.components().insertSnapshot(newAnalysis(project).setUuid("a4").setCreatedAt(4_000_000_000L));
547 SearchResponse result = call(SearchRequest.builder()
548 .setProject(project.getDbKey())
549 .setFrom(formatDateTime(2_000_000_000L))
550 .setTo(formatDateTime(3_000_000_000L))
553 assertThat(result.getAnalysesList())
554 .extracting(Analysis::getKey)
555 .containsOnly(a2.getUuid(), a3.getUuid())
556 .doesNotContain(a1.getUuid(), a4.getUuid());
560 public void filter_by_dates_using_date_format() {
561 ComponentDto project = db.components().insertPrivateProject();
562 userSession.addProjectPermission(UserRole.USER, project);
563 SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project).setUuid("a1").setCreatedAt(1_000_000_000L));
564 SnapshotDto a2 = db.components().insertSnapshot(newAnalysis(project).setUuid("a2").setCreatedAt(2_000_000_000L));
565 SnapshotDto a3 = db.components().insertSnapshot(newAnalysis(project).setUuid("a3").setCreatedAt(3_000_000_000L));
566 SnapshotDto a4 = db.components().insertSnapshot(newAnalysis(project).setUuid("a4").setCreatedAt(4_000_000_000L));
568 SearchResponse result = call(SearchRequest.builder()
569 .setProject(project.getDbKey())
570 .setFrom(formatDate(new Date(2_000_000_000L)))
571 .setTo(formatDate(new Date(3_000_000_000L)))
574 assertThat(result.getAnalysesList())
575 .extracting(Analysis::getKey)
576 .containsOnly(a2.getUuid(), a3.getUuid())
577 .doesNotContain(a1.getUuid(), a4.getUuid());
581 public void branch() {
582 ComponentDto project = db.components().insertPrivateProject();
583 userSession.addProjectPermission(UserRole.USER, project);
584 ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
585 SnapshotDto analysis = db.components().insertSnapshot(newAnalysis(branch));
586 EventDto event = db.events().insertEvent(newEvent(analysis).setCategory(QUALITY_GATE.getLabel()));
588 List<Analysis> result = call(SearchRequest.builder()
589 .setProject(project.getKey())
590 .setBranch("my_branch")
594 assertThat(result).extracting(Analysis::getKey).containsExactlyInAnyOrder(analysis.getUuid());
595 assertThat(result.get(0).getEventsList()).extracting(Event::getKey).containsExactlyInAnyOrder(event.getUuid());
599 public void empty_response() {
600 ComponentDto project = db.components().insertPrivateProject();
601 userSession.addProjectPermission(UserRole.USER, project);
603 SearchResponse result = call(project.getDbKey());
605 assertThat(result.hasPaging()).isTrue();
606 assertThat(result.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsExactly(1, 100, 0);
607 assertThat(result.getAnalysesCount()).isZero();
611 public void populates_projectVersion_and_buildString() {
612 ComponentDto project = db.components().insertPrivateProject();
613 userSession.addProjectPermission(UserRole.USER, project);
614 SnapshotDto[] analyses = new SnapshotDto[] {
615 db.components().insertSnapshot(newAnalysis(project).setProjectVersion(null).setBuildString(null)),
616 db.components().insertSnapshot(newAnalysis(project).setProjectVersion("a").setBuildString(null)),
617 db.components().insertSnapshot(newAnalysis(project).setProjectVersion(null).setBuildString("b")),
618 db.components().insertSnapshot(newAnalysis(project).setProjectVersion("c").setBuildString("d"))
621 SearchResponse result = call(project.getDbKey());
623 assertThat(result.getAnalysesList())
624 .extracting(Analysis::getKey, Analysis::getProjectVersion, Analysis::getBuildString)
626 tuple(analyses[0].getUuid(), "", ""),
627 tuple(analyses[1].getUuid(), "a", ""),
628 tuple(analyses[2].getUuid(), "", "b"),
629 tuple(analyses[3].getUuid(), "c", "d"));
633 public void fail_if_not_enough_permissions() {
634 userSession.anonymous();
635 ComponentDto project = db.components().insertPrivateProject();
637 var projectDbKey = project.getDbKey();
638 assertThatThrownBy(() -> call(projectDbKey))
639 .isInstanceOf(ForbiddenException.class);
643 public void fail_if_project_does_not_exist() {
644 assertThatThrownBy(() -> call("P1"))
645 .isInstanceOf(NotFoundException.class);
649 public void fail_if_not_a_project_portfolio_or_application() {
650 ComponentDto project = db.components().insertPrivateProject();
651 ComponentDto file = db.components().insertComponent(newFileDto(project));
652 db.components().insertSnapshot(newAnalysis(project));
653 userSession.registerComponents(project, file);
655 var fileDbKey = file.getDbKey();
656 assertThatThrownBy(() -> call(fileDbKey))
657 .isInstanceOf(IllegalArgumentException.class)
658 .hasMessage("A project, portfolio or application is required");
662 public void fail_if_branch_does_not_exist() {
663 ComponentDto project = db.components().insertPrivateProject();
664 userSession.addProjectPermission(UserRole.USER, project);
665 db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
667 var searchRequest = SearchRequest.builder()
668 .setProject(project.getKey())
669 .setBranch("another_branch")
672 assertThatThrownBy(() -> call(searchRequest))
673 .isInstanceOf(NotFoundException.class)
674 .hasMessage(format("Component '%s' on branch '%s' not found", project.getKey(), "another_branch"));
678 public void definition() {
679 WebService.Action definition = ws.getDef();
681 assertThat(definition.key()).isEqualTo("search");
682 assertThat(definition.since()).isEqualTo("6.3");
683 assertThat(definition.responseExampleAsString()).isNotEmpty();
684 assertThat(definition.param("project").isRequired()).isTrue();
685 assertThat(definition.param("category")).isNotNull();
686 assertThat(definition.params()).hasSize(7);
688 Param from = definition.param("from");
689 assertThat(from.since()).isEqualTo("6.5");
691 Param to = definition.param("to");
692 assertThat(to.since()).isEqualTo("6.5");
694 Param branch = definition.param("branch");
695 assertThat(branch.since()).isEqualTo("6.6");
696 assertThat(branch.isInternal()).isTrue();
697 assertThat(branch.isRequired()).isFalse();
700 private EventComponentChangeDto generateEventComponentChange(EventDto event, ChangeCategory category, String name, String key, @Nullable String branch,
701 String componentUuid) {
702 return new EventComponentChangeDto()
703 .setCategory(category)
704 .setUuid(uuidFactoryFast.create())
705 .setComponentName(name)
706 .setComponentKey(key)
707 .setComponentBranchKey(branch)
708 .setComponentUuid(componentUuid)
709 .setEventUuid(event.getUuid());
712 private void insertEventComponentChanges(ComponentDto component, SnapshotDto analysis, EventComponentChangeDto... changes) {
713 EventPurgeData eventPurgeData = new EventPurgeData(component.uuid(), analysis.getUuid());
714 for (EventComponentChangeDto change : changes) {
715 db.getDbClient().eventComponentChangeDao().insert(db.getSession(), change, eventPurgeData);
717 db.getSession().commit();
720 private static Function<Event, String> wsToDbCategory() {
721 return e -> e == null ? null : EventCategory.valueOf(e.getCategory()).getLabel();
724 private SearchResponse call(@Nullable String project) {
725 SearchRequest.Builder request = SearchRequest.builder();
726 ofNullable(project).ifPresent(request::setProject);
727 return call(request.build());
730 private SearchResponse call(SearchRequest wsRequest) {
731 TestRequest request = ws.newRequest()
732 .setMethod(POST.name());
733 ofNullable(wsRequest.getProject()).ifPresent(project -> request.setParam(PARAM_PROJECT, project));
734 ofNullable(wsRequest.getBranch()).ifPresent(branch1 -> request.setParam(PARAM_BRANCH, branch1));
735 ofNullable(wsRequest.getCategory()).ifPresent(category -> request.setParam(PARAM_CATEGORY, category.name()));
736 ofNullable(wsRequest.getPage()).ifPresent(page -> request.setParam(Param.PAGE, String.valueOf(page)));
737 ofNullable(wsRequest.getPageSize()).ifPresent(pageSize -> request.setParam(Param.PAGE_SIZE, String.valueOf(pageSize)));
738 ofNullable(wsRequest.getFrom()).ifPresent(from -> request.setParam(PARAM_FROM, from));
739 ofNullable(wsRequest.getTo()).ifPresent(to -> request.setParam(PARAM_TO, to));
741 return request.executeProtobuf(SearchResponse.class);