Skip to content

Fix Flaky Integration Tests + Migrate some tests to Test Containers#1754

Merged
siri-varma merged 70 commits into
dapr:masterfrom
siri-varma:users/sveigraju/fix-integ-tests-2
Jul 9, 2026
Merged

Fix Flaky Integration Tests + Migrate some tests to Test Containers#1754
siri-varma merged 70 commits into
dapr:masterfrom
siri-varma:users/sveigraju/fix-integ-tests-2

Conversation

@siri-varma

@siri-varma siri-varma commented May 25, 2026

Copy link
Copy Markdown
Contributor

Phase 1 — Fix Flaky Integration Tests + Migrate Some ITs to Testcontainers

What this PR does

  1. Fixes flakes in the existing sdk-tests harness (actor registration race, tracing span-name mismatch, streaming subscription reconnect).
  2. Migrates 13 ITs off the legacy BaseIT/DaprRun harness (which requires a host-installed Dapr CLI + dapr init) onto Testcontainers via DaprContainer, matching the spring-boot-4-sdk-tests module.

Notable behavior fixes

  • Actor IT registration timeoutBaseContainerIT.startAppAndAttach now starts AppRun before the daprd container, with daprd's HTTP/gRPC host ports pinned (via setPortBindings) to the same DaprPorts values the app's env already received. Previously daprd would probe the app channel before the JVM had bound the host port, fetch /dapr/config against the SSH bridge, get nothing, report actor types [] to placement, and never re-query.
  • Tracing span name — Zipkin records calllocal/<APP_NAME>/<method>; the assertion expected the bogus <APP_NAME>-service suffix. Fixed in both grpc and http TracingITs.
  • Streaming subscription reconnect (sdk/src/main/java/io/dapr/client/Subscription.java) — receiver thread now releases the state-change semaphore on onError, sleeps with a doubled-long backoff (1s → 30s cap, no overflow path), and is interrupted on close() so shutdown isn't blocked by a backoff sleep.

Files deleted in this PR — and why

File Lines Replacement Notes
pubsub/http/PubSubIT.java 723 testcontainers/pubsub/http/DaprPubSubIT.java Legacy BaseIT harness. Method mapping: publishPubSubNotFoundshouldReceiveInvalidArgument, testBulkPublishPubSubNotFoundshouldReceiveInvalidArgumentWithBulkPublish, testBulkPublishshouldPublishSomePayloadTypesWithNoError, testPubSubtestPubSub, testPubSubBinaryshouldPublishBinary, testPubSubTTLMetadatatestPubSubTTLMetadata, testLongValuestestLongValues. testPubSubBulkSubscribe was missing — added on the migrated DaprPubSubIT in this PR.
pubsub/http/SubscriberController.java 265 (n/a) Companion subprocess for legacy PubSubIT. The Testcontainers IT runs the Spring Boot subscriber inside the test JVM, so a separate process file isn't needed; a different SubscriberController already lives under testcontainers/pubsub/http/.
pubsub/http/SubscriberService.java 46 (n/a) main() entry-point that spawned the legacy subscriber. No replacement needed.
pubsub/stream/PubSubStreamIT.java 339 testcontainers/pubsub/stream/DaprPubSubStreamIT.java testPubSubDeadLetterTopic was missing — added on the migrated DaprPubSubStreamIT in this PR.
state/HelloWorldClientIT.java 74 state/GRPCStateClientIT.rawGrpcStubGetAndDeleteState Single test of DaprClient.newGrpcStub(...) (raw DaprGrpc.DaprBlockingStub save/get/delete). Restored as a method on the migrated GRPCStateClientIT — no companion subprocess needed.
state/HelloWorldGrpcStateService.java 68 (n/a) Companion subprocess for HelloWorldClientIT.

Mongo state store

AbstractStateClientIT.saveAndQueryAndDeleteState (Dapr preview Query State API, requires Mongo) was briefly @Disabled during the migration since Redis doesn't support that API and we'd dropped the docker-compose Mongo service. Now restored: SharedTestInfra.mongo() runs a pinned mongo:7 Testcontainer, and BaseContainerIT.mongoStateStore(...) registers a state.mongodb component on GRPCStateClientIT.

Container image pinning

SharedTestInfra now pins all three shared containers (redis:7-alpine, mongo:7, openzipkin/zipkin:3.4) instead of letting any of them float on :latest.

