Skip to content

test(postgresql): add Orleans smoke test pinned to OrleansVersion - #5

Merged
snovak7 merged 2 commits into
mainfrom
test/orleans-smoke
Sep 27, 2026
Merged

snovak7 merged 2 commits into
mainfrom
test/orleans-smoke

Conversation

@snovak7

@snovak7 snovak7 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a smoke test that runs a real Orleans silo against a freshly migrated database, using the Orleans version the schema claims to match. This is the optional item left over from #2.

  • Orleans packages (Microsoft.Orleans.Server, .Clustering.AdoNet, .Persistence.AdoNet, .Reminders.AdoNet) are pinned to $(OrleansVersion) in Directory.Packages.props, so bumping OrleansVersion also bumps the Orleans the tests run against.

  • Three tests, each starting a silo that uses all three ADO.NET providers:

    Test What it checks
    SiloJoinsAndLeavesCluster The silo is active (via IManagementGrain and the membership table) and marked dead after shutdown.
    GrainStorageWritesReadsAndClears State is written, read back after deactivation, and the row is deleted on clear (DeleteStateOnClear).
    RemindersRegisterAndUnregister A reminder is registered, found, and unregistered, with the table row appearing and disappearing.
  • README mentions the smoke test and that its Orleans packages follow OrleansVersion.

Notes

  • Run against the old platform schema (migrated only up to 20230510224234), the silo fails to start with "DeleteStateOnClear=true is not supported … check persistence scripts". So the smoke test catches real Orleans incompatibilities, not just schema shape.
  • Test code refers to Orleans types as global::Orleans…, because inside the Escendit.Orleans… namespace a plain Orleans resolves to the package's own namespace.

Test plan

  • dotnet restore --locked-mode and dotnet build -c Release pass with no warnings
  • dotnet test -c Release locally: 20 passed
  • CI passes on ubuntu-latest

🤖 Generated with Claude Code

Summary by Sourcery

Validate PostgreSQL Orleans schema compatibility by exercising a real, OrleansVersion-matched silo across all ADO.NET providers.

New Features:

  • Add PostgreSQL smoke tests that run a version-matched Orleans silo against a freshly migrated schema and exercise clustering, grain storage, and reminders.

Enhancements:

  • Add database assertions for silo membership, grain storage, and reminder rows.
  • Pin Orleans test packages to the schema's OrleansVersion.

Build:

  • Update test package dependencies and lock data for the Orleans smoke test.

Documentation:

  • Document the Orleans smoke test and its dependency on OrleansVersion.

Tests:

  • Verify silo lifecycle, persistent state read/write/clear behavior, and reminder registration/unregistration against PostgreSQL.

- Reference the Orleans server and ADO.NET clustering, persistence and
  reminders packages at $(OrleansVersion)
- Run an Orleans silo against a freshly migrated database and check
  that it joins and leaves the cluster, writes, reads and clears grain
  state with DeleteStateOnClear, and registers and unregisters reminders
- Mention the smoke test in the README

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Adds a real Orleans silo smoke-test suite that migrates a fresh PostgreSQL database, exercises clustering, grain persistence, and reminders through all three ADO.NET providers, and verifies the resulting database state; the Orleans packages used by the test are pinned to the schema’s OrleansVersion.

Sequence diagram for the Orleans PostgreSQL smoke tests

sequenceDiagram
    participant Test as OrleansSmokeTests
    participant DB as PostgreSQL
    participant Silo as OrleansSilo
    participant Cluster as IManagementGrain
    participant Grain as SmokeGrain
    participant Reminder as ReminderService

    Test->>DB: MigrateAsync()
    Test->>Silo: StartAsync()
    Silo->>DB: MembershipTable
    Test->>Cluster: GetHosts()
    Cluster-->>Test: Active silo
    Test->>Grain: SetState()
    Grain->>DB: GrainStorage
    Test->>Grain: GetState()
    Grain-->>Test: Persisted state
    Test->>Reminder: RegisterOrUpdateReminder()
    Reminder->>DB: RemindersTable
    Test->>Reminder: GetReminder()
    Test->>Reminder: UnregisterReminder()
    Reminder->>DB: Delete reminder row
    Test->>Silo: StopAsync()
    Silo->>DB: Mark silo dead
Loading

Entity relationship diagram for Orleans PostgreSQL smoke-test data

erDiagram
    MEMBERSHIP_TABLE {
        string silo_address
        string status
    }
    GRAIN_STATE {
        string grain_id
        string state
    }
    REMINDERS_TABLE {
        string grain_id
        string reminder_name
    }
    ORLEANS_SILO ||--o| MEMBERSHIP_TABLE : registers
    SMOKE_GRAIN ||--o| GRAIN_STATE : persists
    SMOKE_GRAIN ||--o| REMINDERS_TABLE : schedules
Loading

File-Level Changes

Change Details Files
Add end-to-end Orleans silo smoke coverage against the migrated PostgreSQL schema.
  • Start a real silo with ADO.NET clustering, grain storage, and reminder providers.
  • Verify membership transitions from active to dead.
  • Verify persisted grain state survives deactivation and is deleted when cleared.
  • Verify reminders are registered, persisted, discoverable, and removed.
