Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QuarkusComponentTest does not register Quarkus Config Converters #41709

Closed
diversit opened this issue Jul 5, 2024 · 3 comments · Fixed by #41747
Closed

QuarkusComponentTest does not register Quarkus Config Converters #41709

diversit opened this issue Jul 5, 2024 · 3 comments · Fixed by #41747
Assignees
Labels
Milestone

Comments

@diversit
Copy link

diversit commented Jul 5, 2024

Describe the bug

A QuarkusComponentTest fails for components which rely on MicroProfile Config Converters provided by Quarkus.
E.g. io.quarkus.runtime.configuration.DurationConverter.

Testing a component using

@ConfigProperty(name = "my.duration", defaultValue = "60s")
private Duration myDuration;

fails with an exception: java.lang.RuntimeException: Error injecting java.time.Duration io.quarkus.issue.MyComponent.myDuration.

Running the application works fine since then the Quarkus Config Converters are available.

Expected behavior

Expected the Quarkus Config Converters to be available and used when running a QuarkusComponentTest test
or to have some kind of mechanism with which custom converters can be registered.

Actual behavior

None of the Quarkus Config Converters are registered.

The exception is caused by the default Duration converter provided by SmallRye's io.smallrye.config.ImplicitConverters which tries to use the java.time.Duration.parse function to parse the value but this does not support the syntax: 60s.

Full stack trace:

2024-07-05 13:31:32,203 INFO  [io.qua.arc.pro.BeanProcessor] (main) Found unrecommended usage of private members (use package-private instead) in application beans:
	- @Inject field io.quarkus.issue.MyComponent#myDuration

java.lang.RuntimeException: Error injecting java.time.Duration io.quarkus.issue.MyComponent.myDuration

	at io.quarkus.issue.MyComponent_Bean.doCreate(Unknown Source)
	at io.quarkus.issue.MyComponent_Bean.create(Unknown Source)
	at io.quarkus.issue.MyComponent_Bean.create(Unknown Source)
	at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
	at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:32)
	at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
	at io.quarkus.arc.impl.ComputingCacheContextInstances.computeIfAbsent(ComputingCacheContextInstances.java:19)
	at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
	at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:21)
	at io.quarkus.issue.MyComponent_ClientProxy.arc$delegate(Unknown Source)
	at io.quarkus.issue.MyComponent_ClientProxy.getMyDuration(Unknown Source)
	at io.quarkus.issue.MyComponentTest.testMyDuration(MyComponentTest.java:17)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
Caused by: jakarta.enterprise.inject.CreationException: Error creating synthetic bean [WtUE35eXet8tKWS0XAkVy-_OpGY]: java.lang.IllegalArgumentException: SRCFG00039: The config property sco.maxcurrentlistener.down.duration with the config value "60s" threw an Exception whilst being converted SRCFG00020: Failed to convert value with static method
	at io.quarkus.arc.generator.Object_WtUE35eXet8tKWS0XAkVy-_OpGY_Synthetic_Bean.doCreate(Unknown Source)
	at io.quarkus.arc.generator.Object_WtUE35eXet8tKWS0XAkVy-_OpGY_Synthetic_Bean.create(Unknown Source)
	at io.quarkus.arc.generator.Object_WtUE35eXet8tKWS0XAkVy-_OpGY_Synthetic_Bean.get(Unknown Source)
	at io.quarkus.arc.impl.CurrentInjectionPointProvider.get(CurrentInjectionPointProvider.java:48)
	... 17 more
Caused by: java.lang.IllegalArgumentException: SRCFG00039: The config property sco.maxcurrentlistener.down.duration with the config value "60s" threw an Exception whilst being converted SRCFG00020: Failed to convert value with static method
	at io.smallrye.config.SmallRyeConfig.convertValue(SmallRyeConfig.java:421)
	at io.smallrye.config.inject.ConfigProducerUtil.getValue(ConfigProducerUtil.java:100)
	at io.smallrye.config.inject.ConfigProducerUtil.getValue(ConfigProducerUtil.java:60)
	at io.quarkus.test.component.ConfigPropertyBeanCreator.create(ConfigPropertyBeanCreator.java:40)
	at io.quarkus.arc.generator.Object_WtUE35eXet8tKWS0XAkVy-_OpGY_Synthetic_Bean.createSynthetic(Unknown Source)
	... 21 more