ITs intentionally NOT migrated (per spec Non-Goals)

BindingIT, ActorReminderFailoverIT, ActorReminderRecoveryIT, ActorTimerRecoveryIT, ActorStateIT, WaitForSidecarIT, ActorSdkResiliencyIT, plus 2 ITs in durabletask-client/. These remain on the legacy BaseIT/DaprRun harness.

Checklist

  • Code compiles correctly
  • Created/updated tests
  • Extended the documentation

siri-varma and others added 16 commits May 25, 2026 09:38
Port the BookTrip compensation (Saga) workflow from the plain Java
examples into the Spring Boot workflow patterns module, adding
@Component-annotated activities and a /wfp/compensation REST endpoint.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Spec for migrating 13 sdk-tests integration tests from the dapr-run
based BaseIT/DaprRun/AppRun harness to Testcontainers via DaprContainer.
Captures architecture, startup ordering, per-IT plan, CI changes, and
the 8 non-migrated ITs that stay on DaprRun.


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
- Move ActorStateIT out of migration list (it's a sidecar-restart test).
  Migration count: 12 logical ITs / 13 files (9 non-migrated).
- Add D9: per-class @BeforeAll lifecycle for all migrated ITs, with
  TracingIT mitigation (per-test trace IDs).
- Add D10: each migrated IT subclass owns its own static DaprContainer
  + AppRun fields; BaseContainerIT provides helpers only, no fields.
- Reconcile IT counts throughout (12 logical, 13 files migrated,
  9 non-migrated, 22 total).


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
19-task plan covering: SharedTestInfra + BaseContainerIT foundation,
AppRun port-override overload, 13 IT migrations (5 easy + 4 actor +
2 method-invoke + 2 tracing), CI compose-up trim, and push/observe.


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
- Define final startAppAndAttach signature (returning DaprAndApp record)
  directly in Task 3 instead of refactoring it in Task 9.
- Drop the startApp tombstone that threw UnsupportedOperationException.
- Drop the unused newActorClient(dapr, ResiliencyOptions) overload — no
  migrated actor IT calls it (ActorSdkResiliencyIT is non-migrated).
- Fix Task 15's Configuration constructor: it takes 3 args (name,
  tracing, appHttpPipeline); pass null for the last.


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Provides a JVM-wide Network and lazy Redis container shared across all
migrated integration tests. Uses withReuse(true) for dev-loop speed.


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Lets BaseContainerIT point the spawned app subprocess at a Testcontainer
DaprContainer's mapped HTTP/gRPC ports. Existing callers untouched.


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
- Add Apache 2.0 license header to new files (matches package convention).
- Change AppRun's new constructor overrides from Integer to int to remove
  NPE risk on a package-private API.


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
BaseContainerIT lives in io.dapr.it.containers and Java packages are not
hierarchical, so the constructor needs public visibility to be callable
from the sub-package. Caught during Task 3 implementation.


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Provides daprBuilder, startAppAndAttach (returning DaprAndApp record),
newDaprClient(dapr), Component factories, and @afterall cleanup. Each
subclass owns its own static DaprContainer + AppRun fields (D10 from
the spec).

Smoke test boots a no-component DaprContainer to verify the helper
plumbing end-to-end.


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Document the single-thread JUnit lifecycle assumption on the cleanup
deques, clarify withReusablePlacement vs SharedTestInfra's withReuse,
and distinguish the two deferStop overloads.


Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
@siri-varma siri-varma force-pushed the users/sveigraju/fix-integ-tests-2 branch from dc20276 to 280c572 Compare May 25, 2026 16:40
siri-varma added 13 commits May 25, 2026 09:42
Boots Dapr via DaprContainer with secretstores.local.file pointing at a
JSON payload generated in-memory and injected via withCopyToContainer
(Transferable.of). No host file involvement.

Drops initSecretFile/clearSecretFile/LOCAL_SECRET_FILE_PATH — workarounds
for the old DaprRun harness writing to a shared host file.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Replaces double-brace HashMap initialization with Map.of to set a
cleaner template for the 12 downstream IT migrations. Drops unused
HashMap import.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Replaces in-method startDaprApp + run.checkRunState polling with
a per-class DaprContainer in @BeforeAll and a dapr.isRunning() poll
to verify shutdown.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Boots Dapr via DaprContainer with the redisConfigStore component, seeds
the shared SharedTestInfra Redis via Jedis (replacing the previous
docker-exec redis-cli shell-out which targeted the dapr_redis container).

