quarkiverse / quarkus-wiremock

Quarkus extension for launching an in-process Wiremock server
https://wiremock.org/
Apache License 2.0
16 stars 10 forks source link

How to use this in a QuarkusIntegrationTest? #80

Closed edeandrea closed 6 months ago

edeandrea commented 9 months ago

I've read the documentation in https://docs.quarkiverse.io/quarkus-wiremock/dev/index.html#_configuration_retrieval but it doesn't seem the documentation is in-line with how things actually work.

I need to get a grasp on the wiremock port in an integration test. There is no such class called WireMockConfigKey in the library.

Additionally, it seems I need to create a QuarkusTestResourceLifecycleManager which also implements io.quarkus.test.common.DevServicesContext.ContextAware. What's strange though is that when I query the DevServicesContext, like this:

@Override
    public void setIntegrationTestContext(DevServicesContext context) {
      context.devServicesProperties().forEach((k, v) -> System.out.println("%s = %s".formatted()));
    }

I do not see a quarkus.wiremock.devservices.port. Instead I see %dev,test.quarkus.wiremock.devservices.port. This isn't really consistent with how other quarkus dev services work. I've never had to prefix a property with %anything when trying to pull a property from the dev services context.

Furthermore, the example ConfigProvider.getConfig().getValue(WireMockConfigKey.PORT, Integer.class); doesn't work either.

chberger commented 9 months ago

Hi @edeandrea,