Caused by: java.lang.IllegalArgumentException: SRCFG00020: Failed to convert value with static method
	at io.smallrye.config.ImplicitConverters$StaticMethodConverter.convert(ImplicitConverters.java:133)
	at io.smallrye.config.SmallRyeConfig.convertValue(SmallRyeConfig.java:419)
	... 25 more
Caused by: java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at io.smallrye.config.ImplicitConverters$StaticMethodConverter.convert(ImplicitConverters.java:131)
	... 26 more
Caused by: java.time.format.DateTimeParseException: Text cannot be parsed to a Duration
	at java.base/java.time.Duration.parse(Duration.java:419)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	... 28 more


Process finished with exit code 255

How to Reproduce?

Component:

package io.quarkus.issue;

import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import java.time.Duration;

@ApplicationScoped
public class MyComponent {

    @ConfigProperty(name = "sco.maxcurrentlistener.down.duration", defaultValue = "60s")
    private Duration myDuration;

    public String getMyDuration() {
        return myDuration.toString();
    }
}

Component Test:

package io.quarkus.issue;

import io.quarkus.test.component.QuarkusComponentTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

@QuarkusComponentTest
class MyComponentTest {

    @Inject
    MyComponent myComponent;

    @Test
    public void testMyDuration() {
        assertEquals("60s", myComponent.getMyDuration());
    }
}

Output of uname -a or ver

Darwin MacBook-Pro-8.local 22.5.0 Darwin Kernel Version 22.5.0: Mon Apr 24 20:53:19 PDT 2023; root:xnu-8796.121.2~5/RELEASE_ARM64_T6020 arm64

Output of java -version

openjdk version "22" 2024-03-19 OpenJDK Runtime Environment Temurin-22+36 (build 22+36) OpenJDK 64-Bit Server VM Temurin-22+36 (build 22+36, mixed mode)

Quarkus version or git rev

3.11.1

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

Maven home: /Users/user/.m2/wrapper/dists/apache-maven-3.9.3-bin/326f10f4/apache-maven-3.9.3 Java version: 22, vendor: Eclipse Adoptium, runtime: /Users/user/.sdkman/candidates/java/22-tem Default locale: en_GB, platform encoding: UTF-8 OS name: "mac os x", version: "13.4", arch: "aarch64", family: "mac"

Additional information

I tried adding the converter manually in test code by

    @BeforeEach
    public void registerConverter() {
        var instance = ConfigProviderResolver.instance();
        var builder = instance.getBuilder()
                .withConverters(new DurationConverter())
                .build();
        instance.registerConfig(builder, MyComponentTest.class.getClassLoader());
    }

or adding in the test method itself but this does not work.
The actual used Config from which the Converter is obtained is different than the one created in the test.
When trying to use the correct ClassLoader

        var contextClassLoader = Thread.currentThread().getContextClassLoader();
        instance.registerConfig(builder, contextClassLoader);

the test failed since a Config is already registered for that class loader.

@diversit diversit added the kind/bug Something isn't working label Jul 5, 2024
@quarkus-bot
Copy link

quarkus-bot bot commented Jul 5, 2024

/cc @radcortez (config)

@geoand
Copy link
Contributor

geoand commented Jul 5, 2024

cc @mkouba

@mkouba
Copy link
Contributor

mkouba commented Jul 8, 2024

Expected the Quarkus Config Converters to be available and used when running a QuarkusComponentTest test
or to have some kind of mechanism with which custom converters can be registered.

@diversit You're right that we don't register any Quarkus-specific converter and there's also no way to modify the underlying SmallRyeConfigBuilder. And I think that we should support both.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3 participants