]> source.dussan.org Git - sonarqube.git/blob
c4d6e850c0dbc184489b2a18b6518e3c117b00cf
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2021 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.projectanalysis.ws;
21
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;
65
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;
95
96 @RunWith(DataProviderRunner.class)
97 public class SearchActionTest {
98
99   @Rule
100   public UserSessionRule userSession = UserSessionRule.standalone();
101   @Rule
102   public DbTester db = DbTester.create();
103   @Rule
104   public LogTester logTester = new LogTester();
105
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();
109
110   @DataProvider
111   public static Object[][] changedBranches() {
112     return new Object[][] {
113       {null, "newbranch"},
114       {"newbranch", "anotherbranch"},
115       {"newbranch", null},
116     };
117   }
118
119   @Test
120   public void json_example() {
121     ComponentDto project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto().setDbKey(KEY_PROJECT_EXAMPLE_001));
122
123     userSession.addProjectPermission(UserRole.USER, project);
124     SnapshotDto a1 = db.components().insertSnapshot(newAnalysis(project)
125       .setUuid("A1")
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)
131       .setUuid("A2")
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)
137       .setUuid("P1")
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)
145       .setValue("Jenkins")
146       .setCreatedAt(1L));
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()));
154     db.commit();
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()));
166
167     EventDto eventDto = db.events().insertEvent(newEvent(a3)
168       .setUuid("E31")
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);
176
177     String result = ws.newRequest()
178       .setParam(PARAM_PROJECT, KEY_PROJECT_EXAMPLE_001)
179       .execute().getInput();
180
181     assertJson(result).isSimilarTo(getClass().getResource("search-example.json"));
182   }
183
184   @Test
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));
191
192     List<Analysis> result = call(project.getKey()).getAnalysesList();
193
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));
199   }
200
201   @Test
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));
207
208     List<Analysis> result = call("P1").getAnalysesList();
209
210     assertThat(result).hasSize(1);
211     assertThat(result.get(0).getKey()).isEqualTo("A1");
212   }
213
214   @Test
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));
223
224     List<Analysis> result = call("P1").getAnalysesList();
225
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()));
232   }
233
234   @Test
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));
241
242     List<Analysis> result = call(application.getDbKey()).getAnalysesList();
243
244     assertThat(result)
245       .hasSize(3)
246       .extracting(Analysis::getKey).containsExactly(thirdAnalysis.getUuid(), secondAnalysis.getUuid(), firstAnalysis.getUuid());
247
248     assertThat(result.get(0).getEventsList()).isEmpty();
249     assertThat(result.get(1).getEventsList()).isEmpty();
250     assertThat(result.get(2).getEventsList()).isEmpty();
251   }
252
253   @Test
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);
262
263     List<Analysis> result = call(application.getDbKey()).getAnalysesList();
264
265     assertThat(result).hasSize(1);
266     List<Event> events = result.get(0).getEventsList();
267     assertThat(events)
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)
272       .containsExactly(
273         tuple("ADDED", "My project", "app1", "", ""),
274         tuple("REMOVED", "Another project", "app2", "", ""));
275   }
276
277   @Test
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);
287
288     List<Analysis> result = call(application.getDbKey()).getAnalysesList();
289
290     assertThat(result).hasSize(1);
291     List<Event> events = result.get(0).getEventsList();
292     assertThat(events)
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));
298   }
299
300   @Test
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();
312
313     List<Analysis> result = call(application.getDbKey()).getAnalysesList();
314
315     assertThat(result).hasSize(1);
316     List<Event> events = result.get(0).getEventsList();
317     assertThat(events)
318       .extracting(Event::getName, Event::getCategory, Event::getKey)
319       .containsExactly(tuple("", DEFINITION_CHANGE.name(), "E11"));
320     assertThat(events.get(0).getDefinitionChange().getProjectsList())
321       .isEmpty();
322
323     assertThat(logTester.getLogs(LoggerLevel.ERROR))
324       .extracting(LogAndArguments::getFormattedMsg)
325       .containsExactly(
326         format("Incorrect changes : [uuid=%s change=ADDED, branch=master] and [uuid=%s, change=ADDED, branch=master]", changeDto1.getUuid(), changeDto2.getUuid()));
327   }
328
329   @Test
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();
339
340     List<Analysis> result = call(application.getDbKey()).getAnalysesList();
341
342     assertThat(result).hasSize(1);
343     List<Event> events = result.get(0).getEventsList();
344     assertThat(events)
345       .extracting(Event::getName, Event::getCategory, Event::getKey)
346       .containsExactly(tuple("", DEFINITION_CHANGE.name(), "E11"));
347     assertThat(events.get(0).getDefinitionChange().getProjectsList())
348       .isEmpty();
349
350     assertThat(logTester.getLogs(LoggerLevel.ERROR))
351       .extracting(LogAndArguments::getFormattedMsg)
352       .containsExactly("Unknown change FAILED_QUALITY_GATE for eventComponentChange uuid: " + changeDto1.getUuid());
353   }
354
355   @Test
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();
369
370     List<Analysis> result = call(application.getDbKey()).getAnalysesList();
371
372     assertThat(result).hasSize(1);
373     List<Event> events = result.get(0).getEventsList();
374     assertThat(events)
375       .extracting(Event::getName, Event::getCategory, Event::getKey)
376       .containsExactly(tuple("", DEFINITION_CHANGE.name(), "E11"));
377     assertThat(events.get(0).getDefinitionChange().getProjectsList())
378       .isEmpty();
379
380     assertThat(logTester.getLogs(LoggerLevel.ERROR))
381       .extracting(LogAndArguments::getFormattedMsg)
382       .containsExactly(
383         format("Too many changes on same project (3) for eventComponentChange uuids : %s,%s,%s", changeDto1.getUuid(), changeDto2.getUuid(), changeDto3.getUuid()));
384   }
385
386   @Test
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)
393         .setName("")
394         .setUuid("E11")
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();
401
402     List<Analysis> result = call(application.getDbKey()).getAnalysesList();
403
404     assertThat(result).hasSize(1);
405     List<Event> events = result.get(0).getEventsList();
406     assertThat(events)
407       .extracting(Event::getName, Event::getCategory, Event::getKey)
408       .containsExactly(tuple("", QUALITY_GATE.name(), "E11"));
409
410     // Verify that the values are not populated
411     assertThat(events.get(0).getQualityGate().hasStatus()).isFalse();
412     assertThat(events.get(0).getQualityGate().hasStillFailing()).isFalse();
413
414     assertThat(logTester.getLogs(LoggerLevel.ERROR))
415       .extracting(LogAndArguments::getFormattedMsg)
416       .containsExactly("Unable to retrieve data from event uuid=E11");
417   }
418
419   @Test
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));
426
427     List<Analysis> result = call(view.getDbKey()).getAnalysesList();
428
429     assertThat(result)
430       .hasSize(3)
431       .extracting(Analysis::getKey).containsExactly(thirdAnalysis.getUuid(), secondAnalysis.getUuid(), firstAnalysis.getUuid());
432   }
433
434   @Test
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)));
439
440     SearchResponse result = call(SearchRequest.builder()
441       .setProject(project.getDbKey())
442       .setPage(2)
443       .setPageSize(3)
444       .build());
445
446     assertThat(result.getAnalysesList()).extracting(Analysis::getKey)
447       .containsExactly("A6", "A5", "A4");
448   }
449
450   @Test
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()));
462
463     List<Analysis> result = call(SearchRequest.builder()
464       .setProject("P1")
465       .setCategory(QUALITY_GATE)
466       .build()).getAnalysesList();
467
468     assertThat(result).extracting(Analysis::getKey).containsOnly("A1", "A2");
469   }
470
471   @Test
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()));
485
486     SearchResponse result = call(SearchRequest.builder()
487       .setProject("P1")
488       .setCategory(QUALITY_GATE)
489       .setPage(2)
490       .setPageSize(1)
491       .build());
492
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);
496   }
497
498   @Test
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));
506
507     SearchResponse result = call(SearchRequest.builder()
508       .setProject(project.getDbKey())
509       .setFrom(formatDateTime(2_000_000_000L))
510       .build());
511
512     assertThat(result.getAnalysesList())
513       .extracting(Analysis::getKey)
514       .containsOnly(a2.getUuid(), a3.getUuid(), a4.getUuid())
515       .doesNotContain(a1.getUuid());
516   }
517
518   @Test
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));
526
527     SearchResponse result = call(SearchRequest.builder()
528       .setProject(project.getDbKey())
529       .setTo(formatDateTime(2_000_000_000L))
530       .build());
531
532     assertThat(result.getAnalysesList())
533       .extracting(Analysis::getKey)
534       .containsOnly(a1.getUuid(), a2.getUuid())
535       .doesNotContain(a3.getUuid(), a4.getUuid());
536   }
537
538   @Test
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));
546
547     SearchResponse result = call(SearchRequest.builder()
548       .setProject(project.getDbKey())
549       .setFrom(formatDateTime(2_000_000_000L))
550       .setTo(formatDateTime(3_000_000_000L))
551       .build());
552
553     assertThat(result.getAnalysesList())
554       .extracting(Analysis::getKey)
555       .containsOnly(a2.getUuid(), a3.getUuid())
556       .doesNotContain(a1.getUuid(), a4.getUuid());
557   }
558
559   @Test
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));
567
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)))
572       .build());
573
574     assertThat(result.getAnalysesList())
575       .extracting(Analysis::getKey)
576       .containsOnly(a2.getUuid(), a3.getUuid())
577       .doesNotContain(a1.getUuid(), a4.getUuid());
578   }
579
580   @Test
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()));
587
588     List<Analysis> result = call(SearchRequest.builder()
589       .setProject(project.getKey())
590       .setBranch("my_branch")
591       .build())
592         .getAnalysesList();
593
594     assertThat(result).extracting(Analysis::getKey).containsExactlyInAnyOrder(analysis.getUuid());
595     assertThat(result.get(0).getEventsList()).extracting(Event::getKey).containsExactlyInAnyOrder(event.getUuid());
596   }
597
598   @Test
599   public void empty_response() {
600     ComponentDto project = db.components().insertPrivateProject();
601     userSession.addProjectPermission(UserRole.USER, project);
602
603     SearchResponse result = call(project.getDbKey());
604
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();
608   }
609
610   @Test
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"))
619     };
620
621     SearchResponse result = call(project.getDbKey());
622
623     assertThat(result.getAnalysesList())
624       .extracting(Analysis::getKey, Analysis::getProjectVersion, Analysis::getBuildString)
625       .containsOnly(
626         tuple(analyses[0].getUuid(), "", ""),
627         tuple(analyses[1].getUuid(), "a", ""),
628         tuple(analyses[2].getUuid(), "", "b"),
629         tuple(analyses[3].getUuid(), "c", "d"));
630   }
631
632   @Test
633   public void fail_if_not_enough_permissions() {
634     userSession.anonymous();
635     ComponentDto project = db.components().insertPrivateProject();
636
637     var projectDbKey = project.getDbKey();
638     assertThatThrownBy(() -> call(projectDbKey))
639       .isInstanceOf(ForbiddenException.class);
640   }
641
642   @Test
643   public void fail_if_project_does_not_exist() {
644     assertThatThrownBy(() -> call("P1"))
645       .isInstanceOf(NotFoundException.class);
646   }
647
648   @Test
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);
654
655     var fileDbKey = file.getDbKey();
656     assertThatThrownBy(() -> call(fileDbKey))
657       .isInstanceOf(IllegalArgumentException.class)
658       .hasMessage("A project, portfolio or application is required");
659   }
660
661   @Test
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"));
666
667     var searchRequest = SearchRequest.builder()
668       .setProject(project.getKey())
669       .setBranch("another_branch")
670       .build();
671
672     assertThatThrownBy(() -> call(searchRequest))
673       .isInstanceOf(NotFoundException.class)
674       .hasMessage(format("Component '%s' on branch '%s' not found", project.getKey(), "another_branch"));
675   }
676
677   @Test
678   public void definition() {
679     WebService.Action definition = ws.getDef();
680
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);
687
688     Param from = definition.param("from");
689     assertThat(from.since()).isEqualTo("6.5");
690
691     Param to = definition.param("to");
692     assertThat(to.since()).isEqualTo("6.5");
693
694     Param branch = definition.param("branch");
695     assertThat(branch.since()).isEqualTo("6.6");
696     assertThat(branch.isInternal()).isTrue();
697     assertThat(branch.isRequired()).isFalse();
698   }
699
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());
710   }
711
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);
716     }
717     db.getSession().commit();
718   }
719
720   private static Function<Event, String> wsToDbCategory() {
721     return e -> e == null ? null : EventCategory.valueOf(e.getCategory()).getLabel();
722   }
723
724   private SearchResponse call(@Nullable String project) {
725     SearchRequest.Builder request = SearchRequest.builder();
726     ofNullable(project).ifPresent(request::setProject);
727     return call(request.build());
728   }
729
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));
740
741     return request.executeProtobuf(SearchResponse.class);
742   }
743 }