thanks for your feedback. The dev documentation reflects the code base of main. However, these code changes aren't released yet. That's the reason why you don't find these classes and that's why the mentioned examples aren't working. If you would work with the main branch, everything should work as expected. This is really misleading ... sorry for that ... IMHO: The only way to avoid that is by releasing main changes immediately. However, I'm just working on that project in my spare time and I was not ready to release the current code base yet :-(

I do not see a quarkus.wiremock.devservices.port. Instead I see %dev,test.quarkus.wiremock.devservices.port. This isn't really consistent with how other quarkus dev services work. I've never had to prefix a property with %anything when trying to pull a property from the dev services context.

Generally, this Dev Service is somehow special. Normally, an extension provides some arbitrary functionality and additionally the Dev Service capabilities. In the WireMock case, the main purpose of the extension is the Dev Service itself. This means, this extension must not be part of the final production build. If I would declare properties without the %dev or %test prefix, these kind of properties would be eligible for the prod profile as well. However, since the extension is no present in the prod artifact, Quarkus would complain that these kind of properties are unknown. The only way I've figured out to overcome this limitation was by using those profile aware properties.

To be honest, I've never found an extension like this. All the other Quarkus extensions I've found did provide Dev Service in addition to other functionalities (so they are part of the production build with dev service capabilities disabled), or they have been part of the production build, although they are just relevant during dev and testing.

I'm happy to discuss other ideas ...

edeandrea commented 9 months ago

Ah got it re: the main vs released changes! Makes sense!

To be honest, I've never found an extension like this. All the other Quarkus extensions I've found did provide Dev Service in addition to other functionalities (so they are part of the production build with dev service capabilities disabled), or they have been part of the production build, although they are just relevant during dev and testing.

You may want to look at the microcks extension. It is very similar to what you are doing here.

Additionally, since most of the work being done here is in the deployment module of the extension, it won't end up on the production classpath. Quarkus makes sure of that. So sure there may be quarkus.wiremock properties, but they would be meaningless.

edeandrea commented 9 months ago

Oh and nice work on the extension! It's surely been needed!

chberger commented 9 months ago

You may want to look at the microcks extension. It is very similar to what you are doing here.

Thanks, I'll do so.

Additionally, since most of the work being done here is in the deployment module of the extension, it won't end up on the production classpath. Quarkus makes sure of that. So sure there may be quarkus.wiremock properties, but they would be meaningless.

I'm totally aware of that. IMHO, from an end user perspective, it feels more natural to declare the WireMock extension as a test dependency. I just need the capabilities during dev and test. If I would declare it as a compile dependency, the feature name would be present in the production build, I would get those unknown property warnings etc. I'm aware that almost nothing would end up the production classpath, but it would be still confusing for an end user ...

edeandrea commented 9 months ago

The issue with using the prefixes is that when the integration test starts, it does -D%dev,test.quarkus.wiremock.devservices.port= rather than just a property name combined with the quarkus profile that is used during launch, where quarkus can then look for properties given the correct profile. Now my code has to explicitly look for a property that begins with dev,test., which isn't really natural at all.

Also by declaring the dependency scope as test, it doesn't work at all in dev mode. I have to change scope to compile or provided in order to serve a mappings json file during dev mode.

chberger commented 9 months ago

The issue with using the prefixes is that when the integration test starts, it does -D%dev,test.quarkus.wiremock.devservices.port= rather than just a property name combined with the quarkus profile that is used during launch, where quarkus can then look for properties given the correct profile.

But that's exactly by intention. If I don't prefix the properties Quarkus would pass -Dquarkus.wiremock.devservices.port= which would lead to those unknown property warnings, since the extension is not part of the production build (test dependency).

Now my code has to explicitly look for a property that begins with dev,test., which isn't really natural at all.

That's not my experience. When the property has been propagated via -D%dev,test.quarkus.wiremock.devservices.port= you can even access it from within an integration test without that prefix:

https://github.com/quarkiverse/quarkus-wiremock/blob/main/integration-tests/src/test/java/io/quarkiverse/wiremock/devservice/WireMockDynamicPortTest.java#L29 (edit: at least via a QuarkusTest, not integration test)

Also by declaring the dependency scope as test, it doesn't work at all in dev mode. I have to change scope to compile or provided in order to serve a mappings json file during dev mode.

That's something new to me which I have to investigate further.

Overall, I got your point to switch to properties without a profile prefix and to declare this extension with compile scope, but it has been a long discussion where even @gastaldi has been involved and the outcome was to declare the extension via test scope: https://github.com/quarkiverse/quarkus-wiremock/pull/40#discussion_r1337208849

image

In case the json mapping is actually not working in dev mode, then for sure, we have to change it.

edeandrea commented 9 months ago

That's not my experience. When the property has been propagated via -D%dev,test.quarkus.wiremock.devservices.port= you can even access it from within an integration test without that prefix:

That just isn't true, as it doesn't work if I don't explicitly try to get the property using %dev,test.quarkus.wiremock.devservices.port:

image

Also, If I try to do ConfigProvider.getConfig().getValue("quarkus.wiremock.devservices.port", Integer.class), I get

java.lang.RuntimeException: java.util.NoSuchElementException: SRCFG00014: The config property quarkus.wiremock.devservices.port is required but it could not be found in any config source

    at io.quarkus.test.junit.QuarkusIntegrationTestExtension.throwBootFailureException(QuarkusIntegrationTestExtension.java:366)
    at io.quarkus.test.junit.QuarkusIntegrationTestExtension.beforeEach(QuarkusIntegrationTestExtension.java:117)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.util.NoSuchElementException: SRCFG00014: The config property quarkus.wiremock.devservices.port is required but it could not be found in any config source
    at io.smallrye.config.SmallRyeConfig.convertValue(SmallRyeConfig.java:296)
    at io.smallrye.config.SmallRyeConfig.getValue(SmallRyeConfig.java:241)
    at io.smallrye.config.SmallRyeConfig.getValue(SmallRyeConfig.java:167)
    at io.quarkus.sample.superheroes.narration.rest.NarrationResourceIT$WiremockTestResourceLifecycleManager.setIntegrationTestContext(NarrationResourceIT.java:214)
    at io.quarkus.test.common.TestResourceManager.<init>(TestResourceManager.java:98)
    at io.quarkus.test.common.TestResourceManager.<init>(TestResourceManager.java:61)
    at io.quarkus.test.junit.QuarkusIntegrationTestExtension.doProcessStart(QuarkusIntegrationTestExtension.java:214)
    at io.quarkus.test.junit.QuarkusIntegrationTestExtension.ensureStarted(QuarkusIntegrationTestExtension.java:163)
    at io.quarkus.test.junit.QuarkusIntegrationTestExtension.beforeAll(QuarkusIntegrationTestExtension.java:130)
    ... 1 more

That's something new to me which I have to investigate further.

Weird - it must have been something I was doing prior, because now when I re-declare the dependency as test scope it does work in dev mode. I must have been doing something else to cause it to not work, so I'm sorry about that!

edeandrea commented 9 months ago

Weird - it must have been something I was doing prior, because now when I re-declare the dependency as test scope it does work in dev mode. I must have been doing something else to cause it to not work, so I'm sorry about that!

Actually, I take that back. I had changed the scope of the wrong dependency. When I have the quarkus-wiremock dependency as test scope and try to run dev mode with %dev,test.quarkus.langchain4j.openai.base-url=http://localhost:${quarkus.wiremock.devservices.port}/v1/ in my properties, dev mode fails to start:

12:37:13 ERROR [io.qu.ru.Application] (Quarkus Main Thread) Failed to start application (with profile [dev]): java.lang.RuntimeException: Failed to start quarkus
        at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)
        at io.quarkus.runtime.Application.start(Application.java:101)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:111)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:71)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:124)
        at io.quarkus.runner.GeneratedMain.main(Unknown Source)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:113)
        at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: io.smallrye.config.ConfigValidationException: Configuration validation failed:
        java.util.NoSuchElementException: SRCFG00011: Could not expand value quarkus.wiremock.devservices.port in property quarkus.langchain4j.openai.base-url
        at io.smallrye.config.ConfigMappingProvider.mapConfigurationInternal(ConfigMappingProvider.java:1004)
        at io.smallrye.config.ConfigMappingProvider.lambda$mapConfiguration$3(ConfigMappingProvider.java:962)
        at io.smallrye.config.SecretKeys.doUnlocked(SecretKeys.java:28)
        at io.smallrye.config.ConfigMappingProvider.mapConfiguration(ConfigMappingProvider.java:962)
        at io.smallrye.config.ConfigMappings.mapConfiguration(ConfigMappings.java:91)
        at io.smallrye.config.SmallRyeConfigBuilder.build(SmallRyeConfigBuilder.java:699)
        at io.quarkus.runtime.generated.Config.readConfig(Unknown Source)
        at io.quarkus.runtime.generated.Config.createRunTimeConfig(Unknown Source)
        at io.quarkus.deployment.steps.RuntimeConfigSetup.deploy(Unknown Source)
        ... 13 more

2023-12-13 12:37:13,157 ERROR [io.qua.run.Application] (Quarkus Main Thread) Failed to start application (with profile [dev]): java.lang.RuntimeException: Failed to start quarkus
        at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)
        at io.quarkus.runtime.Application.start(Application.java:101)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:111)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:71)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:124)
        at io.quarkus.runner.GeneratedMain.main(Unknown Source)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:113)
        at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: io.smallrye.config.ConfigValidationException: Configuration validation failed:
        java.util.NoSuchElementException: SRCFG00011: Could not expand value quarkus.wiremock.devservices.port in property quarkus.langchain4j.openai.base-url
        at io.smallrye.config.ConfigMappingProvider.mapConfigurationInternal(ConfigMappingProvider.java:1004)
        at io.smallrye.config.ConfigMappingProvider.lambda$mapConfiguration$3(ConfigMappingProvider.java:962)
        at io.smallrye.config.SecretKeys.doUnlocked(SecretKeys.java:28)
        at io.smallrye.config.ConfigMappingProvider.mapConfiguration(ConfigMappingProvider.java:962)
        at io.smallrye.config.ConfigMappings.mapConfiguration(ConfigMappings.java:91)
        at io.smallrye.config.SmallRyeConfigBuilder.build(SmallRyeConfigBuilder.java:699)
        at io.quarkus.runtime.generated.Config.readConfig(Unknown Source)
        at io.quarkus.runtime.generated.Config.createRunTimeConfig(Unknown Source)
        at io.quarkus.deployment.steps.RuntimeConfigSetup.deploy(Unknown Source)
        ... 13 more

12:37:13 INFO  [io.qu.de.de.IsolatedDevModeMain] (main) Attempting to start live reload endpoint to recover from previous Quarkus startup failure
2023-12-13 12:37:13,165 INFO  [io.qua.dep.dev.IsolatedDevModeMain] (main) Attempting to start live reload endpoint to recover from previous Quarkus startup failure

Switching the scope of quarkus-wiremock to provided solves this problem.

edeandrea commented 9 months ago

It also doesn't work when I do ./mvnw verify -Dquarkus.container-image.build=true. When the container process starts:

docker run \
  -javaagent:/Users/edeandre/.m2/repository/org/jacoco/org.jacoco.agent/0.8.11/org.jacoco.agent-0.8.11-runtime.jar=destfile=/Users/edeandre/workspaces/quarkus/quarkus-super-heroes/rest-narration/target/jacoco-quarkus-it.exec,append=true \
  --name quarkus-integration-test-VyyGF \
  -i \
  --rm \
  -p 52561:52561 \
  -p 8444:8444 \
  --net=quarkus-integration-test-WWfXR \
  --env QUARKUS_LOG_CATEGORY__IO_QUARKUS__LEVEL=INFO \
  --env QUARKUS_HTTP_PORT=52561 \
  --env QUARKUS_HTTP_SSL_PORT=8444 \
  --env TEST_URL=http://localhost:0 \
  --env _DEV_TEST_QUARKUS_WIREMOCK_DEVSERVICES_PORT=8089 \
  --env NARRATION_MAKE_LIVE_CALLS=true \
  --env QUARKUS_LANGCHAIN4J_OPENAI_MAX_RETRIES=1 \
  --env QUARKUS_LANGCHAIN4J_OPENAI_LOG_RESPONSES=true \
  --env QUARKUS_LANGCHAIN4J_OPENAI_BASE_URL=http://localhost:8089/v1/ \
  --env QUARKUS_LANGCHAIN4J_OPENAI_LOG_REQUESTS=true \
  --env QUARKUS_CONTAINER_IMAGE_BUILD=true \
  --env QUARKUS_LANGCHAIN4J_OPENAI_TIMEOUT=3s \
  --env QUARKUS_TEST_ARG_LINE=-javaagent:/Users/edeandre/.m2/repository/org/jacoco/org.jacoco.agent/0.8.11/org.jacoco.agent-0.8.11-runtime.jar=destfile=/Users/edeandre/workspaces/quarkus/quarkus-super-heroes/rest-narration/target/jacoco-quarkus-it.exec,append=true \
  --env QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=my-key \
  quay.io/quarkus-super-heroes/rest-narration:1.0

