]> source.dussan.org Git - sonarqube.git/blob
b3ebcac7130de800369d7512b71111a21d3d76c9
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 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.pushapi.issues;
21
22 import java.nio.charset.StandardCharsets;
23 import java.util.Deque;
24 import java.util.HashMap;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.Set;
28 import javax.annotation.Nullable;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.sonar.api.rules.RuleType;
32 import org.sonar.core.issue.DefaultIssue;
33 import org.sonar.core.issue.FieldDiffs;
34 import org.sonar.db.DbTester;
35 import org.sonar.db.component.BranchDto;
36 import org.sonar.db.component.BranchType;
37 import org.sonar.db.component.ComponentDto;
38 import org.sonar.db.issue.IssueDto;
39 import org.sonar.db.project.ProjectDto;
40 import org.sonar.db.pushevent.PushEventDto;
41 import org.sonar.db.rule.RuleDto;
42 import org.sonarqube.ws.Common;
43
44 import static org.assertj.core.api.Assertions.assertThat;
45 import static org.assertj.core.api.Assertions.tuple;
46 import static org.sonar.api.issue.DefaultTransitions.CONFIRM;
47 import static org.sonar.api.issue.DefaultTransitions.FALSE_POSITIVE;
48 import static org.sonar.api.issue.DefaultTransitions.REOPEN;
49 import static org.sonar.api.issue.DefaultTransitions.RESOLVE;
50 import static org.sonar.api.issue.DefaultTransitions.UNCONFIRM;
51 import static org.sonar.api.issue.DefaultTransitions.WONT_FIX;
52 import static org.sonar.api.rules.RuleType.CODE_SMELL;
53 import static org.sonar.db.component.ComponentTesting.newFileDto;
54 import static org.sonarqube.ws.Common.Severity.BLOCKER;
55 import static org.sonarqube.ws.Common.Severity.CRITICAL;
56 import static org.sonarqube.ws.Common.Severity.MAJOR;
57
58 public class IssueChangeEventServiceImplTest {
59
60   @Rule
61   public DbTester db = DbTester.create();
62
63   public final IssueChangeEventServiceImpl underTest = new IssueChangeEventServiceImpl(db.getDbClient());
64
65   @Test
66   public void distributeIssueChangeEvent_singleIssueChange_severityChange() {
67     ComponentDto componentDto = db.components().insertPublicProject().getMainBranchComponent();
68     ProjectDto project = db.getDbClient().projectDao().selectByUuid(db.getSession(), componentDto.uuid()).get();
69     BranchDto branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), project.getUuid()).get();
70     RuleDto rule = db.rules().insert();
71     IssueDto issue = db.issues().insert(rule, project, componentDto, i -> i.setSeverity(MAJOR.name()));
72
73     assertPushEventIsPersisted(project, branch, issue, BLOCKER.name(), null, null, null, 1);
74   }
75
76   @Test
77   public void distributeIssueChangeEvent_singleIssueChange_typeChange() {
78     ComponentDto componentDto = db.components().insertPublicProject().getMainBranchComponent();
79     ProjectDto project = db.getDbClient().projectDao().selectByUuid(db.getSession(), componentDto.uuid()).get();
80     BranchDto branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), project.getUuid()).get();
81     RuleDto rule = db.rules().insert();
82     IssueDto issue = db.issues().insert(rule, project, componentDto, i -> i.setSeverity(MAJOR.name()));
83
84     assertPushEventIsPersisted(project, branch, issue, null, Common.RuleType.BUG.name(), null, null, 1);
85   }
86
87   @Test
88   public void distributeIssueChangeEvent_singleIssueChange_transitionChanges() {
89     ComponentDto componentDto = db.components().insertPublicProject().getMainBranchComponent();
90     ProjectDto project = db.getDbClient().projectDao().selectByUuid(db.getSession(), componentDto.uuid()).get();
91     BranchDto branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), project.getUuid()).get();
92     RuleDto rule = db.rules().insert();
93     IssueDto issue = db.issues().insert(rule, project, componentDto, i -> i.setSeverity(MAJOR.name()));
94
95     assertPushEventIsPersisted(project, branch, issue, null, null, WONT_FIX, true, 1);
96     assertPushEventIsPersisted(project, branch, issue, null, null, REOPEN, false, 2);
97     assertPushEventIsPersisted(project, branch, issue, null, null, FALSE_POSITIVE, true, 3);
98     assertPushEventIsPersisted(project, branch, issue, null, null, REOPEN, false, 4);
99     assertPushEventIsPersisted(project, branch, issue, null, null, RESOLVE, false, 5);
100     assertPushEventIsPersisted(project, branch, issue, null, null, REOPEN, false, 6);
101     assertNoIssueDistribution(project, branch, issue, null, null, CONFIRM, 7);
102     assertNoIssueDistribution(project, branch, issue, null, null, UNCONFIRM, 8);
103   }
104
105   @Test
106   public void distributeIssueChangeEvent_singleIssueChange_severalChanges() {
107     ComponentDto componentDto = db.components().insertPublicProject().getMainBranchComponent();
108     ProjectDto project = db.getDbClient().projectDao().selectByUuid(db.getSession(), componentDto.uuid()).get();
109     BranchDto branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), project.getUuid()).get();
110     RuleDto rule = db.rules().insert();
111     IssueDto issue = db.issues().insert(rule, project, componentDto, i -> i.setSeverity(MAJOR.name()));
112
113     assertPushEventIsPersisted(project, branch, issue, BLOCKER.name(), Common.RuleType.BUG.name(), WONT_FIX, true, 1);
114   }
115
116   @Test
117   public void distributeIssueChangeEvent_bulkIssueChange() {
118     RuleDto rule = db.rules().insert();
119
120     ComponentDto componentDto1 = db.components().insertPublicProject().getMainBranchComponent();
121     ProjectDto project1 = db.getDbClient().projectDao().selectByUuid(db.getSession(), componentDto1.uuid()).get();
122     BranchDto branch1 = db.getDbClient().branchDao().selectByUuid(db.getSession(), project1.getUuid()).get();
123     IssueDto issue1 = db.issues().insert(rule, project1, componentDto1, i -> i.setSeverity(MAJOR.name()).setType(RuleType.BUG));
124
125     ComponentDto componentDto2 = db.components().insertPublicProject().getMainBranchComponent();
126     ProjectDto project2 = db.getDbClient().projectDao().selectByUuid(db.getSession(), componentDto2.uuid()).get();
127     BranchDto branch2 = db.getDbClient().branchDao().selectByUuid(db.getSession(), project2.getUuid()).get();
128     IssueDto issue2 = db.issues().insert(rule, project2, componentDto2, i -> i.setSeverity(MAJOR.name()).setType(RuleType.BUG));
129
130     ComponentDto componentDto3 = db.components().insertPublicProject().getMainBranchComponent();
131     ProjectDto project3 = db.getDbClient().projectDao().selectByUuid(db.getSession(), componentDto3.uuid()).get();
132     BranchDto branch3 = db.getDbClient().branchDao().selectByUuid(db.getSession(), project3.getUuid()).get();
133     IssueDto issue3 = db.issues().insert(rule, project3, componentDto3, i -> i.setSeverity(MAJOR.name()).setType(RuleType.BUG));
134
135     DefaultIssue defaultIssue1 = issue1.toDefaultIssue().setCurrentChangeWithoutAddChange(new FieldDiffs()
136       .setDiff("resolution", null, null)
137       .setDiff("severity", MAJOR.name(), CRITICAL.name())
138       .setDiff("type", RuleType.BUG.name(), CODE_SMELL.name()));
139     DefaultIssue defaultIssue2 = issue2.toDefaultIssue().setCurrentChangeWithoutAddChange(new FieldDiffs()
140       .setDiff("resolution", "OPEN", "FALSE-POSITIVE")
141       .setDiff("severity", MAJOR.name(), CRITICAL.name())
142       .setDiff("type", RuleType.BUG.name(), CODE_SMELL.name()));
143
144     Set<DefaultIssue> issues = Set.of(defaultIssue1, defaultIssue2, issue3.toDefaultIssue());
145     Map<String, ComponentDto> projectsByUuid = new HashMap<>();
146     projectsByUuid.put(componentDto1.branchUuid(), componentDto1);
147     projectsByUuid.put(componentDto2.branchUuid(), componentDto2);
148     projectsByUuid.put(componentDto3.branchUuid(), componentDto3);
149     Map<String, BranchDto> branchesByProjectUuid = new HashMap<>();
150     branchesByProjectUuid.put(componentDto1.branchUuid(), branch1);
151     branchesByProjectUuid.put(componentDto2.branchUuid(), branch2);
152     branchesByProjectUuid.put(componentDto3.branchUuid(), branch3);
153
154     underTest.distributeIssueChangeEvent(issues, projectsByUuid, branchesByProjectUuid);
155
156     Deque<PushEventDto> issueChangedEvents = db.getDbClient().pushEventDao()
157       .selectChunkByProjectUuids(db.getSession(), Set.of(project1.getUuid(), project2.getUuid()),
158         1l, null, 3);
159
160     assertThat(issueChangedEvents).hasSize(2);
161
162     assertThat(issueChangedEvents)
163       .extracting(PushEventDto::getName, PushEventDto::getProjectUuid)
164       .containsExactlyInAnyOrder(
165         tuple("IssueChanged", project1.getUuid()),
166         tuple("IssueChanged", project2.getUuid()));
167
168     Optional<PushEventDto> project1Event = issueChangedEvents.stream().filter(e -> e.getProjectUuid().equals(project1.getUuid())).findFirst();
169     Optional<PushEventDto> project2Event = issueChangedEvents.stream().filter(e -> e.getProjectUuid().equals(project2.getUuid())).findFirst();
170
171     assertThat(project1Event).isPresent();
172     assertThat(project2Event).isPresent();
173
174     String firstPayload = new String(project1Event.get().getPayload(), StandardCharsets.UTF_8);
175     assertThat(firstPayload)
176       .contains("\"userSeverity\":\"" + CRITICAL.name() + "\"",
177         "\"userType\":\"" + CODE_SMELL.name() + "\"",
178         "\"resolved\":" + false);
179
180     String secondPayload = new String(project2Event.get().getPayload(), StandardCharsets.UTF_8);
181     assertThat(secondPayload)
182       .contains("\"userSeverity\":\"" + CRITICAL.name() + "\"",
183         "\"userType\":\"" + CODE_SMELL.name() + "\"",
184         "\"resolved\":" + true);
185   }
186
187   @Test
188   public void doNotDistributeIssueChangeEvent_forPullRequestIssues() {
189     RuleDto rule = db.rules().insert();
190
191     ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
192     ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setKey("myBranch1")
193       .setBranchType(BranchType.PULL_REQUEST)
194       .setMergeBranchUuid(project.uuid()));
195     BranchDto branch1 = db.getDbClient().branchDao().selectByUuid(db.getSession(), pullRequest.uuid()).get();
196     ComponentDto file = db.components().insertComponent(newFileDto(pullRequest));
197     IssueDto issue1 = db.issues().insert(rule, pullRequest, file, i -> i.setSeverity(MAJOR.name()).setType(RuleType.BUG));
198
199     DefaultIssue defaultIssue1 = issue1.toDefaultIssue().setCurrentChangeWithoutAddChange(new FieldDiffs()
200       .setDiff("resolution", null, null)
201       .setDiff("severity", MAJOR.name(), CRITICAL.name())
202       .setDiff("type", RuleType.BUG.name(), CODE_SMELL.name()));
203
204     Set<DefaultIssue> issues = Set.of(defaultIssue1);
205     Map<String, ComponentDto> projectsByUuid = new HashMap<>();
206     projectsByUuid.put(project.branchUuid(), project);
207     Map<String, BranchDto> branchesByProjectUuid = new HashMap<>();
208     branchesByProjectUuid.put(project.branchUuid(), branch1);
209
210     underTest.distributeIssueChangeEvent(issues, projectsByUuid, branchesByProjectUuid);
211
212     Deque<PushEventDto> events = db.getDbClient().pushEventDao()
213       .selectChunkByProjectUuids(db.getSession(), Set.of(project.uuid()), 1l, null, 20);
214     assertThat(events).isEmpty();
215   }
216
217   private void assertNoIssueDistribution(ProjectDto project, BranchDto branch, IssueDto issue, @Nullable String severity,
218     @Nullable String type, @Nullable String transition, int page) {
219     underTest.distributeIssueChangeEvent(issue.toDefaultIssue(), severity, type, transition, branch, project.getKey());
220
221     Deque<PushEventDto> events = db.getDbClient().pushEventDao()
222       .selectChunkByProjectUuids(db.getSession(), Set.of(project.getUuid()), 1l, null, page);
223     assertThat(events).hasSizeLessThan(page);
224   }
225
226   private void assertPushEventIsPersisted(ProjectDto project, BranchDto branch, IssueDto issue, @Nullable String severity,
227     @Nullable String type, @Nullable String transition, Boolean resolved, int page) {
228     underTest.distributeIssueChangeEvent(issue.toDefaultIssue(), severity, type, transition, branch, project.getKey());
229
230     Deque<PushEventDto> events = db.getDbClient().pushEventDao()
231       .selectChunkByProjectUuids(db.getSession(), Set.of(project.getUuid()), 1l, null, page);
232     assertThat(events).isNotEmpty();
233     assertThat(events).extracting(PushEventDto::getName, PushEventDto::getProjectUuid)
234       .contains(tuple("IssueChanged", project.getUuid()));
235
236     String payload = new String(events.getLast().getPayload(), StandardCharsets.UTF_8);
237     if (severity != null) {
238       assertThat(payload).contains("\"userSeverity\":\"" + severity + "\"");
239     }
240
241     if (type != null) {
242       assertThat(payload).contains("\"userType\":\"" + type + "\"");
243     }
244
245     if (resolved != null) {
246       assertThat(payload).contains("\"resolved\":" + resolved);
247     }
248
249   }
250
251 }