From cb71af4e3f961d0fba7897deae6ec4cd64dbc04c Mon Sep 17 00:00:00 2001
From: Artur Ciocanu
Date: Sat, 21 Feb 2026 14:28:06 -0800
Subject: [PATCH 01/11] Migrate additional actor ITs to Testcontainers
Signed-off-by: Artur Ciocanu
---
.../io/dapr/it/actors/ActorExceptionIT.java | 76 +++++++++++------
.../io/dapr/it/actors/ActorMethodNameIT.java | 53 ++++++++----
.../java/io/dapr/it/actors/ActorStateIT.java | 83 ++++++++++---------
.../actors/ActorTurnBasedConcurrencyIT.java | 56 ++++++++-----
...ActorRuntimeRegistrationConfiguration.java | 46 ++++++++++
...ActorRuntimeRegistrationConfiguration.java | 48 +++++++++++
6 files changed, 259 insertions(+), 103 deletions(-)
create mode 100644 sdk-tests/src/test/java/io/dapr/it/actors/MyActorRuntimeRegistrationConfiguration.java
create mode 100644 sdk-tests/src/test/java/io/dapr/it/actors/StatefulActorRuntimeRegistrationConfiguration.java
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/ActorExceptionIT.java b/sdk-tests/src/test/java/io/dapr/it/actors/ActorExceptionIT.java
index 64d0f3ae8b..6beb1c6bd4 100644
--- a/sdk-tests/src/test/java/io/dapr/it/actors/ActorExceptionIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/ActorExceptionIT.java
@@ -14,44 +14,64 @@
package io.dapr.it.actors;
import io.dapr.actors.ActorId;
+import io.dapr.actors.client.ActorClient;
import io.dapr.actors.client.ActorProxyBuilder;
-import io.dapr.it.BaseIT;
-import io.dapr.it.DaprRun;
+import io.dapr.config.Properties;
import io.dapr.it.actors.app.MyActor;
-import io.dapr.it.actors.app.MyActorService;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
+import io.dapr.it.actors.app.TestApplication;
+import io.dapr.it.testcontainers.actors.TestDaprActorsConfiguration;
+import io.dapr.testcontainers.Component;
+import io.dapr.testcontainers.DaprContainer;
+import io.dapr.testcontainers.DaprLogLevel;
+import io.dapr.testcontainers.internal.DaprContainerFactory;
+import io.dapr.testcontainers.internal.DaprSidecarContainer;
+import io.dapr.testcontainers.internal.spring.DaprSpringBootTest;
+import io.dapr.testcontainers.wait.strategy.DaprWait;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import java.util.Map;
import static io.dapr.it.Retry.callWithRetry;
import static io.dapr.it.TestUtils.assertThrowsDaprExceptionSubstring;
+@DaprSpringBootTest(classes = {
+ TestApplication.class,
+ TestDaprActorsConfiguration.class,
+ MyActorRuntimeRegistrationConfiguration.class
+})
+public class ActorExceptionIT {
-public class ActorExceptionIT extends BaseIT {
+ private static final Logger logger = LoggerFactory.getLogger(ActorExceptionIT.class);
- private static Logger logger = LoggerFactory.getLogger(ActorExceptionIT.class);
+ @DaprSidecarContainer
+ private static final DaprContainer DAPR_CONTAINER = DaprContainerFactory.createForSpringBootTest("actor-exception-it")
+ .withComponent(new Component("kvstore", "state.in-memory", "v1", Map.of("actorStateStore", "true")))
+ .withDaprLogLevel(DaprLogLevel.DEBUG)
+ .withLogConsumer(outputFrame -> logger.info(outputFrame.getUtf8String()));
- private static DaprRun run;
+ @Autowired
+ private ActorClient actorClient;
- @BeforeAll
- public static void start() throws Exception {
- // The call below will fail if service cannot start successfully.
- run = startDaprApp(
- ActorExceptionIT.class.getSimpleName(),
- MyActorService.SUCCESS_MESSAGE,
- MyActorService.class,
- true,
- 60000);
+ @BeforeEach
+ public void setUp() {
+ org.testcontainers.Testcontainers.exposeHostPorts(DAPR_CONTAINER.getAppPort());
+ DaprWait.forActors().waitUntilReady(DAPR_CONTAINER);
+ }
+
+ private ActorClient newActorClient(Map metadata) {
+ return new ActorClient(new Properties(Map.of(
+ "dapr.http.endpoint", "http://127.0.0.1:" + DAPR_CONTAINER.getHttpPort(),
+ "dapr.grpc.endpoint", "127.0.0.1:" + DAPR_CONTAINER.getGrpcPort())), metadata, null);
}
@Test
public void exceptionTest() throws Exception {
ActorProxyBuilder proxyBuilder =
- new ActorProxyBuilder("MyActorTest", MyActor.class, deferClose(run.newActorClient()));
+ new ActorProxyBuilder<>("MyActorTest", MyActor.class, actorClient);
MyActor proxy = proxyBuilder.build(new ActorId("1"));
callWithRetry(() -> {
@@ -66,15 +86,17 @@ public void exceptionTest() throws Exception {
public void exceptionDueToMetadataTest() throws Exception {
// Setting this HTTP header via actor metadata will cause the Actor HTTP server to error.
Map metadata = Map.of("Content-Length", "9999");
- ActorProxyBuilder proxyBuilderMetadataOverride =
- new ActorProxyBuilder("MyActorTest", MyActor.class, deferClose(run.newActorClient(metadata)));
+ try (ActorClient actorClientWithMetadata = newActorClient(metadata)) {
+ ActorProxyBuilder proxyBuilderMetadataOverride =
+ new ActorProxyBuilder<>("MyActorTest", MyActor.class, actorClientWithMetadata);
- MyActor proxyWithMetadata = proxyBuilderMetadataOverride.build(new ActorId("2"));
- callWithRetry(() -> {
- assertThrowsDaprExceptionSubstring(
- "INTERNAL",
- "ContentLength=9999 with Body length 13",
- () -> proxyWithMetadata.say("hello world"));
- }, 10000);
+ MyActor proxyWithMetadata = proxyBuilderMetadataOverride.build(new ActorId("2"));
+ callWithRetry(() -> {
+ assertThrowsDaprExceptionSubstring(
+ "INTERNAL",
+ "ContentLength=9999 with Body length 13",
+ () -> proxyWithMetadata.say("hello world"));
+ }, 10000);
+ }
}
}
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/ActorMethodNameIT.java b/sdk-tests/src/test/java/io/dapr/it/actors/ActorMethodNameIT.java
index bf9a2eb749..4142ec4745 100644
--- a/sdk-tests/src/test/java/io/dapr/it/actors/ActorMethodNameIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/ActorMethodNameIT.java
@@ -14,35 +14,59 @@
package io.dapr.it.actors;
import io.dapr.actors.ActorId;
+import io.dapr.actors.client.ActorClient;
import io.dapr.actors.client.ActorProxy;
import io.dapr.actors.client.ActorProxyBuilder;
-import io.dapr.it.BaseIT;
import io.dapr.it.actors.app.MyActor;
-import io.dapr.it.actors.app.MyActorService;
+import io.dapr.it.actors.app.TestApplication;
+import io.dapr.it.testcontainers.actors.TestDaprActorsConfiguration;
+import io.dapr.testcontainers.Component;
+import io.dapr.testcontainers.DaprContainer;
+import io.dapr.testcontainers.DaprLogLevel;
+import io.dapr.testcontainers.internal.DaprContainerFactory;
+import io.dapr.testcontainers.internal.DaprSidecarContainer;
+import io.dapr.testcontainers.internal.spring.DaprSpringBootTest;
+import io.dapr.testcontainers.wait.strategy.DaprWait;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.Map;
import static io.dapr.it.Retry.callWithRetry;
import static org.junit.jupiter.api.Assertions.assertTrue;
-public class ActorMethodNameIT extends BaseIT {
+@DaprSpringBootTest(classes = {
+ TestApplication.class,
+ TestDaprActorsConfiguration.class,
+ MyActorRuntimeRegistrationConfiguration.class
+})
+public class ActorMethodNameIT {
+
+ private static final Logger logger = LoggerFactory.getLogger(ActorMethodNameIT.class);
- private static Logger logger = LoggerFactory.getLogger(ActorMethodNameIT.class);
+ @DaprSidecarContainer
+ private static final DaprContainer DAPR_CONTAINER = DaprContainerFactory.createForSpringBootTest("actor-method-name-it")
+ .withComponent(new Component("kvstore", "state.in-memory", "v1", Map.of("actorStateStore", "true")))
+ .withDaprLogLevel(DaprLogLevel.DEBUG)
+ .withLogConsumer(outputFrame -> logger.info(outputFrame.getUtf8String()));
+
+ @Autowired
+ private ActorClient actorClient;
+
+ @BeforeEach
+ void setUp() {
+ org.testcontainers.Testcontainers.exposeHostPorts(DAPR_CONTAINER.getAppPort());
+ DaprWait.forActors().waitUntilReady(DAPR_CONTAINER);
+ }
@Test
public void actorMethodNameChange() throws Exception {
- // The call below will fail if service cannot start successfully.
- var run = startDaprApp(
- ActorMethodNameIT.class.getSimpleName(),
- MyActorService.SUCCESS_MESSAGE,
- MyActorService.class,
- true,
- 60000);
-
logger.debug("Creating proxy builder");
ActorProxyBuilder proxyBuilder =
- new ActorProxyBuilder("MyActorTest", MyActor.class, deferClose(run.newActorClient()));
+ new ActorProxyBuilder<>("MyActorTest", MyActor.class, actorClient);
logger.debug("Creating actorId");
ActorId actorId1 = new ActorId("1");
logger.debug("Building proxy");
@@ -57,7 +81,7 @@ public void actorMethodNameChange() throws Exception {
logger.debug("Creating proxy builder 2");
ActorProxyBuilder proxyBuilder2 =
- new ActorProxyBuilder("MyActorTest", ActorProxy.class, deferClose(run.newActorClient()));
+ new ActorProxyBuilder<>("MyActorTest", ActorProxy.class, actorClient);
logger.debug("Building proxy 2");
ActorProxy proxy2 = proxyBuilder2.build(actorId1);
@@ -67,6 +91,5 @@ public void actorMethodNameChange() throws Exception {
logger.debug("asserting true response 2: [" + response + "]");
assertTrue(response);
}, 60000);
-
}
}
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/ActorStateIT.java b/sdk-tests/src/test/java/io/dapr/it/actors/ActorStateIT.java
index ffd5d3c3dd..8be3acb784 100644
--- a/sdk-tests/src/test/java/io/dapr/it/actors/ActorStateIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/ActorStateIT.java
@@ -14,36 +14,58 @@
package io.dapr.it.actors;
import io.dapr.actors.ActorId;
+import io.dapr.actors.client.ActorClient;
import io.dapr.actors.client.ActorProxy;
import io.dapr.actors.client.ActorProxyBuilder;
-import io.dapr.it.BaseIT;
-import io.dapr.it.DaprRun;
+import io.dapr.it.actors.services.springboot.DaprApplication;
import io.dapr.it.actors.services.springboot.StatefulActor;
-import io.dapr.it.actors.services.springboot.StatefulActorService;
+import io.dapr.it.testcontainers.actors.TestDaprActorsConfiguration;
+import io.dapr.testcontainers.Component;
+import io.dapr.testcontainers.DaprContainer;
+import io.dapr.testcontainers.DaprLogLevel;
+import io.dapr.testcontainers.internal.DaprContainerFactory;
+import io.dapr.testcontainers.internal.DaprSidecarContainer;
+import io.dapr.testcontainers.internal.spring.DaprSpringBootTest;
+import io.dapr.testcontainers.wait.strategy.DaprWait;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.Map;
import static io.dapr.it.Retry.callWithRetry;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
-public class ActorStateIT extends BaseIT {
+@DaprSpringBootTest(classes = {
+ DaprApplication.class,
+ TestDaprActorsConfiguration.class,
+ StatefulActorRuntimeRegistrationConfiguration.class
+})
+public class ActorStateIT {
+
+ private static final Logger logger = LoggerFactory.getLogger(ActorStateIT.class);
- private static Logger logger = LoggerFactory.getLogger(ActorStateIT.class);
+ @DaprSidecarContainer
+ private static final DaprContainer DAPR_CONTAINER = DaprContainerFactory.createForSpringBootTest("actor-state-it")
+ .withComponent(new Component("kvstore", "state.in-memory", "v1", Map.of("actorStateStore", "true")))
+ .withDaprLogLevel(DaprLogLevel.DEBUG)
+ .withLogConsumer(outputFrame -> logger.info(outputFrame.getUtf8String()));
+
+ @Autowired
+ private ActorClient actorClient;
+
+ @BeforeEach
+ void setUp() {
+ org.testcontainers.Testcontainers.exposeHostPorts(DAPR_CONTAINER.getAppPort());
+ DaprWait.forActorType("StatefulActorTest").waitUntilReady(DAPR_CONTAINER);
+ }
@Test
public void writeReadState() throws Exception {
- logger.debug("Starting actor runtime ...");
- // The call below will fail if service cannot start successfully.
- DaprRun run = startDaprApp(
- this.getClass().getSimpleName(),
- StatefulActorService.SUCCESS_MESSAGE,
- StatefulActorService.class,
- true,
- 60000);
-
String message = "This is a message to be saved and retrieved.";
String name = "Jon Doe";
byte[] bytes = new byte[] { 0x1 };
@@ -52,12 +74,9 @@ public void writeReadState() throws Exception {
String actorType = "StatefulActorTest";
logger.debug("Building proxy ...");
ActorProxyBuilder proxyBuilder =
- new ActorProxyBuilder(actorType, ActorProxy.class, deferClose(run.newActorClient()));
+ new ActorProxyBuilder(actorType, ActorProxy.class, actorClient);
ActorProxy proxy = proxyBuilder.build(actorId);
- // waiting for actor to be activated
- Thread.sleep(5000);
-
// Validate conditional read works.
callWithRetry(() -> {
logger.debug("Invoking readMessage where data is not present yet ... ");
@@ -123,49 +142,31 @@ public void writeReadState() throws Exception {
assertArrayEquals(bytes, result);
}, 5000);
- logger.debug("Waiting, so actor can be deactivated ...");
+ // Wait for actor idle timeout + scan interval so cached actor gets deactivated.
Thread.sleep(10000);
- logger.debug("Stopping service ...");
- run.stop();
-
- logger.debug("Starting service ...");
- DaprRun run2 = startDaprApp(
- this.getClass().getSimpleName(),
- StatefulActorService.SUCCESS_MESSAGE,
- StatefulActorService.class,
- true,
- 60000);
-
- // Need new proxy builder because the proxy builder holds the channel.
- proxyBuilder = new ActorProxyBuilder(actorType, ActorProxy.class, deferClose(run2.newActorClient()));
- ActorProxy newProxy = proxyBuilder.build(actorId);
-
- // waiting for actor to be activated
- Thread.sleep(2000);
-
callWithRetry(() -> {
logger.debug("Invoking readMessage where data is not cached ... ");
- String result = newProxy.invokeMethod("readMessage", String.class).block();
+ String result = proxy.invokeMethod("readMessage", String.class).block();
assertEquals(message, result);
}, 5000);
callWithRetry(() -> {
logger.debug("Invoking readData where data is not cached ... ");
- StatefulActor.MyData result = newProxy.invokeMethod("readData", StatefulActor.MyData.class).block();
+ StatefulActor.MyData result = proxy.invokeMethod("readData", StatefulActor.MyData.class).block();
assertEquals(mydata.value, result.value);
}, 5000);
logger.debug("Finished testing actor string state.");
callWithRetry(() -> {
logger.debug("Invoking readName where empty content is not cached ... ");
- String result = newProxy.invokeMethod("readName", String.class).block();
+ String result = proxy.invokeMethod("readName", String.class).block();
assertEquals("", result);
}, 5000);
callWithRetry(() -> {
logger.debug("Invoking readBytes where content is not cached ... ");
- byte[] result = newProxy.invokeMethod("readBytes", byte[].class).block();
+ byte[] result = proxy.invokeMethod("readBytes", byte[].class).block();
assertArrayEquals(bytes, result);
}, 5000);
}
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/ActorTurnBasedConcurrencyIT.java b/sdk-tests/src/test/java/io/dapr/it/actors/ActorTurnBasedConcurrencyIT.java
index dd021d98b9..3fd200709a 100644
--- a/sdk-tests/src/test/java/io/dapr/it/actors/ActorTurnBasedConcurrencyIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/ActorTurnBasedConcurrencyIT.java
@@ -14,23 +14,33 @@
package io.dapr.it.actors;
import io.dapr.actors.ActorId;
+import io.dapr.actors.client.ActorClient;
import io.dapr.actors.client.ActorProxy;
import io.dapr.actors.client.ActorProxyBuilder;
import io.dapr.actors.runtime.DaprClientHttpUtils;
-import io.dapr.config.Properties;
-import io.dapr.it.BaseIT;
-import io.dapr.it.actors.app.MyActorService;
+import io.dapr.it.actors.app.TestApplication;
+import io.dapr.it.testcontainers.actors.TestDaprActorsConfiguration;
+import io.dapr.testcontainers.Component;
+import io.dapr.testcontainers.DaprContainer;
+import io.dapr.testcontainers.DaprLogLevel;
+import io.dapr.testcontainers.internal.DaprContainerFactory;
+import io.dapr.testcontainers.internal.DaprSidecarContainer;
+import io.dapr.testcontainers.internal.spring.DaprSpringBootTest;
+import io.dapr.testcontainers.wait.strategy.DaprWait;
import io.dapr.utils.Version;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
@@ -39,10 +49,21 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-public class ActorTurnBasedConcurrencyIT extends BaseIT {
+@DaprSpringBootTest(classes = {
+ TestApplication.class,
+ TestDaprActorsConfiguration.class,
+ MyActorRuntimeRegistrationConfiguration.class
+})
+public class ActorTurnBasedConcurrencyIT {
private static final Logger logger = LoggerFactory.getLogger(ActorTurnBasedConcurrencyIT.class);
+ @DaprSidecarContainer
+ private static final DaprContainer DAPR_CONTAINER = DaprContainerFactory.createForSpringBootTest("actor-turn-based-concurrency-it")
+ .withComponent(new Component("kvstore", "state.in-memory", "v1", Map.of("actorStateStore", "true")))
+ .withDaprLogLevel(DaprLogLevel.DEBUG)
+ .withLogConsumer(outputFrame -> logger.info(outputFrame.getUtf8String()));
+
private static final String TIMER_METHOD_NAME = "clock";
private static final String REMINDER_METHOD_NAME = "receiveReminder";
@@ -53,6 +74,15 @@ public class ActorTurnBasedConcurrencyIT extends BaseIT {
private static final String ACTOR_ID = "1";
+ @Autowired
+ private ActorClient actorClient;
+
+ @BeforeEach
+ public void setUp() {
+ org.testcontainers.Testcontainers.exposeHostPorts(DAPR_CONTAINER.getAppPort());
+ DaprWait.forActorType(ACTOR_TYPE).waitUntilReady(DAPR_CONTAINER);
+ }
+
@AfterEach
public void cleanUpTestCase() {
// Delete the reminder in case the test failed, otherwise it may interfere with future tests since it is persisted.
@@ -79,20 +109,11 @@ public void cleanUpTestCase() {
@Test
public void invokeOneActorMethodReminderAndTimer() throws Exception {
System.out.println("Starting test 'actorTest1'");
-
- var run = startDaprApp(
- ActorTurnBasedConcurrencyIT.class.getSimpleName(),
- MyActorService.SUCCESS_MESSAGE,
- MyActorService.class,
- true,
- 60000);
-
- Thread.sleep(5000);
String actorType="MyActorTest";
logger.debug("Creating proxy builder");
ActorProxyBuilder proxyBuilder =
- new ActorProxyBuilder(actorType, ActorProxy.class, deferClose(run.newActorClient()));
+ new ActorProxyBuilder(actorType, ActorProxy.class, actorClient);
logger.debug("Creating actorId");
ActorId actorId1 = new ActorId(ACTOR_ID);
logger.debug("Building proxy");
@@ -230,12 +251,7 @@ void validateEventNotObserved(List logs, String startingPoin
}
private static ManagedChannel buildManagedChannel() {
- int port = Properties.GRPC_PORT.get();
- if (port <= 0) {
- throw new IllegalStateException("Invalid port.");
- }
-
- return ManagedChannelBuilder.forAddress(Properties.SIDECAR_IP.get(), port)
+ return ManagedChannelBuilder.forAddress("127.0.0.1", DAPR_CONTAINER.getGrpcPort())
.usePlaintext()
.userAgent(Version.getSdkVersion())
.build();
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/MyActorRuntimeRegistrationConfiguration.java b/sdk-tests/src/test/java/io/dapr/it/actors/MyActorRuntimeRegistrationConfiguration.java
new file mode 100644
index 0000000000..d74a3edbe4
--- /dev/null
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/MyActorRuntimeRegistrationConfiguration.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2026 The Dapr Authors
+ * Licensed 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 io.dapr.it.actors;
+
+import io.dapr.actors.runtime.ActorRuntime;
+import io.dapr.it.actors.app.MyActorBinaryImpl;
+import io.dapr.it.actors.app.MyActorObjectImpl;
+import io.dapr.it.actors.app.MyActorStringImpl;
+import jakarta.annotation.PostConstruct;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MyActorRuntimeRegistrationConfiguration {
+
+ @Bean
+ public MyActorRuntimeRegistrar myActorRuntimeRegistrar(ActorRuntime actorRuntime) {
+ return new MyActorRuntimeRegistrar(actorRuntime);
+ }
+
+ static final class MyActorRuntimeRegistrar {
+ private final ActorRuntime actorRuntime;
+
+ private MyActorRuntimeRegistrar(ActorRuntime actorRuntime) {
+ this.actorRuntime = actorRuntime;
+ }
+
+ @PostConstruct
+ void registerActors() {
+ actorRuntime.registerActor(MyActorStringImpl.class);
+ actorRuntime.registerActor(MyActorBinaryImpl.class);
+ actorRuntime.registerActor(MyActorObjectImpl.class);
+ }
+ }
+}
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/StatefulActorRuntimeRegistrationConfiguration.java b/sdk-tests/src/test/java/io/dapr/it/actors/StatefulActorRuntimeRegistrationConfiguration.java
new file mode 100644
index 0000000000..417d2b9094
--- /dev/null
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/StatefulActorRuntimeRegistrationConfiguration.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2026 The Dapr Authors
+ * Licensed 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 io.dapr.it.actors;
+
+import io.dapr.actors.runtime.ActorRuntime;
+import io.dapr.it.actors.services.springboot.StatefulActorImpl;
+import jakarta.annotation.PostConstruct;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.time.Duration;
+
+@Configuration
+public class StatefulActorRuntimeRegistrationConfiguration {
+
+ @Bean
+ public StatefulActorRuntimeRegistrar statefulActorRuntimeRegistrar(ActorRuntime actorRuntime) {
+ return new StatefulActorRuntimeRegistrar(actorRuntime);
+ }
+
+ static final class StatefulActorRuntimeRegistrar {
+ private final ActorRuntime actorRuntime;
+
+ private StatefulActorRuntimeRegistrar(ActorRuntime actorRuntime) {
+ this.actorRuntime = actorRuntime;
+ }
+
+ @PostConstruct
+ void registerActors() {
+ actorRuntime.getConfig().setActorIdleTimeout(Duration.ofSeconds(5));
+ actorRuntime.getConfig().setActorScanInterval(Duration.ofSeconds(2));
+ actorRuntime.getConfig().setDrainOngoingCallTimeout(Duration.ofSeconds(10));
+ actorRuntime.getConfig().setDrainBalancedActors(true);
+ actorRuntime.registerActor(StatefulActorImpl.class);
+ }
+ }
+}
From 5aaea5421f416235e48414e09797fd1eaa847365 Mon Sep 17 00:00:00 2001
From: Artur Ciocanu
Date: Sat, 21 Feb 2026 14:28:45 -0800
Subject: [PATCH 02/11] Checkpoint migrated integration test code changes
Signed-off-by: Artur Ciocanu
---
.../it/actors/ActivationDeactivationIT.java | 58 +-
.../src/test/java/io/dapr/it/api/ApiIT.java | 32 +-
.../io/dapr/it/binding/http/BindingIT.java | 236 +++---
.../binding/http/InputBindingController.java | 15 +
.../configuration/ConfigurationClientIT.java | 330 +++++----
.../it/methodinvoke/grpc/MethodInvokeIT.java | 211 +++---
.../it/methodinvoke/http/MethodInvokeIT.java | 246 +++----
.../java/io/dapr/it/pubsub/http/PubSubIT.java | 683 +-----------------
.../it/pubsub/http/SubscriberController.java | 10 +
.../dapr/it/pubsub/stream/PubSubStreamIT.java | 172 ++---
.../dapr/it/resiliency/WaitForSidecarIT.java | 89 ++-
.../io/dapr/it/secrets/SecretsClientIT.java | 135 ++--
.../dapr/it/state/AbstractStateClientIT.java | 7 +-
.../io/dapr/it/state/GRPCStateClientIT.java | 51 +-
.../io/dapr/it/state/HelloWorldClientIT.java | 58 +-
.../pubsub/http/DaprPubSubIT.java | 42 +-
.../pubsub/http/SubscriberController.java | 10 +
.../io/dapr/it/tracing/grpc/TracingIT.java | 95 ++-
.../io/dapr/it/tracing/http/TracingIT.java | 79 +-
19 files changed, 1046 insertions(+), 1513 deletions(-)
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/ActivationDeactivationIT.java b/sdk-tests/src/test/java/io/dapr/it/actors/ActivationDeactivationIT.java
index 369d02945e..d856a6ce4a 100644
--- a/sdk-tests/src/test/java/io/dapr/it/actors/ActivationDeactivationIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/ActivationDeactivationIT.java
@@ -14,15 +14,29 @@
package io.dapr.it.actors;
import io.dapr.actors.ActorId;
+import io.dapr.actors.client.ActorClient;
import io.dapr.actors.client.ActorProxyBuilder;
-import io.dapr.it.BaseIT;
import io.dapr.it.actors.services.springboot.DemoActor;
-import io.dapr.it.actors.services.springboot.DemoActorService;
+import io.dapr.actors.runtime.ActorRuntime;
+import io.dapr.it.actors.services.springboot.DaprApplication;
+import io.dapr.it.actors.services.springboot.DemoActorImpl;
+import io.dapr.it.testcontainers.actors.TestDaprActorsConfiguration;
+import io.dapr.testcontainers.Component;
+import io.dapr.testcontainers.DaprContainer;
+import io.dapr.testcontainers.DaprLogLevel;
+import io.dapr.testcontainers.internal.DaprContainerFactory;
+import io.dapr.testcontainers.internal.DaprSidecarContainer;
+import io.dapr.testcontainers.internal.spring.DaprSpringBootTest;
+import io.dapr.testcontainers.wait.strategy.DaprWait;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import java.time.Duration;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import static io.dapr.it.Retry.callWithRetry;
@@ -30,24 +44,40 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-public class ActivationDeactivationIT extends BaseIT {
+@DaprSpringBootTest(classes = {DaprApplication.class, TestDaprActorsConfiguration.class})
+public class ActivationDeactivationIT {
- private static Logger logger = LoggerFactory.getLogger(ActivationDeactivationIT.class);
+ private static final Logger logger = LoggerFactory.getLogger(ActivationDeactivationIT.class);
+
+ @DaprSidecarContainer
+ private static final DaprContainer DAPR_CONTAINER = DaprContainerFactory.createForSpringBootTest("activation-deactivation-it")
+ .withComponent(new Component("kvstore", "state.in-memory", "v1", Map.of("actorStateStore", "true")))
+ .withDaprLogLevel(DaprLogLevel.DEBUG)
+ .withLogConsumer(outputFrame -> logger.info(outputFrame.getUtf8String()));
+
+ @Autowired
+ private ActorClient actorClient;
+
+ @Autowired
+ private ActorRuntime actorRuntime;
+
+ @BeforeEach
+ void setUp() {
+ DemoActorImpl.ACTIVE_ACTOR.clear();
+ actorRuntime.getConfig().setActorIdleTimeout(Duration.ofSeconds(5));
+ actorRuntime.getConfig().setActorScanInterval(Duration.ofSeconds(2));
+ actorRuntime.getConfig().setDrainOngoingCallTimeout(Duration.ofSeconds(10));
+ actorRuntime.getConfig().setDrainBalancedActors(true);
+ actorRuntime.registerActor(DemoActorImpl.class);
+ org.testcontainers.Testcontainers.exposeHostPorts(DAPR_CONTAINER.getAppPort());
+ DaprWait.forActors().waitUntilReady(DAPR_CONTAINER);
+ }
@Test
public void activateInvokeDeactivate() throws Exception {
- // The call below will fail if service cannot start successfully.
- var run = startDaprApp(
- ActivationDeactivationIT.class.getSimpleName(),
- DemoActorService.SUCCESS_MESSAGE,
- DemoActorService.class,
- true,
- 60000);
-
final AtomicInteger atomicInteger = new AtomicInteger(1);
logger.debug("Creating proxy builder");
- ActorProxyBuilder proxyBuilder
- = new ActorProxyBuilder(DemoActor.class, deferClose(run.newActorClient()));
+ ActorProxyBuilder proxyBuilder = new ActorProxyBuilder<>(DemoActor.class, actorClient);
logger.debug("Creating actorId");
ActorId actorId1 = new ActorId(Integer.toString(atomicInteger.getAndIncrement()));
logger.debug("Building proxy");
diff --git a/sdk-tests/src/test/java/io/dapr/it/api/ApiIT.java b/sdk-tests/src/test/java/io/dapr/it/api/ApiIT.java
index 8b37c5ad34..48ec7b1187 100644
--- a/sdk-tests/src/test/java/io/dapr/it/api/ApiIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/api/ApiIT.java
@@ -1,30 +1,42 @@
package io.dapr.it.api;
import io.dapr.client.DaprClient;
-import io.dapr.client.DaprClientBuilder;
-import io.dapr.it.BaseIT;
-import io.dapr.it.DaprRun;
+import io.dapr.it.testcontainers.DaprClientFactory;
+import io.dapr.testcontainers.DaprContainer;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Tag;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ApiIT extends BaseIT {
+import static io.dapr.it.testcontainers.ContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+@Testcontainers
+@Tag("testcontainers")
+public class ApiIT {
private static final Logger logger = LoggerFactory.getLogger(ApiIT.class);
private static final int DEFAULT_TIMEOUT = 60000;
+ @Container
+ private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
+ .withAppName("api-it");
+
@Test
public void testShutdownAPI() throws Exception {
- DaprRun run = startDaprApp(this.getClass().getSimpleName(), DEFAULT_TIMEOUT);
-
- // TODO(artursouza): change this to wait for the sidecar to be healthy (new method needed in DaprClient).
- Thread.sleep(3000);
- try (DaprClient client = run.newDaprClientBuilder().build()) {
+ try (DaprClient client = DaprClientFactory.createDaprClientBuilder(DAPR_CONTAINER).build()) {
+ client.waitForSidecar(10000).block();
logger.info("Sending shutdown request.");
client.shutdown().block();
logger.info("Ensuring dapr has stopped.");
- run.checkRunState(DEFAULT_TIMEOUT, false);
+ long start = System.currentTimeMillis();
+ while (DAPR_CONTAINER.isRunning() && System.currentTimeMillis() - start < DEFAULT_TIMEOUT) {
+ Thread.sleep(100);
+ }
+ assertFalse(DAPR_CONTAINER.isRunning(), "Dapr sidecar is expected to stop after shutdown API");
}
}
}
diff --git a/sdk-tests/src/test/java/io/dapr/it/binding/http/BindingIT.java b/sdk-tests/src/test/java/io/dapr/it/binding/http/BindingIT.java
index 2bdf7bb3ca..1a13f5a4de 100644
--- a/sdk-tests/src/test/java/io/dapr/it/binding/http/BindingIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/binding/http/BindingIT.java
@@ -15,14 +15,19 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.client.DaprClient;
-import io.dapr.client.DaprClientBuilder;
import io.dapr.client.domain.HttpExtension;
import io.dapr.exceptions.DaprException;
-import io.dapr.it.BaseIT;
-import io.dapr.it.DaprRun;
+import io.dapr.it.testcontainers.DaprClientFactory;
+import io.dapr.testcontainers.Component;
+import io.dapr.testcontainers.DaprContainer;
+import io.dapr.testcontainers.internal.DaprContainerFactory;
+import io.dapr.testcontainers.internal.DaprSidecarContainer;
+import io.dapr.testcontainers.internal.spring.DaprSpringBootTest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -34,134 +39,129 @@
/**
* Service for input and output binding example.
*/
-public class BindingIT extends BaseIT {
+@DaprSpringBootTest(classes = InputBindingService.class)
+@Tag("testcontainers")
+public class BindingIT {
+
+ private static final String APP_ID = "binding-http-it";
+ private static final String BINDING_NAME = "sample123";
+
+ @DaprSidecarContainer
+ private static final DaprContainer DAPR_CONTAINER = createDaprContainer();
+
+ private static DaprContainer createDaprContainer() {
+ DaprContainer container = DaprContainerFactory.createForSpringBootTest(APP_ID);
+ String appUrl = "http://host.testcontainers.internal:" + container.getAppPort();
+ return container
+ .withComponent(new Component(
+ "github-http-binding-404",
+ "bindings.http",
+ "v1",
+ Map.of("url", appUrl + "/github404")))
+ .withComponent(new Component(
+ "github-http-binding-404-success",
+ "bindings.http",
+ "v1",
+ Map.of(
+ "url", appUrl + "/github404",
+ "errorIfNot2XX", "false")))
+ .withComponent(new Component(
+ BINDING_NAME,
+ "bindings.http",
+ "v1",
+ Map.of("url", appUrl + "/" + BINDING_NAME)));
+ }
+
+ private DaprClient daprClient;
+
+ @BeforeEach
+ public void setup() {
+ org.testcontainers.Testcontainers.exposeHostPorts(DAPR_CONTAINER.getAppPort());
+ daprClient = DaprClientFactory.createDaprClientBuilder(DAPR_CONTAINER).build();
+ daprClient.waitForSidecar(10000).block();
+ daprClient.invokeMethod(APP_ID, "messages/clear", "", HttpExtension.POST).block();
+ }
+
+ @AfterEach
+ public void closeClient() throws Exception {
+ daprClient.close();
+ }
@Test
public void httpOutputBindingError() throws Exception {
- var run = startDaprApp(
- this.getClass().getSimpleName() + "-httpoutputbinding-exception",
- 60000);
- try(DaprClient client = run.newDaprClientBuilder().build()) {
- // Validate error message
- callWithRetry(() -> {
- System.out.println("Checking exception handling for output binding ...");
- try {
- client.invokeBinding("github-http-binding-404", "get", "").block();
- fail("Should throw an exception");
- } catch (DaprException e) {
- assertEquals(404, e.getHttpStatusCode());
- // This HTTP binding did not set `errorIfNot2XX` to false in component metadata, so the error payload is not
- // consistent between HTTP and gRPC.
- assertTrue(new String(e.getPayload()).contains(
- "error invoking output binding github-http-binding-404: received status code 404"));
- }
- }, 10000);
- }
+ callWithRetry(() -> {
+ System.out.println("Checking exception handling for output binding ...");
+ try {
+ daprClient.invokeBinding("github-http-binding-404", "get", "").block();
+ fail("Should throw an exception");
+ } catch (DaprException e) {
+ assertEquals(404, e.getHttpStatusCode());
+ assertTrue(new String(e.getPayload()).contains("received status code 404"));
+ }
+ }, 10000);
}
@Test
public void httpOutputBindingErrorIgnoredByComponent() throws Exception {
- var run = startDaprApp(
- this.getClass().getSimpleName() + "-httpoutputbinding-ignore-error",
- 60000);
- try(DaprClient client = run.newDaprClientBuilder().build()) {
- // Validate error message
- callWithRetry(() -> {
- System.out.println("Checking exception handling for output binding ...");
- try {
- client.invokeBinding("github-http-binding-404-success", "get", "").block();
- fail("Should throw an exception");
- } catch (DaprException e) {
- assertEquals(404, e.getHttpStatusCode());
- // The HTTP binding must set `errorIfNot2XX` to false in component metadata for the error payload to be
- // consistent between HTTP and gRPC.
- assertTrue(new String(e.getPayload()).contains("message"));
- assertTrue(new String(e.getPayload()).contains("Not Found"));
- assertTrue(new String(e.getPayload()).contains("documentation_url"));
- assertTrue(new String(e.getPayload()).contains("https://docs.github.com/rest"));
- }
- }, 10000);
- }
+ callWithRetry(() -> {
+ System.out.println("Checking exception handling for output binding ...");
+ try {
+ daprClient.invokeBinding("github-http-binding-404-success", "get", "").block();
+ fail("Should throw an exception");
+ } catch (DaprException e) {
+ assertEquals(404, e.getHttpStatusCode());
+ assertTrue(new String(e.getPayload()).contains("message"));
+ assertTrue(new String(e.getPayload()).contains("Not Found"));
+ assertTrue(new String(e.getPayload()).contains("documentation_url"));
+ assertTrue(new String(e.getPayload()).contains("https://docs.github.com/rest"));
+ }
+ }, 10000);
}
@Test
public void inputOutputBinding() throws Exception {
- DaprRun daprRun = startDaprApp(
- this.getClass().getSimpleName() + "-grpc",
- InputBindingService.SUCCESS_MESSAGE,
- InputBindingService.class,
- true,
- 60000);
-
- var bidingName = "sample123";
-
- try(DaprClient client = daprRun.newDaprClientBuilder().build()) {
- callWithRetry(() -> {
- System.out.println("Checking if input binding is up before publishing events ...");
- client.invokeBinding(
- bidingName, "create", "ping").block();
-
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new RuntimeException(e);
- }
-
- client.invokeMethod(daprRun.getAppName(), "initialized", "", HttpExtension.GET).block();
- }, 120000);
-
- // This is an example of sending data in a user-defined object. The input binding will receive:
- // {"message":"hello"}
- MyClass myClass = new MyClass();
- myClass.message = "hello";
-
- System.out.println("sending first message");
- client.invokeBinding(
- bidingName, "create", myClass, Map.of("MyMetadata", "MyValue"), Void.class).block();
-
- // This is an example of sending a plain string. The input binding will receive
- // cat
- final String m = "cat";
- System.out.println("sending " + m);
- client.invokeBinding(
- bidingName, "create", m, Map.of("MyMetadata", "MyValue"), Void.class).block();
-
- // Metadata is not used by Kafka component, so it is not possible to validate.
- callWithRetry(() -> {
- System.out.println("Checking results ...");
- final List messages =
- client.invokeMethod(
- daprRun.getAppName(),
- "messages",
- null,
- HttpExtension.GET,
- List.class).block();
- assertEquals(2, messages.size());
-
- MyClass resultClass = null;
- try {
- resultClass = new ObjectMapper().readValue(messages.get(0), MyClass.class);
- } catch (Exception ex) {
- ex.printStackTrace();
- fail("Error on decode message 1");
- }
-
- try {
- assertEquals("cat", new ObjectMapper().readValue(messages.get(1), String.class));
- } catch (Exception ex) {
- ex.printStackTrace();
- fail("Error on decode message 2");
- }
- assertEquals("hello", resultClass.message);
- }, 8000);
- }
+ callWithRetry(() -> {
+ System.out.println("Checking if input binding is up before publishing events ...");
+ daprClient.invokeBinding(BINDING_NAME, "create", "ping").block();
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+
+ daprClient.invokeMethod(APP_ID, "initialized", "", HttpExtension.GET).block();
+ }, 120000);
+
+ MyClass myClass = new MyClass();
+ myClass.message = "hello";
+
+ daprClient.invokeBinding(BINDING_NAME, "create", myClass, Map.of("MyMetadata", "MyValue"), Void.class).block();
+ final String m = "cat";
+ daprClient.invokeBinding(BINDING_NAME, "create", m, Map.of("MyMetadata", "MyValue"), Void.class).block();
+
+ callWithRetry(() -> {
+ final List messages = daprClient.invokeMethod(APP_ID, "messages", null, HttpExtension.GET, List.class).block();
+ assertEquals(2, messages.size());
+
+ MyClass resultClass;
+ try {
+ resultClass = new ObjectMapper().readValue(messages.get(0), MyClass.class);
+ } catch (Exception ex) {
+ throw new RuntimeException("Error on decode message 1", ex);
+ }
+
+ try {
+ assertEquals("cat", new ObjectMapper().readValue(messages.get(1), String.class));
+ } catch (Exception ex) {
+ throw new RuntimeException("Error on decode message 2", ex);
+ }
+ assertEquals("hello", resultClass.message);
+ }, 8000);
}
public static class MyClass {
- public MyClass() {
- }
-
public String message;
}
}
diff --git a/sdk-tests/src/test/java/io/dapr/it/binding/http/InputBindingController.java b/sdk-tests/src/test/java/io/dapr/it/binding/http/InputBindingController.java
index a1de32bdfc..0d591a83dc 100644
--- a/sdk-tests/src/test/java/io/dapr/it/binding/http/InputBindingController.java
+++ b/sdk-tests/src/test/java/io/dapr/it/binding/http/InputBindingController.java
@@ -18,10 +18,12 @@
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.http.ResponseEntity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -53,6 +55,12 @@ public List getMessages() {
return messagesReceived;
}
+ @PostMapping(path = "/messages/clear")
+ public void clearMessages() {
+ messagesReceived.clear();
+ initialized.set(false);
+ }
+
@GetMapping(path = "/")
public String hello() {
return "hello";
@@ -62,6 +70,13 @@ public String hello() {
public void health() {
}
+ @GetMapping(path = "/github404")
+ public ResponseEntity
*/
-public class PubSubIT extends DaprPubSubIT {
+public abstract class PubSubIT extends DaprPubSubIT {
public static class MyObject {
private String id;
diff --git a/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java b/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java
index fa72c12a36..c968d6e1d1 100644
--- a/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java
@@ -19,9 +19,12 @@
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
+import java.net.InetSocketAddress;
+import java.net.Socket;
import java.util.UUID;
import static io.dapr.it.MethodInvokeServiceProtos.SleepRequest;
+import static io.dapr.it.Retry.callWithRetry;
import static io.dapr.it.tracing.OpenTelemetry.createOpenTelemetry;
import static io.dapr.it.tracing.OpenTelemetry.getReactorContext;
@@ -50,7 +53,7 @@ public static void startGrpcApp() throws Exception {
});
appThread.setDaemon(true);
appThread.start();
- Thread.sleep(1000);
+ waitForGrpcAppPort();
}
@BeforeEach
@@ -84,4 +87,14 @@ public void testInvoke() throws Exception {
span.end();
Validation.validate(spanName, "calllocal/tracingitgrpc-service/sleepovergrpc");
}
+
+ private static void waitForGrpcAppPort() throws Exception {
+ callWithRetry(() -> {
+ try (Socket socket = new Socket()) {
+ socket.connect(new InetSocketAddress("127.0.0.1", DAPR_CONTAINER.getAppPort()), 500);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }, 30000);
+ }
}
From 936df7785976fa5fd8193a603d3c870d0ea71e2c Mon Sep 17 00:00:00 2001
From: Artur Ciocanu
Date: Sat, 21 Feb 2026 17:51:50 -0800
Subject: [PATCH 06/11] Consolidate pubsub payload types and unify grpc
readiness waits
Signed-off-by: Artur Ciocanu
---
.../it/methodinvoke/grpc/MethodInvokeIT.java | 27 ++------------
.../{PubSubIT.java => PubSubPayloads.java} | 18 ++++------
.../it/pubsub/http/SubscriberController.java | 4 +--
.../pubsub/http/DaprPubSubIT.java | 36 +++++++++----------
.../pubsub/http/SubscriberController.java | 6 ++--
.../io/dapr/it/tracing/grpc/TracingIT.java | 16 ++-------
6 files changed, 33 insertions(+), 74 deletions(-)
rename sdk-tests/src/test/java/io/dapr/it/pubsub/http/{PubSubIT.java => PubSubPayloads.java} (76%)
diff --git a/sdk-tests/src/test/java/io/dapr/it/methodinvoke/grpc/MethodInvokeIT.java b/sdk-tests/src/test/java/io/dapr/it/methodinvoke/grpc/MethodInvokeIT.java
index 2bd98a1322..732db20615 100644
--- a/sdk-tests/src/test/java/io/dapr/it/methodinvoke/grpc/MethodInvokeIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/methodinvoke/grpc/MethodInvokeIT.java
@@ -6,8 +6,7 @@
import io.dapr.testcontainers.DaprContainer;
import io.dapr.testcontainers.DaprProtocol;
import io.dapr.testcontainers.internal.DaprContainerFactory;
-import io.grpc.ManagedChannel;
-import io.grpc.ManagedChannelBuilder;
+import io.dapr.utils.NetworkUtils;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import org.junit.jupiter.api.AfterEach;
@@ -20,13 +19,11 @@
import java.time.Duration;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
import static io.dapr.it.MethodInvokeServiceProtos.DeleteMessageRequest;
import static io.dapr.it.MethodInvokeServiceProtos.GetMessagesRequest;
import static io.dapr.it.MethodInvokeServiceProtos.PostMessageRequest;
import static io.dapr.it.MethodInvokeServiceProtos.SleepRequest;
-import static io.dapr.it.Retry.callWithRetry;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -62,7 +59,7 @@ public static void startGrpcApp() throws Exception {
});
appThread.setDaemon(true);
appThread.start();
- waitForGrpcAppReady();
+ NetworkUtils.waitForSocket("127.0.0.1", DAPR_CONTAINER.getAppPort(), 60000);
}
@BeforeEach
@@ -138,24 +135,4 @@ io.dapr.client.DaprClientBuilder newBuilder(DaprContainer daprContainer) {
.withPropertyOverride(io.dapr.config.Properties.GRPC_ENDPOINT, "http://localhost:" + daprContainer.getGrpcPort());
}
}
-
- private static void waitForGrpcAppReady() throws Exception {
- callWithRetry(() -> {
- ManagedChannel channel = ManagedChannelBuilder
- .forAddress("127.0.0.1", DAPR_CONTAINER.getAppPort())
- .usePlaintext()
- .build();
- try {
- MethodInvokeServiceGrpc.newBlockingStub(channel).getMessages(GetMessagesRequest.newBuilder().build());
- } finally {
- channel.shutdownNow();
- try {
- channel.awaitTermination(5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new RuntimeException(e);
- }
- }
- }, 60000);
- }
}
diff --git a/sdk-tests/src/test/java/io/dapr/it/pubsub/http/PubSubIT.java b/sdk-tests/src/test/java/io/dapr/it/pubsub/http/PubSubPayloads.java
similarity index 76%
rename from sdk-tests/src/test/java/io/dapr/it/pubsub/http/PubSubIT.java
rename to sdk-tests/src/test/java/io/dapr/it/pubsub/http/PubSubPayloads.java
index f3c11d1a8b..89adf312da 100644
--- a/sdk-tests/src/test/java/io/dapr/it/pubsub/http/PubSubIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/pubsub/http/PubSubPayloads.java
@@ -8,23 +8,17 @@
* 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.
-*/
+ * limitations under the License.
+ */
package io.dapr.it.pubsub.http;
-import io.dapr.it.testcontainers.pubsub.http.DaprPubSubIT;
-
import java.util.Objects;
-/**
- * Backward-compatible test class for PubSub ITs.
- *
- * This class now runs the Testcontainers-based pub/sub integration suite
- * by inheriting from {@link DaprPubSubIT}. The nested payload types remain
- * here to avoid broad refactors in related test components.
- */
-public abstract class PubSubIT extends DaprPubSubIT {
+public final class PubSubPayloads {
+
+ private PubSubPayloads() {
+ }
public static class MyObject {
private String id;
diff --git a/sdk-tests/src/test/java/io/dapr/it/pubsub/http/SubscriberController.java b/sdk-tests/src/test/java/io/dapr/it/pubsub/http/SubscriberController.java
index 633bfefd18..dbbf34aa97 100644
--- a/sdk-tests/src/test/java/io/dapr/it/pubsub/http/SubscriberController.java
+++ b/sdk-tests/src/test/java/io/dapr/it/pubsub/http/SubscriberController.java
@@ -156,7 +156,7 @@ public Mono handleMessageV3(@RequestBody(required = false) CloudEvent enve
@Topic(name = "typedtestingtopic", pubsubName = "messagebus")
@PostMapping(path = "/route1b")
- public Mono handleMessageTyped(@RequestBody(required = false) CloudEvent envelope) {
+ public Mono handleMessageTyped(@RequestBody(required = false) CloudEvent envelope) {
return Mono.fromRunnable(() -> {
try {
String id = envelope.getData() == null ? "" : envelope.getData().getId();
@@ -214,7 +214,7 @@ public Mono handleMessageTTLTopic(@RequestBody(required = false) CloudEven
@Topic(name = "testinglongvalues", pubsubName = "messagebus")
@PostMapping(path = "/testinglongvalues")
- public Mono handleMessageLongValues(@RequestBody(required = false) CloudEvent cloudEvent) {
+ public Mono handleMessageLongValues(@RequestBody(required = false) CloudEvent cloudEvent) {
return Mono.fromRunnable(() -> {
try {
Long message = cloudEvent.getData().getValue();
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java
index 4f1ad83088..ecef5e2a36 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java
@@ -27,7 +27,7 @@
import io.dapr.client.domain.HttpExtension;
import io.dapr.client.domain.Metadata;
import io.dapr.client.domain.PublishEventRequest;
-import io.dapr.it.pubsub.http.PubSubIT;
+import io.dapr.it.pubsub.http.PubSubPayloads;
import io.dapr.it.testcontainers.DaprClientFactory;
import io.dapr.serializer.CustomizableObjectSerializer;
import io.dapr.serializer.DaprObjectSerializer;
@@ -104,10 +104,10 @@ public class DaprPubSubIT {
// typeRefs
private static final TypeRef> CLOUD_EVENT_LIST_TYPE_REF = new TypeRef<>() {
};
- private static final TypeRef>> CLOUD_EVENT_LONG_LIST_TYPE_REF =
+ private static final TypeRef>> CLOUD_EVENT_LONG_LIST_TYPE_REF =
new TypeRef<>() {
};
- private static final TypeRef>> CLOUD_EVENT_MYOBJECT_LIST_TYPE_REF =
+ private static final TypeRef>> CLOUD_EVENT_MYOBJECT_LIST_TYPE_REF =
new TypeRef<>() {
};
@@ -204,7 +204,7 @@ public void testPubSub() throws Exception {
sendBulkMessagesAsText(client, ANOTHER_TOPIC_NAME);
//Publishing an object.
- PubSubIT.MyObject object = new PubSubIT.MyObject();
+ PubSubPayloads.MyObject object = new PubSubPayloads.MyObject();
object.setId("123");
client.publishEvent(PUBSUB_NAME, TOPIC_NAME, object).block();
LOG.info("Published one object.");
@@ -326,7 +326,7 @@ public void testPubSub() throws Exception {
callWithRetry(() -> {
LOG.info("Checking results for topic " + TYPED_TOPIC_NAME);
- List> messages = client.invokeMethod(
+ List> messages = client.invokeMethod(
PUBSUB_APP_ID,
"messages/typedtestingtopic",
null,
@@ -337,8 +337,8 @@ public void testPubSub() throws Exception {
assertThat(messages)
.extracting(CloudEvent::getData)
.filteredOn(Objects::nonNull)
- .filteredOn(PubSubIT.MyObject.class::isInstance)
- .map(PubSubIT.MyObject::getId)
+ .filteredOn(PubSubPayloads.MyObject.class::isInstance)
+ .map(PubSubPayloads.MyObject::getId)
.contains("123");
}, 2000);
@@ -413,9 +413,9 @@ private static void sendBulkMessagesAsText(DaprClient client, String topicName)
}
private void publishMyObjectAsserting(DaprClient client) {
- PubSubIT.MyObject object = new PubSubIT.MyObject();
+ PubSubPayloads.MyObject object = new PubSubPayloads.MyObject();
object.setId("123");
- BulkPublishResponse response = client.publishEvents(
+ BulkPublishResponse response = client.publishEvents(
PUBSUB_NAME,
TOPIC_BULK,
"application/json",
@@ -547,19 +547,19 @@ public void testPubSubTTLMetadata() throws Exception {
public void testLongValues() throws Exception {
Random random = new Random(590518626939830271L);
- Set values = new HashSet<>();
- values.add(new PubSubIT.ConvertToLong().setVal(590518626939830271L));
- PubSubIT.ConvertToLong val;
+ Set values = new HashSet<>();
+ values.add(new PubSubPayloads.ConvertToLong().setVal(590518626939830271L));
+ PubSubPayloads.ConvertToLong val;
for (int i = 0; i < NUM_MESSAGES - 1; i++) {
do {
- val = new PubSubIT.ConvertToLong().setVal(random.nextLong());
+ val = new PubSubPayloads.ConvertToLong().setVal(random.nextLong());
} while (values.contains(val));
values.add(val);
}
- Iterator valuesIt = values.iterator();
+ Iterator valuesIt = values.iterator();
try (DaprClient client = DaprClientFactory.createDaprClientBuilder(DAPR_CONTAINER).build()) {
for (int i = 0; i < NUM_MESSAGES; i++) {
- PubSubIT.ConvertToLong value = valuesIt.next();
+ PubSubPayloads.ConvertToLong value = valuesIt.next();
LOG.info("The long value sent " + value.getValue());
//Publishing messages
client.publishEvent(
@@ -578,17 +578,17 @@ public void testLongValues() throws Exception {
}
}
- Set actual = new HashSet<>();
+ Set actual = new HashSet<>();
try (DaprClient client = DaprClientFactory.createDaprClientBuilder(DAPR_CONTAINER).build()) {
callWithRetry(() -> {
LOG.info("Checking results for topic " + LONG_TOPIC_NAME);
- final List> messages = client.invokeMethod(
+ final List> messages = client.invokeMethod(
PUBSUB_APP_ID,
"messages/testinglongvalues",
null,
HttpExtension.GET, CLOUD_EVENT_LONG_LIST_TYPE_REF).block();
assertNotNull(messages);
- for (CloudEvent message : messages) {
+ for (CloudEvent message : messages) {
actual.add(message.getData());
}
assertThat(values).containsAll(actual);
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/SubscriberController.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/SubscriberController.java
index fc5f4c0f8d..a1e4771c51 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/SubscriberController.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/SubscriberController.java
@@ -21,7 +21,7 @@
import io.dapr.client.domain.BulkSubscribeMessage;
import io.dapr.client.domain.BulkSubscribeMessageEntry;
import io.dapr.client.domain.CloudEvent;
-import io.dapr.it.pubsub.http.PubSubIT;
+import io.dapr.it.pubsub.http.PubSubPayloads;
import io.dapr.springboot.annotations.BulkSubscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -161,7 +161,7 @@ public Mono handleMessageV3(@RequestBody(required = false) CloudEvent enve
@Topic(name = "typedtestingtopic", pubsubName = "pubsub")
@PostMapping(path = "/route1b")
- public Mono handleMessageTyped(@RequestBody(required = false) CloudEvent envelope) {
+ public Mono handleMessageTyped(@RequestBody(required = false) CloudEvent envelope) {
return Mono.fromRunnable(() -> {
try {
String id = envelope.getData() == null ? "" : envelope.getData().getId();
@@ -218,7 +218,7 @@ public Mono handleMessageTTLTopic(@RequestBody(required = false) CloudEven
@Topic(name = "testinglongvalues", pubsubName = "pubsub")
@PostMapping(path = "/testinglongvalues")
- public Mono handleMessageLongValues(@RequestBody(required = false) CloudEvent cloudEvent) {
+ public Mono handleMessageLongValues(@RequestBody(required = false) CloudEvent cloudEvent) {
return Mono.fromRunnable(() -> {
try {
Long message = cloudEvent.getData().getValue();
diff --git a/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java b/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java
index c968d6e1d1..b8e092e1d3 100644
--- a/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java
@@ -6,6 +6,7 @@
import io.dapr.testcontainers.DaprContainer;
import io.dapr.testcontainers.DaprProtocol;
import io.dapr.testcontainers.internal.DaprContainerFactory;
+import io.dapr.utils.NetworkUtils;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
@@ -19,12 +20,9 @@
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
-import java.net.InetSocketAddress;
-import java.net.Socket;
import java.util.UUID;
import static io.dapr.it.MethodInvokeServiceProtos.SleepRequest;
-import static io.dapr.it.Retry.callWithRetry;
import static io.dapr.it.tracing.OpenTelemetry.createOpenTelemetry;
import static io.dapr.it.tracing.OpenTelemetry.getReactorContext;
@@ -53,7 +51,7 @@ public static void startGrpcApp() throws Exception {
});
appThread.setDaemon(true);
appThread.start();
- waitForGrpcAppPort();
+ NetworkUtils.waitForSocket("127.0.0.1", DAPR_CONTAINER.getAppPort(), 30000);
}
@BeforeEach
@@ -87,14 +85,4 @@ public void testInvoke() throws Exception {
span.end();
Validation.validate(spanName, "calllocal/tracingitgrpc-service/sleepovergrpc");
}
-
- private static void waitForGrpcAppPort() throws Exception {
- callWithRetry(() -> {
- try (Socket socket = new Socket()) {
- socket.connect(new InetSocketAddress("127.0.0.1", DAPR_CONTAINER.getAppPort()), 500);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }, 30000);
- }
}
From 429420a8bb11f8680043ce52ed7d7acd4123c279 Mon Sep 17 00:00:00 2001
From: Artur Ciocanu
Date: Sun, 22 Feb 2026 17:26:30 -0800
Subject: [PATCH 07/11] Reuse shared Testcontainers network across integration
tests
Signed-off-by: Artur Ciocanu
---
.../it/actors/ActorReminderFailoverIT.java | 2 +-
.../dapr/it/actors/ActorSdkResiliencyIT.java | 2 +-
.../configuration/ConfigurationClientIT.java | 2 +-
.../dapr/it/pubsub/stream/PubSubStreamIT.java | 2 +-
.../dapr/it/resiliency/SdkResiliencyIT.java | 2 +-
.../dapr/it/resiliency/WaitForSidecarIT.java | 2 +-
.../io/dapr/it/secrets/SecretsClientIT.java | 2 +-
.../spring/data/DaprKeyValueRepositoryIT.java | 2 +-
.../data/MySQLDaprKeyValueTemplateIT.java | 2 +-
.../PostgreSQLDaprKeyValueTemplateIT.java | 2 +-
.../messaging/DaprSpringMessagingIT.java | 2 +-
.../io/dapr/it/state/GRPCStateClientIT.java | 2 +-
.../io/dapr/it/state/HelloWorldClientIT.java | 2 +-
.../testcontainers/TestContainerNetworks.java | 31 +++++++++++++++++++
.../DaprConversationAlpha2IT.java | 2 +-
.../conversations/DaprConversationIT.java | 2 +-
.../it/testcontainers/jobs/DaprJobsIT.java | 2 +-
.../pubsub/http/DaprPubSubIT.java | 2 +-
.../pubsub/outbox/DaprPubSubOutboxIT.java | 2 +-
.../workflows/DaprWorkflowsIT.java | 2 +-
.../WorkflowsMultiAppCallActivityIT.java | 2 +-
.../full/FullVersioningWorkflowsIT.java | 2 +-
.../patch/PatchVersioningWorkflowsIT.java | 2 +-
23 files changed, 53 insertions(+), 22 deletions(-)
create mode 100644 sdk-tests/src/test/java/io/dapr/it/testcontainers/TestContainerNetworks.java
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/ActorReminderFailoverIT.java b/sdk-tests/src/test/java/io/dapr/it/actors/ActorReminderFailoverIT.java
index 79fba9b0b2..c7088afa5c 100644
--- a/sdk-tests/src/test/java/io/dapr/it/actors/ActorReminderFailoverIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/ActorReminderFailoverIT.java
@@ -59,7 +59,7 @@ public class ActorReminderFailoverIT {
private static final String CONTAINER_CLASSPATH = prepareContainerClasspath();
private static final String FIRST_ACTOR_IDENTIFIER = "4111";
private static final String SECOND_ACTOR_IDENTIFIER = "4222";
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private static final DaprPlacementContainer SHARED_PLACEMENT_CONTAINER = new DaprPlacementContainer(
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/ActorSdkResiliencyIT.java b/sdk-tests/src/test/java/io/dapr/it/actors/ActorSdkResiliencyIT.java
index 03beeaf531..186c460696 100644
--- a/sdk-tests/src/test/java/io/dapr/it/actors/ActorSdkResiliencyIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/ActorSdkResiliencyIT.java
@@ -76,7 +76,7 @@ public class ActorSdkResiliencyIT {
private static final int MAX_RETRIES = -1; // Infinity
- private static final Network NETWORK = Network.newNetwork();
+ private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final int GRPC_PROXY_PORT = 8666;
private static final int HTTP_PROXY_PORT = 8667;
diff --git a/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java b/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java
index ceacf988e0..49ce1cba29 100644
--- a/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java
@@ -46,7 +46,7 @@ public class ConfigurationClientIT {
private static final String CONFIG_STORE_NAME = "redisconfigstore";
- private static final Network NETWORK = Network.newNetwork();
+ private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java b/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java
index aab4eef5ef..640ec7ed89 100644
--- a/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java
@@ -54,7 +54,7 @@ public class PubSubStreamIT {
private static final String TOPIC_NAME_RAWPAYLOAD = "stream-topic-rawpayload";
private static final String PUBSUB_NAME = "messagebus";
- private static final Network NETWORK = Network.newNetwork();
+ private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/resiliency/SdkResiliencyIT.java b/sdk-tests/src/test/java/io/dapr/it/resiliency/SdkResiliencyIT.java
index bf17f7d88d..74d19d836d 100644
--- a/sdk-tests/src/test/java/io/dapr/it/resiliency/SdkResiliencyIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/resiliency/SdkResiliencyIT.java
@@ -69,7 +69,7 @@
public class SdkResiliencyIT {
public static final int WIREMOCK_PORT = 8888;
- private static final Network NETWORK = Network.newNetwork();
+ private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final String STATE_STORE_NAME = "kvstore";
private static final int INFINITE_RETRY = -1;
diff --git a/sdk-tests/src/test/java/io/dapr/it/resiliency/WaitForSidecarIT.java b/sdk-tests/src/test/java/io/dapr/it/resiliency/WaitForSidecarIT.java
index 82a32bef29..e2c4728b82 100644
--- a/sdk-tests/src/test/java/io/dapr/it/resiliency/WaitForSidecarIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/resiliency/WaitForSidecarIT.java
@@ -44,7 +44,7 @@ public class WaitForSidecarIT {
// Use a number large enough to make sure it will respect the entire timeout.
private static final Duration LATENCY = Duration.ofSeconds(5);
- private static final Network NETWORK = Network.newNetwork();
+ private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final String APP_ID = "wait-for-sidecar-it";
@Container
diff --git a/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java b/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
index bbc647f7c2..751cdb1240 100644
--- a/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
@@ -47,7 +47,7 @@ public class SecretsClientIT {
private static final String KYE2 = UUID.randomUUID().toString();
- private static final Network NETWORK = Network.newNetwork();
+ private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/spring/data/DaprKeyValueRepositoryIT.java b/sdk-tests/src/test/java/io/dapr/it/spring/data/DaprKeyValueRepositoryIT.java
index 5a7ceb2920..0318d37207 100644
--- a/sdk-tests/src/test/java/io/dapr/it/spring/data/DaprKeyValueRepositoryIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/spring/data/DaprKeyValueRepositoryIT.java
@@ -55,7 +55,7 @@ public class DaprKeyValueRepositoryIT {
private static final Map BINDING_PROPERTIES = Map.of("connectionString", CONNECTION_STRING);
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private static final PostgreSQLContainer> POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>("postgres:16-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/spring/data/MySQLDaprKeyValueTemplateIT.java b/sdk-tests/src/test/java/io/dapr/it/spring/data/MySQLDaprKeyValueTemplateIT.java
index e4397d496d..27d1b34585 100644
--- a/sdk-tests/src/test/java/io/dapr/it/spring/data/MySQLDaprKeyValueTemplateIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/spring/data/MySQLDaprKeyValueTemplateIT.java
@@ -64,7 +64,7 @@ public class MySQLDaprKeyValueTemplateIT {
private static final Map BINDING_PROPERTIES = Map.of("url", BINDING_DSN);
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final WaitStrategy MYSQL_WAIT_STRATEGY = Wait
.forLogMessage(".*port: 3306 MySQL Community Server \\(GPL\\).*", 1)
diff --git a/sdk-tests/src/test/java/io/dapr/it/spring/data/PostgreSQLDaprKeyValueTemplateIT.java b/sdk-tests/src/test/java/io/dapr/it/spring/data/PostgreSQLDaprKeyValueTemplateIT.java
index 3f5253758a..0d63801e5c 100644
--- a/sdk-tests/src/test/java/io/dapr/it/spring/data/PostgreSQLDaprKeyValueTemplateIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/spring/data/PostgreSQLDaprKeyValueTemplateIT.java
@@ -59,7 +59,7 @@ public class PostgreSQLDaprKeyValueTemplateIT {
private static final Map BINDING_PROPERTIES = Map.of("connectionString", CONNECTION_STRING);
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private static final PostgreSQLContainer> POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>("postgres:16-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/spring/messaging/DaprSpringMessagingIT.java b/sdk-tests/src/test/java/io/dapr/it/spring/messaging/DaprSpringMessagingIT.java
index 26dc6b5db3..17b9412239 100644
--- a/sdk-tests/src/test/java/io/dapr/it/spring/messaging/DaprSpringMessagingIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/spring/messaging/DaprSpringMessagingIT.java
@@ -56,7 +56,7 @@ public class DaprSpringMessagingIT {
private static final String PUBSUB_NAME = "pubsub";
private static final String TOPIC = "mockTopic";
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final int APP_PORT = 8080;
@Container
diff --git a/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java b/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java
index afd19636cf..4193c83ad3 100644
--- a/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java
@@ -40,7 +40,7 @@
@Tag("testcontainers")
public class GRPCStateClientIT extends AbstractStateClientIT {
- private static final Network NETWORK = Network.newNetwork();
+ private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java b/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java
index 45116ec355..d7309867e7 100644
--- a/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java
@@ -38,7 +38,7 @@ public class HelloWorldClientIT {
private static final String STATE_STORE_NAME = "statestore";
- private static final Network NETWORK = Network.newNetwork();
+ private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/TestContainerNetworks.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/TestContainerNetworks.java
new file mode 100644
index 0000000000..beddc26df9
--- /dev/null
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/TestContainerNetworks.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2026 The Dapr Authors
+ * Licensed 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 io.dapr.it.testcontainers;
+
+import org.testcontainers.containers.Network;
+
+/**
+ * Shared Docker network for integration tests.
+ *
+ * Creating a network per test class can exhaust Docker address pools in CI.
+ * Reusing one network per JVM avoids that failure mode while preserving
+ * inter-container communication semantics needed by multi-container tests.
+ */
+public final class TestContainerNetworks {
+
+ private TestContainerNetworks() {
+ }
+
+ public static final Network SHARED_NETWORK = Network.newNetwork();
+}
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationAlpha2IT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationAlpha2IT.java
index 00522eae66..d71b1885ac 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationAlpha2IT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationAlpha2IT.java
@@ -68,7 +68,7 @@
@Tag("testcontainers")
public class DaprConversationAlpha2IT {
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final Random RANDOM = new Random();
private static final int PORT = RANDOM.nextInt(1000) + 8000;
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationIT.java
index dd35ce6905..464c6c3b23 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationIT.java
@@ -52,7 +52,7 @@
@Tag("testcontainers")
public class DaprConversationIT {
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final Random RANDOM = new Random();
private static final int PORT = RANDOM.nextInt(1000) + 8000;
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/jobs/DaprJobsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/jobs/DaprJobsIT.java
index b17c834139..b80cdae11a 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/jobs/DaprJobsIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/jobs/DaprJobsIT.java
@@ -58,7 +58,7 @@
@Tag("testcontainers")
public class DaprJobsIT {
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final Random RANDOM = new Random();
private static final int PORT = RANDOM.nextInt(1000) + 8000;
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java
index ecef5e2a36..013be472de 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java
@@ -81,7 +81,7 @@
public class DaprPubSubIT {
private static final Logger LOG = LoggerFactory.getLogger(DaprPubSubIT.class);
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final Random RANDOM = new Random();
private static final int PORT = RANDOM.nextInt(1000) + 8000;
private static final String APP_FOUND_MESSAGE_PATTERN = ".*application discovered on port.*";
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/outbox/DaprPubSubOutboxIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/outbox/DaprPubSubOutboxIT.java
index d4139bcf91..6254ccc92c 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/outbox/DaprPubSubOutboxIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/outbox/DaprPubSubOutboxIT.java
@@ -59,7 +59,7 @@
public class DaprPubSubOutboxIT {
private static final Logger LOG = LoggerFactory.getLogger(DaprPubSubOutboxIT.class);
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final Random RANDOM = new Random();
private static final int PORT = RANDOM.nextInt(1000) + 8000;
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/DaprWorkflowsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/DaprWorkflowsIT.java
index fe89a4326c..9fb9d7812c 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/DaprWorkflowsIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/DaprWorkflowsIT.java
@@ -57,7 +57,7 @@
@Tag("testcontainers")
public class DaprWorkflowsIT {
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/multiapp/WorkflowsMultiAppCallActivityIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/multiapp/WorkflowsMultiAppCallActivityIT.java
index b1b0f123ec..bbce5f90eb 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/multiapp/WorkflowsMultiAppCallActivityIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/multiapp/WorkflowsMultiAppCallActivityIT.java
@@ -54,7 +54,7 @@
@Tag("testcontainers")
public class WorkflowsMultiAppCallActivityIT {
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
@Container
private final static DaprPlacementContainer sharedPlacementContainer = new DaprPlacementContainer(DAPR_PLACEMENT_IMAGE_TAG)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java
index e20283280a..c9932d4e6f 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java
@@ -71,7 +71,7 @@
@Tag("testcontainers")
public class FullVersioningWorkflowsIT {
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final WaitStrategy MYSQL_WAIT_STRATEGY = Wait
.forLogMessage(".*port: 3306 MySQL Community Server \\(GPL\\).*", 1)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/patch/PatchVersioningWorkflowsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/patch/PatchVersioningWorkflowsIT.java
index 9e13a9db33..9895ac81fd 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/patch/PatchVersioningWorkflowsIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/patch/PatchVersioningWorkflowsIT.java
@@ -66,7 +66,7 @@
@Tag("testcontainers")
public class PatchVersioningWorkflowsIT {
- private static final Network DAPR_NETWORK = Network.newNetwork();
+ private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
private static final WaitStrategy MYSQL_WAIT_STRATEGY = Wait
.forLogMessage(".*port: 3306 MySQL Community Server \\(GPL\\).*", 1)
From 8de94e36f5fe793c3f526c4e5932191462d03465 Mon Sep 17 00:00:00 2001
From: Artur Ciocanu
Date: Sun, 22 Feb 2026 19:33:25 -0800
Subject: [PATCH 08/11] Harden testcontainer networking and clean
fully-qualified usages
Signed-off-by: Artur Ciocanu
---
.../it/actors/ActorReminderFailoverIT.java | 12 ++++++---
.../dapr/it/actors/ActorSdkResiliencyIT.java | 4 ++-
.../configuration/ConfigurationClientIT.java | 4 ++-
.../it/methodinvoke/grpc/MethodInvokeIT.java | 10 ++++---
.../dapr/it/pubsub/stream/PubSubStreamIT.java | 4 ++-
.../dapr/it/resiliency/SdkResiliencyIT.java | 4 ++-
.../dapr/it/resiliency/WaitForSidecarIT.java | 4 ++-
.../io/dapr/it/secrets/SecretsClientIT.java | 4 ++-
.../spring/data/DaprKeyValueRepositoryIT.java | 4 ++-
.../data/MySQLDaprKeyValueTemplateIT.java | 4 ++-
.../PostgreSQLDaprKeyValueTemplateIT.java | 4 ++-
.../messaging/DaprSpringMessagingIT.java | 4 ++-
.../io/dapr/it/state/GRPCStateClientIT.java | 4 ++-
.../io/dapr/it/state/HelloWorldClientIT.java | 4 ++-
.../testcontainers/TestContainerNetworks.java | 27 +++++++++++++++----
.../DaprConversationAlpha2IT.java | 8 +++---
.../conversations/DaprConversationIT.java | 8 +++---
.../it/testcontainers/jobs/DaprJobsIT.java | 8 +++---
.../pubsub/http/DaprPubSubIT.java | 7 ++---
.../pubsub/outbox/DaprPubSubOutboxIT.java | 8 +++---
.../workflows/DaprWorkflowsIT.java | 4 ++-
.../WorkflowsMultiAppCallActivityIT.java | 4 ++-
.../full/FullVersioningWorkflowsIT.java | 4 ++-
.../patch/PatchVersioningWorkflowsIT.java | 4 ++-
.../io/dapr/it/tracing/grpc/TracingIT.java | 8 +++---
.../it/tracing/http/OpenTelemetryConfig.java | 26 +++++++++---------
26 files changed, 123 insertions(+), 63 deletions(-)
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/ActorReminderFailoverIT.java b/sdk-tests/src/test/java/io/dapr/it/actors/ActorReminderFailoverIT.java
index c7088afa5c..6338d3e0ff 100644
--- a/sdk-tests/src/test/java/io/dapr/it/actors/ActorReminderFailoverIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/ActorReminderFailoverIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.actors;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.actors.ActorId;
import io.dapr.actors.client.ActorClient;
import io.dapr.actors.client.ActorProxy;
@@ -46,8 +48,10 @@
import java.util.UUID;
import static io.dapr.it.Retry.callWithRetry;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static io.dapr.testcontainers.DaprContainerConstants.DAPR_PLACEMENT_IMAGE_TAG;
+import static io.dapr.testcontainers.DaprContainerConstants.DAPR_SCHEDULER_IMAGE_TAG;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
@Testcontainers
public class ActorReminderFailoverIT {
@@ -59,18 +63,18 @@ public class ActorReminderFailoverIT {
private static final String CONTAINER_CLASSPATH = prepareContainerClasspath();
private static final String FIRST_ACTOR_IDENTIFIER = "4111";
private static final String SECOND_ACTOR_IDENTIFIER = "4222";
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.ACTORS_NETWORK;
@Container
private static final DaprPlacementContainer SHARED_PLACEMENT_CONTAINER = new DaprPlacementContainer(
- io.dapr.testcontainers.DaprContainerConstants.DAPR_PLACEMENT_IMAGE_TAG)
+ DAPR_PLACEMENT_IMAGE_TAG)
.withNetwork(DAPR_NETWORK)
.withNetworkAliases("placement")
.withReuse(false);
@Container
private static final DaprSchedulerContainer SHARED_SCHEDULER_CONTAINER = new DaprSchedulerContainer(
- io.dapr.testcontainers.DaprContainerConstants.DAPR_SCHEDULER_IMAGE_TAG)
+ DAPR_SCHEDULER_IMAGE_TAG)
.withNetwork(DAPR_NETWORK)
.withNetworkAliases("scheduler")
.withReuse(false);
diff --git a/sdk-tests/src/test/java/io/dapr/it/actors/ActorSdkResiliencyIT.java b/sdk-tests/src/test/java/io/dapr/it/actors/ActorSdkResiliencyIT.java
index 186c460696..02687c0627 100644
--- a/sdk-tests/src/test/java/io/dapr/it/actors/ActorSdkResiliencyIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/actors/ActorSdkResiliencyIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.actors;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import eu.rekawek.toxiproxy.Proxy;
import eu.rekawek.toxiproxy.ToxiproxyClient;
import eu.rekawek.toxiproxy.model.ToxicDirection;
@@ -76,7 +78,7 @@ public class ActorSdkResiliencyIT {
private static final int MAX_RETRIES = -1; // Infinity
- private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network NETWORK = TestContainerNetworks.ACTORS_NETWORK;
private static final int GRPC_PROXY_PORT = 8666;
private static final int HTTP_PROXY_PORT = 8667;
diff --git a/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java b/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java
index 49ce1cba29..81509420fe 100644
--- a/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.configuration;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprClient;
import io.dapr.client.domain.ConfigurationItem;
import io.dapr.client.domain.SubscribeConfigurationResponse;
@@ -46,7 +48,7 @@ public class ConfigurationClientIT {
private static final String CONFIG_STORE_NAME = "redisconfigstore";
- private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network NETWORK = TestContainerNetworks.STATE_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/methodinvoke/grpc/MethodInvokeIT.java b/sdk-tests/src/test/java/io/dapr/it/methodinvoke/grpc/MethodInvokeIT.java
index 732db20615..62ace6d35b 100644
--- a/sdk-tests/src/test/java/io/dapr/it/methodinvoke/grpc/MethodInvokeIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/methodinvoke/grpc/MethodInvokeIT.java
@@ -1,7 +1,9 @@
package io.dapr.it.methodinvoke.grpc;
import io.dapr.client.DaprClient;
+import io.dapr.client.DaprClientBuilder;
import io.dapr.client.resiliency.ResiliencyOptions;
+import io.dapr.config.Properties;
import io.dapr.it.MethodInvokeServiceGrpc;
import io.dapr.testcontainers.DaprContainer;
import io.dapr.testcontainers.DaprProtocol;
@@ -129,10 +131,10 @@ private MethodInvokeServiceGrpc.MethodInvokeServiceBlockingStub createGrpcStub(D
}
private static class DaprClientBuilderFactory {
- io.dapr.client.DaprClientBuilder newBuilder(DaprContainer daprContainer) {
- return new io.dapr.client.DaprClientBuilder()
- .withPropertyOverride(io.dapr.config.Properties.HTTP_ENDPOINT, "http://localhost:" + daprContainer.getHttpPort())
- .withPropertyOverride(io.dapr.config.Properties.GRPC_ENDPOINT, "http://localhost:" + daprContainer.getGrpcPort());
+ DaprClientBuilder newBuilder(DaprContainer daprContainer) {
+ return new DaprClientBuilder()
+ .withPropertyOverride(Properties.HTTP_ENDPOINT, "http://localhost:" + daprContainer.getHttpPort())
+ .withPropertyOverride(Properties.GRPC_ENDPOINT, "http://localhost:" + daprContainer.getGrpcPort());
}
}
}
diff --git a/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java b/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java
index 640ec7ed89..65a74e73dc 100644
--- a/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.pubsub.stream;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprClient;
import io.dapr.client.DaprPreviewClient;
import io.dapr.client.SubscriptionListener;
@@ -54,7 +56,7 @@ public class PubSubStreamIT {
private static final String TOPIC_NAME_RAWPAYLOAD = "stream-topic-rawpayload";
private static final String PUBSUB_NAME = "messagebus";
- private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network NETWORK = TestContainerNetworks.PUBSUB_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/resiliency/SdkResiliencyIT.java b/sdk-tests/src/test/java/io/dapr/it/resiliency/SdkResiliencyIT.java
index 74d19d836d..5f8cd408c3 100644
--- a/sdk-tests/src/test/java/io/dapr/it/resiliency/SdkResiliencyIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/resiliency/SdkResiliencyIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.resiliency;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import eu.rekawek.toxiproxy.Proxy;
@@ -69,7 +71,7 @@
public class SdkResiliencyIT {
public static final int WIREMOCK_PORT = 8888;
- private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network NETWORK = TestContainerNetworks.GENERAL_NETWORK;
private static final String STATE_STORE_NAME = "kvstore";
private static final int INFINITE_RETRY = -1;
diff --git a/sdk-tests/src/test/java/io/dapr/it/resiliency/WaitForSidecarIT.java b/sdk-tests/src/test/java/io/dapr/it/resiliency/WaitForSidecarIT.java
index e2c4728b82..3a19ef7671 100644
--- a/sdk-tests/src/test/java/io/dapr/it/resiliency/WaitForSidecarIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/resiliency/WaitForSidecarIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.resiliency;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import eu.rekawek.toxiproxy.Proxy;
import eu.rekawek.toxiproxy.ToxiproxyClient;
import eu.rekawek.toxiproxy.model.ToxicDirection;
@@ -44,7 +46,7 @@ public class WaitForSidecarIT {
// Use a number large enough to make sure it will respect the entire timeout.
private static final Duration LATENCY = Duration.ofSeconds(5);
- private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network NETWORK = TestContainerNetworks.GENERAL_NETWORK;
private static final String APP_ID = "wait-for-sidecar-it";
@Container
diff --git a/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java b/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
index 751cdb1240..1ff349c424 100644
--- a/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.secrets;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprClient;
import io.dapr.it.testcontainers.DaprClientFactory;
import io.dapr.testcontainers.Component;
@@ -47,7 +49,7 @@ public class SecretsClientIT {
private static final String KYE2 = UUID.randomUUID().toString();
- private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network NETWORK = TestContainerNetworks.STATE_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/spring/data/DaprKeyValueRepositoryIT.java b/sdk-tests/src/test/java/io/dapr/it/spring/data/DaprKeyValueRepositoryIT.java
index 0318d37207..7e1c0341a1 100644
--- a/sdk-tests/src/test/java/io/dapr/it/spring/data/DaprKeyValueRepositoryIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/spring/data/DaprKeyValueRepositoryIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.spring.data;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.testcontainers.Component;
import io.dapr.testcontainers.DaprContainer;
import io.dapr.testcontainers.DaprLogLevel;
@@ -55,7 +57,7 @@ public class DaprKeyValueRepositoryIT {
private static final Map BINDING_PROPERTIES = Map.of("connectionString", CONNECTION_STRING);
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.DATA_NETWORK;
@Container
private static final PostgreSQLContainer> POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>("postgres:16-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/spring/data/MySQLDaprKeyValueTemplateIT.java b/sdk-tests/src/test/java/io/dapr/it/spring/data/MySQLDaprKeyValueTemplateIT.java
index 27d1b34585..5644cc7ec5 100644
--- a/sdk-tests/src/test/java/io/dapr/it/spring/data/MySQLDaprKeyValueTemplateIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/spring/data/MySQLDaprKeyValueTemplateIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.spring.data;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprClient;
import io.dapr.spring.data.DaprKeyValueTemplate;
import io.dapr.testcontainers.Component;
@@ -64,7 +66,7 @@ public class MySQLDaprKeyValueTemplateIT {
private static final Map BINDING_PROPERTIES = Map.of("url", BINDING_DSN);
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.DATA_NETWORK;
private static final WaitStrategy MYSQL_WAIT_STRATEGY = Wait
.forLogMessage(".*port: 3306 MySQL Community Server \\(GPL\\).*", 1)
diff --git a/sdk-tests/src/test/java/io/dapr/it/spring/data/PostgreSQLDaprKeyValueTemplateIT.java b/sdk-tests/src/test/java/io/dapr/it/spring/data/PostgreSQLDaprKeyValueTemplateIT.java
index 0d63801e5c..e2b84d4598 100644
--- a/sdk-tests/src/test/java/io/dapr/it/spring/data/PostgreSQLDaprKeyValueTemplateIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/spring/data/PostgreSQLDaprKeyValueTemplateIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.spring.data;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprClient;
import io.dapr.spring.data.DaprKeyValueTemplate;
import io.dapr.testcontainers.Component;
@@ -59,7 +61,7 @@ public class PostgreSQLDaprKeyValueTemplateIT {
private static final Map BINDING_PROPERTIES = Map.of("connectionString", CONNECTION_STRING);
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.DATA_NETWORK;
@Container
private static final PostgreSQLContainer> POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>("postgres:16-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/spring/messaging/DaprSpringMessagingIT.java b/sdk-tests/src/test/java/io/dapr/it/spring/messaging/DaprSpringMessagingIT.java
index 17b9412239..6d096421d0 100644
--- a/sdk-tests/src/test/java/io/dapr/it/spring/messaging/DaprSpringMessagingIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/spring/messaging/DaprSpringMessagingIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.spring.messaging;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.domain.CloudEvent;
import io.dapr.spring.boot.autoconfigure.client.DaprClientAutoConfiguration;
import io.dapr.spring.messaging.DaprMessagingTemplate;
@@ -56,7 +58,7 @@ public class DaprSpringMessagingIT {
private static final String PUBSUB_NAME = "pubsub";
private static final String TOPIC = "mockTopic";
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.GENERAL_NETWORK;
private static final int APP_PORT = 8080;
@Container
diff --git a/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java b/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java
index 4193c83ad3..835f2f0579 100644
--- a/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.state;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprClient;
import io.dapr.client.domain.State;
import io.dapr.it.testcontainers.DaprClientFactory;
@@ -40,7 +42,7 @@
@Tag("testcontainers")
public class GRPCStateClientIT extends AbstractStateClientIT {
- private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network NETWORK = TestContainerNetworks.STATE_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java b/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java
index d7309867e7..eb347797da 100644
--- a/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.state;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprClient;
import io.dapr.it.testcontainers.DaprClientFactory;
import io.dapr.testcontainers.Component;
@@ -38,7 +40,7 @@ public class HelloWorldClientIT {
private static final String STATE_STORE_NAME = "statestore";
- private static final Network NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network NETWORK = TestContainerNetworks.STATE_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/TestContainerNetworks.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/TestContainerNetworks.java
index beddc26df9..bdd7ee8bac 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/TestContainerNetworks.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/TestContainerNetworks.java
@@ -15,17 +15,34 @@
import org.testcontainers.containers.Network;
+import java.io.IOException;
+import java.net.ServerSocket;
+
/**
- * Shared Docker network for integration tests.
+ * Scoped Docker networks for integration tests.
*
- * Creating a network per test class can exhaust Docker address pools in CI.
- * Reusing one network per JVM avoids that failure mode while preserving
- * inter-container communication semantics needed by multi-container tests.
+ * Creating one network per test class can exhaust Docker address pools in CI.
+ * Creating one global network for all tests can cause alias/DNS contention.
+ * This class provides a small set of reused networks to balance both concerns.
*/
public final class TestContainerNetworks {
private TestContainerNetworks() {
}
- public static final Network SHARED_NETWORK = Network.newNetwork();
+ public static final Network ACTORS_NETWORK = Network.newNetwork();
+ public static final Network STATE_NETWORK = Network.newNetwork();
+ public static final Network DATA_NETWORK = Network.newNetwork();
+ public static final Network PUBSUB_NETWORK = Network.newNetwork();
+ public static final Network WORKFLOWS_NETWORK = Network.newNetwork();
+ public static final Network GENERAL_NETWORK = Network.newNetwork();
+
+ public static int allocateFreePort() {
+ try (ServerSocket socket = new ServerSocket(0)) {
+ socket.setReuseAddress(true);
+ return socket.getLocalPort();
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to allocate free port", e);
+ }
+ }
}
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationAlpha2IT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationAlpha2IT.java
index d71b1885ac..d656169889 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationAlpha2IT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationAlpha2IT.java
@@ -13,6 +13,8 @@
package io.dapr.it.testcontainers.conversations;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprPreviewClient;
import io.dapr.client.domain.AssistantMessage;
import io.dapr.client.domain.ConversationInputAlpha2;
@@ -50,7 +52,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Random;
import static io.dapr.it.testcontainers.ContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -68,9 +69,8 @@
@Tag("testcontainers")
public class DaprConversationAlpha2IT {
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
- private static final Random RANDOM = new Random();
- private static final int PORT = RANDOM.nextInt(1000) + 8000;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.GENERAL_NETWORK;
+ private static final int PORT = TestContainerNetworks.allocateFreePort();
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationIT.java
index 464c6c3b23..64fc909f48 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/conversations/DaprConversationIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.testcontainers.conversations;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprPreviewClient;
import io.dapr.client.domain.ConversationInput;
import io.dapr.client.domain.ConversationRequest;
@@ -37,7 +39,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.Random;
import static io.dapr.it.testcontainers.ContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
@@ -52,9 +53,8 @@
@Tag("testcontainers")
public class DaprConversationIT {
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
- private static final Random RANDOM = new Random();
- private static final int PORT = RANDOM.nextInt(1000) + 8000;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.GENERAL_NETWORK;
+ private static final int PORT = TestContainerNetworks.allocateFreePort();
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/jobs/DaprJobsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/jobs/DaprJobsIT.java
index b80cdae11a..30bd972ad4 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/jobs/DaprJobsIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/jobs/DaprJobsIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.testcontainers.jobs;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprClient;
import io.dapr.client.domain.ConstantFailurePolicy;
import io.dapr.client.domain.DeleteJobRequest;
@@ -42,7 +44,6 @@
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
-import java.util.Random;
import static io.dapr.it.testcontainers.ContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
import static org.junit.Assert.assertEquals;
@@ -58,9 +59,8 @@
@Tag("testcontainers")
public class DaprJobsIT {
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
- private static final Random RANDOM = new Random();
- private static final int PORT = RANDOM.nextInt(1000) + 8000;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.GENERAL_NETWORK;
+ private static final int PORT = TestContainerNetworks.allocateFreePort();
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java
index 013be472de..63f3467649 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/http/DaprPubSubIT.java
@@ -12,6 +12,8 @@
*/
package io.dapr.it.testcontainers.pubsub.http;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -81,9 +83,8 @@
public class DaprPubSubIT {
private static final Logger LOG = LoggerFactory.getLogger(DaprPubSubIT.class);
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
- private static final Random RANDOM = new Random();
- private static final int PORT = RANDOM.nextInt(1000) + 8000;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.PUBSUB_NETWORK;
+ private static final int PORT = TestContainerNetworks.allocateFreePort();
private static final String APP_FOUND_MESSAGE_PATTERN = ".*application discovered on port.*";
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/outbox/DaprPubSubOutboxIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/outbox/DaprPubSubOutboxIT.java
index 6254ccc92c..757b626313 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/outbox/DaprPubSubOutboxIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/pubsub/outbox/DaprPubSubOutboxIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.testcontainers.pubsub.outbox;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.client.DaprClient;
import io.dapr.client.domain.ExecuteStateTransactionRequest;
import io.dapr.client.domain.State;
@@ -43,7 +45,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Random;
import static io.dapr.it.testcontainers.ContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
@@ -59,9 +60,8 @@
public class DaprPubSubOutboxIT {
private static final Logger LOG = LoggerFactory.getLogger(DaprPubSubOutboxIT.class);
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
- private static final Random RANDOM = new Random();
- private static final int PORT = RANDOM.nextInt(1000) + 8000;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.PUBSUB_NETWORK;
+ private static final int PORT = TestContainerNetworks.allocateFreePort();
private static final String PUBSUB_APP_ID = "pubsub-dapr-app";
private static final String PUBSUB_NAME = "pubsub";
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/DaprWorkflowsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/DaprWorkflowsIT.java
index 9fb9d7812c..2d898c16b0 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/DaprWorkflowsIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/DaprWorkflowsIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.testcontainers.workflows;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -57,7 +59,7 @@
@Tag("testcontainers")
public class DaprWorkflowsIT {
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.WORKFLOWS_NETWORK;
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/multiapp/WorkflowsMultiAppCallActivityIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/multiapp/WorkflowsMultiAppCallActivityIT.java
index bbce5f90eb..98016bab4c 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/multiapp/WorkflowsMultiAppCallActivityIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/multiapp/WorkflowsMultiAppCallActivityIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.testcontainers.workflows.multiapp;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.it.testcontainers.ContainerConstants;
import io.dapr.testcontainers.Component;
import io.dapr.testcontainers.DaprContainer;
@@ -54,7 +56,7 @@
@Tag("testcontainers")
public class WorkflowsMultiAppCallActivityIT {
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.WORKFLOWS_NETWORK;
@Container
private final static DaprPlacementContainer sharedPlacementContainer = new DaprPlacementContainer(DAPR_PLACEMENT_IMAGE_TAG)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java
index c9932d4e6f..072e754cde 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.testcontainers.workflows.version.full;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.config.Properties;
import io.dapr.it.spring.data.CustomMySQLContainer;
@@ -71,7 +73,7 @@
@Tag("testcontainers")
public class FullVersioningWorkflowsIT {
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.WORKFLOWS_NETWORK;
private static final WaitStrategy MYSQL_WAIT_STRATEGY = Wait
.forLogMessage(".*port: 3306 MySQL Community Server \\(GPL\\).*", 1)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/patch/PatchVersioningWorkflowsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/patch/PatchVersioningWorkflowsIT.java
index 9895ac81fd..e078b5f846 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/patch/PatchVersioningWorkflowsIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/patch/PatchVersioningWorkflowsIT.java
@@ -13,6 +13,8 @@
package io.dapr.it.testcontainers.workflows.version.patch;
+import io.dapr.it.testcontainers.TestContainerNetworks;
+
import io.dapr.config.Properties;
import io.dapr.it.spring.data.CustomMySQLContainer;
import io.dapr.it.testcontainers.ContainerConstants;
@@ -66,7 +68,7 @@
@Tag("testcontainers")
public class PatchVersioningWorkflowsIT {
- private static final Network DAPR_NETWORK = io.dapr.it.testcontainers.TestContainerNetworks.SHARED_NETWORK;
+ private static final Network DAPR_NETWORK = TestContainerNetworks.WORKFLOWS_NETWORK;
private static final WaitStrategy MYSQL_WAIT_STRATEGY = Wait
.forLogMessage(".*port: 3306 MySQL Community Server \\(GPL\\).*", 1)
diff --git a/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java b/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java
index b8e092e1d3..19ef095209 100644
--- a/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/tracing/grpc/TracingIT.java
@@ -1,6 +1,8 @@
package io.dapr.it.tracing.grpc;
import io.dapr.client.DaprClient;
+import io.dapr.client.DaprClientBuilder;
+import io.dapr.config.Properties;
import io.dapr.client.domain.HttpExtension;
import io.dapr.it.tracing.Validation;
import io.dapr.testcontainers.DaprContainer;
@@ -56,9 +58,9 @@ public static void startGrpcApp() throws Exception {
@BeforeEach
public void setup() {
- daprClient = new io.dapr.client.DaprClientBuilder()
- .withPropertyOverride(io.dapr.config.Properties.HTTP_ENDPOINT, "http://localhost:" + DAPR_CONTAINER.getHttpPort())
- .withPropertyOverride(io.dapr.config.Properties.GRPC_ENDPOINT, "http://localhost:" + DAPR_CONTAINER.getGrpcPort())
+ daprClient = new DaprClientBuilder()
+ .withPropertyOverride(Properties.HTTP_ENDPOINT, "http://localhost:" + DAPR_CONTAINER.getHttpPort())
+ .withPropertyOverride(Properties.GRPC_ENDPOINT, "http://localhost:" + DAPR_CONTAINER.getGrpcPort())
.build();
daprClient.waitForSidecar(10000).block();
}
diff --git a/sdk-tests/src/test/java/io/dapr/it/tracing/http/OpenTelemetryConfig.java b/sdk-tests/src/test/java/io/dapr/it/tracing/http/OpenTelemetryConfig.java
index c66e795887..6a9e8642d4 100644
--- a/sdk-tests/src/test/java/io/dapr/it/tracing/http/OpenTelemetryConfig.java
+++ b/sdk-tests/src/test/java/io/dapr/it/tracing/http/OpenTelemetryConfig.java
@@ -13,23 +13,25 @@
package io.dapr.it.tracing.http;
-import io.opentelemetry.api.OpenTelemetry;
-import io.opentelemetry.api.trace.Tracer;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class OpenTelemetryConfig {
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.trace.Tracer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static io.dapr.it.tracing.OpenTelemetry.createOpenTelemetry;
+
+@Configuration
+public class OpenTelemetryConfig {
public static final String TRACER_NAME = "integration testing tracer";
public static final String SERVICE_NAME = "integration testing service over http";
- @Bean
- public OpenTelemetry initOpenTelemetry() throws InterruptedException {
- return io.dapr.it.tracing.OpenTelemetry.createOpenTelemetry(SERVICE_NAME);
- }
+ @Bean
+ public OpenTelemetry initOpenTelemetry() throws InterruptedException {
+ return createOpenTelemetry(SERVICE_NAME);
+ }
@Bean
public Tracer initTracer(@Autowired OpenTelemetry openTelemetry) {
From 0bc36122cd949b052594bf0e3effc5edcb8b2fbd Mon Sep 17 00:00:00 2001
From: Artur Ciocanu
Date: Sun, 22 Feb 2026 21:04:48 -0800
Subject: [PATCH 09/11] Stabilize testcontainer startup ordering and Spring
container binding
Signed-off-by: Artur Ciocanu
---
.../it/configuration/ConfigurationClientIT.java | 6 ++++--
.../dapr/it/pubsub/stream/PubSubStreamIT.java | 5 +++--
.../io/dapr/it/secrets/SecretsClientIT.java | 6 ++++--
.../io/dapr/it/state/GRPCStateClientIT.java | 13 ++++++++-----
.../io/dapr/it/state/HelloWorldClientIT.java | 8 +++++---
.../DaprSpringBootContextInitializer.java | 17 ++++++++++++-----
.../spring/DaprSpringBootExtension.java | 5 ++++-
7 files changed, 40 insertions(+), 20 deletions(-)
diff --git a/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java b/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java
index 81509420fe..46c6ac39ef 100644
--- a/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/configuration/ConfigurationClientIT.java
@@ -47,13 +47,14 @@
public class ConfigurationClientIT {
private static final String CONFIG_STORE_NAME = "redisconfigstore";
+ private static final String REDIS_ALIAS = "configuration-redis";
private static final Network NETWORK = TestContainerNetworks.STATE_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
.withNetwork(NETWORK)
- .withNetworkAliases("redis");
+ .withNetworkAliases(REDIS_ALIAS);
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
@@ -63,7 +64,8 @@ public class ConfigurationClientIT {
CONFIG_STORE_NAME,
"configuration.redis",
"v1",
- Map.of("redisHost", "redis:6379", "redisPassword", "")));
+ Map.of("redisHost", REDIS_ALIAS + ":6379", "redisPassword", "")))
+ .dependsOn(REDIS);
@BeforeAll
public static void init() throws Exception {
diff --git a/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java b/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java
index 65a74e73dc..bdbbc1af0c 100644
--- a/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/pubsub/stream/PubSubStreamIT.java
@@ -55,13 +55,14 @@ public class PubSubStreamIT {
private static final String TOPIC_NAME_CLOUDEVENT = "stream-topic-cloudevent";
private static final String TOPIC_NAME_RAWPAYLOAD = "stream-topic-rawpayload";
private static final String PUBSUB_NAME = "messagebus";
+ private static final String REDIS_ALIAS = "pubsub-stream-redis";
private static final Network NETWORK = TestContainerNetworks.PUBSUB_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
.withNetwork(NETWORK)
- .withNetworkAliases("redis");
+ .withNetworkAliases(REDIS_ALIAS);
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
@@ -73,7 +74,7 @@ public class PubSubStreamIT {
"pubsub.redis",
"v1",
Map.of(
- "redisHost", "redis:6379",
+ "redisHost", REDIS_ALIAS + ":6379",
"redisPassword", "",
"processingTimeout", "100ms",
"redeliverInterval", "100ms")));
diff --git a/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java b/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
index 1ff349c424..d246c908d6 100644
--- a/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
@@ -48,13 +48,14 @@ public class SecretsClientIT {
private static final String KEY1 = UUID.randomUUID().toString();
private static final String KYE2 = UUID.randomUUID().toString();
+ private static final String REDIS_ALIAS = "secrets-redis";
private static final Network NETWORK = TestContainerNetworks.STATE_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
.withNetwork(NETWORK)
- .withNetworkAliases("redis");
+ .withNetworkAliases(REDIS_ALIAS);
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
@@ -64,7 +65,8 @@ public class SecretsClientIT {
SECRETS_STORE_NAME,
"secretstores.redis",
"v1",
- Map.of("redisHost", "redis:6379", "redisPassword", "")));
+ Map.of("redisHost", REDIS_ALIAS + ":6379", "redisPassword", "")))
+ .dependsOn(REDIS);
@BeforeAll
public static void init() throws Exception {
diff --git a/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java b/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java
index 835f2f0579..365af30073 100644
--- a/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/state/GRPCStateClientIT.java
@@ -43,16 +43,18 @@
public class GRPCStateClientIT extends AbstractStateClientIT {
private static final Network NETWORK = TestContainerNetworks.STATE_NETWORK;
+ private static final String REDIS_ALIAS = "grpc-state-redis";
+ private static final String MONGO_ALIAS = "grpc-state-mongo";
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
.withNetwork(NETWORK)
- .withNetworkAliases("redis");
+ .withNetworkAliases(REDIS_ALIAS);
@Container
private static final GenericContainer> MONGO = new GenericContainer<>("mongo:7")
.withNetwork(NETWORK)
- .withNetworkAliases("mongo");
+ .withNetworkAliases(MONGO_ALIAS);
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
@@ -63,7 +65,7 @@ public class GRPCStateClientIT extends AbstractStateClientIT {
"state.redis",
"v1",
Map.of(
- "redisHost", "redis:6379",
+ "redisHost", REDIS_ALIAS + ":6379",
"redisPassword", "",
"actorStateStore", "true")))
.withComponent(new Component(
@@ -71,9 +73,10 @@ public class GRPCStateClientIT extends AbstractStateClientIT {
"state.mongodb",
"v1",
Map.of(
- "host", "mongo:27017",
+ "host", MONGO_ALIAS + ":27017",
"databaseName", "local",
- "collectionName", "testCollection")));
+ "collectionName", "testCollection")))
+ .dependsOn(REDIS, MONGO);
private static DaprClient daprClient;
diff --git a/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java b/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java
index eb347797da..f78f3b7094 100644
--- a/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/state/HelloWorldClientIT.java
@@ -39,13 +39,14 @@
public class HelloWorldClientIT {
private static final String STATE_STORE_NAME = "statestore";
+ private static final String REDIS_ALIAS = "hello-world-redis";
private static final Network NETWORK = TestContainerNetworks.STATE_NETWORK;
@Container
private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
.withNetwork(NETWORK)
- .withNetworkAliases("redis");
+ .withNetworkAliases(REDIS_ALIAS);
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
@@ -56,8 +57,9 @@ public class HelloWorldClientIT {
"state.redis",
"v1",
Map.of(
- "redisHost", "redis:6379",
- "redisPassword", "")));
+ "redisHost", REDIS_ALIAS + ":6379",
+ "redisPassword", "")))
+ .dependsOn(REDIS);
@BeforeAll
public static void waitForSidecar() throws Exception {
diff --git a/sdk-tests/src/test/java/io/dapr/testcontainers/internal/spring/DaprSpringBootContextInitializer.java b/sdk-tests/src/test/java/io/dapr/testcontainers/internal/spring/DaprSpringBootContextInitializer.java
index dc4de7a0fd..6d71cd6946 100644
--- a/sdk-tests/src/test/java/io/dapr/testcontainers/internal/spring/DaprSpringBootContextInitializer.java
+++ b/sdk-tests/src/test/java/io/dapr/testcontainers/internal/spring/DaprSpringBootContextInitializer.java
@@ -39,6 +39,7 @@ public class DaprSpringBootContextInitializer
implements ApplicationContextInitializer {
private static final String PROPERTY_SOURCE_NAME = "daprTestcontainersProperties";
+ private static final String CURRENT_TEST_CLASS_PROPERTY = "dapr.testcontainers.current-test-class";
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
@@ -59,11 +60,17 @@ public void initialize(ConfigurableApplicationContext applicationContext) {
}
private DaprContainer findContainer() {
- // Return the first container in the registry
- // In a test scenario, there should only be one test class running at a time
- return DaprSpringBootExtension.CONTAINER_REGISTRY.values().stream()
- .findFirst()
- .orElse(null);
+ String currentTestClass = System.getProperty(CURRENT_TEST_CLASS_PROPERTY);
+ if (currentTestClass != null) {
+ return DaprSpringBootExtension.CONTAINER_REGISTRY.entrySet().stream()
+ .filter(entry -> entry.getKey().getName().equals(currentTestClass))
+ .map(Map.Entry::getValue)
+ .findFirst()
+ .orElse(null);
+ }
+
+ // Fallback for unexpected bootstrap order.
+ return DaprSpringBootExtension.CONTAINER_REGISTRY.values().stream().findFirst().orElse(null);
}
/**
diff --git a/sdk-tests/src/test/java/io/dapr/testcontainers/internal/spring/DaprSpringBootExtension.java b/sdk-tests/src/test/java/io/dapr/testcontainers/internal/spring/DaprSpringBootExtension.java
index d258f13df1..2d0d418a72 100644
--- a/sdk-tests/src/test/java/io/dapr/testcontainers/internal/spring/DaprSpringBootExtension.java
+++ b/sdk-tests/src/test/java/io/dapr/testcontainers/internal/spring/DaprSpringBootExtension.java
@@ -36,6 +36,7 @@
* This extension is automatically registered when using {@link DaprSpringBootTest}.
*/
public class DaprSpringBootExtension implements BeforeAllCallback {
+ private static final String CURRENT_TEST_CLASS_PROPERTY = "dapr.testcontainers.current-test-class";
/**
* Registry of DaprContainers by test class. Used by {@link DaprSpringBootContextInitializer}
@@ -80,8 +81,10 @@ public void beforeAll(ExtensionContext context) throws Exception {
);
}
- // Register container for the context initializer
+ // Keep a single active registration to avoid stale container lookups across test classes.
+ CONTAINER_REGISTRY.clear();
CONTAINER_REGISTRY.put(testClass, container);
+ System.setProperty(CURRENT_TEST_CLASS_PROPERTY, testClass.getName());
// Note: Testcontainers.exposeHostPorts() is NOT called here because of timing requirements.
// It must be called in @BeforeEach, after the container starts to ensure proper Dapr-to-app communication.
From 823a79bda37def54fd35e034158ff6db20170d5e Mon Sep 17 00:00:00 2001
From: Artur Ciocanu
Date: Mon, 23 Feb 2026 10:42:11 -0800
Subject: [PATCH 10/11] Fix SecretsClientIT for Testcontainers runtime
components
Signed-off-by: Artur Ciocanu
---
.../io/dapr/it/secrets/SecretsClientIT.java | 48 ++++++++-----------
1 file changed, 20 insertions(+), 28 deletions(-)
diff --git a/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java b/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
index d246c908d6..9dc54b4b0f 100644
--- a/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/secrets/SecretsClientIT.java
@@ -14,22 +14,19 @@
package io.dapr.it.secrets;
import io.dapr.it.testcontainers.TestContainerNetworks;
-
import io.dapr.client.DaprClient;
import io.dapr.it.testcontainers.DaprClientFactory;
import io.dapr.testcontainers.Component;
import io.dapr.testcontainers.DaprContainer;
import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
-import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
+import org.testcontainers.images.builder.Transferable;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.Map;
-import java.util.UUID;
import static io.dapr.it.testcontainers.ContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -37,36 +34,39 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
- * Test Secrets Store APIs backed by redis secret store.
+ * Test Secrets Store APIs backed by local file secret store.
*/
@Testcontainers
@Tag("testcontainers")
public class SecretsClientIT {
private static final String SECRETS_STORE_NAME = "localSecretStore";
-
- private static final String KEY1 = UUID.randomUUID().toString();
-
- private static final String KYE2 = UUID.randomUUID().toString();
- private static final String REDIS_ALIAS = "secrets-redis";
+ private static final String SECRET_FILE_PATH = "/dapr-resources/secrets.json";
+
+ private static final String KEY1 = "metrics";
+ private static final String KEY2 = "person";
+ private static final String SECRET_FILE_CONTENT = "{\n"
+ + " \"" + KEY1 + "\": {\n"
+ + " \"title\": \"The Metrics IV\",\n"
+ + " \"year\": \"2020\"\n"
+ + " },\n"
+ + " \"" + KEY2 + "\": {\n"
+ + " \"name\": \"Jon Doe\"\n"
+ + " }\n"
+ + "}\n";
private static final Network NETWORK = TestContainerNetworks.STATE_NETWORK;
- @Container
- private static final GenericContainer> REDIS = new GenericContainer<>("redis:7-alpine")
- .withNetwork(NETWORK)
- .withNetworkAliases(REDIS_ALIAS);
-
@Container
private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
.withNetwork(NETWORK)
.withAppName("secrets-it")
+ .withCopyToContainer(Transferable.of(SECRET_FILE_CONTENT), SECRET_FILE_PATH)
.withComponent(new Component(
SECRETS_STORE_NAME,
- "secretstores.redis",
+ "secretstores.local.file",
"v1",
- Map.of("redisHost", REDIS_ALIAS + ":6379", "redisPassword", "")))
- .dependsOn(REDIS);
+ Map.of("secretsFile", SECRET_FILE_PATH, "multiValued", "true")));
@BeforeAll
public static void init() throws Exception {
@@ -75,14 +75,6 @@ public static void init() throws Exception {
}
}
- @BeforeEach
- public void setup() throws Exception {
- REDIS.execInContainer("redis-cli", "DEL", KEY1);
- REDIS.execInContainer("redis-cli", "DEL", KYE2);
- REDIS.execInContainer("redis-cli", "HSET", KEY1, "title", "The Metrics IV", "year", "2020");
- REDIS.execInContainer("redis-cli", "HSET", KYE2, "name", "Jon Doe");
- }
-
@Test
public void getSecret() throws Exception {
try (DaprClient daprClient = DaprClientFactory.createDaprClientBuilder(DAPR_CONTAINER).build()) {
@@ -101,8 +93,8 @@ public void getBulkSecret() throws Exception {
assertEquals(2, data.get(KEY1).size());
assertEquals("The Metrics IV", data.get(KEY1).get("title"));
assertEquals("2020", data.get(KEY1).get("year"));
- assertEquals(1, data.get(KYE2).size());
- assertEquals("Jon Doe", data.get(KYE2).get("name"));
+ assertEquals(1, data.get(KEY2).size());
+ assertEquals("Jon Doe", data.get(KEY2).get("name"));
}
}
From 073cc89a219e276c46c20cc90f6df61b8cc1eebd Mon Sep 17 00:00:00 2001
From: Artur Ciocanu
Date: Mon, 23 Feb 2026 19:25:08 -0800
Subject: [PATCH 11/11] Harden full workflow versioning integration test
cutover
Signed-off-by: Artur Ciocanu
---
.../full/FullVersioningWorkflowsIT.java | 189 ++++++++++++------
1 file changed, 125 insertions(+), 64 deletions(-)
diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java
index 072e754cde..c48334840a 100644
--- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java
+++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/workflows/version/full/FullVersioningWorkflowsIT.java
@@ -13,12 +13,10 @@
package io.dapr.it.testcontainers.workflows.version.full;
-import io.dapr.it.testcontainers.TestContainerNetworks;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.config.Properties;
import io.dapr.it.spring.data.CustomMySQLContainer;
import io.dapr.it.testcontainers.ContainerConstants;
+import io.dapr.it.testcontainers.TestContainerNetworks;
import io.dapr.it.testcontainers.workflows.TestWorkflowsApplication;
import io.dapr.it.testcontainers.workflows.TestWorkflowsConfiguration;
import io.dapr.testcontainers.Component;
@@ -27,13 +25,10 @@
import io.dapr.testcontainers.DaprPlacementContainer;
import io.dapr.testcontainers.DaprSchedulerContainer;
import io.dapr.workflows.client.DaprWorkflowClient;
+import io.dapr.workflows.client.WorkflowRuntimeStatus;
import io.dapr.workflows.client.WorkflowState;
-import io.dapr.workflows.runtime.WorkflowRuntime;
-import io.dapr.workflows.runtime.WorkflowRuntimeBuilder;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.DynamicPropertyRegistry;
@@ -45,10 +40,10 @@
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
-import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.MountableFile;
import java.time.Duration;
+import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
@@ -57,10 +52,8 @@
import static io.dapr.it.testcontainers.ContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
import static io.dapr.testcontainers.DaprContainerConstants.DAPR_PLACEMENT_IMAGE_TAG;
import static io.dapr.testcontainers.DaprContainerConstants.DAPR_SCHEDULER_IMAGE_TAG;
-import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
@SpringBootTest(
webEnvironment = WebEnvironment.RANDOM_PORT,
@@ -73,6 +66,13 @@
@Tag("testcontainers")
public class FullVersioningWorkflowsIT {
+ private static final Duration WORKFLOW_START_TIMEOUT = Duration.ofSeconds(20);
+ private static final Duration WORKFLOW_COMPLETION_TIMEOUT = Duration.ofSeconds(20);
+ private static final Duration ACTIVITY1_EXECUTION_TIMEOUT = Duration.ofSeconds(30);
+ private static final Duration STATE_VISIBILITY_TIMEOUT = Duration.ofSeconds(30);
+ private static final Duration EVENT_RETRY_TIMEOUT = Duration.ofSeconds(30);
+ private static final Duration POLL_INTERVAL = Duration.ofMillis(250);
+
private static final Network DAPR_NETWORK = TestContainerNetworks.WORKFLOWS_NETWORK;
private static final WaitStrategy MYSQL_WAIT_STRATEGY = Wait
@@ -92,16 +92,18 @@ public class FullVersioningWorkflowsIT {
.waitingFor(MYSQL_WAIT_STRATEGY);
@Container
- private final static DaprPlacementContainer sharedPlacementContainer = new DaprPlacementContainer(DAPR_PLACEMENT_IMAGE_TAG)
- .withNetwork(DAPR_NETWORK)
- .withNetworkAliases("placement")
- .withReuse(false);
+ private static final DaprPlacementContainer SHARED_PLACEMENT_CONTAINER =
+ new DaprPlacementContainer(DAPR_PLACEMENT_IMAGE_TAG)
+ .withNetwork(DAPR_NETWORK)
+ .withNetworkAliases("placement")
+ .withReuse(false);
@Container
- private final static DaprSchedulerContainer sharedSchedulerContainer = new DaprSchedulerContainer(DAPR_SCHEDULER_IMAGE_TAG)
- .withNetwork(DAPR_NETWORK)
- .withNetworkAliases("scheduler")
- .withReuse(false);
+ private static final DaprSchedulerContainer SHARED_SCHEDULER_CONTAINER =
+ new DaprSchedulerContainer(DAPR_SCHEDULER_IMAGE_TAG)
+ .withNetwork(DAPR_NETWORK)
+ .withNetworkAliases("scheduler")
+ .withReuse(false);
@Container
private static final DaprContainer DAPR_CONTAINER_V1 = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
@@ -109,27 +111,27 @@ public class FullVersioningWorkflowsIT {
.withNetworkAliases("dapr-worker-v1")
.withNetwork(DAPR_NETWORK)
.withComponent(new Component(STATE_STORE_NAME, "state.mysql", "v1", STATE_STORE_PROPERTIES))
- .withPlacementContainer(sharedPlacementContainer)
- .withSchedulerContainer(sharedSchedulerContainer)
+ .withPlacementContainer(SHARED_PLACEMENT_CONTAINER)
+ .withSchedulerContainer(SHARED_SCHEDULER_CONTAINER)
.withDaprLogLevel(DaprLogLevel.DEBUG)
- .withLogConsumer(outputFrame -> System.out.println("daprV1 -> " +outputFrame.getUtf8String()))
+ .withLogConsumer(outputFrame -> System.out.println("daprV1 -> " + outputFrame.getUtf8String()))
.withAppChannelAddress("host.testcontainers.internal")
- .dependsOn(MY_SQL_CONTAINER, sharedPlacementContainer, sharedSchedulerContainer);
+ .dependsOn(MY_SQL_CONTAINER, SHARED_PLACEMENT_CONTAINER, SHARED_SCHEDULER_CONTAINER);
private static final DaprContainer DAPR_CONTAINER_V2 = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
.withAppName("dapr-worker")
.withNetworkAliases("dapr-worker-v2")
.withNetwork(DAPR_NETWORK)
.withComponent(new Component(STATE_STORE_NAME, "state.mysql", "v1", STATE_STORE_PROPERTIES))
- .withPlacementContainer(sharedPlacementContainer)
- .withSchedulerContainer(sharedSchedulerContainer)
+ .withPlacementContainer(SHARED_PLACEMENT_CONTAINER)
+ .withSchedulerContainer(SHARED_SCHEDULER_CONTAINER)
.withDaprLogLevel(DaprLogLevel.DEBUG)
.withLogConsumer(outputFrame -> System.out.println("daprV2 -> " + outputFrame.getUtf8String()))
.withAppChannelAddress("host.testcontainers.internal")
- .dependsOn(MY_SQL_CONTAINER, sharedPlacementContainer, sharedSchedulerContainer);
+ .dependsOn(MY_SQL_CONTAINER, SHARED_PLACEMENT_CONTAINER, SHARED_SCHEDULER_CONTAINER);
@Container
- private final static GenericContainer> workerV1 = new GenericContainer<>(ContainerConstants.JDK_17_TEMURIN_JAMMY)
+ private static final GenericContainer> WORKER_V1 = new GenericContainer<>(ContainerConstants.JDK_17_TEMURIN_JAMMY)
.withCopyFileToContainer(MountableFile.forHostPath("target"), "/app")
.withWorkingDirectory("/app")
.withCommand("java", "-cp", "test-classes:classes:dependency/*:*",
@@ -142,8 +144,8 @@ public class FullVersioningWorkflowsIT {
.waitingFor(Wait.forLogMessage(".*WorkerV1 started.*", 1))
.withLogConsumer(outputFrame -> System.out.println("WorkerV1: " + outputFrame.getUtf8String()));
-// This container will be started manually
- private final static GenericContainer> workerV2 = new GenericContainer<>(ContainerConstants.JDK_17_TEMURIN_JAMMY)
+ // This container is started manually during cutover.
+ private static final GenericContainer> WORKER_V2 = new GenericContainer<>(ContainerConstants.JDK_17_TEMURIN_JAMMY)
.withCopyFileToContainer(MountableFile.forHostPath("target"), "/app")
.withWorkingDirectory("/app")
.withCommand("java", "-cp", "test-classes:classes:dependency/*:*",
@@ -156,15 +158,12 @@ public class FullVersioningWorkflowsIT {
.waitingFor(Wait.forLogMessage(".*WorkerV2 started.*", 1))
.withLogConsumer(outputFrame -> System.out.println("WorkerV2: " + outputFrame.getUtf8String()));
-
private static Map createStateStoreProperties() {
Map result = new HashMap<>();
-
result.put("keyPrefix", "name");
result.put("schemaName", "dapr_db");
result.put("actorStateStore", "true");
result.put("connectionString", STATE_STORE_DSN);
-
return result;
}
@@ -176,53 +175,115 @@ static void daprProperties(DynamicPropertyRegistry registry) {
@Test
public void testWorkflows() throws Exception {
- DaprWorkflowClient workflowClientV1 = daprWorkflowClient(DAPR_CONTAINER_V1.getHttpEndpoint(), DAPR_CONTAINER_V1.getGrpcEndpoint());
-// Start workflow V1
- String instanceIdV1 = workflowClientV1.scheduleNewWorkflow("VersionWorkflow");
- workflowClientV1.waitForWorkflowStart(instanceIdV1, Duration.ofSeconds(10), false);
+ String instanceIdV1;
+ try (DaprWorkflowClient workflowClientV1 =
+ daprWorkflowClient(DAPR_CONTAINER_V1.getHttpEndpoint(), DAPR_CONTAINER_V1.getGrpcEndpoint())) {
+ instanceIdV1 = workflowClientV1.scheduleNewWorkflow("VersionWorkflow");
+ workflowClientV1.waitForWorkflowStart(instanceIdV1, WORKFLOW_START_TIMEOUT, false);
+ waitForContainerLog(WORKER_V1, "Activity1 called.", ACTIVITY1_EXECUTION_TIMEOUT);
+ }
- // Stop worker and dapr
- workerV1.stop();
+ WORKER_V1.stop();
DAPR_CONTAINER_V1.stop();
- // Start new worker with patched workflow
DAPR_CONTAINER_V2.start();
- workerV2.start();
- Thread.sleep(1000);
- DaprWorkflowClient workflowClientV2 = daprWorkflowClient(DAPR_CONTAINER_V2.getHttpEndpoint(), DAPR_CONTAINER_V2.getGrpcEndpoint());
-
- // Start workflow V2
- String instanceIdV2 = workflowClientV2.scheduleNewWorkflow("VersionWorkflow");
- workflowClientV2.waitForWorkflowStart(instanceIdV2, Duration.ofSeconds(10), false);
+ WORKER_V2.start();
- // Continue workflow V1
- workflowClientV2.raiseEvent(instanceIdV1, "test", null);
+ try (DaprWorkflowClient workflowClientV2 =
+ daprWorkflowClient(DAPR_CONTAINER_V2.getHttpEndpoint(), DAPR_CONTAINER_V2.getGrpcEndpoint())) {
+ String instanceIdV2 = workflowClientV2.scheduleNewWorkflow("VersionWorkflow");
+ workflowClientV2.waitForWorkflowStart(instanceIdV2, WORKFLOW_START_TIMEOUT, false);
- // Wait for workflow to complete
- Duration timeout = Duration.ofSeconds(10);
- WorkflowState workflowStatusV1 = workflowClientV2.waitForWorkflowCompletion(instanceIdV1, timeout, true);
- WorkflowState workflowStatusV2 = workflowClientV2.waitForWorkflowCompletion(instanceIdV2, timeout, true);
+ waitForWorkflowStateVisibility(workflowClientV2, instanceIdV1, STATE_VISIBILITY_TIMEOUT);
+ raiseEventWithRetry(workflowClientV2, instanceIdV1, "test", null, EVENT_RETRY_TIMEOUT);
- assertNotNull(workflowStatusV1);
- assertNotNull(workflowStatusV2);
+ WorkflowState workflowStatusV1 =
+ workflowClientV2.waitForWorkflowCompletion(instanceIdV1, WORKFLOW_COMPLETION_TIMEOUT, true);
+ WorkflowState workflowStatusV2 =
+ workflowClientV2.waitForWorkflowCompletion(instanceIdV2, WORKFLOW_COMPLETION_TIMEOUT, true);
- String resultV1 = workflowStatusV1.readOutputAs(String.class);
- assertEquals("Activity1, Activity2", resultV1);
-
- String resultV2 = workflowStatusV2.readOutputAs(String.class);
- assertEquals("Activity3, Activity4", resultV2);
+ assertNotNull(workflowStatusV1);
+ assertNotNull(workflowStatusV2);
+ assertEquals("Activity1, Activity2", workflowStatusV1.readOutputAs(String.class));
+ assertEquals("Activity3, Activity4", workflowStatusV2.readOutputAs(String.class));
+ }
}
- public DaprWorkflowClient daprWorkflowClient(
- String daprHttpEndpoint,
- String daprGrpcEndpoint
- ){
+ public DaprWorkflowClient daprWorkflowClient(String daprHttpEndpoint, String daprGrpcEndpoint) {
Map overrides = Map.of(
"dapr.http.endpoint", daprHttpEndpoint,
"dapr.grpc.endpoint", daprGrpcEndpoint
);
-
return new DaprWorkflowClient(new Properties(overrides));
}
-}
+ private static void waitForContainerLog(
+ GenericContainer> container,
+ String expectedText,
+ Duration timeout
+ ) throws InterruptedException {
+ Instant deadline = Instant.now().plus(timeout);
+ while (Instant.now().isBefore(deadline)) {
+ if (container.getLogs().contains(expectedText)) {
+ return;
+ }
+ Thread.sleep(POLL_INTERVAL.toMillis());
+ }
+ throw new AssertionError("Timed out waiting for container log: " + expectedText);
+ }
+
+ private static void waitForWorkflowStateVisibility(
+ DaprWorkflowClient workflowClient,
+ String instanceId,
+ Duration timeout
+ ) throws InterruptedException {
+ Instant deadline = Instant.now().plus(timeout);
+ while (Instant.now().isBefore(deadline)) {
+ WorkflowState workflowState = workflowClient.getWorkflowState(instanceId, false);
+ if (workflowState != null && workflowState.getRuntimeStatus() != WorkflowRuntimeStatus.PENDING) {
+ return;
+ }
+ Thread.sleep(POLL_INTERVAL.toMillis());
+ }
+ throw new AssertionError("Timed out waiting for workflow state visibility for instance: " + instanceId);
+ }
+
+ private static void raiseEventWithRetry(
+ DaprWorkflowClient workflowClient,
+ String instanceId,
+ String eventName,
+ Object payload,
+ Duration timeout
+ ) throws InterruptedException {
+ Instant deadline = Instant.now().plus(timeout);
+ RuntimeException lastException = null;
+ while (Instant.now().isBefore(deadline)) {
+ try {
+ workflowClient.raiseEvent(instanceId, eventName, payload);
+ return;
+ } catch (RuntimeException ex) {
+ if (!isTransientActorResolutionFailure(ex)) {
+ throw ex;
+ }
+ lastException = ex;
+ Thread.sleep(POLL_INTERVAL.toMillis());
+ }
+ }
+
+ throw new AssertionError("Timed out raising event for workflow instance " + instanceId, lastException);
+ }
+
+ private static boolean isTransientActorResolutionFailure(Throwable throwable) {
+ Throwable current = throwable;
+ while (current != null) {
+ String message = current.getMessage();
+ if (message != null
+ && (message.contains("did not find address for actor")
+ || message.contains("DaprBuiltInActorNotFoundRetries"))) {
+ return true;
+ }
+ current = current.getCause();
+ }
+ return false;
+ }
+}