You'll notice the --env _DEV_TEST_QUARKUS_WIREMOCK_DEVSERVICES_PORT=8089, which then becomes completely unrecognizeable (because in var -> env translation, anything not a character gets translated to _, including the leading %).

chberger commented 9 months ago

To be honest, I'm a little surprised at all the things that don't work. 😲 I feel very embarrassed.

Anyway, I'll try to answer all your findings as structured as possible:

Accessing properties without the profile prefix:

At least, this repo has an integration test that can access the propagated -D%dev,test.quarkus.wiremock.devservices.port=50687 property without a profile prefix:

[INFO] Running io.quarkiverse.wiremock.devservice.WireMockDevServiceBasicIT
Executing "/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home/bin/java -Dquarkus.http.port=8081 -Dquarkus.http.ssl-port=8444 -Dtest.url=http://localhost:8081 -Dquarkus.log.file.path=/Users/chberger/git/quarkiverse/quarkus-wiremock/integration-tests/target/quarkus.log -Dquarkus.log.file.enable=true -Dquarkus.log.category."io.quarkus".level=INFO -D%dev,test.quarkus.wiremock.devservices.port=50687 -jar /Users/chberger/git/quarkiverse/quarkus-wiremock/integration-tests/target/quarkus-app/quarkus-run.jar"

Related test class: https://github.com/quarkiverse/quarkus-wiremock/blob/main/integration-tests/src/test/java/io/quarkiverse/wiremock/devservice/WireMockDevServiceBasicIT.java#L39

However, I totally agree when u access the DevServiceContext directly, you need to specify the exact key including all profiles. That's because this is the way how the property gets published: https://github.com/quarkiverse/quarkus-wiremock/blob/main/deployment/src/main/java/io/quarkiverse/wiremock/devservice/WireMockServerProcessor.java#L87C9-L87C15

I am also of the opinion that this feels not natural at all. However, I wasn't aware that extension users will use the DevServiceContext to retrieve configurations. IMHO a user should use DI or the ConfigProviderto retrieve configurations, and not the DevServiceContext, but yeah it's a public API.

Serve json mappings during dev mode

I've just checked that quickly. But it seems like you are right, the json mappings are being ignored in dev mode in case the extension has been declared with testscope. :-(

Dev mode fails to start

That's also something I have to investigate further. I've successfully tested property expansion in test mode only. But it looks like the dev mode behaves differently here as well.

Issue with the env mapping in container

To be honest, I don't get that point as it looks like this is the expected representation of the property in form of an environment variable.

Summary

Considering all your findings I believe it was the wrong decision to use the test scope for this extension. Especially, as this has led to the introduction of profile aware properties. Furthermore, it seems like that a lot of issues might be solved just by using properties without profiles and declaring the extension via compile scope.

I would be willing to change the code base accordingly. Thanks!

edeandrea commented 9 months ago

Thank you for the detailed comments!

I feel very embarrassed.

Oh, please don't! This extension is great and something thats been missing for a long time! I'm just trying to help make it better!

However, I wasn't aware that extension users will use the DevServiceContext to retrieve configurations.

This is how I've usually done it - inside a QuarkusTestResourceLifecycleManager class (or a QuarkusTestProfile instance). It needs to be done/read there because the value needs to be set inside another property value which needs to be done before the application starts up. Doing it inside the ConfigProvider inside the test class is too late in the lifecycle.

Your comments did give me an idea. I tried something a bit different just now. In my QuarkusTestProfile I just now tried this:

@Override
public Map<String, String> start() {
  return Map.of(
    "quarkus.langchain4j.openai.base-url", "http://localhost:${%dev,test.quarkus.wiremock.devservices.port}/v1/"
  );
}

And it does seem that the variable interpolation does indeed work. However, it doesn't work if I remove the %dev,test prefix, which as you concluded just doesn't feel natural and is something I've never had to do with any other extension before.

This use case may be a bit different because its in an @QuarkusIntegrationTest, not a @QuarkusTest. @QuarkusIntegrationTests run in the prod profile.

Dev mode fails to start

Things in dev mode don't have access to dependencies in the test scope.

it seems like that a lot of issues might be solved just by using properties without profiles and declaring the extension via compile scope.

provided scope may be a great compromise here (that's actually how I'm declaring my wiremock extension dependency), while I keep the quarkus-wiremock-test dependency as test scope.

Issue with the env mapping in container

I may have to investigate this further, but when I try to run the integration tests in a container (via ./mvnw verify -Dquarkus.container-image.build=true) it can't successfully resolve the URL to the container.

