From d44482b279f26b210c140b0c48e35734d3dd008b Mon Sep 17 00:00:00 2001 From: Lukasz Jarocki Date: Wed, 16 Feb 2022 08:43:46 -0300 Subject: [PATCH] SONAR-16029 integration test for push endpoint --- .../server/pushapi/ServerPushClient.java | 5 +++ .../sonarlint/SonarLintClientsRegistry.java | 3 +- .../server/pushapi/ServerPushClientTest.java | 18 ++++++++- .../sonarqube/ws/client/DefaultWsClient.java | 8 ++++ .../org/sonarqube/ws/client/WsClient.java | 3 ++ .../push/SonarLintServerPushService.java | 39 +++++++++++++++++++ 6 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/push/SonarLintServerPushService.java diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java index 458b716a3af..6d8b552e24a 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java @@ -52,10 +52,15 @@ public abstract class ServerPushClient { } public void writeAndFlush(String payload) throws IOException { + payload = ensureCorrectMessageEnding(payload); output().write(payload.getBytes(StandardCharsets.UTF_8)); flush(); } + private static String ensureCorrectMessageEnding(String payload) { + return payload.endsWith("\n\n") ? payload : (payload + "\n\n"); + } + public void writeAndFlush(char character) { write(character); flush(); diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java index 10af5984615..008fdb0c3f9 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java @@ -49,14 +49,13 @@ public class SonarLintClientsRegistry implements RuleActivationListener { private final RuleActivatorEventsDistributor ruleActivatorEventsDistributor; private final SonarLintClientPermissionsValidator sonarLintClientPermissionsValidator; + private final List clients = new CopyOnWriteArrayList<>(); public SonarLintClientsRegistry(RuleActivatorEventsDistributor ruleActivatorEventsDistributor, SonarLintClientPermissionsValidator permissionsValidator) { this.ruleActivatorEventsDistributor = ruleActivatorEventsDistributor; this.sonarLintClientPermissionsValidator = permissionsValidator; } - private final List clients = new CopyOnWriteArrayList<>(); - public void registerClient(SonarLintClient sonarLintClient) { clients.add(sonarLintClient); sonarLintClient.scheduleHeartbeat(); diff --git a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/ServerPushClientTest.java b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/ServerPushClientTest.java index 47de0e39b9e..e7eca1cf790 100644 --- a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/ServerPushClientTest.java +++ b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/ServerPushClientTest.java @@ -20,6 +20,7 @@ package org.sonar.server.pushapi; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -33,7 +34,6 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doThrow; @@ -69,6 +69,22 @@ public class ServerPushClientTest { .schedule(any(HeartbeatTask.class), anyLong(), any()); } + @Test + public void writeAndFlush_payloadAlwaysEndsWithSlashNSlashN() throws IOException { + underTest.writeAndFlush("payload"); + + verify(outputStream, Mockito.times(1)).flush(); + verify(outputStream, Mockito.times(1)).write("payload\n\n".getBytes(StandardCharsets.UTF_8)); + } + + @Test + public void writeAndFlush_payloadAlwaysEndsWithASingleSlashNSlashN_whenMessageAlreadyContainsIt() throws IOException { + underTest.writeAndFlush("payload\n\n"); + + verify(outputStream, Mockito.times(1)).flush(); + verify(outputStream, Mockito.times(1)).write("payload\n\n".getBytes(StandardCharsets.UTF_8)); + } + @Test public void writeAndFlush_writeIsCalledOnceAndFlushIsCalledOnce() throws IOException { underTest.writeAndFlush('a'); diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/DefaultWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/DefaultWsClient.java index 9a3432d45c5..39c97e4b4ba 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/DefaultWsClient.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/DefaultWsClient.java @@ -55,6 +55,7 @@ import org.sonarqube.ws.client.projectpullrequests.ProjectPullRequestsService; import org.sonarqube.ws.client.projects.ProjectsService; import org.sonarqube.ws.client.projecttags.ProjectTagsService; import org.sonarqube.ws.client.properties.PropertiesService; +import org.sonarqube.ws.client.push.SonarLintServerPushService; import org.sonarqube.ws.client.qualitygates.QualitygatesService; import org.sonarqube.ws.client.qualityprofiles.QualityprofilesService; import org.sonarqube.ws.client.roots.RootsService; @@ -140,6 +141,7 @@ class DefaultWsClient implements WsClient { private final WebservicesService webservicesService; private final BatchService batchService; private final SecurityReportsService securityReportsService; + private final SonarLintServerPushService sonarLintPushService; DefaultWsClient(WsConnector wsConnector) { this.wsConnector = wsConnector; @@ -198,6 +200,7 @@ class DefaultWsClient implements WsClient { this.webservicesService = new WebservicesService(wsConnector); this.batchService = new BatchService(wsConnector); this.securityReportsService = new SecurityReportsService(wsConnector); + this.sonarLintPushService = new SonarLintServerPushService(wsConnector); } @Override @@ -306,6 +309,11 @@ class DefaultWsClient implements WsClient { return monitoringService; } + @Override + public SonarLintServerPushService sonarLintPush() { + return sonarLintPushService; + } + @Override public NavigationService navigation() { return navigationService; diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java index e18bd387c7a..23f90cfd7ad 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java @@ -55,6 +55,7 @@ import org.sonarqube.ws.client.projectpullrequests.ProjectPullRequestsService; import org.sonarqube.ws.client.projects.ProjectsService; import org.sonarqube.ws.client.projecttags.ProjectTagsService; import org.sonarqube.ws.client.properties.PropertiesService; +import org.sonarqube.ws.client.push.SonarLintServerPushService; import org.sonarqube.ws.client.qualitygates.QualitygatesService; import org.sonarqube.ws.client.qualityprofiles.QualityprofilesService; import org.sonarqube.ws.client.roots.RootsService; @@ -205,4 +206,6 @@ public interface WsClient { SecurityReportsService securityReports(); MonitoringService monitoring(); + + SonarLintServerPushService sonarLintPush(); } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/push/SonarLintServerPushService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/push/SonarLintServerPushService.java new file mode 100644 index 00000000000..6e4842d2a6b --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/push/SonarLintServerPushService.java @@ -0,0 +1,39 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarqube.ws.client.push; + +import org.sonarqube.ws.client.BaseService; +import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.WsConnector; +import org.sonarqube.ws.client.WsResponse; + +public class SonarLintServerPushService extends BaseService { + public SonarLintServerPushService(WsConnector wsConnector) { + super(wsConnector, "api/push"); + } + + public WsResponse connect(String projectKeys, String languages) { + return call( + new GetRequest(path("sonarlint_events")) + .setParam("projectKeys", projectKeys) + .setParam("languages", languages) + .setHeader("accept", "text/event-stream")); + } +} -- 2.39.5