quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.54k stars 2.61k forks source link

HTTP 400 while invoking RESTeasy endpoint #40907

Closed nicolasduminil closed 3 months ago

nicolasduminil commented 3 months ago

Hello,

I've used the Quarkus quick start examples here to implement a simple RESTesay endpoint. The original example works as expected, of course, however mine doesn't and, while executing the associated unit test, it keeps returning HTTP 400.

I tried to reduce the two implementations such that to figure out what exactly produces this behavior and, finally, I came up with two almost identical implementations, the original one slightly modified which works as expected, and the one adapted by me, which doesn't and which, whatever I do, returns HTTP 400.

Could anyone please let me know what might be the problem here ?

Expected behavior

Running the quick start unit test works as expected by running the simple example adapted by me from this same quick start doesn't and returns HTTP 400.

Actual behavior

I expect that both implementations run identically given that, apart a few minor details, that's almost the same code.

How to Reproduce?

  1. Clone the sample repository:

    git clone https://github.com/nicolasduminil/quarkus-s3.git

  2. cd to the root directory

    cd quarkus-s3

  3. Run the unit test:

    mvn test

The unit test fails as shown below:

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running fr.simplex_software.aws.iac.quarkus.s3.tests.S3FileManagementTest
2024-05-30 19:26:53,980 INFO  [org.tes.doc.DockerClientProviderStrategy] (build-5) Loaded org.testcontainers.dockerclient.UnixSocketClientProviderStrategy from ~/.testcontainers.properties, will try it first
2024-05-30 19:26:54,212 INFO  [org.tes.doc.DockerClientProviderStrategy] (build-5) Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
2024-05-30 19:26:54,214 INFO  [org.tes.DockerClientFactory] (build-5) Docker host IP address is localhost
2024-05-30 19:26:54,228 INFO  [org.tes.DockerClientFactory] (build-5) Connected to docker: 
  Server Version: 26.0.1
  API Version: 1.45
  Operating System: Ubuntu 22.04.4 LTS
  Total Memory: 31730 MB
2024-05-30 19:26:54,236 INFO  [org.tes.ima.PullPolicy] (build-5) Image pull policy will be performed by: DefaultPullPolicy()
2024-05-30 19:26:54,237 INFO  [org.tes.uti.ImageNameSubstitutor] (build-5) Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
2024-05-30 19:26:54,240 INFO  [org.tes.DockerClientFactory] (build-5) Checking the system...
2024-05-30 19:26:54,240 INFO  [org.tes.DockerClientFactory] (build-5) ✔︎ Docker server version should be at least 1.6.0
2024-05-30 19:26:54,344 INFO  [tc.loc.0.1] (build-5) LOCALSTACK_HOST environment variable set to localstack-1xdyp (to match last network alias on container with non-default network)
2024-05-30 19:26:54,345 INFO  [tc.tes.7.0] (build-5) Creating container for image: testcontainers/ryuk:0.7.0
2024-05-30 19:26:54,348 INFO  [org.tes.uti.RegistryAuthLocator] (build-5) Failure when attempting to lookup auth config. Please ignore if you don't have images in an authenticated registry. Details: (dockerImageName: testcontainers/ryuk:0.7.0, configFile: /home/nicolas/.docker/config.json, configEnv: DOCKER_AUTH_CONFIG). Falling back to docker-java default behaviour. Exception message: Status 404: No config supplied. Checked in order: /home/nicolas/.docker/config.json (file not found), DOCKER_AUTH_CONFIG (not set)
2024-05-30 19:26:54,503 INFO  [tc.tes.7.0] (build-5) Container testcontainers/ryuk:0.7.0 is starting: 8a696bea2ef1ec20a42f90d0505ac9f28767d2670d60dae9dc51a15990a4a077
2024-05-30 19:26:54,830 INFO  [tc.tes.7.0] (build-5) Container testcontainers/ryuk:0.7.0 started in PT0.485372478S
2024-05-30 19:26:54,834 INFO  [tc.loc.0.1] (build-5) Creating container for image: localstack/localstack:3.0.1
2024-05-30 19:26:55,679 INFO  [tc.loc.0.1] (build-5) Container localstack/localstack:3.0.1 is starting: ed   ee8305f7e2bb19423723ec602cdc234a0464cc7fde5f91530680729e0181f2
2024-05-30 19:26:58,331 INFO  [tc.loc.0.1] (build-5) Container localstack/localstack:3.0.1 started in PT3.496597954S
2024-05-30 19:27:00,558 INFO  [io.qua.ama.com.dep.DevServicesLocalStackProcessor] (build-5) Amazon Dev Services for localstack (s3) started. Other Quarkus applications in dev mode will find the LocalStack automatically.
2024-05-30 19:27:01,138 INFO  [io.quarkus] (main) quarkus-s3 0.1 on JVM (powered by Quarkus 3.11.0) started in 8.838s. Listening on: http://localhost:8081
2024-05-30 19:27:01,139 INFO  [io.quarkus] (main) Profile test activated. 