UNLESS (this is just a thought - I haven't yet proven it out) - wiremock is running in the jvm of the test class & the container is running, well, on the container network. So it would be impossible for the app to connect to localhost:8089, because in that context, localhost is the container network, not the host network, and in there nothing is running on port 8089.

I think IF wiremock were running as a container rather than an in-memory process, then this would work fine. Its how things like microcks work.

I've seen similar issues with things like the Kafka companion used in an integration test class trying to connect to an Apicurio instance that is running on the container network (somewhat the inverse issue of what we're experiencing here).

Like I mentioned at the beginning of this comment, this is great stuff! I'm in process of integrating it into the Quarkus Superheroes Sample application, hence where all my questions/comments are coming from. I'm the primary maintainer of that project.

chberger commented 9 months ago

Thank you for the detailed comments!

I feel very embarrassed.

Oh, please don't! This extension is great and something thats been missing for a long time! I'm just trying to help make it better!

Thanks, your feedback is highly appreciated!

However, I wasn't aware that extension users will use the DevServiceContext to retrieve configurations.

This is how I've usually done it - inside a QuarkusTestResourceLifecycleManager class (or a QuarkusTestProfile instance). It needs to be done/read there because the value needs to be set inside another property value which needs to be done before the application starts up. Doing it inside the ConfigProvider inside the test class is too late in the lifecycle.

Your comments did give me an idea. I tried something a bit different just now. In my QuarkusTestProfile I just now tried this:

@Override
public Map<String, String> start() {
  return Map.of(
    "quarkus.langchain4j.openai.base-url", "http://localhost:${%dev,test.quarkus.wiremock.devservices.port}/v1/"
  );
}

And it does seem that the variable interpolation does indeed work. However, it doesn't work if I remove the %dev,test prefix, which as you concluded just doesn't feel natural and is something I've never had to do with any other extension before.

Yeah, I agree this is wired. Although, I still believe variable interpolation should work without the %dev,test prefix in test mode. At least I remember a scenario where I already used it. 🤔

This use case may be a bit different because its in an @QuarkusIntegrationTest, not a @QuarkusTest. @QuarkusIntegrationTests run in the prod profile.

Dev mode fails to start

Things in dev mode don't have access to dependencies in the test scope.

Ah, wasn't ware of that. This means the test classpath is entirely different from the dev classpath?

it seems like that a lot of issues might be solved just by using properties without profiles and declaring the extension via compile scope.

provided scope may be a great compromise here (that's actually how I'm declaring my wiremock extension dependency), while I keep the quarkus-wiremock-test dependency as test scope.

I agree, keeping the quarkus-wiremock-test dependency as test scope makes absolutely sense. However, declaring the wiremock extension with the provided scope might raise those unknown property warnings again. This is just my assumption, I haven't tested it yet, but from my understanding in case of a QuarkusIntegrationTest the app runs in the prod profile while Quarkus passes the DevServiceContext configurations via system properties to app under test. And since the extension is not present in the production classpath, Quarkus should complain about those unrecognized properties. That's the whole dilemma why I introduced those properties prefixed with profiles.

Using the compile scope might solve this issue. I only have to make sure that almost nothing gets into the production classpath, beside the build time config? @edeandrea wdyt?

Issue with the env mapping in container

I may have to investigate this further, but when I try to run the integration tests in a container (via ./mvnw verify -Dquarkus.container-image.build=true) it can't successfully resolve the URL to the container.

UNLESS (this is just a thought - I haven't yet proven it out) - wiremock is running in the jvm of the test class & the container is running, well, on the container network. So it would be impossible for the app to connect to localhost:8089, because in that context, localhost is the container network, not the host network, and in there nothing is running on port 8089.

I think IF wiremock were running as a container rather than an in-memory process, then this would work fine. Its how things like microcks work.

I've already thought about it too. However, starting WireMock in a container would just add another layer of complexity. I know for Dev Services this is the common case using Testcontainers. Anyway, I thought it might work without the requirement of a container runtime. At least for Docker there is special DNS name host.docker.internal which will resolve to the internal IP address used by the host. However, I agree this only works within the Docker's default bridge network, not within custom networks. Not sure if Podman has something similar.

I've seen similar issues with things like the Kafka companion used in an integration test class trying to connect to an Apicurio instance that is running on the container network (somewhat the inverse issue of what we're experiencing here).

Like I mentioned at the beginning of this comment, this is great stuff! I'm in process of integrating it into the Quarkus Superheroes Sample application, hence where all my questions/comments are coming from. I'm the primary maintainer of that project.

Yeah - this sounds really cool!

edeandrea commented 9 months ago

Using the compile scope might solve this issue. I only have to make sure that almost nothing gets into the production classpath, beside the build time config?

Maybe I'm mis-understanding the scopes, but I was under the assumption that provided was essentially the same as compile, except that it was not including in the runtime artifact. This way at build time (i.e. dev mode) the libs would be there, but they wouldn't be included in the built artifact. Isn't that what we want? I use provided scope for other extensions to solve this exact same problem.

Re the container thing - I'm totally with you that using the container adds an extra layer of complexity and also removes functionality (not sure you could then inject the WireMock instance to do extra stubbing/verification with that).

Just thinking out loud, but what about using host.testcontainers.internal as the hostname (https://java.testcontainers.org/features/networking/#exposing-host-ports-to-the-container)? Not sure this will work, though, because its not running as a container. Maybe I'll give it a try.

I still believe variable interpolation should work without the %dev,test prefix in test mode. At least I remember a scenario where I already used it.

Maybe thats the case (I'm not using it that way in test mode). I'm using it in integration tests, which runs in prod profile.

chberger commented 9 months ago

Using the compile scope might solve this issue. I only have to make sure that almost nothing gets into the production classpath, beside the build time config?

Maybe I'm mis-understanding the scopes, but I was under the assumption that provided was essentially the same as compile, except that it was not including in the runtime artifact. This way at build time (i.e. dev mode) the libs would be there, but they wouldn't be included in the built artifact. Isn't that what we want? I use provided scope for other extensions to solve this exact same problem.

Yeah that's exactly what we want beside the fact that I believe that Quarkus will complain about unrecognized wiremock properties when running in production mode while executing integration tests. That's because provided will avoid that anything from the extension (especially the WireMockServerBuildTimeConfig) will end up in the production classpath. But maybe I'm wrong again, so let me try to reproduce it.

Re the container thing - I'm totally with you that using the container adds an extra layer of complexity and also removes functionality (not sure you could then inject the WireMock instance to do extra stubbing/verification with that).

Just thinking out loud, but what about using host.testcontainers.internal as the hostname (https://java.testcontainers.org/features/networking/#exposing-host-ports-to-the-container)? Not sure this will work, though, because its not running as a container. Maybe I'll give it a try.

Yeah, sounds promising.

I still believe variable interpolation should work without the %dev,test prefix in test mode. At least I remember a scenario where I already used it.

Maybe thats the case (I'm not using it that way in test mode). I'm using it in integration tests, which runs in prod profile.

chberger commented 9 months ago

Yep, I've been able to reproduce it:

        <dependency>
            <groupId>io.quarkiverse.wiremock</groupId>
            <artifactId>quarkus-wiremock</artifactId>
            <version>${project.version}</version>
            <scope>provided</scope>
        </dependency>
INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running io.quarkiverse.wiremock.devservice.WireMockDevServiceBasicIT
Executing "java -Dquarkus.http.port=8081 -Dquarkus.http.ssl-port=8444 -Dtest.url=http://localhost:8081 -Dquarkus.log.file.path=C:\Users\Christian\git\quarkiverse\quarkus-wiremock\integration-tests\target\quarkus.log -Dquarkus.log.file.enable=true -Dquarkus.log.category."io.quarkus".level=INFO -Dquarkus.wire
mock.devservices.port=58752 -jar C:\Users\Christian\git\quarkiverse\quarkus-wiremock\integration-tests\target\quarkus-app\quarkus-run.jar"
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-12-14 18:01:47,493 WARN  [io.qua.config] (main) Unrecognized configuration key "quarkus.wiremock.devservices.port" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
2023-12-14 18:01:47,493 WARN  [io.qua.config] (main) Unrecognized configuration key "quarkus.log.category.io.quarkus.level" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
2023-12-14 18:01:47,787 INFO  [io.quarkus] (main) quarkus-wiremock-integration-tests 1.0.1-SNAPSHOT on JVM (powered by Quarkus 3.2.9.Final) started in 0.475s. Listening on: http://0.0.0.0:8081
2023-12-14 18:01:47,788 INFO  [io.quarkus] (main) Profile prod activated.
2023-12-14 18:01:47,788 INFO  [io.quarkus] (main) Installed features: [cdi, rest-client-reactive, resteasy-reactive, smallrye-context-propagation, vertx]
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.090 s -- in io.quarkiverse.wiremock.devservice.WireMockDevServiceBasicIT

This is the message I've been talking about all along 2023-12-14 18:01:47,493 WARN [io.qua.config] (main) Unrecognized configuration key "quarkus.wiremock.devservices.port" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo

edeandrea commented 9 months ago

Yeah I see those messages all the time from lots of extensions that only do work at build time (kubernetes/openshift/minishift/knative/etc).

chberger commented 9 months ago

Well, if we have to live with that, then it is like it is ...

edeandrea commented 9 months ago

Hi @chberger I just wanted to re-visit this as I have a little bit more information re: the use of %dev,test.quarkus.wiremock.devservices.port

I've just attempted to run the integration test as a container image by doing ./mvnw clean verify -Dquarkus.container-image.build=true and the container it created failed to start up in the integration test:

[INFO] --- failsafe:3.2.3:integration-test (default) @ rest-narration ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running io.quarkus.sample.superheroes.narration.rest.NarrationResourceIT
10:59:26 WARN  [or.te.do.DockerClientProviderStrategy] (main) Can't instantiate a strategy from org.testcontainers.dockerclient.UnixSocketClientProviderStrategy (ClassNotFoundException). This probably means that cached configuration refers to a client provider class that is not available in this version of Testcontainers. Other strategies will be tried instead.
10:59:26 WARN  [or.te.do.DockerClientProviderStrategy] (main) DOCKER_HOST tcp://127.0.0.1:49217 is not listening
10:59:26 WARN  [or.te.do.DockerClientProviderStrategy] (main) DOCKER_HOST tcp://127.0.0.1:49217 is not listening
10:59:26 ERROR [or.te.do.DockerClientProviderStrategy] (main) Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
As no valid configuration was found, execution cannot continue.
See https://java.testcontainers.org/on_failure.html for more details.
10:59:27 INFO  [io.qu.te.co.DefaultDockerContainerLauncher] (main) Executing "docker run --name quarkus-integration-test-vFiIY -i --rm -p 54948:54948 -p 8444:8444 --net=quarkus-integration-test-TFGgX --env QUARKUS_LOG_CATEGORY__IO_QUARKUS__LEVEL=INFO --env QUARKUS_HTTP_PORT=54948 --env QUARKUS_HTTP_SSL_PORT=8444 --env TEST_URL=http://0.0.0.0:0 --env _DEV_TEST_QUARKUS_WIREMOCK_DEVSERVICES_PORT=8089 --env QUARKUS_LANGCHAIN4J_OPENAI_MAX_RETRIES=2 --env QUARKUS_LANGCHAIN4J_OPENAI_LOG_RESPONSES=true --env QUARKUS_LANGCHAIN4J_OPENAI_BASE_URL=http://localhost:${%dev,test.quarkus.wiremock.devservices.port}/v1/ --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_MAX_RETRIES=2 --env QUARKUS_CONTAINER_IMAGE_BUILD=true --env QUARKUS_LANGCHAIN4J_OPENAI_TIMEOUT=3s --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_LOG_REQUESTS=true --env QUARKUS_TEST_ARG_LINE= --env NARRATION_MAKE_LIVE_CALLS=true --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_BASE_URL=http://localhost:${%dev,test.quarkus.wiremock.devservices.port}/v1/ --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_TIMEOUT=3s --env QUARKUS_CONFIGURATION_BUILD_TIME_MISMATCH_AT_RUNTIME=fail --env QUARKUS_LANGCHAIN4J_OPENAI_LOG_REQUESTS=true --env QUARKUS_HTTP_HOST=0.0.0.0 --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_LOG_RESPONSES=true quay.io/quarkus-super-heroes/rest-narration:1.0"
Application was not started: ERROR: Failed to start application (with profile [prod])
Failed to launch the application. The application logs can be found at: /Users/edeandre/workspaces/quarkus/quarkus-super-heroes/rest-narration/target/quarkus.log
[ERROR] Tests run: 5, Failures: 0, Errors: 1, Skipped: 4, Time elapsed: 4.026 s <<< FAILURE! -- in io.quarkus.sample.superheroes.narration.rest.NarrationResourceIT
[ERROR] io.quarkus.sample.superheroes.narration.rest.NarrationResourceIT.helloEndpoint -- Time elapsed: 0.004 s <<< ERROR!
java.lang.RuntimeException: java.lang.IllegalStateException: Unable to determine the status of the running process. See the above logs for details
        at io.quarkus.test.junit.QuarkusIntegrationTestExtension.throwBootFailureException(QuarkusIntegrationTestExtension.java:366)
        at io.quarkus.test.junit.QuarkusIntegrationTestExtension.beforeEach(QuarkusIntegrationTestExtension.java:117)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.IllegalStateException: Unable to determine the status of the running process. See the above logs for details
        at io.quarkus.test.common.LauncherUtil.waitForCapturedListeningData(LauncherUtil.java:102)
        at io.quarkus.test.common.DefaultDockerContainerLauncher.start(DefaultDockerContainerLauncher.java:179)
        at io.quarkus.test.junit.IntegrationTestUtil.startLauncher(IntegrationTestUtil.java:195)
        at io.quarkus.test.junit.QuarkusIntegrationTestExtension.doProcessStart(QuarkusIntegrationTestExtension.java:294)
        at io.quarkus.test.junit.QuarkusIntegrationTestExtension.ensureStarted(QuarkusIntegrationTestExtension.java:163)
        at io.quarkus.test.junit.QuarkusIntegrationTestExtension.beforeAll(QuarkusIntegrationTestExtension.java:130)
        ... 1 more

When I further inspect the generated quarkus.log file, this is what I find:

Starting the Java application using /opt/jboss/container/java/run/run-java.sh ...
INFO exec -a "java" java -XX:MaxRAMPercentage=80.0 -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:+ExitOnOutOfMemoryError -cp "." -jar /deployments/quarkus-run.jar 
INFO running in /deployments
Jan 05, 2024 3:59:27 PM org.hibernate.validator.internal.util.Version
INFO: HV000001: Hibernate Validator %s
Jan 05, 2024 3:59:28 PM io.quarkus.runtime.ApplicationLifecycleManager run
ERROR: Failed to start application (with profile [prod])
java.lang.RuntimeException: Failed to start quarkus
    at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)
    at io.quarkus.runtime.Application.start(Application.java:101)
    at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:111)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:71)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:124)
    at io.quarkus.runner.GeneratedMain.main(Unknown Source)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at io.quarkus.bootstrap.runner.QuarkusEntryPoint.doRun(QuarkusEntryPoint.java:61)
    at io.quarkus.bootstrap.runner.QuarkusEntryPoint.main(QuarkusEntryPoint.java:32)
