This is an automated email from the ASF dual-hosted git repository.

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new f7f8d08ebe Use WireMock for Dropbox integration tests
f7f8d08ebe is described below

commit f7f8d08ebe0321da4cea9a361592ccf3caeaa42f
Author: James Netherton <[email protected]>
AuthorDate: Sat Oct 24 07:55:11 2026 +0100

    Use WireMock for Dropbox integration tests
    
    Fixes #4047
---
 integration-tests/dropbox/README.adoc              |   8 +-
 integration-tests/dropbox/pom.xml                  |   5 +
 .../component/dropbox/it/DropboxProducers.java     |  66 ++++++++++
 .../component/dropbox/it/DropboxResource.java      |   8 +-
 .../quarkus/component/dropbox/it/DropboxIT.java    |   3 -
 .../quarkus/component/dropbox/it/DropboxTest.java  |   9 +-
 .../component/dropbox/it/DropboxTestResource.java  | 135 +++++++++++++++++++++
 7 files changed, 222 insertions(+), 12 deletions(-)

diff --git a/integration-tests/dropbox/README.adoc 
b/integration-tests/dropbox/README.adoc
index 5feca47141..4b1248215f 100644
--- a/integration-tests/dropbox/README.adoc
+++ b/integration-tests/dropbox/README.adoc
@@ -1,6 +1,8 @@
 == Camel Quarkus Dropbox Integration Tests
 
-To run the Dropbox integration tests, you need a valid Dropbox 
https://www.dropbox.com/developers[developer account]. Then from
+By default the Dropbox integration tests use WireMock to stub the API 
interactions.
+
+To run the Dropbox integration tests against the real API, you need a valid 
Dropbox https://www.dropbox.com/developers[developer account]. Then from
 the developer console, create a new app and make a note of the access token.
 
 You will also require a refresh token for authentication. If you're planning 
on repeatedly performing tests, then it's a good idea to
@@ -19,3 +21,7 @@ export DROPBOX_API_SECRET=your-app-api-secret
 export DROPBOX_CLIENT_IDENTIFIER=your-client-identifier
 export DROPBOX_REFRESH_TOKEN=your-refresh-token
 ----
+
+WireMock is only disabled when all of the above environment variables are set.
+
+The WireMock stubs are defined in the `stubFor` blocks within 
`DropboxTestResource`. WireMock recording is not supported, because the Dropbox 
API is spread across multiple hosts.
diff --git a/integration-tests/dropbox/pom.xml 
b/integration-tests/dropbox/pom.xml
index 5918c83b32..1515acc200 100644
--- a/integration-tests/dropbox/pom.xml
+++ b/integration-tests/dropbox/pom.xml
@@ -51,6 +51,11 @@
             <artifactId>rest-assured</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-integration-wiremock-support</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 
diff --git 
a/integration-tests/dropbox/src/main/java/org/apache/camel/quarkus/component/dropbox/it/DropboxProducers.java
 
b/integration-tests/dropbox/src/main/java/org/apache/camel/quarkus/component/dropbox/it/DropboxProducers.java
new file mode 100644
index 0000000000..09ae54f391
--- /dev/null
+++ 
b/integration-tests/dropbox/src/main/java/org/apache/camel/quarkus/component/dropbox/it/DropboxProducers.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.quarkus.component.dropbox.it;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.util.Optional;
+
+import com.dropbox.core.DbxRequestConfig;
+import com.dropbox.core.http.StandardHttpRequestor;
+import com.dropbox.core.v2.DbxClientV2;
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Named;
+import jakarta.inject.Singleton;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+public class DropboxProducers {
+
+    /**
+     * The Dropbox SDK always builds https URLs from bare host names, so it 
cannot be pointed at WireMock
+     * via DbxHost. Instead, requests are redirected to WireMock at the HTTP 
layer.
+     */
+    @Produces
+    @Singleton
+    @Named("wireMockDropboxClient")
+    DbxClientV2 wireMockDropboxClient(
+            @ConfigProperty(name = "wiremock.url") Optional<String> 
wireMockUrl,
+            @ConfigProperty(name = "DROPBOX_CLIENT_IDENTIFIER") String 
clientIdentifier,
+            @ConfigProperty(name = "DROPBOX_ACCESS_TOKEN") String accessToken) 
{
+        DbxRequestConfig config = DbxRequestConfig.newBuilder(clientIdentifier)
+                .withHttpRequestor(new 
WireMockHttpRequestor(wireMockUrl.orElseThrow()))
+                .build();
+        return new DbxClientV2(config, accessToken);
+    }
+
+    static final class WireMockHttpRequestor extends StandardHttpRequestor {
+        private final String wireMockUrl;
+
+        WireMockHttpRequestor(String wireMockUrl) {
+            super(Config.DEFAULT_INSTANCE);
+            this.wireMockUrl = wireMockUrl;
+        }
+
+        @Override
+        protected HttpURLConnection prepRequest(String url, Iterable<Header> 
headers, boolean streaming) throws IOException {
+            URI uri = URI.create(url);
+            String query = uri.getRawQuery() != null ? "?" + uri.getRawQuery() 
: "";
+            return super.prepRequest(wireMockUrl + uri.getRawPath() + query, 
headers, streaming);
+        }
+    }
+}
diff --git 
a/integration-tests/dropbox/src/main/java/org/apache/camel/quarkus/component/dropbox/it/DropboxResource.java
 
