From 829584d08029c89fd328b9f9722c10bba4042c03 Mon Sep 17 00:00:00 2001 From: Leticia Konno Date: Tue, 21 Nov 2023 12:18:16 -0300 Subject: [PATCH] test(itest): updated the tests and created a localstack resource (#160) Co-authored-by: Andrew Azores --- .../io/cryostat/recordings/Recordings.java | 16 +- .../java/io/cryostat/reports/Reports.java | 10 +- .../resources/application-test.properties | 13 +- .../cryostat/resources/GrafanaResource.java | 12 +- .../resources/JFRDatasourceResource.java | 12 +- .../resources/LocalStackResource.java | 93 ++++++++ src/test/java/itest/NonExistentTargetIT.java | 2 - src/test/java/itest/NoopAuthV2IT.java | 40 ++-- src/test/java/itest/NotificationsUrlIT.java | 74 +++---- ...flowIT.java => RecordingWorkflowTest.java} | 199 +++++++----------- .../java/itest/bases/StandardSelfTest.java | 5 + src/test/java/itest/util/Utils.java | 34 +++ 12 files changed, 291 insertions(+), 219 deletions(-) create mode 100644 src/test/java/io/cryostat/resources/LocalStackResource.java rename src/test/java/itest/{RecordingWorkflowIT.java => RecordingWorkflowTest.java} (61%) diff --git a/src/main/java/io/cryostat/recordings/Recordings.java b/src/main/java/io/cryostat/recordings/Recordings.java index f70ad6358..88de455d3 100644 --- a/src/main/java/io/cryostat/recordings/Recordings.java +++ b/src/main/java/io/cryostat/recordings/Recordings.java @@ -305,28 +305,20 @@ public List agentGet(@RestPath String jvmId) { @DELETE @Blocking - @Path("/api/beta/recordings/{jvmId}/{filename}") + @Path("/api/beta/recordings/{connectUrl}/{filename}") @RolesAllowed("write") public void agentDelete( - @RestPath String jvmId, + @RestPath String connectUrl, @RestPath String filename, @RestForm("recording") FileUpload recording, @RestForm("labels") JsonObject rawLabels) throws Exception { - var metadata = - recordingHelper - .getArchivedRecordingMetadata(jvmId, filename) - .orElseGet(Metadata::empty); - var connectUrl = - Target.getTargetByJvmId(jvmId) - .map(t -> t.connectUrl) - .map(c -> c.toString()) - .orElseGet(() -> metadata.labels.computeIfAbsent("connectUrl", k -> jvmId)); + var target = Target.getTargetByConnectUrl(URI.create(connectUrl)); var resp = storage.deleteObject( DeleteObjectRequest.builder() .bucket(archiveBucket) - .key(recordingHelper.archivedRecordingKey(jvmId, filename)) + .key(recordingHelper.archivedRecordingKey(target.jvmId, filename)) .build()); if (resp.sdkHttpResponse().isSuccessful()) { bus.publish( diff --git a/src/main/java/io/cryostat/reports/Reports.java b/src/main/java/io/cryostat/reports/Reports.java index 49eaab0b5..4f19da8c3 100644 --- a/src/main/java/io/cryostat/reports/Reports.java +++ b/src/main/java/io/cryostat/reports/Reports.java @@ -16,13 +16,10 @@ package io.cryostat.reports; import java.io.BufferedInputStream; -import java.io.IOException; import java.net.URI; import java.util.HashMap; import java.util.Map; -import org.openjdk.jmc.flightrecorder.CouldNotLoadRecordingException; - import io.cryostat.ConfigProperties; import io.cryostat.Producers; import io.cryostat.core.reports.InterruptibleReportGenerator; @@ -108,8 +105,7 @@ public Response getV1(@RestPath String recordingName) { @Path("/api/v3/reports/{encodedKey}") @Produces(MediaType.APPLICATION_JSON) @RolesAllowed("read") - public Uni> get(@RestPath String encodedKey) - throws IOException, CouldNotLoadRecordingException { + public Uni> get(@RestPath String encodedKey) { // TODO implement query parameter for evaluation predicate return Uni.createFrom() .future( @@ -122,7 +118,7 @@ public Uni> get(@RestPath String encodedKey) @Blocking @GET @Path("/api/v1/targets/{targetId}/reports/{recordingName}") - @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_HTML, MediaType.TEXT_PLAIN}) + @Produces({MediaType.APPLICATION_JSON}) @RolesAllowed("read") @Deprecated(since = "3.0", forRemoval = true) public Response getActiveV1(@RestPath String targetId, @RestPath String recordingName) { @@ -146,7 +142,7 @@ public Response getActiveV1(@RestPath String targetId, @RestPath String recordin @CacheResult(cacheName = ACTIVE_CACHE) @GET @Path("/api/v3/targets/{targetId}/reports/{recordingId}") - @Produces(MediaType.APPLICATION_JSON) + @Produces({MediaType.APPLICATION_JSON}) @RolesAllowed("read") @Deprecated(since = "3.0", forRemoval = true) public Uni> getActive( diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index e98288880..9753b8dcb 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -18,15 +18,6 @@ quarkus.datasource.devservices.container-env.POSTGRESQL_DATABASE=quarkus quarkus.datasource.devservices.username=quarkus quarkus.datasource.devservices.password=quarkus quarkus.datasource.devservices.db-name=quarkus -# !!! -quarkus.s3.devservices.enabled=true -quarkus.s3.devservices.buckets=archivedrecordings -# FIXME the following overrides should not be required, but currently seem to help with testcontainers reliability -quarkus.aws.devservices.localstack.image-name=localstack/localstack:2.1.0 -quarkus.aws.devservices.localstack.container-properties.START_WEB=0 -quarkus.aws.devservices.localstack.container-properties.SERVICES=s3 -quarkus.aws.devservices.localstack.container-properties.EAGER_SERVICE_LOADING=1 -quarkus.aws.devservices.localstack.container-properties.SKIP_SSL_CERT_DOWNLOAD=1 -quarkus.aws.devservices.localstack.container-properties.SKIP_INFRA_DOWNLOADS=1 -quarkus.aws.devservices.localstack.container-properties.DISABLE_EVENTS=1 +quarkus.s3.devservices.enabled=false +# !!! diff --git a/src/test/java/io/cryostat/resources/GrafanaResource.java b/src/test/java/io/cryostat/resources/GrafanaResource.java index 8cd5a5bc3..fb8bf2d44 100644 --- a/src/test/java/io/cryostat/resources/GrafanaResource.java +++ b/src/test/java/io/cryostat/resources/GrafanaResource.java @@ -27,9 +27,9 @@ public class GrafanaResource implements QuarkusTestResourceLifecycleManager, DevServicesContext.ContextAware { - private static int GRAFANA_PORT = 3000; - private static String IMAGE_NAME = "quay.io/cryostat/cryostat-grafana-dashboard:latest"; - private static Map envMap = + private static final int GRAFANA_PORT = 3000; + private static final String IMAGE_NAME = "quay.io/cryostat/cryostat-grafana-dashboard:latest"; + private static final Map envMap = Map.of( "GF_INSTALL_PLUGINS", "grafana-simple-json-datasource", "GF_AUTH_ANONYMOUS_ENABLED", "true", @@ -59,8 +59,10 @@ public Map start() { @Override public void stop() { - container.stop(); - container.close(); + if (container != null) { + container.stop(); + container.close(); + } } @Override diff --git a/src/test/java/io/cryostat/resources/JFRDatasourceResource.java b/src/test/java/io/cryostat/resources/JFRDatasourceResource.java index 8f3e9a59f..0b33e9bd0 100644 --- a/src/test/java/io/cryostat/resources/JFRDatasourceResource.java +++ b/src/test/java/io/cryostat/resources/JFRDatasourceResource.java @@ -27,9 +27,9 @@ public class JFRDatasourceResource implements QuarkusTestResourceLifecycleManager, DevServicesContext.ContextAware { - private static int JFR_DATASOURCE_PORT = 8080; - private static String IMAGE_NAME = "quay.io/cryostat/jfr-datasource:latest"; - private static Map envMap = Map.of(); + private static final int JFR_DATASOURCE_PORT = 8080; + private static final String IMAGE_NAME = "quay.io/cryostat/jfr-datasource:latest"; + private static final Map envMap = Map.of(); private Optional containerNetworkId; private GenericContainer container; @@ -56,8 +56,10 @@ public Map start() { @Override public void stop() { - container.stop(); - container.close(); + if (container != null) { + container.stop(); + container.close(); + } } @Override diff --git a/src/test/java/io/cryostat/resources/LocalStackResource.java b/src/test/java/io/cryostat/resources/LocalStackResource.java new file mode 100644 index 000000000..63611d466 --- /dev/null +++ b/src/test/java/io/cryostat/resources/LocalStackResource.java @@ -0,0 +1,93 @@ +/* + * Copyright The Cryostat Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cryostat.resources; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import io.quarkus.test.common.DevServicesContext; +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; +import org.jboss.logging.Logger; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +public class LocalStackResource + implements QuarkusTestResourceLifecycleManager, DevServicesContext.ContextAware { + + private static int S3_PORT = 4566; + private static final String IMAGE_NAME = "docker.io/localstack/localstack:latest"; + private static final Map envMap = + Map.of( + "START_WEB", "0", + "SERVICES", "s3", + "EAGER_SERVICE_LOADING", "1", + "SKIP_SSL_CERT_DOWNLOAD", "1", + "DISABLE_EVENTS", "1"); + private static final Logger logger = Logger.getLogger(LocalStackResource.class); + private Optional containerNetworkId; + private GenericContainer container; + + @Override + public Map start() { + container = + new GenericContainer<>(DockerImageName.parse(IMAGE_NAME)) + .withExposedPorts(S3_PORT) + .withEnv(envMap) + .waitingFor(Wait.forHealthcheck()); + containerNetworkId.ifPresent(container::withNetworkMode); + + container.start(); + + String networkHostPort = + "http://" + container.getHost() + ":" + container.getMappedPort(S3_PORT); + + Map properties = new HashMap(); + properties.put("quarkus.s3.aws.region", "us-east-1"); + properties.put("s3.url.override", networkHostPort); + properties.put("quarkus.s3.endpoint-override", properties.get("s3.url.override")); + properties.put("quarkus.s3.path-style-access", "true"); + properties.put("quarkus.s3.aws.credentials.type", "static"); + properties.put("quarkus.s3.aws.credentials.static-provider.access-key-id", "unused"); + properties.put("quarkus.s3.aws.credentials.static-provider.secret-access-key", "unused"); + properties.put( + "aws.access-key-id", + properties.get("quarkus.s3.aws.credentials.static-provider.access-key-id")); + properties.put("aws.accessKeyId", properties.get("aws.access-key-id")); + properties.put( + "aws.secret-access-key", + properties.get("quarkus.s3.aws.credentials.static-provider.secret-access-key")); + properties.put("aws.secretAccessKey", properties.get("aws.secret-access-key")); + properties.entrySet().forEach(e -> System.setProperty(e.getKey(), e.getValue())); + logger.infov("Configured properties: {0}", properties); + + return properties; + } + + @Override + public void stop() { + if (container != null) { + container.stop(); + container.close(); + } + } + + @Override + public void setIntegrationTestContext(DevServicesContext context) { + containerNetworkId = context.containerNetworkId(); + } +} diff --git a/src/test/java/itest/NonExistentTargetIT.java b/src/test/java/itest/NonExistentTargetIT.java index 05f34f140..cf43d91c2 100644 --- a/src/test/java/itest/NonExistentTargetIT.java +++ b/src/test/java/itest/NonExistentTargetIT.java @@ -26,11 +26,9 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @QuarkusIntegrationTest -@Disabled("TODO") public class NonExistentTargetIT extends StandardSelfTest { static final String BAD_TARGET_CONNECT_URL = diff --git a/src/test/java/itest/NoopAuthV2IT.java b/src/test/java/itest/NoopAuthV2IT.java index 770d8830a..228b9812b 100644 --- a/src/test/java/itest/NoopAuthV2IT.java +++ b/src/test/java/itest/NoopAuthV2IT.java @@ -27,11 +27,9 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @QuarkusIntegrationTest -@Disabled("TODO") public class NoopAuthV2IT extends StandardSelfTest { HttpRequest req; @@ -44,23 +42,27 @@ void createRequest() { @Test public void shouldRespond200() throws Exception { CompletableFuture future = new CompletableFuture<>(); - req.send( - ar -> { - if (ar.succeeded()) { - future.complete(ar.result().bodyAsJsonObject()); - } else { - future.completeExceptionally(ar.cause()); - } - }); - JsonObject expected = - new JsonObject( - Map.of( - "meta", - Map.of( - "status", "OK", - "type", "application/json"), - "data", Map.of("result", Map.of("username", "")))); + req.basicAuthentication("user", "pass") + .send( + ar -> { + if (ar.succeeded()) { + future.complete(ar.result().bodyAsJsonObject()); + } else { + future.completeExceptionally(ar.cause()); + } + }); + + JsonObject response = future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + MatcherAssert.assertThat(response.getJsonObject("meta"), Matchers.notNullValue()); + MatcherAssert.assertThat( + response.getJsonObject("meta").getString("status"), Matchers.equalTo("OK")); + MatcherAssert.assertThat( + response.getJsonObject("meta").getString("type"), + Matchers.equalTo("application/json")); + MatcherAssert.assertThat(response.getJsonObject("data"), Matchers.notNullValue()); MatcherAssert.assertThat( - future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS), Matchers.equalTo(expected)); + response.getJsonObject("data").getString("result"), + Matchers.equalTo(Map.of("username", "user").toString())); } } diff --git a/src/test/java/itest/NotificationsUrlIT.java b/src/test/java/itest/NotificationsUrlIT.java index c9a60889c..ad41e2cb8 100644 --- a/src/test/java/itest/NotificationsUrlIT.java +++ b/src/test/java/itest/NotificationsUrlIT.java @@ -26,11 +26,9 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @QuarkusIntegrationTest -@Disabled("TODO") public class NotificationsUrlIT extends StandardSelfTest { HttpRequest req; @@ -43,14 +41,15 @@ void createRequest() { @Test public void shouldSucceed() throws Exception { CompletableFuture future = new CompletableFuture<>(); - req.send( - ar -> { - if (ar.succeeded()) { - future.complete(ar.result().statusCode()); - } else { - future.completeExceptionally(ar.cause()); - } - }); + req.basicAuthentication("user", "pass") + .send( + ar -> { + if (ar.succeeded()) { + future.complete(ar.result().statusCode()); + } else { + future.completeExceptionally(ar.cause()); + } + }); MatcherAssert.assertThat( future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS), Matchers.equalTo(200)); } @@ -58,14 +57,15 @@ public void shouldSucceed() throws Exception { @Test public void shouldReturnOK() throws Exception { CompletableFuture future = new CompletableFuture<>(); - req.send( - ar -> { - if (ar.succeeded()) { - future.complete(ar.result().statusMessage()); - } else { - future.completeExceptionally(ar.cause()); - } - }); + req.basicAuthentication("user", "pass") + .send( + ar -> { + if (ar.succeeded()) { + future.complete(ar.result().statusMessage()); + } else { + future.completeExceptionally(ar.cause()); + } + }); MatcherAssert.assertThat( future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS), Matchers.equalTo("OK")); } @@ -73,35 +73,37 @@ public void shouldReturnOK() throws Exception { @Test public void shouldReturnContentTypeJson() throws Exception { CompletableFuture future = new CompletableFuture<>(); - req.send( - ar -> { - if (ar.succeeded()) { - future.complete(ar.result().getHeader("Content-Type")); - } else { - future.completeExceptionally(ar.cause()); - } - }); + req.basicAuthentication("user", "pass") + .send( + ar -> { + if (ar.succeeded()) { + future.complete(ar.result().getHeader("Content-Type")); + } else { + future.completeExceptionally(ar.cause()); + } + }); MatcherAssert.assertThat( future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS), - Matchers.equalTo("application/json")); + Matchers.equalTo("application/json;charset=UTF-8")); } @Test public void shouldReturnJsonMessage() throws Exception { CompletableFuture future = new CompletableFuture<>(); - req.send( - ar -> { - if (ar.succeeded()) { - future.complete(ar.result().bodyAsString()); - } else { - future.completeExceptionally(ar.cause()); - } - }); + req.basicAuthentication("user", "pass") + .send( + ar -> { + if (ar.succeeded()) { + future.complete(ar.result().bodyAsString()); + } else { + future.completeExceptionally(ar.cause()); + } + }); MatcherAssert.assertThat( future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS), Matchers.equalTo( String.format( "{\"notificationsUrl\":\"ws://%s:%d/api/v1/notifications\"}", - Utils.WEB_HOST, Utils.WEB_PORT))); + "0.0.0.0", Utils.WEB_PORT))); } } diff --git a/src/test/java/itest/RecordingWorkflowIT.java b/src/test/java/itest/RecordingWorkflowTest.java similarity index 61% rename from src/test/java/itest/RecordingWorkflowIT.java rename to src/test/java/itest/RecordingWorkflowTest.java index 53804775e..b23f7f7c4 100644 --- a/src/test/java/itest/RecordingWorkflowIT.java +++ b/src/test/java/itest/RecordingWorkflowTest.java @@ -15,41 +15,41 @@ */ package itest; -import java.io.File; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import io.cryostat.resources.LocalStackResource; import io.cryostat.util.HttpMimeType; -import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; import io.vertx.core.MultiMap; -import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpHeaders; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.client.HttpResponse; +import io.vertx.ext.web.codec.BodyCodec; import itest.bases.StandardSelfTest; import itest.util.ITestCleanupFailedException; import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordingFile; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.select.Elements; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -@QuarkusIntegrationTest -@Disabled("TODO") -public class RecordingWorkflowIT extends StandardSelfTest { +@QuarkusTest +@QuarkusTestResource(LocalStackResource.class) +public class RecordingWorkflowTest extends StandardSelfTest { static final String TEST_RECORDING_NAME = "workflow_itest"; - static final String TARGET_ALIAS = "io-cryostat-Cryostat"; + static final String TARGET_ALIAS = "selftest"; @Test public void testWorkflow() throws Exception { @@ -61,6 +61,7 @@ public void testWorkflow() throws Exception { "/api/v1/targets/%s/recordings", getSelfReferenceConnectUrlEncoded())) .basicAuthentication("user", "pass") + .followRedirects(true) .send( ar -> { if (assertRequestStatus(ar, listRespFuture1)) { @@ -70,27 +71,22 @@ public void testWorkflow() throws Exception { JsonArray listResp = listRespFuture1.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); Assertions.assertTrue(listResp.isEmpty()); + List archivedRecordingFilenames = new ArrayList<>(); try { // create an in-memory recording - CompletableFuture dumpRespFuture = new CompletableFuture<>(); MultiMap form = MultiMap.caseInsensitiveMultiMap(); form.add("recordingName", TEST_RECORDING_NAME); form.add("duration", "5"); form.add("events", "template=ALL"); webClient + .extensions() .post( String.format( "/api/v1/targets/%s/recordings", - getSelfReferenceConnectUrlEncoded())) - .basicAuthentication("user", "pass") - .sendForm( + getSelfReferenceConnectUrlEncoded()), + true, form, - ar -> { - if (assertRequestStatus(ar, dumpRespFuture)) { - dumpRespFuture.complete(null); - } - }); - dumpRespFuture.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + REQUEST_TIMEOUT_SECONDS); // verify in-memory recording created CompletableFuture listRespFuture2 = new CompletableFuture<>(); @@ -99,6 +95,7 @@ public void testWorkflow() throws Exception { String.format( "/api/v1/targets/%s/recordings", getSelfReferenceConnectUrlEncoded())) + .followRedirects(true) .basicAuthentication("user", "pass") .send( ar -> { @@ -120,22 +117,22 @@ public void testWorkflow() throws Exception { Thread.sleep(2_000L); // wait some time to save a portion of the recording // save a copy of the partial recording dump - CompletableFuture saveRespFuture = new CompletableFuture<>(); - webClient - .patch( - String.format( - "/api/v1/targets/%s/recordings/%s", - getSelfReferenceConnectUrlEncoded(), TEST_RECORDING_NAME)) - .basicAuthentication("user", "pass") - .putHeader(HttpHeaders.CONTENT_TYPE.toString(), HttpMimeType.PLAINTEXT.mime()) - .sendBuffer( - Buffer.buffer("SAVE"), - ar -> { - if (assertRequestStatus(ar, saveRespFuture)) { - saveRespFuture.complete(null); - } - }); - saveRespFuture.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + MultiMap saveHeaders = MultiMap.caseInsensitiveMultiMap(); + saveHeaders.add(HttpHeaders.CONTENT_TYPE.toString(), HttpMimeType.PLAINTEXT.mime()); + String archivedRecordingFilename = + webClient + .extensions() + .patch( + String.format( + "/api/v1/targets/%s/recordings/%s", + getSelfReferenceConnectUrlEncoded(), + TEST_RECORDING_NAME), + true, + saveHeaders, + "SAVE", + REQUEST_TIMEOUT_SECONDS) + .bodyAsString(); + archivedRecordingFilenames.add(archivedRecordingFilename); // check that the in-memory recording list hasn't changed CompletableFuture listRespFuture3 = new CompletableFuture<>(); @@ -144,6 +141,7 @@ public void testWorkflow() throws Exception { String.format( "/api/v1/targets/%s/recordings", getSelfReferenceConnectUrlEncoded())) + .followRedirects(true) .basicAuthentication("user", "pass") .send( ar -> { @@ -167,6 +165,7 @@ public void testWorkflow() throws Exception { webClient .get("/api/v1/recordings") .basicAuthentication("user", "pass") + .followRedirects(true) .send( ar -> { if (assertRequestStatus(ar, listRespFuture4)) { @@ -195,6 +194,7 @@ public void testWorkflow() throws Exception { String.format( "/api/v1/targets/%s/recordings", getSelfReferenceConnectUrlEncoded())) + .followRedirects(true) .basicAuthentication("user", "pass") .send( ar -> { @@ -218,10 +218,11 @@ public void testWorkflow() throws Exception { // the fully completed in-memory recording is larger than the saved partial copy String inMemoryDownloadUrl = recordingInfo.getString("downloadUrl"); Path inMemoryDownloadPath = - downloadFileAbs(inMemoryDownloadUrl, TEST_RECORDING_NAME, ".jfr") + downloadFile(inMemoryDownloadUrl, TEST_RECORDING_NAME, ".jfr") .get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + Path savedDownloadPath = - downloadFileAbs(savedDownloadUrl, TEST_RECORDING_NAME + "_saved", ".jfr") + downloadFile(savedDownloadUrl, TEST_RECORDING_NAME + "_saved", ".jfr") .get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); MatcherAssert.assertThat( inMemoryDownloadPath.toFile().length(), Matchers.greaterThan(0L)); @@ -234,103 +235,57 @@ public void testWorkflow() throws Exception { inMemoryEvents.size(), Matchers.greaterThan(savedEvents.size())); String reportUrl = recordingInfo.getString("reportUrl"); - MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - headers.add(HttpHeaders.ACCEPT.toString(), HttpMimeType.HTML.mime()); - Path reportPath = - downloadFileAbs(reportUrl, TEST_RECORDING_NAME + "_report", ".html", headers) - .get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); - File reportFile = reportPath.toFile(); - MatcherAssert.assertThat(reportFile.length(), Matchers.greaterThan(0L)); - Document doc = Jsoup.parse(reportFile, "UTF-8"); - - Elements head = doc.getElementsByTag("head"); - Elements titles = head.first().getElementsByTag("title"); - Elements body = doc.getElementsByTag("body"); - Elements script = head.first().getElementsByTag("script"); - MatcherAssert.assertThat("Expected one ", head.size(), Matchers.equalTo(1)); - MatcherAssert.assertThat(titles.size(), Matchers.equalTo(1)); - MatcherAssert.assertThat("Expected one ", body.size(), Matchers.equalTo(1)); + HttpResponse reportResponse = + webClient + .get(reportUrl) + .basicAuthentication("user", "pass") + .as(BodyCodec.jsonObject()) + .send() + .toCompletionStage() + .toCompletableFuture() + .get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); MatcherAssert.assertThat( - "Expected at least one