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 diff --git a/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs b/src/Papst.EventStore/Aggregation/EventAggregatorBase.cs index e52c9b5..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 @@ -41,6 +55,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.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") diff --git a/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs b/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs new file mode 100644 index 0000000..77cdfc8 --- /dev/null +++ b/tests/Papst.EventStore.Tests/EventAggregatorBaseTests.cs @@ -0,0 +1,166 @@ +#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); + } + + [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; } + } + + 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); + + public void CallUpdate(T? value, Action setter) where T : struct + => Update(value, setter); + } +}