b/integration-tests/dropbox/src/main/java/org/apache/camel/quarkus/component/dropbox/it/DropboxResource.java
index 2065a0fe45..1f109612db 100644
--- 
a/integration-tests/dropbox/src/main/java/org/apache/camel/quarkus/component/dropbox/it/DropboxResource.java
+++ 
b/integration-tests/dropbox/src/main/java/org/apache/camel/quarkus/component/dropbox/it/DropboxResource.java
@@ -20,6 +20,7 @@ import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.Optional;
 
 import com.dropbox.core.v2.files.DownloadErrorException;
 import jakarta.inject.Inject;
@@ -33,6 +34,7 @@ import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 import org.apache.camel.CamelExecutionException;
 import org.apache.camel.ProducerTemplate;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
 
 import static 
org.apache.camel.component.dropbox.util.DropboxConstants.HEADER_PUT_FILE_NAME;
 
@@ -46,6 +48,9 @@ public class DropboxResource {
     @Inject
     ProducerTemplate producerTemplate;
 
+    @ConfigProperty(name = "wiremock.url")
+    Optional<String> wireMockUrl;
+
     @Path("/create")
     @POST
     @Consumes(MediaType.TEXT_PLAIN)
@@ -89,11 +94,12 @@ public class DropboxResource {
     }
 
     private String getCredentialsUriOptions() {
-        return "accessToken={{DROPBOX_ACCESS_TOKEN}}" +
+        String options = "accessToken={{DROPBOX_ACCESS_TOKEN}}" +
                 "&clientIdentifier={{DROPBOX_CLIENT_IDENTIFIER}}" +
                 "&refreshToken={{DROPBOX_REFRESH_TOKEN}}" +
                 "&apiKey={{DROPBOX_API_KEY}}" +
                 "&apiSecret={{DROPBOX_API_SECRET}}" +
                 "&expireIn={{DROPBOX_ACCESS_TOKEN_EXPIRES_IN}}";
+        return wireMockUrl.isPresent() ? options + 
"&client=#wireMockDropboxClient" : options;
     }
 }
diff --git 
a/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxIT.java
 
b/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxIT.java
index 5ff45906d9..c147642450 100644
--- 
a/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxIT.java
+++ 
b/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxIT.java
@@ -17,10 +17,7 @@
 package org.apache.camel.quarkus.component.dropbox.it;
 
 import io.quarkus.test.junit.QuarkusIntegrationTest;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
 