test/PostgreSQL.Tests/OrleansSmokeTests.cs
test/PostgreSQL.Tests/Grains/ISmokeGrain.cs
test/PostgreSQL.Tests/Grains/SmokeGrain.cs
test/PostgreSQL.Tests/Grains/SmokeState.cs
Add database-level assertions supporting the smoke tests.
  • Read the membership status and count storage and reminder rows using PostgreSQL scalar queries.
  • Honor the test cancellation token for database operations.
test/PostgreSQL.Tests/Database.cs
Tie the smoke-test Orleans runtime to the schema's declared Orleans version.
  • Pin the Orleans server and ADO.NET provider packages to OrleansVersion.
  • Update the project lock data for the new dependencies.
  • Document that the smoke test follows OrleansVersion.
Directory.Packages.props
test/PostgreSQL.Tests/Escendit.Orleans.Migrations.Cluster.PostgreSQL.Tests.csproj
test/PostgreSQL.Tests/packages.lock.json
README.md

Possibly linked issues

  • #Add tests for PostgreSQL cluster migrations: The PR directly implements the issue’s optional Orleans smoke-test scope using the migrated PostgreSQL database.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@snovak7 snovak7 self-assigned this Sep 27, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="test/PostgreSQL.Tests/Grains/SmokeGrain.cs" line_range="38" />
<code_context>
+
+    /// <inheritdoc />
+    public Task RegisterReminderAsync(string name) =>
+        this.RegisterOrUpdateReminder(name, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
+
+    /// <inheritdoc />
</code_context>
<issue_to_address>
**nitpick (bug_risk):** RegisterReminderAsync ignores its `name` argument and always registers the reminder named `smoke`, so callers passing any other name cannot register the reminder they requested.

**Triggers:** When the smoke grain is called with a reminder name other than `smoke`.

**Suggested fix:** Pass `name` to `RegisterOrUpdateReminder` instead of the hard-coded `"smoke"` literal.
</issue_to_address>

### Comment 2
<location path="test/PostgreSQL.Tests/OrleansSmokeTests.cs" line_range="107" />
<code_context>
+                options.ClusterId = "smoke";
+                options.ServiceId = "smoke";
+            })
+            .ConfigureEndpoints(IPAddress.Loopback, GetFreePort(), GetFreePort())
+            .UseAdoNetClustering(options =>
+            {
</code_context>
<issue_to_address>
**issue (bug_risk):** The two endpoint ports are obtained from listeners which are disposed before Orleans binds them, and the two calls can return the same port or a port claimed by another process; Orleans then fails to start with an endpoint bind error.

**Triggers:** When the two free-port calls collide or another process binds a returned port before `host.StartAsync`.

**Suggested fix:** Reserve both ports until the silo has bound them, or use Orleans' dynamic endpoint-port configuration instead of probing ports with disposable listeners.
</issue_to_address>

### Comment 3
<location path="test/PostgreSQL.Tests/OrleansSmokeTests.cs" line_range="59" />
<code_context>
+        var grain = host.Services.GetRequiredService<IGrainFactory>().GetGrain<ISmokeGrain>("storage");
+
+        await grain.SetValueAsync("hello");
+        await grain.DeactivateAsync();
+
+        Assert.Equal("hello", await grain.GetValueAsync());
</code_context>
<issue_to_address>
**issue (testing):** `DeactivateOnIdle()` only schedules deactivation after the current grain call; awaiting `DeactivateAsync` does not wait until the grain has actually been deactivated. The immediately following `GetValueAsync` can therefore read the in-memory state, so the test passes without proving that state is reloaded from PostgreSQL after deactivation.

**Triggers:** When Orleans has not completed the scheduled deactivation before the next grain call is dispatched.

**Suggested fix:** Wait for observable deactivation and reactivation, or use a test hook/management API that confirms the grain has been removed before issuing the read.
</issue_to_address>

Sourcery assessment

Approval pending. 2 findings to address first.

Blocking findings: test/PostgreSQL.Tests/OrleansSmokeTests.cs:107, test/PostgreSQL.Tests/OrleansSmokeTests.cs:59


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread test/PostgreSQL.Tests/Grains/SmokeGrain.cs
Comment thread test/PostgreSQL.Tests/OrleansSmokeTests.cs Outdated
Comment thread test/PostgreSQL.Tests/OrleansSmokeTests.cs
@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Test Results

20 tests   20 ✅  10s ⏱️
 1 suites   0 💤
 1 files     0 ❌

Results for commit fffc213.

♻️ This comment has been updated with latest results.

- Pick the silo and gateway ports while both listeners are open, so
  they are always distinct
- Assert that grain state is read back by a new activation, proving it
  was loaded from PostgreSQL rather than from memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
Escendit.Orleans.Migrations.Cluster.PostgreSQL 100% 100% 102 ✔
Summary 100% (853 / 854) 100% (0 / 0) 102 ✔

@snovak7
snovak7 merged commit 861f7d8 into main Sep 27, 2026
9 of 11 checks passed
@snovak7 snovak7 added the maintenance Upkeep and tech debt reduction label Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance Upkeep and tech debt reduction

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant