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);
+ }
+}