Caused by: io.smallrye.config.ConfigValidationException: Configuration validation failed:
    java.util.NoSuchElementException: SRCFG00011: Could not expand value %dev,test.quarkus.wiremock.devservices.port in property quarkus.langchain4j.azure-openai.base-url
    java.util.NoSuchElementException: SRCFG00011: Could not expand value %dev,test.quarkus.wiremock.devservices.port in property quarkus.langchain4j.openai.base-url
    at io.smallrye.config.ConfigMappingProvider.mapConfigurationInternal(ConfigMappingProvider.java:1004)
    at io.smallrye.config.ConfigMappingProvider.lambda$mapConfiguration$3(ConfigMappingProvider.java:962)
    at io.smallrye.config.SecretKeys.doUnlocked(SecretKeys.java:28)
    at io.smallrye.config.ConfigMappingProvider.mapConfiguration(ConfigMappingProvider.java:962)
    at io.smallrye.config.ConfigMappings.mapConfiguration(ConfigMappings.java:91)
    at io.smallrye.config.SmallRyeConfigBuilder.build(SmallRyeConfigBuilder.java:699)
    at io.quarkus.runtime.generated.Config.readConfig(Unknown Source)
    at io.quarkus.runtime.generated.Config.createRunTimeConfig(Unknown Source)
    at io.quarkus.deployment.steps.RuntimeConfigSetup.deploy(Unknown Source)
    ... 11 more

