Skip to content

V5.0.7/service update#54

Merged
gimlichael merged 27 commits into
mainfrom
v5.0.7/service-update
May 26, 2026
Merged

V5.0.7/service update#54
gimlichael merged 27 commits into
mainfrom
v5.0.7/service-update

Conversation

@codebelt-aicia
Copy link
Copy Markdown
Contributor

This is a service update that focuses on package dependencies.

Automated changes:

  • Codebelt/Cuemon package versions bumped to latest compatible
  • PackageReleaseNotes.txt updated for v5.0.7
  • CHANGELOG.md entry added for v5.0.7

Note: Third-party packages (Microsoft.Extensions.*, BenchmarkDotNet, etc.) are not auto-updated.
Use Dependabot or manual updates for those.

Generated by codebelt-aicia
Triggered by: yamldotnet @ 10.1.3

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 23, 2026

Greptile Summary

This PR is a v5.0.7 service update that consolidates package dependencies, refactors NATS/Azure/Amazon components for testability, fixes the EfCoreRepository.GetByIdAsync API contract, and expands test coverage across most extension modules.

  • Dependency updates: Codebelt/Cuemon bumped to 10.5.2/10.1.3, NATS.Client to 2.8.0, AWSSDK to 4.0.2.32/.34, Dapper to 2.1.79, EFCore 9→9.0.16/10→10.0.8, and Azure.Identity consolidated from a TFM-split (1.17.2/1.21.0) to a single unconditional 1.21.0 for all frameworks.
  • Testability improvements: NatsCommandQueue and NatsEventBus expose new protected virtual methods and inner ReceivedNatsMessage wrapper classes; AzureQueue<T> and AzureEventBus add protected constructors accepting pre-built SDK clients; AmazonMessage, AmazonCommandQueue, and AmazonEventBus extract CreateSimpleQueueServiceClient/CreateSimpleNotificationServiceClient as protected virtual factory methods.
  • Bug fix: EfCoreRepository.GetByIdAsync now correctly uses new object[] { id } instead of new[] { id } to match the FindAsync(object[], CancellationToken) overload, fixing key boxing for value-type TKeys.

Confidence Score: 5/5

Safe to merge; the refactoring is mechanical and well-scoped, the EFCore key-boxing fix is correct, and all testability changes use protected/virtual correctly.

The code changes are narrowly scoped — extracting inline SDK calls into overridable factory methods and wrappers without altering any public API surface or observable behavior. The EFCore fix closes a real key-boxing gap for value-type keys. The Microsoft.Extensions.Logging.Abstractions version consolidation mirrors the already-merged Azure.Identity consolidation and is low-risk given the package's additive-only versioning policy. No concurrency, security, or data-integrity regressions were found.

Directory.Packages.props — verify the net9 CI build resolves Microsoft.Extensions.Logging.Abstractions 10.0.8 without TFM compatibility errors.

Important Files Changed

