From 39c40ef59f3f7e51bf05db4579e4d5e230168407 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 29 Jun 2016 12:04:54 +0200 Subject: [PATCH] SONAR-1891 Add upload plugin WS --- .../platformlevel/PlatformLevel4.java | 8 +- .../updatecenter/UpdateCenterModule.java | 37 +++++ .../updatecenter/ws/UpdateCenterWs.java | 9 ++ .../updatecenter/ws/UpdateCenterWsAction.java | 26 +++ .../server/updatecenter/ws/UploadAction.java | 80 ++++++++++ .../server/updatecenter/ws/package-info.java | 23 +++ .../sonar/server/ws/LocalRequestAdapter.java | 5 + .../org/sonar/server/ws/ServletRequest.java | 14 +- .../updatecenter/UpdateCenterModuleTest.java | 34 ++++ .../updatecenter/ws/UpdateCenterWsTest.java | 22 ++- .../updatecenter/ws/UploadActionTest.java | 149 ++++++++++++++++++ .../java/org/sonar/server/ws/TestRequest.java | 13 ++ .../org/sonar/server/ws/TestResponse.java | 4 + .../sonar/server/ws/WebServiceEngineTest.java | 98 +++--------- .../java/org/sonar/server/ws/WsTester.java | 12 ++ .../ws/UploadActionTest/anotherPlugin.jar | Bin 0 -> 3421 bytes .../ws/UploadActionTest/plugin.jar | Bin 0 -> 4454 bytes .../java/org/sonar/api/server/ws/Request.java | 20 +++ .../api/server/ws/internal/PartImpl.java | 45 ++++++ .../server/ws/internal/SimpleGetRequest.java | 12 ++ .../server/ws/internal/ValidatingRequest.java | 13 +- .../org/sonar/api/server/ws/RequestTest.java | 32 ++++ .../ws/internal/SimpleGetRequestTest.java | 39 +++-- 23 files changed, 589 insertions(+), 106 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/updatecenter/UpdateCenterModule.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UpdateCenterWsAction.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UploadAction.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/package-info.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/updatecenter/UpdateCenterModuleTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UploadActionTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/updatecenter/ws/UploadActionTest/anotherPlugin.jar create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/updatecenter/ws/UploadActionTest/plugin.jar create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/PartImpl.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index f1dcdddb3a8..7313694d9d3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -168,8 +168,6 @@ import org.sonar.server.platform.ws.SystemWs; import org.sonar.server.platform.ws.UpgradesAction; import org.sonar.server.plugins.PluginDownloader; import org.sonar.server.plugins.ServerExtensionInstaller; -import org.sonar.server.plugins.UpdateCenterClient; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.server.plugins.privileged.PrivilegedPluginsBootstraper; import org.sonar.server.plugins.privileged.PrivilegedPluginsStopper; import org.sonar.server.plugins.ws.AvailableAction; @@ -275,7 +273,7 @@ import org.sonar.server.ui.ws.ComponentNavigationAction; import org.sonar.server.ui.ws.GlobalNavigationAction; import org.sonar.server.ui.ws.NavigationWs; import org.sonar.server.ui.ws.SettingsNavigationAction; -import org.sonar.server.updatecenter.ws.UpdateCenterWs; +import org.sonar.server.updatecenter.UpdateCenterModule; import org.sonar.server.user.DefaultUserFinder; import org.sonar.server.user.DefaultUserService; import org.sonar.server.user.DeprecatedUserFinder; @@ -367,9 +365,7 @@ public class PlatformLevel4 extends PlatformLevel { IssueTagCloudWidget.class, // update center - UpdateCenterClient.class, - UpdateCenterMatrixFactory.class, - UpdateCenterWs.class, + UpdateCenterModule.class, // quality profile ActiveRuleIndexer.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/updatecenter/UpdateCenterModule.java b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/UpdateCenterModule.java new file mode 100644 index 00000000000..0164e05e1fd --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/UpdateCenterModule.java @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.sonar.server.updatecenter; + +import org.sonar.core.platform.Module; +import org.sonar.server.plugins.UpdateCenterClient; +import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.server.updatecenter.ws.UpdateCenterWs; +import org.sonar.server.updatecenter.ws.UploadAction; + +public class UpdateCenterModule extends Module { + @Override + protected void configureModule() { + add( + UpdateCenterClient.class, + UpdateCenterMatrixFactory.class, + UploadAction.class, + UpdateCenterWs.class); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UpdateCenterWs.java b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UpdateCenterWs.java index 5a5e5cec792..b311da9ac4e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UpdateCenterWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UpdateCenterWs.java @@ -25,6 +25,12 @@ import org.sonar.api.server.ws.WebService; public class UpdateCenterWs implements WebService { + private final UpdateCenterWsAction[] actions; + + public UpdateCenterWs(UpdateCenterWsAction... actions) { + this.actions = actions; + } + @Override public void define(Context context) { NewController controller = context.createController("api/updatecenter") @@ -32,6 +38,9 @@ public class UpdateCenterWs implements WebService { .setSince("2.10"); defineInstalledPluginsAction(controller); + for (UpdateCenterWsAction action : actions) { + action.define(controller); + } controller.done(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UpdateCenterWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UpdateCenterWsAction.java new file mode 100644 index 00000000000..95663f67805 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UpdateCenterWsAction.java @@ -0,0 +1,26 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.sonar.server.updatecenter.ws; + +import org.sonar.server.ws.WsAction; + +interface UpdateCenterWsAction extends WsAction { + // Marker interface +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UploadAction.java b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UploadAction.java new file mode 100644 index 00000000000..7bdcb3f5d61 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UploadAction.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.sonar.server.updatecenter.ws; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.apache.commons.io.IOUtils.closeQuietly; +import static org.sonar.api.server.ws.Request.Part; + +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.server.platform.DefaultServerFileSystem; +import org.sonar.server.user.UserSession; + +public class UploadAction implements UpdateCenterWsAction { + + public static final String PARAM_FILE = "file"; + + private final UserSession userSession; + private final File downloadDir; + + public UploadAction(UserSession userSession, DefaultServerFileSystem fileSystem) { + this.userSession = userSession; + this.downloadDir = fileSystem.getDownloadedPluginsDir(); + } + + @Override + public void define(WebService.NewController context) { + WebService.NewAction action = context.createAction("upload") + .setDescription("Upload a plugin.
Requires 'Administer System' permission.") + .setSince("6.0") + .setPost(true) + .setInternal(true) + .setHandler(this); + + action.createParam(PARAM_FILE) + .setDescription("The jar file of the plugin to install") + .setRequired(true); + } + + @Override + public void handle(Request request, Response response) throws Exception { + userSession.checkPermission(GlobalPermissions.SYSTEM_ADMIN); + + Part part = request.mandatoryParamAsPart(PARAM_FILE); + String fileName = part.getFileName(); + checkArgument(fileName.endsWith(".jar"), "Only jar file is allowed"); + InputStream inputStream = part.getInputStream(); + try { + File destPlugin = new File(downloadDir, fileName); + Files.copy(inputStream, destPlugin.toPath(), REPLACE_EXISTING); + response.noContent(); + } finally { + closeQuietly(inputStream); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/package-info.java new file mode 100644 index 00000000000..bc02dde1a5c --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.server.updatecenter.ws; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-server/src/main/java/org/sonar/server/ws/LocalRequestAdapter.java b/server/sonar-server/src/main/java/org/sonar/server/ws/LocalRequestAdapter.java index f271938dcfb..98e452668ec 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ws/LocalRequestAdapter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ws/LocalRequestAdapter.java @@ -47,6 +47,11 @@ public class LocalRequestAdapter extends ValidatingRequest { return new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8)); } + @Override + protected Part readPart(String key) { + throw new UnsupportedOperationException("reading part is not supported yet by local WS calls"); + } + @Override public boolean hasParam(String key) { return localRequest.hasParam(key); diff --git a/server/sonar-server/src/main/java/org/sonar/server/ws/ServletRequest.java b/server/sonar-server/src/main/java/org/sonar/server/ws/ServletRequest.java index 998b64d70f7..75efe64c1f5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ws/ServletRequest.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ws/ServletRequest.java @@ -30,7 +30,7 @@ import java.io.InputStream; import java.util.Map; import javax.annotation.CheckForNull; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.Part; +import org.sonar.api.server.ws.internal.PartImpl; import org.sonar.api.server.ws.internal.ValidatingRequest; import org.sonarqube.ws.MediaTypes; @@ -73,15 +73,22 @@ public class ServletRequest extends ValidatingRequest { @Override protected InputStream readInputStreamParam(String key) { + Part part = readPart(key); + return (part == null) ? null : part.getInputStream(); + } + + @Override + @CheckForNull + public Part readPart(String key) { try { if (!isMultipartContent()) { return null; } - Part part = source.getPart(key); + javax.servlet.http.Part part = source.getPart(key); if (part == null || part.getSize() == 0) { return null; } - return part.getInputStream(); + return new PartImpl(part.getInputStream(), part.getSubmittedFileName()); } catch (Exception e) { throw new IllegalStateException("Can't read file part", e); } @@ -117,4 +124,5 @@ public class ServletRequest extends ValidatingRequest { public String getPath() { return source.getRequestURI().replaceFirst(source.getContextPath(), ""); } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/updatecenter/UpdateCenterModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/updatecenter/UpdateCenterModuleTest.java new file mode 100644 index 00000000000..8bbca3d0dfd --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/updatecenter/UpdateCenterModuleTest.java @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.sonar.server.updatecenter; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.sonar.core.platform.ComponentContainer; + +public class UpdateCenterModuleTest { + @Test + public void verify_count_of_added_components() { + ComponentContainer container = new ComponentContainer(); + new UpdateCenterModule().configure(container); + assertThat(container.size()).isEqualTo(2 + 4); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UpdateCenterWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UpdateCenterWsTest.java index a493b170afa..78b5140f8ff 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UpdateCenterWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UpdateCenterWsTest.java @@ -19,21 +19,24 @@ */ package org.sonar.server.updatecenter.ws; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + import org.junit.Before; import org.junit.Test; import org.sonar.api.server.ws.RailsHandler; import org.sonar.api.server.ws.WebService; +import org.sonar.server.platform.DefaultServerFileSystem; import org.sonar.server.ws.WsTester; -import static org.assertj.core.api.Assertions.assertThat; - public class UpdateCenterWsTest { WsTester tester; @Before public void setUp() { - tester = new WsTester(new UpdateCenterWs()); + tester = new WsTester(new UpdateCenterWs( + new UploadAction(null, mock(DefaultServerFileSystem.class)))); } @Test @@ -42,7 +45,7 @@ public class UpdateCenterWsTest { assertThat(controller).isNotNull(); assertThat(controller.since()).isEqualTo("2.10"); assertThat(controller.description()).isNotEmpty(); - assertThat(controller.actions()).hasSize(1); + assertThat(controller.actions()).hasSize(2); } @Test @@ -56,4 +59,15 @@ public class UpdateCenterWsTest { assertThat(action.params()).hasSize(1); } + @Test + public void define_upload_action() throws Exception { + WebService.Controller controller = tester.controller("api/updatecenter"); + + WebService.Action action = controller.action("upload"); + assertThat(action).isNotNull(); + assertThat(action.handler()).isNotNull(); + assertThat(action.isInternal()).isTrue(); + assertThat(action.isPost()).isTrue(); + assertThat(action.params()).hasSize(1); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UploadActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UploadActionTest.java new file mode 100644 index 00000000000..14589593da6 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UploadActionTest.java @@ -0,0 +1,149 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.sonar.server.updatecenter.ws; + +import static java.nio.file.Files.newInputStream; +import static org.assertj.core.api.Java6Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.core.permission.GlobalPermissions.PROVISIONING; +import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; +import static org.sonar.test.ExceptionCauseMatcher.hasType; + +import java.io.File; +import java.io.InputStream; +import java.nio.channels.ClosedChannelException; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.platform.DefaultServerFileSystem; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.TestResponse; +import org.sonar.server.ws.WsActionTester; + +public class UploadActionTest { + + static final String PLUGIN_NAME = "plugin.jar"; + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + + DefaultServerFileSystem fileSystem = mock(DefaultServerFileSystem.class); + File pluginDirectory; + + File plugin = new File(getClass().getResource("UploadActionTest/plugin.jar").getFile()); + WsActionTester wsTester; + + @Before + public void setUp() throws Exception { + pluginDirectory = folder.newFolder(); + when(fileSystem.getDownloadedPluginsDir()).thenReturn(pluginDirectory); + wsTester = new WsActionTester(new UploadAction(userSession, fileSystem)); + } + + @Test + public void upload_plugin() throws Exception { + setSystemAdminUser(); + + TestResponse response = call(newInputStream(plugin.toPath()), PLUGIN_NAME); + + assertThat(response.getStatus()).isEqualTo(204); + assertPluginIsUploaded(PLUGIN_NAME); + } + + @Test + public void erase_existing_plugin_if_already_exists() throws Exception { + setSystemAdminUser(); + + File plugin1 = new File(getClass().getResource("UploadActionTest/plugin.jar").getFile()); + call(newInputStream(plugin1.toPath()), PLUGIN_NAME); + + File plugin2 = new File(getClass().getResource("UploadActionTest/anotherPlugin.jar").getFile()); + call(newInputStream(plugin2.toPath()), PLUGIN_NAME); + + File result = new File(pluginDirectory, PLUGIN_NAME); + assertThat(result.exists()).isTrue(); + assertThat(result.length()).isNotEqualTo(plugin1.length()).isEqualTo(plugin2.length()); + } + + @Test + public void fail_when_plugin_extension_is_not_jar() throws Exception { + setSystemAdminUser(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Only jar file is allowed"); + call(newInputStream(plugin.toPath()), "plugin.zip"); + } + + @Test + public void fail_when_no_files_param() throws Exception { + setSystemAdminUser(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The 'file' parameter is missing"); + wsTester.newRequest().execute(); + } + + @Test + public void input_stream_should_be_closed() throws Exception { + setSystemAdminUser(); + + InputStream inputStream = newInputStream(plugin.toPath()); + call(inputStream, PLUGIN_NAME); + + // As the same InputStream is used, it will fail as it should have been called during the first execution of the WS + expectedException.expectCause(hasType(ClosedChannelException.class)); + call(inputStream, PLUGIN_NAME); + } + + @Test + public void fail_if_not_system_admin() throws Exception { + userSession.login().setGlobalPermissions(PROVISIONING); + + expectedException.expect(ForbiddenException.class); + call(newInputStream(plugin.toPath()), PLUGIN_NAME); + } + + private TestResponse call(InputStream inputStream, String fileName) { + return wsTester.newRequest() + .setPart("file", inputStream, fileName) + .execute(); + } + + private void setSystemAdminUser() { + userSession.login().setGlobalPermissions(SYSTEM_ADMIN); + } + + private void assertPluginIsUploaded(String pluginName) { + File result = new File(pluginDirectory, pluginName); + assertThat(result.exists()).isTrue(); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java b/server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java index e02e2c04a5a..ac7e439c5a2 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java @@ -22,15 +22,18 @@ package org.sonar.server.ws; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Throwables; +import com.google.common.collect.Maps; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.IOUtils; +import org.sonar.api.server.ws.internal.PartImpl; import org.sonar.api.server.ws.internal.ValidatingRequest; public class TestRequest extends ValidatingRequest { private final Map params = new HashMap<>(); + private final Map parts = Maps.newHashMap(); private String method = "GET"; private String mimeType = "application/octet-stream"; private String path; @@ -49,6 +52,16 @@ public class TestRequest extends ValidatingRequest { return IOUtils.toInputStream(value); } + @Override + protected Part readPart(String key) { + return parts.get(key); + } + + public TestRequest setPart(String key, InputStream input, String fileName) { + parts.put(key, new PartImpl(input, fileName)); + return this; + } + @Override public String method() { return method; diff --git a/server/sonar-server/src/test/java/org/sonar/server/ws/TestResponse.java b/server/sonar-server/src/test/java/org/sonar/server/ws/TestResponse.java index 2dea511d8c3..5fcdcc523d3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ws/TestResponse.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ws/TestResponse.java @@ -42,4 +42,8 @@ public class TestResponse { public String getMediaType() { return dumbResponse.stream().mediaType(); } + + public int getStatus() { + return dumbResponse.stream().status(); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java b/server/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java index 5f30364e2b8..b72623743d1 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ws/WebServiceEngineTest.java @@ -23,12 +23,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.google.common.collect.Maps; import java.io.IOException; -import java.io.InputStream; import java.util.Locale; -import java.util.Map; -import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Before; @@ -73,7 +69,7 @@ public class WebServiceEngineTest { @Test public void execute_request() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/health"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -82,7 +78,7 @@ public class WebServiceEngineTest { @Test public void execute_request_when_path_does_not_begin_with_slash() { - ValidatingRequest request = new SimpleRequest("GET", "api/system/health"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -91,7 +87,7 @@ public class WebServiceEngineTest { @Test public void execute_request_with_action_suffix() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/health"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -100,7 +96,7 @@ public class WebServiceEngineTest { @Test public void bad_request_if_action_suffix_is_not_supported() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/health.bat"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health.bat"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -111,7 +107,7 @@ public class WebServiceEngineTest { @Test public void no_content() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/alive"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/alive"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -120,7 +116,7 @@ public class WebServiceEngineTest { @Test public void bad_controller() { - ValidatingRequest request = new SimpleRequest("GET", "/api/xxx/health"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/xxx/health"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -129,7 +125,7 @@ public class WebServiceEngineTest { @Test public void bad_action() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/xxx"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/xxx"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -138,7 +134,7 @@ public class WebServiceEngineTest { @Test public void method_get_not_allowed() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/ping"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/ping"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -147,7 +143,7 @@ public class WebServiceEngineTest { @Test public void method_post_required() { - ValidatingRequest request = new SimpleRequest("POST", "/api/system/ping"); + ValidatingRequest request = new TestRequest().setMethod("POST").setPath("/api/system/ping"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -156,7 +152,7 @@ public class WebServiceEngineTest { @Test public void unknown_parameter_is_set() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/fail_with_undeclared_parameter").setParam("unknown", "Unknown"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail_with_undeclared_parameter").setParam("unknown", "Unknown"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -165,7 +161,7 @@ public class WebServiceEngineTest { @Test public void required_parameter_is_not_set() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/print"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -174,7 +170,7 @@ public class WebServiceEngineTest { @Test public void optional_parameter_is_not_set() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/print").setParam("message", "Hello World"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print").setParam("message", "Hello World"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -183,7 +179,7 @@ public class WebServiceEngineTest { @Test public void optional_parameter_is_set() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/print") + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print") .setParam("message", "Hello World") .setParam("author", "Marcel"); DumbResponse response = new DumbResponse(); @@ -194,7 +190,7 @@ public class WebServiceEngineTest { @Test public void param_value_is_in_possible_values() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/print") + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print") .setParam("message", "Hello World") .setParam("format", "json"); DumbResponse response = new DumbResponse(); @@ -205,7 +201,7 @@ public class WebServiceEngineTest { @Test public void param_value_is_not_in_possible_values() { - ValidatingRequest request = new SimpleRequest("GET", "api/system/print") + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print") .setParam("message", "Hello World") .setParam("format", "html"); DumbResponse response = new DumbResponse(); @@ -216,7 +212,7 @@ public class WebServiceEngineTest { @Test public void internal_error() { - ValidatingRequest request = new SimpleRequest("GET", "/api/system/fail"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -228,7 +224,7 @@ public class WebServiceEngineTest { @Test public void bad_request_with_i18n_message() { userSessionRule.setLocale(Locale.ENGLISH); - ValidatingRequest request = new SimpleRequest("GET", "api/system/fail_with_i18n_message").setParam("count", "3"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail_with_i18n_message").setParam("count", "3"); DumbResponse response = new DumbResponse(); when(i18n.message(Locale.ENGLISH, "bad.request.reason", "bad.request.reason", 0)).thenReturn("reason #0"); @@ -242,7 +238,7 @@ public class WebServiceEngineTest { @Test public void bad_request_with_multiple_messages() { - ValidatingRequest request = new SimpleRequest("GET", "api/system/fail_with_multiple_messages").setParam("count", "3"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail_with_multiple_messages").setParam("count", "3"); DumbResponse response = new DumbResponse(); underTest.execute(request, response); @@ -260,7 +256,7 @@ public class WebServiceEngineTest { public void bad_request_with_multiple_i18n_messages() { userSessionRule.setLocale(Locale.ENGLISH); - ValidatingRequest request = new SimpleRequest("GET", "api/system/fail_with_multiple_i18n_messages").setParam("count", "3"); + ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail_with_multiple_i18n_messages").setParam("count", "3"); DumbResponse response = new DumbResponse(); when(i18n.message(Locale.ENGLISH, "bad.request.reason", "bad.request.reason", 0)).thenReturn("reason #0"); when(i18n.message(Locale.ENGLISH, "bad.request.reason", "bad.request.reason", 1)).thenReturn("reason #1"); @@ -286,62 +282,6 @@ public class WebServiceEngineTest { assertThat(response.getHeader(name)).isEqualTo(value); } - private static class SimpleRequest extends ValidatingRequest { - private final String method; - private String path; - private Map params = Maps.newHashMap(); - - private SimpleRequest(String method, String path) { - this.method = method; - this.path = path; - } - - @Override - public String method() { - return method; - } - - @Override - public String getMediaType() { - return MediaTypes.JSON; - } - - @Override - public boolean hasParam(String key) { - return params.keySet().contains(key); - } - - @Override - public String getPath() { - return path; - } - - @Override - protected String readParam(String key) { - return params.get(key); - } - - @Override - protected InputStream readInputStreamParam(String key) { - String param = readParam(key); - - return param == null ? null : IOUtils.toInputStream(param); - } - - public SimpleRequest setParams(Map m) { - this.params = m; - return this; - } - - public SimpleRequest setParam(String key, @Nullable String value) { - if (value != null) { - params.put(key, value); - } - return this; - } - - } - static class SystemWs implements WebService { @Override public void define(Context context) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/ws/WsTester.java b/server/sonar-server/src/test/java/org/sonar/server/ws/WsTester.java index 7b0e580cdb0..0d4036364de 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ws/WsTester.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ws/WsTester.java @@ -37,6 +37,7 @@ import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; +import org.sonar.api.server.ws.internal.PartImpl; import org.sonar.api.server.ws.internal.ValidatingRequest; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.utils.text.XmlWriter; @@ -55,6 +56,7 @@ public class WsTester { private String path; private Map params = Maps.newHashMap(); + private final Map parts = Maps.newHashMap(); private TestRequest(String method) { this.method = method; @@ -109,6 +111,16 @@ public class WsTester { return param == null ? null : IOUtils.toInputStream(param); } + @Override + protected Part readPart(String key) { + return parts.get(key); + } + + public TestRequest setPart(String key, InputStream input, String fileName) { + parts.put(key, new PartImpl(input, fileName)); + return this; + } + public Result execute() throws Exception { TestResponse response = new TestResponse(); verifyRequest(action(), this); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/updatecenter/ws/UploadActionTest/anotherPlugin.jar b/server/sonar-server/src/test/resources/org/sonar/server/updatecenter/ws/UploadActionTest/anotherPlugin.jar new file mode 100644 index 0000000000000000000000000000000000000000..c7bc88e1d0ec97e6ef130bc9a4eca6ae6de4b817 GIT binary patch literal 3421 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 literal 0 HcmV?d00001 diff --git a/server/sonar-server/src/test/resources/org/sonar/server/updatecenter/ws/UploadActionTest/plugin.jar b/server/sonar-server/src/test/resources/org/sonar/server/updatecenter/ws/UploadActionTest/plugin.jar new file mode 100644 index 0000000000000000000000000000000000000000..089521146df02be5fe82b85bc8dd3a5cbddeddc0 GIT binary patch literal 4454 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 Ckt;R; literal 0 HcmV?d00001 diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java index 50dbabfdc58..48d9f427b2e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java @@ -19,6 +19,8 @@ */ package org.sonar.api.server.ws; +import static com.google.common.base.Preconditions.checkArgument; + import com.google.common.annotations.Beta; import com.google.common.base.Splitter; import com.google.common.collect.Lists; @@ -122,6 +124,15 @@ public abstract class Request { @CheckForNull public abstract InputStream paramAsInputStream(String key); + @CheckForNull + public abstract Part paramAsPart(String key); + + public Part mandatoryParamAsPart(String key) { + Part part = paramAsPart(key); + checkArgument(part != null, "The '%s' parameter is missing", key); + return part; + } + /** * @deprecated to be dropped in 4.4. Default values are declared in ws metadata */ @@ -270,4 +281,13 @@ public abstract class Request { * @since 6.0 */ public abstract String getPath(); + + /** + * @since 6.0 + */ + public interface Part { + InputStream getInputStream(); + + String getFileName(); + } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/PartImpl.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/PartImpl.java new file mode 100644 index 00000000000..a0d61f3a5e0 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/PartImpl.java @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.sonar.api.server.ws.internal; + +import java.io.InputStream; +import org.sonar.api.server.ws.Request; + +public class PartImpl implements Request.Part { + + private final InputStream inputStream; + private final String fileName; + + public PartImpl(InputStream inputStream, String fileName) { + this.inputStream = inputStream; + this.fileName = fileName; + } + + @Override + public InputStream getInputStream() { + return inputStream; + } + + @Override + public String getFileName() { + return fileName; + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/SimpleGetRequest.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/SimpleGetRequest.java index 3e583f691a1..9b2348f48d2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/SimpleGetRequest.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/SimpleGetRequest.java @@ -37,6 +37,7 @@ import org.sonar.api.server.ws.Request; public class SimpleGetRequest extends Request { private final Map params = Maps.newHashMap(); + private final Map parts = Maps.newHashMap(); private String mediaType = "application/json"; private String path; @@ -82,6 +83,16 @@ public class SimpleGetRequest extends Request { return params; } + @Override + public Part paramAsPart(String key) { + return parts.get(key); + } + + public SimpleGetRequest setPart(String key, InputStream input, String fileName) { + parts.put(key, new PartImpl(input, fileName)); + return this; + } + @Override public LocalConnector localConnector() { throw new UnsupportedOperationException(); @@ -96,4 +107,5 @@ public class SimpleGetRequest extends Request { this.path = path; return this; } + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/ValidatingRequest.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/ValidatingRequest.java index 58615ec636e..fd1c2e2270d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/ValidatingRequest.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/internal/ValidatingRequest.java @@ -19,6 +19,8 @@ */ package org.sonar.api.server.ws.internal; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.collect.Lists; @@ -34,8 +36,6 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.log.Loggers; -import static com.google.common.base.Preconditions.checkNotNull; - /** * @since 4.2 */ @@ -74,6 +74,12 @@ public abstract class ValidatingRequest extends Request { return readInputStreamParam(key); } + @Override + @CheckForNull + public Part paramAsPart(String key) { + return readPart(key); + } + @CheckForNull private String param(String key, boolean validateValue) { WebService.Param definition = action.param(key); @@ -139,6 +145,9 @@ public abstract class ValidatingRequest extends Request { @CheckForNull protected abstract InputStream readInputStreamParam(String key); + @CheckForNull + protected abstract Part readPart(String key); + private static void validate(String value, WebService.Param definition) { Set possibleValues = definition.possibleValues(); if (possibleValues != null && !possibleValues.contains(value)) { diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java index a8b939ee116..8f6613176c5 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java @@ -32,6 +32,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.rule.RuleStatus; +import org.sonar.api.server.ws.internal.PartImpl; import org.sonar.api.server.ws.internal.ValidatingRequest; import org.sonar.api.utils.DateUtils; @@ -259,9 +260,30 @@ public class RequestTest { assertThat(IOUtils.toString(underTest.setParam("a_string", "foo").paramAsInputStream("a_string"))).isEqualTo("foo"); } + @Test + public void param_as_part() throws Exception { + InputStream inputStream = mock(InputStream.class); + underTest.setPart("key", inputStream, "filename"); + + Request.Part part = underTest.paramAsPart("key"); + assertThat(part.getInputStream()).isEqualTo(inputStream); + assertThat(part.getFileName()).isEqualTo("filename"); + + assertThat(underTest.paramAsPart("unknown")).isNull(); + } + + @Test + public void mandatory_param_as_part() throws Exception { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The 'required_param' parameter is missing"); + + underTest.mandatoryParamAsPart("required_param"); + } + private static class FakeRequest extends ValidatingRequest { private final Map params = Maps.newHashMap(); + private final Map parts = Maps.newHashMap(); @Override public String method() { @@ -301,6 +323,16 @@ public class RequestTest { return param == null ? null : IOUtils.toInputStream(param); } + + @Override + protected Part readPart(String key) { + return parts.get(key); + } + + public FakeRequest setPart(String key, InputStream input, String fileName) { + parts.put(key, new PartImpl(input, fileName)); + return this; + } } private static class FakeWs implements WebService { diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/internal/SimpleGetRequestTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/internal/SimpleGetRequestTest.java index 9d80da9247f..08f69177262 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/internal/SimpleGetRequestTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/internal/SimpleGetRequestTest.java @@ -19,29 +19,44 @@ */ package org.sonar.api.server.ws.internal; -import org.junit.Test; - import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import java.io.InputStream; +import org.junit.Test; +import org.sonar.api.server.ws.Request; public class SimpleGetRequestTest { + SimpleGetRequest underTest = new SimpleGetRequest(); + @Test public void method() { - SimpleGetRequest request = new SimpleGetRequest(); - assertThat(request.method()).isEqualTo("GET"); + assertThat(underTest.method()).isEqualTo("GET"); - request.setParam("foo", "bar"); - assertThat(request.param("foo")).isEqualTo("bar"); - assertThat(request.param("unknown")).isNull(); + underTest.setParam("foo", "bar"); + assertThat(underTest.param("foo")).isEqualTo("bar"); + assertThat(underTest.param("unknown")).isNull(); } @Test public void has_param() { - SimpleGetRequest request = new SimpleGetRequest(); - assertThat(request.method()).isEqualTo("GET"); + assertThat(underTest.method()).isEqualTo("GET"); + + underTest.setParam("foo", "bar"); + assertThat(underTest.hasParam("foo")).isTrue(); + assertThat(underTest.hasParam("unknown")).isFalse(); + } + + @Test + public void get_part() throws Exception { + InputStream inputStream = mock(InputStream.class); + underTest.setPart("key", inputStream, "filename"); + + Request.Part part = underTest.paramAsPart("key"); + assertThat(part.getInputStream()).isEqualTo(inputStream); + assertThat(part.getFileName()).isEqualTo("filename"); - request.setParam("foo", "bar"); - assertThat(request.hasParam("foo")).isTrue(); - assertThat(request.hasParam("unknown")).isFalse(); + assertThat(underTest.paramAsPart("unknown")).isNull(); } } -- 2.39.5