Skip to content

test: Added Integration Tests for Outbox#349

Open
samtrion wants to merge 23 commits intomainfrom
test/integration-tests-outbox
Open

test: Added Integration Tests for Outbox#349
samtrion wants to merge 23 commits intomainfrom
test/integration-tests-outbox

Conversation

@samtrion
Copy link
Copy Markdown
Contributor

@samtrion samtrion commented Apr 10, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added comprehensive integration test infrastructure supporting multiple databases (In-Memory, SQLite, SQL Server)
    • Enhanced Entity Framework outbox configuration with improved bulk operation handling
  • Bug Fixes

    • Fixed memory consistency issue in outbox message processing
  • Breaking Changes

    • SQLite timestamp storage format updated (from TEXT to INTEGER using UTC ticks) - data migration may be required
    • Default message transport behavior changed from in-memory to no-op dispatch mode
  • Tests

    • Expanded integration test suite with multi-database scenario coverage
    • Added unit tests for message transport behavior

@samtrion samtrion self-assigned this Apr 10, 2026
@samtrion samtrion added state:ready for merge Indicates that a pull request has been reviewed and approved, and is ready to be merged into the mai type:chore Indicates some housework that needs to be done. labels Apr 10, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR introduces test infrastructure for outbox integration testing across multiple database providers (InMemory, SQLite, SQL Server), refactors Entity Framework repository bulk operations to handle in-memory vs. provider-specific execution paths, replaces InMemoryMessageTransport with a no-op NullMessageTransport in default service registration, and updates SQLite storage for timestamp properties from TEXT to INTEGER with UTC ticks conversion.

Changes