Filename Overview
Directory.Packages.props Package versions bumped across Codebelt, Cuemon, NATS, AWSSDK, Dapper, EFCore, and Azure packages; Azure.Identity consolidated to 1.21.0 unconditionally; Microsoft.Extensions.Logging.Abstractions promoted from TFM-conditional 9.0.15/10.0.6 to unconditional 10.0.8, which is a major-version jump for net9 under CentralPackageTransitivePinningEnabled.
src/Savvyio.Extensions.EFCore/EfCoreRepository.cs Fixes GetByIdAsync to use new object[] { id } so FindAsync resolves to the correct object[]/CancellationToken overload, fixing key boxing for value-type TKey generics.
src/Savvyio.Extensions.NATS/Commands/NatsCommandQueue.cs Adds protected virtual PublishMessageAsync, CreateConsumerAsync, CreateJetStreamContext, FetchMessagesAsync methods and sealed ReceivedNatsMessage inner class; refactors ReceiveAsync to use the new abstractions. Logic is sound and closure captures are safe.
src/Savvyio.Extensions.NATS/EventDriven/NatsEventBus.cs Adds protected virtual PublishMessageAsync and SubscribeMessagesAsync methods and sealed ReceivedNatsMessage inner class; delegates are consistent with the call sites in PublishAsync and SubscribeAsync.
src/Savvyio.Extensions.QueueStorage/AzureQueue.cs Adds protected constructor accepting injected QueueServiceClient/QueueClient for testing; correctly falls back to instance-field-initialized default formatters when optional formatter params are null.
src/Savvyio.Extensions.QueueStorage/EventDriven/AzureEventBus.cs Adds protected constructor for testability and extracts FormatCloudEventQueueMessage as a static helper; the static method reference in base() call is safe since no instance members are accessed pre-construction.
src/Savvyio.Extensions.SimpleQueueService/AmazonMessage.cs Extracts SQS client creation into protected virtual CreateSimpleQueueServiceClient; call sites in RetrieveMessagesAsync are updated consistently.
src/Savvyio.Extensions.SimpleQueueService/Commands/AmazonCommandQueue.cs SendAsync and GetHealthCheckTarget both updated to use the new CreateSimpleQueueServiceClient factory, eliminating duplicated SQS client construction.
src/Savvyio.Extensions.SimpleQueueService/EventDriven/AmazonEventBus.cs Adds protected virtual CreateSimpleNotificationServiceClient; PublishAsync and GetHealthCheckTarget updated consistently.
src/Savvyio.Extensions.RabbitMQ/Commands/RabbitMqCommandQueueOptions.cs Durable default changed from false to true to comply with RabbitMQ 4.x deprecation; already flagged in a prior review comment.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class AzureQueue~TRequest~ {
        #AzureQueue(marshaller, options, serviceClient, client, sendFormatter, receiveFormatter)
        #AzureQueue(marshaller, options, sendFormatter, receiveFormatter)
        #SendMessageAsync()
        #ReceiveMessagesAsync()
    }
    class AzureEventBus {
        +AzureEventBus(marshaller, queueOptions, eventBusOptions)
        #AzureEventBus(marshaller, queueOptions, eventBusOptions, queueServiceClient, queueClient, eventGridClient)
        +PublishAsync()
        +SubscribeAsync()
        -FormatCloudEventQueueMessage()
    }
    class NatsCommandQueue {
        +SendAsync()
        +ReceiveAsync()
        #CreateJetStreamContext()
        #PublishMessageAsync()
        #CreateConsumerAsync()
        #FetchMessagesAsync()
        #ReceivedNatsMessage
    }
    class NatsEventBus {
        +PublishAsync()
        +SubscribeAsync()
        #PublishMessageAsync()
        #SubscribeMessagesAsync()
        #ReceivedNatsMessage
    }
    class AmazonMessage~TRequest~ {
        #RetrieveMessagesAsync()
        #CreateSimpleQueueServiceClient()
    }
    class AmazonCommandQueue {
        +SendAsync()
        +GetHealthCheckTarget()
    }
    class AmazonEventBus {
        +PublishAsync()
        +GetHealthCheckTarget()
        #CreateSimpleNotificationServiceClient()
    }
    AzureQueue~TRequest~ <|-- AzureEventBus
    AmazonMessage~TRequest~ <|-- AmazonCommandQueue
    AmazonMessage~TRequest~ <|-- AmazonEventBus
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
Directory.Packages.props:25
**`Microsoft.Extensions.Logging.Abstractions` cross-major-version pin for net9**

The package was previously declared per-TFM (`9.0.15` for net9, `10.0.6` for net10). It is now unconditionally pinned to `10.0.8` for all frameworks. With `CentralPackageTransitivePinningEnabled=true`, net9 builds will resolve all transitive references — including those from `Microsoft.EntityFrameworkCore` 9.0.16, which depends on the 9.0.x series — to 10.0.8.

`Microsoft.Extensions.Logging.Abstractions` is additive-only across major versions, so this is unlikely to cause runtime breakage. However, if the package's net10-only TFMs are chosen over a `netstandard2.0`/`net9.0` target during restore, net9 projects could fail to build. Verify that the net9 CI leg resolves and builds cleanly after this change.

