citrusframework / citrus

Framework for automated integration tests with focus on messaging integration
https://citrusframework.org
Apache License 2.0
453 stars 134 forks source link

Quarkus - Citrus support not working if quarkus-jacoco is used, since Quarkus 3.13.0 #1195

Closed rokkolesa closed 1 week ago

rokkolesa commented 1 month ago

Citrus Version 4.2.1

Expected behavior The test should start and successfully complete.

Actual behavior If a project specifies the dependency on quarkus-jacoco, Citrus support cannot be enabled at the start of testing, yielding an error when starting the CitrusInstanceManager, specifically when calling CitrusContextProvider.lookup().

It was not an issue in Quarkus <= 3.12.3. I also tested it on java17 and java21 -> same result.

This is the stack trace:

java.lang.RuntimeException: java.lang.RuntimeException: Unable to start Quarkus test resource class org.citrusframework.quarkus.CitrusTestResource
    at io.quarkus.test.junit.QuarkusTestExtension.throwBootFailureException(QuarkusTestExtension.java:634)
    at io.quarkus.test.junit.QuarkusTestExtension.interceptTestClassConstructor(QuarkusTestExtension.java:718)
    at java.base/java.util.Optional.orElseGet(Optional.java:364)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.RuntimeException: Unable to start Quarkus test resource class org.citrusframework.quarkus.CitrusTestResource
    at io.quarkus.test.common.TestResourceManager$TestResourceEntryRunnable.run(TestResourceManager.java:548)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1583)
    Suppressed: java.lang.RuntimeException: Unable to stop Quarkus test resource org.citrusframework.quarkus.CitrusTestResource@3a72e2e8
        at io.quarkus.test.common.TestResourceManager.close(TestResourceManager.java:182)
        at io.quarkus.test.junit.QuarkusTestExtension.doJavaStart(QuarkusTestExtension.java:312)
        at io.quarkus.test.junit.QuarkusTestExtension.ensureStarted(QuarkusTestExtension.java:601)
        at io.quarkus.test.junit.QuarkusTestExtension.beforeAll(QuarkusTestExtension.java:651)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    Caused by: java.lang.NullPointerException: Cannot invoke "org.citrusframework.Citrus.afterSuite(String, String[])" because "this.citrus" is null
        at org.citrusframework.quarkus.CitrusTestResource.stop(CitrusTestResource.java:64)
        at io.quarkus.test.common.TestResourceManager.close(TestResourceManager.java:180)
        ... 4 more
Caused by: java.lang.NullPointerException: Cannot invoke "java.net.URL.toString()" because the return value of "java.security.CodeSource.getLocation()" is null
    at org.citrusframework.spi.ResourcePathTypeResolver.resolveAllFromJar(ResourcePathTypeResolver.java:208)
    at org.citrusframework.spi.ResourcePathTypeResolver.determineTypeLookup(ResourcePathTypeResolver.java:164)
    at org.citrusframework.spi.ResourcePathTypeResolver.lambda$resolveAll$1(ResourcePathTypeResolver.java:145)
    at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
    at org.citrusframework.spi.ResourcePathTypeResolver.resolveAll(ResourcePathTypeResolver.java:143)
    at org.citrusframework.spi.TypeResolver.resolveAll(TypeResolver.java:91)
    at org.citrusframework.CitrusContextProvider.lookup(CitrusContextProvider.java:58)
    at org.citrusframework.CitrusInstanceManager.newInstance(CitrusInstanceManager.java:51)
    at org.citrusframework.quarkus.CitrusTestResource.start(CitrusTestResource.java:51)
    at io.quarkus.test.common.TestResourceManager$TestResourceEntryRunnable.run(TestResourceManager.java:542)
    ... 4 more

Here is also the difference between Quarkus 3.12.3:

image

... and 3.13.0:

image

Test case sample

Please, share the test case (as small as possible) which shows the issue

Basically any sample that includes the @CitrusSupport annotation will do.

package com.bug.citrus;

import org.citrusframework.quarkus.CitrusSupport;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
@CitrusSupport
class ExampleResourceTest
{
    @Test
    void testCitrus()
    {
        // we just add the @CitrusSupport annotation
    }
}
bbortt commented 1 month ago

I assume that is because the current quarkus platform version is too old: https://github.com/citrusframework/citrus/blob/f7f5cf729ceb55b1292e46abba4f0dc2a6bb9758/runtime/citrus-quarkus/pom.xml#L18.

bbortt commented 4 weeks ago

@rokkolesa please check with the latest release. let me know if the issue persists.

rokkolesa commented 4 weeks ago