Cohort / File(s) Summary
Package and Configuration
Directory.Packages.props, src/NetEvolve.Pulse/OutboxExtensions.cs
Added NuGet dependencies for EF Core variants, test provider, and utilities; replaced default IMessageTransport registration from InMemoryMessageTransport to NullMessageTransport.
InMemoryMessageTransport Removal
src/NetEvolve.Pulse/Outbox/InMemoryMessageTransport.cs, tests/NetEvolve.Pulse.Tests.Unit/Outbox/InMemoryMessageTransportTests.cs
Deleted in-memory message transport implementation and its unit test suite.
NullMessageTransport Addition
src/NetEvolve.Pulse/Outbox/NullMessageTransport.cs, tests/NetEvolve.Pulse.Tests.Unit/Outbox/NullMessageTransportTests.cs
Added new no-op message transport and unit tests for null/cancelled token scenarios.
Service Registration Refactoring
src/NetEvolve.Pulse.EntityFramework/EntityFrameworkExtensions.cs
Delegated option and service registration to configurator, replaced manual DI setup with explicit service removal and re-registration.
SQLite Storage Mapping
src/NetEvolve.Pulse.EntityFramework/Configurations/SqliteOutboxMessageConfiguration.cs
Updated timestamp column storage from TEXT to INTEGER using UTC ticks conversion for CreatedAt, UpdatedAt, ProcessedAt, NextRetryAt.
EF Repository Bulk Operations
src/NetEvolve.Pulse.EntityFramework/Outbox/EntityFrameworkOutboxRepository.cs
Refactored bulk updates and deletes to detect in-memory provider and apply entity-level mutations vs. provider bulk operations; removed compiled async query delegates in favor of direct EF queries.
Outbox Processor Threading
src/NetEvolve.Pulse/Outbox/OutboxProcessorHostedService.cs
Changed pending count read from direct field access to Volatile.Read for memory safety.
Test Database Infrastructure
tests/NetEvolve.Pulse.Tests.Integration/Internals/DatabaseType.cs, IDatabaseInitializer.cs, IDatabaseServiceFixture.cs, InMemoryDatabaseServiceFixture.cs, SQLiteDatabaseServiceFixture.cs, SqlServerDatabaseServiceFixture.cs, EntityFrameworkInitializer.cs, TestHelper.cs
Added abstraction interfaces and implementations for database fixture management, EF initialization with provider-specific configuration (in-memory, SQLite with busy timeout and WAL, SQL Server with retry logic), and test framework helpers.
Base Test Classes
tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs, tests/NetEvolve.Pulse.Tests.Integration/Outbox/OutboxTestsBase.cs
Created abstract base classes centralizing host setup, database initialization, mediator publishing utilities, and comprehensive outbox state-transition test scenarios (pending/failed/retry/dead-letter workflows).
Database-Specific Integration Tests
tests/NetEvolve.Pulse.Tests.Integration/Outbox/InMemoryEntityFrameworkOutboxTests.cs, SQLiteEntityFrameworkOutboxTests.cs, SqlServerEntityFrameworkOutboxTests.cs
Added parameterized test classes inheriting outbox test suite for in-memory, SQLite, and SQL Server providers.
Integration Test Snapshots
tests/NetEvolve.Pulse.Tests.Integration/_snapshots/Outbox/*.verified.txt
Added verified snapshots capturing outbox message state across test scenarios for in-memory, SQLite, and SQL Server providers.
Project Dependencies
tests/NetEvolve.Pulse.Tests.Integration/NetEvolve.Pulse.Tests.Integration.csproj
Updated to include EF Core, time provider testing, and assertion utilities; linked shared template file.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A message transport takes its final bow,
In null it finds its purpose now,
With tests across each database kind,
And timestamps stored, at last, aligned.
The outbox dances, batch by batch,
A perfect fit—no strings attached! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title "test: Added Integration Tests for Outbox" accurately summarizes the primary changes, which include new integration test infrastructure, test base classes, and database-specific test implementations for the Outbox feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

❌ Patch coverage is 98.42520% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.47%. Comparing base (0aae259) to head (db7029a).

Files with missing lines Patch % Lines
...ramework/Outbox/EntityFrameworkOutboxRepository.cs 98.04% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #349      +/-   ##
==========================================
+ Coverage   71.21%   76.47%   +5.25%     
==========================================
  Files         104      104              
  Lines        3822     3927     +105     
  Branches      341      353      +12     
==========================================
+ Hits         2722     3003     +281     
+ Misses       1025      845     -180     
- Partials       75       79       +4     

☔ 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.

@samtrion samtrion marked this pull request as ready for review April 10, 2026 22:22
@samtrion samtrion requested a review from a team as a code owner April 10, 2026 22:22
@samtrion samtrion requested a review from Hnogared April 10, 2026 22:22
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs (1)

34-34: Remove no-op host configuration callbacks.

Line 34 and Line 43 currently add empty delegates; removing them keeps the test host setup clearer without behavior change.

Also applies to: 43-43

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs` at line
34, Remove the no-op host configuration callbacks by deleting the empty
ConfigureAppConfiguration delegates in PulseTestsBase (the two occurrences of
.ConfigureAppConfiguration((hostContext, configBuilder) => { }) near the
host/build setup); simply remove those calls so the HostBuilder/host setup code
is clearer and unchanged in behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/NetEvolve.Pulse/Outbox/NullMessageTransport.cs`:
- Line 17: NullMessageTransport.SendAsync currently returns Task.CompletedTask
even when passed a null OutboxMessage; update SendAsync in class
NullMessageTransport to validate the input and throw an ArgumentNullException
when message is null (to honor the IMessageTransport contract), then return
Task.CompletedTask for non-null messages; reference the SendAsync method and
OutboxMessage parameter in your change.

In `@src/NetEvolve.Pulse/OutboxExtensions.cs`:
- Line 69: The current default registration
services.TryAddSingleton<IMessageTransport, NullMessageTransport>() silently
drops messages; replace it with a fail-fast default (e.g., register a
FailFastMessageTransport implementing IMessageTransport) that throws a clear
exception on any send/dispatch call indicating misconfiguration and instructing
the caller to call UseMessageTransport(...). Update the service registration in
OutboxExtensions (replace NullMessageTransport registration) and ensure
FailFastMessageTransport references IMessageTransport methods to throw with a
descriptive message; keep UseMessageTransport(...) behavior unchanged so
consumers can register a real transport to override the fail-fast default.

In `@tests/NetEvolve.Pulse.Tests.Integration/Internals/DatabaseType.cs`:
- Around line 3-10: The DatabaseType enum overstates supported backends; update
DatabaseType to only include the backends handled by EntityFrameworkInitializer
(InMemory and SQLite) to avoid spurious NotSupportedException at runtime: remove
SqlServer, PostgreSQL, and MySql entries from the enum (DatabaseType) and update
any tests or switch/conditionals that reference those removed values to use
InMemory/SQLite or add explicit guards; alternatively, if you prefer supporting
those DBs, extend EntityFrameworkInitializer to implement their initialization
paths (methods referenced in EntityFrameworkInitializer) instead—pick one
approach and make enum and initializer consistent.

In
`@tests/NetEvolve.Pulse.Tests.Integration/Internals/InMemoryDatabaseServiceFixture.cs`:
- Line 5: The ConnectionString property in InMemoryDatabaseServiceFixture
currently generates a new GUID on every access which makes tests
non-deterministic; change it to return a single cached value by creating a
private readonly (or lazy-initialized) backing field (e.g., _connectionString)
initialized once with Guid.NewGuid().ToString("N") and have the ConnectionString
getter return that field so repeated UseInMemoryDatabase(...) calls point to the
same in-memory store.

In `@tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs`:
- Around line 46-50: The test currently starts the host (host.StartAsync) before
creating the test database, which allows IHostedService implementations like
OutboxProcessorHostedService (whose ExecuteAsync calls RefreshPendingCountAsync
→ GetPendingCountAsync) to run and race against missing tables; change the
sequence so DatabaseInitializer.CreateDatabaseAsync(host.Services) is awaited
and completes before calling host.StartAsync (you can still call
host.GetTestServer() after starting) to ensure the outbox table exists prior to
any background processing.

In `@tests/NetEvolve.Pulse.Tests.Unit/Outbox/NullMessageTransportTests.cs`:
- Around line 33-38: The test SendAsync_WithNullMessage_CompletesSuccessfully
conflicts with the IMessageTransport contract which requires
ArgumentNullException for null messages; update the NullMessageTransport unit
test to call NullMessageTransport.SendAsync(null) and assert that it throws an
ArgumentNullException (use the async exception assertion pattern your test
framework provides) instead of expecting successful completion so the test
matches the IMessageTransport contract.

---

Nitpick comments:
In `@tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs`:
- Line 34: Remove the no-op host configuration callbacks by deleting the empty
ConfigureAppConfiguration delegates in PulseTestsBase (the two occurrences of
.ConfigureAppConfiguration((hostContext, configBuilder) => { }) near the
host/build setup); simply remove those calls so the HostBuilder/host setup code
is clearer and unchanged in behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bdf470dd-5858-4ff5-85ed-fb00b9ce4c41

📥 Commits

Reviewing files that changed from the base of the PR and between 7b1e20b and 7e99c8d.

📒 Files selected for processing (22)
  • Directory.Packages.props
  • src/NetEvolve.Pulse.EntityFramework/Configurations/SqliteOutboxMessageConfiguration.cs
  • src/NetEvolve.Pulse.EntityFramework/EntityFrameworkExtensions.cs
  • src/NetEvolve.Pulse/Outbox/InMemoryMessageTransport.cs
  • src/NetEvolve.Pulse/Outbox/NullMessageTransport.cs
  • src/NetEvolve.Pulse/OutboxExtensions.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/DatabaseType.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/EntityFrameworkInitializer.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/IDatabaseInitializer.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/IDatabaseServiceFixture.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/InMemoryDatabaseServiceFixture.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/InternalTestWebApplicationFactory.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/SQLiteDatabaseServiceFixture.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/SqlServerDatabaseServiceFixture.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/WebApplicationTestBase.cs
  • tests/NetEvolve.Pulse.Tests.Integration/NetEvolve.Pulse.Tests.Integration.csproj
  • tests/NetEvolve.Pulse.Tests.Integration/Outbox/OutboxTestsBase.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Outbox/SQLiteEntityFrameworkOutboxTests.cs
  • tests/NetEvolve.Pulse.Tests.Integration/_snapshots/Outbox/SQLiteEntityFrameworkOutboxTests.Should_Persist_Expected_Messages_dda0938409f8d1d7.verified.txt
  • tests/NetEvolve.Pulse.Tests.Unit/Outbox/InMemoryMessageTransportTests.cs
  • tests/NetEvolve.Pulse.Tests.Unit/Outbox/NullMessageTransportTests.cs
💤 Files with no reviewable changes (2)
  • tests/NetEvolve.Pulse.Tests.Unit/Outbox/InMemoryMessageTransportTests.cs
  • src/NetEvolve.Pulse/Outbox/InMemoryMessageTransport.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/NetEvolve.Pulse.EntityFramework/Outbox/EntityFrameworkOutboxRepository.cs`:
- Around line 67-103: The fetch may return messages another worker claimed
because you only filter the final query by ids (and you lost the original
OrderBy), so change the flow to only return messages that this call actually
transitioned to Processing: have UpdateEntitiesAsync (or a new helper) perform
an atomic update that returns the set of claimed Ids (e.g., via a SQL UPDATE ...
OUTPUT/RETURNING or provider-appropriate raw SQL) and have the caller capture
that returned id list; then use that exact list to fetch and return
OutboxMessages (preserving the original CreatedAt ordering by ordering the final
result according to the captured id list or by re-applying the same OrderBy on
the initial selection constrained to the captured ids). Ensure references: ids
variable, _context.OutboxMessages, UpdateEntitiesAsync, Status ==
OutboxMessageStatus.Processing, and ordering by CreatedAt are updated
accordingly.

In `@tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs`:
- Around line 33-63: The test currently calls
host.StartAsync()/host.StopAsync(), which activates OutboxProcessorHostedService
and races repository state; change RunAndVerify to build the Host but do not
start it: remove the host.StartAsync(cancellationToken) and
host.StopAsync(cancellationToken) calls so the host remains unstarted (no hosted
services run) while still using host.GetTestServer() and
server.Services.CreateAsyncScope() to resolve services and run the test code;
this prevents OutboxProcessorHostedService (ExecuteAsync in
OutboxProcessorHostedService) from polling the repository during assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 477c7ae8-9cbd-48ca-a593-ef9b332ab58a

📥 Commits

Reviewing files that changed from the base of the PR and between 01dac1c and 66a0bab.

📒 Files selected for processing (9)
  • src/NetEvolve.Pulse.EntityFramework/Outbox/EntityFrameworkOutboxRepository.cs
  • src/NetEvolve.Pulse/Outbox/OutboxProcessorHostedService.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/EntityFrameworkInitializer.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/InMemoryDatabaseServiceFixture.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Outbox/InMemoryEntityFrameworkOutboxTests.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Outbox/OutboxTestsBase.cs
  • tests/NetEvolve.Pulse.Tests.Integration/_snapshots/Outbox/InMemoryEntityFrameworkOutboxTests.Should_Persist_Expected_Messages_b0c5fb5f6a10ddef.verified.txt
  • tests/NetEvolve.Pulse.Tests.Unit/Outbox/NullMessageTransportTests.cs
✅ Files skipped from review due to trivial changes (1)
  • tests/NetEvolve.Pulse.Tests.Integration/_snapshots/Outbox/InMemoryEntityFrameworkOutboxTests.Should_Persist_Expected_Messages_b0c5fb5f6a10ddef.verified.txt
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/NetEvolve.Pulse.Tests.Unit/Outbox/NullMessageTransportTests.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/InMemoryDatabaseServiceFixture.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs (1)

50-61: ⚠️ Potential issue | 🟠 Major

Avoid starting the host in RunAndVerify.

This helper only needs a built service provider, but host.StartAsync() will also start any registered hosted services. For the outbox cases, that means repository state can change in the background while the assertions are running, which makes the suite flaky. Resolve the scope from host.Services and keep this helper unstarted; if a future test needs a live server, add a separate helper for that path.

Suggested change
         await DatabaseInitializer.CreateDatabaseAsync(host.Services, cancellationToken).ConfigureAwait(false);
-        await host.StartAsync(cancellationToken).ConfigureAwait(false);
-
-        using var server = host.GetTestServer();
 
         using (Assert.Multiple())
         {
-            await using var scope = server.Services.CreateAsyncScope();
+            await using var scope = host.Services.CreateAsyncScope();
             await testableCode.Invoke(scope.ServiceProvider, cancellationToken).ConfigureAwait(false);
         }
-
-        await host.StopAsync(cancellationToken).ConfigureAwait(false);

Run this to verify that RunAndVerify is only using the server for DI access while the host startup path can activate background services:

#!/bin/bash
set -euo pipefail

echo "=== RunAndVerify implementation ==="
sed -n '24,70p' tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs

echo
echo "=== Hosted service registrations ==="
rg -n --type=cs -C2 'IHostedService|AddHostedService<|BackgroundService' src tests

echo
echo "=== TestServer usage inside integration tests ==="
rg -n --type=cs -C2 'GetTestServer\(|CreateClient\(|SendAsync\(' tests/NetEvolve.Pulse.Tests.Integration
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs` around
lines 50 - 61, The RunAndVerify helper currently calls host.StartAsync() which
activates hosted/background services and can mutate repository state during
assertions; change RunAndVerify to NOT call host.StartAsync(), instead resolve
the DI scope directly from host.Services (use host.Services.CreateAsyncScope())
and invoke the testableCode within that scope; keep creating a TestServer only
if you need its server-specific features but avoid starting the host or calling
host.StartAsync()/host.StopAsync() in RunAndVerify (update references to
RunAndVerify, host.StartAsync/StopAsync, and the use of server.GetTestServer()
accordingly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs`:
- Around line 50-61: The RunAndVerify helper currently calls host.StartAsync()
which activates hosted/background services and can mutate repository state
during assertions; change RunAndVerify to NOT call host.StartAsync(), instead
resolve the DI scope directly from host.Services (use
host.Services.CreateAsyncScope()) and invoke the testableCode within that scope;
keep creating a TestServer only if you need its server-specific features but
avoid starting the host or calling host.StartAsync()/host.StopAsync() in
RunAndVerify (update references to RunAndVerify, host.StartAsync/StopAsync, and
the use of server.GetTestServer() accordingly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d7cacaf-2b84-457b-9b18-d3ec649b5469

📥 Commits

Reviewing files that changed from the base of the PR and between 66a0bab and 58ad9b7.

📒 Files selected for processing (10)
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/EntityFrameworkInitializer.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/IDatabaseInitializer.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/PulseTestsBase.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/SQLiteDatabaseServiceFixture.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/SqlServerDatabaseServiceFixture.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/TestHelper.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Outbox/OutboxTestsBase.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Outbox/SQLiteEntityFrameworkOutboxTests.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Outbox/SqlServerEntityFrameworkOutboxTests.cs
  • tests/NetEvolve.Pulse.Tests.Integration/_snapshots/Outbox/SqlServerEntityFrameworkOutboxTests.Should_Persist_Expected_Messages_cde079fe90d49e58.verified.txt
✅ Files skipped from review due to trivial changes (2)
  • tests/NetEvolve.Pulse.Tests.Integration/_snapshots/Outbox/SqlServerEntityFrameworkOutboxTests.Should_Persist_Expected_Messages_cde079fe90d49e58.verified.txt
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/IDatabaseInitializer.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/SQLiteDatabaseServiceFixture.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/SqlServerDatabaseServiceFixture.cs
  • tests/NetEvolve.Pulse.Tests.Integration/Internals/EntityFrameworkInitializer.cs

@samtrion samtrion force-pushed the test/integration-tests-outbox branch 3 times, most recently from 571cd73 to 81e1859 Compare April 13, 2026 09:02
samtrion and others added 16 commits April 13, 2026 11:10
- Replace InMemoryMessageTransport with NullMessageTransport as the default; remove in-memory transport and its tests
- Register NullMessageTransport in DI and update docs
- Add unit tests for NullMessageTransport
- Refactor EF test infrastructure: new IDatabaseInitializer, improved EntityFrameworkInitializer, internalize fixtures, and add PulseTestsBase/OutboxTestsBase
- Add SQLite+EF outbox integration tests and verified output
- Store DateTimeOffset columns as INTEGER (UTC ticks) in SQLite for correct ordering
- Add test dependencies: EFCore.InMemory, TimeProvider.Testing, Verify.ParametersHashing
- Remove obsolete OutboxTests and related code
Clean up file formatting by deleting a stray '-' character
before the closing </Project> tag. No functional changes.
Removed unnecessary Assert.That(true).IsTrue() statements from unit tests. The tests now rely on the absence of exceptions to verify correct behavior, simplifying the test code.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Martin Stühmer <me@samtrion.net>
…yDatabaseServiceFixture.cs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Martin Stühmer <me@samtrion.net>
- Replace pre-compiled queries in EntityFrameworkOutboxRepository with direct LINQ using AsNoTracking and ToArrayAsync for simplicity and testability
- Make EntityFrameworkInitializer and InMemoryDatabaseServiceFixture public; improve SQLite concurrency with WAL mode
- Add InMemoryEntityFrameworkOutboxTests and expand OutboxTestsBase with extensive scenarios (pending, batch size, completion, failure, dead-letter, retry scheduling, deletion)
- Use FakeTimeProvider for time-dependent tests
- Ensure database creation before host start and proper service scope management in tests
- Add PublishEventsAsync helper for event publishing with cancellation support
- Improve thread-safety in OutboxProcessorHostedService
- Overall, greatly increase test coverage and reliability for Outbox operations, especially for in-memory and SQLite scenarios
EntityFrameworkOutboxRepository now detects and adapts to the InMemory provider, using tracked entity updates/deletes instead of bulk operations. Added helper methods for this logic. InMemory integration tests now run by default. Added a verified snapshot for InMemory persistence. Removed obsolete null message test from NullMessageTransportTests.
- Improve Entity Framework Outbox test isolation by generating unique database/schema names per test run using target framework and GUID.
- Update IDatabaseInitializer and EntityFrameworkInitializer for robust, thread-safe database/table creation with CancellationToken support.
- Configure OutboxOptions.Schema for test isolation; disable OutboxProcessor during tests.
- Add [Timeout(60_000)] to OutboxTestsBase for CI reliability.
- Enhance SQL Server fixture with unique DB names and container startup timeout/error handling.
- Remove unused InternalTestWebApplicationFactory and WebApplicationTestBase.
- Add TestHelper for target framework detection.
- Update test data sources for proper fixture/initializer sharing.
- Add verified snapshot for SQL Server Outbox tests.
- Add PostgreSqlDatabaseServiceFixture and PostgreSqlEntityFrameworkOutboxTests using Testcontainers and TUnit for PostgreSQL Outbox integration testing
- Add Npgsql and Npgsql.EntityFrameworkCore.PostgreSQL dependencies for all target frameworks
- Generate EF Core index and PK names with TruncateIdentifier to enforce DB identifier length limits and uniqueness (esp. for PostgreSQL)
- Update tests and configuration to use new index naming convention (including schema/table)
- EntityFrameworkInitializer now supports PostgreSQL via UseNpgsql
- Add ContainerParallelLimiter for container-based test parallelism
- Outbox integration tests now always disable background processing for determinism
- SqlServerEntityFrameworkOutboxTests now uses SharedType.None for fixture/initializer
- Refactor OutboxProcessorHostedServiceTests to use WaitForMarkingsAsync instead of Task.Delay for reliable async test synchronization
- Marking methods in InMemoryOutboxRepository now signal a semaphore for test coordination
- Update OutboxMessageConfigurationMetadataTests for new index names
- Minor: remove unused using, add verified snapshot for PostgreSQL tests
Refactored all unit and integration tests to accept a CancellationToken parameter and pass it to all async methods under test and framework calls. Updated test signatures, lambda expressions, and helper invocations accordingly. This improves test reliability, enables proper cancellation support, and aligns with .NET async best practices. No production code was changed—only test code is affected.
Refactored SqlServerDatabaseServiceFixture to generate and use a consistent test database name, explicitly create the database during initialization, and ensure reliable connection strings. Updated verified outbox test output for consistency in payloads, timestamps, and event IDs, resulting in more stable and predictable integration tests.
Refactor integration test infrastructure to introduce shared PostgreSQL and SQL Server container fixtures, reducing container startup overhead. Database service fixtures now create per-test databases within shared containers. Removed custom parallel limiter and updated test classes for improved efficiency and maintainability.
Added a continue statement after the delay when processing is disabled, ensuring the background service loop immediately restarts and avoids executing further logic in that iteration. This prevents unnecessary processing when the service is set to disabled.
Rename _isInMemory to _useTrackingOperations to better reflect when tracking operations are required (currently only for the InMemory provider). Update all relevant checks and assignments for improved clarity and maintainability. No change to underlying logic.
Ensures each test gets a fresh model by disabling EF Core's
internal service provider caching. This prevents tests from
reusing cached models when using the same connection string,
avoiding interference and improving test isolation.
Replace EnableServiceProviderCaching(false) with a custom IModelCacheKeyFactory that includes the table name in the EF Core model cache key. This prevents model reuse across tests sharing a connection string, avoiding "Table '...' already exists" errors, while preserving correct type-mapping initialization.
The check for `_options.DisableProcessing` and its delay was moved from outside to inside the `try` block in `OutboxProcessorHostedService`. This ensures any exceptions during the check or delay are now handled by the existing exception handling logic.
- Implements provider-specific outbox operations via IOutboxOperationsExecutor and new executor classes
- Maps Guid to binary(16) and DateTimeOffset to bigint for MySQL (Oracle) to work around provider limitations
- Adds MySql.EntityFrameworkCore and Testcontainers.MySql dependencies
- Adds MySQL integration test fixture and test suite
- Updates model metadata tests and documentation for MySQL
- Refactors repository to delegate all mutations to the correct executor
- Improves SQL Server test container connection string and test clarity
Introduce ProviderName class to centralize EF Core provider name constants, replacing scattered string literals and private constants. Update all provider selection logic to use these constants. Refactor OutboxOperationsExecutor constructors to accept maxDegreeOfParallelism for improved concurrency control. Enhance maintainability and reduce errors in provider-specific logic.
@samtrion samtrion force-pushed the test/integration-tests-outbox branch from 96879c4 to 53c3b65 Compare April 13, 2026 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state:ready for merge Indicates that a pull request has been reviewed and approved, and is ready to be merged into the mai type:chore Indicates some housework that needs to be done.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant