From bb8b433fc28d0be2560d21d1204eec07603b9bf7 Mon Sep 17 00:00:00 2001 From: "James R. Perkins" Date: Tue, 11 Jun 2024 09:31:00 -0700 Subject: [PATCH] [308] Add file upload multipart/form-data test. Signed-off-by: James R. Perkins --- testsuite/integration-tests/pom.xml | 7 + .../integration/MultiPartClientTest.java | 146 ++++++++++++++++++ .../integration/resource/MultiPartClient.java | 49 ++++++ .../resource/MultiPartResource.java | 92 +++++++++++ .../test/resources/filtered/arquillian.xml | 2 +- .../test/resources/multipart/test-file1.txt | 1 + .../test/resources/multipart/test-file2.txt | 1 + 7 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/MultiPartClientTest.java create mode 100644 testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/resource/MultiPartClient.java create mode 100644 testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/resource/MultiPartResource.java create mode 100644 testsuite/integration-tests/src/test/resources/multipart/test-file1.txt create mode 100644 testsuite/integration-tests/src/test/resources/multipart/test-file2.txt diff --git a/testsuite/integration-tests/pom.xml b/testsuite/integration-tests/pom.xml index 65737c59..ef4bc1e1 100644 --- a/testsuite/integration-tests/pom.xml +++ b/testsuite/integration-tests/pom.xml @@ -114,6 +114,12 @@ ${version.resteasy.testsuite} test + + org.jboss.resteasy + resteasy-multipart-provider + ${version.resteasy.testsuite} + test + org.junit.jupiter @@ -222,6 +228,7 @@ ${jboss.home} + ${project.build.directory} diff --git a/testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/MultiPartClientTest.java b/testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/MultiPartClientTest.java new file mode 100644 index 00000000..8f090f86 --- /dev/null +++ b/testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/MultiPartClientTest.java @@ -0,0 +1,146 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2024 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * 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 org.jboss.resteasy.microprofile.test.client.integration; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.net.URI; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.PropertyPermission; +import java.util.stream.Collectors; + +import jakarta.ws.rs.core.EntityPart; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit5.ArquillianExtension; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.resteasy.microprofile.test.client.integration.resource.MultiPartClient; +import org.jboss.resteasy.microprofile.test.client.integration.resource.MultiPartResource; +import org.jboss.resteasy.microprofile.test.util.TestEnvironment; +import org.jboss.shrinkwrap.api.Archive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.wildfly.testing.tools.deployments.DeploymentDescriptors; + +/** + * Tests the {@link EntityPart} arguments work with an MP REST Client. + * + * @author James R. Perkins + */ +@ExtendWith(ArquillianExtension.class) +@RunAsClient +public class MultiPartClientTest { + + @ArquillianResource + private URI uri; + + @Deployment + public static Archive serviceDeploy() { + return TestEnvironment.createWar(MultiPartClientTest.class) + .addClasses(MultiPartResource.class) + .addAsManifestResource(DeploymentDescriptors.createPermissionsXmlAsset( + DeploymentDescriptors.createTempDirPermission("read,write"), + new PropertyPermission("java.io.tmpdir", "read")), "permissions.xml"); + } + + @Test + public void uploadFile(final TestInfo testInfo) throws Exception { + final String methodName = testInfo.getTestMethod().orElseThrow().getName(); + try (MultiPartClient client = RestClientBuilder.newBuilder().baseUri(uri).build(MultiPartClient.class)) { + final byte[] content; + try (InputStream in = MultiPartClientTest.class.getResourceAsStream("/multipart/test-file1.txt")) { + Assertions.assertNotNull(in, "Could not find /multipart/test-file1.txt"); + content = in.readAllBytes(); + } + // Send in an InputStream to ensure it works with an InputStream + final List files = List.of(EntityPart.withFileName("test-file1.txt") + .content(new ByteArrayInputStream(content)) + .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE) + .build()); + client.uploadFile(methodName, files); + final List downloads = client.download(methodName); + Assertions.assertNotNull(downloads); + Assertions.assertEquals(1, downloads.size(), () -> "Expected 1 entity but got " + downloads); + final EntityPart downloadedFile = downloads.get(0); + Assertions.assertEquals("test-file1.txt", downloadedFile.getName()); + Assertions.assertArrayEquals(content, downloadedFile.getContent().readAllBytes()); + } + } + + @Test + public void uploadMultipleFiles(final TestInfo testInfo) throws Exception { + final String methodName = testInfo.getTestMethod().orElseThrow().getName(); + try (MultiPartClient client = RestClientBuilder.newBuilder().baseUri(uri).build(MultiPartClient.class)) { + final Map entityPartContent = new LinkedHashMap<>(2); + try (InputStream in = MultiPartClientTest.class.getResourceAsStream("/multipart/test-file1.txt")) { + Assertions.assertNotNull(in, "Could not find /multipart/test-file1.txt"); + entityPartContent.put("test-file1.txt", in.readAllBytes()); + } + try (InputStream in = MultiPartClientTest.class.getResourceAsStream("/multipart/test-file2.txt")) { + Assertions.assertNotNull(in, "Could not find /multipart/test-file2.txt"); + entityPartContent.put("test-file2.txt", in.readAllBytes()); + } + final List files = entityPartContent.entrySet() + .stream() + .map((entry) -> { + try { + return EntityPart.withName(entry.getKey()) + .fileName(entry.getKey()) + .content(entry.getValue()) + .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE) + .build(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }) + .collect(Collectors.toList()); + client.uploadFile(methodName, files); + final List downloads = client.download(methodName); + Assertions.assertNotNull(downloads); + Assertions.assertEquals(2, downloads.size(), + () -> "Expected 2 entity but got " + System.lineSeparator() + toString(downloads)); + } + } + + private static String toString(final Collection parts) { + final StringBuilder builder = new StringBuilder(); + try { + for (EntityPart part : parts) { + builder.append("name=").append(part.getName()) + .append(", fileName=").append(part.getFileName().orElse(null)) + .append(", content=").append(part.getContent(String.class)) + .append(System.lineSeparator()); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return builder.toString(); + } +} diff --git a/testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/resource/MultiPartClient.java b/testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/resource/MultiPartClient.java new file mode 100644 index 00000000..47d7959d --- /dev/null +++ b/testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/resource/MultiPartClient.java @@ -0,0 +1,49 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2024 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * 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 org.jboss.resteasy.microprofile.test.client.integration.resource; + +import java.io.IOException; +import java.util.List; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.EntityPart; +import jakarta.ws.rs.core.MediaType; + +/** + * @author James R. Perkins + */ +@Consumes(MediaType.MULTIPART_FORM_DATA) +@Produces(MediaType.MULTIPART_FORM_DATA) +@Path("/test-app") +public interface MultiPartClient extends AutoCloseable { + + @POST + @Path("upload/{testName}") + void uploadFile(@PathParam("testName") String testName, List entityParts) throws IOException; + + @GET + @Path("/download/{testName}") + List download(@PathParam("testName") String testName) throws IOException; +} diff --git a/testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/resource/MultiPartResource.java b/testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/resource/MultiPartResource.java new file mode 100644 index 00000000..6e90e147 --- /dev/null +++ b/testsuite/integration-tests/src/test/java/org/jboss/resteasy/microprofile/test/client/integration/resource/MultiPartResource.java @@ -0,0 +1,92 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2024 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * 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 org.jboss.resteasy.microprofile.test.client.integration.resource; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.BadRequestException; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.EntityPart; +import jakarta.ws.rs.core.MediaType; + +/** + * @author James R. Perkins + */ +@Consumes(MediaType.MULTIPART_FORM_DATA) +@Produces(MediaType.MULTIPART_FORM_DATA) +@ApplicationScoped +@Path("/") +public class MultiPartResource { + private final java.nio.file.Path dir; + + public MultiPartResource() { + dir = Paths.get(System.getProperty("java.io.tmpdir"), "multipart"); + } + + @POST + @Path("upload/{testName}") + public void uploadFiles(@PathParam("testName") final String testName, final List entityParts) + throws IOException { + final var path = dir.resolve(testName); + Files.createDirectories(path); + for (EntityPart part : entityParts) { + if (part.getFileName().isPresent()) { + Files.copy(part.getContent(), path.resolve(part.getFileName().get())); + } else { + throw new BadRequestException("No file name for entity part " + part); + } + } + } + + @GET + @Path("/download/{testName}") + public List download(@PathParam("testName") final String testName) throws IOException { + final var path = dir.resolve(testName); + if (Files.notExists(path)) { + throw new NotFoundException("Could not find download path " + testName); + } + try (var paths = Files.walk(path)) { + return paths.filter(Files::isRegularFile) + .map((file) -> { + try { + return EntityPart.withName(file.getFileName().toString()) + .fileName(file.getFileName().toString()) + .content(Files.newInputStream(file)) + .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE) + .build(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }).collect(Collectors.toList()); + } + } +} diff --git a/testsuite/integration-tests/src/test/resources/filtered/arquillian.xml b/testsuite/integration-tests/src/test/resources/filtered/arquillian.xml index 832a8981..e632d563 100644 --- a/testsuite/integration-tests/src/test/resources/filtered/arquillian.xml +++ b/testsuite/integration-tests/src/test/resources/filtered/arquillian.xml @@ -24,7 +24,7 @@ ${jboss.home} - ${jvm.debug.args} + -Djava.io.tmpdir=${project.build.directory} ${jvm.debug.args} ${jboss.server.config.file.name:standalone.xml} ${jboss.arguments} diff --git a/testsuite/integration-tests/src/test/resources/multipart/test-file1.txt b/testsuite/integration-tests/src/test/resources/multipart/test-file1.txt new file mode 100644 index 00000000..9944a9f2 --- /dev/null +++ b/testsuite/integration-tests/src/test/resources/multipart/test-file1.txt @@ -0,0 +1 @@ +This is a test file \ No newline at end of file diff --git a/testsuite/integration-tests/src/test/resources/multipart/test-file2.txt b/testsuite/integration-tests/src/test/resources/multipart/test-file2.txt new file mode 100644 index 00000000..9944a9f2 --- /dev/null +++ b/testsuite/integration-tests/src/test/resources/multipart/test-file2.txt @@ -0,0 +1 @@ +This is a test file \ No newline at end of file