[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 11.62 s <<< FAILURE! -- in fr.simplex_software.aws.iac.quarkus.s3.tests.S3FileManagementTest
[ERROR] fr.simplex_software.aws.iac.quarkus.s3.tests.S3FileManagementTest.testUploadFile -- Time elapsed: 0.746 s <<< FAILURE!
java.lang.AssertionError: 
1 expectation failed.
Expected status code <201> but was <400>.

        at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
        at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:73)
        at  org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:108)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:57)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:263)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:277)
        at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure.validate(ResponseSpecificationImpl.groovy:512)
        at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure$validate$1.call(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:139)
        at io.restassured.internal.ResponseSpecificationImpl.validateResponseIfRequired(ResponseSpecificationImpl.groovy:696)
        at io.restassured.internal.ResponseSpecificationImpl.this$2$validateResponseIfRequired(ResponseSpecificationImpl.groovy)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at org.codehaus.groovy.runtime.callsite.PlainObjectMetaMethodSite.doInvoke(PlainObjectMetaMethodSite.java:43)
        at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:198)
        at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:62)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:171)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:185)
        at io.restassured.internal.ResponseSpecificationImpl.statusCode(ResponseSpecificationImpl.groovy:135)
        at io.restassured.specification.ResponseSpecification$statusCode$0.callCurrent(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:171)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:185)
        at io.restassured.internal.ResponseSpecificationImpl.statusCode(ResponseSpecificationImpl.groovy:143)
        at io.restassured.internal.ValidatableResponseOptionsImpl.statusCode(ValidatableResponseOptionsImpl.java:89)
        at fr.simplex_software.aws.iac.quarkus.s3.tests.S3FileManagementTest.testUploadFile(S3FileManagementTest.java:36)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:1017)
        at io.quarkus.test.junit.QuarkusTestExtension.interceptTestMethod(QuarkusTestExtension.java:831)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

Output of uname -a or ver

Linux nicolas-XPS-15-9570 5.15.0-107-generic #117-Ubuntu SMP Fri Apr 26 12:26:49 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Output of java -version

java version "21.0.3" 2024-04-16 LTS Java(TM) SE Runtime Environment (build 21.0.3+7-LTS-152) Java HotSpot(TM) 64-Bit Server VM (build 21.0.3+7-LTS-152, mixed mode, sharing)

Quarkus version or git rev

3.11

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.9.5 (57804ffe001d7215b5e7bcb531cf83df38f93546) Maven home: /opt/apache-maven-3.9.5 Java version: 21.0.3, vendor: Oracle Corporation, runtime: /usr/lib/jvm/jdk-21-oracle-x64 Default locale: en_US, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-107-generic", arch: "amd64", family: "unix"

Additional information

N/A

geoand commented 3 months ago

The problem is that FileMetadata does not have a default constructor -if you add one everything works as expected.

This is a general issue with the multipart handling that is being tracked in other issues, so I am going to close this one. Hopefully we can update the multipart handling soon to address all these cases - cc @FroMage

nicolasduminil commented 3 months ago

Yes, I confirm that doing that solves the issue. My bad, I didn't realize that adding an explicit non default constructor removes the parameterless one. I've noticed that the original quick-start didn't declare a non default constructor and I thought to remove it as well from my example, but finally I didn't, 'cause it didn't seem to me the most likely reason to prevent the implementation from working.

Perhaps a more intuitive behavior, like a more specific exception, would be helpful ?

Many thanks for your help and support.

geoand commented 3 months ago

Perhaps a more intuitive behavior, like a more specific exception, would be helpful ?

Indeed, but ideally we would just make it work and not require a non-args constructor :)

Many thanks for your help and support.

YW

FroMage commented 3 months ago

This is a general issue with the multipart handling that is being tracked in other issues, so I am going to close this one. Hopefully we can update the multipart handling soon to address all these cases - cc @FroMage

Any other issue in particular?