-@EnabledIfEnvironmentVariable(named = "DROPBOX_ACCESS_TOKEN", matches = ".+")
-@EnabledIfEnvironmentVariable(named = "DROPBOX_CLIENT_IDENTIFIER", matches = 
".+")
 @QuarkusIntegrationTest
 class DropboxIT extends DropboxTest {
 
diff --git 
a/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxTest.java
 
b/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxTest.java
index 628bfa258b..22883afea7 100644
--- 
a/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxTest.java
+++ 
b/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxTest.java
@@ -16,20 +16,15 @@
  */
 package org.apache.camel.quarkus.component.dropbox.it;
 
+import io.quarkus.test.common.QuarkusTestResource;
 import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
 import io.restassured.http.ContentType;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
 
 import static org.hamcrest.Matchers.is;
 
-@EnabledIfEnvironmentVariable(named = "DROPBOX_ACCESS_TOKEN", matches = ".+")
-@EnabledIfEnvironmentVariable(named = "DROPBOX_ACCESS_TOKEN_EXPIRES_IN", 
matches = ".+")
-@EnabledIfEnvironmentVariable(named = "DROPBOX_API_KEY", matches = ".+")
-@EnabledIfEnvironmentVariable(named = "DROPBOX_API_SECRET", matches = ".+")
-@EnabledIfEnvironmentVariable(named = "DROPBOX_CLIENT_IDENTIFIER", matches = 
".+")
-@EnabledIfEnvironmentVariable(named = "DROPBOX_REFRESH_TOKEN", matches = ".+")
+@QuarkusTestResource(DropboxTestResource.class)
 @QuarkusTest
 class DropboxTest {
 
diff --git 
a/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxTestResource.java
 
b/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxTestResource.java
new file mode 100644
index 0000000000..89a862a26c
--- /dev/null
+++ 
b/integration-tests/dropbox/src/test/java/org/apache/camel/quarkus/component/dropbox/it/DropboxTestResource.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.quarkus.component.dropbox.it;
+
+import java.util.Map;
+
+import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
+import 
org.apache.camel.quarkus.test.wiremock.WireMockTestResourceLifecycleManager;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
+
+/**
+ * Dropbox splits its API across api.dropboxapi.com and 
content.dropboxapi.com, so stubs are defined here
+ * rather than recorded against a single target. The endpoint paths do not 
overlap, so one server serves both.
+ */
+public class DropboxTestResource extends WireMockTestResourceLifecycleManager {
+
+    private static final String[] DROPBOX_ENV_VARS = {
+            "DROPBOX_ACCESS_TOKEN",
+            "DROPBOX_ACCESS_TOKEN_EXPIRES_IN",
+            "DROPBOX_API_KEY",
+            "DROPBOX_API_SECRET",
+            "DROPBOX_CLIENT_IDENTIFIER",
+            "DROPBOX_REFRESH_TOKEN",
+    };
+    private static final String SCENARIO = "dropbox-file";
+    private static final String FILE_DELETED = "file-deleted";
+    private static final String FILE_PATH = DropboxResource.REMOTE_PATH + 
DropboxResource.FILE_NAME;
+    private static final String FILE_METADATA = """
+            {
+              ".tag": "file",
+              "name": "%s",
+              "id": "id:camel-quarkus-test",
+              "client_modified": "2026-01-01T00:00:00Z",
+              "server_modified": "2026-01-01T00:00:00Z",
+              "rev": "0123456789abcdef",
+              "size": %d,
+              "path_lower": "%s",
+              "path_display": "%s",
+              "is_downloadable": true
+            }
+            """.formatted(DropboxResource.FILE_NAME, 
DropboxResource.FILE_CONTENT.length(), FILE_PATH, FILE_PATH);
+
+    @Override
+    public Map<String, String> start() {
+        Map<String, String> properties = super.start();
+        if (server != null) {
+            stubDropboxApi();
+            for (String envVar : DROPBOX_ENV_VARS) {
+                properties.put(envVar, "test-" + envVar.toLowerCase());
+            }
+            properties.put("DROPBOX_ACCESS_TOKEN_EXPIRES_IN", 
String.valueOf(Long.MAX_VALUE));
+        }
+        return properties;
+    }
+
+    private void stubDropboxApi() {
+        // The producer checks whether the remote path exists before uploading
+        server.stubFor(post(urlPathEqualTo("/2/files/get_metadata"))
+                .willReturn(pathError("not_found")));
+
+        server.stubFor(post(urlPathEqualTo("/2/files/upload"))
+                .willReturn(json(FILE_METADATA)));
+
+        // The consumer lists the remote path as a folder before falling back 
to downloading it as a file
+        server.stubFor(post(urlPathEqualTo("/2/files/list_folder"))
+                .inScenario(SCENARIO)
+                .whenScenarioStateIs(STARTED)
+                .willReturn(pathError("not_folder")));
+
+        server.stubFor(post(urlPathEqualTo("/2/files/download"))
+                .inScenario(SCENARIO)
+                .whenScenarioStateIs(STARTED)
+                .willReturn(aResponse()
+                        .withHeader("Content-Type", "application/octet-stream")
+                        .withHeader("Dropbox-API-Result", 
FILE_METADATA.replaceAll("\\s+", ""))
+                        .withBody(DropboxResource.FILE_CONTENT)));
+
+        server.stubFor(post(urlPathEqualTo("/2/files/delete_v2"))
+                .inScenario(SCENARIO)
+                .whenScenarioStateIs(STARTED)
+                .willSetStateTo(FILE_DELETED)
+                .willReturn(json("{\"metadata\":" + FILE_METADATA + "}")));
+
+        server.stubFor(post(urlPathEqualTo("/2/files/list_folder"))
+                .inScenario(SCENARIO)
+                .whenScenarioStateIs(FILE_DELETED)
+                .willReturn(pathError("not_found")));
+
+        server.stubFor(post(urlPathEqualTo("/2/files/download"))
+                .inScenario(SCENARIO)
+                .whenScenarioStateIs(FILE_DELETED)
+                .willReturn(pathError("not_found")));
+    }
+
+    private static ResponseDefinitionBuilder json(String body) {
+        return aResponse()
+                .withHeader("Content-Type", "application/json")
+                .withBody(body);
+    }
+
+    private static ResponseDefinitionBuilder pathError(String tag) {
+        return json("""
+                {"error_summary": "path/%s/", "error": {".tag": "path", 
"path": {".tag": "%s"}}}
+                """.formatted(tag, tag))
+                .withStatus(409);
+    }
+
+    @Override
+    protected String getRecordTargetBaseUrl() {
+        return null;
+    }
+
+    @Override
+    protected boolean isMockingEnabled() {
+        return !envVarsPresent(DROPBOX_ENV_VARS);
+    }
+}

Reply via email to