]> source.dussan.org Git - sonarqube.git/blob
938909cafd93457002979dbeef5efb198f9c5489
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2024 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.developers.ws;
21
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;
40
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.assertj.core.api.Assertions.assertThat;
45 import static org.assertj.core.api.Assertions.tuple;
46 import static org.mockito.Mockito.mock;
47 import static org.mockito.Mockito.when;
48 import static org.sonar.api.utils.DateUtils.formatDateTime;
49 import static org.sonar.api.web.UserRole.USER;
50 import static org.sonar.db.component.BranchType.BRANCH;
51 import static org.sonar.db.component.BranchType.PULL_REQUEST;
52 import static org.sonar.db.event.EventTesting.newEvent;
53 import static org.sonar.server.developers.ws.SearchEventsAction.PARAM_FROM;
54 import static org.sonar.server.developers.ws.SearchEventsAction.PARAM_PROJECTS;
55
56 public class SearchEventsActionQualityGateIT {
57
58   @Rule
59   public DbTester db = DbTester.create();
60   @Rule
61   public EsTester es = EsTester.create();
62   @Rule
63   public UserSessionRule userSession = UserSessionRule.standalone().logIn();
64
65   private static final long ANY_TIMESTAMP = 1666666666L;
66
67   private final Server server = mock(Server.class);
68   private final IssueIndex issueIndex = new IssueIndex(es.client(), null, userSession, null);
69   private final IssueIndexSyncProgressChecker issueIndexSyncProgressChecker = mock(IssueIndexSyncProgressChecker.class);
70   private final WsActionTester ws = new WsActionTester(new SearchEventsAction(db.getDbClient(), userSession, server, issueIndex,
71     issueIndexSyncProgressChecker));
72
73   @Test
74   public void quality_gate_events() {
75     when(server.getPublicRootUrl()).thenReturn("https://sonarcloud.io");
76     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
77     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project));
78     SnapshotDto projectAnalysis = insertSuccessfulActivity(project, 1_500_000_000_000L);
79     db.events().insertEvent(newQualityGateEvent(projectAnalysis).setDate(projectAnalysis.getCreatedAt()).setName("Failed"));
80
81     long from = 1_000_000_000_000L;
82     SearchEventsWsResponse result = ws.newRequest()
83       .setParam(PARAM_PROJECTS, project.getKey())
84       .setParam(PARAM_FROM, formatDateTime(from))
85       .executeProtobuf(SearchEventsWsResponse.class);
86
87     assertThat(result.getEventsList())
88       .extracting(Event::getCategory, Event::getProject, Event::getMessage, Event::getLink, Event::getDate)
89       .containsOnly(
90         tuple("QUALITY_GATE", project.getKey(),
91           format("Quality Gate status of project '%s' changed to 'Failed'", project.name()),
92           format("https://sonarcloud.io/dashboard?id=%s", project.getKey()),
93           formatDateTime(projectAnalysis.getCreatedAt()))
94       );
95   }
96
97   @Test
98   public void branch_quality_gate_events() {
99     when(server.getPublicRootUrl()).thenReturn("https://sonarcloud.io");
100     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
101     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project));
102     String branchName = randomAlphanumeric(248);
103     ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH).setKey(branchName));
104     insertSuccessfulActivity(project, 1_500_000_000_000L);
105     SnapshotDto branchAnalysis = insertSuccessfulActivity(branch, 1_500_000_000_000L);
106     insertActivity(project.uuid(), branchAnalysis, CeActivityDto.Status.SUCCESS);
107     db.events().insertEvent(newQualityGateEvent(branchAnalysis).setDate(branchAnalysis.getCreatedAt()).setName("Failed"));
108
109     SearchEventsWsResponse result = ws.newRequest()
110       .setParam(PARAM_PROJECTS, project.getKey())
111       .setParam(PARAM_FROM, formatDateTime(branchAnalysis.getCreatedAt() - 1_000L))
112       .executeProtobuf(SearchEventsWsResponse.class);
113
114     assertThat(result.getEventsList())
115       .extracting(Event::getCategory, Event::getProject, Event::getMessage, Event::getLink)
116       .containsOnly(
117         tuple("QUALITY_GATE", project.getKey(),
118           format("Quality Gate status of project '%s' on branch '%s' changed to 'Failed'", project.name(), branchName),
119           format("https://sonarcloud.io/dashboard?id=%s&branch=%s", project.getKey(), branchName))
120       );
121   }
122
123   @Test
124   public void does_not_return_quality_gate_events_on_pull_request() {
125     userSession.logIn().setSystemAdministrator();
126     when(server.getPublicRootUrl()).thenReturn("https://sonarcloud.io");
127     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
128     ComponentDto pr = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
129     SnapshotDto prAnalysis = insertSuccessfulActivity(pr, 1_500_000_000_000L);
130     insertActivity(project.uuid(), prAnalysis, CeActivityDto.Status.SUCCESS);
131     db.events().insertEvent(newQualityGateEvent(prAnalysis).setDate(prAnalysis.getCreatedAt()).setName("Failed"));
132
133     SearchEventsWsResponse result = ws.newRequest()
134       .setParam(PARAM_PROJECTS, project.getKey())
135       .setParam(PARAM_FROM, formatDateTime(prAnalysis.getCreatedAt() - 1_000L))
136       .executeProtobuf(SearchEventsWsResponse.class);
137
138     assertThat(result.getEventsList()).isEmpty();
139   }
140
141   @Test
142   public void return_only_latest_quality_gate_event() {
143     ComponentDto project = db.components().insertPrivateProject(p -> p.setName("My Project")).getMainBranchComponent();
144     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project));
145     SnapshotDto a1 = insertSuccessfulActivity(project, 1_500_000_000_000L);
146     EventDto e1 = db.events().insertEvent(newQualityGateEvent(a1).setName("Failed").setDate(a1.getCreatedAt()));
147     SnapshotDto a2 = insertSuccessfulActivity(project, 1_500_000_000_001L);
148     EventDto e2 = db.events().insertEvent(newQualityGateEvent(a2).setName("Passed").setDate(a2.getCreatedAt() + 1L));
149
150     SearchEventsWsResponse result = ws.newRequest()
151       .setParam(PARAM_PROJECTS, project.getKey())
152       .setParam(PARAM_FROM, formatDateTime(a1.getCreatedAt() - 1_000L))
153       .executeProtobuf(SearchEventsWsResponse.class);
154
155     assertThat(result.getEventsList()).extracting(Event::getMessage)
156       .containsExactly("Quality Gate status of project 'My Project' changed to 'Passed'");
157   }
158
159   @Test
160   public void return_link_to_dashboard_for_quality_gate_event() {
161     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
162     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project));
163     SnapshotDto analysis = insertSuccessfulActivity(project, 1_500_000_000_000L);
164     EventDto e1 = db.events().insertEvent(newQualityGateEvent(analysis).setName("Failed").setDate(analysis.getCreatedAt()));
165     when(server.getPublicRootUrl()).thenReturn("https://sonarcloud.io");
166
167     SearchEventsWsResponse result = ws.newRequest()
168       .setParam(PARAM_PROJECTS, project.getKey())
169       .setParam(PARAM_FROM, formatDateTime(analysis.getCreatedAt() - 1_000L))
170       .executeProtobuf(SearchEventsWsResponse.class);
171
172     assertThat(result.getEventsList()).extracting(Event::getLink)
173       .containsExactly("https://sonarcloud.io/dashboard?id=" + project.getKey());
174   }
175
176   @Test
177   public void encode_link() {
178     ComponentDto project = db.components().insertPrivateProject(p -> p.setKey("M&M's")).getMainBranchComponent();
179     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project));
180     SnapshotDto analysis = insertSuccessfulActivity(project, 1_500_000_000_000L);
181     EventDto event = db.events().insertEvent(newQualityGateEvent(analysis).setName("Failed").setDate(analysis.getCreatedAt()));
182     when(server.getPublicRootUrl()).thenReturn("http://sonarcloud.io");
183
184     SearchEventsWsResponse result = ws.newRequest()
185       .setParam(PARAM_PROJECTS, project.getKey())
186       .setParam(PARAM_FROM, formatDateTime(analysis.getCreatedAt() - 1_000L))
187       .executeProtobuf(SearchEventsWsResponse.class);
188
189     assertThat(result.getEventsList()).extracting(Event::getLink)
190       .containsExactly("http://sonarcloud.io/dashboard?id=M%26M%27s");
191   }
192
193   @Test
194   public void filter_quality_gate_event() {
195     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
196     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project));
197     SnapshotDto analysis = insertSuccessfulActivity(project, 1_500_000_000_000L);
198     EventDto qualityGateEvent = db.events().insertEvent(newQualityGateEvent(analysis).setDate(analysis.getCreatedAt()));
199     EventDto versionEvent = db.events().insertEvent(newEvent(analysis).setCategory(EventCategory.VERSION.getLabel()).setDate(analysis.getCreatedAt()));
200     EventDto qualityProfileEvent = db.events().insertEvent(newEvent(analysis).setCategory(EventCategory.QUALITY_PROFILE.getLabel()).setDate(analysis.getCreatedAt()));
201
202     SearchEventsWsResponse result = ws.newRequest()
203       .setParam(PARAM_PROJECTS, project.getKey())
204       .setParam(PARAM_FROM, formatDateTime(analysis.getCreatedAt() - 1_000L))
205       .executeProtobuf(SearchEventsWsResponse.class);
206
207     assertThat(result.getEventsList()).extracting(Event::getCategory)
208       .containsExactly("QUALITY_GATE");
209   }
210
211   @Test
212   public void filter_by_from_date_inclusive() {
213     ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
214     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project1));
215     ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
216     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project2));
217     ComponentDto project3 = db.components().insertPrivateProject().getMainBranchComponent();
218     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project3));
219     long from1 = 1_500_000_000_000L;
220     long from2 = 1_400_000_000_000L;
221     long from3 = 1_300_000_000_000L;
222     SnapshotDto a1 = insertSuccessfulActivity(project1, from1 - 1L);
223     db.events().insertEvent(newQualityGateEvent(a1).setDate(a1.getCreatedAt()));
224     SnapshotDto a2 = insertSuccessfulActivity(project2, from2);
225     db.events().insertEvent(newQualityGateEvent(a2).setDate(from2));
226     SnapshotDto a3 = insertSuccessfulActivity(project3, from3 + 1L);
227     db.events().insertEvent(newQualityGateEvent(a3).setDate(from3 + 1L));
228
229     SearchEventsWsResponse result = ws.newRequest()
230       .setParam(PARAM_PROJECTS, join(",", project1.getKey(), project2.getKey(), project3.getKey()))
231       .setParam(PARAM_FROM, join(",", formatDateTime(from1 - 1_000L), formatDateTime(from2 - 1_000L), formatDateTime(from3 - 1_000L)))
232       .executeProtobuf(SearchEventsWsResponse.class);
233
234     assertThat(result.getEventsList())
235       .extracting(Event::getProject)
236       .containsExactlyInAnyOrder(project2.getKey(), project3.getKey());
237   }
238
239   @Test
240   public void return_one_quality_gate_change_per_project() {
241     ComponentDto project1 = db.components().insertPrivateProject(p -> p.setName("p1")).getMainBranchComponent();
242     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project1));
243     ComponentDto project2 = db.components().insertPrivateProject(p -> p.setName("p2")).getMainBranchComponent();
244     userSession.addProjectPermission(USER, db.components().getProjectDtoByMainBranch(project2));
245     long from = 1_500_000_000_000L;
246     SnapshotDto a11 = insertSuccessfulActivity(project1, from);
247     SnapshotDto a12 = insertSuccessfulActivity(project1, from + 1L);
248     SnapshotDto a21 = insertSuccessfulActivity(project2, from);
249     SnapshotDto a22 = insertSuccessfulActivity(project2, from + 1L);
250     EventDto e11 = db.events().insertEvent(newQualityGateEvent(a11).setName("e11").setDate(from));
251     EventDto e12 = db.events().insertEvent(newQualityGateEvent(a12).setName("e12").setDate(from + 1L));
252     EventDto e21 = db.events().insertEvent(newQualityGateEvent(a21).setName("e21").setDate(from));
253     EventDto e22 = db.events().insertEvent(newQualityGateEvent(a22).setName("e22").setDate(from + 1L));
254     String fromDate = formatDateTime(from - 1_000L);
255
256     SearchEventsWsResponse result = ws.newRequest()
257       .setParam(PARAM_PROJECTS, join(",", project1.getKey(), project2.getKey()))
258       .setParam(PARAM_FROM, join(",", fromDate, fromDate))
259       .executeProtobuf(SearchEventsWsResponse.class);
260
261     assertThat(result.getEventsList())
262       .extracting(Event::getProject, Event::getMessage)
263       .containsExactlyInAnyOrder(
264         tuple(project1.getKey(), "Quality Gate status of project 'p1' changed to 'e12'"),
265         tuple(project2.getKey(), "Quality Gate status of project 'p2' changed to 'e22'"));
266   }
267
268   private static EventDto newQualityGateEvent(SnapshotDto analysis) {
269     return newEvent(analysis).setCategory(EventCategory.QUALITY_GATE.getLabel());
270   }
271
272   private SnapshotDto insertSuccessfulActivity(ComponentDto project, long analysisDate) {
273     SnapshotDto analysis = db.components().insertSnapshot(project, s -> s.setCreatedAt(analysisDate));
274     insertActivity(project.uuid(), analysis, CeActivityDto.Status.SUCCESS);
275     return analysis;
276   }
277
278   private CeActivityDto insertActivity(String mainBranchUuid, SnapshotDto analysis, CeActivityDto.Status status) {
279     CeQueueDto queueDto = new CeQueueDto();
280     queueDto.setTaskType(CeTaskTypes.REPORT);
281     queueDto.setComponentUuid(mainBranchUuid);
282     queueDto.setUuid(randomAlphanumeric(40));
283     queueDto.setCreatedAt(ANY_TIMESTAMP);
284     CeActivityDto activityDto = new CeActivityDto(queueDto);
285     activityDto.setStatus(status);
286     activityDto.setExecutionTimeMs(1000L);
287     activityDto.setExecutedAt(ANY_TIMESTAMP);
288     activityDto.setAnalysisUuid(analysis.getUuid());
289     db.getDbClient().ceActivityDao().insert(db.getSession(), activityDto);
290     db.commit();
291     return activityDto;
292   }
293 }