3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.developers.ws;
22 import org.junit.Rule;
23 import org.junit.Test;
24 import org.sonar.api.platform.Server;
25 import org.sonar.db.DbTester;
26 import org.sonar.db.ce.CeActivityDto;
27 import org.sonar.db.ce.CeQueueDto;
28 import org.sonar.db.ce.CeTaskTypes;
29 import org.sonar.db.component.ComponentDto;
30 import org.sonar.db.component.SnapshotDto;
31 import org.sonar.db.event.EventDto;
32 import org.sonar.server.es.EsTester;
33 import org.sonar.server.issue.index.IssueIndex;
34 import org.sonar.server.issue.index.IssueIndexSyncProgressChecker;
35 import org.sonar.server.projectanalysis.ws.EventCategory;
36 import org.sonar.server.tester.UserSessionRule;
37 import org.sonar.server.ws.WsActionTester;
38 import org.sonarqube.ws.Developers.SearchEventsWsResponse;
39 import org.sonarqube.ws.Developers.SearchEventsWsResponse.Event;
41 import static java.lang.String.format;
42 import static java.lang.String.join;
43 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
44 import static org.apache.commons.lang.math.RandomUtils.nextLong;
45 import static org.assertj.core.api.Assertions.assertThat;
46 import static org.assertj.core.api.Assertions.tuple;
47 import static org.mockito.Mockito.mock;
48 import static org.mockito.Mockito.when;
49 import static org.sonar.api.utils.DateUtils.formatDateTime;
50 import static org.sonar.api.web.UserRole.USER;
51 import static org.sonar.db.component.BranchType.BRANCH;
52 import static org.sonar.db.component.BranchType.PULL_REQUEST;
53 import static org.sonar.db.event.EventTesting.newEvent;
54 import static org.sonar.server.developers.ws.SearchEventsAction.PARAM_FROM;
55 import static org.sonar.server.developers.ws.SearchEventsAction.PARAM_PROJECTS;
57 public class SearchEventsActionQualityGateTest {
60 public DbTester db = DbTester.create();
62 public EsTester es = EsTester.create();
64 public UserSessionRule userSession = UserSessionRule.standalone().logIn();
66 private Server server = mock(Server.class);
67 private IssueIndex issueIndex = new IssueIndex(es.client(), null, userSession, null);
68 private IssueIndexSyncProgressChecker issueIndexSyncProgressChecker = mock(IssueIndexSyncProgressChecker.class);
69 private WsActionTester ws = new WsActionTester(new SearchEventsAction(db.getDbClient(), userSession, server, issueIndex,
70 issueIndexSyncProgressChecker));
73 public void quality_gate_events() {
74 when(server.getPublicRootUrl()).thenReturn("https://sonarcloud.io");
75 ComponentDto project = db.components().insertPrivateProject();
76 userSession.addProjectPermission(USER, project);
77 SnapshotDto projectAnalysis = insertSuccessfulActivity(project, 1_500_000_000_000L);
78 db.events().insertEvent(newQualityGateEvent(projectAnalysis).setDate(projectAnalysis.getCreatedAt()).setName("Failed"));
80 long from = 1_000_000_000_000L;
81 SearchEventsWsResponse result = ws.newRequest()
82 .setParam(PARAM_PROJECTS, project.getKey())
83 .setParam(PARAM_FROM, formatDateTime(from))
84 .executeProtobuf(SearchEventsWsResponse.class);
86 assertThat(result.getEventsList())
87 .extracting(Event::getCategory, Event::getProject, Event::getMessage, Event::getLink, Event::getDate)
89 tuple("QUALITY_GATE", project.getKey(),
90 format("Quality Gate status of project '%s' changed to 'Failed'", project.name()),
91 format("https://sonarcloud.io/dashboard?id=%s", project.getKey()),
92 formatDateTime(projectAnalysis.getCreatedAt()))
97 public void branch_quality_gate_events() {
98 when(server.getPublicRootUrl()).thenReturn("https://sonarcloud.io");
99 ComponentDto project = db.components().insertPrivateProject();
100 userSession.addProjectPermission(USER, project);
101 ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
102 SnapshotDto projectAnalysis = insertSuccessfulActivity(project, 1_500_000_000_000L);
103 SnapshotDto branchAnalysis = insertSuccessfulActivity(branch, 1_500_000_000_000L);
104 insertActivity(branch, branchAnalysis, CeActivityDto.Status.SUCCESS);
105 db.events().insertEvent(newQualityGateEvent(branchAnalysis).setDate(branchAnalysis.getCreatedAt()).setName("Failed"));
107 SearchEventsWsResponse result = ws.newRequest()
108 .setParam(PARAM_PROJECTS, project.getKey())
109 .setParam(PARAM_FROM, formatDateTime(branchAnalysis.getCreatedAt() - 1_000L))
110 .executeProtobuf(SearchEventsWsResponse.class);
112 assertThat(result.getEventsList())
113 .extracting(Event::getCategory, Event::getProject, Event::getMessage, Event::getLink)
115 tuple("QUALITY_GATE", project.getKey(),
116 format("Quality Gate status of project '%s' on branch '%s' changed to 'Failed'", project.name(), branch.getBranch()),
117 format("https://sonarcloud.io/dashboard?id=%s&branch=%s", project.getKey(), branch.getBranch()))
122 public void does_not_return_quality_gate_events_on_pull_request() {
123 userSession.logIn().setSystemAdministrator();
124 when(server.getPublicRootUrl()).thenReturn("https://sonarcloud.io");
125 ComponentDto project = db.components().insertPrivateProject();
126 ComponentDto pr = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
127 SnapshotDto prAnalysis = insertSuccessfulActivity(pr, 1_500_000_000_000L);
128 insertActivity(pr, prAnalysis, CeActivityDto.Status.SUCCESS);
129 db.events().insertEvent(newQualityGateEvent(prAnalysis).setDate(prAnalysis.getCreatedAt()).setName("Failed"));
131 SearchEventsWsResponse result = ws.newRequest()
132 .setParam(PARAM_PROJECTS, project.getKey())
133 .setParam(PARAM_FROM, formatDateTime(prAnalysis.getCreatedAt() - 1_000L))
134 .executeProtobuf(SearchEventsWsResponse.class);
136 assertThat(result.getEventsList()).isEmpty();
140 public void return_only_latest_quality_gate_event() {
141 ComponentDto project = db.components().insertPrivateProject(p -> p.setName("My Project"));
142 userSession.addProjectPermission(USER, project);
143 SnapshotDto a1 = insertSuccessfulActivity(project, 1_500_000_000_000L);
144 EventDto e1 = db.events().insertEvent(newQualityGateEvent(a1).setName("Failed").setDate(a1.getCreatedAt()));
145 SnapshotDto a2 = insertSuccessfulActivity(project, 1_500_000_000_001L);
146 EventDto e2 = db.events().insertEvent(newQualityGateEvent(a2).setName("Passed").setDate(a2.getCreatedAt() + 1L));
148 SearchEventsWsResponse result = ws.newRequest()
149 .setParam(PARAM_PROJECTS, project.getKey())
150 .setParam(PARAM_FROM, formatDateTime(a1.getCreatedAt() - 1_000L))
151 .executeProtobuf(SearchEventsWsResponse.class);
153 assertThat(result.getEventsList()).extracting(Event::getMessage)
154 .containsExactly("Quality Gate status of project 'My Project' changed to 'Passed'");
158 public void return_link_to_dashboard_for_quality_gate_event() {
159 ComponentDto project = db.components().insertPrivateProject();
160 userSession.addProjectPermission(USER, project);
161 SnapshotDto analysis = insertSuccessfulActivity(project, 1_500_000_000_000L);
162 EventDto e1 = db.events().insertEvent(newQualityGateEvent(analysis).setName("Failed").setDate(analysis.getCreatedAt()));
163 when(server.getPublicRootUrl()).thenReturn("https://sonarcloud.io");
165 SearchEventsWsResponse result = ws.newRequest()
166 .setParam(PARAM_PROJECTS, project.getKey())
167 .setParam(PARAM_FROM, formatDateTime(analysis.getCreatedAt() - 1_000L))
168 .executeProtobuf(SearchEventsWsResponse.class);
170 assertThat(result.getEventsList()).extracting(Event::getLink)
171 .containsExactly("https://sonarcloud.io/dashboard?id=" + project.getKey());
175 public void encode_link() {
176 ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("M&M's"));
177 userSession.addProjectPermission(USER, project);
178 SnapshotDto analysis = insertSuccessfulActivity(project, 1_500_000_000_000L);
179 EventDto event = db.events().insertEvent(newQualityGateEvent(analysis).setName("Failed").setDate(analysis.getCreatedAt()));
180 when(server.getPublicRootUrl()).thenReturn("http://sonarcloud.io");
182 SearchEventsWsResponse result = ws.newRequest()
183 .setParam(PARAM_PROJECTS, project.getKey())
184 .setParam(PARAM_FROM, formatDateTime(analysis.getCreatedAt() - 1_000L))
185 .executeProtobuf(SearchEventsWsResponse.class);
187 assertThat(result.getEventsList()).extracting(Event::getLink)
188 .containsExactly("http://sonarcloud.io/dashboard?id=M%26M%27s");
192 public void filter_quality_gate_event() {
193 ComponentDto project = db.components().insertPrivateProject();
194 userSession.addProjectPermission(USER, project);
195 SnapshotDto analysis = insertSuccessfulActivity(project, 1_500_000_000_000L);
196 EventDto qualityGateEvent = db.events().insertEvent(newQualityGateEvent(analysis).setDate(analysis.getCreatedAt()));
197 EventDto versionEvent = db.events().insertEvent(newEvent(analysis).setCategory(EventCategory.VERSION.getLabel()).setDate(analysis.getCreatedAt()));
198 EventDto qualityProfileEvent = db.events().insertEvent(newEvent(analysis).setCategory(EventCategory.QUALITY_PROFILE.getLabel()).setDate(analysis.getCreatedAt()));
200 SearchEventsWsResponse result = ws.newRequest()
201 .setParam(PARAM_PROJECTS, project.getKey())
202 .setParam(PARAM_FROM, formatDateTime(analysis.getCreatedAt() - 1_000L))
203 .executeProtobuf(SearchEventsWsResponse.class);
205 assertThat(result.getEventsList()).extracting(Event::getCategory)
206 .containsExactly("QUALITY_GATE");
210 public void filter_by_from_date_inclusive() {
211 ComponentDto project1 = db.components().insertPrivateProject();
212 userSession.addProjectPermission(USER, project1);
213 ComponentDto project2 = db.components().insertPrivateProject();
214 userSession.addProjectPermission(USER, project2);
215 ComponentDto project3 = db.components().insertPrivateProject();
216 userSession.addProjectPermission(USER, project3);
217 long from1 = 1_500_000_000_000L;
218 long from2 = 1_400_000_000_000L;
219 long from3 = 1_300_000_000_000L;
220 SnapshotDto a1 = insertSuccessfulActivity(project1, from1 - 1L);
221 db.events().insertEvent(newQualityGateEvent(a1).setDate(a1.getCreatedAt()));
222 SnapshotDto a2 = insertSuccessfulActivity(project2, from2);
223 db.events().insertEvent(newQualityGateEvent(a2).setDate(from2));
224 SnapshotDto a3 = insertSuccessfulActivity(project3, from3 + 1L);
225 db.events().insertEvent(newQualityGateEvent(a3).setDate(from3 + 1L));
227 SearchEventsWsResponse result = ws.newRequest()
228 .setParam(PARAM_PROJECTS, join(",", project1.getKey(), project2.getKey(), project3.getKey()))
229 .setParam(PARAM_FROM, join(",", formatDateTime(from1 - 1_000L), formatDateTime(from2 - 1_000L), formatDateTime(from3 - 1_000L)))
230 .executeProtobuf(SearchEventsWsResponse.class);
232 assertThat(result.getEventsList())
233 .extracting(Event::getProject)
234 .containsExactlyInAnyOrder(project2.getKey(), project3.getKey());
238 public void return_one_quality_gate_change_per_project() {
239 ComponentDto project1 = db.components().insertPrivateProject(p -> p.setName("p1"));
240 userSession.addProjectPermission(USER, project1);
241 ComponentDto project2 = db.components().insertPrivateProject(p -> p.setName("p2"));
242 userSession.addProjectPermission(USER, project2);
243 long from = 1_500_000_000_000L;
244 SnapshotDto a11 = insertSuccessfulActivity(project1, from);
245 SnapshotDto a12 = insertSuccessfulActivity(project1, from + 1L);
246 SnapshotDto a21 = insertSuccessfulActivity(project2, from);
247 SnapshotDto a22 = insertSuccessfulActivity(project2, from + 1L);
248 EventDto e11 = db.events().insertEvent(newQualityGateEvent(a11).setName("e11").setDate(from));
249 EventDto e12 = db.events().insertEvent(newQualityGateEvent(a12).setName("e12").setDate(from + 1L));
250 EventDto e21 = db.events().insertEvent(newQualityGateEvent(a21).setName("e21").setDate(from));
251 EventDto e22 = db.events().insertEvent(newQualityGateEvent(a22).setName("e22").setDate(from + 1L));
252 String fromDate = formatDateTime(from - 1_000L);
254 SearchEventsWsResponse result = ws.newRequest()
255 .setParam(PARAM_PROJECTS, join(",", project1.getKey(), project2.getKey()))
256 .setParam(PARAM_FROM, join(",", fromDate, fromDate))
257 .executeProtobuf(SearchEventsWsResponse.class);
259 assertThat(result.getEventsList())
260 .extracting(Event::getProject, Event::getMessage)
261 .containsExactlyInAnyOrder(
262 tuple(project1.getKey(), "Quality Gate status of project 'p1' changed to 'e12'"),
263 tuple(project2.getKey(), "Quality Gate status of project 'p2' changed to 'e22'"));
266 private static EventDto newQualityGateEvent(SnapshotDto analysis) {
267 return newEvent(analysis).setCategory(EventCategory.QUALITY_GATE.getLabel());
270 private SnapshotDto insertSuccessfulActivity(ComponentDto project, long analysisDate) {
271 SnapshotDto analysis = db.components().insertSnapshot(project, s -> s.setCreatedAt(analysisDate));
272 insertActivity(project, analysis, CeActivityDto.Status.SUCCESS);
276 private CeActivityDto insertActivity(ComponentDto project, SnapshotDto analysis, CeActivityDto.Status status) {
277 CeQueueDto queueDto = new CeQueueDto();
278 queueDto.setTaskType(CeTaskTypes.REPORT);
279 String mainBranchProjectUuid = project.getMainBranchProjectUuid();
280 queueDto.setComponentUuid(mainBranchProjectUuid == null ? project.uuid() : mainBranchProjectUuid);
281 queueDto.setUuid(randomAlphanumeric(40));
282 queueDto.setCreatedAt(nextLong());
283 CeActivityDto activityDto = new CeActivityDto(queueDto);
284 activityDto.setStatus(status);
285 activityDto.setExecutionTimeMs(nextLong());
286 activityDto.setExecutedAt(nextLong());
287 activityDto.setAnalysisUuid(analysis.getUuid());
288 db.getDbClient().ceActivityDao().insert(db.getSession(), activityDto);