diff options
Diffstat (limited to 'sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java')
-rw-r--r-- | sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java | 172 |
1 files changed, 96 insertions, 76 deletions
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java index 21d6b3c5317..907e13f92bc 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java @@ -19,98 +19,113 @@ */ package org.sonar.scanner.bootstrap; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.SocketTimeoutException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.Optional; -import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; -import okhttp3.HttpUrl; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okio.Buffer; import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.scanner.bootstrap.ScannerPluginInstaller.InstalledPlugin; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonarqube.ws.client.HttpConnector; import org.sonarqube.ws.client.WsClientFactories; +import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.exactly; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.notFound; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.serverError; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.apache.commons.io.FileUtils.moveFile; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.ThrowableAssert.ThrowingCallable; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.sonar.scanner.bootstrap.PluginFiles.PLUGINS_DOWNLOAD_TIMEOUT_PROPERTY; -public class PluginFilesTest { +class PluginFilesTest { - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - @Rule - public MockWebServer server = new MockWebServer(); + @RegisterExtension + static WireMockExtension sonarqube = WireMockExtension.newInstance() + .options(wireMockConfig().dynamicPort()) + .build(); + + @TempDir + private Path tempDir; + + private final SonarUserHome sonarUserHome = mock(SonarUserHome.class); private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); - private File userHome; private PluginFiles underTest; - @Before - public void setUp() throws Exception { - HttpConnector connector = HttpConnector.newBuilder().acceptGzip(true).url(server.url("/").toString()).build(); + @BeforeEach + void setUp(@TempDir Path sonarUserHomeDir) throws Exception { + when(sonarUserHome.getPath()).thenReturn(sonarUserHomeDir); + + HttpConnector connector = HttpConnector.newBuilder().acceptGzip(true).url(sonarqube.url("/")).build(); GlobalAnalysisMode analysisMode = new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())); DefaultScannerWsClient wsClient = new DefaultScannerWsClient(WsClientFactories.getDefault().newClient(connector), false, analysisMode, analysisWarnings); - userHome = temp.newFolder(); MapSettings settings = new MapSettings(); - settings.setProperty("sonar.userHome", userHome.getAbsolutePath()); settings.setProperty(PLUGINS_DOWNLOAD_TIMEOUT_PROPERTY, 1); - underTest = new PluginFiles(wsClient, settings.asConfig()); + underTest = new PluginFiles(wsClient, settings.asConfig(), sonarUserHome); } @Test - public void get_jar_from_cache_if_present() throws Exception { + void get_jar_from_cache_if_present() throws Exception { FileAndMd5 jar = createFileInCache("foo"); File result = underTest.get(newInstalledPlugin("foo", jar.md5)).get(); - verifySameContent(result, jar); + verifySameContent(result.toPath(), jar); // no requests to server - assertThat(server.getRequestCount()).isZero(); + sonarqube.verify(0, anyRequestedFor(anyUrl())); } @Test - public void download_and_add_jar_to_cache_if_missing() throws Exception { + void download_and_add_jar_to_cache_if_missing() throws Exception { FileAndMd5 tempJar = new FileAndMd5(); - enqueueDownload(tempJar); + stubDownload(tempJar); InstalledPlugin plugin = newInstalledPlugin("foo", tempJar.md5); File result = underTest.get(plugin).get(); - verifySameContent(result, tempJar); - HttpUrl requestedUrl = server.takeRequest().getRequestUrl(); - assertThat(requestedUrl.encodedPath()).isEqualTo("/api/plugins/download"); - assertThat(requestedUrl.encodedQuery()).isEqualTo("plugin=foo"); + verifySameContent(result.toPath(), tempJar); + + sonarqube.verify(exactly(1), getRequestedFor(urlEqualTo("/api/plugins/download?plugin=foo"))); // get from cache on second call result = underTest.get(plugin).get(); - verifySameContent(result, tempJar); - assertThat(server.getRequestCount()).isOne(); + verifySameContent(result.toPath(), tempJar); + + sonarqube.verify(exactly(1), getRequestedFor(urlEqualTo("/api/plugins/download?plugin=foo"))); } @Test - public void return_empty_if_plugin_not_found_on_server() { - server.enqueue(new MockResponse().setResponseCode(404)); + void return_empty_if_plugin_not_found_on_server() { + sonarqube.stubFor(get(anyUrl()) + .willReturn(notFound())); InstalledPlugin plugin = newInstalledPlugin("foo", "abc"); Optional<File> result = underTest.get(plugin); @@ -119,9 +134,9 @@ public class PluginFilesTest { } @Test - public void fail_if_integrity_of_download_is_not_valid() throws IOException { + void fail_if_integrity_of_download_is_not_valid() throws IOException { FileAndMd5 tempJar = new FileAndMd5(); - enqueueDownload(tempJar.file, "invalid_hash"); + stubDownload(tempJar.file, "invalid_hash"); InstalledPlugin plugin = newInstalledPlugin("foo", "abc"); expectISE("foo", "was expected to have checksum invalid_hash but had " + tempJar.md5, @@ -129,63 +144,68 @@ public class PluginFilesTest { } @Test - public void fail_if_md5_header_is_missing_from_response() throws IOException { - File tempJar = temp.newFile(); - enqueueDownload(tempJar, null); + void fail_if_md5_header_is_missing_from_response(@TempDir Path tempDir) throws IOException { + var tempJar = Files.createTempFile(tempDir, "plugin", ".jar"); + stubDownload(tempJar, null); InstalledPlugin plugin = newInstalledPlugin("foo", "abc"); expectISE("foo", "did not return header Sonar-MD5", () -> underTest.get(plugin)); } @Test - public void fail_if_server_returns_error() { - server.enqueue(new MockResponse().setResponseCode(500)); + void fail_if_server_returns_error() { + sonarqube.stubFor(get(anyUrl()) + .willReturn(serverError())); + InstalledPlugin plugin = newInstalledPlugin("foo", "abc"); expectISE("foo", "returned code 500", () -> underTest.get(plugin)); } @Test - public void getPlugin_whenTimeOutReached_thenDownloadFails() { - MockResponse response = new MockResponse().setBody("test").setBodyDelay(2, TimeUnit.SECONDS); - response.setHeader("Sonar-MD5", "md5"); - server.enqueue(response); + void getPlugin_whenTimeOutReached_thenDownloadFails() { + sonarqube.stubFor(get(anyUrl()) + .willReturn(ok() + .withFixedDelay(5000))); + InstalledPlugin plugin = newInstalledPlugin("foo", "abc"); assertThatThrownBy(() -> underTest.get(plugin)) .isInstanceOf(IllegalStateException.class) - .hasMessageStartingWith("Fail to download plugin [" + plugin.key + "]") + .hasMessageStartingWith("Fail to request url") .cause().isInstanceOf(SocketTimeoutException.class); } @Test - public void download_a_new_version_of_plugin_during_blue_green_switch() throws IOException { + void download_a_new_version_of_plugin_during_blue_green_switch() throws IOException { FileAndMd5 tempJar = new FileAndMd5(); - enqueueDownload(tempJar); + stubDownload(tempJar); // expecting to download plugin foo with checksum "abc" InstalledPlugin pluginV1 = newInstalledPlugin("foo", "abc"); File result = underTest.get(pluginV1).get(); - verifySameContent(result, tempJar); + verifySameContent(result.toPath(), tempJar); // new version of downloaded jar is put in cache with the new md5 InstalledPlugin pluginV2 = newInstalledPlugin("foo", tempJar.md5); result = underTest.get(pluginV2).get(); - verifySameContent(result, tempJar); - assertThat(server.getRequestCount()).isOne(); + verifySameContent(result.toPath(), tempJar); + + sonarqube.verify(exactly(1), getRequestedFor(urlEqualTo("/api/plugins/download?plugin=foo"))); // v1 still requests server and downloads v2 - enqueueDownload(tempJar); + stubDownload(tempJar); result = underTest.get(pluginV1).get(); - verifySameContent(result, tempJar); - assertThat(server.getRequestCount()).isEqualTo(2); + verifySameContent(result.toPath(), tempJar); + + sonarqube.verify(exactly(2), getRequestedFor(urlEqualTo("/api/plugins/download?plugin=foo"))); } @Test - public void fail_if_cached_file_is_outside_cache_dir() throws IOException { + void fail_if_cached_file_is_outside_cache_dir() throws IOException { FileAndMd5 tempJar = new FileAndMd5(); - enqueueDownload(tempJar); + stubDownload(tempJar); InstalledPlugin plugin = newInstalledPlugin("foo/bar", "abc"); @@ -200,29 +220,29 @@ public class PluginFilesTest { } private FileAndMd5 moveToCache(String pluginKey, FileAndMd5 jar) throws IOException { - File jarInCache = new File(userHome, "cache/" + jar.md5 + "/sonar-" + pluginKey + "-plugin.jar"); - moveFile(jar.file, jarInCache); + Path jarInCache = sonarUserHome.getPath().resolve("cache/" + jar.md5 + "/sonar-" + pluginKey + "-plugin.jar"); + moveFile(jar.file.toFile(), jarInCache.toFile()); return new FileAndMd5(jarInCache, jar.md5); } /** * Enqueue download of file with valid MD5 */ - private void enqueueDownload(FileAndMd5 file) throws IOException { - enqueueDownload(file.file, file.md5); + private void stubDownload(FileAndMd5 file) throws IOException { + stubDownload(file.file, file.md5); } /** * Enqueue download of file with a MD5 that may not be returned (null) or not valid */ - private void enqueueDownload(File file, @Nullable String md5) throws IOException { - Buffer body = new Buffer(); - body.write(FileUtils.readFileToByteArray(file)); - MockResponse response = new MockResponse().setBody(body); + private void stubDownload(Path file, @Nullable String md5) throws IOException { + var responseDefBuilder = ok(); if (md5 != null) { - response.setHeader("Sonar-MD5", md5); + responseDefBuilder.withHeader("Sonar-MD5", md5); } - server.enqueue(response); + responseDefBuilder.withBody(Files.readAllBytes(file)); + sonarqube.stubFor(get(urlMatching("/api/plugins/download\\?plugin=.*")) + .willReturn(responseDefBuilder)); } private static InstalledPlugin newInstalledPlugin(String pluginKey, String fileChecksum) { @@ -232,10 +252,10 @@ public class PluginFilesTest { return plugin; } - private static void verifySameContent(File file1, FileAndMd5 file2) { - assertThat(file1).isFile().exists(); - assertThat(file2.file).isFile().exists(); - assertThat(file1).hasSameContentAs(file2.file); + private static void verifySameContent(Path file1, FileAndMd5 file2) { + assertThat(file1).isRegularFile(); + assertThat(file2.file).isRegularFile(); + assertThat(file1).hasSameTextualContentAs(file2.file); } private void expectISE(String pluginKey, String message, ThrowingCallable shouldRaiseThrowable) { @@ -246,18 +266,18 @@ public class PluginFilesTest { } private class FileAndMd5 { - private final File file; + private final Path file; private final String md5; - FileAndMd5(File file, String md5) { + FileAndMd5(Path file, String md5) { this.file = file; this.md5 = md5; } FileAndMd5() throws IOException { - this.file = temp.newFile(); - FileUtils.write(this.file, RandomStringUtils.random(3)); - try (InputStream fis = FileUtils.openInputStream(this.file)) { + this.file = Files.createTempFile(tempDir, "jar", null); + Files.write(this.file, RandomStringUtils.random(3).getBytes()); + try (InputStream fis = Files.newInputStream(this.file)) { this.md5 = DigestUtils.md5Hex(fis); } catch (IOException e) { throw new IllegalStateException("Fail to compute md5 of " + this.file, e); |