Reviews (9): Last reviewed commit: "♻️ extract jetstream context creation in..." | Re-trigger Greptile

aicia-bot and others added 7 commits May 23, 2026 22:35
This patch release resolves the Azure.Identity compatibility issue from v5.0.4 through careful TFM-specific version targeting (1.17.2 for net9, 1.21.0 for net10). Also includes updates to Codebelt and Cuemon utility libraries, NATS client, Microsoft test/logging packages, and DocFX build environment (nginx 1.30.0 to 1.31.0).
Root cause: RabbitMQ 4.x deprecated the transient_nonexcl_queues feature — a queue that is simultaneously non-durable (transient) and non-exclusive. RabbitMqCommandQueueOptions defaulted both Durable and Exclusive to false, producing exactly this combination on every QueueDeclareAsync call.

Fix: Changed the default value of Durable to true in RabbitMqCommandQueueOptions.

Why Durable = true is the right long-term default: Durable queues survive broker restarts and are the standard/recommended pattern for command queues. If a consumer needs a truly transient queue they can opt-out explicitly, but the non-durable + non-exclusive combination is the pattern RabbitMQ is actively removing.

RabbitMqEventBus.SubscribeAsync is unaffected — it calls QueueDeclareAsync() with no arguments, which creates a server-named exclusive queue (the standard fanout subscriber pattern). Exclusive queues are not transient_nonexcl and RabbitMQ has no plans to deprecate them.
Expand test coverage across multiple extension packages and core modules:

• EventDriven: Add integration tests, messaging tests (CloudEvents, cryptography, MessageAsyncEnumerable, Acknowledgeable)
• Extensions.Dapper: Expand DataSource and QueryOptions tests
• Extensions.DependencyInjection: Add ServiceCollection registration tests for Dapper and EFCore.Domain
• Extensions.Dispatchers: Add MediatorSync tests and SavvyioOptionsExtensions tests
• Extensions.Newtonsoft.Json: Add converter tests (AggregateRootConverter, ValueObjectConverter) and marshaller tests
• Extensions.QueueStorage: Add AzureEventBusOptions and expand existing AzureEventBusTest
• Extensions.RabbitMQ: Add RabbitMqCommandQueueTest and RabbitMqEventBusTest; update RabbitMqCommandQueueOptionsTest for new Durable default
• Extensions.Text.Json: Add DateTime and DateTimeOffset converter tests, MetadataDictionaryConverter tests
• Domain.EventSourcing: Add EFCore extension tests and TracedDomainEvent extension tests
Add and enhance test coverage across cloud and ORM extension packages:

• Extensions.EFCore: Add DomainEventDispatcher extension tests and expand existing EfCoreDataStore and EfCoreRepository tests
• Extensions.NATS: Add NatsCommandQueue tests and NatsEventBus tests; update NatsEventBusOptions and other options tests
• Extensions.SimpleQueueService (Amazon): Add AmazonMessage tests and expand existing queue and event bus test coverage
Update LocalStack Docker image version from 4.13.1 to 4.14.0 in both Dockerfile.localstack and docker-compose.yml for integration testing.
var ta1 = new TracedAccount(id1, providerId, "Name1", "email1@test.com");
var ta2 = new TracedAccount(id2, providerId, "Name2", "email2@test.com");

sut4.AddRange([ta1, ta2]);
var ta2 = new TracedAccount(id2, providerId, "Name2", "email2@test.com");

sut4.AddRange([ta1, ta2]);
await ds.SaveChangesAsync();
sut.Add("TenantId", 42);
sut["TraceId"] = "abc123";

Assert.True(sut.ContainsKey("tenantid"));
Updated CHANGELOG.md with comprehensive v5.0.7 release notes including:

- Full patch release description highlighting Azure.Identity compatibility, RabbitMQ durability correction, and test coverage expansion
- Added: 7 bullets on comprehensive test coverage across EventDriven, Dapper, DependencyInjection, Dispatchers, Newtonsoft.Json, QueueStorage, RabbitMQ, Text.Json, and Domain.EventSourcing modules
- Changed: 7 bullets on dependency updates (Azure.Identity TFM-specific versions, RabbitMQ Durable default, LocalStack 4.14.0, NATS.Client, Microsoft packages, DocFX nginx, Codebelt/Cuemon libraries)
- Fixed: 2 bullets on RabbitMQ transient_nonexcl_queues deprecation and GetByIdAsync parameter handling
- Removed: NuGet prompt file cleanup
- Updated release date to 2026-05-24 and added [Unreleased] compare link
aicia-bot and others added 8 commits May 24, 2026 03:20
Add detailed release notes for v5.0.7 across all affected packages:

- RabbitMQ: Document Durable default change (false→true) and RabbitMQ 4.x deprecation compliance
- EFCore: Document GetByIdAsync parameter handling fix
- QueueStorage: Detail Azure.Identity TFM-specific versioning (1.17.2 for .NET 9, 1.21.0 for .NET 10)
- NATS packages: Highlight NATS.Client upgrade to 2.8.0 for stability
- DapperExtensions: Call out Dapper and DapperExtensions version bumps

All packages document ALM (Application Lifecycle Management) changes with dependency upgrade specifics. Release is patch-level maintenance with focus on dependency compatibility, test coverage expansion, and API behavior improvements.
…heory tests

EF Core caches the compiled model statically per DbContext type. When another
test (AddRange or GetByIdAsync) shares the same EfCoreDbContext<JsonMarshaller>
type, EF Core reuses the cached service provider and skips OnModelCreating for
subsequent instances. This caused the ModelConstructor lambda never to run,
leaving 'schema' null and throwing NullReferenceException at runtime.

Fix: add EnableServiceProviderCaching(false) to the ContextConfigurator for
both theory test cases (Newtonsoft and JsonMarshaller). This forces EF Core to
build a fresh service provider for each DbContext instance, ensuring
OnModelCreating is always called and 'schema' is always assigned.

Root cause was only observable on Linux (ARM64 and X64 Debug) and Windows
Release due to test execution order differences across platforms/frameworks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
}

Assert.NotNull(createAccountMessage);
var receivedCommand = Assert.IsType<CreateAccount>(createAccountMessage.Data);
@codecov
Copy link
Copy Markdown

codecov Bot commented May 25, 2026

Codecov Report

❌ Patch coverage is 94.94949% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.40%. Comparing base (943c66a) to head (38d904b).

Files with missing lines Patch % Lines
...avvyio.Extensions.NATS/EventDriven/NatsEventBus.cs 73.68% 5 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main      #54       +/-   ##
===========================================
+ Coverage   79.06%   98.40%   +19.34%     
===========================================
  Files         177      177               
  Lines        3711     3773       +62     
  Branches      362      363        +1     
===========================================
+ Hits         2934     3713      +779     
+ Misses        776       58      -718     
- Partials        1        2        +1     

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

gimlichael and others added 7 commits May 25, 2026 22:00
NatsCommandQueue: extracted PublishMessageAsync, CreateConsumerAsync, CreateJetStreamContext, and FetchMessagesAsync as protected virtual methods. Introduced ReceivedNatsMessage inner class to wrap NatsJSMsg<string> and decouple the acknowledge callback from the NATS SDK type directly.

NatsEventBus: extracted PublishMessageAsync and SubscribeMessagesAsync as protected virtual methods. Added ReceivedNatsMessage inner class to carry headers and data from the NATS subscription loop without exposing the SDK NatsMsg type directly.

NatsCommandQueueTest: added SendAsync_ShouldPublishSerializedMessages, SendAsync_ShouldUseJetStreamContext_WhenPublishingMessage, ReceiveAsync_ShouldDeserializeFetchedMessagesAndAcknowledge, and ReceiveAsync_ShouldUseJetStreamContext_WhenCreatingConsumer using Testable and Context subclasses backed by Moq.

NatsEventBusTest: added PublishAsync_ShouldPublishSerializedMessage and SubscribeAsync_ShouldDeserializeMessagesAndInvokeHandler using TestableNatsEventBus.

Added Moq package reference to Savvyio.Extensions.NATS.Tests.csproj.
…ge for testability

AzureQueue<TRequest>: added a protected constructor that accepts QueueServiceClient and QueueClient directly, allowing subclasses to inject test doubles without hitting real Azure endpoints. The new constructor calls options.SetConfiguredClient so the same client lifecycle applies as the public path.

AzureEventBus: added a protected constructor accepting QueueServiceClient, QueueClient, and EventGridPublisherClient for the same reason. Extracted the inline CloudEvent receive formatter lambda into a private static FormatCloudEventQueueMessage method so it is reused between the public and protected constructor paths without duplication.

AzureCommandQueueTest: added SendMessageAsync_ShouldSendFormattedMessages and ReceiveMessagesAsync_ShouldYieldMessagesAndDeleteOnAcknowledge using a TestAzureQueue subclass backed by a mocked QueueClient.

AzureEventBusTest: added corresponding publish and subscribe tests using a TestAzureEventBus subclass with mocked Azure clients.
…estability

AmazonMessage<TRequest>: extracted inline AmazonSQSClient construction into a protected virtual CreateSimpleQueueServiceClient method. All receive paths in RetrieveMessagesAsync now call this single factory instead of repeating the credential/endpoint branching logic.

AmazonCommandQueue: replaced two separate inline AmazonSQSClient constructions in SendAsync and GetHealthCheckTarget with calls to the inherited CreateSimpleQueueServiceClient, removing the duplication entirely.

AmazonEventBus: extracted AmazonSimpleNotificationServiceClient construction into a protected virtual CreateSimpleNotificationServiceClient method. Both PublishAsync and GetHealthCheckTarget now delegate to this factory, eliminating repeated credential/endpoint branching.

AmazonCommandQueueTest: added SendAsync_ShouldSendMessagesInBatches (verifies 11 messages split into two SQS batches with correct entry count and message attributes) and ReceiveAsync_ShouldDeserializeMessagesAndDeleteAcknowledgedMessages using a TestableAmazonCommandQueue subclass backed by a mocked IAmazonSQS.

AmazonEventBusTest: added corresponding publish and subscribe tests using a TestableAmazonEventBus subclass backed by a mocked IAmazonSimpleNotificationService.
CHANGELOG.md: Updated v5.0.7 release highlight and Added/Changed sections to document protected virtual methods and constructors now exposed in NATS, Azure QueueStorage, and Amazon SQS/SNS extensions for improved testability and extensibility. Updated release date to 2026-05-26.

NuGet Release Notes: Added Improvements sections to Savvyio.Extensions.NATS, Savvyio.Extensions.QueueStorage, and Savvyio.Extensions.SimpleQueueService packages documenting the new protected virtual methods, constructors, factory methods, and inner classes exposed for testability.
Comment thread src/Savvyio.Extensions.NATS/Commands/NatsCommandQueue.cs Fixed
aicia-bot added 2 commits May 26, 2026 00:57
Azure.Identity consolidated to 1.21.0 for all target frameworks (previously split as 1.17.2 for net9, 1.21.0 for net10) to resolve transitive dependency conflicts introduced in v5.0.4
Extract CreateJetStreamContext() outside the message loop to avoid redundant allocations per iteration. Pass the context as a parameter to PublishMessageAsync instead of creating a new context each time.
var deserialized = Marshaller.Deserialize(await message.Data.FromBase64().ToStreamAsync().ConfigureAwait(false), messageType) as IMessage<ICommand>;
deserialized!.Properties.Add(nameof(CancellationToken), options.CancellationToken);
deserialized.Properties.Add(nameof(NatsJSMsg<string>), message);
deserialized.Properties.Add(nameof(ReceivedNatsMessage), message);
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 26, 2026

@gimlichael gimlichael merged commit 7604909 into main May 26, 2026
1177 of 1181 checks passed
@gimlichael gimlichael deleted the v5.0.7/service-update branch May 26, 2026 20:07
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