It seems that the issue is still present if i include the quarkus-jacoco dependency 😔 It still goes away if i remove the quarkus-jacoco dependency.

My whole error:

2024-08-14 22:23:48,095 WARN  [io.qua.arc.dep.SplitPackageProcessor] (build-13) Detected a split package usage which is considered a bad practice and should be avoided. Following packages were detected in multiple archives: 
- "org.citrusframework.validation.script.sql" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.validation.script" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.report" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.context" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.annotations" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.message.correlation" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.validation.xml" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.main" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.validation" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.util" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.endpoint.resolver" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.message" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.endpoint.adapter.mapping" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.condition" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.log" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.common" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.xml" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.functions" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.variable" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.validation.matcher" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.variable.dictionary" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.container" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.endpoint" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.main.scan" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
- "org.citrusframework.server" found in [org.citrusframework:citrus-api:4.3.0, org.citrusframework:citrus-base:4.3.0]
2024-08-14 22:23:48,132 INFO  [io.qua.cxf.dep.Java2WsdlProcessor] (build-54) java2ws processed 0 classes
...
java.lang.RuntimeException: java.lang.ExceptionInInitializerError

    at io.quarkus.test.junit.QuarkusTestExtension.throwBootFailureException(QuarkusTestExtension.java:634)
    at io.quarkus.test.junit.QuarkusTestExtension.interceptTestClassConstructor(QuarkusTestExtension.java:718)
    at java.base/java.util.Optional.orElseGet(Optional.java:364)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.ExceptionInInitializerError
    at org.citrusframework.CitrusContextProvider.<clinit>(CitrusContextProvider.java:41)
    at org.citrusframework.CitrusInstanceManager.newInstance(CitrusInstanceManager.java:51)
    at org.citrusframework.quarkus.CitrusTestResource.start(CitrusTestResource.java:51)
    at io.quarkus.test.common.TestResourceManager$TestResourceEntryRunnable.run(TestResourceManager.java:542)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1583)
    Suppressed: java.lang.RuntimeException: Unable to stop Quarkus test resource org.citrusframework.quarkus.CitrusTestResource@11caa21d
        at io.quarkus.test.common.TestResourceManager.close(TestResourceManager.java:182)
        at io.quarkus.test.junit.QuarkusTestExtension.doJavaStart(QuarkusTestExtension.java:312)
        at io.quarkus.test.junit.QuarkusTestExtension.ensureStarted(QuarkusTestExtension.java:601)
        at io.quarkus.test.junit.QuarkusTestExtension.beforeAll(QuarkusTestExtension.java:651)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    Caused by: java.lang.NullPointerException: Cannot invoke "org.citrusframework.Citrus.afterSuite(String, String[])" because "this.citrus" is null
        at org.citrusframework.quarkus.CitrusTestResource.stop(CitrusTestResource.java:64)
        at io.quarkus.test.common.TestResourceManager.close(TestResourceManager.java:180)
        ... 4 more
Caused by: java.lang.NullPointerException: Cannot invoke "java.net.URL.toString()" because "org.citrusframework.spi.ResourcePathTypeResolver.ROOT" is null
    at org.citrusframework.spi.ResourcePathTypeResolver.<clinit>(ResourcePathTypeResolver.java:75)
    ... 8 more
bbortt commented 1 week ago

