From 110205d9085b91e42cb1e26dd7dd13aacf8ebc0a Mon Sep 17 00:00:00 2001 From: Martin Hans Date: Wed, 18 Mar 2026 11:38:14 +0100 Subject: [PATCH 1/4] Upgrade all NuGet packages to latest versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update 23 packages in Directory.Packages.props, notably: - EF Core 8.0.8 → 10.0.5 (align with net10.0 target) - Microsoft.Extensions.* 10.0.1 → 10.0.5 - MongoDB.Driver 3.3.0 → 3.7.0 - Testcontainers 4.10.0 → 4.11.0 - FluentAssertions 7.0.0 → 7.2.2 (kept on 7.x due to license change) --- Directory.Packages.props | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 1651a6d..318f773 100755 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,14 +5,14 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -21,32 +21,32 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - + + + all From 00d04d47ea131ccf365e1cd3dc2d27b844a1f699 Mon Sep 17 00:00:00 2001 From: Martin Hans Date: Wed, 18 Mar 2026 11:59:30 +0100 Subject: [PATCH 2/4] Add SetIfNotNull overload for nullable value types with tests Add a struct-constrained overload of SetIfNotNull to EventAggregatorBase that handles Nullable value types using .HasValue/.Value, complementing the existing reference type overload. Include unit tests covering int?, DateTime?, and Guid? scenarios. --- .../Aggregation/EventAggregatorBase.cs | 15 +++ .../EventAggregatorBaseTests.cs | 96 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs diff --git a/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs b/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs index e52c9b5..c2af8ff 100644 --- a/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs +++ b/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs @@ -41,6 +41,21 @@ protected void SetIfNotNull(Action setter, TProperty? valu setter.Invoke(value); } } + + /// + /// This overload is for nullable value types. + /// Executes the action when .HasValue is true. + /// + /// + /// + /// + protected void SetIfNotNull(Action setter, TProperty? value) where TProperty : struct + { + if (value.HasValue) + { + setter(value.Value); + } + } /// /// Returns the given Entity wrapped in a Task diff --git a/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs b/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs new file mode 100644 index 0000000..f0dc808 --- /dev/null +++ b/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs @@ -0,0 +1,96 @@ +#nullable enable +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Papst.EventStore.Aggregation; +using Xunit; + +namespace Papst.EventStore.Tests; + +public class EventAggregatorBaseTests +{ + private readonly TestAggregator _sut = new(); + + [Fact] + public void SetIfNotNull_ShouldInvokeSetter_WhenNullableStructHasValue() + { + int result = 0; + int? value = 42; + + _sut.CallSetIfNotNull(v => result = v, value); + + result.Should().Be(42); + } + + [Fact] + public void SetIfNotNull_ShouldNotInvokeSetter_WhenNullableStructIsNull() + { + int result = 0; + int? value = null; + + _sut.CallSetIfNotNull(v => result = v, value); + + result.Should().Be(0); + } + + [Fact] + public void SetIfNotNull_ShouldInvokeSetter_WhenNullableDateTimeHasValue() + { + DateTime result = default; + DateTime? value = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + _sut.CallSetIfNotNull(v => result = v, value); + + result.Should().Be(new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc)); + } + + [Fact] + public void SetIfNotNull_ShouldNotInvokeSetter_WhenNullableDateTimeIsNull() + { + DateTime result = default; + DateTime? value = null; + + _sut.CallSetIfNotNull(v => result = v, value); + + result.Should().Be(default(DateTime)); + } + + [Fact] + public void SetIfNotNull_ShouldInvokeSetter_WhenNullableGuidHasValue() + { + Guid result = Guid.Empty; + Guid expected = Guid.NewGuid(); + Guid? value = expected; + + _sut.CallSetIfNotNull(v => result = v, value); + + result.Should().Be(expected); + } + + [Fact] + public void SetIfNotNull_ShouldNotInvokeSetter_WhenNullableGuidIsNull() + { + Guid result = Guid.Empty; + Guid? value = null; + + _sut.CallSetIfNotNull(v => result = v, value); + + result.Should().Be(Guid.Empty); + } + + private class TestEntity + { + public int IntValue { get; set; } + } + + private record TestEvent; + + private class TestAggregator : EventAggregatorBase + { + public override ValueTask ApplyAsync(TestEvent evt, TestEntity entity, IAggregatorStreamContext ctx) + => AsTask(entity); + + public void CallSetIfNotNull(Action setter, T? value) where T : struct + => SetIfNotNull(setter, value); + } +} From 1db04b114516817f687906f355a2958cfcbdd68d Mon Sep 17 00:00:00 2001 From: Martin Hans Date: Wed, 18 Mar 2026 14:08:16 +0100 Subject: [PATCH 3/4] Fix CosmosDbItegrationTestFixture --- .../CosmosDbIntegrationTestFixture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Papst.EventStore.AzureCosmos.Tests/CosmosDbIntegrationTestFixture.cs b/tests/Papst.EventStore.AzureCosmos.Tests/CosmosDbIntegrationTestFixture.cs index 6c4f854..97ab911 100755 --- a/tests/Papst.EventStore.AzureCosmos.Tests/CosmosDbIntegrationTestFixture.cs +++ b/tests/Papst.EventStore.AzureCosmos.Tests/CosmosDbIntegrationTestFixture.cs @@ -17,7 +17,7 @@ public class CosmosDbIntegrationTestFixture : IAsyncLifetime private readonly CosmosDbContainer _cosmosDbContainer = //new CosmosDbBuilder("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview") - new CosmosDbBuilder("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest") + new CosmosDbBuilder("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview") .WithPortBinding(InternalPort, true) //.WithEnvironment("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", "10") .WithEnvironment("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTANCE", "false") From e63003f803cfa1c47bc6a21e0a057774213b1e04 Mon Sep 17 00:00:00 2001 From: Martin Hans Date: Wed, 18 Mar 2026 15:29:44 +0100 Subject: [PATCH 4/4] Add Update overload for nullable value types with tests --- .../Aggregation/EventAggregatorBase.cs | 14 ++++ .../EventAggregatorBaseTests.cs | 70 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs b/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs index c2af8ff..72503fc 100644 --- a/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs +++ b/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs @@ -27,6 +27,20 @@ protected void Update(TProperty? value, Action setter) setter.Invoke(value); } } + + /// + /// Executes the action when .HasValue is true + /// + /// + /// + /// + protected void Update(TProperty? value, Action setter) where TProperty : struct + { + if (value.HasValue) + { + setter.Invoke(value.Value); + } + } /// /// Executes the action when is not null diff --git a/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs b/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs index f0dc808..77cdfc8 100644 --- a/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs +++ b/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs @@ -78,6 +78,73 @@ public void SetIfNotNull_ShouldNotInvokeSetter_WhenNullableGuidIsNull() result.Should().Be(Guid.Empty); } + [Fact] + public void Update_ShouldInvokeSetter_WhenNullableStructHasValue() + { + int result = 0; + int? value = 42; + + _sut.CallUpdate(value, v => result = v); + + result.Should().Be(42); + } + + [Fact] + public void Update_ShouldNotInvokeSetter_WhenNullableStructIsNull() + { + int result = 0; + int? value = null; + + _sut.CallUpdate(value, v => result = v); + + result.Should().Be(0); + } + + [Fact] + public void Update_ShouldInvokeSetter_WhenNullableDateTimeHasValue() + { + DateTime result = default; + DateTime? value = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + _sut.CallUpdate(value, v => result = v); + + result.Should().Be(new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc)); + } + + [Fact] + public void Update_ShouldNotInvokeSetter_WhenNullableDateTimeIsNull() + { + DateTime result = default; + DateTime? value = null; + + _sut.CallUpdate(value, v => result = v); + + result.Should().Be(default(DateTime)); + } + + [Fact] + public void Update_ShouldInvokeSetter_WhenNullableGuidHasValue() + { + Guid result = Guid.Empty; + Guid expected = Guid.NewGuid(); + Guid? value = expected; + + _sut.CallUpdate(value, v => result = v); + + result.Should().Be(expected); + } + + [Fact] + public void Update_ShouldNotInvokeSetter_WhenNullableGuidIsNull() + { + Guid result = Guid.Empty; + Guid? value = null; + + _sut.CallUpdate(value, v => result = v); + + result.Should().Be(Guid.Empty); + } + private class TestEntity { public int IntValue { get; set; } @@ -92,5 +159,8 @@ private class TestAggregator : EventAggregatorBase public void CallSetIfNotNull(Action setter, T? value) where T : struct => SetIfNotNull(setter, value); + + public void CallUpdate(T? value, Action setter) where T : struct + => Update(value, setter); } }