]> source.dussan.org Git - sonarqube.git/blob
48cbb2184110894381ccdf12ea8f7e0625717289
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 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.sonarlint;
21
22 import java.io.IOException;
23 import java.util.Set;
24 import javax.servlet.AsyncContext;
25 import javax.servlet.ServletOutputStream;
26 import javax.servlet.ServletResponse;
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.mockito.ArgumentCaptor;
30 import org.sonar.api.rule.Severity;
31 import org.sonar.core.util.issue.Issue;
32 import org.sonar.core.util.issue.IssueChangedEvent;
33 import org.sonar.core.util.ParamChange;
34 import org.sonar.core.util.rule.RuleChange;
35 import org.sonar.core.util.rule.RuleSetChangedEvent;
36 import org.sonar.server.exceptions.ForbiddenException;
37 import org.sonar.server.pushapi.issues.StandaloneIssueChangeEventsDistributor;
38 import org.sonar.server.pushapi.qualityprofile.StandaloneRuleActivatorEventsDistributor;
39
40 import static org.assertj.core.api.Assertions.assertThat;
41 import static org.mockito.ArgumentMatchers.any;
42 import static org.mockito.ArgumentMatchers.anySet;
43 import static org.mockito.ArgumentMatchers.anyString;
44 import static org.mockito.Mockito.clearInvocations;
45 import static org.mockito.Mockito.doThrow;
46 import static org.mockito.Mockito.mock;
47 import static org.mockito.Mockito.times;
48 import static org.mockito.Mockito.verify;
49 import static org.mockito.Mockito.verifyNoInteractions;
50 import static org.mockito.Mockito.when;
51 import static org.sonar.test.EventAssert.assertThatEvent;
52
53 public class SonarLintClientsRegistryTest {
54
55   private final AsyncContext defaultAsyncContext = mock(AsyncContext.class);
56
57   private final Set<String> exampleKeys = Set.of("project1", "project2", "project3");
58   private final Set<String> languageKeys = Set.of("language1", "language2", "language3");
59   private final String USER_UUID = "userUuid";
60   private final ServletResponse response = mock(ServletResponse.class);
61   private final ServletOutputStream outputStream = mock(ServletOutputStream.class);
62
63   private final SonarLintClientPermissionsValidator permissionsValidator = mock(SonarLintClientPermissionsValidator.class);
64   private final StandaloneRuleActivatorEventsDistributor ruleEventsDistributor = mock(StandaloneRuleActivatorEventsDistributor.class);
65   private final StandaloneIssueChangeEventsDistributor issueChangeEventsDistributor = mock(StandaloneIssueChangeEventsDistributor.class);
66
67   private SonarLintClientsRegistry underTest;
68
69   @Before
70   public void before() {
71     underTest = new SonarLintClientsRegistry(issueChangeEventsDistributor, ruleEventsDistributor, permissionsValidator);
72   }
73
74   @Test
75   public void registerClientAndUnregister_changesNumberOfClients_andClosesClient() {
76     SonarLintClient sonarLintClient = mock(SonarLintClient.class);
77
78     underTest.registerClient(sonarLintClient);
79
80     assertThat(underTest.countConnectedClients()).isEqualTo(1);
81
82     underTest.unregisterClient(sonarLintClient);
83
84     assertThat(underTest.countConnectedClients()).isZero();
85     verify(sonarLintClient).close();
86   }
87
88   @Test
89   public void registering10Clients_10ClientsAreRegistered() {
90     for (int i = 0; i < 10; i++) {
91       AsyncContext newAsyncContext = mock(AsyncContext.class);
92       SonarLintClient sonarLintClient = new SonarLintClient(newAsyncContext, exampleKeys, languageKeys, USER_UUID);
93       underTest.registerClient(sonarLintClient);
94     }
95
96     assertThat(underTest.countConnectedClients()).isEqualTo(10);
97   }
98
99   @Test
100   public void listen_givenOneClientInterestedInJavaEvents_sendOneJavaEvent() throws IOException {
101     Set<String> javaLanguageKey = Set.of("java");
102     when(defaultAsyncContext.getResponse()).thenReturn(response);
103     when(response.getOutputStream()).thenReturn(outputStream);
104     SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleKeys, javaLanguageKey, USER_UUID);
105
106     underTest.registerClient(sonarLintClient);
107
108     RuleChange javaRule = createRuleChange();
109
110     RuleChange[] activatedRules = {javaRule};
111     String[] deactivatedRules = {"repo2:rule-key2"};
112     RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules, "java");
113     underTest.listen(ruleSetChangedEvent);
114
115     ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
116     verify(outputStream).write(captor.capture());
117     String message = new String(captor.getValue());
118     assertThatEvent(message)
119       .hasType("RuleSetChanged")
120       .hasJsonData(getClass().getResource("rule-change-event-data.json"));
121   }
122
123   @Test
124   public void listen_givenOneClientInterestedInJsEventsAndJavaEventGenerated_sendZeroEvents() throws IOException {
125     Set<String> jsLanguageKey = Set.of("js");
126     when(defaultAsyncContext.getResponse()).thenReturn(response);
127     when(response.getOutputStream()).thenReturn(outputStream);
128     SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleKeys, jsLanguageKey, USER_UUID);
129
130     underTest.registerClient(sonarLintClient);
131
132     RuleChange[] activatedRules = {};
133     String[] deactivatedRules = {"repo:rule-key"};
134     RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules, "java");
135     underTest.listen(ruleSetChangedEvent);
136
137     verifyNoInteractions(outputStream);
138   }
139
140   @Test
141   public void listen_givenOneClientInterestedInProjA_DontCheckPermissionsForProjB() throws IOException {
142     when(defaultAsyncContext.getResponse()).thenReturn(response);
143     when(response.getOutputStream()).thenReturn(outputStream);
144     Set<String> clientProjectKeys = Set.of("projA");
145     Set<String> eventProjectKeys = Set.of("projA", "projB");
146     SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, clientProjectKeys, Set.of("java"), USER_UUID);
147
148     underTest.registerClient(sonarLintClient);
149
150     RuleChange[] activatedRules = {};
151     String[] deactivatedRules = {"repo:rule-key"};
152     RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(eventProjectKeys.toArray(String[]::new), activatedRules, deactivatedRules, "java");
153     underTest.listen(ruleSetChangedEvent);
154
155     ArgumentCaptor<Set<String>> argument = ArgumentCaptor.forClass(Set.class);
156     verify(permissionsValidator).validateUserCanReceivePushEventForProjects(anyString(), argument.capture());
157     assertThat(argument.getValue()).isEqualTo(clientProjectKeys);
158   }
159
160   @Test
161   public void listen_givenUserNotPermittedToReceiveRuleSetChangedEvent_closeConnection() {
162     RuleChange[] activatedRules = {};
163     String[] deactivatedRules = {"repo:rule-key"};
164     RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules, "java");
165
166     SonarLintClient sonarLintClient = createSampleSLClient();
167     underTest.registerClient(sonarLintClient);
168     doThrow(new ForbiddenException("Access forbidden")).when(permissionsValidator).validateUserCanReceivePushEventForProjects(anyString(), anySet());
169
170     underTest.listen(ruleSetChangedEvent);
171
172     verify(sonarLintClient).close();
173   }
174
175   @Test
176   public void listen_givenUserNotPermittedToReceiveIssueChangeEvent_closeConnection() {
177     Issue[] issues = new Issue[]{ new Issue("issue-1", "branch-1")};
178     IssueChangedEvent issueChangedEvent = new IssueChangedEvent("project1", issues, true, "BLOCKER", "BUG");
179
180     SonarLintClient sonarLintClient = createSampleSLClient();
181     underTest.registerClient(sonarLintClient);
182     doThrow(new ForbiddenException("Access forbidden")).when(permissionsValidator).validateUserCanReceivePushEventForProjects(anyString(), anySet());
183
184     underTest.listen(issueChangedEvent);
185
186     verify(sonarLintClient).close();
187   }
188
189   @Test
190   public void listen_givenUnregisteredClient_closeConnection() throws IOException {
191     RuleChange[] activatedRules = {};
192     String[] deactivatedRules = {"repo:rule-key"};
193     RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules, "java");
194
195     SonarLintClient sonarLintClient = createSampleSLClient();
196     underTest.registerClient(sonarLintClient);
197     doThrow(new IOException("Broken pipe")).when(sonarLintClient).writeAndFlush(anyString());
198
199     underTest.listen(ruleSetChangedEvent);
200
201     underTest.registerClient(sonarLintClient);
202     doThrow(new IllegalStateException("Things went wrong")).when(sonarLintClient).writeAndFlush(anyString());
203
204     underTest.listen(ruleSetChangedEvent);
205
206     verify(sonarLintClient, times(2)).close();
207   }
208
209   @Test
210   public void registerClient_whenCalledFirstTime_registerAlsoToListenToEvents() {
211     underTest.registerClient(createSampleSLClient());
212
213     verify(ruleEventsDistributor).subscribe(underTest);
214     verify(issueChangeEventsDistributor).subscribe(underTest);
215   }
216
217   @Test
218   public void registerClient_whenCalledSecondTime_doNotRegisterToEvents() {
219     underTest.registerClient(createSampleSLClient());
220     clearInvocations(ruleEventsDistributor);
221     clearInvocations(issueChangeEventsDistributor);
222
223     underTest.registerClient(createSampleSLClient());
224     verifyNoInteractions(ruleEventsDistributor);
225     verifyNoInteractions(issueChangeEventsDistributor);
226   }
227
228   @Test
229   public void registerClient_whenExceptionAndCalledSecondTime_registerToRuleEvents() {
230     doThrow(new RuntimeException()).when(ruleEventsDistributor).subscribe(any());
231     underTest.registerClient(createSampleSLClient());
232     clearInvocations(ruleEventsDistributor);
233
234     underTest.registerClient(createSampleSLClient());
235     verify(ruleEventsDistributor).subscribe(underTest);
236   }
237
238   @Test
239   public void registerClient_whenExceptionAndCalledSecondTime_registerToIssueChangeEvents() {
240     doThrow(new RuntimeException()).when(issueChangeEventsDistributor).subscribe(any());
241     underTest.registerClient(createSampleSLClient());
242     clearInvocations(issueChangeEventsDistributor);
243
244     underTest.registerClient(createSampleSLClient());
245     verify(issueChangeEventsDistributor).subscribe(underTest);
246   }
247
248   private SonarLintClient createSampleSLClient() {
249     SonarLintClient mock = mock(SonarLintClient.class);
250     when(mock.getLanguages()).thenReturn(Set.of("java"));
251     when(mock.getClientProjectKeys()).thenReturn(exampleKeys);
252     when(mock.getUserUuid()).thenReturn("userUuid");
253     return mock;
254   }
255
256   private RuleChange createRuleChange() {
257     RuleChange javaRule = new RuleChange();
258     javaRule.setLanguage("java");
259     javaRule.setParams(new ParamChange[]{new ParamChange("param-key", "param-value")});
260     javaRule.setTemplateKey("repo:template-key");
261     javaRule.setSeverity(Severity.CRITICAL);
262     javaRule.setKey("repo:rule-key");
263     return javaRule;
264   }
265 }