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