It seems that even though it ran the command

docker run --name quarkus-integration-test-vFiIY -i --rm -p 54948:54948 -p 8444:8444 --net=quarkus-integration-test-TFGgX --env QUARKUS_LOG_CATEGORY__IO_QUARKUS__LEVEL=INFO --env QUARKUS_HTTP_PORT=54948 --env QUARKUS_HTTP_SSL_PORT=8444 --env TEST_URL=http://0.0.0.0:0 --env _DEV_TEST_QUARKUS_WIREMOCK_DEVSERVICES_PORT=8089 --env QUARKUS_LANGCHAIN4J_OPENAI_MAX_RETRIES=2 --env QUARKUS_LANGCHAIN4J_OPENAI_LOG_RESPONSES=true --env QUARKUS_LANGCHAIN4J_OPENAI_BASE_URL=http://localhost:${%dev,test.quarkus.wiremock.devservices.port}/v1/ --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_MAX_RETRIES=2 --env QUARKUS_CONTAINER_IMAGE_BUILD=true --env QUARKUS_LANGCHAIN4J_OPENAI_TIMEOUT=3s --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_LOG_REQUESTS=true --env QUARKUS_TEST_ARG_LINE= --env NARRATION_MAKE_LIVE_CALLS=true --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_BASE_URL=http://localhost:${%dev,test.quarkus.wiremock.devservices.port}/v1/ --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_TIMEOUT=3s --env QUARKUS_CONFIGURATION_BUILD_TIME_MISMATCH_AT_RUNTIME=fail --env QUARKUS_LANGCHAIN4J_OPENAI_LOG_REQUESTS=true --env QUARKUS_HTTP_HOST=0.0.0.0 --env QUARKUS_LANGCHAIN4J_AZURE_OPENAI_LOG_RESPONSES=true quay.io/quarkus-super-heroes/rest-narration:1.0

it couldn't resolve the placeholders

chberger commented 8 months ago

@all-contributors add @edeandrea for bug

allcontributors[bot] commented 8 months ago

@chberger

I've put up a pull request to add @edeandrea! :tada:

github-actions[bot] commented 6 months ago

@chberger This is being labeled as stale.

github-actions[bot] commented 6 months ago

@chberger This is being closed due to inactivity.