From 9e912bbf59b691c560ddc6fa9485b4d4f4eeb15e Mon Sep 17 00:00:00 2001 From: adimiko Date: Tue, 11 Mar 2025 16:34:36 +0100 Subject: [PATCH 1/2] Changed clock implementation --- source/EasyWay/Clock.cs | 31 ++------ source/EasyWay/DomainEvent.cs | 5 +- .../CustomDateTimeMustBeUtcException.cs | 8 -- source/EasyWay/Internals/Clocks/Extensions.cs | 19 +++++ .../EasyWay/Internals/Clocks/InternalClock.cs | 17 +++++ source/EasyWay/Internals/Extensions.cs | 2 + .../Internals/GuidGenerators/GuidGenerator.cs | 6 +- tests/Directory.Packages.props | 1 + tests/EasyWay.Tests/Clocks/ClockTests.cs | 74 ------------------- .../DomainEvents/DomainEventTests.cs | 7 +- tests/EasyWay.Tests/EasyWay.Tests.csproj | 1 + 11 files changed, 57 insertions(+), 114 deletions(-) delete mode 100644 source/EasyWay/Internals/Clocks/CustomDateTimeMustBeUtcException.cs create mode 100644 source/EasyWay/Internals/Clocks/Extensions.cs create mode 100644 source/EasyWay/Internals/Clocks/InternalClock.cs delete mode 100644 tests/EasyWay.Tests/Clocks/ClockTests.cs diff --git a/source/EasyWay/Clock.cs b/source/EasyWay/Clock.cs index 28a67e3..2b4aa04 100644 --- a/source/EasyWay/Clock.cs +++ b/source/EasyWay/Clock.cs @@ -2,33 +2,12 @@ namespace EasyWay { - public static class Clock + public sealed class Clock { - [ThreadStatic] private static TimeSpan? _differenceBetweenMoments; + public TimeProvider TimeProvider => InternalClock.TimeProvider; - public static DateTime UtcNow - { - get - { - if (_differenceBetweenMoments.HasValue) - { - return DateTime.UtcNow.Add(_differenceBetweenMoments.Value); - } - - return DateTime.UtcNow; - } - } - - internal static void Set(DateTime customDateTime) - { - if (customDateTime.Kind != DateTimeKind.Utc) - { - throw new CustomDateTimeMustBeUtcException(); - } - - _differenceBetweenMoments = customDateTime - DateTime.UtcNow; - } - - internal static void Reset() => _differenceBetweenMoments = null; + public DateTime UtcNow => InternalClock.UtcNow; + + internal Clock() { } } } diff --git a/source/EasyWay/DomainEvent.cs b/source/EasyWay/DomainEvent.cs index 6167cd9..916fc35 100644 --- a/source/EasyWay/DomainEvent.cs +++ b/source/EasyWay/DomainEvent.cs @@ -1,4 +1,5 @@ -using EasyWay.Internals.GuidGenerators; +using EasyWay.Internals.Clocks; +using EasyWay.Internals.GuidGenerators; namespace EasyWay { @@ -14,7 +15,7 @@ public abstract class DomainEvent protected DomainEvent() { EventId = GuidGenerator.New; - OccurrenceOn = Clock.UtcNow; + OccurrenceOn = InternalClock.UtcNow; } } } diff --git a/source/EasyWay/Internals/Clocks/CustomDateTimeMustBeUtcException.cs b/source/EasyWay/Internals/Clocks/CustomDateTimeMustBeUtcException.cs deleted file mode 100644 index 57cf230..0000000 --- a/source/EasyWay/Internals/Clocks/CustomDateTimeMustBeUtcException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace EasyWay.Internals.Clocks -{ - internal sealed class CustomDateTimeMustBeUtcException : EasyWayException - { - internal CustomDateTimeMustBeUtcException() { } - } -} - diff --git a/source/EasyWay/Internals/Clocks/Extensions.cs b/source/EasyWay/Internals/Clocks/Extensions.cs new file mode 100644 index 0000000..5eaa886 --- /dev/null +++ b/source/EasyWay/Internals/Clocks/Extensions.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace EasyWay.Internals.Clocks +{ + internal static class Extensions + { + internal static IServiceCollection AddClocks(this IServiceCollection services) + { + InternalClock.Initialize(); + + var clock = new Clock(); + + services.AddSingleton(clock); + services.AddSingleton(clock.TimeProvider); + + return services; + } + } +} diff --git a/source/EasyWay/Internals/Clocks/InternalClock.cs b/source/EasyWay/Internals/Clocks/InternalClock.cs new file mode 100644 index 0000000..f9bcd77 --- /dev/null +++ b/source/EasyWay/Internals/Clocks/InternalClock.cs @@ -0,0 +1,17 @@ +namespace EasyWay.Internals.Clocks +{ + internal static class InternalClock + { + [ThreadStatic] private static TimeProvider? _testTimeProvider; + + private static TimeProvider _timeProvider; + + internal static TimeProvider TimeProvider => _testTimeProvider is null ? _timeProvider : _testTimeProvider; + + internal static DateTime UtcNow => TimeProvider.GetUtcNow().UtcDateTime; + + internal static void Initialize() => _timeProvider = TimeProvider.System; + + internal static void Test(TimeProvider timeProvider) => _testTimeProvider = timeProvider; + } +} diff --git a/source/EasyWay/Internals/Extensions.cs b/source/EasyWay/Internals/Extensions.cs index a6a05c1..5508031 100644 --- a/source/EasyWay/Internals/Extensions.cs +++ b/source/EasyWay/Internals/Extensions.cs @@ -1,4 +1,5 @@ using EasyWay.Internals.AggregateRoots; +using EasyWay.Internals.Clocks; using EasyWay.Internals.Commands; using EasyWay.Internals.Contexts; using EasyWay.Internals.DomainEvents; @@ -21,6 +22,7 @@ internal static void AddEasyWay( IEnumerable assemblies) { services + .AddClocks() .AddContexts() .AddAggregateRoots() .AddCommands(moduleType, assemblies) diff --git a/source/EasyWay/Internals/GuidGenerators/GuidGenerator.cs b/source/EasyWay/Internals/GuidGenerators/GuidGenerator.cs index cdeeca0..23718ed 100644 --- a/source/EasyWay/Internals/GuidGenerators/GuidGenerator.cs +++ b/source/EasyWay/Internals/GuidGenerators/GuidGenerator.cs @@ -1,4 +1,6 @@ -namespace EasyWay.Internals.GuidGenerators +using EasyWay.Internals.Clocks; + +namespace EasyWay.Internals.GuidGenerators { internal static class GuidGenerator { @@ -10,6 +12,6 @@ internal static class GuidGenerator internal static void Reset() => _customId = null; - private static Guid Create() => Guid.CreateVersion7(Clock.UtcNow); + private static Guid Create() => Guid.CreateVersion7(InternalClock.UtcNow); } } diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props index 49bde6b..f18cbea 100644 --- a/tests/Directory.Packages.props +++ b/tests/Directory.Packages.props @@ -4,6 +4,7 @@ false + diff --git a/tests/EasyWay.Tests/Clocks/ClockTests.cs b/tests/EasyWay.Tests/Clocks/ClockTests.cs deleted file mode 100644 index 4801e31..0000000 --- a/tests/EasyWay.Tests/Clocks/ClockTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -using EasyWay.Internals.Clocks; - -namespace EasyWay.Tests.Clocks -{ - public sealed class ClockTests - { - [Fact(DisplayName = $"{nameof(Clock)} should return date time UTC now")] - public void ClockShouldReturnUtcNow() - { - // Arrange - var expectedUtcNow = DateTime.UtcNow; - TimeSpan precision = TimeSpan.FromMilliseconds(50); - - // Act - var utcNow = Clock.UtcNow; - - // Assert - Assert.Equal(DateTimeKind.Utc, utcNow.Kind); - Assert.Equal(expectedUtcNow, utcNow, precision); - } - - [Fact(DisplayName = $"{nameof(Clock)} should throw exception when set date time is not UTC")] - public void ClockShouldThrowExceptionWhenSetDateTimeIsNotUtc() - { - // Arrange - var unspecifiedDateTime = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified); - - // Assert - Assert.Throws(() => - { - // Act - Clock.Set(unspecifiedDateTime); - }); - } - - [Fact(DisplayName = $"{nameof(Clock)} should return the expected date time when {nameof(Clock.Set)}")] - public void ClockShouldReturnExceptedDateTimeWhenSet() - { - // Arrange - var expectedUtc = new DateTime(2024,1,1,0,0,0, DateTimeKind.Utc); - TimeSpan precision = TimeSpan.FromMilliseconds(50); - - Clock.Set(expectedUtc); - - // Act - var utcNow = Clock.UtcNow; - - // Assert - Assert.Equal(DateTimeKind.Utc, utcNow.Kind); - Assert.True(expectedUtc != utcNow); - Assert.Equal(expectedUtc, utcNow, precision); - - Clock.Reset(); - } - - [Fact(DisplayName = $"{nameof(Clock)} should return the current UTC date time when {nameof(Clock.Set)} and then {nameof(Clock.Reset)}")] - public void ClockShouldReturnExceptedDate() - { - // Arrange - var expectedUtc = DateTime.UtcNow; - - TimeSpan precision = TimeSpan.FromMilliseconds(50); - - Clock.Set(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)); - - // Act - Clock.Reset(); - - // Assert - Assert.Equal(DateTimeKind.Utc, Clock.UtcNow.Kind); - Assert.Equal(expectedUtc, Clock.UtcNow, precision); - } - } -} diff --git a/tests/EasyWay.Tests/DomainEvents/DomainEventTests.cs b/tests/EasyWay.Tests/DomainEvents/DomainEventTests.cs index 6937bd9..b5af5b7 100644 --- a/tests/EasyWay.Tests/DomainEvents/DomainEventTests.cs +++ b/tests/EasyWay.Tests/DomainEvents/DomainEventTests.cs @@ -1,4 +1,7 @@ -namespace EasyWay.Tests.DomainEvents +using EasyWay.Internals.Clocks; +using Microsoft.Extensions.Time.Testing; + +namespace EasyWay.Tests.DomainEvents { public sealed class DomainEventTests { @@ -11,7 +14,7 @@ public void Test() var expectedDateTime = DateTime.UtcNow.AddMonths(-6); var precision = TimeSpan.FromMilliseconds(50); - Clock.Set(expectedDateTime); + InternalClock.Test(new FakeTimeProvider(expectedDateTime)); // Act var domainEvent = new TestDomainEvent(); diff --git a/tests/EasyWay.Tests/EasyWay.Tests.csproj b/tests/EasyWay.Tests/EasyWay.Tests.csproj index c85ede4..691895b 100644 --- a/tests/EasyWay.Tests/EasyWay.Tests.csproj +++ b/tests/EasyWay.Tests/EasyWay.Tests.csproj @@ -10,6 +10,7 @@ + From 203f197e81129bfe1e2924b70efe0331d89796a5 Mon Sep 17 00:00:00 2001 From: adimiko Date: Tue, 11 Mar 2025 16:34:58 +0100 Subject: [PATCH 2/2] Changed clock implementation --- source/EasyWay/Internals/Clocks/Extensions.cs | 2 -- source/EasyWay/Internals/Clocks/InternalClock.cs | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/source/EasyWay/Internals/Clocks/Extensions.cs b/source/EasyWay/Internals/Clocks/Extensions.cs index 5eaa886..ff55a35 100644 --- a/source/EasyWay/Internals/Clocks/Extensions.cs +++ b/source/EasyWay/Internals/Clocks/Extensions.cs @@ -6,8 +6,6 @@ internal static class Extensions { internal static IServiceCollection AddClocks(this IServiceCollection services) { - InternalClock.Initialize(); - var clock = new Clock(); services.AddSingleton(clock); diff --git a/source/EasyWay/Internals/Clocks/InternalClock.cs b/source/EasyWay/Internals/Clocks/InternalClock.cs index f9bcd77..c43d4be 100644 --- a/source/EasyWay/Internals/Clocks/InternalClock.cs +++ b/source/EasyWay/Internals/Clocks/InternalClock.cs @@ -4,14 +4,12 @@ internal static class InternalClock { [ThreadStatic] private static TimeProvider? _testTimeProvider; - private static TimeProvider _timeProvider; + private static TimeProvider _timeProvider = TimeProvider.System; internal static TimeProvider TimeProvider => _testTimeProvider is null ? _timeProvider : _testTimeProvider; internal static DateTime UtcNow => TimeProvider.GetUtcNow().UtcDateTime; - internal static void Initialize() => _timeProvider = TimeProvider.System; - internal static void Test(TimeProvider timeProvider) => _testTimeProvider = timeProvider; } }