I've taken another look at the stacktrace and think it might be related to changes we've done (aka bugs we've introduced) in the ResourcePathTypeResolver. a bugfix has been submitted in https://github.com/citrusframework/citrus/pull/1207.

additionally I've submitted https://github.com/citrusframework/citrus/pull/1210 just now to fix quarkus-jacoco with the platform version. I've also included it in the citrus-quarkus-it project in order to verify the integration.

we will have to test it again afterwards.

rokkolesa commented 1 week ago

I cloned the main branch and tested it and it still seems to throw the same error 😔

The thing that's weird is that the citrus build goes through smoothly even though you have a test for this scenario. So I looked at the build output and this is what it says:

[INFO] 
[INFO] ---------------< org.citrusframework:citrus-quarkus-it >----------------
[INFO] Building Citrus :: Runtime :: Quarkus Integration Tests 4.4.0-SNAPSHOT [18/64]
[INFO]   from runtime/citrus-quarkus/citrus-quarkus-it/pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- resources:3.3.1:resources (default-resources) @ citrus-quarkus-it ---
[INFO] Copying 3 resources from src/main/resources to target/classes
[INFO] 
[INFO] --- quarkus:3.14.1:generate-code (default) @ citrus-quarkus-it ---
[INFO] 
[INFO] --- compiler:3.11.0:compile (default-compile) @ citrus-quarkus-it ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 1 source file with javac [debug release 17] to target/classes
[INFO] 
[INFO] --- quarkus:3.14.1:generate-code-tests (default) @ citrus-quarkus-it ---
[INFO] 
[INFO] --- resources:3.3.1:testResources (default-testResources) @ citrus-quarkus-it ---
[INFO] skip non existing resourceDirectory /home/runner/work/citrus/citrus/runtime/citrus-quarkus/citrus-quarkus-it/src/test/resources
[INFO] 
[INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ citrus-quarkus-it ---
[INFO] Changes detected - recompiling the module! :dependency
[INFO] Compiling 1 source file with javac [debug release 17] to target/test-classes
[INFO] 
[INFO] --- surefire:2.22.2:test (default-test) @ citrus-quarkus-it ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.citrusframework.quarkus.app.DemoApplicationTest
SLF4J(W): Class path contains multiple SLF4J providers.
SLF4J(W): Found provider [org.slf4j.impl.JBossSlf4jServiceProvider@548a9f61]
SLF4J(W): Found provider [org.apache.logging.slf4j.SLF4JServiceProvider@1753acfe]
SLF4J(W): See https://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J(I): Actual provider is of type [org.slf4j.impl.JBossSlf4jServiceProvider@548a9f61]
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.335 s - in org.citrusframework.quarkus.app.DemoApplicationTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- jar:3.3.0:jar (default-jar) @ citrus-quarkus-it ---
[INFO] Building jar: /home/runner/work/citrus/citrus/runtime/citrus-quarkus/citrus-quarkus-it/target/citrus-quarkus-it-4.4.0-SNAPSHOT.jar
[INFO] 
[INFO] --- source:3.0.1:jar-no-fork (attach-sources) @ citrus-quarkus-it ---
[INFO] Building jar: /home/runner/work/citrus/citrus/runtime/citrus-quarkus/citrus-quarkus-it/target/citrus-quarkus-it-4.4.0-SNAPSHOT-sources.jar
[INFO] 
[INFO] --- javadoc:3.5.0:jar (attach-javadocs) @ citrus-quarkus-it ---
[INFO] No previous run data found, generating javadoc.
[INFO] Building jar: /home/runner/work/citrus/citrus/runtime/citrus-quarkus/citrus-quarkus-it/target/citrus-quarkus-it-4.4.0-SNAPSHOT-javadoc.jar
[INFO] 
[INFO] --- quarkus:3.14.1:build (default) @ citrus-quarkus-it ---
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 993ms
[INFO] 
[INFO] --- failsafe:2.22.2:integration-test (integration-tests) @ citrus-quarkus-it ---
[INFO] 
[INFO] --- failsafe:2.22.2:verify (integration-tests) @ citrus-quarkus-it ---
[INFO] 
[INFO] --- install:3.1.1:install (default-install) @ citrus-quarkus-it ---
[INFO] Installing /home/runner/work/citrus/citrus/runtime/citrus-quarkus/citrus-quarkus-it/pom.xml to /home/runner/.m2/repository/org/citrusframework/citrus-quarkus-it/4.4.0-SNAPSHOT/citrus-quarkus-it-4.4.0-SNAPSHOT.pom
[INFO] Installing /home/runner/work/citrus/citrus/runtime/citrus-quarkus/citrus-quarkus-it/target/citrus-quarkus-it-4.4.0-SNAPSHOT.jar to /home/runner/.m2/repository/org/citrusframework/citrus-quarkus-it/4.4.0-SNAPSHOT/citrus-quarkus-it-4.4.0-SNAPSHOT.jar
[INFO] Installing /home/runner/work/citrus/citrus/runtime/citrus-quarkus/citrus-quarkus-it/target/citrus-quarkus-it-4.4.0-SNAPSHOT-sources.jar to /home/runner/.m2/repository/org/citrusframework/citrus-quarkus-it/4.4.0-SNAPSHOT/citrus-quarkus-it-4.4.0-SNAPSHOT-sources.jar
[INFO] Installing /home/runner/work/citrus/citrus/runtime/citrus-quarkus/citrus-quarkus-it/target/citrus-quarkus-it-4.4.0-SNAPSHOT-javadoc.jar to /home/runner/.m2/repository/org/citrusframework/citrus-quarkus-it/4.4.0-SNAPSHOT/citrus-quarkus-it-4.4.0-SNAPSHOT-javadoc.jar
[INFO] 

The above is copied from your GitHub Actions job. It says Tests run: 0, Failures: 0, Errors: 0, Skipped: 0.. like the tests were not even ran!

But if I run in from IntelliJ, I get this error (same as before):

java.lang.RuntimeException: java.lang.ExceptionInInitializerError

    at io.quarkus.test.junit.QuarkusTestExtension.throwBootFailureException(QuarkusTestExtension.java:643)
    at io.quarkus.test.junit.QuarkusTestExtension.interceptTestClassConstructor(QuarkusTestExtension.java:727)
    at java.base/java.util.Optional.orElseGet(Optional.java:364)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.ExceptionInInitializerError
    at org.citrusframework.CitrusContextProvider.<clinit>(CitrusContextProvider.java:41)
    at org.citrusframework.CitrusInstanceManager.newInstance(CitrusInstanceManager.java:51)
    at org.citrusframework.quarkus.CitrusTestResource.start(CitrusTestResource.java:51)
    at io.quarkus.test.common.TestResourceManager$TestResourceEntryRunnable.run(TestResourceManager.java:542)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1583)
    Suppressed: java.lang.RuntimeException: Unable to stop Quarkus test resource org.citrusframework.quarkus.CitrusTestResource@59fc6d05
        at io.quarkus.test.common.TestResourceManager.close(TestResourceManager.java:182)
        at io.quarkus.test.junit.QuarkusTestExtension.doJavaStart(QuarkusTestExtension.java:311)
        at io.quarkus.test.junit.QuarkusTestExtension.ensureStarted(QuarkusTestExtension.java:610)
        at io.quarkus.test.junit.QuarkusTestExtension.beforeAll(QuarkusTestExtension.java:660)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    Caused by: java.lang.NullPointerException: Cannot invoke "org.citrusframework.Citrus.afterSuite(String, String[])" because "this.citrus" is null
        at org.citrusframework.quarkus.CitrusTestResource.stop(CitrusTestResource.java:64)
        at io.quarkus.test.common.TestResourceManager.close(TestResourceManager.java:180)
        ... 4 more
Caused by: java.lang.NullPointerException: Cannot invoke "java.net.URL.toString()" because "org.citrusframework.spi.ResourcePathTypeResolver.ROOT" is null
    at org.citrusframework.spi.ResourcePathTypeResolver.<clinit>(ResourcePathTypeResolver.java:79)
    ... 8 more

This is actually a false error, as it errors out stopping the CitrusTestResource..

The real exception actually changed from citrus 4.2.1 to 4.3.x..

The original exception in citrus 4.2.1 was this:

Caused by: java.lang.NullPointerException: Cannot invoke "java.net.URL.toString()" because the return value of "java.security.CodeSource.getLocation()" is null
    at org.citrusframework.spi.ResourcePathTypeResolver.resolveAllFromJar(ResourcePathTypeResolver.java:208)
    at org.citrusframework.spi.ResourcePathTypeResolver.determineTypeLookup(ResourcePathTypeResolver.java:164)
    at org.citrusframework.spi.ResourcePathTypeResolver.lambda$resolveAll$1(ResourcePathTypeResolver.java:145)
    at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
    at org.citrusframework.spi.ResourcePathTypeResolver.resolveAll(ResourcePathTypeResolver.java:143)
    at org.citrusframework.spi.TypeResolver.resolveAll(TypeResolver.java:91)
    at org.citrusframework.CitrusContextProvider.lookup(CitrusContextProvider.java:58)
    at org.citrusframework.CitrusInstanceManager.newInstance(CitrusInstanceManager.java:51)
    at org.citrusframework.quarkus.CitrusTestResource.start(CitrusTestResource.java:51)
    at io.quarkus.test.common.TestResourceManager$TestResourceEntryRunnable.run(TestResourceManager.java:542)
    ... 4 more

and the new one is this:

Caused by: java.lang.NullPointerException: Cannot invoke "java.net.URL.toString()" because "org.citrusframework.spi.ResourcePathTypeResolver.ROOT" is null
    at org.citrusframework.spi.ResourcePathTypeResolver.<clinit>(ResourcePathTypeResolver.java:78)
    ... 8 more

It is essentially the same issue, it was just moved from the resolveAllFromJar method to the class constant.

bbortt commented 1 week ago

thanks for testing it @rokkolesa. I've now made sure the tests run in the pipeline.

as a side note; the undocumented null-ability of CodeSource#getLocation is tricky, but I've caught it. I made sure of it with a negative test using 90c96640e1e6b62d70dd43c57935fd079a67d370 but without 54b3f6866d07648b97dfc30d7578d92ea0f1e0d7. as you already did report, this fails. with the fix-commit, the test passes.

see #1211 for more details.