Adds jedis 5.1.0 as a sdk-tests test dependency.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
AbstractStateClientIT now extends BaseContainerIT instead of BaseIT.
GRPCStateClientIT uses DaprContainer + redisStateStore (actor store
enabled) per-class via @BeforeAll. The single MongoDB-dependent test
(saveAndQueryAndDeleteState) is @disabled — out of scope for the
Testcontainers migration per spec D9 / Non-Goals.

QUERY_STATE_STORE constant is moved from the inherited BaseIT into
AbstractStateClientIT locally since the @disabled test still references
it (kept compilable for the eventual MongoDB-on-Testcontainers follow-up).

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Lazy-init openzipkin/zipkin on the shared Network with alias 'zipkin'.
Used by TracingIT migrations in Phase 6 of the Testcontainers migration.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Uses startAppAndAttach to spawn MyActorService in a subprocess and wire
the DaprContainer's appPort/appChannelAddress to host.testcontainers.internal.

Adds a newActorClient(dapr, metadata) overload to BaseContainerIT for
the second test which overrides Content-Length via actor metadata.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Lifecycle shifts from in-method startDaprApp to per-class @BeforeAll
using startAppAndAttach with DemoActorService.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Per-class @BeforeAll using startAppAndAttach with MyActorService.
buildManagedChannel reads gRPC port from dapr.getGrpcPort() (the
DaprContainer's mapped port) instead of the global Properties.GRPC_PORT
which would default to 50001 and miss the container's ephemeral port.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Per-class @BeforeAll using startAppAndAttach with MyActorService.
Both proxy builders share the per-class actorClient.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
@beforeeach -> @BeforeAll per spec D9. The two mutating @test methods
target different endpoints (/messages vs /persons) so per-class lifecycle
is safe. Other three tests are stateless.

App name is now the literal "methodinvoke-http-it" (kebab-case, replacing
the prior "MethodInvokeIThttp" auto-generated from class name).

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
@beforeeach -> @BeforeAll per spec D9. DaprContainer told to speak gRPC
to the app via .withAppProtocol(DaprProtocol.GRPC). createGrpcStub is
now private static using APP_NAME literal.

Only one test (testInvoke) mutates the message store; the two sleep
tests are stateless. Per-class lifecycle is safe.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Wires Zipkin tracing via Configuration + ZipkinTracingConfigurationSettings
so Dapr pushes spans to the shared Zipkin container (alias zipkin:9411).
The test JVM's OpenTelemetry SDK and the Validation REST query both use
the host-mapped Zipkin port.

Adds zipkin-URL-aware overloads to OpenTelemetry.createOpenTelemetry and
Validation.validate so the migrated IT can pass the testcontainerized
host:port. The legacy single-Zipkin-URL overloads remain unchanged for
back-compat.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
@siri-varma

Copy link
Copy Markdown
Contributor Author

@javier-aliaga / @cicoyle / @dapr/maintainers-java-sdk This PR is ready to be reveiwed. Ran the tests 5-6 times and all the instances succeeded.

So, we should definitely see a reduction in number of failures due to flakiness

@siri-varma

Copy link
Copy Markdown
Contributor Author

I think next task would be to see how we can reduce the tests runtime to lower than 20 mins

The legacy BindingIT.httpOutputBindingError test asserted a 404 response
from `https://api.github.com/unknown_path`. GitHub now returns 403 for
unauthenticated requests to unknown paths, so the assertion fails.

httpbin.org/status/404 is purpose-built to return whatever status code
you ask for, eliminating the GitHub API-policy coupling. The component
name stays as `github-http-binding-404` to avoid churning unrelated
references in the test code — the historical name is harmless.

Long-term, this test should be migrated to WireMock so the upstream
behavior is fully under test control, but the URL swap unblocks CI now.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
This reverts commit f1efe9f.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
@siri-varma siri-varma force-pushed the users/sveigraju/fix-integ-tests-2 branch from 8202704 to 3e60255 Compare May 26, 2026 21:24
@siri-varma siri-varma self-assigned this May 26, 2026
@siri-varma

Copy link
Copy Markdown
Contributor Author

@javier-aliaga / @cicoyle Just a bump on this PR. Would like to merge and get as many runs as possible to get data points

Comment thread sdk/src/main/java/io/dapr/client/Subscription.java
Comment thread sdk/src/main/java/io/dapr/client/Subscription.java Outdated
…ream on per-event error

Addresses two review comments from Javier on PR dapr#1754:

1. Backoff was doubled up to 30s on every reconnect but never reset.
   Once a healthy stream disconnected after running for a while, the
   next reconnect (and every one thereafter) waited the capped 30s. Now
   the backoff resets to 1s if the just-terminated stream delivered at
   least one event.

2. The onNext catch-all called this.onError(...) which released the
   receiverStateChange semaphore and triggered a full reconnect on any
   per-event failure (e.g., a bad payload). It also did not actually
   cancel the gRPC stream. Now we just notify the listener via
   listener.onError(...) and keep the stream alive -- one bad event
   should not tear down the whole subscription.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
Reverts all branch-local edits to sdk/src/main/java/io/dapr/client/
Subscription.java (commits 2dfc129, 52ba75f, 783a77d, ed8b398)
so the file matches upstream/master. The backoff/error-handling
changes will be revisited separately -- keeping this PR focused on
the Testcontainers migration.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
…andling

Restores ed8b398 (which was reverted in 731f319). Reverting to
master caused failures; adding back both changes:

1. Reset reconnect backoff to 1s if the just-terminated stream had
   delivered at least one event (tracked per-stream via
   streamReceivedEvent).
2. On per-event errors in onNext, call listener.onError(...) instead
   of this.onError(...) so a single bad event does not tear down the
   whole subscription.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
New sdk/src/test/java/io/dapr/client/SubscriptionTest.java covers:
- perEventExceptionKeepsStreamAlive: an exception thrown while
  handling an event must notify the listener without triggering a
  reconnect of the whole subscription.
- backoffResetsAfterHealthyStreamDisconnect: reconnect backoff
  escalates on empty streams but resets to 1s once a stream has
  delivered at least one event.
- closeInterruptsReconnectBackoff: close() must unblock the receiver
  even while it is sleeping in its reconnect backoff, so shutdown is
  not delayed by up to 30s.

Subscription.close() now also interrupts the receiver thread so the
third test passes and so real callers aren't blocked waiting for the
receiver's Thread.sleep(backoffMs) to return.

Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com>
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.33333% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.00%. Comparing base (3c12975) to head (aa2a7f9).

Files with missing lines Patch % Lines
sdk/src/main/java/io/dapr/client/Subscription.java 73.33% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1754      +/-   ##
============================================
+ Coverage     76.93%   77.00%   +0.07%     
- Complexity     2318     2322       +4     
============================================
  Files           245      245              
  Lines          7180     7194      +14     
  Branches        756      759       +3     
============================================
+ Hits           5524     5540      +16     
+ Misses         1289     1288       -1     
+ Partials        367      366       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@siri-varma

Copy link
Copy Markdown
Contributor Author

@javier-aliaga Addressed the comments on this

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 47 out of 48 changed files in this pull request and generated 8 comments.

Comments suppressed due to low confidence (2)

sdk-tests/src/test/java/io/dapr/it/testcontainers/jobs/DaprJobsIT.java:173

  • Same issue for TTL: Instant#toString() can omit .000 and make this assertion flaky. Format the response TTL with the same formatter (or compare Instants truncated to millis).
    spring-boot-4-sdk-tests/src/test/java/io/dapr/it/springboot4/testcontainers/jobs/DaprJobsIT.java:173
  • Same issue for TTL: Instant#toString() can omit .000 and make this assertion flaky. Format the response TTL with the same formatter (or compare Instants truncated to millis).

Comment thread sdk/src/main/java/io/dapr/client/Subscription.java

@javier-aliaga javier-aliaga left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will approve, copilot comments are in test file, I will let up to you to fix it or leave as is, I have removed the auto-merge so you can take a look and click the button if you want

@siri-varma

Copy link
Copy Markdown
Contributor Author

I will address them in the subsequent pr. Thank you for the quick review Javier.

@siri-varma siri-varma merged commit 3c9875a into dapr:master Jul 9, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants