From 528ccbbee1a6af93d29843ee9d003ab7f5cfc2d3 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Thu, 17 Oct 2024 17:43:37 +0200 Subject: [PATCH 01/17] Add .NET 9 and Test Platform --- .github/workflows/pull-request.yml | 5 +- Directory.Packages.props | 206 +++++++++--------- Eventuous.sln | 27 +-- .../version-0.15/application/command-api.md | 2 +- props/Common.props | 11 + samples/Directory.Build.props | 1 + .../Bookings.Payments.csproj | 60 ++--- samples/esdb/Bookings.Payments/Program.cs | 2 +- samples/esdb/Bookings/Bookings.csproj | 74 +++---- samples/esdb/Bookings/Registrations.cs | 3 +- .../Bookings.Payments.csproj | 5 +- samples/postgres/Bookings.Payments/Program.cs | 2 +- samples/postgres/Bookings/Bookings.csproj | 64 +++--- .../postgres/Bookings/Infrastructure/Mongo.cs | 7 +- .../Bookings/Infrastructure/Telemetry.cs | 3 +- .../Eventuous.Persistence/AggregateFactory.cs | 11 + .../Checkpoints/CheckpointCommitHandler.cs | 8 +- .../Eventuous.Tests.Application.csproj | 2 + .../ServiceTestBase.Amendments.cs | 9 +- .../ServiceTestBase.cs | 3 +- .../StateWithIdTests.cs | 3 +- .../Eventuous.Tests.Diagnostics.csproj | 6 - .../Eventuous.Tests.Persistence.Base.csproj | 29 +-- .../Fixtures/DomainFixture.cs | 2 +- .../Fixtures/StoreFixtureBase.cs | 7 +- .../Store/OtherMethods.cs | 5 +- .../Store/Read.cs | 11 +- .../Traits/CategoryAttribute.cs | 9 +- .../Traits/CategoryDiscoverer.cs | 48 ++-- .../Eventuous.Tests.Persistence.csproj | 9 - .../TieredStoreTests.cs | 11 - .../Eventuous.Tests.Subscriptions.Base.csproj | 1 + .../Fixtures/SubscriptionFixtureBase.cs | 7 +- .../Fixtures/TestEventHandler.cs | 2 +- .../SubscribeToAll.cs | 6 +- .../SubscribeToStream.cs | 4 +- .../AutofixtureExtensions.cs | 3 +- .../Eventuous.Tests.Subscriptions.csproj | 1 + .../RegistrationTests.cs | 15 +- .../SequenceTests.cs | 4 +- .../OperateOnAggregateWithId.cs | 4 +- .../Aggregates/OperateOnExistingSpec.cs | 12 +- .../Aggregates/TwoAggregateOpsSpec.cs | 12 +- .../Eventuous.Tests/Eventuous.Tests.csproj | 18 +- .../test/Eventuous.Tests/ForgotToSetId.cs | 5 +- .../test/Eventuous.Tests/StoringEvents.cs | 5 +- .../StoringEventsWithCustomStream.cs | 11 +- .../Eventuous.Tests/StreamNameMapTests.cs | 12 +- .../test/Eventuous.Tests/StreamNameTests.cs | 24 +- .../Eventuous.Tests/TypeRegistrationTests.cs | 9 +- .../Eventuous.Tests.OpenTelemetry.csproj | 8 + .../Fakes/TestHandler.cs | 2 + .../MetricsSubscriptionFixtureBase.cs | 2 +- .../MetricsTests.cs | 4 +- src/Directory.Build.props | 12 +- .../AppServiceTests.cs | 10 +- .../Eventuous.Tests.EventStore.csproj | 1 + .../Fixtures/DomainFixture.cs | 3 +- .../ProducerTracesTests.cs | 7 +- .../RegistrationTests.cs | 6 +- .../Store/AggregateStoreTests.cs | 8 +- .../Store/EventStoreAggregateTests.cs | 20 +- .../Fixtures/LegacySubscriptionFixture.cs | 7 +- .../Fixtures/PersistentSubscriptionFixture.cs | 7 +- ...PublishAndSubscribeManyPartitionedTests.cs | 5 +- .../PublishAndSubscribeManyTests.cs | 5 +- .../PublishAndSubscribeOneTests.cs | 7 +- ...mPersistentPublishAndSubscribeManyTests.cs | 9 +- .../StreamSubscriptionDeletedEventsTests.cs | 19 +- .../StreamSubscriptionWithLinksTests.cs | 3 +- .../SubscriptionIgnoredMessagesTests.cs | 7 +- .../ElasticPlayground.csproj | 2 +- .../Eventuous.Extensions.AspNetCore.csproj | 1 + .../Logging/AppBuilderLoggingExtensions.cs | 26 +++ .../AggregateFactoryHostExtensions.cs | 32 --- ...uous.Extensions.DependencyInjection.csproj | 5 +- .../LoggingHostExtensions.cs | 42 ---- .../LoggingServiceProviderExtensions.cs | 27 +++ .../README.md | 6 +- .../Registrations/AggregateFactory.cs | 2 - .../Eventuous.Extensions.Logging.csproj | 5 + .../LoggerFactoryExtensions.cs | 23 ++ .../Eventuous.Sut.AspNetCore.csproj | 1 - .../AggregateFactoryRegistrationTests.cs | 3 +- ...Eventuous.Tests.DependencyInjection.csproj | 1 + .../ControllerTests.cs | 5 +- ...entuous.Tests.Extensions.AspNetCore.csproj | 6 +- .../Fixture/ServerFixture.cs | 5 +- .../Eventuous.Tests.Gateway.csproj | 1 + .../Eventuous.Tests.GooglePubSub.csproj | 3 + .../PubSubFixture.cs | 4 +- .../PubSubTests.cs | 16 +- .../BasicProducerTests.cs | 5 +- .../Eventuous.Tests.Kafka.csproj | 3 +- .../Eventuous.Tests.Kafka/KafkaFixture.cs | 6 +- ...Eventuous.Tests.Projections.MongoDB.csproj | 1 + .../Fixtures/IntegrationFixture.cs | 4 +- .../ProjectingWithTypedHandlers.cs | 3 +- .../ProjectionTestBase.cs | 11 +- .../Eventuous.Tests.Postgres.csproj | 1 + .../Projections/ProjectorTests.cs | 13 +- .../RabbitMqFixture.cs | 4 +- .../SubscriptionSpec.cs | 24 +- .../Eventuous.Tests.Redis.csproj | 17 +- .../Fixtures/DomainFixture.cs | 4 +- .../Fixtures/IntegrationFixture.cs | 4 +- .../Fixtures/SubscriptionFixture.cs | 11 +- .../test/Eventuous.Tests.Redis/Store/Read.cs | 9 +- .../Subscriptions/SubscribeToAll.cs | 12 +- .../Subscriptions/SubscribeToStream.cs | 12 +- .../Eventuous.Tests.SqlServer.csproj | 35 +-- .../Projections/ProjectorTests.cs | 13 +- test/Directory.Build.props | 3 +- .../Eventuous.TestHelpers.csproj | 5 +- test/Eventuous.TestHelpers/Logging.cs | 14 -- .../Logging/LoggingExtensions.cs | 34 +++ .../Logging/xUnitLogger.cs | 85 ++++++++ .../Logging/xUnitLoggerOptions.cs | 31 +++ 118 files changed, 866 insertions(+), 666 deletions(-) create mode 100644 props/Common.props delete mode 100644 src/Core/test/Eventuous.Tests.Diagnostics/Eventuous.Tests.Diagnostics.csproj delete mode 100644 src/Core/test/Eventuous.Tests.Persistence/Eventuous.Tests.Persistence.csproj delete mode 100644 src/Core/test/Eventuous.Tests.Persistence/TieredStoreTests.cs create mode 100644 src/Extensions/src/Eventuous.Extensions.AspNetCore/Logging/AppBuilderLoggingExtensions.cs delete mode 100644 src/Extensions/src/Eventuous.Extensions.DependencyInjection/AggregateFactoryHostExtensions.cs delete mode 100644 src/Extensions/src/Eventuous.Extensions.DependencyInjection/LoggingHostExtensions.cs create mode 100644 src/Extensions/src/Eventuous.Extensions.DependencyInjection/LoggingServiceProviderExtensions.cs create mode 100644 src/Extensions/src/Eventuous.Extensions.Logging/Eventuous.Extensions.Logging.csproj create mode 100644 src/Extensions/src/Eventuous.Extensions.Logging/LoggerFactoryExtensions.cs delete mode 100644 test/Eventuous.TestHelpers/Logging.cs create mode 100644 test/Eventuous.TestHelpers/Logging/LoggingExtensions.cs create mode 100644 test/Eventuous.TestHelpers/Logging/xUnitLogger.cs create mode 100644 test/Eventuous.TestHelpers/Logging/xUnitLoggerOptions.cs diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 29a7d81da..eb2d4b862 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - dotnet: [ 'net6.0', 'net8.0' ] + dotnet: [ 'net9.0', 'net8.0' ] env: NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages TC_CLOUD_TOKEN: ${{ secrets.TC_TOKEN }} @@ -32,7 +32,8 @@ jobs: name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: '8.0' + dotnet-version: '9.0' + dotnet-quality: 'preview' - name: Build run: | diff --git a/Directory.Packages.props b/Directory.Packages.props index af2b1b4dd..43c5623e3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,104 +1,106 @@ - - true - - - 8.0 - 8.0.6 - - - [6.0.5,7) - 2.3.0 - - - 8.0.6 - 3.0.0 - - - 3.9.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + true + + + 9.0.0-rc.2.24474.3 + 9.0.0-rc.2.24473.5 + 9.0.0-rc.2.24474.3 + + + 8.0.6 + 8.0 + 8.0.8 + + + 3.10.0 + + + 8.0.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Eventuous.sln b/Eventuous.sln index 634def5f0..87a241254 100644 --- a/Eventuous.sln +++ b/Eventuous.sln @@ -163,8 +163,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.Diagnostics", "sr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.Application", "src\Core\src\Eventuous.Application\Eventuous.Application.csproj", "{3743969A-4635-40DE-B45C-46F80CBB5733}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.Tests.Diagnostics", "src\Core\test\Eventuous.Tests.Diagnostics\Eventuous.Tests.Diagnostics.csproj", "{EAB7C8CC-FD8D-437B-ADB5-FA02FC62AAF9}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.Tests.Application", "src\Core\test\Eventuous.Tests.Application\Eventuous.Tests.Application.csproj", "{2C44A145-81E5-4F43-9C9A-AB3CF822AF82}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.Redis", "src\Redis\src\Eventuous.Redis\Eventuous.Redis.csproj", "{179C730A-CD2E-4B4D-935B-38F7E9CFE33B}" @@ -213,10 +211,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bookings.Payments", "sample EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bookings", "samples\esdb\Bookings\Bookings.csproj", "{C666D8E7-FB55-4435-A8D4-CF9815660E85}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.Tests.Persistence", "src\Core\test\Eventuous.Tests.Persistence\Eventuous.Tests.Persistence.csproj", "{F24F066B-FC7A-4298-B007-19CC86BB31E1}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Brokers", "Brokers", "{86D92758-EBB6-4B8C-94B7-BD91AF1E31D2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.Extensions.Logging", "src\Extensions\src\Eventuous.Extensions.Logging\Eventuous.Extensions.Logging.csproj", "{E1F6CDD8-D37E-487B-A429-25A06C590FE4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -474,12 +472,6 @@ Global {3743969A-4635-40DE-B45C-46F80CBB5733}.Release|Any CPU.Build.0 = Release|Any CPU {3743969A-4635-40DE-B45C-46F80CBB5733}.Debug CI|Any CPU.ActiveCfg = Debug CI|Any CPU {3743969A-4635-40DE-B45C-46F80CBB5733}.Debug CI|Any CPU.Build.0 = Debug CI|Any CPU - {EAB7C8CC-FD8D-437B-ADB5-FA02FC62AAF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EAB7C8CC-FD8D-437B-ADB5-FA02FC62AAF9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EAB7C8CC-FD8D-437B-ADB5-FA02FC62AAF9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EAB7C8CC-FD8D-437B-ADB5-FA02FC62AAF9}.Release|Any CPU.Build.0 = Release|Any CPU - {EAB7C8CC-FD8D-437B-ADB5-FA02FC62AAF9}.Debug CI|Any CPU.ActiveCfg = Debug CI|Any CPU - {EAB7C8CC-FD8D-437B-ADB5-FA02FC62AAF9}.Debug CI|Any CPU.Build.0 = Debug CI|Any CPU {2C44A145-81E5-4F43-9C9A-AB3CF822AF82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2C44A145-81E5-4F43-9C9A-AB3CF822AF82}.Debug|Any CPU.Build.0 = Debug|Any CPU {2C44A145-81E5-4F43-9C9A-AB3CF822AF82}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -552,12 +544,12 @@ Global {C666D8E7-FB55-4435-A8D4-CF9815660E85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C666D8E7-FB55-4435-A8D4-CF9815660E85}.Debug|Any CPU.Build.0 = Debug|Any CPU {C666D8E7-FB55-4435-A8D4-CF9815660E85}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F24F066B-FC7A-4298-B007-19CC86BB31E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F24F066B-FC7A-4298-B007-19CC86BB31E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F24F066B-FC7A-4298-B007-19CC86BB31E1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F24F066B-FC7A-4298-B007-19CC86BB31E1}.Release|Any CPU.Build.0 = Release|Any CPU - {F24F066B-FC7A-4298-B007-19CC86BB31E1}.Debug CI|Any CPU.ActiveCfg = Debug CI|Any CPU - {F24F066B-FC7A-4298-B007-19CC86BB31E1}.Debug CI|Any CPU.Build.0 = Debug CI|Any CPU + {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Release|Any CPU.Build.0 = Release|Any CPU + {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Debug CI|Any CPU.ActiveCfg = Debug CI|Any CPU + {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Debug CI|Any CPU.Build.0 = Debug CI|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -629,7 +621,6 @@ Global {8B155B20-B23E-490E-85B8-9712BCA91F04} = {7BBD9F8E-EB6A-4E2D-84A9-AF15C1784401} {A25D6B10-5B40-46B2-ACBB-F47C76ECFAB8} = {7BBD9F8E-EB6A-4E2D-84A9-AF15C1784401} {3743969A-4635-40DE-B45C-46F80CBB5733} = {7BBD9F8E-EB6A-4E2D-84A9-AF15C1784401} - {EAB7C8CC-FD8D-437B-ADB5-FA02FC62AAF9} = {0ED6785B-60EF-46B4-B938-EF04189FC8BC} {2C44A145-81E5-4F43-9C9A-AB3CF822AF82} = {0ED6785B-60EF-46B4-B938-EF04189FC8BC} {179C730A-CD2E-4B4D-935B-38F7E9CFE33B} = {0E2520E7-B4A6-47E7-AED8-662C88441A84} {5DACCAF8-9CD4-4B0B-96B7-15EF049EB199} = {B1AAD6CA-2710-41E0-9495-B43C313D6BCA} @@ -653,10 +644,10 @@ Global {7D86A33D-7C1A-45F7-BEFF-1B95525716D6} = {75F337AF-7E15-4ED1-8E4F-A582DABEA373} {7D24DAB3-FD49-443C-811A-96F0CA6A6F9A} = {75F337AF-7E15-4ED1-8E4F-A582DABEA373} {C666D8E7-FB55-4435-A8D4-CF9815660E85} = {75F337AF-7E15-4ED1-8E4F-A582DABEA373} - {F24F066B-FC7A-4298-B007-19CC86BB31E1} = {0ED6785B-60EF-46B4-B938-EF04189FC8BC} {1970CA0D-C5E8-4384-8485-82D712289002} = {86D92758-EBB6-4B8C-94B7-BD91AF1E31D2} {6E545DFE-FE70-4486-92E0-E47E86E66210} = {86D92758-EBB6-4B8C-94B7-BD91AF1E31D2} {2E59C5F8-3E5A-4450-B902-7648AD7ECC0F} = {86D92758-EBB6-4B8C-94B7-BD91AF1E31D2} + {E1F6CDD8-D37E-487B-A429-25A06C590FE4} = {2B7F84B7-C0E5-408F-ABAF-BF23C8305486} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0691467B-C257-46DB-BC4F-88EB7CD615B8} diff --git a/docs/versioned_docs/version-0.15/application/command-api.md b/docs/versioned_docs/version-0.15/application/command-api.md index 921bc2ba9..6331f6a6e 100644 --- a/docs/versioned_docs/version-0.15/application/command-api.md +++ b/docs/versioned_docs/version-0.15/application/command-api.md @@ -24,7 +24,7 @@ Here is an example of a command API controller: ```csharp [Route("/booking")] -public class CommandApi(ICommandService service) +public class CommandApi(ICommandService service) : CommandHttpApiBase { [HttpPost] [Route("book")] diff --git a/props/Common.props b/props/Common.props new file mode 100644 index 000000000..2d45cd706 --- /dev/null +++ b/props/Common.props @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props index 53ba4c935..da955ecae 100644 --- a/samples/Directory.Build.props +++ b/samples/Directory.Build.props @@ -10,4 +10,5 @@ $(RepoRoot)\src Debug;Release + \ No newline at end of file diff --git a/samples/esdb/Bookings.Payments/Bookings.Payments.csproj b/samples/esdb/Bookings.Payments/Bookings.Payments.csproj index c3758e9f8..aa9f8bed0 100644 --- a/samples/esdb/Bookings.Payments/Bookings.Payments.csproj +++ b/samples/esdb/Bookings.Payments/Bookings.Payments.csproj @@ -1,32 +1,32 @@ - - Debug;Release - - - - - - - - - - - - - - - - true - PreserveNewest - PreserveNewest - - - - - - - - - - + + Debug;Release + + + + + + + + + + + + + + + + true + PreserveNewest + PreserveNewest + + + + + + + + + + \ No newline at end of file diff --git a/samples/esdb/Bookings.Payments/Program.cs b/samples/esdb/Bookings.Payments/Program.cs index e8075f99e..f2679abe2 100644 --- a/samples/esdb/Bookings.Payments/Program.cs +++ b/samples/esdb/Bookings.Payments/Program.cs @@ -19,7 +19,7 @@ builder.Host.UseSerilog(); var app = builder.Build(); -app.UseEventuousLogs(); +app.Services.AddEventuousLogs(); app.UseSwagger(); app.UseOpenTelemetryPrometheusScrapingEndpoint(); diff --git a/samples/esdb/Bookings/Bookings.csproj b/samples/esdb/Bookings/Bookings.csproj index b77713ee3..1aaebe78f 100644 --- a/samples/esdb/Bookings/Bookings.csproj +++ b/samples/esdb/Bookings/Bookings.csproj @@ -1,39 +1,39 @@ - - Linux - Debug;Release - AnyCPU - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - PreserveNewest - - + + Linux + Debug;Release + AnyCPU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + PreserveNewest + + \ No newline at end of file diff --git a/samples/esdb/Bookings/Registrations.cs b/samples/esdb/Bookings/Registrations.cs index 3c4f79f46..35f524952 100644 --- a/samples/esdb/Bookings/Registrations.cs +++ b/samples/esdb/Bookings/Registrations.cs @@ -11,6 +11,7 @@ using Eventuous.EventStore.Subscriptions; using Eventuous.Projections.MongoDB; using Eventuous.Subscriptions.Registrations; +using MongoDB.Driver.Core.Extensions.DiagnosticSources; using NodaTime; using NodaTime.Serialization.SystemTextJson; using OpenTelemetry.Metrics; @@ -76,7 +77,7 @@ public static void AddTelemetry(this IServiceCollection services) { .AddAspNetCoreInstrumentation() .AddGrpcClientInstrumentation() .AddEventuousTracing() - .AddMongoDBInstrumentation() + .AddSource(typeof(DiagnosticsActivityEventSubscriber).Assembly.GetName().Name!) .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("bookings")) .SetSampler(new AlwaysOnSampler()); diff --git a/samples/postgres/Bookings.Payments/Bookings.Payments.csproj b/samples/postgres/Bookings.Payments/Bookings.Payments.csproj index b19b7048b..2447ff1b2 100644 --- a/samples/postgres/Bookings.Payments/Bookings.Payments.csproj +++ b/samples/postgres/Bookings.Payments/Bookings.Payments.csproj @@ -5,7 +5,8 @@ - + + @@ -33,7 +34,7 @@ - + diff --git a/samples/postgres/Bookings.Payments/Program.cs b/samples/postgres/Bookings.Payments/Program.cs index 4b0e16263..92c29e128 100644 --- a/samples/postgres/Bookings.Payments/Program.cs +++ b/samples/postgres/Bookings.Payments/Program.cs @@ -20,7 +20,7 @@ var app = builder.Build(); -app.UseEventuousLogs(); +app.Services.AddEventuousLogs(); app.UseSwagger().UseSwaggerUI(); app.UseOpenTelemetryPrometheusScrapingEndpoint(); diff --git a/samples/postgres/Bookings/Bookings.csproj b/samples/postgres/Bookings/Bookings.csproj index ec6a13e80..413f971ad 100644 --- a/samples/postgres/Bookings/Bookings.csproj +++ b/samples/postgres/Bookings/Bookings.csproj @@ -1,34 +1,34 @@ - - Linux - Debug;Release - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Linux + Debug;Release + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/postgres/Bookings/Infrastructure/Mongo.cs b/samples/postgres/Bookings/Infrastructure/Mongo.cs index d10633e1c..400375d6d 100644 --- a/samples/postgres/Bookings/Infrastructure/Mongo.cs +++ b/samples/postgres/Bookings/Infrastructure/Mongo.cs @@ -12,14 +12,11 @@ public static IMongoDatabase ConfigureMongo(IConfiguration configuration) { var settings = MongoClientSettings.FromConnectionString(config.ConnectionString); if (config is { User: not null, Password: not null }) { - settings.Credential = new MongoCredential( - null, - new MongoInternalIdentity("admin", config.User), - new PasswordEvidence(config.Password) - ); + settings.Credential = new(null, new MongoInternalIdentity("admin", config.User), new PasswordEvidence(config.Password)); } settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber()); + return new MongoClient(settings).GetDatabase(config.Database); } diff --git a/samples/postgres/Bookings/Infrastructure/Telemetry.cs b/samples/postgres/Bookings/Infrastructure/Telemetry.cs index 02b4ec22f..d2069283f 100644 --- a/samples/postgres/Bookings/Infrastructure/Telemetry.cs +++ b/samples/postgres/Bookings/Infrastructure/Telemetry.cs @@ -1,4 +1,5 @@ using Eventuous.Diagnostics.OpenTelemetry; +using MongoDB.Driver.Core.Extensions.DiagnosticSources; using Npgsql; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; @@ -30,7 +31,7 @@ public static void AddTelemetry(this IServiceCollection services) { .AddAspNetCoreInstrumentation() .AddEventuousTracing() .AddNpgsql() - .AddMongoDBInstrumentation(); + .AddSource(typeof(DiagnosticsActivityEventSubscriber).Assembly.GetName().Name!); if (otelEnabled) builder.AddOtlpExporter(); diff --git a/src/Core/src/Eventuous.Persistence/AggregateFactory.cs b/src/Core/src/Eventuous.Persistence/AggregateFactory.cs index 440b9549a..82a8d6909 100644 --- a/src/Core/src/Eventuous.Persistence/AggregateFactory.cs +++ b/src/Core/src/Eventuous.Persistence/AggregateFactory.cs @@ -12,6 +12,15 @@ public class AggregateFactoryRegistry { /// Aggregate factory registry singleton instance /// public static readonly AggregateFactoryRegistry Instance = new(); + + private AggregateFactoryRegistry() { } + + [UsedImplicitly] + public AggregateFactoryRegistry(IServiceProvider sp, IEnumerable resolvers) { + foreach (var resolver in resolvers) { + UnsafeCreateAggregateUsing(resolver.Type, () => resolver.CreateInstance(sp)); + } + } internal readonly Dictionary> Registry = new(); @@ -39,3 +48,5 @@ public AggregateFactoryRegistry CreateAggregateUsing(AggregateFactory } public delegate T AggregateFactory() where T : Aggregate where TState : State, new(); + +public record ResolveAggregateFactory(Type Type, Func CreateInstance); diff --git a/src/Core/src/Eventuous.Subscriptions/Checkpoints/CheckpointCommitHandler.cs b/src/Core/src/Eventuous.Subscriptions/Checkpoints/CheckpointCommitHandler.cs index 0fcf1500e..678356ff7 100644 --- a/src/Core/src/Eventuous.Subscriptions/Checkpoints/CheckpointCommitHandler.cs +++ b/src/Core/src/Eventuous.Subscriptions/Checkpoints/CheckpointCommitHandler.cs @@ -79,7 +79,7 @@ async ValueTask Process(CommitPosition[] list, CancellationToken cancellationTok [PublicAPI] public ValueTask Commit(CommitPosition position, CancellationToken cancellationToken) { if (Diagnostic.IsEnabled(CommitOperation)) Diagnostic.Write(CommitOperation, new CommitEvent(_subscriptionId, position, _positions.Min)); - position.LogContext.PositionReceived(position); + position.LogContext?.PositionReceived(position); return _worker.Write(position, cancellationToken); } @@ -124,7 +124,7 @@ async Task CommitInternal(CommitPosition position, bool force, CancellationToken return; } - position.LogContext.CommittingPosition(position); + position.LogContext?.CommittingPosition(position); await _commitCheckpoint(new(_subscriptionId, position.Position), force, cancellationToken).NoContext(); _lastCommit = position; _positions.RemoveWhere(x => x.Sequence <= position.Sequence); @@ -132,7 +132,7 @@ async Task CommitInternal(CommitPosition position, bool force, CancellationToken await _commitCheckpoint(new(_subscriptionId, position.Position), true, default).NoContext(); _positions.RemoveWhere(x => x.Sequence <= position.Sequence); } catch (Exception e) { - position.LogContext.UnableToCommitPosition(position, e); + position.LogContext?.UnableToCommitPosition(position, e); } } @@ -149,7 +149,7 @@ public async ValueTask DisposeAsync() { public readonly record struct CommitPosition(ulong Position, ulong Sequence, DateTime Timestamp) { public bool Valid { get; private init; } = true; - public LogContext LogContext { get; init; } + public LogContext? LogContext { get; init; } public static readonly CommitPosition None = new(0, 0, DateTime.MinValue) { Valid = false }; diff --git a/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj b/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj index c46c553b7..e57200336 100644 --- a/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj +++ b/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj @@ -2,6 +2,8 @@ true true + Exe + true diff --git a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.Amendments.cs b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.Amendments.cs index d51bb415a..1f6fab2e4 100644 --- a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.Amendments.cs +++ b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.Amendments.cs @@ -1,5 +1,6 @@ using Eventuous.Sut.Domain; using Eventuous.Testing; +using static Xunit.TestContext; namespace Eventuous.Tests.Application; @@ -9,9 +10,9 @@ public async Task Should_amend_event_from_command() { var service = CreateService(amendEvent: AmendEvent); var cmd = CreateCommand(); - await service.Handle(cmd, default); + await service.Handle(cmd, Current.CancellationToken); - var stream = await Store.ReadStream(StreamName.For(cmd.BookingId), StreamReadPosition.Start); + var stream = await Store.ReadStream(StreamName.For(cmd.BookingId), StreamReadPosition.Start, cancellationToken: Current.CancellationToken); stream[0].Metadata["userId"].Should().Be(cmd.ImportedBy); } @@ -31,9 +32,9 @@ public async Task Should_combine_amendments() { var service = CreateService(amendEvent: AmendEvent, amendAll: AddMeta); var cmd = CreateCommand(); - await service.Handle(cmd, default); + await service.Handle(cmd, Current.CancellationToken); - var stream = await Store.ReadStream(StreamName.For(cmd.BookingId), StreamReadPosition.Start); + var stream = await Store.ReadStream(StreamName.For(cmd.BookingId), StreamReadPosition.Start, cancellationToken: Current.CancellationToken); stream[0].Metadata["userId"].Should().Be(cmd.ImportedBy); stream[0].Metadata["foo"].Should().Be("bar"); } diff --git a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.cs b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.cs index 2d2319a49..1da2f4464 100644 --- a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.cs +++ b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.cs @@ -4,6 +4,7 @@ using Eventuous.Testing; using NodaTime; using static Eventuous.Sut.Domain.BookingEvents; +using static Xunit.TestContext; namespace Eventuous.Tests.Application; @@ -16,7 +17,7 @@ public async Task Ensure_builder_is_thread_safe() { var tasks = Enumerable .Range(1, threadCount) - .Select(bookingId => Task.Run(() => service.Handle(Helpers.GetBookRoom(bookingId.ToString()), default))) + .Select(bookingId => Task.Run(() => service.Handle(Helpers.GetBookRoom(bookingId.ToString()), Current.CancellationToken))) .ToList(); await Task.WhenAll(tasks); diff --git a/src/Core/test/Eventuous.Tests.Application/StateWithIdTests.cs b/src/Core/test/Eventuous.Tests.Application/StateWithIdTests.cs index 0b55c3ad7..f6b1e6a7f 100644 --- a/src/Core/test/Eventuous.Tests.Application/StateWithIdTests.cs +++ b/src/Core/test/Eventuous.Tests.Application/StateWithIdTests.cs @@ -1,4 +1,5 @@ using NodaTime; +using static Xunit.TestContext; namespace Eventuous.Tests.Application; @@ -24,7 +25,7 @@ public async Task ShouldGetIdForNew() { result.TryGet(out var ok).Should().BeTrue(); ok!.State.Id.Should().Be(bookingId); - var instance = await _store.LoadAggregate(bookingId, map, true); + var instance = await _store.LoadAggregate(bookingId, map, true, cancellationToken: Current.CancellationToken); // Ensure that the id was set when the aggregate was loaded instance.State.Id.Should().Be(bookingId); diff --git a/src/Core/test/Eventuous.Tests.Diagnostics/Eventuous.Tests.Diagnostics.csproj b/src/Core/test/Eventuous.Tests.Diagnostics/Eventuous.Tests.Diagnostics.csproj deleted file mode 100644 index 1d616f398..000000000 --- a/src/Core/test/Eventuous.Tests.Diagnostics/Eventuous.Tests.Diagnostics.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - true - true - - diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj b/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj index dc4de2531..1095f2d15 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj @@ -1,16 +1,17 @@  - - true - - - - - - - - - - - - + + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/DomainFixture.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/DomainFixture.cs index f68362ce3..69ef7810f 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/DomainFixture.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/DomainFixture.cs @@ -4,7 +4,7 @@ namespace Eventuous.Tests.Persistence.Base.Fixtures; public static class DomainFixture { - static DomainFixture() => TypeMap.RegisterKnownEventTypes(); + static DomainFixture() => TypeMap.RegisterKnownEventTypes(typeof(DomainFixture).Assembly); public static Commands.ImportBooking CreateImportBooking(IFixture auto) { var from = auto.Create(); diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs index e380699be..2268f767e 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs @@ -5,6 +5,7 @@ using Bogus; using DotNet.Testcontainers.Containers; using Eventuous.TestHelpers; +using Eventuous.TestHelpers.Logging; using MicroElements.AutoFixture.NodaTime; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -22,7 +23,7 @@ public abstract class StoreFixtureBase { } public abstract partial class StoreFixtureBase : StoreFixtureBase, IAsyncLifetime where TContainer : DockerContainer { - public virtual async Task InitializeAsync() { + public virtual async ValueTask InitializeAsync() { Container = CreateContainer(); await Container.StartAsync(); @@ -30,7 +31,7 @@ public virtual async Task InitializeAsync() { if (Output != null) { services.AddSingleton(Output); - services.AddLogging(cfg => cfg.AddXunit(Output, LogLevel.Debug).SetMinimumLevel(LogLevel.Debug)); + services.AddLogging(cfg => cfg.AddXUnit(Output).SetMinimumLevel(LogLevel.Debug)); } Serializer = new DefaultEventSerializer(TestPrimitives.DefaultOptions, TypeMapper); @@ -55,7 +56,7 @@ protected async Task Start() { } } - public virtual async Task DisposeAsync() { + public virtual async ValueTask DisposeAsync() { if (_disposed) return; _disposed = true; diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/OtherMethods.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/OtherMethods.cs index 06e871685..6d6051433 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/OtherMethods.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/OtherMethods.cs @@ -1,5 +1,6 @@ using Eventuous.Sut.Domain; using Eventuous.Tests.Persistence.Base.Fixtures; +using static Xunit.TestContext; namespace Eventuous.Tests.Persistence.Base.Store; @@ -18,7 +19,7 @@ public async Task StreamShouldExist() { var streamName = _fixture.GetStreamName(); await _fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream); - var exists = await _fixture.EventStore.StreamExists(streamName, default); + var exists = await _fixture.EventStore.StreamExists(streamName, Current.CancellationToken); exists.Should().BeTrue(); } @@ -26,7 +27,7 @@ public async Task StreamShouldExist() { [Trait("Category", "Store")] public async Task StreamShouldNotExist() { var streamName = _fixture.GetStreamName(); - var exists = await _fixture.EventStore.StreamExists(streamName, default); + var exists = await _fixture.EventStore.StreamExists(streamName, Current.CancellationToken); exists.Should().BeFalse(); } } diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Read.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Read.cs index 06cdbf1c4..4742b029b 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Read.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Read.cs @@ -1,6 +1,7 @@ using System.Text.Json; using Eventuous.Sut.Domain; using Eventuous.Tests.Persistence.Base.Fixtures; +using static Xunit.TestContext; // ReSharper disable CoVariantArrayConversion @@ -21,7 +22,7 @@ public async Task ShouldReadOne() { var streamName = _fixture.GetStreamName(); await _fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream); - var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, default); + var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); result.Length.Should().Be(1); result[0].Payload.Should().BeEquivalentTo(evt); } @@ -33,7 +34,7 @@ public async Task ShouldReadMany() { var streamName = _fixture.GetStreamName(); await _fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); - var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, default); + var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); var actual = result.Select(x => x.Payload); actual.Should().BeEquivalentTo(events); } @@ -45,7 +46,7 @@ public async Task ShouldReadTail() { var streamName = _fixture.GetStreamName(); await _fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); - var result = await _fixture.EventStore.ReadEvents(streamName, new(10), 100, default); + var result = await _fixture.EventStore.ReadEvents(streamName, new(10), 100, Current.CancellationToken); var expected = events.Skip(10); var actual = result.Select(x => x.Payload); actual.Should().BeEquivalentTo(expected); @@ -58,7 +59,7 @@ public async Task ShouldReadHead() { var streamName = _fixture.GetStreamName(); await _fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); - var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 10, default); + var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 10, Current.CancellationToken); var expected = events.Take(10); var actual = result.Select(x => x.Payload); actual.Should().BeEquivalentTo(expected); @@ -72,7 +73,7 @@ public async Task ShouldReadMetadata() { await _fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream, new() { { "Key1", "Value1" }, { "Key2", "Value2" } }); - var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, default); + var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); result.Length.Should().Be(1); result[0].Payload.Should().BeEquivalentTo(evt); diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryAttribute.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryAttribute.cs index 6678197ac..1d1401811 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryAttribute.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryAttribute.cs @@ -1,18 +1,17 @@ using JetBrains.Annotations; +using Xunit.v3; // ReSharper disable once CheckNamespace namespace Xunit; -using Sdk; - /// /// Apply this attribute to your test method to specify a category. /// [UsedImplicitly] -[TraitDiscoverer("CategoryDiscoverer", "TraitExtensibility")] [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class CategoryAttribute(string category) : Attribute, ITraitAttribute { - [UsedImplicitly] - public string Name { get; } = category; + // [UsedImplicitly] + // public string Name { get; } = category; + public IReadOnlyCollection> GetTraits() => [new("Category", category)]; } diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryDiscoverer.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryDiscoverer.cs index f696e803f..e96ee3d44 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryDiscoverer.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryDiscoverer.cs @@ -1,24 +1,24 @@ -using JetBrains.Annotations; - -// ReSharper disable once CheckNamespace -namespace Xunit; - -using Sdk; - -/// -/// This class discovers all the tests and test classes that have -/// applied the Category attribute -/// -[UsedImplicitly] -public class CategoryDiscoverer : ITraitDiscoverer { - /// - /// Gets the trait values from the Category attribute. - /// - /// The trait attribute containing the trait values. - /// The trait values. - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) { - var categoryName = traitAttribute.GetNamedArgument("Name"); - - yield return new("Category", categoryName); - } -} +// using JetBrains.Annotations; +// +// // ReSharper disable once CheckNamespace +// namespace Xunit; +// +// using Sdk; +// +// /// +// /// This class discovers all the tests and test classes that have +// /// applied the Category attribute +// /// +// [UsedImplicitly] +// public class CategoryDiscoverer : ITraitDiscoverer { +// /// +// /// Gets the trait values from the Category attribute. +// /// +// /// The trait attribute containing the trait values. +// /// The trait values. +// public IEnumerable> GetTraits(IAttributeInfo traitAttribute) { +// var categoryName = traitAttribute.GetNamedArgument("Name"); +// +// yield return new("Category", categoryName); +// } +// } diff --git a/src/Core/test/Eventuous.Tests.Persistence/Eventuous.Tests.Persistence.csproj b/src/Core/test/Eventuous.Tests.Persistence/Eventuous.Tests.Persistence.csproj deleted file mode 100644 index 959329ee0..000000000 --- a/src/Core/test/Eventuous.Tests.Persistence/Eventuous.Tests.Persistence.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - true - true - - - - - diff --git a/src/Core/test/Eventuous.Tests.Persistence/TieredStoreTests.cs b/src/Core/test/Eventuous.Tests.Persistence/TieredStoreTests.cs deleted file mode 100644 index e3913a27e..000000000 --- a/src/Core/test/Eventuous.Tests.Persistence/TieredStoreTests.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Eventuous.Testing; - -namespace Eventuous.Tests.Persistence; - -public class TieredStoreTests { - readonly InMemoryEventStore _hotStore = new(); - readonly InMemoryEventStore _coldStore = new(); - - // [Fact] - // public async Task -} diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj index ab4bffe81..d5eeb36a8 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj @@ -12,5 +12,6 @@ + diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs index 5c5534895..ba0037cb7 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs @@ -2,6 +2,7 @@ using Eventuous.Subscriptions; using Eventuous.Subscriptions.Checkpoints; using Eventuous.Sut.Domain; +using Eventuous.TestHelpers.Logging; using Eventuous.Tests.Persistence.Base.Fixtures; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -63,7 +64,7 @@ protected override void SetupServices(IServiceCollection services) { var host = services.First(x => !x.IsKeyedService && x.ImplementationFactory?.GetType() == typeof(Func)); services.Remove(host); - services.AddLogging(b => ConfigureLogging(b.AddXunit(_outputHelper, _logLevel).SetMinimumLevel(_logLevel))); + services.AddLogging(b => ConfigureLogging(b.AddXUnit(_outputHelper).SetMinimumLevel(_logLevel))); } protected override void GetDependencies(IServiceProvider provider) { @@ -80,12 +81,12 @@ protected override void GetDependencies(IServiceProvider provider) { public abstract Task GetLastPosition(); - public override async Task InitializeAsync() { + public override async ValueTask InitializeAsync() { await base.InitializeAsync(); if (_autoStart) await StartSubscription(); } - public override async Task DisposeAsync() { + public override async ValueTask DisposeAsync() { if (_autoStart) await StopSubscription(); await base.DisposeAsync(); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs index 0f3f0f9b5..ac047eac6 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs @@ -25,7 +25,7 @@ public Hypothesis AssertCollection(TimeSpan deadline, List colle => Hypothesis.On(_observer).Timebox(deadline).Exactly(collection.Count).Match(collection.Contains); public override async ValueTask HandleEvent(IMessageConsumeContext context) { - options?.Output?.WriteLine(context.Message!.ToString()); + options?.Output?.WriteLine(context.Message!.ToString() ?? string.Empty); await Task.Delay(_delay); await _observer.Add(context.Message!, context.CancellationToken); Count++; diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToAll.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToAll.cs index cf4fbb30b..f6d98ec34 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToAll.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToAll.cs @@ -59,7 +59,7 @@ protected async Task ShouldUseExistingCheckpoint() { await fixture.CheckpointStore.StoreCheckpoint(new(fixture.SubscriptionId, last), true, default); var l = await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, default); - outputHelper.WriteLine("Last checkpoint: {0}", l.Position); + outputHelper.WriteLine("Last checkpoint: {0}", l.Position!); await fixture.StartSubscription(); await Task.Delay(TimeSpan.FromSeconds(1)); @@ -85,7 +85,7 @@ async Task> GenerateAndHandleCommands(int count) { return commands; } - public Task InitializeAsync() => fixture.InitializeAsync(); + public async ValueTask InitializeAsync() => await fixture.InitializeAsync(); - public Task DisposeAsync() => fixture.DisposeAsync(); + public async ValueTask DisposeAsync() => await fixture.DisposeAsync(); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToStream.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToStream.cs index 7bbea63fa..e57d9f8e0 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToStream.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToStream.cs @@ -94,7 +94,7 @@ async Task> GenerateAndProduceEvents(int count) { return events; } - public Task InitializeAsync() => fixture.InitializeAsync(); + public async ValueTask InitializeAsync() => await fixture.InitializeAsync(); - public Task DisposeAsync() => fixture.DisposeAsync(); + public async ValueTask DisposeAsync() => await fixture.DisposeAsync(); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/AutofixtureExtensions.cs b/src/Core/test/Eventuous.Tests.Subscriptions/AutofixtureExtensions.cs index da366b83e..2b197f746 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/AutofixtureExtensions.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/AutofixtureExtensions.cs @@ -1,10 +1,11 @@ using Eventuous.Subscriptions.Context; +using Eventuous.TestHelpers.Logging; namespace Eventuous.Tests.Subscriptions; public static class AutoFixtureExtensions { public static MessageConsumeContext CreateContext(this Fixture auto, ITestOutputHelper output) { - var factory = new LoggerFactory().AddXunit(output, LogLevel.Trace); + var factory = new LoggerFactory().AddXUnit(output); return auto.Build().With(x => x.LogContext, () => new("test", factory)).Create(); } } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj index 8ef6c259e..ade1a7e97 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj +++ b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj @@ -2,6 +2,7 @@ true true + Exe diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs index 58eb910d9..4b6344121 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs @@ -3,14 +3,14 @@ using Eventuous.Subscriptions.Context; using Eventuous.Subscriptions.Diagnostics; using Eventuous.Subscriptions.Filters; -using Eventuous.Subscriptions.Logging; -using Eventuous.TestHelpers; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Logging.Abstractions; +using static Xunit.TestContext; +using LoggingExtensions = Eventuous.TestHelpers.Logging.LoggingExtensions; // ReSharper disable ClassNeverInstantiated.Local @@ -19,7 +19,7 @@ namespace Eventuous.Tests.Subscriptions; public class RegistrationTests(ITestOutputHelper outputHelper) { readonly TestServer _server = new(BuildHost()); readonly Fixture _auto = new(); - readonly ILoggerFactory _logger = Logging.GetLoggerFactory(outputHelper); + readonly ILoggerFactory _logger = LoggingExtensions.GetLoggerFactory(outputHelper); [Fact] public void ShouldBeSingletons() { @@ -64,7 +64,7 @@ public async Task SubsShouldHaveHandlers(int position, Type handlerType) { new Metadata(), current.SubscriptionId, default - ) { LogContext = new LogContext(current.SubscriptionId, _logger) }; + ) { LogContext = new(current.SubscriptionId, _logger) }; await current.Pipe.Send(ctx); var handled = logger.Records.Where(x => x.Context.SubscriptionId == current.SubscriptionId).ToArray(); @@ -93,7 +93,7 @@ public async Task BothShouldBeRunningAndReportHealthy() { subs.Should().AllSatisfy(x => x.IsRunning.Should().BeTrue()); health.Should().NotBeNull(); - var check = await health!.CheckHealthAsync(new HealthCheckContext()); + var check = await health!.CheckHealthAsync(new(), Current.CancellationToken); check.Data["sub1"].Should().Be("Healthy"); check.Data["sub2"].Should().Be("Healthy"); check.Status.Should().Be(HealthStatus.Healthy); @@ -138,8 +138,7 @@ class TestSub(TestOptions options, ConsumePipe consumePipe) protected override ValueTask Unsubscribe(CancellationToken cancellationToken) => default; - public GetSubscriptionEndOfStream GetMeasure() - => _ => new(new EndOfStream(SubscriptionId, 0, DateTime.UtcNow)); + public GetSubscriptionEndOfStream GetMeasure() => _ => new(new EndOfStream(SubscriptionId, 0, DateTime.UtcNow)); } abstract class BaseTestHandler(TestHandlerLogger logger) : BaseEventHandler { @@ -155,7 +154,7 @@ record TestEvent; class TestHandlerLogger { public ValueTask EventReceived(Type handlerType, IMessageConsumeContext ctx) { - Records.Add(new TestHandlerLogRecord(handlerType, ctx)); + Records.Add(new(handlerType, ctx)); return ValueTask.FromResult(EventHandlingStatus.Success); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs index 129d889ad..94a2fc993 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs @@ -1,14 +1,14 @@ using Eventuous.Subscriptions.Checkpoints; +using Eventuous.TestHelpers.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Xunit.Extensions.Logging; namespace Eventuous.Tests.Subscriptions; public class SequenceTests { public SequenceTests(ITestOutputHelper output) { var factory = new LoggerFactory(); - factory.AddProvider(new XunitLoggerProvider(output, (_, _) => true)); + factory.AddProvider(new XUnitLoggerProvider(output)); var services = new ServiceCollection(); services.AddSingleton(factory); var provider = services.BuildServiceProvider(); diff --git a/src/Core/test/Eventuous.Tests/AggregateWithId/OperateOnAggregateWithId.cs b/src/Core/test/Eventuous.Tests/AggregateWithId/OperateOnAggregateWithId.cs index 59d0dd3dd..c44bf364e 100644 --- a/src/Core/test/Eventuous.Tests/AggregateWithId/OperateOnAggregateWithId.cs +++ b/src/Core/test/Eventuous.Tests/AggregateWithId/OperateOnAggregateWithId.cs @@ -9,10 +9,10 @@ public class OperateOnAggregateWithId : AggregateWithIdSpec Emitted(new TestEvent()); - [Fact] + [Test] public void should_set_id() => Then().State.Id.Value.Should().Be(IdValue); } diff --git a/src/Core/test/Eventuous.Tests/Aggregates/OperateOnExistingSpec.cs b/src/Core/test/Eventuous.Tests/Aggregates/OperateOnExistingSpec.cs index 8dad42a06..b0c7b6694 100644 --- a/src/Core/test/Eventuous.Tests/Aggregates/OperateOnExistingSpec.cs +++ b/src/Core/test/Eventuous.Tests/Aggregates/OperateOnExistingSpec.cs @@ -10,21 +10,21 @@ protected override object[] GivenEvents() => [ protected override void When(Booking booking) => booking.RecordPayment("payment1", new(50), DateTimeOffset.Now); - [Fact] + [Test] public void should_produce_payment_registered() => Emitted(new BookingEvents.BookingPaymentRegistered("payment1", 50)); - [Fact] + [Test] public void should_produce_outstanding_changed() => Emitted(new BookingEvents.BookingOutstandingAmountChanged(50)); - [Fact] + [Test] public void should_not_be_fully_paid() => Then().State.IsFullyPaid().Should().BeFalse(); - [Fact] + [Test] public void should_record_payment() => Then().HasPaymentRecord("payment1").Should().BeTrue(); - [Fact] + [Test] public void should_not_be_overpaid() => Then().State.IsOverpaid().Should().BeFalse(); - [Fact] + [Test] public void should_produce_two_events() => Then().Changes.Should().HaveCount(2); } diff --git a/src/Core/test/Eventuous.Tests/Aggregates/TwoAggregateOpsSpec.cs b/src/Core/test/Eventuous.Tests/Aggregates/TwoAggregateOpsSpec.cs index d4b7008a1..3e6ad68ad 100644 --- a/src/Core/test/Eventuous.Tests/Aggregates/TwoAggregateOpsSpec.cs +++ b/src/Core/test/Eventuous.Tests/Aggregates/TwoAggregateOpsSpec.cs @@ -20,22 +20,22 @@ protected override void When(Booking booking) { booking.RecordPayment(_testData.PaymentId, amount, _testData.PaidAt); } - [Fact] + [Test] public void should_produce_fully_paid_event() => Emitted(new BookingFullyPaid(_testData.PaidAt)); - [Fact] + [Test] public void should_produce_payment_registered() => Emitted(new BookingPaymentRegistered(_testData.PaymentId, _testData.Amount)); - [Fact] + [Test] public void should_produce_outstanding_changed() => Emitted(new BookingOutstandingAmountChanged(0)); - [Fact] + [Test] public void should_make_booking_fully_paid() => Then().State.IsFullyPaid().Should().BeTrue(); - [Fact] + [Test] public void should_record_payment() => Then().HasPaymentRecord(_testData.PaymentId).Should().BeTrue(); - [Fact] + [Test] public void should_not_be_overpaid() => Then().State.IsOverpaid().Should().BeFalse(); readonly TestData _testData; diff --git a/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj b/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj index 912bd2e69..87e0e33bc 100644 --- a/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj +++ b/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj @@ -2,13 +2,23 @@ true true + Exe + true + true + true - - + + + + + - - + + + + + diff --git a/src/Core/test/Eventuous.Tests/ForgotToSetId.cs b/src/Core/test/Eventuous.Tests/ForgotToSetId.cs index 98500d1e5..0aa5b30ea 100644 --- a/src/Core/test/Eventuous.Tests/ForgotToSetId.cs +++ b/src/Core/test/Eventuous.Tests/ForgotToSetId.cs @@ -1,14 +1,15 @@ using Eventuous.Tests.Fixtures; +using static Xunit.TestContext; namespace Eventuous.Tests; public class ForgotToSetId : NaiveFixture { public ForgotToSetId() => Service = new(this.EventStore); - [Fact] + [Test] public async Task ShouldFailWithNoId() { var cmd = new DoIt(Auto.Create()); - var result = await Service.Handle(cmd, default); + var result = await Service.Handle(cmd, Current.CancellationToken); result.Success.Should().BeTrue(); } diff --git a/src/Core/test/Eventuous.Tests/StoringEvents.cs b/src/Core/test/Eventuous.Tests/StoringEvents.cs index 7c74c9bcb..ceb36e9ec 100644 --- a/src/Core/test/Eventuous.Tests/StoringEvents.cs +++ b/src/Core/test/Eventuous.Tests/StoringEvents.cs @@ -1,5 +1,6 @@ global using NodaTime; using static Eventuous.Sut.Domain.BookingEvents; +using static Xunit.TestContext; namespace Eventuous.Tests; @@ -15,7 +16,7 @@ public StoringEvents() { BookingService Service { get; } - [Fact] + [Test] public async Task StoreInitial() { var cmd = new Commands.BookRoom( Auto.Create(), @@ -27,7 +28,7 @@ public async Task StoreInitial() { Change[] expected = [new(new RoomBooked(cmd.RoomId, cmd.CheckIn, cmd.CheckOut, cmd.Price), TypeNames.RoomBooked)]; - var result = await Service.Handle(cmd, default); + var result = await Service.Handle(cmd, Current.CancellationToken); result.TryGet(out var ok).Should().BeTrue(); ok!.Changes.Should().BeEquivalentTo(expected); diff --git a/src/Core/test/Eventuous.Tests/StoringEventsWithCustomStream.cs b/src/Core/test/Eventuous.Tests/StoringEventsWithCustomStream.cs index 269207e5d..f3b184824 100644 --- a/src/Core/test/Eventuous.Tests/StoringEventsWithCustomStream.cs +++ b/src/Core/test/Eventuous.Tests/StoringEventsWithCustomStream.cs @@ -3,6 +3,7 @@ using Eventuous.Sut.App; using Eventuous.Sut.Domain; using static Eventuous.Sut.Domain.BookingEvents; +using static Xunit.TestContext; namespace Eventuous.Tests; @@ -16,13 +17,13 @@ public StoringEventsWithCustomStream() { BookingService Service { get; } - [Fact] + [Test] public async Task TestOnNew() { var cmd = CreateBookRoomCommand(); Change[] expected = [new(new RoomBooked(cmd.RoomId, cmd.CheckIn, cmd.CheckOut, cmd.Price), TypeNames.RoomBooked)]; - var result = await Service.Handle(cmd, default); + var result = await Service.Handle(cmd, Current.CancellationToken); result.TryGet(out var ok).Should().BeTrue(); ok!.Changes.Should().BeEquivalentTo(expected); @@ -32,11 +33,11 @@ public async Task TestOnNew() { evt[0].Payload.Should().BeEquivalentTo(ok.Changes.First().Event); } - [Fact] + [Test] public async Task TestOnExisting() { var cmd = CreateBookRoomCommand(); - await Service.Handle(cmd, default); + await Service.Handle(cmd, Current.CancellationToken); var secondCmd = new Commands.RecordPayment(new(cmd.BookingId), Auto.Create(), new(cmd.Price), DateTimeOffset.Now); @@ -46,7 +47,7 @@ public async Task TestOnExisting() { new(new BookingFullyPaid(secondCmd.PaidAt), TypeNames.BookingFullyPaid) }; - var result = await Service.Handle(secondCmd, default); + var result = await Service.Handle(secondCmd, Current.CancellationToken); result.TryGet(out var ok).Should().BeTrue(); ok!.Changes.Should().BeEquivalentTo(expected); diff --git a/src/Core/test/Eventuous.Tests/StreamNameMapTests.cs b/src/Core/test/Eventuous.Tests/StreamNameMapTests.cs index f096554ea..34598ea81 100644 --- a/src/Core/test/Eventuous.Tests/StreamNameMapTests.cs +++ b/src/Core/test/Eventuous.Tests/StreamNameMapTests.cs @@ -7,19 +7,19 @@ namespace Eventuous.Tests; public class StreamNameMapTests { readonly StreamNameMap _sut = new(); - [Fact] - public void Should_get_stream_name_for_id() { + [Test] + public async Task Should_get_stream_name_for_id() { var idString = GetId(); var id = new BookingId(idString); var streamName = StreamNameFactory.For(id); - streamName.ToString().Should().Be($"Booking-{idString}"); + await Assert.That(streamName.ToString()).IsEqualTo($"Booking-{idString}"); } - [Fact] - public void Should_get_default_stream_name_for_id() { + [Test] + public async Task Should_get_default_stream_name_for_id() { var idString = GetId(); var id = new BookingId(idString); var streamName = _sut.GetStreamName(id); - streamName.ToString().Should().Be($"Booking-{idString}"); + await Assert.That(streamName.ToString()).IsEqualTo($"Booking-{idString}"); } } diff --git a/src/Core/test/Eventuous.Tests/StreamNameTests.cs b/src/Core/test/Eventuous.Tests/StreamNameTests.cs index 6344cd63d..3b65d1f32 100644 --- a/src/Core/test/Eventuous.Tests/StreamNameTests.cs +++ b/src/Core/test/Eventuous.Tests/StreamNameTests.cs @@ -5,26 +5,26 @@ namespace Eventuous.Tests; using static Fixtures.IdGenerator; public class StreamNameTests { - [Fact] - public void Should_get_stream_name_for_state() { + [Test] + public async Task Should_get_stream_name_for_state() { var idString = GetId(); var streamName = StreamName.ForState(idString); - streamName.ToString().Should().Be($"Booking-{idString}"); + await Assert.That(streamName.ToString()).IsEqualTo($"Booking-{idString}"); } - [Fact] - public void Should_fail_when_id_is_null() { - Assert.Throws(() => _ = StreamName.For(null!)); + [Test] + public async Task Should_fail_when_id_is_null() { + await Assert.That(() => _ = StreamName.For(null!)).Throws(); } - [Fact] - public void Should_fail_when_id_is_empty() { - Assert.Throws(() => _ = StreamName.For(" ")); + [Test] + public async Task Should_fail_when_id_is_empty() { + await Assert.That(() => _ = StreamName.For(" ")).Throws(); } - [Fact] - public void Should_trim_id() { + [Test] + public async Task Should_trim_id() { var streamName = StreamName.ForState(" 123"); - streamName.ToString().Should().Be("Booking-123"); + await Assert.That(streamName.ToString()).IsEqualTo("Booking-123"); } } diff --git a/src/Core/test/Eventuous.Tests/TypeRegistrationTests.cs b/src/Core/test/Eventuous.Tests/TypeRegistrationTests.cs index a9c247f76..86831194d 100644 --- a/src/Core/test/Eventuous.Tests/TypeRegistrationTests.cs +++ b/src/Core/test/Eventuous.Tests/TypeRegistrationTests.cs @@ -1,3 +1,4 @@ +using TUnit.Assertions.Extensions.Generic; using static Eventuous.Sut.Domain.BookingEvents; namespace Eventuous.Tests; @@ -7,9 +8,9 @@ public class TypeRegistrationTests { public TypeRegistrationTests() => _typeMapper.RegisterKnownEventTypes(typeof(BookingCancelled).Assembly); - [Fact] - public void ShouldResolveDecoratedEvent() { - _typeMapper.GetTypeName().Should().Be(TypeNames.BookingCancelled); - _typeMapper.GetType(TypeNames.BookingCancelled).Should().Be(); + [Test] + public async Task ShouldResolveDecoratedEvent() { + await Assert.That(_typeMapper.GetTypeName()).IsEqualTo(TypeNames.BookingCancelled); + await Assert.That(_typeMapper.GetType(TypeNames.BookingCancelled)).IsEqualTo(typeof(BookingCancelled)); } } \ No newline at end of file diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj index cb503d315..3db109014 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj @@ -1,5 +1,6 @@ + false true true true @@ -13,6 +14,8 @@ + + @@ -31,4 +34,9 @@ + + + + + diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fakes/TestHandler.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fakes/TestHandler.cs index cab09ff32..c70f11e60 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fakes/TestHandler.cs +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fakes/TestHandler.cs @@ -1,3 +1,5 @@ +using Microsoft.Extensions.Logging; + namespace Eventuous.Tests.OpenTelemetry.Fakes; class TestHandler(MessageCounter counter, ILogger log) : BaseEventHandler { diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs index e9b29db41..60ffd21cc 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs @@ -70,7 +70,7 @@ protected override void GetDependencies(IServiceProvider provider) { public MessageCounter Counter { get; private set; } = null!; public TestExporter Exporter { get; } = new(); - public override async Task DisposeAsync() { + public override async ValueTask DisposeAsync() { await base.DisposeAsync(); Exporter.Dispose(); _listener?.Dispose(); diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs index 273aeddc6..d6df3292d 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs @@ -44,7 +44,7 @@ public void ShouldMeasureSubscriptionDuration() { static MetricValue? GetValue(MetricValue[] values, string metric) => values.FirstOrDefault(x => x.Name == metric); - public async Task InitializeAsync() { + public async ValueTask InitializeAsync() { await Fixture.InitializeAsync(); var testEvents = Fixture.Auto.CreateMany(Fixture.Count).ToList(); await Fixture.Producer.Produce(Fixture.Stream, testEvents, new Metadata()); @@ -61,7 +61,7 @@ public async Task InitializeAsync() { } } - public async Task DisposeAsync() { + public async ValueTask DisposeAsync() { await Fixture.DisposeAsync(); _es.Dispose(); } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 875b45537..b1a98442e 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,6 @@ - net6.0;net8.0 + net9.0;net8.0 preview $(NoWarn);CS1591;CS0618; enable @@ -21,11 +21,15 @@ true false + trx%3bLogFileName=$(MSBuildProjectName).trx $(RepoRoot)/test-results/$(TargetFramework) false true + true + true + true true @@ -56,21 +60,17 @@ - + - - - - diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/AppServiceTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/AppServiceTests.cs index a2a842985..ee92bd63b 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/AppServiceTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/AppServiceTests.cs @@ -1,6 +1,7 @@ using Eventuous.Sut.App; using Eventuous.Sut.Domain; using Eventuous.TestHelpers; +using static Xunit.TestContext; namespace Eventuous.Tests.EventStore; @@ -24,15 +25,10 @@ public async Task ProcessAnyForNew() { var expected = new object[] { new BookingEvents.BookingImported(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut) }; - var handlingResult = await Service.Handle(cmd, default); + var handlingResult = await Service.Handle(cmd, Current.CancellationToken); handlingResult.Success.Should().BeTrue(); - var events = await _fixture.EventStore.ReadEvents( - StreamName.For(cmd.BookingId), - StreamReadPosition.Start, - int.MaxValue, - default - ); + var events = await _fixture.EventStore.ReadEvents(StreamName.For(cmd.BookingId), StreamReadPosition.Start, int.MaxValue, Current.CancellationToken); var result = events.Select(x => x.Payload).ToArray(); diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj b/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj index 12980d371..82edcbc42 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj @@ -3,6 +3,7 @@ true true true + Exe diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/DomainFixture.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/DomainFixture.cs index d638f1d7b..d8a039f47 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/DomainFixture.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/DomainFixture.cs @@ -1,10 +1,11 @@ using Eventuous.Sut.App; +using Eventuous.Sut.Domain; using MicroElements.AutoFixture.NodaTime; namespace Eventuous.Tests.EventStore.Fixtures; public static class DomainFixture { - static DomainFixture() => TypeMap.RegisterKnownEventTypes(); + static DomainFixture() => TypeMap.RegisterKnownEventTypes(typeof(BookingEvents.BookingImported).Assembly); static IFixture Auto { get; } = new Fixture().Customize(new NodaTimeCustomization()); diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/ProducerTracesTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/ProducerTracesTests.cs index 0b1632bfe..c24642b64 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/ProducerTracesTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/ProducerTracesTests.cs @@ -4,6 +4,7 @@ using Eventuous.TestHelpers; using Eventuous.Tests.EventStore.Subscriptions.Fixtures; using Eventuous.Tests.Subscriptions.Base; +using static Xunit.TestContext; namespace Eventuous.Tests.EventStore; @@ -33,11 +34,11 @@ public class TracesTests : LegacySubscriptionFixture, IDisposable public async Task ShouldPropagateRemoteContext() { var testEvent = Auto.Create(); - await Producer.Produce(Stream, testEvent, new()); + await Producer.Produce(Stream, testEvent, new(), cancellationToken: Current.CancellationToken); await Start(); - var writtenEvent = (await StoreFixture.EventStore.ReadEvents(Stream, StreamReadPosition.Start, 1, default))[0]; + var writtenEvent = (await StoreFixture.EventStore.ReadEvents(Stream, StreamReadPosition.Start, 1, Current.CancellationToken))[0]; var meta = writtenEvent.Metadata; var (traceId, spanId, _) = meta.GetTracingMeta(); @@ -46,7 +47,7 @@ public async Task ShouldPropagateRemoteContext() { spanId.Should().NotBe(RecordedTrace.DefaultSpanId); while (Handler.Contexts.Count == 0) { - await Task.Delay(100); + await Task.Delay(100, Current.CancellationToken); } await Stop(); diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/RegistrationTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/RegistrationTests.cs index 0a712fbfa..2451863e5 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/RegistrationTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/RegistrationTests.cs @@ -42,7 +42,7 @@ public void ShouldHaveNoOpStore() { store.Should().BeOfType(); } - public Task InitializeAsync() { + public ValueTask InitializeAsync() { var services = new ServiceCollection(); services.AddSingleton(fixture.Client); @@ -59,10 +59,10 @@ public Task InitializeAsync() { Provider = services.BuildServiceProvider(); Sub = Provider.GetService()!; - return Task.CompletedTask; + return ValueTask.CompletedTask; } - public Task DisposeAsync() => Task.CompletedTask; + public ValueTask DisposeAsync() => ValueTask.CompletedTask; } public class TestHandler : BaseEventHandler { diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs index 438bce6fc..7c7f82ba2 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs @@ -1,6 +1,8 @@ using System.Collections.Immutable; +using Eventuous.TestHelpers.Logging; using JetBrains.Annotations; using static Eventuous.AggregateFactoryRegistry; +using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Store; @@ -11,7 +13,7 @@ public class AggregateStoreTests : IClassFixture { public AggregateStoreTests(StoreFixture fixture, ITestOutputHelper output) { _fixture = fixture; _fixture.TypeMapper.AddType("testAggregateEvent"); - var loggerFactory = LoggerFactory.Create(cfg => cfg.AddXunit(output).SetMinimumLevel(LogLevel.Debug)); + var loggerFactory = LoggerFactory.Create(cfg => cfg.AddXUnit(output).SetMinimumLevel(LogLevel.Debug)); _log = loggerFactory.CreateLogger(); } @@ -64,12 +66,12 @@ public async Task ShouldReadAggregateStreamManyTimes() { var id = new TestId(Guid.NewGuid().ToString("N")); var aggregate = Instance.CreateInstance(); aggregate.DoIt("test"); - await _fixture.AggregateStore.Store(aggregate, id, default); + await _fixture.AggregateStore.Store(aggregate, id, Current.CancellationToken); const int numberOfReads = 100; foreach (var unused in Enumerable.Range(0, numberOfReads)) { - var read = await _fixture.AggregateStore.Load(id, default); + var read = await _fixture.AggregateStore.Load(id, Current.CancellationToken); read.State.Should().BeEquivalentTo(aggregate.State); } } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs index 90d9c87cf..7c8de04a5 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs @@ -1,6 +1,8 @@ using System.Collections.Immutable; +using Eventuous.TestHelpers.Logging; using JetBrains.Annotations; using static Eventuous.AggregateFactoryRegistry; +using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Store; @@ -12,7 +14,7 @@ public class EventStoreAggregateTests : IClassFixture, IDisposable public EventStoreAggregateTests(StoreFixture fixture, ITestOutputHelper output) { _fixture = fixture; _fixture.TypeMapper.AddType("testEvent"); - _loggerFactory = LoggerFactory.Create(cfg => cfg.AddXunit(output).SetMinimumLevel(LogLevel.Debug)); + _loggerFactory = LoggerFactory.Create(cfg => cfg.AddXUnit(output).SetMinimumLevel(LogLevel.Debug)); _log = _loggerFactory.CreateLogger(); } @@ -22,10 +24,10 @@ public async Task AppendedEventShouldBeTraced() { var id = new TestId(Guid.NewGuid().ToString("N")); var aggregate = Instance.CreateInstance(); aggregate.DoIt("test"); - await _fixture.EventStore.StoreAggregate(aggregate, id); + await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: Current.CancellationToken); var streamName = StreamNameFactory.For(id); - var events = await _fixture.EventStore.ReadStream(streamName, StreamReadPosition.Start); + var events = await _fixture.EventStore.ReadStream(streamName, StreamReadPosition.Start, cancellationToken: Current.CancellationToken); var first = events[0]; first.Metadata["trace-id"].Should().NotBeNull(); @@ -49,15 +51,15 @@ public async Task ShouldReadLongAggregateStream() { if (counter != 1000) continue; _log.LogInformation("Storing batch of events.."); - await _fixture.EventStore.StoreAggregate(aggregate, id); - aggregate = await _fixture.EventStore.LoadAggregate(id); + await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: Current.CancellationToken); + aggregate = await _fixture.EventStore.LoadAggregate(id, cancellationToken: Current.CancellationToken); counter = 0; } - await _fixture.EventStore.StoreAggregate(aggregate, id); + await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: Current.CancellationToken); _log.LogInformation("Loading large aggregate stream.."); - var restored = await _fixture.EventStore.LoadAggregate(id); + var restored = await _fixture.EventStore.LoadAggregate(id, cancellationToken: Current.CancellationToken); restored.State.Values.Count.Should().Be(count); restored.State.Values.Should().BeEquivalentTo(aggregate.State.Values); @@ -69,12 +71,12 @@ public async Task ShouldReadAggregateStreamManyTimes() { var id = new TestId(Guid.NewGuid().ToString("N")); var aggregate = Instance.CreateInstance(); aggregate.DoIt("test"); - await _fixture.EventStore.StoreAggregate(aggregate, id); + await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: Current.CancellationToken); const int numberOfReads = 100; foreach (var unused in Enumerable.Range(0, numberOfReads)) { - var read = await _fixture.EventStore.LoadAggregate(id); + var read = await _fixture.EventStore.LoadAggregate(id, cancellationToken: Current.CancellationToken); read.State.Should().BeEquivalentTo(aggregate.State); } } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/LegacySubscriptionFixture.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/LegacySubscriptionFixture.cs index 7e3f9a889..facb4e3fb 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/LegacySubscriptionFixture.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/LegacySubscriptionFixture.cs @@ -2,6 +2,7 @@ using Eventuous.EventStore.Subscriptions; using Eventuous.Subscriptions.Filters; using Eventuous.Tests.Subscriptions.Base; +using LoggingExtensions = Eventuous.TestHelpers.Logging.LoggingExtensions; namespace Eventuous.Tests.EventStore.Subscriptions.Fixtures; @@ -26,7 +27,7 @@ protected LegacySubscriptionFixture( _autoStart = autoStart; if (stream is { } s) Stream = s; - LoggerFactory = TestHelpers.Logging.GetLoggerFactory(output, logLevel); + LoggerFactory = LoggingExtensions.GetLoggerFactory(output, logLevel); Handler = handler; Log = LoggerFactory.CreateLogger(GetType()); StoreFixture.TypeMapper.RegisterKnownEventTypes(typeof(TestEvent).Assembly); @@ -39,7 +40,7 @@ protected LegacySubscriptionFixture( readonly bool _autoStart; - public async Task InitializeAsync() { + public async ValueTask InitializeAsync() { await StoreFixture.InitializeAsync(); Producer = new(StoreFixture.Client); @@ -61,7 +62,7 @@ public async Task InitializeAsync() { if (_autoStart) await Start(); } - public async Task DisposeAsync() { + public async ValueTask DisposeAsync() { if (_autoStart) await Stop(); await StoreFixture.DisposeAsync(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/PersistentSubscriptionFixture.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/PersistentSubscriptionFixture.cs index 62bfb06ab..2800b14a6 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/PersistentSubscriptionFixture.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/PersistentSubscriptionFixture.cs @@ -2,6 +2,7 @@ using Eventuous.EventStore.Producers; using Eventuous.EventStore.Subscriptions; using Eventuous.Tests.Subscriptions.Base; +using LoggingExtensions = Eventuous.TestHelpers.Logging.LoggingExtensions; namespace Eventuous.Tests.EventStore.Subscriptions.Fixtures; @@ -32,11 +33,11 @@ public abstract class PersistentSubscriptionFixture(count).ToList(); await Start(); - await Producer.Produce(Stream, testEvents, new()); - await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(); + await Producer.Produce(Stream, testEvents, new(), cancellationToken: Current.CancellationToken); + await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); await Stop(); CheckpointStore.GetCheckpoint(Subscription.SubscriptionId).Should().Be(count - 1); diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeOneTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeOneTests.cs index 7482ddf60..e42e05f87 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeOneTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeOneTests.cs @@ -1,6 +1,7 @@ using Eventuous.Producers; using Eventuous.Tests.EventStore.Subscriptions.Fixtures; using Eventuous.Tests.Subscriptions.Base; +using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Subscriptions; @@ -13,11 +14,11 @@ public async Task SubscribeAndProduce() { var testEvent = Auto.Create(); await Start(); - await Producer.Produce(Stream, testEvent, new Metadata()); - await Handler.AssertCollection(5.Seconds(), [testEvent]).Validate(); + await Producer.Produce(Stream, testEvent, new Metadata(), cancellationToken: Current.CancellationToken); + await Handler.AssertCollection(5.Seconds(), [testEvent]).Validate(Current.CancellationToken); await Stop(); - await Task.Delay(100); + await Task.Delay(100, Current.CancellationToken); CheckpointStore.GetCheckpoint(Subscription.SubscriptionId).Should().Be(0); } } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamPersistentPublishAndSubscribeManyTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamPersistentPublishAndSubscribeManyTests.cs index b62aaa18f..8f7be23c2 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamPersistentPublishAndSubscribeManyTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamPersistentPublishAndSubscribeManyTests.cs @@ -4,6 +4,7 @@ using Eventuous.Subscriptions.Filters; using Eventuous.Tests.EventStore.Subscriptions.Fixtures; using Eventuous.Tests.Subscriptions.Base; +using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Subscriptions; @@ -18,8 +19,8 @@ public async Task SubscribeAndProduceMany() { var testEvents = Auto.CreateMany(count).ToList(); await Start(); - await Producer.Produce(Stream, testEvents, new()); - await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(); + await Producer.Produce(Stream, testEvents, new(), cancellationToken: Current.CancellationToken); + await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); await Stop(); } @@ -46,8 +47,8 @@ public async Task SubscribeAndProduceMany() { var testEvents = Auto.CreateMany(count).ToList(); await Start(); - await Producer.Produce(Stream, testEvents, new()); - await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(); + await Producer.Produce(Stream, testEvents, new(), cancellationToken: Current.CancellationToken); + await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); await Stop(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs index ff923c53b..c238a0d19 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs @@ -5,7 +5,9 @@ using Eventuous.Subscriptions.Filters; using Eventuous.Sut.App; using Eventuous.Sut.Domain; +using Eventuous.TestHelpers.Logging; using Eventuous.Tests.Subscriptions.Base; +using static Xunit.TestContext; using StreamSubscription = Eventuous.EventStore.Subscriptions.StreamSubscription; namespace Eventuous.Tests.EventStore.Subscriptions; @@ -18,7 +20,7 @@ public sealed class StreamSubscriptionDeletedEventsTests : IClassFixture cfg.AddXunit(output, LogLevel.Debug).SetMinimumLevel(LogLevel.Debug)); + _loggerFactory = LoggerFactory.Create(cfg => cfg.AddXUnit(output).SetMinimumLevel(LogLevel.Debug)); _listener = new(_loggerFactory); _fixture.TypeMapper.RegisterKnownEventTypes(typeof(BookingEvents.BookingImported).Assembly); } @@ -31,7 +33,8 @@ public async Task StreamSubscriptionGetsDeletedEvents() { ulong? startPosition = null; try { - var last = await _fixture.Client.ReadStreamAsync(Direction.Backwards, categoryStream, StreamPosition.End, 1).ToArrayAsync(); + var last = await _fixture.Client.ReadStreamAsync(Direction.Backwards, categoryStream, StreamPosition.End, 1, cancellationToken: Current.CancellationToken) + .ToArrayAsync(Current.CancellationToken); startPosition = last[0].OriginalEventNumber; } catch (StreamNotFoundException) { } @@ -40,7 +43,9 @@ public async Task StreamSubscriptionGetsDeletedEvents() { var commands = Enumerable.Range(0, produceCount).Select(_ => DomainFixture.CreateImportBooking()).ToArray(); - await Task.WhenAll(commands.Select(x => service.Handle(x, CancellationToken.None))); + foreach (var command in commands) { + await service.Handle(command, CancellationToken.None); + } var delete = Enumerable.Range(5, deleteCount).Select(x => commands[x]).ToList(); @@ -55,10 +60,10 @@ await Task.WhenAll( var subscription = new StreamSubscription( _fixture.Client, new() { - StreamName = categoryStream, - SubscriptionId = subscriptionId, - ResolveLinkTos = true, - ThrowOnError = true, + StreamName = categoryStream, + SubscriptionId = subscriptionId, + ResolveLinkTos = true, + ThrowOnError = true, }, new NoOpCheckpointStore(startPosition), new ConsumePipe().AddSystemEventsFilter().AddDefaultConsumer(handler), diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionWithLinksTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionWithLinksTests.cs index eecca8c79..a655d9d5c 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionWithLinksTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionWithLinksTests.cs @@ -5,6 +5,7 @@ using Eventuous.Subscriptions.Context; using Eventuous.Tests.Subscriptions.Base; using Microsoft.Extensions.DependencyInjection; +using static Xunit.TestContext; using StreamSubscription = Eventuous.EventStore.Subscriptions.StreamSubscription; namespace Eventuous.Tests.EventStore.Subscriptions; @@ -36,7 +37,7 @@ public async Task ShouldHandleHalfOfTheEvents() { const int expectedCount = count / 2; var checkpointStore = Provider.GetRequiredService(); - await checkpointStore.StoreCheckpoint(new(SubId, expectedCount - 1), true, default); + await checkpointStore.StoreCheckpoint(new(SubId, expectedCount - 1), true, Current.CancellationToken); await Start(); await Execute(count, expectedCount); diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscriptionIgnoredMessagesTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscriptionIgnoredMessagesTests.cs index 8b3ec79fc..dca93ce51 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscriptionIgnoredMessagesTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscriptionIgnoredMessagesTests.cs @@ -6,6 +6,7 @@ using Eventuous.Producers; using Eventuous.Tests.Subscriptions.Base; using Microsoft.Extensions.DependencyInjection; +using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Subscriptions; @@ -32,17 +33,17 @@ public async Task SubscribeAndProduceManyWithIgnored() { TypeMapper.AddType(TestEvent.TypeName); TypeMapper.AddType("ignored"); Output?.WriteLine($"Producing to {_stream}"); - await _producer.Produce(_stream, testEvents, new Metadata()); + await _producer.Produce(_stream, testEvents, new Metadata(), cancellationToken: Current.CancellationToken); Output?.WriteLine("Produce complete"); TypeMapper.RemoveType(); var expected = testEvents.Where(x => x.GetType() == typeof(TestEvent)).ToList(); await Start(); - await _handler.AssertCollection(5.Seconds(), expected).Validate(); + await _handler.AssertCollection(5.Seconds(), expected).Validate(Current.CancellationToken); await DisposeAsync(); - var last = await _checkpointStore.GetLastCheckpoint(_subscriptionId, default); + var last = await _checkpointStore.GetLastCheckpoint(_subscriptionId, Current.CancellationToken); last.Position.Should().Be((ulong)(testEvents.Count - 1)); return; diff --git a/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj b/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj index 5a9645724..a68958853 100644 --- a/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj +++ b/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj @@ -1,6 +1,6 @@ - net6.0 + net8.0 Exe false true diff --git a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Eventuous.Extensions.AspNetCore.csproj b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Eventuous.Extensions.AspNetCore.csproj index 2829a5cd7..1d6178dfc 100644 --- a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Eventuous.Extensions.AspNetCore.csproj +++ b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Eventuous.Extensions.AspNetCore.csproj @@ -2,6 +2,7 @@ + diff --git a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Logging/AppBuilderLoggingExtensions.cs b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Logging/AppBuilderLoggingExtensions.cs new file mode 100644 index 000000000..62d8b1930 --- /dev/null +++ b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Logging/AppBuilderLoggingExtensions.cs @@ -0,0 +1,26 @@ +// Copyright (C) Ubiquitous AS.All rights reserved +// Licensed under the Apache License, Version 2.0. + +using System.Diagnostics.Tracing; +using Microsoft.AspNetCore.Builder; + +// ReSharper disable once CheckNamespace +namespace Microsoft.Extensions.Hosting; + + +[PublicAPI] +public static class AppBuilderLoggingExtensions { + /// + /// Add Eventuous logging from internal event sources to the application logging + /// + /// Host builder + /// Event level, default is Verbose. Decrease the level to improve performance. + /// Event keywords, default is All + /// + public static IApplicationBuilder UseEventuousLogs(this IApplicationBuilder host, EventLevel level = EventLevel.Verbose, EventKeywords keywords = EventKeywords.All) { + host.ApplicationServices.AddEventuousLogs(level, keywords); + + return host; + } + +} diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/AggregateFactoryHostExtensions.cs b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/AggregateFactoryHostExtensions.cs deleted file mode 100644 index 48afd218f..000000000 --- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/AggregateFactoryHostExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) Ubiquitous AS. All rights reserved -// Licensed under the Apache License, Version 2.0. - -// ReSharper disable CheckNamespace - -namespace Microsoft.Extensions.Hosting; - -using DependencyInjection; - -[PublicAPI] -public static class AggregateFactoryBuilderExtensions { - /// - /// Adds registered aggregate factories to the registry. The registry is then used by - /// and - /// - /// - /// - public static IHost UseAggregateFactory(this IHost host) { - UseAggregateFactory(host.Services); - - return host; - } - - static void UseAggregateFactory(IServiceProvider sp) { - var resolvers = sp.GetServices(); - var registry = sp.GetService() ?? AggregateFactoryRegistry.Instance; - - foreach (var resolver in resolvers) { - registry.UnsafeCreateAggregateUsing(resolver.Type, () => resolver.CreateInstance(sp)); - } - } -} diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Eventuous.Extensions.DependencyInjection.csproj b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Eventuous.Extensions.DependencyInjection.csproj index 448112999..069882f0d 100644 --- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Eventuous.Extensions.DependencyInjection.csproj +++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Eventuous.Extensions.DependencyInjection.csproj @@ -3,11 +3,14 @@ README.md - + + + + diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/LoggingHostExtensions.cs b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/LoggingHostExtensions.cs deleted file mode 100644 index d1edf03d3..000000000 --- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/LoggingHostExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) Ubiquitous AS. All rights reserved -// Licensed under the Apache License, Version 2.0. - -using System.Diagnostics.Tracing; -using Eventuous.Diagnostics.Logging; - -// ReSharper disable once CheckNamespace -namespace Microsoft.Extensions.Hosting; - -using DependencyInjection; -using Logging; - -[PublicAPI] -public static class LoggingAppBuilderExtensions { - /// - /// Add Eventuous logging from internal event sources to the application logging - /// - /// Host builder - /// Event level, default is Verbose. Decrease the level to improve performance. - /// Event keywords, default is All - /// - public static IHost UseEventuousLogs(this IHost host, EventLevel level = EventLevel.Verbose, EventKeywords keywords = EventKeywords.All) { - AddEventuousLogs(host.Services, level, keywords); - - return host; - } - - /// - /// Adds the Eventuous logging from internal event sources to the application logging. - /// You'd not normally call this method directly, but use - /// - /// - /// - /// - public static void AddEventuousLogs(this IServiceProvider provider, EventLevel level = EventLevel.Verbose, EventKeywords keywords = EventKeywords.All) { - var factory = provider.GetService(); - - if (factory != null) listener ??= new(factory, level: level, keywords: keywords); - } - - static LoggingEventListener? listener; -} diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/LoggingServiceProviderExtensions.cs b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/LoggingServiceProviderExtensions.cs new file mode 100644 index 000000000..d155271cb --- /dev/null +++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/LoggingServiceProviderExtensions.cs @@ -0,0 +1,27 @@ +// Copyright (C) Ubiquitous AS. All rights reserved +// Licensed under the Apache License, Version 2.0. + +using System.Diagnostics.Tracing; +using Eventuous.Extensions.Logging; + +// ReSharper disable once CheckNamespace +namespace Microsoft.Extensions.Hosting; + +using DependencyInjection; +using Logging; + +[PublicAPI] +public static class LoggingServiceProviderExtensions { + /// + /// Adds the Eventuous logging from internal event sources to the application logging. + /// You'd not normally call this method directly, but use UseEventuousLogs from Eventuous.Extensions.AspNetCore/> + /// + /// + /// + /// + public static void AddEventuousLogs(this IServiceProvider provider, EventLevel level = EventLevel.Verbose, EventKeywords keywords = EventKeywords.All) { + var factory = provider.GetService(); + + factory.AddEventuousLogs(level, keywords); + } +} diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/README.md b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/README.md index 9fa21e40f..cc3a08d9a 100644 --- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/README.md +++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/README.md @@ -3,11 +3,7 @@ This package adds several DI extensions for `IServiceCollection`: - `AddCommandService` to register app services -- `AddAggregateStore` to register the `AggregateStore` and a given `IEventStore` +- `AddAggregateStore` to register the `AggregateStore` and a given `IEventStore` (Eventuous does not need aggregate store to be registered, only use it if you use the aggregate store in your application directly) - `AddAggregate` to register aggregate types that require dependencies Keep in mind that we don't recommend having dependencies in aggregates, so you'd normally not need to use `AddAggregate`. - -When using `AddAggregate`, you should also call `builder.UseAggregateFactory()` in `Startup.Configure`. - -You can also add Eventuous logs to the logging provider by calling `app.AddEventuousLogs()` diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateFactory.cs b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateFactory.cs index 8fe9c9510..f319aca0c 100644 --- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateFactory.cs +++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateFactory.cs @@ -43,5 +43,3 @@ public static IServiceCollection AddAggregate(this IServiceCollection return services.AddSingleton(new ResolveAggregateFactory(typeof(T), createInstance)); } } - -record ResolveAggregateFactory(Type Type, Func CreateInstance); diff --git a/src/Extensions/src/Eventuous.Extensions.Logging/Eventuous.Extensions.Logging.csproj b/src/Extensions/src/Eventuous.Extensions.Logging/Eventuous.Extensions.Logging.csproj new file mode 100644 index 000000000..411072b55 --- /dev/null +++ b/src/Extensions/src/Eventuous.Extensions.Logging/Eventuous.Extensions.Logging.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Extensions/src/Eventuous.Extensions.Logging/LoggerFactoryExtensions.cs b/src/Extensions/src/Eventuous.Extensions.Logging/LoggerFactoryExtensions.cs new file mode 100644 index 000000000..6ea14a56a --- /dev/null +++ b/src/Extensions/src/Eventuous.Extensions.Logging/LoggerFactoryExtensions.cs @@ -0,0 +1,23 @@ +// Copyright (C) Ubiquitous AS. All rights reserved +// Licensed under the Apache License, Version 2.0. + +using System.Diagnostics.Tracing; +using Eventuous.Diagnostics.Logging; +using Microsoft.Extensions.Logging; + +namespace Eventuous.Extensions.Logging; + +public static class LoggerFactoryExtensions { + /// + /// Adds the Eventuous logging from internal event sources to the application logging. + /// Use it only if you are not building an ASP.NET Core application, otherwise use UseEventuousLogs from Eventuous.Extensions.AspNetCore + /// + /// Logger factory instance + /// + /// + public static void AddEventuousLogs(this ILoggerFactory? factory, EventLevel level = EventLevel.Verbose, EventKeywords keywords = EventKeywords.All) { + if (factory != null) listener ??= new(factory, level: level, keywords: keywords); + } + + static LoggingEventListener? listener; +} diff --git a/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj b/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj index caa2a0c08..35f52bf90 100644 --- a/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj +++ b/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Extensions/test/Eventuous.Tests.DependencyInjection/AggregateFactoryRegistrationTests.cs b/src/Extensions/test/Eventuous.Tests.DependencyInjection/AggregateFactoryRegistrationTests.cs index e8269d35f..0df2d38e4 100644 --- a/src/Extensions/test/Eventuous.Tests.DependencyInjection/AggregateFactoryRegistrationTests.cs +++ b/src/Extensions/test/Eventuous.Tests.DependencyInjection/AggregateFactoryRegistrationTests.cs @@ -1,7 +1,6 @@ using Eventuous.Tests.AspNetCore.Sut; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; namespace Eventuous.Tests.AspNetCore; @@ -10,8 +9,8 @@ public class AggregateFactoryRegistrationTests { public AggregateFactoryRegistrationTests() { var host = BuildHost(); + host.Services.AddAggregate(); var app = host.Build(); - app.UseAggregateFactory(); _registry = app.Services.GetRequiredService(); } diff --git a/src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj b/src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj index c729223a6..0efebea26 100644 --- a/src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj +++ b/src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj @@ -2,6 +2,7 @@ true Eventuous.Tests.AspNetCore + Exe diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/ControllerTests.cs b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/ControllerTests.cs index 1c533a45c..0738ec123 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/ControllerTests.cs +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/ControllerTests.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc.Testing; using static Eventuous.Sut.App.Commands; using static Eventuous.Sut.AspNetCore.BookingApi; +using static Xunit.TestContext; namespace Eventuous.Tests.Extensions.AspNetCore; @@ -40,12 +41,12 @@ public async Task RecordPaymentUsingMappedCommand() { var bookRoom = _fixture.GetBookRoom(); - await client.PostJsonAsync("/book", bookRoom); + await client.PostJsonAsync("/book", bookRoom, cancellationToken: Current.CancellationToken); var registerPayment = new RegisterPaymentHttp(bookRoom.BookingId, bookRoom.RoomId, 100, DateTimeOffset.Now); var request = new RestRequest("/v2/pay").AddJsonBody(registerPayment); - var response = await client.ExecutePostAsync.Ok>(request); + var response = await client.ExecutePostAsync.Ok>(request, cancellationToken: Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); var expected = new BookingEvents.BookingFullyPaid(registerPayment.PaidAt); diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj index 9446e2267..f75bd19db 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj @@ -2,9 +2,10 @@ true true + Exe - + @@ -12,8 +13,7 @@ - - + diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs index b033d7355..4aff5d01a 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs @@ -3,6 +3,7 @@ using System.Text.Json; using Eventuous.TestHelpers; +using Eventuous.TestHelpers.Logging; using Microsoft.AspNetCore.Mvc.Testing; using RestSharp.Serializers.Json; @@ -29,7 +30,7 @@ public ServerFixture( if (configure != null) services.AddSingleton(configure); } ) - .ConfigureLogging(x => x.AddXunit(output).AddConsole().SetMinimumLevel(LogLevel.Debug)); + .ConfigureLogging(x => x.AddXUnit(output).AddConsole().SetMinimumLevel(LogLevel.Debug)); } ); builder.Server.PreserveExecutionContext = false; @@ -41,7 +42,7 @@ public ServerFixture( readonly WebApplicationFactory _app; public RestClient GetClient() { - return new RestClient( + return new( _app.CreateClient(), disposeHttpClient: true, configureSerialization: s => s.UseSerializer(() => new SystemTextJsonSerializer(_options)) diff --git a/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj b/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj index 80470d47e..39e128d09 100644 --- a/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj +++ b/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj @@ -1,6 +1,7 @@ true + Exe diff --git a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj index 0dd870442..34de9ee70 100644 --- a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj +++ b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj @@ -1,4 +1,7 @@ + + Exe + diff --git a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs index 9d7518f89..ebcc15650 100644 --- a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs +++ b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs @@ -23,7 +23,7 @@ public static async Task DeleteTopic(string topicId) { IContainer _container = null!; - public async Task InitializeAsync() { + public async ValueTask InitializeAsync() { const int port = 8085; _container = new ContainerBuilder() @@ -38,7 +38,7 @@ public async Task InitializeAsync() { Environment.SetEnvironmentVariable("PUBSUB_PROJECT_ID", PubsubProjectId); } - public async Task DisposeAsync() { + public async ValueTask DisposeAsync() { await _container.StopAsync(); } } diff --git a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs index da8c1ec4d..4d4a52523 100644 --- a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs +++ b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs @@ -2,8 +2,10 @@ using Eventuous.GooglePubSub.Subscriptions; using Eventuous.Producers; using Eventuous.Subscriptions.Filters; +using Eventuous.TestHelpers.Logging; using Eventuous.Tests.Subscriptions.Base; using Google.Api.Gax; +using static Xunit.TestContext; namespace Eventuous.Tests.GooglePubSub; @@ -21,7 +23,7 @@ public class PubSubTests : IAsyncLifetime, IClassFixture { // ReSharper disable once UnusedParameter.Local public PubSubTests(PubSubFixture _, ITestOutputHelper outputHelper) { - var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddXunit(outputHelper)); + var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddXUnit(outputHelper)); _log = loggerFactory.CreateLogger(); _pubsubTopic = new($"test-{Guid.NewGuid():N}"); @@ -49,9 +51,9 @@ public PubSubTests(PubSubFixture _, ITestOutputHelper outputHelper) { public async Task SubscribeAndProduce() { var testEvent = Auto.Create(); - await _producer.Produce(_pubsubTopic, testEvent, null); + await _producer.Produce(_pubsubTopic, testEvent, null, cancellationToken: Current.CancellationToken); - await _handler.AssertThat().Timebox(10.Seconds()).Any().Match(x => x as TestEvent == testEvent).Validate(); + await _handler.AssertThat().Timebox(10.Seconds()).Any().Match(x => x as TestEvent == testEvent).Validate(Current.CancellationToken); } [Fact] @@ -60,16 +62,16 @@ public async Task SubscribeAndProduceMany() { var testEvents = Auto.CreateMany(count).ToList(); - await _producer.Produce(_pubsubTopic, testEvents, null); - await _handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(); + await _producer.Produce(_pubsubTopic, testEvents, null, cancellationToken: Current.CancellationToken); + await _handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); } - public async Task InitializeAsync() { + public async ValueTask InitializeAsync() { await _producer.StartAsync(); await _subscription.SubscribeWithLog(_log); } - public async Task DisposeAsync() { + public async ValueTask DisposeAsync() { await _producer.StopAsync(); await _subscription.UnsubscribeWithLog(_log); diff --git a/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs b/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs index dfdca9c52..9a60d97bf 100644 --- a/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs +++ b/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs @@ -6,6 +6,7 @@ using Eventuous.Tools; using static System.String; using static Eventuous.DeserializationResult; +using static Xunit.TestContext; namespace Eventuous.Tests.Kafka; @@ -39,8 +40,8 @@ public async Task ShouldProduceAndWait() { async Task Produce() { await using var producer = new KafkaBasicProducer(new(new() { BootstrapServers = _fixture.BootstrapServers })); - await producer.StartAsync(default); - await producer.Produce(new(topicName), events, new(), new("test")); + await producer.StartAsync(Current.CancellationToken); + await producer.Produce(new(topicName), events, new(), new("test"), cancellationToken: Current.CancellationToken); } async Task ExecuteConsume() { diff --git a/src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj b/src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj index 10ca12d94..31a7ce2e0 100644 --- a/src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj +++ b/src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj @@ -1,8 +1,9 @@ false - osx.13-arm64 + osx-arm64 false + Exe diff --git a/src/Kafka/test/Eventuous.Tests.Kafka/KafkaFixture.cs b/src/Kafka/test/Eventuous.Tests.Kafka/KafkaFixture.cs index 714ba2c3c..2b5623b03 100644 --- a/src/Kafka/test/Eventuous.Tests.Kafka/KafkaFixture.cs +++ b/src/Kafka/test/Eventuous.Tests.Kafka/KafkaFixture.cs @@ -8,7 +8,7 @@ namespace Eventuous.Tests.Kafka; public class KafkaFixture : IAsyncLifetime { KafkaContainer _kafkaContainer = null!; - public async Task InitializeAsync() { + public async ValueTask InitializeAsync() { _kafkaContainer = new KafkaBuilder() .WithImage("confluentinc/cp-kafka:7.2.6") .Build(); @@ -17,7 +17,5 @@ public async Task InitializeAsync() { public string BootstrapServers => _kafkaContainer.GetBootstrapAddress(); - public async Task DisposeAsync() { - await _kafkaContainer.DisposeAsync(); - } + public async ValueTask DisposeAsync() => await _kafkaContainer.DisposeAsync(); } diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj index a93ce3baa..88e36e840 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj @@ -3,6 +3,7 @@ true true true + Exe diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs index 2cc041283..65b5c28ec 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs @@ -32,7 +32,7 @@ static IntegrationFixture() { EventStoreDbContainer _esdbContainer = null!; MongoDbContainer _mongoContainer = null!; - public async Task InitializeAsync() { + public async ValueTask InitializeAsync() { _esdbContainer = new EventStoreDbBuilder().Build(); await _esdbContainer.StartAsync(); var settings = EventStoreClientSettings.Create(_esdbContainer.GetConnectionString()); @@ -44,7 +44,7 @@ public async Task InitializeAsync() { Mongo = new MongoClient(mongoSettings).GetDatabase("bookings"); } - public async Task DisposeAsync() { + public async ValueTask DisposeAsync() { await Client.DisposeAsync(); await _esdbContainer.DisposeAsync(); } diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs index 304f8ee4d..d65df595b 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs @@ -4,6 +4,7 @@ using Eventuous.Tests.Projections.MongoDB.Fixtures; using MongoDB.Driver; using static Eventuous.Sut.Domain.BookingEvents; +using static Xunit.TestContext; namespace Eventuous.Tests.Projections.MongoDB; @@ -29,7 +30,7 @@ public async Task ShouldProjectImported() { StreamPosition = (ulong)append.NextExpectedVersion }; - var actual = await Fixture.Mongo.LoadDocument(id.ToString()); + var actual = await Fixture.Mongo.LoadDocument(id.ToString(), cancellationToken: Current.CancellationToken); actual.Should().Be(expected); } diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs index 219d2dac3..fee081702 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs @@ -2,6 +2,7 @@ using Eventuous.Projections.MongoDB; using Eventuous.Subscriptions; using Eventuous.Subscriptions.Checkpoints; +using Eventuous.TestHelpers.Logging; using Eventuous.Tests.Projections.MongoDB.Fixtures; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -16,11 +17,11 @@ protected ProjectionTestBase(string id, IntegrationFixture fixture, ITestOutputH Fixture = fixture; var builder = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder() - .ConfigureLogging(cfg => cfg.AddXunit(output, LogLevel.Debug).SetMinimumLevel(LogLevel.Trace)) + .ConfigureLogging(cfg => cfg.AddXUnit(output).SetMinimumLevel(LogLevel.Trace)) .ConfigureServices(collection => ConfigureServices(collection, id)); Host = builder.Build(); - Host.UseEventuousLogs(); + Host.Services.AddEventuousLogs(); } void ConfigureServices(IServiceCollection services, string id) @@ -48,9 +49,7 @@ protected async Task WaitForPosition(ulong position) { } } - public Task InitializeAsync() - => Host.StartAsync(); + public async ValueTask InitializeAsync() => await Host.StartAsync(); - public Task DisposeAsync() - => Host.StopAsync(); + public async ValueTask DisposeAsync() => await Host.StopAsync(); } diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj b/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj index 5ca5eb77c..1647eda67 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj @@ -3,6 +3,7 @@ true true true + Exe diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Projections/ProjectorTests.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Projections/ProjectorTests.cs index e8f02e744..35d29b510 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Projections/ProjectorTests.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Projections/ProjectorTests.cs @@ -8,6 +8,7 @@ using Eventuous.Tests.Persistence.Base.Fixtures; using Eventuous.Tests.Postgres.Subscriptions; using Npgsql; +using static Xunit.TestContext; namespace Eventuous.Tests.Postgres.Projections; @@ -28,17 +29,17 @@ public async Task ProjectImportedBookingsToTable() { await CreateSchema(); var commands = await GenerateAndProduceEvents(100); - await Task.Delay(1000); + await Task.Delay(1000, Current.CancellationToken); - await using var connection = await _fixture.DataSource.OpenConnectionAsync(); + await using var connection = await _fixture.DataSource.OpenConnectionAsync(Current.CancellationToken); var select = $"select * from {_fixture.SchemaName}.bookings where booking_id = @bookingId"; foreach (var command in commands) { await using var cmd = new NpgsqlCommand(select, connection); cmd.Parameters.AddWithValue("@bookingId", command.BookingId); - await using var reader = await cmd.ExecuteReaderAsync(); - await reader.ReadAsync(); + await using var reader = await cmd.ExecuteReaderAsync(Current.CancellationToken); + await reader.ReadAsync(Current.CancellationToken); reader["checkin_date"].Should().Be(command.CheckIn.ToDateTimeUnspecified()); reader["price"].Should().Be(command.Price); } @@ -71,9 +72,9 @@ async Task CreateSchema() { static BookingEvents.BookingImported ToEvent(Commands.ImportBooking cmd) => new(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut); - public Task InitializeAsync() => _fixture.InitializeAsync(); + public async ValueTask InitializeAsync() => await _fixture.InitializeAsync(); - public Task DisposeAsync() => _fixture.DisposeAsync(); + public async ValueTask DisposeAsync() => await _fixture.DisposeAsync(); } public class TestProjector : PostgresProjector { diff --git a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/RabbitMqFixture.cs b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/RabbitMqFixture.cs index 7525b122a..26222a5eb 100644 --- a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/RabbitMqFixture.cs +++ b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/RabbitMqFixture.cs @@ -7,11 +7,11 @@ public class RabbitMqFixture : IAsyncLifetime { public ConnectionFactory ConnectionFactory { get; private set; } = null!; - public async Task InitializeAsync() { + public async ValueTask InitializeAsync() { _rabbitMq = new RabbitMqBuilder().Build(); await _rabbitMq.StartAsync(); ConnectionFactory = new ConnectionFactory { Uri = new Uri(_rabbitMq.GetConnectionString()), DispatchConsumersAsync = true }; } - public async Task DisposeAsync() => await _rabbitMq.DisposeAsync(); + public async ValueTask DisposeAsync() => await _rabbitMq.DisposeAsync(); } diff --git a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs index 109ff98a3..bc61e0615 100644 --- a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs +++ b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs @@ -3,7 +3,9 @@ using Eventuous.RabbitMq.Subscriptions; using Eventuous.Subscriptions.Filters; using Eventuous.TestHelpers; +using Eventuous.TestHelpers.Logging; using Eventuous.Tests.Subscriptions.Base; +using static Xunit.TestContext; namespace Eventuous.Tests.RabbitMq; @@ -23,9 +25,9 @@ public class SubscriptionSpec : IAsyncLifetime, IClassFixture { public SubscriptionSpec(RabbitMqFixture fixture, ITestOutputHelper outputHelper) { _fixture = fixture; - _es = new TestEventListener(outputHelper); - _exchange = new StreamName(Auto.Create()); - _loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddXunit(outputHelper, LogLevel.Trace)); + _es = new(outputHelper); + _exchange = new(Auto.Create()); + _loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddXUnit(outputHelper)); _log = _loggerFactory.CreateLogger(); } @@ -33,8 +35,8 @@ public SubscriptionSpec(RabbitMqFixture fixture, ITestOutputHelper outputHelper) [Fact] public async Task SubscribeAndProduce() { var testEvent = Auto.Create(); - await _producer.Produce(_exchange, testEvent, new Metadata()); - await _handler.AssertThat().Timebox(10.Seconds()).Any().Match(x => x as TestEvent == testEvent).Validate(); + await _producer.Produce(_exchange, testEvent, new Metadata(), cancellationToken: Current.CancellationToken); + await _handler.AssertThat().Timebox(10.Seconds()).Any().Match(x => x as TestEvent == testEvent).Validate(Current.CancellationToken); } [Fact] @@ -42,13 +44,13 @@ public async Task SubscribeAndProduceMany() { const int count = 10000; var testEvents = Auto.CreateMany(count).ToList(); - await _producer.Produce(_exchange, testEvents, new Metadata()); - await _handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(); + await _producer.Produce(_exchange, testEvents, new Metadata(), cancellationToken: Current.CancellationToken); + await _handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); } - public async Task InitializeAsync() { - _handler = new TestEventHandler(); - _producer = new RabbitMqProducer(_fixture.ConnectionFactory); + public async ValueTask InitializeAsync() { + _handler = new(); + _producer = new(_fixture.ConnectionFactory); var queue = Auto.Create(); @@ -67,7 +69,7 @@ public async Task InitializeAsync() { await _producer.StartAsync(); } - public async Task DisposeAsync() { + public async ValueTask DisposeAsync() { await _producer.StopAsync(); await _subscription.UnsubscribeWithLog(_log); _es.Dispose(); diff --git a/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj b/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj index c03cc857f..1c4a49dd8 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj +++ b/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj @@ -1,16 +1,17 @@ true + Exe - - - - - + + + + + - - - + + + diff --git a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/DomainFixture.cs b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/DomainFixture.cs index 0d72d7f21..f5fd0c50c 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/DomainFixture.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/DomainFixture.cs @@ -1,4 +1,5 @@ using Eventuous.Sut.App; +using Eventuous.Sut.Domain; using MicroElements.AutoFixture.NodaTime; using NodaTime; @@ -7,8 +8,7 @@ namespace Eventuous.Tests.Redis.Fixtures; public static class DomainFixture { static IFixture Auto { get; } = new Fixture().Customize(new NodaTimeCustomization()); - static DomainFixture() - => TypeMap.RegisterKnownEventTypes(); + static DomainFixture() => TypeMap.RegisterKnownEventTypes(typeof(BookingEvents.BookingImported).Assembly); public static Commands.ImportBooking CreateImportBooking() { var from = Auto.Create(); diff --git a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs index b9420392e..0910cd540 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs @@ -21,7 +21,7 @@ public IntegrationFixture() { DefaultEventSerializer.SetDefaultSerializer(Serializer); } - public async Task InitializeAsync() { + public async ValueTask InitializeAsync() { _redisContainer = new RedisBuilder().WithImage("redis:7.0.12-alpine").Build(); await _redisContainer.StartAsync(); @@ -42,7 +42,7 @@ IDatabase GetDb() { } } - public async Task DisposeAsync() { + public async ValueTask DisposeAsync() { await _redisContainer.DisposeAsync(); _listener.Dispose(); } diff --git a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/SubscriptionFixture.cs b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/SubscriptionFixture.cs index bd56ac9ce..f8b7aba5d 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/SubscriptionFixture.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/SubscriptionFixture.cs @@ -2,6 +2,7 @@ using Eventuous.Redis.Subscriptions; using Eventuous.Subscriptions; using Eventuous.Subscriptions.Filters; +using Eventuous.TestHelpers.Logging; using Eventuous.Tests.Subscriptions.Base; namespace Eventuous.Tests.Redis.Fixtures; @@ -21,7 +22,7 @@ protected SubscriptionFixture(ITestOutputHelper outputHelper, bool subscribeToAl _subscribeToAll = subscribeToAll; _autoStart = autoStart; Stream = new StreamName(SharedAutoFixture.Auto.Create()); - LoggerFactory = TestHelpers.Logging.GetLoggerFactory(outputHelper, logLevel); + LoggerFactory = LoggingExtensions.GetLoggerFactory(outputHelper, logLevel); SubscriptionId = $"test-{Guid.NewGuid():N}"; Log = LoggerFactory.CreateLogger(GetType()); _listener = new LoggingEventListener(LoggerFactory); @@ -39,7 +40,7 @@ protected SubscriptionFixture(ITestOutputHelper outputHelper, bool subscribeToAl readonly bool _autoStart; readonly LoggingEventListener _listener; - public async Task InitializeAsync() { + public async ValueTask InitializeAsync() { IntegrationFixture = new(); await IntegrationFixture.InitializeAsync(); Handler = GetHandler(); @@ -67,14 +68,14 @@ public async Task InitializeAsync() { if (_autoStart) await Start(); } - public async Task DisposeAsync() { + public async ValueTask DisposeAsync() { if (_autoStart) await Stop(); - await FlushDB(); + await FlushDb(); _listener.Dispose(); await IntegrationFixture.DisposeAsync(); } - async Task FlushDB() { + async Task FlushDb() { var database = IntegrationFixture.GetDatabase(); await database.ExecuteAsync("FLUSHDB"); } diff --git a/src/Redis/test/Eventuous.Tests.Redis/Store/Read.cs b/src/Redis/test/Eventuous.Tests.Redis/Store/Read.cs index dbdc8a76c..5224be7ab 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Store/Read.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Store/Read.cs @@ -1,5 +1,6 @@ using Eventuous.Tests.Redis.Fixtures; using static Eventuous.Tests.Redis.Store.Helpers; +using static Xunit.TestContext; namespace Eventuous.Tests.Redis.Store; @@ -10,7 +11,7 @@ public async Task ShouldReadOne() { var streamName = GetStreamName(); await fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream); - var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 100, default); + var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); result.Length.Should().Be(1); result[0].Payload.Should().BeEquivalentTo(evt); @@ -23,7 +24,7 @@ public async Task ShouldReadMany() { var streamName = GetStreamName(); await fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); - var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 100, default); + var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); var actual = result.Select(x => x.Payload); actual.Should().BeEquivalentTo(events); @@ -41,7 +42,7 @@ public async Task ShouldReadTail() { var events2 = CreateEvents(10).ToArray(); await fixture.AppendEvents(streamName, events2, ExpectedStreamVersion.Any); - var result = await fixture.EventReader.ReadEvents(streamName, new StreamReadPosition((long)position), 100, default); + var result = await fixture.EventReader.ReadEvents(streamName, new((long)position), 100, Current.CancellationToken); var actual = result.Select(x => x.Payload); actual.Should().BeEquivalentTo(events2); @@ -54,7 +55,7 @@ public async Task ShouldReadHead() { var streamName = GetStreamName(); await fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); - var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 10, default); + var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 10, Current.CancellationToken); var expected = events.Take(10); var actual = result.Select(x => x.Payload); diff --git a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs index 74e2feea6..f54aff673 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs @@ -1,9 +1,9 @@ -using Eventuous.Subscriptions.Checkpoints; using Eventuous.Subscriptions.Logging; using Eventuous.Tests.Redis.Fixtures; using Eventuous.Tests.Subscriptions.Base; using static Eventuous.Sut.App.Commands; using static Eventuous.Sut.Domain.BookingEvents; +using static Xunit.TestContext; namespace Eventuous.Tests.Redis.Subscriptions; @@ -15,7 +15,7 @@ public async Task ShouldConsumeProducedEvents() { var (testEvents, _) = await GenerateAndProduceEvents(count); await Start(); - await Handler.AssertThat().Timebox(2.Seconds()).Exactly(count).Match(x => testEvents.Contains(x)).Validate(); + await Handler.AssertThat().Timebox(2.Seconds()).Exactly(count).Match(x => testEvents.Contains(x)).Validate(Current.CancellationToken); await Stop(); Handler.Count.Should().Be(10); @@ -38,7 +38,7 @@ async Task TestConsumptionOfProducedEvents() { var (testEvents, _) = await GenerateAndProduceEvents(count); await Start(); - await Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(); + await Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(Current.CancellationToken); await Stop(); Handler.Count.Should().Be(10); @@ -51,12 +51,12 @@ public async Task ShouldUseExistingCheckpoint() { var (_, result) = await GenerateAndProduceEvents(count); - await CheckpointStore.GetLastCheckpoint(SubscriptionId, default); + await CheckpointStore.GetLastCheckpoint(SubscriptionId, Current.CancellationToken); Logger.ConfigureIfNull(SubscriptionId, LoggerFactory); - await CheckpointStore.StoreCheckpoint(new Checkpoint(SubscriptionId, result.GlobalPosition), true, default); + await CheckpointStore.StoreCheckpoint(new(SubscriptionId, result.GlobalPosition), true, Current.CancellationToken); await Start(); - await Task.Delay(TimeSpan.FromSeconds(1)); + await Task.Delay(TimeSpan.FromSeconds(1), Current.CancellationToken); await Stop(); Handler.Count.Should().Be(0); } diff --git a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs index 5f184bc23..75c987b78 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs @@ -1,9 +1,9 @@ -using Eventuous.Subscriptions.Checkpoints; using Eventuous.Subscriptions.Logging; using Eventuous.Tests.Redis.Fixtures; using Eventuous.Tests.Subscriptions.Base; using static Eventuous.Sut.App.Commands; using static Eventuous.Sut.Domain.BookingEvents; +using static Xunit.TestContext; namespace Eventuous.Tests.Redis.Subscriptions; @@ -15,7 +15,7 @@ public async Task ShouldConsumeProducedEvents() { var testEvents = await GenerateAndProduceEvents(count); await Start(); - await Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(); + await Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(Current.CancellationToken); await Stop(); Handler.Count.Should().Be(10); @@ -38,7 +38,7 @@ async Task TestConsumptionOfProducedEvents() { var testEvents = await GenerateAndProduceEvents(count); await Start(); - await Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(); + await Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(Current.CancellationToken); await Stop(); Handler.Count.Should().Be(10); @@ -51,13 +51,13 @@ public async Task ShouldUseExistingCheckpoint() { await GenerateAndProduceEvents(count); - await CheckpointStore.GetLastCheckpoint(SubscriptionId, default); + await CheckpointStore.GetLastCheckpoint(SubscriptionId, Current.CancellationToken); var streamPosition = await GetStreamPosition(count); Logger.ConfigureIfNull(SubscriptionId, LoggerFactory); - await CheckpointStore.StoreCheckpoint(new Checkpoint(SubscriptionId, (ulong)streamPosition), true, default); + await CheckpointStore.StoreCheckpoint(new(SubscriptionId, (ulong)streamPosition), true, Current.CancellationToken); await Start(); - await Task.Delay(TimeSpan.FromSeconds(1)); + await Task.Delay(TimeSpan.FromSeconds(1), Current.CancellationToken); await Stop(); Handler.Count.Should().Be(0); } diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj b/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj index 24d5b6f18..3273c70cd 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj @@ -1,19 +1,20 @@ - - true - true - - - - - - - - - - - - - - + + true + true + Exe + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Projections/ProjectorTests.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Projections/ProjectorTests.cs index 5ee2ddea3..890e59b80 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Projections/ProjectorTests.cs +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Projections/ProjectorTests.cs @@ -9,6 +9,7 @@ using Eventuous.Tests.Persistence.Base.Fixtures; using Eventuous.Tests.SqlServer.Subscriptions; using Microsoft.Data.SqlClient; +using static Xunit.TestContext; namespace Eventuous.Tests.SqlServer.Projections; @@ -33,9 +34,9 @@ public async Task ProjectImportedBookingsToTable() { await CreateSchema(); var commands = await GenerateAndProduceEvents(100); - await Task.Delay(1000); + await Task.Delay(1000, Current.CancellationToken); - await using var connection = await ConnectionFactory.GetConnection(_fixture.ConnectionString, default); + await using var connection = await ConnectionFactory.GetConnection(_fixture.ConnectionString, Current.CancellationToken); var select = $"SELECT * FROM {_fixture.SchemaName}.Bookings where BookingId = @BookingId"; @@ -48,8 +49,8 @@ public async Task ProjectImportedBookingsToTable() { async Task ValidateProjectedObject(SqlConnection conn, Commands.ImportBooking command) { await using var cmd = new SqlCommand(select, conn); cmd.Parameters.AddWithValue("@BookingId", command.BookingId); - await using var reader = await cmd.ExecuteReaderAsync(); - await reader.ReadAsync(); + await using var reader = await cmd.ExecuteReaderAsync(Current.CancellationToken); + await reader.ReadAsync(Current.CancellationToken); reader["CheckinDate"].Should().Be(command.CheckIn.ToDateTimeUnspecified()); reader["Price"].Should().Be(command.Price); } @@ -80,9 +81,9 @@ async Task CreateSchema() { static BookingEvents.BookingImported ToEvent(Commands.ImportBooking cmd) => new(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut); - public Task InitializeAsync() => _fixture.InitializeAsync(); + public async ValueTask InitializeAsync() => await _fixture.InitializeAsync(); - public Task DisposeAsync() => _fixture.DisposeAsync(); + public async ValueTask DisposeAsync() => await _fixture.DisposeAsync(); } public class TestProjector : SqlServerProjector { diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 8638ada0b..8ef563fc5 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -1,6 +1,6 @@ - net6.0;net7.0;net8.0 + net9.0;net8.0 false preview enable @@ -14,6 +14,7 @@ true false + diff --git a/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj b/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj index 482c8bf13..a9942088e 100644 --- a/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj +++ b/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj @@ -7,14 +7,13 @@ - - + - + diff --git a/test/Eventuous.TestHelpers/Logging.cs b/test/Eventuous.TestHelpers/Logging.cs deleted file mode 100644 index 36e943783..000000000 --- a/test/Eventuous.TestHelpers/Logging.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Eventuous.TestHelpers; - -public static class Logging { - public static ILoggerFactory GetLoggerFactory(ITestOutputHelper outputHelper, LogLevel logLevel = LogLevel.Debug) - => LoggerFactory.Create( - builder => builder - .SetMinimumLevel(logLevel) - .AddFilter("Microsoft", LogLevel.Warning) - .AddFilter("Grpc.Net.Client", LogLevel.Warning) - .AddXunit(outputHelper, logLevel) - ); -} diff --git a/test/Eventuous.TestHelpers/Logging/LoggingExtensions.cs b/test/Eventuous.TestHelpers/Logging/LoggingExtensions.cs new file mode 100644 index 000000000..45d67efe2 --- /dev/null +++ b/test/Eventuous.TestHelpers/Logging/LoggingExtensions.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.Logging; + +namespace Eventuous.TestHelpers.Logging; + +public static class LoggingExtensions { + public static ILoggerFactory GetLoggerFactory(ITestOutputHelper outputHelper, LogLevel logLevel = LogLevel.Debug) + => LoggerFactory.Create( + builder => builder + .SetMinimumLevel(logLevel) + .AddFilter("Microsoft", LogLevel.Warning) + .AddFilter("Grpc.Net.Client", LogLevel.Warning) + .AddXUnit(outputHelper) + ); + + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, XUnitLoggerOptions? options = null) { + factory.AddProvider(new XUnitLoggerProvider(outputHelper, options)); + + return factory; + } + + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper, XUnitLoggerOptions? options = null) + => builder.AddProvider(new XUnitLoggerProvider(outputHelper, options)); +} + +public sealed class XUnitLoggerProvider(ITestOutputHelper testOutputHelper, XUnitLoggerOptions? options = null) : ILoggerProvider { + private readonly XUnitLoggerOptions _options = options ?? new XUnitLoggerOptions(); + private readonly LoggerExternalScopeProvider _scopeProvider = new(); + + public XUnitLoggerProvider(ITestOutputHelper testOutputHelper, bool appendScope) : this(testOutputHelper, new XUnitLoggerOptions { IncludeScopes = appendScope }) { } + + public ILogger CreateLogger(string categoryName) => new XUnitLogger(testOutputHelper, _scopeProvider, categoryName, _options); + + public void Dispose() { } +} diff --git a/test/Eventuous.TestHelpers/Logging/xUnitLogger.cs b/test/Eventuous.TestHelpers/Logging/xUnitLogger.cs new file mode 100644 index 000000000..24045c152 --- /dev/null +++ b/test/Eventuous.TestHelpers/Logging/xUnitLogger.cs @@ -0,0 +1,85 @@ +// Copyright (C) Ubiquitous AS.All rights reserved +// Licensed under the Apache License, Version 2.0. + +using System.Text; +using Microsoft.Extensions.Logging; + +namespace Eventuous.TestHelpers.Logging; + +public sealed class XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider) : XUnitLogger(testOutputHelper, scopeProvider, typeof(T).FullName), ILogger; + +public class XUnitLogger : ILogger { + private readonly ITestOutputHelper _testOutputHelper; + private readonly string? _categoryName; + private readonly XUnitLoggerOptions _options; + private readonly LoggerExternalScopeProvider _scopeProvider; + + public static ILogger CreateLogger(ITestOutputHelper testOutputHelper) => new XUnitLogger(testOutputHelper, new(), ""); + + public static ILogger CreateLogger(ITestOutputHelper testOutputHelper) => new XUnitLogger(testOutputHelper, new()); + + public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider, string? categoryName, bool appendScope = true) + : this(testOutputHelper, scopeProvider, categoryName, options: new() { IncludeScopes = appendScope }) { } + + public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider, string? categoryName, XUnitLoggerOptions? options) { + _testOutputHelper = testOutputHelper; + _scopeProvider = scopeProvider; + _categoryName = categoryName; + _options = options ?? new(); + } + + public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None; + + public IDisposable? BeginScope(TState state) where TState : notnull => _scopeProvider.Push(state); + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { + var sb = new StringBuilder(); + + if (_options.TimestampFormat is not null) { + var now = _options.UseUtcTimestamp ? DateTimeOffset.UtcNow : DateTimeOffset.Now; + var timestamp = now.ToString(_options.TimestampFormat); + sb.Append(timestamp).Append(' '); + } + + if (_options.IncludeLogLevel) { + sb.Append(GetLogLevelString(logLevel)).Append(' '); + } + + if (_options.IncludeCategory) { + sb.Append('[').Append(_categoryName).Append("] "); + } + + sb.Append(formatter(state, exception)); + + if (exception is not null) { + sb.Append('\n').Append(exception); + } + + // Append scopes + if (_options.IncludeScopes) { + _scopeProvider.ForEachScope( + (scope, s) => { + s.Append("\n => "); + s.Append(scope); + }, + sb + ); + } + + try { + _testOutputHelper.WriteLine(sb.ToString()); + } catch { + // This can happen when the test is not active + } + } + + private static string GetLogLevelString(LogLevel logLevel) => logLevel switch { + LogLevel.Trace => "trce", + LogLevel.Debug => "dbug", + LogLevel.Information => "info", + LogLevel.Warning => "warn", + LogLevel.Error => "fail", + LogLevel.Critical => "crit", + _ => throw new ArgumentOutOfRangeException(nameof(logLevel)) + }; +} diff --git a/test/Eventuous.TestHelpers/Logging/xUnitLoggerOptions.cs b/test/Eventuous.TestHelpers/Logging/xUnitLoggerOptions.cs new file mode 100644 index 000000000..b3f008811 --- /dev/null +++ b/test/Eventuous.TestHelpers/Logging/xUnitLoggerOptions.cs @@ -0,0 +1,31 @@ +// Copyright (C) Ubiquitous AS.All rights reserved +// Licensed under the Apache License, Version 2.0. + +namespace Eventuous.TestHelpers.Logging; + +public record XUnitLoggerOptions { + /// + /// Includes scopes when . + /// + public bool IncludeScopes { get; set; } + + /// + /// Includes category when . + /// + public bool IncludeCategory { get; set; } = true; + + /// + /// Includes log level when . + /// + public bool IncludeLogLevel { get; set; } = true; + + /// + /// Gets or sets format string used to format timestamp in logging messages. Defaults to . + /// + public string? TimestampFormat { get; set; } + + /// + /// Gets or sets indication whether UTC timezone should be used to format timestamps in logging messages. Defaults to . + /// + public bool UseUtcTimestamp { get; set; } +} From fe70e3639f81e8be2a51ed2ab0735ebac4f96706 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Thu, 17 Oct 2024 17:51:05 +0200 Subject: [PATCH 02/17] Fix docs build action --- .github/workflows/pull-request-docs.yml | 7 +++++-- docs/package.json | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pull-request-docs.yml b/.github/workflows/pull-request-docs.yml index a28ed530d..abe4a08f0 100644 --- a/.github/workflows/pull-request-docs.yml +++ b/.github/workflows/pull-request-docs.yml @@ -9,6 +9,9 @@ jobs: docs: runs-on: ubuntu-latest steps: + - + name: Install pnpm + uses: pnpm/action-setup@v4 - name: Checkout uses: actions/checkout@v4 @@ -16,5 +19,5 @@ jobs: name: Build docs run: | cd docs - yarn install - yarn build + pnpm install + pnpm build diff --git a/docs/package.json b/docs/package.json index 90c9baaca..2869ffdee 100644 --- a/docs/package.json +++ b/docs/package.json @@ -46,7 +46,7 @@ ] }, "engines": { - "node": ">=16.14" + "node": ">=18.19.0" }, "packageManager": "pnpm@9.10.0" } From 3def7201e3917c6a13a568090b016cc5ce3eff46 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Thu, 17 Oct 2024 17:53:58 +0200 Subject: [PATCH 03/17] Path to packages --- .github/workflows/pull-request-docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pull-request-docs.yml b/.github/workflows/pull-request-docs.yml index abe4a08f0..2045c65b6 100644 --- a/.github/workflows/pull-request-docs.yml +++ b/.github/workflows/pull-request-docs.yml @@ -12,6 +12,8 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@v4 + with: + package_json_file: docs/package.json - name: Checkout uses: actions/checkout@v4 From fc8fe5beaac08c18f37591389fd3c2e83b4f8b25 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Thu, 17 Oct 2024 17:56:00 +0200 Subject: [PATCH 04/17] Use version --- .github/workflows/pull-request-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request-docs.yml b/.github/workflows/pull-request-docs.yml index 2045c65b6..420b55e3a 100644 --- a/.github/workflows/pull-request-docs.yml +++ b/.github/workflows/pull-request-docs.yml @@ -13,7 +13,7 @@ jobs: name: Install pnpm uses: pnpm/action-setup@v4 with: - package_json_file: docs/package.json + version: 9 - name: Checkout uses: actions/checkout@v4 From bcb562da0d5211d726f09481801270d71221ed5b Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Sun, 20 Oct 2024 15:56:08 +0200 Subject: [PATCH 05/17] Slight test refactoring, but the issue is in EventStore client --- Directory.Packages.props | 1 + .../Diagnostics/ProducerEventSource.cs | 12 ++-- .../Fixtures/SubscriptionExtensions.cs | 8 +-- .../Eventuous.Tests.OpenTelemetry.csproj | 1 + src/Directory.Build.props | 3 + .../EventStoreCatchUpSubscriptionBase.cs | 2 +- .../Eventuous.Tests.EventStore.csproj | 1 + .../StreamSubscriptionDeletedEventsTests.cs | 58 +++++++++++++------ 8 files changed, 57 insertions(+), 29 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 43c5623e3..e295b78c8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -52,6 +52,7 @@ + diff --git a/src/Core/src/Eventuous.Producers/Diagnostics/ProducerEventSource.cs b/src/Core/src/Eventuous.Producers/Diagnostics/ProducerEventSource.cs index 24d5404a6..ea5b5b313 100644 --- a/src/Core/src/Eventuous.Producers/Diagnostics/ProducerEventSource.cs +++ b/src/Core/src/Eventuous.Producers/Diagnostics/ProducerEventSource.cs @@ -20,7 +20,7 @@ public class ProducerEventSource : EventSource where T : class { [NonEvent] public void ProduceAcknowledged(ProducedMessage message) { if (IsEnabled(EventLevel.Verbose, EventKeywords.All)) { - ProduceAcknowledged(ProducerName, message); + ProduceAcknowledged(ProducerName, message.GetType().Name); } } @@ -29,14 +29,14 @@ public void ProduceNotAcknowledged(ProducedMessage message, string error, Except if (!IsEnabled(EventLevel.Verbose, EventKeywords.All)) return; var errorMessage = $"{error} {e?.Message}"; - ProduceNotAcknowledged(ProducerName, message, errorMessage); + ProduceNotAcknowledged(ProducerName, message.GetType().Name, errorMessage); } [Event(ProduceAcknowledgedId, Level = EventLevel.Verbose, Message = "[{0}] Produce acknowledged: {1}")] - void ProduceAcknowledged(string producer, object message) - => WriteEvent(ProduceAcknowledgedId, producer, message.GetType().Name); + void ProduceAcknowledged(string producer, string messageType) + => WriteEvent(ProduceAcknowledgedId, producer, messageType); [Event(ProduceNotAcknowledgedId, Level = EventLevel.Verbose, Message = "[{0}] Produce not acknowledged: {1} {2}")] - void ProduceNotAcknowledged(string producer, object message, string error) - => WriteEvent(ProduceNotAcknowledgedId, producer, message.GetType().Name, error); + void ProduceNotAcknowledged(string producer, string messageType, string error) + => WriteEvent(ProduceNotAcknowledgedId, producer, messageType, error); } \ No newline at end of file diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs index 25af43ffb..8b8b0f8cc 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs @@ -3,16 +3,16 @@ namespace Eventuous.Tests.Subscriptions.Base; public static class SubscriptionExtensions { - public static ValueTask SubscribeWithLog(this IMessageSubscription subscription, ILogger log) + public static ValueTask SubscribeWithLog(this IMessageSubscription subscription, ILogger log, CancellationToken cancellationToken = default) => subscription.Subscribe( id => log.LogInformation("{Subscription} subscribed", id), (id, reason, ex) => log.LogWarning(ex, "{Subscription} dropped {Reason}", id, reason), - CancellationToken.None + cancellationToken ); - public static ValueTask UnsubscribeWithLog(this IMessageSubscription subscription, ILogger log) + public static ValueTask UnsubscribeWithLog(this IMessageSubscription subscription, ILogger log, CancellationToken cancellationToken = default) => subscription.Unsubscribe( id => log.LogInformation("{Subscription} unsubscribed", id), - CancellationToken.None + cancellationToken ); } \ No newline at end of file diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj index 3db109014..5848b539b 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index b1a98442e..ab8e68dd8 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -30,6 +30,8 @@ true true true + --report-trx + false true @@ -66,6 +68,7 @@ + diff --git a/src/EventStore/src/Eventuous.EventStore/Subscriptions/EventStoreCatchUpSubscriptionBase.cs b/src/EventStore/src/Eventuous.EventStore/Subscriptions/EventStoreCatchUpSubscriptionBase.cs index a3e3b9233..0a897d32f 100644 --- a/src/EventStore/src/Eventuous.EventStore/Subscriptions/EventStoreCatchUpSubscriptionBase.cs +++ b/src/EventStore/src/Eventuous.EventStore/Subscriptions/EventStoreCatchUpSubscriptionBase.cs @@ -47,8 +47,8 @@ protected EventStoreCatchUpSubscriptionBase( /// protected override async ValueTask Unsubscribe(CancellationToken cancellationToken) { try { - Subscription?.Dispose(); Stopping.Cancel(false); + Subscription?.Dispose(); await Task.Delay(100, cancellationToken); } catch (Exception) { // Nothing to see here diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj b/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj index 82edcbc42..1b6c63101 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj @@ -18,6 +18,7 @@ + diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs index c238a0d19..e1e71ab7d 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs @@ -1,4 +1,5 @@ -using EventStore.Client; +// using EventStore.Client; + using Eventuous.Diagnostics.Logging; using Eventuous.EventStore.Subscriptions; using Eventuous.Subscriptions.Context; @@ -12,15 +13,14 @@ namespace Eventuous.Tests.EventStore.Subscriptions; -[Collection("Database")] -public sealed class StreamSubscriptionDeletedEventsTests : IClassFixture, IDisposable { +public sealed class StreamSubscriptionDeletedEventsTests : IAsyncLifetime { readonly StoreFixture _fixture; readonly ILoggerFactory _loggerFactory; readonly LoggingEventListener _listener; - public StreamSubscriptionDeletedEventsTests(StoreFixture fixture, ITestOutputHelper output) { - _fixture = fixture; - _loggerFactory = LoggerFactory.Create(cfg => cfg.AddXUnit(output).SetMinimumLevel(LogLevel.Debug)); + public StreamSubscriptionDeletedEventsTests(ITestOutputHelper output) { + _fixture = new(); + _loggerFactory = LoggerFactory.Create(cfg => cfg.AddXUnit(output).AddConsole().SetMinimumLevel(LogLevel.Debug)); _listener = new(_loggerFactory); _fixture.TypeMapper.RegisterKnownEventTypes(typeof(BookingEvents.BookingImported).Assembly); } @@ -32,11 +32,11 @@ public async Task StreamSubscriptionGetsDeletedEvents() { var categoryStream = new StreamName("$ce-Booking"); ulong? startPosition = null; - try { - var last = await _fixture.Client.ReadStreamAsync(Direction.Backwards, categoryStream, StreamPosition.End, 1, cancellationToken: Current.CancellationToken) - .ToArrayAsync(Current.CancellationToken); - startPosition = last[0].OriginalEventNumber; - } catch (StreamNotFoundException) { } + // try { + // var last = await _fixture.Client.ReadStreamAsync(Direction.Backwards, categoryStream, StreamPosition.End, 1, cancellationToken: Current.CancellationToken) + // .ToArrayAsync(Current.CancellationToken); + // startPosition = last[0].OriginalEventNumber; + // } catch (StreamNotFoundException) { } const int produceCount = 20; const int deleteCount = 5; @@ -70,9 +70,14 @@ await Task.WhenAll( eventSerializer: _fixture.Serializer ); - var log = _loggerFactory.CreateLogger("Test"); + var expected = commands.Except(delete).Select(x => x.BookingId); + var log = _loggerFactory.CreateLogger("Test"); + + LogCollection("Produced", commands); + LogCollection("Deleted", delete); + LogCollection("Expected", commands.Except(delete)); - await subscription.SubscribeWithLog(log); + await subscription.SubscribeWithLog(log, Current.CancellationToken); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(200)); @@ -80,10 +85,15 @@ await Task.WhenAll( await Task.Delay(100, cts.Token); } - await subscription.UnsubscribeWithLog(log); + await subscription.UnsubscribeWithLog(log, Current.CancellationToken); var actual = handler.Processed.Select(x => x.Stream.GetId()).ToList(); - actual.Should().BeEquivalentTo(commands.Except(delete).Select(x => x.BookingId)); + log.LogInformation("Actual:\n {Join}", string.Join("\n", actual)); + actual.Should().BeEquivalentTo(expected); + + return; + + void LogCollection(string what, IEnumerable collection) => log.LogInformation("{What}:\n {Join}", what, string.Join("\n", collection.Select(x => x.BookingId))); } class TestHandler : BaseEventHandler { @@ -102,8 +112,20 @@ public override ValueTask HandleEvent(IMessageConsumeContex } } - public void Dispose() { - _loggerFactory.Dispose(); - _listener.Dispose(); + public async ValueTask DisposeAsync() { + await _fixture.DisposeAsync(); + await CastAndDispose(_loggerFactory); + await CastAndDispose(_listener); + + return; + + static async ValueTask CastAndDispose(IDisposable resource) { + if (resource is IAsyncDisposable resourceAsyncDisposable) + await resourceAsyncDisposable.DisposeAsync(); + else + resource.Dispose(); + } } + + public ValueTask InitializeAsync() => _fixture.InitializeAsync(); } From 71264f1cf180bffd1c739d2504983606d67703f5 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Tue, 5 Nov 2024 14:36:53 +0100 Subject: [PATCH 06/17] Update ESDB client --- Directory.Packages.props | 210 +++++++++--------- .../SubscriptionBuilderExtensions.cs | 3 +- 2 files changed, 106 insertions(+), 107 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index e295b78c8..6ffff17a9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,107 +1,107 @@ - - true - - - 9.0.0-rc.2.24474.3 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24474.3 - - - 8.0.6 - 8.0 - 8.0.8 - - - 3.10.0 - - - 8.0.5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + true + + + 9.0.0-rc.2.24474.3 + 9.0.0-rc.2.24473.5 + 9.0.0-rc.2.24474.3 + + + 8.0.6 + 8.0 + 8.0.8 + + + 3.10.0 + + + 8.0.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilderExtensions.cs b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilderExtensions.cs index a9e346d2f..8667b5000 100644 --- a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilderExtensions.cs +++ b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilderExtensions.cs @@ -42,8 +42,7 @@ public static SubscriptionBuilder WithPartitioningByStream(this SubscriptionBuil /// Subscription options type /// Checkpoint store type /// - public static SubscriptionBuilder UseCheckpointStore - (this SubscriptionBuilder builder) + public static SubscriptionBuilder UseCheckpointStore(this SubscriptionBuilder builder) where T : class, ICheckpointStore where TSubscription : EventSubscriptionWithCheckpoint where TOptions : SubscriptionWithCheckpointOptions { From 5464938ab4cf6531b851d2f6349947266f504cad Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Wed, 6 Nov 2024 14:10:31 +0100 Subject: [PATCH 07/17] Refactoring tests --- Directory.Packages.props | 212 +++++++++--------- Eventuous.sln | 18 ++ .../EventStore/TieredEventReader.cs | 4 +- .../CommandServiceTests.cs | 3 +- .../Eventuous.Tests.Application.csproj | 10 + .../FunctionalServiceTests.cs | 3 +- .../ServiceTestBase.Amendments.cs | 19 +- .../ServiceTestBase.OnAny.cs | 2 +- .../ServiceTestBase.OnExisting.cs | 4 +- .../ServiceTestBase.OnNew.cs | 4 +- .../ServiceTestBase.cs | 16 +- .../StateWithIdTests.cs | 7 +- .../Eventuous.Tests.Persistence.Base.csproj | 5 + .../Fixtures/DomainFixture.cs | 1 + .../Fixtures/Helpers.cs | 1 + .../Fixtures/StoreFixtureBase.cs | 30 ++- .../Store/Append.cs | 35 ++- .../Store/OtherMethods.cs | 23 +- .../Store/Read.cs | 66 +++--- .../Store/TieredStoreTests.cs | 9 +- .../Traits/CategoryAttribute.cs | 17 -- .../Traits/CategoryDiscoverer.cs | 24 -- .../Eventuous.Tests.Subscriptions.Base.csproj | 4 + .../Fixtures/SubscriptionExtensions.cs | 1 + .../Fixtures/SubscriptionFixtureBase.cs | 32 +-- .../Fixtures/TestEventHandler.cs | 10 +- .../SubscribeToAll.cs | 40 ++-- .../SubscribeToStream.cs | 56 ++--- .../SubscriptionTestBase.cs | 13 ++ .../AutofixtureExtensions.cs | 6 +- .../ConsumePipeTests.cs | 10 +- .../DefaultConsumerTests.cs | 10 +- .../Eventuous.Tests.Subscriptions.csproj | 8 +- .../HandlingStatusTests.cs | 32 +-- .../RegistrationTests.cs | 39 ++-- .../SequenceTests.cs | 37 ++- .../Eventuous.Tests/Eventuous.Tests.csproj | 1 - .../test/Eventuous.Tests/ForgotToSetId.cs | 5 +- .../test/Eventuous.Tests/StoringEvents.cs | 5 +- .../StoringEventsWithCustomStream.cs | 11 +- .../Eventuous.Tests/TypeRegistrationTests.cs | 1 - .../Eventuous.Tests.OpenTelemetry.csproj | 7 +- .../MetricsSubscriptionFixtureBase.cs | 11 +- .../MetricsTests.cs | 43 ++-- src/Directory.Build.props | 6 +- src/Directory.Testable.targets | 7 +- .../AppServiceTests.cs | 32 +-- .../Eventuous.Tests.EventStore.csproj | 1 + .../Metrics/MetricsTests.cs | 15 +- .../ProducerTracesTests.cs | 18 +- .../RegistrationTests.cs | 21 +- .../Store/AggregateStoreTests.cs | 42 ++-- .../Store/EventStoreAggregateTests.cs | 45 ++-- .../Store/StoreTests.cs | 6 + .../Store/TieredStoreTests.cs | 7 +- .../Fixtures/CatchUpSubscriptionFixture.cs | 14 +- .../Fixtures/LegacySubscriptionFixture.cs | 36 +-- .../Fixtures/PersistentSubscriptionFixture.cs | 33 ++- ...PublishAndSubscribeManyPartitionedTests.cs | 18 +- .../PublishAndSubscribeManyTests.cs | 15 +- .../PublishAndSubscribeOneTests.cs | 17 +- ...mPersistentPublishAndSubscribeManyTests.cs | 74 +++--- .../StreamSubscriptionDeletedEventsTests.cs | 23 +- .../StreamSubscriptionWithLinksTests.cs | 28 ++- .../Subscriptions/SubscribeTests.cs | 67 +++--- .../SubscriptionIgnoredMessagesTests.cs | 25 +-- .../AggregateFactoryRegistrationTests.cs | 6 +- ...entuous.Tests.Extensions.AspNetCore.csproj | 1 + .../RegistrationTests.cs | 23 +- .../PubSubFixture.cs | 5 +- .../PubSubTests.cs | 32 +-- .../BasicProducerTests.cs | 42 ++-- .../Eventuous.Tests.Kafka/KafkaFixture.cs | 8 +- ...Eventuous.Tests.Projections.MongoDB.csproj | 34 +-- .../Fixtures/IntegrationFixture.cs | 5 +- .../ProjectWithBuilder.cs | 6 +- .../ProjectWithBulkBuilder.cs | 10 +- .../ProjectingWithTypedHandlers.cs | 12 +- .../ProjectionTestBase.cs | 44 ++-- .../src/Eventuous.Postgresql/Schema.cs | 2 +- .../Eventuous.Tests.Postgres.csproj | 1 + .../Metrics/MetricsTests.cs | 16 +- .../Projections/ProjectorTests.cs | 28 ++- .../Registrations/RegistrationTests.cs | 15 +- .../Store/StoreTests.cs | 9 +- .../Store/TieredStoreTests.cs | 4 +- .../Subscriptions/SubscribeTests.cs | 61 +++-- .../Subscriptions/SubscriptionFixture.cs | 6 +- .../Eventuous.Tests.RabbitMq.csproj | 10 +- .../RabbitMqFixture.cs | 7 +- .../SubscriptionSpec.cs | 34 +-- .../Fixtures/IntegrationFixture.cs | 9 +- .../Fixtures/SubscriptionFixture.cs | 44 ++-- .../Eventuous.Tests.Redis/Store/Append.cs | 33 +-- .../Eventuous.Tests.Redis/Store/Helpers.cs | 9 +- .../test/Eventuous.Tests.Redis/Store/Read.cs | 38 ++-- .../Subscriptions/SubscribeToAll.cs | 67 +++--- .../Subscriptions/SubscribeToStream.cs | 68 +++--- .../Eventuous.Tests.SqlServer.csproj | 4 + .../Metrics/MetricsTests.cs | 15 +- .../Projections/ProjectorTests.cs | 25 +-- .../Registrations/RegistrationTests.cs | 4 +- .../Store/StoreTests.cs | 9 +- .../Store/TieredStoreTests.cs | 10 +- .../Subscriptions/SubscribeTests.cs | 61 +++-- .../Subscriptions/SubscriptionFixture.cs | 6 +- .../Eventuous.TestHelpers.TUnit.csproj | 10 + .../Logging/LoggingExtensions.cs | 30 +++ .../Logging/TUnitLogger.cs | 43 ++++ .../TestEventListener.cs | 38 ++++ .../Eventuous.TestHelpers.XUnit.csproj | 13 ++ .../Logging/LoggingExtensions.cs | 0 .../Logging/xUnitLogger.cs | 0 .../Logging/xUnitLoggerOptions.cs | 0 .../TestEventListener.cs | 0 .../Eventuous.TestHelpers.csproj | 9 - 116 files changed, 1286 insertions(+), 1125 deletions(-) delete mode 100644 src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryAttribute.cs delete mode 100644 src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryDiscoverer.cs create mode 100644 src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscriptionTestBase.cs create mode 100644 test/Eventuous.TestHelpers.TUnit/Eventuous.TestHelpers.TUnit.csproj create mode 100644 test/Eventuous.TestHelpers.TUnit/Logging/LoggingExtensions.cs create mode 100644 test/Eventuous.TestHelpers.TUnit/Logging/TUnitLogger.cs create mode 100644 test/Eventuous.TestHelpers.TUnit/TestEventListener.cs create mode 100644 test/Eventuous.TestHelpers.XUnit/Eventuous.TestHelpers.XUnit.csproj rename test/{Eventuous.TestHelpers => Eventuous.TestHelpers.XUnit}/Logging/LoggingExtensions.cs (100%) rename test/{Eventuous.TestHelpers => Eventuous.TestHelpers.XUnit}/Logging/xUnitLogger.cs (100%) rename test/{Eventuous.TestHelpers => Eventuous.TestHelpers.XUnit}/Logging/xUnitLoggerOptions.cs (100%) rename test/{Eventuous.TestHelpers => Eventuous.TestHelpers.XUnit}/TestEventListener.cs (100%) diff --git a/Directory.Packages.props b/Directory.Packages.props index 6ffff17a9..3797554ca 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,107 +1,109 @@ - - true - - - 9.0.0-rc.2.24474.3 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24474.3 - - - 8.0.6 - 8.0 - 8.0.8 - - - 3.10.0 - - - 8.0.5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + true + + + 9.0.0-rc.2.24474.3 + 9.0.0-rc.2.24473.5 + 9.0.0-rc.2.24474.3 + + + 8.0.6 + 8.0 + 8.0.8 + + + 3.10.0 + + + 8.0.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Eventuous.sln b/Eventuous.sln index 87a241254..e935424f4 100644 --- a/Eventuous.sln +++ b/Eventuous.sln @@ -215,6 +215,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Brokers", "Brokers", "{86D9 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.Extensions.Logging", "src\Extensions\src\Eventuous.Extensions.Logging\Eventuous.Extensions.Logging.csproj", "{E1F6CDD8-D37E-487B-A429-25A06C590FE4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.TestHelpers.XUnit", "test\Eventuous.TestHelpers.XUnit\Eventuous.TestHelpers.XUnit.csproj", "{8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.TestHelpers.TUnit", "test\Eventuous.TestHelpers.TUnit\Eventuous.TestHelpers.TUnit.csproj", "{2A816CFD-5D05-4F64-8222-F7214B229EBC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -550,6 +554,18 @@ Global {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Release|Any CPU.Build.0 = Release|Any CPU {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Debug CI|Any CPU.ActiveCfg = Debug CI|Any CPU {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Debug CI|Any CPU.Build.0 = Debug CI|Any CPU + {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Release|Any CPU.Build.0 = Release|Any CPU + {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Debug CI|Any CPU.ActiveCfg = Debug CI|Any CPU + {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Debug CI|Any CPU.Build.0 = Debug CI|Any CPU + {2A816CFD-5D05-4F64-8222-F7214B229EBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A816CFD-5D05-4F64-8222-F7214B229EBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A816CFD-5D05-4F64-8222-F7214B229EBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A816CFD-5D05-4F64-8222-F7214B229EBC}.Release|Any CPU.Build.0 = Release|Any CPU + {2A816CFD-5D05-4F64-8222-F7214B229EBC}.Debug CI|Any CPU.ActiveCfg = Debug CI|Any CPU + {2A816CFD-5D05-4F64-8222-F7214B229EBC}.Debug CI|Any CPU.Build.0 = Debug CI|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -648,6 +664,8 @@ Global {6E545DFE-FE70-4486-92E0-E47E86E66210} = {86D92758-EBB6-4B8C-94B7-BD91AF1E31D2} {2E59C5F8-3E5A-4450-B902-7648AD7ECC0F} = {86D92758-EBB6-4B8C-94B7-BD91AF1E31D2} {E1F6CDD8-D37E-487B-A429-25A06C590FE4} = {2B7F84B7-C0E5-408F-ABAF-BF23C8305486} + {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26} = {C60C6094-2A03-45B6-AB33-C514C35DF823} + {2A816CFD-5D05-4F64-8222-F7214B229EBC} = {C60C6094-2A03-45B6-AB33-C514C35DF823} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0691467B-C257-46DB-BC4F-88EB7CD615B8} diff --git a/src/Core/src/Eventuous.Persistence/EventStore/TieredEventReader.cs b/src/Core/src/Eventuous.Persistence/EventStore/TieredEventReader.cs index b268f2678..306b2e3ad 100644 --- a/src/Core/src/Eventuous.Persistence/EventStore/TieredEventReader.cs +++ b/src/Core/src/Eventuous.Persistence/EventStore/TieredEventReader.cs @@ -14,10 +14,10 @@ public async Task ReadEvents(StreamName streamName, StreamReadPos var hotEvents = await LoadStreamEvents(hotReader, start, count).NoContext(); var archivedEvents = hotEvents.Length == 0 || hotEvents[0].Position > start.Value - ? await LoadStreamEvents(archiveReader, start, count - hotEvents.Length).NoContext() + ? await LoadStreamEvents(archiveReader, start, (int)hotEvents[0].Position).NoContext() : Enumerable.Empty(); - return hotEvents.Concat(archivedEvents.Select(x => x with { FromArchive = true })).Distinct(Comparer).ToArray(); + return archivedEvents.Select(x => x with { FromArchive = true }).Concat(hotEvents).Distinct(Comparer).ToArray(); async Task LoadStreamEvents(IEventReader reader, StreamReadPosition startPosition, int localCount) { try { diff --git a/src/Core/test/Eventuous.Tests.Application/CommandServiceTests.cs b/src/Core/test/Eventuous.Tests.Application/CommandServiceTests.cs index 6b37ced91..a6c3dfe7e 100644 --- a/src/Core/test/Eventuous.Tests.Application/CommandServiceTests.cs +++ b/src/Core/test/Eventuous.Tests.Application/CommandServiceTests.cs @@ -4,7 +4,8 @@ namespace Eventuous.Tests.Application; // ReSharper disable once UnusedType.Global -public class CommandServiceTests(ITestOutputHelper output) : ServiceTestBase(output) { +[InheritsTests] +public class CommandServiceTests : ServiceTestBase { protected override ICommandService CreateService( AmendEvent? amendEvent = null, AmendEvent? amendAll = null diff --git a/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj b/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj index e57200336..024226d4a 100644 --- a/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj +++ b/src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj @@ -7,10 +7,20 @@ + ServiceTestBase.cs + + ServiceTestBase.cs + + + ServiceTestBase.cs + + + ServiceTestBase.cs + diff --git a/src/Core/test/Eventuous.Tests.Application/FunctionalServiceTests.cs b/src/Core/test/Eventuous.Tests.Application/FunctionalServiceTests.cs index 7462f9b81..df45d3021 100644 --- a/src/Core/test/Eventuous.Tests.Application/FunctionalServiceTests.cs +++ b/src/Core/test/Eventuous.Tests.Application/FunctionalServiceTests.cs @@ -5,7 +5,8 @@ namespace Eventuous.Tests.Application; using Sut.Domain; // ReSharper disable once UnusedType.Global -public class FunctionalServiceTests(ITestOutputHelper output) : ServiceTestBase(output) { +[InheritsTests] +public class FunctionalServiceTests() : ServiceTestBase() { protected override ICommandService CreateService( AmendEvent? amendEvent = null, AmendEvent? amendAll = null diff --git a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.Amendments.cs b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.Amendments.cs index 1f6fab2e4..09db048a0 100644 --- a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.Amendments.cs +++ b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.Amendments.cs @@ -1,22 +1,21 @@ using Eventuous.Sut.Domain; using Eventuous.Testing; -using static Xunit.TestContext; namespace Eventuous.Tests.Application; public abstract partial class ServiceTestBase { - [Fact] - public async Task Should_amend_event_from_command() { + [Test] + public async Task Should_amend_event_from_command(CancellationToken cancellationToken) { var service = CreateService(amendEvent: AmendEvent); var cmd = CreateCommand(); - await service.Handle(cmd, Current.CancellationToken); + await service.Handle(cmd, cancellationToken); - var stream = await Store.ReadStream(StreamName.For(cmd.BookingId), StreamReadPosition.Start, cancellationToken: Current.CancellationToken); + var stream = await Store.ReadStream(StreamName.For(cmd.BookingId), StreamReadPosition.Start, cancellationToken: cancellationToken); stream[0].Metadata["userId"].Should().Be(cmd.ImportedBy); } - [Fact] + [Test] public async Task Should_amend_event_with_static_meta() { var cmd = Helpers.GetBookRoom(); @@ -27,14 +26,14 @@ await CommandServiceFixture .Then(x => x.StreamIs(e => e[0].Metadata["foo"].Should().Be("bar"))); } - [Fact] - public async Task Should_combine_amendments() { + [Test] + public async Task Should_combine_amendments(CancellationToken cancellationToken) { var service = CreateService(amendEvent: AmendEvent, amendAll: AddMeta); var cmd = CreateCommand(); - await service.Handle(cmd, Current.CancellationToken); + await service.Handle(cmd, cancellationToken); - var stream = await Store.ReadStream(StreamName.For(cmd.BookingId), StreamReadPosition.Start, cancellationToken: Current.CancellationToken); + var stream = await Store.ReadStream(StreamName.For(cmd.BookingId), StreamReadPosition.Start, cancellationToken: cancellationToken); stream[0].Metadata["userId"].Should().Be(cmd.ImportedBy); stream[0].Metadata["foo"].Should().Be("bar"); } diff --git a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnAny.cs b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnAny.cs index 7c4890c45..1eac4e8ff 100644 --- a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnAny.cs +++ b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnAny.cs @@ -5,7 +5,7 @@ namespace Eventuous.Tests.Application; public abstract partial class ServiceTestBase { - [Fact] + [Test] public async Task Should_execute_on_any_no_stream() { var bookRoom = Helpers.GetBookRoom(); diff --git a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnExisting.cs b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnExisting.cs index 7a28f51c6..26aa7e8f2 100644 --- a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnExisting.cs +++ b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnExisting.cs @@ -6,7 +6,7 @@ namespace Eventuous.Tests.Application; public abstract partial class ServiceTestBase { - [Fact] + [Test] public async Task Should_execute_on_existing_stream_exists() { var seedCmd = Helpers.GetBookRoom(); var seed = new BookingEvents.RoomBooked(seedCmd.RoomId, seedCmd.CheckIn, seedCmd.CheckOut, seedCmd.Price); @@ -27,7 +27,7 @@ await CommandServiceFixture .Then(result => result.ResultIsOk().NewStreamEventsAre(expectedResult)); } - [Fact] + [Test] public async Task Should_fail_on_existing_no_stream() { var seedCmd = Helpers.GetBookRoom(); var paymentTime = DateTimeOffset.Now; diff --git a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnNew.cs b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnNew.cs index c2ae68116..3731188ac 100644 --- a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnNew.cs +++ b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.OnNew.cs @@ -4,7 +4,7 @@ namespace Eventuous.Tests.Application; public abstract partial class ServiceTestBase { - [Fact] + [Test] public async Task Should_run_on_new_no_stream() { var cmd = Helpers.GetBookRoom(); var expected = new BookingEvents.RoomBooked(cmd.RoomId, cmd.CheckIn, cmd.CheckOut, cmd.Price); @@ -16,7 +16,7 @@ await CommandServiceFixture .Then(result => result.ResultIsOk(x => x.Changes.Should().HaveCount(1)).FullStreamEventsAre(expected)); } - [Fact] + [Test] public async Task Should_fail_on_new_stream_exists() { var cmd = Helpers.GetBookRoom(); var seed = new BookingEvents.RoomBooked(cmd.RoomId, cmd.CheckIn, cmd.CheckOut, cmd.Price); diff --git a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.cs b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.cs index 1da2f4464..534613798 100644 --- a/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.cs +++ b/src/Core/test/Eventuous.Tests.Application/ServiceTestBase.cs @@ -1,23 +1,22 @@ using Eventuous.Sut.App; using Eventuous.Sut.Domain; -using Eventuous.TestHelpers; +using Eventuous.TestHelpers.TUnit; using Eventuous.Testing; using NodaTime; using static Eventuous.Sut.Domain.BookingEvents; -using static Xunit.TestContext; namespace Eventuous.Tests.Application; -public abstract partial class ServiceTestBase : IDisposable { - [Fact] - public async Task Ensure_builder_is_thread_safe() { +public abstract partial class ServiceTestBase { + [Test] + public async Task Ensure_builder_is_thread_safe(CancellationToken cancellationToken) { const int threadCount = 3; var service = CreateService(); var tasks = Enumerable .Range(1, threadCount) - .Select(bookingId => Task.Run(() => service.Handle(Helpers.GetBookRoom(bookingId.ToString()), Current.CancellationToken))) + .Select(bookingId => Task.Run(() => service.Handle(Helpers.GetBookRoom(bookingId.ToString()), cancellationToken))) .ToList(); await Task.WhenAll(tasks); @@ -43,8 +42,8 @@ static ImportBooking CreateCommand() { return cmd; } - protected ServiceTestBase(ITestOutputHelper output) { - _listener = new(output); + protected ServiceTestBase() { + _listener = new(); TypeMap.RegisterKnownEventTypes(typeof(RoomBooked).Assembly); } @@ -67,5 +66,6 @@ protected record ImportBooking( string ImportedBy ); + [After(Test)] public void Dispose() => _listener.Dispose(); } diff --git a/src/Core/test/Eventuous.Tests.Application/StateWithIdTests.cs b/src/Core/test/Eventuous.Tests.Application/StateWithIdTests.cs index f6b1e6a7f..bd9e9028d 100644 --- a/src/Core/test/Eventuous.Tests.Application/StateWithIdTests.cs +++ b/src/Core/test/Eventuous.Tests.Application/StateWithIdTests.cs @@ -1,5 +1,4 @@ using NodaTime; -using static Xunit.TestContext; namespace Eventuous.Tests.Application; @@ -13,8 +12,8 @@ public class StateWithIdTests { public StateWithIdTests() => _service = new(_store); - [Fact] - public async Task ShouldGetIdForNew() { + [Test] + public async Task ShouldGetIdForNew(CancellationToken cancellationToken) { var map = new StreamNameMap(); var id = Guid.NewGuid().ToString(); var result = await Seed(id); @@ -25,7 +24,7 @@ public async Task ShouldGetIdForNew() { result.TryGet(out var ok).Should().BeTrue(); ok!.State.Id.Should().Be(bookingId); - var instance = await _store.LoadAggregate(bookingId, map, true, cancellationToken: Current.CancellationToken); + var instance = await _store.LoadAggregate(bookingId, map, true, cancellationToken: cancellationToken); // Ensure that the id was set when the aggregate was loaded instance.State.Id.Should().Be(bookingId); diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj b/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj index 1095f2d15..20223ed8d 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj @@ -1,8 +1,10 @@  true + false + @@ -10,8 +12,11 @@ + + + \ No newline at end of file diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/DomainFixture.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/DomainFixture.cs index 69ef7810f..5483f9560 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/DomainFixture.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/DomainFixture.cs @@ -1,3 +1,4 @@ +using AutoFixture; using Eventuous.Sut.App; using NodaTime; diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/Helpers.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/Helpers.cs index cd4e00e27..3a22e4c0a 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/Helpers.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/Helpers.cs @@ -1,3 +1,4 @@ +using AutoFixture; using static Eventuous.Sut.App.Commands; using static Eventuous.Sut.Domain.BookingEvents; diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs index 2268f767e..172b19bcc 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs @@ -1,38 +1,34 @@ -// Copyright (C) Ubiquitous AS.All rights reserved -// Licensed under the Apache License, Version 2.0. - using System.Text.RegularExpressions; +using AutoFixture; using Bogus; using DotNet.Testcontainers.Containers; using Eventuous.TestHelpers; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using MicroElements.AutoFixture.NodaTime; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using TUnit.Core.Interfaces; namespace Eventuous.Tests.Persistence.Base.Fixtures; public abstract class StoreFixtureBase { - public IEventStore EventStore { get; protected private set; } = null!; - public IFixture Auto { get; } = new Fixture().Customize(new NodaTimeCustomization()); - protected static Faker Faker { get; } = new(); - protected ServiceProvider Provider { get; set; } = null!; - protected bool AutoStart { get; init; } = true; - public ITestOutputHelper? Output { get; set; } - public TypeMapper TypeMapper { get; } = new(); + public IEventStore EventStore { get; protected private set; } = null!; + public IFixture Auto { get; } = new Fixture().Customize(new NodaTimeCustomization()); + protected static Faker Faker { get; } = new(); + protected ServiceProvider Provider { get; set; } = null!; + protected bool AutoStart { get; init; } = true; + public TypeMapper TypeMapper { get; } = new(); } -public abstract partial class StoreFixtureBase : StoreFixtureBase, IAsyncLifetime where TContainer : DockerContainer { - public virtual async ValueTask InitializeAsync() { +public abstract partial class StoreFixtureBase : StoreFixtureBase, IAsyncInitializer, IAsyncDisposable where TContainer : DockerContainer { + public virtual async Task InitializeAsync() { Container = CreateContainer(); await Container.StartAsync(); var services = new ServiceCollection(); - if (Output != null) { - services.AddSingleton(Output); - services.AddLogging(cfg => cfg.AddXUnit(Output).SetMinimumLevel(LogLevel.Debug)); - } + services.AddLogging(cfg => cfg.AddTUnit().SetMinimumLevel(LogLevel.Debug)); Serializer = new DefaultEventSerializer(TestPrimitives.DefaultOptions, TypeMapper); services.AddSingleton(Serializer); diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Append.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Append.cs index 42fd41ff3..aa353d71b 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Append.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Append.cs @@ -6,7 +6,7 @@ namespace Eventuous.Tests.Persistence.Base.Store; -public abstract class StoreAppendTests : IClassFixture where T : StoreFixtureBase { +public abstract class StoreAppendTests where T : StoreFixtureBase { readonly T _fixture; protected StoreAppendTests(T fixture) { @@ -14,18 +14,18 @@ protected StoreAppendTests(T fixture) { _fixture = fixture; } - [Fact] - [Trait("Category", "Store")] + [Test] + [Category("Store")] public async Task ShouldAppendToNoStream() { var evt = _fixture.CreateEvent(); var streamName = _fixture.GetStreamName(); var result = await _fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream); - result.NextExpectedVersion.Should().Be(0); + await Assert.That(result.NextExpectedVersion).IsEqualTo(0); } - [Fact] - [Trait("Category", "Store")] + [Test] + [Category("Store")] public async Task ShouldAppendOneByOne() { var evt = _fixture.CreateEvent(); var stream = _fixture.GetStreamName(); @@ -37,11 +37,11 @@ public async Task ShouldAppendOneByOne() { var version = new ExpectedStreamVersion(result.NextExpectedVersion); result = await _fixture.AppendEvent(stream, evt, version); - result.NextExpectedVersion.Should().Be(1); + await Assert.That(result.NextExpectedVersion).IsEqualTo(1); } - [Fact] - [Trait("Category", "Store")] + [Test] + [Category("Store")] public async Task ShouldFailOnWrongVersionNoStream() { var evt = _fixture.CreateEvent(); var stream = _fixture.GetStreamName(); @@ -50,12 +50,11 @@ public async Task ShouldFailOnWrongVersionNoStream() { evt = _fixture.CreateEvent(); - var task = () => _fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream); - await task.Should().ThrowAsync(); + await Assert.That(() => _fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream)).Throws(); } - [Fact] - [Trait("Category", "Store")] + [Test] + [Category("Store")] public async Task ShouldFailOnWrongVersion() { var evt = _fixture.CreateEvent(); var stream = _fixture.GetStreamName(); @@ -64,13 +63,12 @@ public async Task ShouldFailOnWrongVersion() { evt = _fixture.CreateEvent(); - var task = () => _fixture.AppendEvent(stream, evt, new(3)); - await task.Should().ThrowAsync(); + await Assert.That(() => _fixture.AppendEvent(stream, evt, new(3))).Throws(); } - [Fact] - [Trait("Category", "Store")] + [Test] + [Category("Store")] public async Task ShouldFailOnWrongVersionWithOptimisticConcurrencyException() { var evt = _fixture.CreateEvent(); var stream = _fixture.GetStreamName(); @@ -79,7 +77,6 @@ public async Task ShouldFailOnWrongVersionWithOptimisticConcurrencyException() { evt = _fixture.CreateEvent(); - var task = () => _fixture.StoreChanges(stream, evt, new(3)); - await task.Should().ThrowAsync(); + await Assert.That(() => _fixture.StoreChanges(stream, evt, new(3))).Throws(); } } diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/OtherMethods.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/OtherMethods.cs index 6d6051433..439885867 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/OtherMethods.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/OtherMethods.cs @@ -1,10 +1,9 @@ using Eventuous.Sut.Domain; using Eventuous.Tests.Persistence.Base.Fixtures; -using static Xunit.TestContext; namespace Eventuous.Tests.Persistence.Base.Store; -public abstract class StoreOtherOpsTests : IClassFixture where T : StoreFixtureBase { +public abstract class StoreOtherOpsTests where T : StoreFixtureBase { readonly T _fixture; protected StoreOtherOpsTests(T fixture) { @@ -12,22 +11,22 @@ protected StoreOtherOpsTests(T fixture) { fixture.TypeMapper.RegisterKnownEventTypes(typeof(BookingEvents.BookingImported).Assembly); } - [Fact] - [Trait("Category", "Store")] - public async Task StreamShouldExist() { + [Test] + [Category("Store")] + public async Task StreamShouldExist(CancellationToken cancellationToken) { var evt = _fixture.CreateEvent(); var streamName = _fixture.GetStreamName(); await _fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream); - var exists = await _fixture.EventStore.StreamExists(streamName, Current.CancellationToken); - exists.Should().BeTrue(); + var exists = await _fixture.EventStore.StreamExists(streamName, cancellationToken); + await Assert.That(exists).IsTrue(); } - [Fact] - [Trait("Category", "Store")] - public async Task StreamShouldNotExist() { + [Test] + [Category("Store")] + public async Task StreamShouldNotExist(CancellationToken cancellationToken) { var streamName = _fixture.GetStreamName(); - var exists = await _fixture.EventStore.StreamExists(streamName, Current.CancellationToken); - exists.Should().BeFalse(); + var exists = await _fixture.EventStore.StreamExists(streamName, cancellationToken); + await Assert.That(exists).IsFalse(); } } diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Read.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Read.cs index 4742b029b..45e5ce2d2 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Read.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/Read.cs @@ -1,13 +1,12 @@ using System.Text.Json; using Eventuous.Sut.Domain; using Eventuous.Tests.Persistence.Base.Fixtures; -using static Xunit.TestContext; // ReSharper disable CoVariantArrayConversion namespace Eventuous.Tests.Persistence.Base.Store; -public abstract class StoreReadTests : IClassFixture where T : StoreFixtureBase { +public abstract class StoreReadTests where T : StoreFixtureBase { readonly T _fixture; protected StoreReadTests(T fixture) { @@ -15,72 +14,71 @@ protected StoreReadTests(T fixture) { _fixture = fixture; } - [Fact] - [Trait("Category", "Store")] - public async Task ShouldReadOne() { + [Test] + [Category("Store")] + public async Task ShouldReadOne(CancellationToken cancellationToken) { var evt = _fixture.CreateEvent(); var streamName = _fixture.GetStreamName(); await _fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream); - var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); - result.Length.Should().Be(1); - result[0].Payload.Should().BeEquivalentTo(evt); + var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, cancellationToken); + await Assert.That(result.Length).IsEqualTo(1); + await Assert.That(result[0].Payload).IsEquivalentTo(evt); } - [Fact] - [Trait("Category", "Store")] - public async Task ShouldReadMany() { + [Test] + [Category("Store")] + public async Task ShouldReadMany(CancellationToken cancellationToken) { object[] events = _fixture.CreateEvents(20).ToArray(); var streamName = _fixture.GetStreamName(); await _fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); - var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); + var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, cancellationToken); var actual = result.Select(x => x.Payload); - actual.Should().BeEquivalentTo(events); + await Assert.That(actual).IsEquivalentTo(events); } - [Fact] - [Trait("Category", "Store")] - public async Task ShouldReadTail() { + [Test] + [Category("Store")] + public async Task ShouldReadTail(CancellationToken cancellationToken) { object[] events = _fixture.CreateEvents(20).ToArray(); var streamName = _fixture.GetStreamName(); await _fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); - var result = await _fixture.EventStore.ReadEvents(streamName, new(10), 100, Current.CancellationToken); + var result = await _fixture.EventStore.ReadEvents(streamName, new(10), 100, cancellationToken); var expected = events.Skip(10); var actual = result.Select(x => x.Payload); - actual.Should().BeEquivalentTo(expected); + await Assert.That(actual).IsEquivalentTo(expected); } - [Fact] - [Trait("Category", "Store")] - public async Task ShouldReadHead() { + [Test] + [Category("Store")] + public async Task ShouldReadHead(CancellationToken cancellationToken) { object[] events = _fixture.CreateEvents(20).ToArray(); var streamName = _fixture.GetStreamName(); await _fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); - var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 10, Current.CancellationToken); + var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 10, cancellationToken); var expected = events.Take(10); - var actual = result.Select(x => x.Payload); - actual.Should().BeEquivalentTo(expected); + + IEnumerable actual = result.Select(x => x.Payload)!; + await Assert.That(actual).IsEquivalentCollectionTo(expected); } - [Fact] - [Trait("Category", "Store")] - public async Task ShouldReadMetadata() { + [Test] + [Category("Store")] + public async Task ShouldReadMetadata(CancellationToken cancellationToken) { var evt = _fixture.CreateEvent(); var streamName = _fixture.GetStreamName(); await _fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream, new() { { "Key1", "Value1" }, { "Key2", "Value2" } }); - var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); + var result = await _fixture.EventStore.ReadEvents(streamName, StreamReadPosition.Start, 100, cancellationToken); - result.Length.Should().Be(1); - result[0].Payload.Should().BeEquivalentTo(evt); + await Assert.That(result.Length).IsEqualTo(1); + await Assert.That(result[0].Payload).IsEquivalentTo(evt); - result[0] - .Metadata.ToDictionary(m => m.Key, m => ((JsonElement)m.Value!).GetString()) - .Should() - .Contain([new("Key1", "Value1"), new("Key2", "Value2")]); + await Assert.That(result[0].Metadata.ToDictionary(m => m.Key, m => ((JsonElement)m.Value!).GetString())) + .ContainsKey("Key1").And.ContainsKey("Key2"); } } diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/TieredStoreTests.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/TieredStoreTests.cs index 44ad6eeac..0a65b7ed3 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Store/TieredStoreTests.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Store/TieredStoreTests.cs @@ -1,3 +1,4 @@ +using AutoFixture; using DotNet.Testcontainers.Containers; using Eventuous.Tests.Persistence.Base.Fixtures; using JetBrains.Annotations; @@ -10,7 +11,7 @@ protected async Task Should_load_hot_and_archive() { var store = _storeFixture.EventStore; var archive = new ArchiveStore(_storeFixture.EventStore); - var testEvents = _fixture.CreateMany(count).ToList(); + var testEvents = _fixture.CreateMany(count).ToArray(); var stream = new StreamName($"Test-{Guid.NewGuid():N}"); await store.Store(stream, ExpectedStreamVersion.NoStream, testEvents); @@ -21,10 +22,10 @@ protected async Task Should_load_hot_and_archive() { var loaded = (await combined.ReadStream(stream, StreamReadPosition.Start)).ToArray(); var actual = loaded.Select(x => (TestEventForTiers)x.Payload!).ToArray(); - actual.Should().BeEquivalentTo(testEvents); + await Assert.That(actual).IsEquivalentCollectionTo(testEvents); - loaded.Take(50).Select(x => x.FromArchive).Should().AllSatisfy(x => x.Should().BeFalse()); - loaded.Skip(50).Select(x => x.FromArchive).Should().AllSatisfy(x => x.Should().BeTrue()); + await Assert.That(loaded.Take(50).Select(x => x.FromArchive)).DoesNotContain(false); + await Assert.That(loaded.Skip(50).Select(x => x.FromArchive)).DoesNotContain(true); } readonly Fixture _fixture = new(); diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryAttribute.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryAttribute.cs deleted file mode 100644 index 1d1401811..000000000 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryAttribute.cs +++ /dev/null @@ -1,17 +0,0 @@ -using JetBrains.Annotations; -using Xunit.v3; - -// ReSharper disable once CheckNamespace - -namespace Xunit; - -/// -/// Apply this attribute to your test method to specify a category. -/// -[UsedImplicitly] -[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] -public class CategoryAttribute(string category) : Attribute, ITraitAttribute { - // [UsedImplicitly] - // public string Name { get; } = category; - public IReadOnlyCollection> GetTraits() => [new("Category", category)]; -} diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryDiscoverer.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryDiscoverer.cs deleted file mode 100644 index e96ee3d44..000000000 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Traits/CategoryDiscoverer.cs +++ /dev/null @@ -1,24 +0,0 @@ -// using JetBrains.Annotations; -// -// // ReSharper disable once CheckNamespace -// namespace Xunit; -// -// using Sdk; -// -// /// -// /// This class discovers all the tests and test classes that have -// /// applied the Category attribute -// /// -// [UsedImplicitly] -// public class CategoryDiscoverer : ITraitDiscoverer { -// /// -// /// Gets the trait values from the Category attribute. -// /// -// /// The trait attribute containing the trait values. -// /// The trait values. -// public IEnumerable> GetTraits(IAttributeInfo traitAttribute) { -// var categoryName = traitAttribute.GetNamedArgument("Name"); -// -// yield return new("Category", categoryName); -// } -// } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj index d5eeb36a8..8a4a253cd 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj @@ -2,8 +2,10 @@ true true + false + @@ -13,5 +15,7 @@ + + diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs index 8b8b0f8cc..16edd47ae 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs @@ -1,4 +1,5 @@ using Eventuous.Subscriptions; +using Microsoft.Extensions.Logging; namespace Eventuous.Tests.Subscriptions.Base; diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs index ba0037cb7..37b905c61 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs @@ -2,32 +2,32 @@ using Eventuous.Subscriptions; using Eventuous.Subscriptions.Checkpoints; using Eventuous.Sut.Domain; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using Eventuous.Tests.Persistence.Base.Fixtures; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using TUnit.Core.Interfaces; +using ILogger = Microsoft.Extensions.Logging.ILogger; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace Eventuous.Tests.Subscriptions.Base; +public interface IStartableFixture : IAsyncInitializer, IAsyncDisposable; + public abstract class SubscriptionFixtureBase - : StoreFixtureBase + : StoreFixtureBase, IStartableFixture where TEventHandler : class, IEventHandler where TContainer : DockerContainer where TCheckpointStore : class, ICheckpointStore where TSubscription : EventSubscription where TSubscriptionOptions : SubscriptionOptions { - readonly ITestOutputHelper _outputHelper; - readonly bool _autoStart; - readonly LogLevel _logLevel; - - protected SubscriptionFixtureBase( - ITestOutputHelper outputHelper, - bool autoStart = true, - LogLevel logLevel = LogLevel.Trace - ) { - _outputHelper = outputHelper; - _autoStart = autoStart; - _logLevel = logLevel; + readonly bool _autoStart; + readonly LogLevel _logLevel; + + protected SubscriptionFixtureBase(bool autoStart = true, LogLevel logLevel = LogLevel.Trace) { + _autoStart = autoStart; + _logLevel = logLevel; TypeMapper.RegisterKnownEventTypes(typeof(BookingEvents.BookingImported).Assembly); } @@ -64,7 +64,7 @@ protected override void SetupServices(IServiceCollection services) { var host = services.First(x => !x.IsKeyedService && x.ImplementationFactory?.GetType() == typeof(Func)); services.Remove(host); - services.AddLogging(b => ConfigureLogging(b.AddXUnit(_outputHelper).SetMinimumLevel(_logLevel))); + services.AddLogging(b => ConfigureLogging(b.AddTUnit().SetMinimumLevel(_logLevel))); } protected override void GetDependencies(IServiceProvider provider) { @@ -81,7 +81,7 @@ protected override void GetDependencies(IServiceProvider provider) { public abstract Task GetLastPosition(); - public override async ValueTask InitializeAsync() { + public override async Task InitializeAsync() { await base.InitializeAsync(); if (_autoStart) await StartSubscription(); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs index ac047eac6..4a812f30e 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs @@ -2,7 +2,9 @@ using Eventuous.Subscriptions.Context; using Hypothesist; using Hypothesist.Builders; + // ReSharper disable NotAccessedPositionalProperty.Global +// ReSharper disable MethodHasAsyncOverload namespace Eventuous.Tests.Subscriptions.Base; @@ -12,7 +14,9 @@ public record TestEvent(string Data, int Number) { public const string TypeName = "test-event"; } -public class TestEventHandler(TestEventHandlerOptions? options = null) : BaseEventHandler { +public class TestEventHandler(TestEventHandlerOptions? options) : BaseEventHandler { + public TestEventHandler() : this(null) { } + readonly TimeSpan _delay = options?.Delay ?? TimeSpan.Zero; public int Count { get; private set; } @@ -25,7 +29,7 @@ public Hypothesis AssertCollection(TimeSpan deadline, List colle => Hypothesis.On(_observer).Timebox(deadline).Exactly(collection.Count).Match(collection.Contains); public override async ValueTask HandleEvent(IMessageConsumeContext context) { - options?.Output?.WriteLine(context.Message!.ToString() ?? string.Empty); + TestContext.Current?.OutputWriter.WriteLine(context.Message!.ToString() ?? string.Empty); await Task.Delay(_delay); await _observer.Add(context.Message!, context.CancellationToken); Count++; @@ -36,4 +40,4 @@ public override async ValueTask HandleEvent(IMessageConsume public void Reset() => Count = 0; } -public record TestEventHandlerOptions(TimeSpan? Delay = null, ITestOutputHelper? Output = null); +public record TestEventHandlerOptions(TimeSpan? Delay = null); diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToAll.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToAll.cs index f6d98ec34..5dee0cea3 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToAll.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToAll.cs @@ -9,26 +9,25 @@ namespace Eventuous.Tests.Subscriptions.Base; public abstract class SubscribeToAllBase( - ITestOutputHelper outputHelper, SubscriptionFixtureBase fixture - ) : IAsyncLifetime + ) : SubscriptionTestBase(fixture) where TContainer : DockerContainer where TSubscription : EventSubscription where TSubscriptionOptions : SubscriptionOptions where TCheckpointStore : class, ICheckpointStore { - protected async Task ShouldConsumeProducedEvents() { + protected async Task ShouldConsumeProducedEvents(CancellationToken cancellationToken) { const int count = 10; var commands = await GenerateAndHandleCommands(count); var testEvents = commands.Select(ToEvent).ToList(); await fixture.StartSubscription(); - await fixture.Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(); + await fixture.Handler.AssertCollection(TimeSpan.FromSeconds(2), [..testEvents]).Validate(cancellationToken); await fixture.StopSubscription(); - fixture.Handler.Count.Should().Be(10); + await Assert.That(fixture.Handler.Count).IsEqualTo(10); } - protected async Task ShouldConsumeProducedEventsWhenRestarting() { + protected async Task ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { await TestConsumptionOfProducedEvents(); fixture.Handler.Reset(); @@ -39,32 +38,33 @@ protected async Task ShouldConsumeProducedEventsWhenRestarting() { return; async Task TestConsumptionOfProducedEvents() { - const int count = 10; - var commands = await GenerateAndHandleCommands(count); - var testEvents = commands.Select(ToEvent).ToList(); + const int count = 10; + + var commands = await GenerateAndHandleCommands(count); + var testEvents = commands.Select(ToEvent).ToList(); await fixture.StartSubscription(); - await fixture.Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(); + await fixture.Handler.AssertCollection(TimeSpan.FromSeconds(2), [..testEvents]).Validate(cancellationToken); await fixture.StopSubscription(); - fixture.Handler.Count.Should().Be(10); + await Assert.That(fixture.Handler.Count).IsEqualTo(10); } } - protected async Task ShouldUseExistingCheckpoint() { + protected async Task ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { const int count = 10; await GenerateAndHandleCommands(count); - await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, default); + await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, cancellationToken); var last = await fixture.GetLastPosition(); - await fixture.CheckpointStore.StoreCheckpoint(new(fixture.SubscriptionId, last), true, default); - - var l = await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, default); - outputHelper.WriteLine("Last checkpoint: {0}", l.Position!); + await fixture.CheckpointStore.StoreCheckpoint(new(fixture.SubscriptionId, last), true, cancellationToken); + + var l = await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, cancellationToken); + TestContext.Current?.OutputWriter.WriteLine("Last checkpoint: {0}", l.Position!); await fixture.StartSubscription(); await Task.Delay(TimeSpan.FromSeconds(1)); await fixture.StopSubscription(); - fixture.Handler.Count.Should().Be(0); + await Assert.That(fixture.Handler.Count).IsEqualTo(0); } static BookingImported ToEvent(ImportBooking cmd) => new(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut); @@ -84,8 +84,4 @@ async Task> GenerateAndHandleCommands(int count) { return commands; } - - public async ValueTask InitializeAsync() => await fixture.InitializeAsync(); - - public async ValueTask DisposeAsync() => await fixture.DisposeAsync(); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToStream.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToStream.cs index e57d9f8e0..0743ecc17 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToStream.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscribeToStream.cs @@ -6,81 +6,83 @@ using static Eventuous.Sut.App.Commands; using static Eventuous.Sut.Domain.BookingEvents; +// ReSharper disable MethodHasAsyncOverload + namespace Eventuous.Tests.Subscriptions.Base; public abstract class SubscribeToStreamBase( - ITestOutputHelper outputHelper, StreamName streamName, SubscriptionFixtureBase fixture - ) : IAsyncLifetime + ) : SubscriptionTestBase(fixture) where TContainer : DockerContainer where TSub : EventSubscription where TSubOptions : SubscriptionOptions where TCheckpointStore : class, ICheckpointStore { - protected async Task ShouldConsumeProducedEvents() { - const int count = 10; + protected async Task ShouldConsumeProducedEvents(CancellationToken cancellationToken) { + const int count = 10; + const ulong expected = count - 1; var testEvents = await GenerateAndProduceEvents(count); await fixture.StartSubscription(); - await fixture.Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(); + await fixture.Handler.AssertCollection(TimeSpan.FromSeconds(2), [..testEvents]).Validate(cancellationToken); await fixture.StopSubscription(); - fixture.Handler.Count.Should().Be(10); + await Assert.That(fixture.Handler.Count).IsEqualTo(10); - var checkpoint = await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, default); - checkpoint.Position.Should().Be(count - 1); + var checkpoint = await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, cancellationToken); + await Assert.That(checkpoint.Position).IsEqualTo(expected); } - protected async Task ShouldConsumeProducedEventsWhenRestarting() { - outputHelper.WriteLine("Phase one"); + protected async Task ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { + TestContext.Current?.OutputWriter.WriteLine("Phase one"); await TestConsumptionOfProducedEvents(); - outputHelper.WriteLine("Resetting handler"); + TestContext.Current?.OutputWriter.WriteLine("Resetting handler"); fixture.Handler.Reset(); - outputHelper.WriteLine("Phase two"); + TestContext.Current?.OutputWriter.WriteLine("Phase two"); await TestConsumptionOfProducedEvents(); - var checkpoint = await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, default); - checkpoint.Position.Should().Be(19); + var checkpoint = await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, cancellationToken); + await Assert.That(checkpoint.Position).IsEqualTo(19UL); return; async Task TestConsumptionOfProducedEvents() { const int count = 10; - outputHelper.WriteLine("Generating and producing events"); + TestContext.Current?.OutputWriter.WriteLine("Generating and producing events"); var testEvents = await GenerateAndProduceEvents(count); - outputHelper.WriteLine("Starting subscription"); + TestContext.Current?.OutputWriter.WriteLine("Starting subscription"); await fixture.StartSubscription(); - await fixture.Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(); - outputHelper.WriteLine("Stopping subscription"); + await fixture.Handler.AssertCollection(TimeSpan.FromSeconds(2), [..testEvents]).Validate(); + TestContext.Current?.OutputWriter.WriteLine("Stopping subscription"); await fixture.StopSubscription(); - fixture.Handler.Count.Should().Be(10); + await Assert.That(fixture.Handler.Count).IsEqualTo(10); } } - public async Task ShouldUseExistingCheckpoint() { + public async Task ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { const int count = 10; await GenerateAndProduceEvents(count); - await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, default); + await fixture.CheckpointStore.GetLastCheckpoint(fixture.SubscriptionId, cancellationToken); Logger.ConfigureIfNull(fixture.SubscriptionId, fixture.LoggerFactory); - await fixture.CheckpointStore.StoreCheckpoint(new(fixture.SubscriptionId, 9), true, default); + await fixture.CheckpointStore.StoreCheckpoint(new(fixture.SubscriptionId, 9), true, cancellationToken); await fixture.StartSubscription(); - await Task.Delay(TimeSpan.FromSeconds(1)); + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); await fixture.StopSubscription(); - fixture.Handler.Count.Should().Be(0); + await Assert.That(fixture.Handler.Count).IsEqualTo(0); } static BookingImported ToEvent(ImportBooking cmd) => new(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut); async Task> GenerateAndProduceEvents(int count) { - outputHelper.WriteLine($"Producing events to {streamName}"); + await TestContext.Current!.OutputWriter.WriteLineAsync($"Producing events to {streamName}")!; var commands = Enumerable .Range(0, count) @@ -94,7 +96,7 @@ async Task> GenerateAndProduceEvents(int count) { return events; } - public async ValueTask InitializeAsync() => await fixture.InitializeAsync(); + protected async Task InitializeAsync() => await fixture.InitializeAsync(); - public async ValueTask DisposeAsync() => await fixture.DisposeAsync(); + protected async Task DisposeAsync() => await fixture.DisposeAsync(); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscriptionTestBase.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscriptionTestBase.cs new file mode 100644 index 000000000..6b778a758 --- /dev/null +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscriptionTestBase.cs @@ -0,0 +1,13 @@ +namespace Eventuous.Tests.Subscriptions.Base; + +public abstract class SubscriptionTestBase(IStartableFixture fixture) { + [Before(Test)] + public async Task Startup() { + await fixture.InitializeAsync(); + } + + [After(Test)] + public async Task Shutdown() { + await fixture.DisposeAsync(); + } +} diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/AutofixtureExtensions.cs b/src/Core/test/Eventuous.Tests.Subscriptions/AutofixtureExtensions.cs index 2b197f746..90ddd2e06 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/AutofixtureExtensions.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/AutofixtureExtensions.cs @@ -1,11 +1,11 @@ using Eventuous.Subscriptions.Context; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; namespace Eventuous.Tests.Subscriptions; public static class AutoFixtureExtensions { - public static MessageConsumeContext CreateContext(this Fixture auto, ITestOutputHelper output) { - var factory = new LoggerFactory().AddXUnit(output); + public static MessageConsumeContext CreateContext(this Fixture auto) { + var factory = new LoggerFactory().AddTUnit(); return auto.Build().With(x => x.LogContext, () => new("test", factory)).Create(); } } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/ConsumePipeTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/ConsumePipeTests.cs index eae919e30..b7f4051b5 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/ConsumePipeTests.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/ConsumePipeTests.cs @@ -4,14 +4,14 @@ namespace Eventuous.Tests.Subscriptions; -public class ConsumePipeTests(ITestOutputHelper outputHelper) { +public class ConsumePipeTests() { static readonly Fixture Auto = new(); - [Fact] + [Test] public async Task ShouldCallHandlers() { var handler = new TestHandler(); var pipe = new ConsumePipe().AddDefaultConsumer(handler); - var ctx = Auto.CreateContext(outputHelper); + var ctx = Auto.CreateContext(); await pipe.Send(ctx); @@ -20,7 +20,7 @@ public async Task ShouldCallHandlers() { const string Key = "test-baggage"; - [Fact] + [Test] public async Task ShouldAddContextBaggage() { var handler = new TestHandler(); var pipe = new ConsumePipe().AddDefaultConsumer(handler); @@ -28,7 +28,7 @@ public async Task ShouldAddContextBaggage() { pipe.AddFilterFirst(new TestFilter(Key, baggage)); - var ctx = Auto.CreateContext(outputHelper); + var ctx = Auto.CreateContext(); await pipe.Send(ctx); diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/DefaultConsumerTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/DefaultConsumerTests.cs index 4e29823ae..e6c135e47 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/DefaultConsumerTests.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/DefaultConsumerTests.cs @@ -1,20 +1,20 @@ using Eventuous.Subscriptions; using Eventuous.Subscriptions.Consumers; using Eventuous.Subscriptions.Context; -using Eventuous.TestHelpers; +using Eventuous.TestHelpers.TUnit; namespace Eventuous.Tests.Subscriptions; -public class DefaultConsumerTests(ITestOutputHelper output) : IDisposable { - readonly TestEventListener _listener = new(output); +public class DefaultConsumerTests() : IDisposable { + readonly TestEventListener _listener = new(); static readonly Fixture Auto = new(); - [Fact] + [Test] public async Task ShouldFailWhenHandlerNacks() { var handler = new FailingHandler(); var consumer = new DefaultConsumer([handler]); - var ctx = Auto.CreateContext(output); + var ctx = Auto.CreateContext(); await consumer.Consume(ctx); diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj index ade1a7e97..f6b9b4312 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj +++ b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj @@ -5,8 +5,10 @@ Exe - - - + + + + + diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/HandlingStatusTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/HandlingStatusTests.cs index b58d7757e..005ae0e3b 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/HandlingStatusTests.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/HandlingStatusTests.cs @@ -3,43 +3,43 @@ namespace Eventuous.Tests.Subscriptions; -public class HandlingStatusTests(ITestOutputHelper output) { +public class HandlingStatusTests() { static Fixture Auto { get; } = new(); - [Fact] + [Test] public void AckAndNackShouldNack() { const EventHandlingStatus actual = EventHandlingStatus.Success | EventHandlingStatus.Failure; (actual & EventHandlingStatus.Handled).Should().Be(EventHandlingStatus.Failure); } - [Fact] + [Test] public void AckAndIgnoreShouldAck() { const EventHandlingStatus actual = EventHandlingStatus.Success | EventHandlingStatus.Ignored; (actual & EventHandlingStatus.Handled).Should().Be(EventHandlingStatus.Success); } - [Fact] + [Test] public void NackAndIgnoreShouldNack() { const EventHandlingStatus actual = EventHandlingStatus.Failure | EventHandlingStatus.Ignored; (actual & EventHandlingStatus.Handled).Should().Be(EventHandlingStatus.Failure); } - [Fact] + [Test] public void PendingShouldBeHandled() { const EventHandlingStatus actual = EventHandlingStatus.Pending; (actual & EventHandlingStatus.Handled).Should().NotBe(EventHandlingStatus.Failure); (actual & EventHandlingStatus.Handled).Should().NotBe(EventHandlingStatus.Ignored); } - [Fact] + [Test] public void IgnoredShouldBeIgnored() { const EventHandlingStatus actual = EventHandlingStatus.Ignored; (actual & EventHandlingStatus.Handled).Should().Be(0); } - [Fact] + [Test] public void NackAndIgnoreShouldFail() { - var context = Auto.CreateContext(output); + var context = Auto.CreateContext(); context.Nack(new Exception()); context.Ignore("test"); context.HasFailed().Should().BeTrue(); @@ -47,9 +47,9 @@ public void NackAndIgnoreShouldFail() { context.HandlingResults.IsPending().Should().BeFalse(); } - [Fact] + [Test] public void NackAckAndIgnoreShouldFail() { - var context = Auto.CreateContext(output); + var context = Auto.CreateContext(); context.Nack(new Exception()); context.Ack(); context.Ignore(); @@ -58,9 +58,9 @@ public void NackAckAndIgnoreShouldFail() { context.HandlingResults.IsPending().Should().BeFalse(); } - [Fact] + [Test] public void AckAndIgnoreShouldSucceed() { - var context = Auto.CreateContext(output); + var context = Auto.CreateContext(); context.Ack(); context.Ignore(); context.HasFailed().Should().BeFalse(); @@ -68,18 +68,18 @@ public void AckAndIgnoreShouldSucceed() { context.HandlingResults.IsPending().Should().BeFalse(); } - [Fact] + [Test] public void IgnoreAndIgnoreShouldIgnore() { - var context = Auto.CreateContext(output); + var context = Auto.CreateContext(); context.Ignore(); context.Ignore(); context.WasIgnored().Should().BeTrue(); context.HandlingResults.IsPending().Should().BeFalse(); } - [Fact] + [Test] public void PendingShouldBePending() { - var context = Auto.CreateContext(output); + var context = Auto.CreateContext(); context.WasIgnored().Should().BeFalse(); context.HasFailed().Should().BeFalse(); context.HandlingResults.IsPending().Should().BeTrue(); diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs index 4b6344121..3fc505f75 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/RegistrationTests.cs @@ -9,19 +9,18 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Logging.Abstractions; -using static Xunit.TestContext; -using LoggingExtensions = Eventuous.TestHelpers.Logging.LoggingExtensions; +using LoggingExtensions = Eventuous.TestHelpers.TUnit.Logging.LoggingExtensions; // ReSharper disable ClassNeverInstantiated.Local namespace Eventuous.Tests.Subscriptions; -public class RegistrationTests(ITestOutputHelper outputHelper) { +public class RegistrationTests() { readonly TestServer _server = new(BuildHost()); readonly Fixture _auto = new(); - readonly ILoggerFactory _logger = LoggingExtensions.GetLoggerFactory(outputHelper); + readonly ILoggerFactory _logger = LoggingExtensions.GetLoggerFactory(); - [Fact] + [Test] public void ShouldBeSingletons() { var subs1 = _server.Services.GetServices().ToArray(); var subs2 = _server.Services.GetServices().ToArray(); @@ -29,22 +28,22 @@ public void ShouldBeSingletons() { subs1[1].Should().BeSameAs(subs2[1]); } - [Fact] + [Test] public void ShouldRegisterBothSubs() { var subs = _server.Services.GetServices().ToArray(); subs.Length.Should().Be(2); } - [Fact] + [Test] public void SubsShouldHaveProperIds() { var subs = _server.Services.GetServices().ToArray(); subs[0].Options.SubscriptionId.Should().Be("sub1"); subs[1].Options.SubscriptionId.Should().Be("sub2"); } - [Theory] - [InlineData(0, typeof(Handler1))] - [InlineData(1, typeof(Handler2))] + [Test] + [Arguments(0, typeof(Handler1))] + [Arguments(1, typeof(Handler2))] public async Task SubsShouldHaveHandlers(int position, Type handlerType) { var subs = _server.Services.GetServices().ToArray(); var logger = _server.Services.GetRequiredService(); @@ -74,7 +73,7 @@ public async Task SubsShouldHaveHandlers(int position, Type handlerType) { handled[0].Context.MessageType.Should().Be(ctx.MessageType); } - [Fact] + [Test] public void ShouldRegisterBothAsHealthReporters() { var services = _server.Services.GetServices().ToArray(); var health = _server.Services.GetServices().ToArray(); @@ -84,8 +83,8 @@ public void ShouldRegisterBothAsHealthReporters() { services.Single().Should().BeSameAs(health.Single()); } - [Fact] - public async Task BothShouldBeRunningAndReportHealthy() { + [Test] + public async Task BothShouldBeRunningAndReportHealthy(CancellationToken cancellationToken) { var subs = _server.Services.GetServices().ToArray(); var health = _server.Services.GetRequiredService() as SubscriptionHealthCheck; @@ -93,13 +92,13 @@ public async Task BothShouldBeRunningAndReportHealthy() { subs.Should().AllSatisfy(x => x.IsRunning.Should().BeTrue()); health.Should().NotBeNull(); - var check = await health!.CheckHealthAsync(new(), Current.CancellationToken); + var check = await health!.CheckHealthAsync(new(), cancellationToken); check.Data["sub1"].Should().Be("Healthy"); check.Data["sub2"].Should().Be("Healthy"); check.Status.Should().Be(HealthStatus.Healthy); } - [Fact] + [Test] public void ShouldRegisterTwoMeasures() { var subs = _server.Services.GetServices().ToArray(); subs.Should().NotBeEmpty(); @@ -141,18 +140,18 @@ class TestSub(TestOptions options, ConsumePipe consumePipe) public GetSubscriptionEndOfStream GetMeasure() => _ => new(new EndOfStream(SubscriptionId, 0, DateTime.UtcNow)); } - abstract class BaseTestHandler(TestHandlerLogger logger) : BaseEventHandler { + public abstract class BaseTestHandler(TestHandlerLogger logger) : BaseEventHandler { public override ValueTask HandleEvent(IMessageConsumeContext ctx) => logger.EventReceived(GetType(), ctx); } - class Handler1(TestHandlerLogger logger) : BaseTestHandler(logger); + public class Handler1(TestHandlerLogger logger) : BaseTestHandler(logger); - class Handler2(TestHandlerLogger logger) : BaseTestHandler(logger); + public class Handler2(TestHandlerLogger logger) : BaseTestHandler(logger); record TestEvent; } -class TestHandlerLogger { +public class TestHandlerLogger { public ValueTask EventReceived(Type handlerType, IMessageConsumeContext ctx) { Records.Add(new(handlerType, ctx)); @@ -162,4 +161,4 @@ public ValueTask EventReceived(Type handlerType, IMessageCo public List Records { get; } = []; } -record TestHandlerLogRecord(Type HandlerType, IMessageConsumeContext Context); +public record TestHandlerLogRecord(Type HandlerType, IMessageConsumeContext Context); diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs index 94a2fc993..ef029b647 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs @@ -1,42 +1,42 @@ using Eventuous.Subscriptions.Checkpoints; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Eventuous.Tests.Subscriptions; public class SequenceTests { - public SequenceTests(ITestOutputHelper output) { + public SequenceTests() { var factory = new LoggerFactory(); - factory.AddProvider(new XUnitLoggerProvider(output)); + factory.AddProvider(new TUnitLoggerProvider()); var services = new ServiceCollection(); services.AddSingleton(factory); var provider = services.BuildServiceProvider(); provider.AddEventuousLogs(); } - [Theory] - [MemberData(nameof(TestData))] + [Test] + [MethodDataSource(nameof(TestData))] public void ShouldReturnFirstBefore(CommitPositionSequence sequence, CommitPosition expected) { var first = sequence.FirstBeforeGap(); first.Should().Be(expected); } - [Fact] + [Test] public void ShouldWorkForOne() { var timestamp = DateTime.Now; var sequence = new CommitPositionSequence { new(0, 1, timestamp) }; sequence.FirstBeforeGap().Should().Be(new CommitPosition(0, 1, timestamp)); } - [Fact] + [Test] public void ShouldWorkForRandomGap() { var random = new Random(); var sequence = new CommitPositionSequence(); var start = (ulong)random.Next(1); for (var i = start; i < start + 100; i++) { - sequence.Add(new CommitPosition(i, i, DateTime.Now)); + sequence.Add(new(i, i, DateTime.Now)); } var gapPlace = random.Next(1, sequence.Count - 1); @@ -47,7 +47,7 @@ public void ShouldWorkForRandomGap() { first.Should().Be(sequence.ElementAt(gapPlace - 1)); } - [Fact] + [Test] public void ShouldWorkForNormalCase() { var sequence = new CommitPositionSequence(); var timestamp = DateTime.Now; @@ -60,21 +60,10 @@ public void ShouldWorkForNormalCase() { first.Should().Be(new CommitPosition(9, 9, timestamp)); } - public static IEnumerable TestData { - get { - var timestamp = DateTime.Now; - - object[] sequence1 = [ - new CommitPositionSequence { new(0, 1, timestamp), new(0, 2, timestamp), new(0, 4, timestamp), new(0, 6, timestamp) }, - new CommitPosition(0, 2, timestamp) - ]; - - object[] sequence2 = [ - new CommitPositionSequence { new(0, 1, timestamp), new(0, 2, timestamp), new(0, 8, timestamp), new(0, 6, timestamp) }, - new CommitPosition(0, 2, timestamp) - ]; + public static IEnumerable<(CommitPositionSequence, CommitPosition)> TestData() { + var timestamp = DateTime.Now; - return [sequence1, sequence2]; - } + yield return ([new(0, 1, timestamp), new(0, 2, timestamp), new(0, 4, timestamp), new(0, 6, timestamp)], new(0, 2, timestamp)); + yield return ([new(0, 1, timestamp), new(0, 2, timestamp), new(0, 8, timestamp), new(0, 6, timestamp)], new(0, 2, timestamp)); } } diff --git a/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj b/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj index 87e0e33bc..13938e703 100644 --- a/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj +++ b/src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj @@ -10,7 +10,6 @@ - diff --git a/src/Core/test/Eventuous.Tests/ForgotToSetId.cs b/src/Core/test/Eventuous.Tests/ForgotToSetId.cs index 0aa5b30ea..4f43d7742 100644 --- a/src/Core/test/Eventuous.Tests/ForgotToSetId.cs +++ b/src/Core/test/Eventuous.Tests/ForgotToSetId.cs @@ -1,5 +1,4 @@ using Eventuous.Tests.Fixtures; -using static Xunit.TestContext; namespace Eventuous.Tests; @@ -7,9 +6,9 @@ public class ForgotToSetId : NaiveFixture { public ForgotToSetId() => Service = new(this.EventStore); [Test] - public async Task ShouldFailWithNoId() { + public async Task ShouldFailWithNoId(CancellationToken cancellationToken) { var cmd = new DoIt(Auto.Create()); - var result = await Service.Handle(cmd, Current.CancellationToken); + var result = await Service.Handle(cmd, cancellationToken); result.Success.Should().BeTrue(); } diff --git a/src/Core/test/Eventuous.Tests/StoringEvents.cs b/src/Core/test/Eventuous.Tests/StoringEvents.cs index ceb36e9ec..f574f0ba9 100644 --- a/src/Core/test/Eventuous.Tests/StoringEvents.cs +++ b/src/Core/test/Eventuous.Tests/StoringEvents.cs @@ -1,6 +1,5 @@ global using NodaTime; using static Eventuous.Sut.Domain.BookingEvents; -using static Xunit.TestContext; namespace Eventuous.Tests; @@ -17,7 +16,7 @@ public StoringEvents() { BookingService Service { get; } [Test] - public async Task StoreInitial() { + public async Task StoreInitial(CancellationToken cancellationToken) { var cmd = new Commands.BookRoom( Auto.Create(), Auto.Create(), @@ -28,7 +27,7 @@ public async Task StoreInitial() { Change[] expected = [new(new RoomBooked(cmd.RoomId, cmd.CheckIn, cmd.CheckOut, cmd.Price), TypeNames.RoomBooked)]; - var result = await Service.Handle(cmd, Current.CancellationToken); + var result = await Service.Handle(cmd, cancellationToken); result.TryGet(out var ok).Should().BeTrue(); ok!.Changes.Should().BeEquivalentTo(expected); diff --git a/src/Core/test/Eventuous.Tests/StoringEventsWithCustomStream.cs b/src/Core/test/Eventuous.Tests/StoringEventsWithCustomStream.cs index f3b184824..53b5fcba8 100644 --- a/src/Core/test/Eventuous.Tests/StoringEventsWithCustomStream.cs +++ b/src/Core/test/Eventuous.Tests/StoringEventsWithCustomStream.cs @@ -3,7 +3,6 @@ using Eventuous.Sut.App; using Eventuous.Sut.Domain; using static Eventuous.Sut.Domain.BookingEvents; -using static Xunit.TestContext; namespace Eventuous.Tests; @@ -18,12 +17,12 @@ public StoringEventsWithCustomStream() { BookingService Service { get; } [Test] - public async Task TestOnNew() { + public async Task TestOnNew(CancellationToken cancellationToken) { var cmd = CreateBookRoomCommand(); Change[] expected = [new(new RoomBooked(cmd.RoomId, cmd.CheckIn, cmd.CheckOut, cmd.Price), TypeNames.RoomBooked)]; - var result = await Service.Handle(cmd, Current.CancellationToken); + var result = await Service.Handle(cmd, cancellationToken); result.TryGet(out var ok).Should().BeTrue(); ok!.Changes.Should().BeEquivalentTo(expected); @@ -34,10 +33,10 @@ public async Task TestOnNew() { } [Test] - public async Task TestOnExisting() { + public async Task TestOnExisting(CancellationToken cancellationToken) { var cmd = CreateBookRoomCommand(); - await Service.Handle(cmd, Current.CancellationToken); + await Service.Handle(cmd, cancellationToken); var secondCmd = new Commands.RecordPayment(new(cmd.BookingId), Auto.Create(), new(cmd.Price), DateTimeOffset.Now); @@ -47,7 +46,7 @@ public async Task TestOnExisting() { new(new BookingFullyPaid(secondCmd.PaidAt), TypeNames.BookingFullyPaid) }; - var result = await Service.Handle(secondCmd, Current.CancellationToken); + var result = await Service.Handle(secondCmd, cancellationToken); result.TryGet(out var ok).Should().BeTrue(); ok!.Changes.Should().BeEquivalentTo(expected); diff --git a/src/Core/test/Eventuous.Tests/TypeRegistrationTests.cs b/src/Core/test/Eventuous.Tests/TypeRegistrationTests.cs index 86831194d..6c056f48a 100644 --- a/src/Core/test/Eventuous.Tests/TypeRegistrationTests.cs +++ b/src/Core/test/Eventuous.Tests/TypeRegistrationTests.cs @@ -1,4 +1,3 @@ -using TUnit.Assertions.Extensions.Generic; using static Eventuous.Sut.Domain.BookingEvents; namespace Eventuous.Tests; diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj index 5848b539b..ed473b928 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Eventuous.Tests.OpenTelemetry.csproj @@ -11,12 +11,12 @@ + - - - + + @@ -36,7 +36,6 @@ - diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs index 60ffd21cc..94b0cfc8d 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs @@ -1,6 +1,3 @@ -// Copyright (C) Ubiquitous AS.All rights reserved -// Licensed under the Apache License, Version 2.0. - using DotNet.Testcontainers.Containers; using Eventuous.Diagnostics; using Eventuous.Subscriptions.Registrations; @@ -42,9 +39,7 @@ static MetricsSubscriptionFixtureBase() { protected abstract void ConfigureSubscription(TSubscriptionOptions options); protected override void SetupServices(IServiceCollection services) { - if (Output != null) { - _listener = new(Output); - } + _listener = new(); services.AddProducer(); services.AddSingleton(); @@ -77,6 +72,6 @@ public override async ValueTask DisposeAsync() { } } -class TestListener(ITestOutputHelper output) : GenericListener(SubscriptionMetrics.ListenerName) { - protected override void OnEvent(KeyValuePair obj) => output.WriteLine($"{obj.Key} {obj.Value}"); +class TestListener() : GenericListener(SubscriptionMetrics.ListenerName) { + protected override void OnEvent(KeyValuePair obj) => TestContext.Current?.OutputWriter.WriteLine($"{obj.Key} {obj.Value}"); } diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs index d6df3292d..751529e71 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs @@ -1,25 +1,26 @@ using DotNet.Testcontainers.Containers; +using Eventuous.TestHelpers.TUnit; using Eventuous.Tests.OpenTelemetry.Fakes; using Eventuous.Tests.Subscriptions.Base; +using Assert = TUnit.Assertions.Assert; +// ReSharper disable MethodHasAsyncOverload // ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract namespace Eventuous.Tests.OpenTelemetry; -public abstract class MetricsTestsBase(ITestOutputHelper outputHelper) : IAsyncLifetime +public abstract class MetricsTestsBase where T : MetricsSubscriptionFixtureBase, new() where TContainer : DockerContainer where TProducer : class, IProducer where TSubscription : EventSubscriptionWithCheckpoint, IMeasuredSubscription where TSubscriptionOptions : SubscriptionWithCheckpointOptions { - T Fixture { get; } = new() { Output = outputHelper }; - - [Fact] - [Trait("Category", "Diagnostics")] - public void ShouldMeasureSubscriptionGapCount() { - Fixture.Output?.WriteLine($"Stream {Fixture.Stream}"); - Assert.NotNull(_values); - var gapCount = GetValue(_values, SubscriptionMetrics.GapCountMetricName)!; + T Fixture { get; } = new(); + + protected async Task ShouldMeasureSubscriptionGapCountBase() { + TestContext.Current?.OutputWriter.WriteLine($"Stream {Fixture.Stream}"); + await Assert.That(_values).IsNotNull(); + var gapCount = GetValue(_values!, SubscriptionMetrics.GapCountMetricName)!; var expectedGap = Fixture.Count - Fixture.Counter.Count; gapCount.Should().NotBeNull(); @@ -30,16 +31,16 @@ public void ShouldMeasureSubscriptionGapCount() { // [Fact] // [Trait("Category", "Diagnostics")] - public void ShouldMeasureSubscriptionDuration() { - Fixture.Output?.WriteLine($"Stream {Fixture.Stream}"); - Assert.NotNull(_values); - var duration = GetValue(_values, SubscriptionMetrics.ProcessingRateName)!; - - duration.Should().NotBeNull(); - duration.CheckTag(SubscriptionMetrics.SubscriptionIdTag, Fixture.SubscriptionId); - duration.CheckTag(Fixture.DefaultTagKey, Fixture.DefaultTagValue); - duration.CheckTag(SubscriptionMetrics.MessageTypeTag, TestEvent.TypeName); - } + // public void ShouldMeasureSubscriptionDuration() { + // Fixture.Output?.WriteLine($"Stream {Fixture.Stream}"); + // Assert.NotNull(_values); + // var duration = GetValue(_values, SubscriptionMetrics.ProcessingRateName)!; + // + // duration.Should().NotBeNull(); + // duration.CheckTag(SubscriptionMetrics.SubscriptionIdTag, Fixture.SubscriptionId); + // duration.CheckTag(Fixture.DefaultTagKey, Fixture.DefaultTagValue); + // duration.CheckTag(SubscriptionMetrics.MessageTypeTag, TestEvent.TypeName); + // } static MetricValue? GetValue(MetricValue[] values, string metric) => values.FirstOrDefault(x => x.Name == metric); @@ -57,7 +58,7 @@ public async ValueTask InitializeAsync() { _values = Fixture.Exporter.CollectValues(); foreach (var value in _values) { - Fixture.Output?.WriteLine(value.ToString()); + TestContext.Current?.OutputWriter.WriteLine(value.ToString()); } } @@ -66,7 +67,7 @@ public async ValueTask DisposeAsync() { _es.Dispose(); } - readonly TestEventListener _es = new(outputHelper, null, "OpenTelemetry"); + readonly TestEventListener _es = new(null, "OpenTelemetry"); MetricValue[]? _values; } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ab8e68dd8..f3815edd2 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -30,7 +30,7 @@ true true true - --report-trx + --report-trx --results-directory $(RepoRoot)/test-results/$(TargetFramework) false @@ -66,14 +66,12 @@ - - + - diff --git a/src/Directory.Testable.targets b/src/Directory.Testable.targets index 890dcb454..cfb8cf129 100644 --- a/src/Directory.Testable.targets +++ b/src/Directory.Testable.targets @@ -1,8 +1,11 @@ - - + + false CA1816 + + + diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/AppServiceTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/AppServiceTests.cs index ee92bd63b..c227fc3c1 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/AppServiceTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/AppServiceTests.cs @@ -1,39 +1,43 @@ using Eventuous.Sut.App; using Eventuous.Sut.Domain; -using Eventuous.TestHelpers; -using static Xunit.TestContext; +using Eventuous.TestHelpers.TUnit; namespace Eventuous.Tests.EventStore; -public class AppServiceTests : IClassFixture, IDisposable { - readonly TestEventListener _listener; +[ClassDataSource(Shared = SharedType.None)] +public class AppServiceTests { + readonly TestEventListener _listener = new(); readonly StoreFixture _fixture; - public AppServiceTests(StoreFixture fixture, ITestOutputHelper output) { - _fixture = fixture; - _listener = new(output); - Service = new(fixture.EventStore); + public AppServiceTests(StoreFixture fixture) { + _fixture = fixture; _fixture.TypeMapper.AddType(); } - BookingService Service { get; } + BookingService Service { get; set; } = null!; - [Fact] - [Trait("Category", "Application")] - public async Task ProcessAnyForNew() { + [Before(Test)] + public void BeforeTest() { + Service = new(_fixture.EventStore); + } + + [Test] + [Category("Application")] + public async Task ProcessAnyForNew(CancellationToken cancellationToken) { var cmd = DomainFixture.CreateImportBooking(); var expected = new object[] { new BookingEvents.BookingImported(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut) }; - var handlingResult = await Service.Handle(cmd, Current.CancellationToken); + var handlingResult = await Service.Handle(cmd, cancellationToken); handlingResult.Success.Should().BeTrue(); - var events = await _fixture.EventStore.ReadEvents(StreamName.For(cmd.BookingId), StreamReadPosition.Start, int.MaxValue, Current.CancellationToken); + var events = await _fixture.EventStore.ReadEvents(StreamName.For(cmd.BookingId), StreamReadPosition.Start, int.MaxValue, cancellationToken); var result = events.Select(x => x.Payload).ToArray(); result.Should().BeEquivalentTo(expected); } + [After(Test)] public void Dispose() => _listener.Dispose(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj b/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj index 1b6c63101..bac49f0a3 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Eventuous.Tests.EventStore.csproj @@ -19,6 +19,7 @@ + diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Metrics/MetricsTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Metrics/MetricsTests.cs index 3ae28d16d..7ad238440 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Metrics/MetricsTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Metrics/MetricsTests.cs @@ -6,6 +6,15 @@ namespace Eventuous.Tests.EventStore.Metrics; -[Collection("Database")] -public class MetricsTests(ITestOutputHelper outputHelper) - : MetricsTestsBase(outputHelper); +public class MetricsTests : MetricsTestsBase { + [Test] + public async Task ShouldMeasureSubscriptionGapCount() { + await ShouldMeasureSubscriptionGapCountBase(); + } + + [Before(Test)] + public async Task Setup() => await InitializeAsync(); + + [After(Test)] + public async Task TearDown() => await DisposeAsync(); +} diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/ProducerTracesTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/ProducerTracesTests.cs index c24642b64..0d94a7f3c 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/ProducerTracesTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/ProducerTracesTests.cs @@ -4,16 +4,15 @@ using Eventuous.TestHelpers; using Eventuous.Tests.EventStore.Subscriptions.Fixtures; using Eventuous.Tests.Subscriptions.Base; -using static Xunit.TestContext; namespace Eventuous.Tests.EventStore; -public class TracesTests : LegacySubscriptionFixture, IDisposable { +public class TracesTests : LegacySubscriptionFixture { readonly ActivityListener _listener; static TracesTests() => TypeMap.Instance.AddType(TestEvent.TypeName); - public TracesTests(ITestOutputHelper output) : base(output, new(), false) { + public TracesTests() : base(new()) { _listener = new() { ShouldListenTo = _ => true, Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllData, @@ -29,16 +28,16 @@ public class TracesTests : LegacySubscriptionFixture, IDisposable ActivitySource.AddActivityListener(_listener); } - [Fact] - [Trait("Category", "Diagnostics")] - public async Task ShouldPropagateRemoteContext() { + [Test] + [Category("Diagnostics")] + public async Task ShouldPropagateRemoteContext(CancellationToken cancellationToken) { var testEvent = Auto.Create(); - await Producer.Produce(Stream, testEvent, new(), cancellationToken: Current.CancellationToken); + await Producer.Produce(Stream, testEvent, new(), cancellationToken: cancellationToken); await Start(); - var writtenEvent = (await StoreFixture.EventStore.ReadEvents(Stream, StreamReadPosition.Start, 1, Current.CancellationToken))[0]; + var writtenEvent = (await StoreFixture.EventStore.ReadEvents(Stream, StreamReadPosition.Start, 1, cancellationToken))[0]; var meta = writtenEvent.Metadata; var (traceId, spanId, _) = meta.GetTracingMeta(); @@ -47,7 +46,7 @@ public async Task ShouldPropagateRemoteContext() { spanId.Should().NotBe(RecordedTrace.DefaultSpanId); while (Handler.Contexts.Count == 0) { - await Task.Delay(100, Current.CancellationToken); + await Task.Delay(100, cancellationToken); } await Stop(); @@ -62,5 +61,6 @@ public async Task ShouldPropagateRemoteContext() { recordedTrace.ParentSpanId!.Value.ToString().Should().Be(spanId); } + [After(Test)] public void Dispose() => _listener.Dispose(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/RegistrationTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/RegistrationTests.cs index 2451863e5..40aa88b65 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/RegistrationTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/RegistrationTests.cs @@ -7,7 +7,8 @@ namespace Eventuous.Tests.EventStore; -public class RegistrationTests(StoreFixture fixture) : IClassFixture, IAsyncLifetime { +[ClassDataSource] +public class RegistrationTests(StoreFixture fixture) { const string SubId = "Test"; static readonly StreamName Stream = new("teststream"); @@ -15,33 +16,34 @@ public class RegistrationTests(StoreFixture fixture) : IClassFixture(); } - [Fact] - [Trait("Category", "Dependency injection")] + [Test] + [Category("Dependency injection")] public void ShouldHaveProperId() => Sub.SubscriptionId.Should().Be(SubId); - [Fact] - [Trait("Category", "Dependency injection")] + [Test] + [Category("Dependency injection")] public void ShouldHaveEventStoreClient() { var client = Sub.GetPrivateMember("EventStoreClient"); client.Should().Be(fixture.Client); } - [Fact] - [Trait("Category", "Dependency injection")] + [Test] + [Category("Dependency injection")] public void ShouldHaveNoOpStore() { var store = Sub.GetPrivateMember("CheckpointStore"); store.Should().BeOfType(); } + [Before(Test)] public ValueTask InitializeAsync() { var services = new ServiceCollection(); @@ -62,6 +64,7 @@ public ValueTask InitializeAsync() { return ValueTask.CompletedTask; } + [After(Test)] public ValueTask DisposeAsync() => ValueTask.CompletedTask; } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs index 7c7f82ba2..fbc38e961 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs @@ -1,36 +1,36 @@ using System.Collections.Immutable; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using JetBrains.Annotations; using static Eventuous.AggregateFactoryRegistry; -using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Store; -public class AggregateStoreTests : IClassFixture { +[ClassDataSource] +public class AggregateStoreTests { readonly StoreFixture _fixture; readonly ILogger _log; - public AggregateStoreTests(StoreFixture fixture, ITestOutputHelper output) { + public AggregateStoreTests(StoreFixture fixture) { _fixture = fixture; _fixture.TypeMapper.AddType("testAggregateEvent"); - var loggerFactory = LoggerFactory.Create(cfg => cfg.AddXUnit(output).SetMinimumLevel(LogLevel.Debug)); + var loggerFactory = LoggerFactory.Create(cfg => cfg.AddTUnit().SetMinimumLevel(LogLevel.Debug)); _log = loggerFactory.CreateLogger(); } - [Fact] - [Trait("Category", "Store")] + [Test] + [Category("Store")] [Obsolete("Obsolete")] - public async Task AppendedEventShouldBeTraced() { + public async Task AppendedEventShouldBeTraced(CancellationToken cancellationToken) { var id = new TestId(Guid.NewGuid().ToString("N")); var aggregate = Instance.CreateInstance(); aggregate.DoIt("test"); - await _fixture.AggregateStore.Store(aggregate, id, CancellationToken.None); + await _fixture.AggregateStore.Store(aggregate, id, cancellationToken); } - [Fact] - [Trait("Category", "Store")] + [Test] + [Category("Store")] [Obsolete("Obsolete")] - public async Task ShouldReadLongAggregateStream() { + public async Task ShouldReadLongAggregateStream(CancellationToken cancellationToken) { const int count = 9000; var id = new TestId(Guid.NewGuid().ToString("N")); @@ -45,33 +45,33 @@ public async Task ShouldReadLongAggregateStream() { if (counter != 1000) continue; _log.LogInformation("Storing batch of events.."); - await _fixture.AggregateStore.Store(aggregate, id, CancellationToken.None); - aggregate = await _fixture.AggregateStore.Load(id, CancellationToken.None); + await _fixture.AggregateStore.Store(aggregate, id, cancellationToken); + aggregate = await _fixture.AggregateStore.Load(id, cancellationToken); counter = 0; } - await _fixture.AggregateStore.Store(aggregate, id, CancellationToken.None); + await _fixture.AggregateStore.Store(aggregate, id, cancellationToken); _log.LogInformation("Loading large aggregate stream.."); - var restored = await _fixture.AggregateStore.Load(id, CancellationToken.None); + var restored = await _fixture.AggregateStore.Load(id, cancellationToken); restored.State.Values.Count.Should().Be(count); restored.State.Values.Should().BeEquivalentTo(aggregate.State.Values); } - [Fact] - [Trait("Category", "Store")] + [Test] + [Category("Store")] [Obsolete("Obsolete")] - public async Task ShouldReadAggregateStreamManyTimes() { + public async Task ShouldReadAggregateStreamManyTimes(CancellationToken cancellationToken) { var id = new TestId(Guid.NewGuid().ToString("N")); var aggregate = Instance.CreateInstance(); aggregate.DoIt("test"); - await _fixture.AggregateStore.Store(aggregate, id, Current.CancellationToken); + await _fixture.AggregateStore.Store(aggregate, id, cancellationToken); const int numberOfReads = 100; foreach (var unused in Enumerable.Range(0, numberOfReads)) { - var read = await _fixture.AggregateStore.Load(id, Current.CancellationToken); + var read = await _fixture.AggregateStore.Load(id, cancellationToken); read.State.Should().BeEquivalentTo(aggregate.State); } } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs index 7c8de04a5..99f4cf091 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs @@ -1,42 +1,42 @@ using System.Collections.Immutable; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using JetBrains.Annotations; using static Eventuous.AggregateFactoryRegistry; -using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Store; -public class EventStoreAggregateTests : IClassFixture, IDisposable { +[ClassDataSource] +public class EventStoreAggregateTests { readonly StoreFixture _fixture; readonly ILogger _log; readonly ILoggerFactory _loggerFactory; - public EventStoreAggregateTests(StoreFixture fixture, ITestOutputHelper output) { + public EventStoreAggregateTests(StoreFixture fixture) { _fixture = fixture; _fixture.TypeMapper.AddType("testEvent"); - _loggerFactory = LoggerFactory.Create(cfg => cfg.AddXUnit(output).SetMinimumLevel(LogLevel.Debug)); + _loggerFactory = LoggerFactory.Create(cfg => cfg.AddTUnit().SetMinimumLevel(LogLevel.Debug)); _log = _loggerFactory.CreateLogger(); } - [Fact] - [Trait("Category", "Store")] - public async Task AppendedEventShouldBeTraced() { + [Test] + [Category("Store")] + public async Task AppendedEventShouldBeTraced(CancellationToken cancellationToken) { var id = new TestId(Guid.NewGuid().ToString("N")); var aggregate = Instance.CreateInstance(); aggregate.DoIt("test"); - await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: Current.CancellationToken); + await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: cancellationToken); var streamName = StreamNameFactory.For(id); - var events = await _fixture.EventStore.ReadStream(streamName, StreamReadPosition.Start, cancellationToken: Current.CancellationToken); + var events = await _fixture.EventStore.ReadStream(streamName, StreamReadPosition.Start, cancellationToken: cancellationToken); var first = events[0]; first.Metadata["trace-id"].Should().NotBeNull(); first.Metadata["span-id"].Should().NotBeNull(); } - [Fact] - [Trait("Category", "Store")] - public async Task ShouldReadLongAggregateStream() { + [Test] + [Category("Store")] + public async Task ShouldReadLongAggregateStream(CancellationToken cancellationToken) { const int count = 9000; var id = new TestId(Guid.NewGuid().ToString("N")); @@ -51,32 +51,32 @@ public async Task ShouldReadLongAggregateStream() { if (counter != 1000) continue; _log.LogInformation("Storing batch of events.."); - await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: Current.CancellationToken); - aggregate = await _fixture.EventStore.LoadAggregate(id, cancellationToken: Current.CancellationToken); + await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: cancellationToken); + aggregate = await _fixture.EventStore.LoadAggregate(id, cancellationToken: cancellationToken); counter = 0; } - await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: Current.CancellationToken); + await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: cancellationToken); _log.LogInformation("Loading large aggregate stream.."); - var restored = await _fixture.EventStore.LoadAggregate(id, cancellationToken: Current.CancellationToken); + var restored = await _fixture.EventStore.LoadAggregate(id, cancellationToken: cancellationToken); restored.State.Values.Count.Should().Be(count); restored.State.Values.Should().BeEquivalentTo(aggregate.State.Values); } - [Fact] - [Trait("Category", "Store")] - public async Task ShouldReadAggregateStreamManyTimes() { + [Test] + [Category("Store")] + public async Task ShouldReadAggregateStreamManyTimes(CancellationToken cancellationToken) { var id = new TestId(Guid.NewGuid().ToString("N")); var aggregate = Instance.CreateInstance(); aggregate.DoIt("test"); - await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: Current.CancellationToken); + await _fixture.EventStore.StoreAggregate(aggregate, id, cancellationToken: cancellationToken); const int numberOfReads = 100; foreach (var unused in Enumerable.Range(0, numberOfReads)) { - var read = await _fixture.EventStore.LoadAggregate(id, cancellationToken: Current.CancellationToken); + var read = await _fixture.EventStore.LoadAggregate(id, cancellationToken: cancellationToken); read.State.Should().BeEquivalentTo(aggregate.State); } } @@ -96,5 +96,6 @@ class TestAggregate : Aggregate { record TestEvent(string Data); + [After(Test)] public void Dispose() => _loggerFactory.Dispose(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Store/StoreTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Store/StoreTests.cs index 02a217d76..5b050bdba 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Store/StoreTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Store/StoreTests.cs @@ -4,8 +4,14 @@ namespace Eventuous.Tests.EventStore.Store; +[InheritsTests] +[ClassDataSource] public class Append(StoreFixture fixture) : StoreAppendTests(fixture); +[InheritsTests] +[ClassDataSource] public class Read(StoreFixture fixture) : StoreReadTests(fixture); +[InheritsTests] +[ClassDataSource] public class OtherMethods(StoreFixture fixture) : StoreOtherOpsTests(fixture); diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Store/TieredStoreTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Store/TieredStoreTests.cs index 22be37e2a..3da1f3dd9 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Store/TieredStoreTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Store/TieredStoreTests.cs @@ -1,12 +1,11 @@ using Eventuous.Tests.Persistence.Base.Store; -using JetBrains.Annotations; using Testcontainers.EventStoreDb; namespace Eventuous.Tests.EventStore.Store; -[UsedImplicitly] -public class TieredStoreTests(StoreFixture storeFixture) : TieredStoreTestsBase(storeFixture), IClassFixture { - [Fact] +[ClassDataSource] +public class TieredStoreTests(StoreFixture storeFixture) : TieredStoreTestsBase(storeFixture) { + [Test] public async Task Esdb_should_load_hot_and_archive() { await Should_load_hot_and_archive(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/CatchUpSubscriptionFixture.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/CatchUpSubscriptionFixture.cs index a334dd473..8ccc94f09 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/CatchUpSubscriptionFixture.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/CatchUpSubscriptionFixture.cs @@ -9,17 +9,11 @@ namespace Eventuous.Tests.EventStore.Subscriptions.Fixtures; public class CatchUpSubscriptionFixture( Action configureOptions, - ITestOutputHelper outputHelper, StreamName streamName, bool autoStart = true, Action? configureServices = null, LogLevel logLevel = LogLevel.Debug - ) - : SubscriptionFixtureBase( - outputHelper, - autoStart, - logLevel - ) + ) : SubscriptionFixtureBase(autoStart, logLevel) where TSubscription : EventStoreCatchUpSubscriptionBase where TSubscriptionOptions : CatchUpSubscriptionOptions where TEventHandler : class, IEventHandler { @@ -33,7 +27,7 @@ protected override void SetupServices(IServiceCollection services) { base.SetupServices(services); services.AddEventStoreClient(Container.GetConnectionString()); services.AddEventStore(); - services.AddSingleton(new TestEventHandlerOptions(null, _outputHelper)); + services.AddSingleton(new TestEventHandlerOptions()); configureServices?.Invoke(services); } @@ -48,7 +42,7 @@ protected override void GetDependencies(IServiceProvider provider) { public override async Task GetLastPosition() { return streamName == "$all" ? await GetLastFromAll() : await GetLastFromStream(); - + async Task GetLastFromStream() { var lastEvent = await Client.ReadStreamAsync(Direction.Backwards, streamName, StreamPosition.End, 1).ToArrayAsync(); @@ -68,8 +62,6 @@ async Task GetLastFromAll() { "Grpc.Net.Client.Internal.GrpcCall" ]; - readonly ITestOutputHelper _outputHelper = outputHelper; - static bool Filter(string? provider, string? category, LogLevel logLevel) { if (category == null) return true; diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/LegacySubscriptionFixture.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/LegacySubscriptionFixture.cs index facb4e3fb..e33ed9200 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/LegacySubscriptionFixture.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/LegacySubscriptionFixture.cs @@ -2,11 +2,12 @@ using Eventuous.EventStore.Subscriptions; using Eventuous.Subscriptions.Filters; using Eventuous.Tests.Subscriptions.Base; -using LoggingExtensions = Eventuous.TestHelpers.Logging.LoggingExtensions; +using TUnit.Core.Interfaces; +using LoggingExtensions = Eventuous.TestHelpers.TUnit.Logging.LoggingExtensions; namespace Eventuous.Tests.EventStore.Subscriptions.Fixtures; -public abstract class LegacySubscriptionFixture : IAsyncLifetime where T : class, IEventHandler { +public abstract class LegacySubscriptionFixture: IAsyncInitializer, IAsyncDisposable where T : class, IEventHandler { protected readonly Fixture Auto = new(); protected StreamName Stream { get; } = new($"test-{Guid.NewGuid():N}"); @@ -17,17 +18,10 @@ public abstract class LegacySubscriptionFixture : IAsyncLifetime where T : cl protected TestCheckpointStore CheckpointStore { get; } = new(); protected StreamSubscription Subscription { get; set; } = null!; - protected LegacySubscriptionFixture( - ITestOutputHelper output, - T handler, - bool autoStart = true, - StreamName? stream = null, - LogLevel logLevel = LogLevel.Debug - ) { - _autoStart = autoStart; + protected LegacySubscriptionFixture(T handler, StreamName? stream = null, LogLevel logLevel = LogLevel.Debug) { if (stream is { } s) Stream = s; - LoggerFactory = LoggingExtensions.GetLoggerFactory(output, logLevel); + LoggerFactory = LoggingExtensions.GetLoggerFactory(logLevel); Handler = handler; Log = LoggerFactory.CreateLogger(GetType()); StoreFixture.TypeMapper.RegisterKnownEventTypes(typeof(TestEvent).Assembly); @@ -38,9 +32,7 @@ protected LegacySubscriptionFixture( protected ValueTask Stop() => Subscription.UnsubscribeWithLog(Log); ILoggerFactory LoggerFactory { get; } - readonly bool _autoStart; - - public async ValueTask InitializeAsync() { + public async Task InitializeAsync() { await StoreFixture.InitializeAsync(); Producer = new(StoreFixture.Client); @@ -59,11 +51,23 @@ public async ValueTask InitializeAsync() { pipe, LoggerFactory ); - if (_autoStart) await Start(); } public async ValueTask DisposeAsync() { - if (_autoStart) await Stop(); await StoreFixture.DisposeAsync(); } } + +public class LegacySubscriptionFixture(TimeSpan? timeout, bool autoStart = true, StreamName? stream = null, LogLevel logLevel = LogLevel.Debug) + : LegacySubscriptionFixture(new(new(timeout)), stream, logLevel) { + [Before(Test)] + public async Task Setup() { + await InitializeAsync(); + if (autoStart) await Start(); + } + + public async Task Teardown() { + if (autoStart) await Stop(); + await DisposeAsync(); + } +} diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/PersistentSubscriptionFixture.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/PersistentSubscriptionFixture.cs index 2800b14a6..3b2769713 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/PersistentSubscriptionFixture.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/Fixtures/PersistentSubscriptionFixture.cs @@ -2,48 +2,45 @@ using Eventuous.EventStore.Producers; using Eventuous.EventStore.Subscriptions; using Eventuous.Tests.Subscriptions.Base; -using LoggingExtensions = Eventuous.TestHelpers.Logging.LoggingExtensions; +using LoggingExtensions = Eventuous.TestHelpers.TUnit.Logging.LoggingExtensions; namespace Eventuous.Tests.EventStore.Subscriptions.Fixtures; -public abstract class PersistentSubscriptionFixture( - ITestOutputHelper outputHelper, - THandler handler, - bool autoStart = true, - LogLevel logLevel = LogLevel.Information - ) : IAsyncLifetime +public class PersistentSubscriptionFixture( + THandler handler, + Func subscriptionFactory, + bool autoStart = true, + LogLevel logLevel = LogLevel.Information + ) where THandler : class, IEventHandler where TSubscription : PersistentSubscriptionBase where TOptions : PersistentSubscriptionOptions { + public readonly Fixture Auto = new(); - protected readonly Fixture Auto = new(); - - protected StreamName Stream { get; } = new($"test-{Guid.NewGuid():N}"); - protected THandler Handler { get; } = handler; - protected EventStoreProducer Producer { get; private set; } = null!; + public StreamName Stream { get; } = new($"test-{Guid.NewGuid():N}"); + public THandler Handler { get; } = handler; + public EventStoreProducer Producer { get; private set; } = null!; protected ILogger Log { get; set; } = null!; protected StoreFixture Fixture { get; } = new(); TSubscription Subscription { get; set; } = null!; - protected ValueTask Start() => Subscription.SubscribeWithLog(Log); + public ValueTask Start() => Subscription.SubscribeWithLog(Log); - protected ValueTask Stop() => Subscription.UnsubscribeWithLog(Log); + public ValueTask Stop() => Subscription.UnsubscribeWithLog(Log); LoggingEventListener _listener = null!; - protected abstract TSubscription CreateSubscription(string id, ILoggerFactory loggerFactory); - public async ValueTask InitializeAsync() { Fixture.TypeMapper.RegisterKnownEventTypes(typeof(TestEvent).Assembly); await Fixture.InitializeAsync(); Producer = new(Fixture.Client); - var loggerFactory = LoggingExtensions.GetLoggerFactory(outputHelper, logLevel); + var loggerFactory = LoggingExtensions.GetLoggerFactory(logLevel); var subscriptionId = $"test-{Guid.NewGuid():N}"; Log = loggerFactory.CreateLogger(GetType()); _listener = new(loggerFactory); - Subscription = CreateSubscription(subscriptionId, loggerFactory); + Subscription = subscriptionFactory(subscriptionId, Fixture.Container.GetConnectionString(), Stream, Handler, loggerFactory); if (autoStart) await Start(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeManyPartitionedTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeManyPartitionedTests.cs index f0030bd0b..135930e3a 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeManyPartitionedTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeManyPartitionedTests.cs @@ -1,22 +1,18 @@ using Eventuous.Producers; using Eventuous.Tests.EventStore.Subscriptions.Fixtures; using Eventuous.Tests.Subscriptions.Base; -using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Subscriptions; -[Collection("Database")] -public class PublishAndSubscribeManyPartitionedTests(ITestOutputHelper output) - : LegacySubscriptionFixture( - output, - new(new(5.Milliseconds(), output)), +public class PublishAndSubscribeManyPartitionedTests() : LegacySubscriptionFixture( + 5.Milliseconds(), false, new StreamName(Guid.NewGuid().ToString("N")), logLevel: LogLevel.Trace ) { - [Fact] - [Trait("Category", "Stream catch-up subscription")] - public async Task SubscribeAndProduceMany() { + [Test] + [Category("Stream catch-up subscription")] + public async Task SubscribeAndProduceMany(CancellationToken cancellationToken) { const int count = 10; var testEvents = Enumerable.Range(1, count) @@ -24,8 +20,8 @@ public async Task SubscribeAndProduceMany() { .ToList(); await Start(); - await Producer.Produce(Stream, testEvents, new Metadata(), cancellationToken: Current.CancellationToken); - await Handler.AssertCollection(5.Seconds(), [..testEvents]).Validate(Current.CancellationToken); + await Producer.Produce(Stream, testEvents, new Metadata(), cancellationToken: cancellationToken); + await Handler.AssertCollection(5.Seconds(), [..testEvents]).Validate(cancellationToken); await Stop(); CheckpointStore.GetCheckpoint(Subscription.SubscriptionId).Should().Be(count - 1); diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeManyTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeManyTests.cs index 757a37d08..3865eda6e 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeManyTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeManyTests.cs @@ -1,23 +1,20 @@ using Eventuous.Producers; using Eventuous.Tests.EventStore.Subscriptions.Fixtures; using Eventuous.Tests.Subscriptions.Base; -using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Subscriptions; -[Collection("Database")] -public class PublishAndSubscribeManyTests(ITestOutputHelper output) - : LegacySubscriptionFixture(output, new(new(1.Milliseconds(), output)), false, logLevel: LogLevel.Debug) { - [Fact] - [Trait("Category", "Stream catch-up subscription")] - public async Task SubscribeAndProduceMany() { +public class PublishAndSubscribeManyTests() : LegacySubscriptionFixture(1.Milliseconds(), false) { + [Test] + [Category("Stream catch-up subscription")] + public async Task SubscribeAndProduceMany(CancellationToken cancellationToken) { const int count = 100; var testEvents = Auto.CreateMany(count).ToList(); await Start(); - await Producer.Produce(Stream, testEvents, new(), cancellationToken: Current.CancellationToken); - await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); + await Producer.Produce(Stream, testEvents, new(), cancellationToken: cancellationToken); + await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(cancellationToken); await Stop(); CheckpointStore.GetCheckpoint(Subscription.SubscriptionId).Should().Be(count - 1); diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeOneTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeOneTests.cs index e42e05f87..e3fd05236 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeOneTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/PublishAndSubscribeOneTests.cs @@ -1,24 +1,21 @@ using Eventuous.Producers; using Eventuous.Tests.EventStore.Subscriptions.Fixtures; using Eventuous.Tests.Subscriptions.Base; -using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Subscriptions; -[Collection("Database")] -public class PublishAndSubscribeOneTests(ITestOutputHelper output) - : LegacySubscriptionFixture(output, new(new(null, output)), false, logLevel: LogLevel.Debug) { - [Fact] - [Trait("Category", "Stream catch-up subscription")] - public async Task SubscribeAndProduce() { +public class PublishAndSubscribeOneTests() : LegacySubscriptionFixture(null, false) { + [Test] + [Category("Stream catch-up subscription")] + public async Task SubscribeAndProduce(CancellationToken cancellationToken) { var testEvent = Auto.Create(); await Start(); - await Producer.Produce(Stream, testEvent, new Metadata(), cancellationToken: Current.CancellationToken); - await Handler.AssertCollection(5.Seconds(), [testEvent]).Validate(Current.CancellationToken); + await Producer.Produce(Stream, testEvent, new(), cancellationToken: cancellationToken); + await Handler.AssertCollection(5.Seconds(), [testEvent]).Validate(cancellationToken); await Stop(); - await Task.Delay(100, Current.CancellationToken); + await Task.Delay(100, cancellationToken); CheckpointStore.GetCheckpoint(Subscription.SubscriptionId).Should().Be(0); } } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamPersistentPublishAndSubscribeManyTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamPersistentPublishAndSubscribeManyTests.cs index 8f7be23c2..980f941e4 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamPersistentPublishAndSubscribeManyTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamPersistentPublishAndSubscribeManyTests.cs @@ -4,65 +4,61 @@ using Eventuous.Subscriptions.Filters; using Eventuous.Tests.EventStore.Subscriptions.Fixtures; using Eventuous.Tests.Subscriptions.Base; -using static Xunit.TestContext; namespace Eventuous.Tests.EventStore.Subscriptions; -[Collection("Database")] -public class StreamPersistentPublishAndSubscribeManyTests1(ITestOutputHelper outputHelper) - : PersistentSubscriptionFixture(outputHelper, new(), false) { - [Fact] - [Trait("Category", "Persistent subscription")] - public async Task SubscribeAndProduceMany() { +public class StreamPersistentPublishAndSubscribeManyTests { + [Test] + [Category("Persistent subscription")] + [MethodDataSource(nameof(GetFixtures))] + public async Task SubscribeAndProduceMany( + PersistentSubscriptionFixture fixture, + CancellationToken cancellationToken + ) { const int count = 1000; - var testEvents = Auto.CreateMany(count).ToList(); + var testEvents = fixture.Auto.CreateMany(count).ToList(); - await Start(); - await Producer.Produce(Stream, testEvents, new(), cancellationToken: Current.CancellationToken); - await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); - await Stop(); + await fixture.InitializeAsync(); + await fixture.Start(); + await fixture.Producer.Produce(fixture.Stream, testEvents, new(), cancellationToken: cancellationToken); + await fixture.Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(cancellationToken); + await fixture.Stop(); + await fixture.DisposeAsync(); } - protected override StreamPersistentSubscription CreateSubscription(string id, ILoggerFactory loggerFactory) - => new( - Fixture.Client, + public static PersistentSubscriptionFixture[] GetFixtures() { + return [ + new(new(), CreateWithRegularClient, false), + new(new(), CreateWithPersistentSubClient, false), + ]; + } + + static StreamPersistentSubscription CreateWithRegularClient(string id, string connectionString, StreamName stream, TestEventHandler handler, ILoggerFactory loggerFactory) { + var settings = EventStoreClientSettings.Create(connectionString); + + return new( + new EventStoreClient(settings), new() { - StreamName = Stream, + StreamName = stream, SubscriptionId = id }, - new ConsumePipe().AddDefaultConsumer(Handler), + new ConsumePipe().AddDefaultConsumer(handler), loggerFactory ); -} - -[Collection("Database")] -public class StreamPersistentPublishAndSubscribeManyTests2(ITestOutputHelper outputHelper) - : PersistentSubscriptionFixture(outputHelper, new(), false) { - [Fact] - [Trait("Category", "Persistent subscription")] - public async Task SubscribeAndProduceMany() { - const int count = 1000; - - var testEvents = Auto.CreateMany(count).ToList(); - - await Start(); - await Producer.Produce(Stream, testEvents, new(), cancellationToken: Current.CancellationToken); - await Handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); - await Stop(); } - protected override StreamPersistentSubscription CreateSubscription(string id, ILoggerFactory loggerFactory) { - var connectionString = Fixture.Container.GetConnectionString(); - var settings = EventStoreClientSettings.Create(connectionString); - var client = new EventStorePersistentSubscriptionsClient(settings); + static StreamPersistentSubscription CreateWithPersistentSubClient(string id, string connectionString, StreamName stream, TestEventHandler handler, ILoggerFactory loggerFactory) { + var settings = EventStoreClientSettings.Create(connectionString); + var client = new EventStorePersistentSubscriptionsClient(settings); + return new( client, new() { - StreamName = Stream, + StreamName = stream, SubscriptionId = id }, - new ConsumePipe().AddDefaultConsumer(Handler), + new ConsumePipe().AddDefaultConsumer(handler), loggerFactory ); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs index e1e71ab7d..ebfa5e231 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs @@ -6,28 +6,27 @@ using Eventuous.Subscriptions.Filters; using Eventuous.Sut.App; using Eventuous.Sut.Domain; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using Eventuous.Tests.Subscriptions.Base; -using static Xunit.TestContext; using StreamSubscription = Eventuous.EventStore.Subscriptions.StreamSubscription; namespace Eventuous.Tests.EventStore.Subscriptions; -public sealed class StreamSubscriptionDeletedEventsTests : IAsyncLifetime { +public sealed class StreamSubscriptionDeletedEventsTests { readonly StoreFixture _fixture; readonly ILoggerFactory _loggerFactory; readonly LoggingEventListener _listener; - public StreamSubscriptionDeletedEventsTests(ITestOutputHelper output) { + public StreamSubscriptionDeletedEventsTests() { _fixture = new(); - _loggerFactory = LoggerFactory.Create(cfg => cfg.AddXUnit(output).AddConsole().SetMinimumLevel(LogLevel.Debug)); + _loggerFactory = LoggerFactory.Create(cfg => cfg.AddTUnit().AddConsole().SetMinimumLevel(LogLevel.Debug)); _listener = new(_loggerFactory); _fixture.TypeMapper.RegisterKnownEventTypes(typeof(BookingEvents.BookingImported).Assembly); } - [Fact] - [Trait("Category", "Special cases")] - public async Task StreamSubscriptionGetsDeletedEvents() { + [Test] + [Category("Special cases")] + public async Task StreamSubscriptionGetsDeletedEvents(CancellationToken cancellationToken) { var service = new BookingService(_fixture.EventStore); var categoryStream = new StreamName("$ce-Booking"); ulong? startPosition = null; @@ -77,7 +76,7 @@ await Task.WhenAll( LogCollection("Deleted", delete); LogCollection("Expected", commands.Except(delete)); - await subscription.SubscribeWithLog(log, Current.CancellationToken); + await subscription.SubscribeWithLog(log, cancellationToken); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(200)); @@ -85,7 +84,7 @@ await Task.WhenAll( await Task.Delay(100, cts.Token); } - await subscription.UnsubscribeWithLog(log, Current.CancellationToken); + await subscription.UnsubscribeWithLog(log, cancellationToken); var actual = handler.Processed.Select(x => x.Stream.GetId()).ToList(); log.LogInformation("Actual:\n {Join}", string.Join("\n", actual)); @@ -112,6 +111,7 @@ public override ValueTask HandleEvent(IMessageConsumeContex } } + [After(Test)] public async ValueTask DisposeAsync() { await _fixture.DisposeAsync(); await CastAndDispose(_loggerFactory); @@ -127,5 +127,6 @@ static async ValueTask CastAndDispose(IDisposable resource) { } } - public ValueTask InitializeAsync() => _fixture.InitializeAsync(); + [Before(Test)] + public Task InitializeAsync() => _fixture.InitializeAsync(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionWithLinksTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionWithLinksTests.cs index a655d9d5c..73cad106b 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionWithLinksTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionWithLinksTests.cs @@ -5,39 +5,37 @@ using Eventuous.Subscriptions.Context; using Eventuous.Tests.Subscriptions.Base; using Microsoft.Extensions.DependencyInjection; -using static Xunit.TestContext; using StreamSubscription = Eventuous.EventStore.Subscriptions.StreamSubscription; +// ReSharper disable MethodHasAsyncOverload namespace Eventuous.Tests.EventStore.Subscriptions; -[Collection("Database")] public class StreamSubscriptionWithLinksTests : StoreFixture { const string SubId = "Test"; readonly List _checkpoints = []; readonly string _prefix = $"{Faker.Commerce.ProductAdjective()}{Faker.Commerce.Product()}"; - public StreamSubscriptionWithLinksTests(ITestOutputHelper output) { - Output = output; + public StreamSubscriptionWithLinksTests() { AutoStart = false; TypeMapper.AddType(TestEvent.TypeName); } - [Fact] - [Trait("Category", "Special cases")] + [Test] + [Category("Special cases")] public async Task ShouldHandleAllEventsFromStart() { await Start(); await Execute(1000, null); } - [Fact] - [Trait("Category", "Special cases")] - public async Task ShouldHandleHalfOfTheEvents() { + [Test] + [Category("Special cases")] + public async Task ShouldHandleHalfOfTheEvents(CancellationToken cancellationToken) { const int count = 1000; const int expectedCount = count / 2; var checkpointStore = Provider.GetRequiredService(); - await checkpointStore.StoreCheckpoint(new(SubId, expectedCount - 1), true, Current.CancellationToken); + await checkpointStore.StoreCheckpoint(new(SubId, expectedCount - 1), true, cancellationToken); await Start(); await Execute(count, expectedCount); @@ -54,7 +52,7 @@ async Task> Seed(IServiceProvider provider, int count) { TypeMap.Instance.AddType(TestEvent.TypeName); var producer = provider.GetRequiredService(); - Output?.WriteLine("Producing events..."); + TestContext.Current?.OutputWriter.WriteLine("Producing events..."); var events = new List(); @@ -65,14 +63,14 @@ async Task> Seed(IServiceProvider provider, int count) { events.Add(evt); } - Output?.WriteLine("Producing complete"); + TestContext.Current?.OutputWriter.WriteLine("Producing complete"); return events; } void ValidateProcessed(IServiceProvider provider, IEnumerable events) { var handler = provider.GetRequiredKeyedService(SubId); - Output?.WriteLine($"Processed {handler.Handled.Count} events"); + TestContext.Current?.OutputWriter.WriteLine($"Processed {handler.Handled.Count} events"); foreach (var evt in events) { handler.Handled.Should().Contain(evt); @@ -101,7 +99,7 @@ async Task WaitForCheckpoint(int count, TimeSpan deadline) { await Task.Delay(500, source.Token); } } catch (OperationCanceledException) { - Output?.WriteLine("Deadline exceeded"); + TestContext.Current?.OutputWriter.WriteLine("Deadline exceeded"); } } @@ -143,7 +141,7 @@ protected override void SetupServices(IServiceCollection services) { return; void CheckpointStoreOnCheckpointStored(object? sender, Checkpoint e) { - Output?.WriteLine($"Stored checkpoint {e.Id}: {e.Position}"); + TestContext.Current?.OutputWriter.WriteLine($"Stored checkpoint {e.Id}: {e.Position}"); _checkpoints.Add(e); } } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscribeTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscribeTests.cs index 55003f531..5522d779c 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscribeTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscribeTests.cs @@ -7,54 +7,55 @@ namespace Eventuous.Tests.EventStore.Subscriptions; -[Collection("Database")] -public class SubscribeToAll(ITestOutputHelper outputHelper) +public class SubscribeToAll() : SubscribeToAllBase( - outputHelper, - new CatchUpSubscriptionFixture(_ => { }, outputHelper, new("$all"), false) + new CatchUpSubscriptionFixture(_ => { }, new("$all"), false) ) { - [Fact] - public async Task Esdb_ShouldConsumeProducedEvents() { - await ShouldConsumeProducedEvents(); + [Test] + public async Task Esdb_ShouldConsumeProducedEvents(CancellationToken cancellationToken) { + await ShouldConsumeProducedEvents(cancellationToken); } - [Fact] - public async Task Esdb_ShouldConsumeProducedEventsWhenRestarting() { - await ShouldConsumeProducedEventsWhenRestarting(); + [Test] + public async Task Esdb_ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { + await ShouldConsumeProducedEventsWhenRestarting(cancellationToken); } - [Fact] - public async Task Esdb_ShouldUseExistingCheckpoint() { - await ShouldUseExistingCheckpoint(); + [Test] + public async Task Esdb_ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { + await ShouldUseExistingCheckpoint(cancellationToken); } } -[Collection("Database")] -public class SubscribeToStream(ITestOutputHelper outputHelper, StreamNameFixture streamNameFixture) +[ClassDataSource(Shared = SharedType.None)] +public class SubscribeToStream(StreamNameFixture streamNameFixture) : SubscribeToStreamBase( - outputHelper, + streamNameFixture.StreamName, + new CatchUpSubscriptionFixture( + opt => ConfigureOptions(opt, streamNameFixture), streamNameFixture.StreamName, - new CatchUpSubscriptionFixture( - opt => ConfigureOptions(opt, streamNameFixture), - outputHelper, - streamNameFixture.StreamName, - false - ) - ), - IClassFixture { - [Fact] - public async Task Esdb_ShouldConsumeProducedEvents() { - await ShouldConsumeProducedEvents(); + false + ) + ) { + [Before(Test)] + public async Task Setup() => await InitializeAsync(); + + [After(Test)] + public async Task TearDown() => await DisposeAsync(); + + [Test] + public async Task Esdb_ShouldConsumeProducedEvents(CancellationToken cancellationToken) { + await ShouldConsumeProducedEvents(cancellationToken); } - [Fact] - public async Task Esdb_ShouldConsumeProducedEventsWhenRestarting() { - await ShouldConsumeProducedEventsWhenRestarting(); + [Test] + public async Task Esdb_ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { + await ShouldConsumeProducedEventsWhenRestarting(cancellationToken); } - [Fact] - public async Task Esdb_ShouldUseExistingCheckpoint() { - await ShouldUseExistingCheckpoint(); + [Test] + public async Task Esdb_ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { + await ShouldUseExistingCheckpoint(cancellationToken); } static void ConfigureOptions(StreamSubscriptionOptions options, StreamNameFixture streamNameFixture) { diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscriptionIgnoredMessagesTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscriptionIgnoredMessagesTests.cs index dca93ce51..25ca7ad9d 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscriptionIgnoredMessagesTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/SubscriptionIgnoredMessagesTests.cs @@ -1,16 +1,12 @@ -// Copyright (C) Ubiquitous AS.All rights reserved -// Licensed under the Apache License, Version 2.0. - using Eventuous.EventStore.Producers; using Eventuous.EventStore.Subscriptions; using Eventuous.Producers; using Eventuous.Tests.Subscriptions.Base; using Microsoft.Extensions.DependencyInjection; -using static Xunit.TestContext; +// ReSharper disable MethodHasAsyncOverload namespace Eventuous.Tests.EventStore.Subscriptions; -[Collection("Database")] public class SubscriptionIgnoredMessagesTests : StoreFixture { readonly string _subscriptionId = $"test-{Guid.NewGuid():N}"; readonly StreamName _stream = new($"test-{Guid.NewGuid():N}"); @@ -18,32 +14,31 @@ public class SubscriptionIgnoredMessagesTests : StoreFixture { ICheckpointStore _checkpointStore = null!; TestEventHandler _handler = null!; - public SubscriptionIgnoredMessagesTests(ITestOutputHelper output) { - Output = output; + public SubscriptionIgnoredMessagesTests() { AutoStart = false; } - [Fact] - [Trait("Category", "Special cases")] - public async Task SubscribeAndProduceManyWithIgnored() { + [Test] + [Category("Special cases")] + public async Task SubscribeAndProduceManyWithIgnored(CancellationToken cancellationToken) { const int count = 10; var testEvents = Generate().ToList(); TypeMapper.AddType(TestEvent.TypeName); TypeMapper.AddType("ignored"); - Output?.WriteLine($"Producing to {_stream}"); - await _producer.Produce(_stream, testEvents, new Metadata(), cancellationToken: Current.CancellationToken); - Output?.WriteLine("Produce complete"); + TestContext.Current?.OutputWriter.WriteLine($"Producing to {_stream}"); + await _producer.Produce(_stream, testEvents, new Metadata(), cancellationToken: cancellationToken); + TestContext.Current?.OutputWriter.WriteLine("Produce complete"); TypeMapper.RemoveType(); var expected = testEvents.Where(x => x.GetType() == typeof(TestEvent)).ToList(); await Start(); - await _handler.AssertCollection(5.Seconds(), expected).Validate(Current.CancellationToken); + await _handler.AssertCollection(5.Seconds(), expected).Validate(cancellationToken); await DisposeAsync(); - var last = await _checkpointStore.GetLastCheckpoint(_subscriptionId, Current.CancellationToken); + var last = await _checkpointStore.GetLastCheckpoint(_subscriptionId, cancellationToken); last.Position.Should().Be((ulong)(testEvents.Count - 1)); return; diff --git a/src/Extensions/test/Eventuous.Tests.DependencyInjection/AggregateFactoryRegistrationTests.cs b/src/Extensions/test/Eventuous.Tests.DependencyInjection/AggregateFactoryRegistrationTests.cs index 0df2d38e4..89bf1a5eb 100644 --- a/src/Extensions/test/Eventuous.Tests.DependencyInjection/AggregateFactoryRegistrationTests.cs +++ b/src/Extensions/test/Eventuous.Tests.DependencyInjection/AggregateFactoryRegistrationTests.cs @@ -14,7 +14,7 @@ public AggregateFactoryRegistrationTests() { _registry = app.Services.GetRequiredService(); } - [Fact] + [Test] public void ShouldCreateNewAggregateWithExplicitFunction() { var instance = _registry.CreateInstance(); instance.Should().BeOfType(); @@ -22,7 +22,7 @@ public void ShouldCreateNewAggregateWithExplicitFunction() { instance.State.Should().NotBeNull(); } - [Fact] + [Test] public void ShouldCreateNewAggregateByResolve() { var instance = _registry.CreateInstance(); instance.Should().BeOfType(); @@ -30,7 +30,7 @@ public void ShouldCreateNewAggregateByResolve() { instance.State.Should().NotBeNull(); } - [Fact] + [Test] public void ShouldCreateTwoSeparateInstances() { var instance1 = _registry.CreateInstance(); var instance2 = _registry.CreateInstance(); diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj index f75bd19db..ee86e5929 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj @@ -6,6 +6,7 @@ + diff --git a/src/Gateway/test/Eventuous.Tests.Gateway/RegistrationTests.cs b/src/Gateway/test/Eventuous.Tests.Gateway/RegistrationTests.cs index 2987b5f93..0fdd69cbf 100644 --- a/src/Gateway/test/Eventuous.Tests.Gateway/RegistrationTests.cs +++ b/src/Gateway/test/Eventuous.Tests.Gateway/RegistrationTests.cs @@ -13,11 +13,12 @@ namespace Eventuous.Tests.Gateway; -public class RegistrationTests : IDisposable { - readonly TestServer _host = new(BuildHost()); - - [Fact] - public void Test() { } +public class RegistrationTests { + [Test] + public void Test() { + TestServer host = new(BuildHost()); + host.Dispose(); + } static IWebHostBuilder BuildHost() => new WebHostBuilder().UseStartup(); @@ -50,11 +51,11 @@ class TestProducer : BaseProducer { public List ProducedMessages { get; } = []; protected override Task ProduceMessages( - StreamName stream, - IEnumerable messages, - TestProduceOptions? options, - CancellationToken cancellationToken = default - ) { + StreamName stream, + IEnumerable messages, + TestProduceOptions? options, + CancellationToken cancellationToken = default + ) { ProducedMessages.AddRange(messages); return Task.CompletedTask; @@ -62,6 +63,4 @@ protected override Task ProduceMessages( } record TestProduceOptions; - - public void Dispose() => _host.Dispose(); } diff --git a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs index ebcc15650..fd2b15a39 100644 --- a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs +++ b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs @@ -1,10 +1,11 @@ using DotNet.Testcontainers.Builders; using DotNet.Testcontainers.Containers; using Google.Api.Gax; +using TUnit.Core.Interfaces; namespace Eventuous.Tests.GooglePubSub; -public class PubSubFixture : IAsyncLifetime { +public class PubSubFixture : IAsyncInitializer, IAsyncDisposable { public static string PubsubProjectId => "test-id"; public static async Task DeleteSubscription(string subscriptionId) { @@ -23,7 +24,7 @@ public static async Task DeleteTopic(string topicId) { IContainer _container = null!; - public async ValueTask InitializeAsync() { + public async Task InitializeAsync() { const int port = 8085; _container = new ContainerBuilder() diff --git a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs index 4d4a52523..1dda5fc97 100644 --- a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs +++ b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs @@ -2,14 +2,14 @@ using Eventuous.GooglePubSub.Subscriptions; using Eventuous.Producers; using Eventuous.Subscriptions.Filters; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using Eventuous.Tests.Subscriptions.Base; using Google.Api.Gax; -using static Xunit.TestContext; namespace Eventuous.Tests.GooglePubSub; -public class PubSubTests : IAsyncLifetime, IClassFixture { +[ClassDataSource] +public class PubSubTests { static PubSubTests() => TypeMap.Instance.RegisterKnownEventTypes(typeof(TestEvent).Assembly); static readonly Fixture Auto = new(); @@ -22,8 +22,8 @@ public class PubSubTests : IAsyncLifetime, IClassFixture { readonly ILogger _log; // ReSharper disable once UnusedParameter.Local - public PubSubTests(PubSubFixture _, ITestOutputHelper outputHelper) { - var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddXUnit(outputHelper)); + public PubSubTests(PubSubFixture _) { + var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddTUnit()); _log = loggerFactory.CreateLogger(); _pubsubTopic = new($"test-{Guid.NewGuid():N}"); @@ -47,31 +47,33 @@ public PubSubTests(PubSubFixture _, ITestOutputHelper outputHelper) { ); } - [Fact] - public async Task SubscribeAndProduce() { + [Test] + public async Task SubscribeAndProduce(CancellationToken cancellationToken) { var testEvent = Auto.Create(); - await _producer.Produce(_pubsubTopic, testEvent, null, cancellationToken: Current.CancellationToken); + await _producer.Produce(_pubsubTopic, testEvent, null, cancellationToken: cancellationToken); - await _handler.AssertThat().Timebox(10.Seconds()).Any().Match(x => x as TestEvent == testEvent).Validate(Current.CancellationToken); + await _handler.AssertThat().Timebox(10.Seconds()).Any().Match(x => x as TestEvent == testEvent).Validate(cancellationToken); } - [Fact] - public async Task SubscribeAndProduceMany() { + [Test] + public async Task SubscribeAndProduceMany(CancellationToken cancellationToken) { const int count = 10000; var testEvents = Auto.CreateMany(count).ToList(); - await _producer.Produce(_pubsubTopic, testEvents, null, cancellationToken: Current.CancellationToken); - await _handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); + await _producer.Produce(_pubsubTopic, testEvents, null, cancellationToken: cancellationToken); + await _handler.AssertCollection(20.Seconds(), [..testEvents]).Validate(cancellationToken); } - public async ValueTask InitializeAsync() { + [Before(Test)] + public async Task InitializeAsync() { await _producer.StartAsync(); await _subscription.SubscribeWithLog(_log); } - public async ValueTask DisposeAsync() { + [After(Test)] + public async Task DisposeAsync() { await _producer.StopAsync(); await _subscription.UnsubscribeWithLog(_log); diff --git a/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs b/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs index 9a60d97bf..4c2fcabef 100644 --- a/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs +++ b/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs @@ -6,26 +6,26 @@ using Eventuous.Tools; using static System.String; using static Eventuous.DeserializationResult; -using static Xunit.TestContext; + +// ReSharper disable MethodHasAsyncOverload namespace Eventuous.Tests.Kafka; -public class BasicProducerTests : IClassFixture { - readonly KafkaFixture _fixture; - readonly ITestOutputHelper _output; +[ClassDataSource] +public class BasicProducerTests { + readonly KafkaFixture _fixture; - public BasicProducerTests(KafkaFixture fixture, ITestOutputHelper output) { + public BasicProducerTests(KafkaFixture fixture) { _fixture = fixture; - _output = output; TypeMap.Instance.AddType("testEvent"); } static readonly Fixture Auto = new(); - [Fact] - public async Task ShouldProduceAndWait() { + [Test] + public async Task ShouldProduceAndWait(CancellationToken cancellationToken) { var topicName = Auto.Create(); - _output.WriteLine($"Topic: {topicName}"); + TestContext.Current?.OutputWriter.WriteLine($"Topic: {topicName}"); var events = Auto.CreateMany().ToArray(); @@ -33,15 +33,15 @@ public async Task ShouldProduceAndWait() { var consumed = new List(); await ExecuteConsume().NoThrow(); - _output.WriteLine($"Consumed {consumed.Count} events"); + TestContext.Current?.OutputWriter.WriteLine($"Consumed {consumed.Count} events"); consumed.Should().BeEquivalentTo(events); return; async Task Produce() { await using var producer = new KafkaBasicProducer(new(new() { BootstrapServers = _fixture.BootstrapServers })); - await producer.StartAsync(Current.CancellationToken); - await producer.Produce(new(topicName), events, new(), new("test"), cancellationToken: Current.CancellationToken); + await producer.StartAsync(cancellationToken); + await producer.Produce(new(topicName), events, new(), new("test"), cancellationToken: cancellationToken); } async Task ExecuteConsume() { @@ -56,11 +56,11 @@ async Task ExecuteConsume() { } } - async Task Consume(IConsumer c, CancellationToken cancellationToken) { - var msg = c.Consume(cancellationToken); + async Task Consume(IConsumer c, CancellationToken ct) { + var msg = c.Consume(ct); if (msg == null) { - await Task.Delay(100, cancellationToken); + await Task.Delay(100, ct); return; } @@ -73,7 +73,7 @@ async Task Consume(IConsumer c, CancellationToken cancellationTo var result = DefaultEventSerializer.Instance.DeserializeEvent(msg.Message.Value, messageType!, contentType!) as SuccessfullyDeserialized; var evt = (result!.Payload as TestEvent)!; - _output.WriteLine($"Consumed {evt}"); + TestContext.Current?.OutputWriter.WriteLine($"Consumed {evt}"); consumed.Add(evt); } } @@ -91,11 +91,11 @@ IConsumer GetConsumer(string groupId) { }; return new ConsumerBuilder(config) - .SetErrorHandler((_, e) => _output.WriteLine($"Error: {e.Reason}")) - .SetStatisticsHandler((_, json) => _output.WriteLine($"Statistics: {json}")) + .SetErrorHandler((_, e) => TestContext.Current?.OutputWriter.WriteLine($"Error: {e.Reason}")) + .SetStatisticsHandler((_, json) => TestContext.Current?.OutputWriter.WriteLine($"Statistics: {json}")) .SetPartitionsAssignedHandler( (c, partitions) => { - _output.WriteLine( + TestContext.Current?.OutputWriter.WriteLine( $"Partitions incrementally assigned: [{Join(',', partitions.Select(p => p.Partition.Value))}], all: [{Join(',', c.Assignment.Concat(partitions).Select(p => p.Partition.Value))}]" ); } @@ -104,12 +104,12 @@ IConsumer GetConsumer(string groupId) { (c, partitions) => { var remaining = c.Assignment.Where(atp => partitions.All(rtp => rtp.TopicPartition != atp)); - _output.WriteLine( + TestContext.Current?.OutputWriter.WriteLine( $"Partitions incrementally revoked: [{Join(',', partitions.Select(p => p.Partition.Value))}], remaining: [{Join(',', remaining.Select(p => p.Partition.Value))}]" ); } ) - .SetPartitionsLostHandler((_, partitions) => _output.WriteLine($"Partitions were lost: [{Join(", ", partitions)}]")) + .SetPartitionsLostHandler((_, partitions) => TestContext.Current?.OutputWriter.WriteLine($"Partitions were lost: [{Join(", ", partitions)}]")) .Build(); } } diff --git a/src/Kafka/test/Eventuous.Tests.Kafka/KafkaFixture.cs b/src/Kafka/test/Eventuous.Tests.Kafka/KafkaFixture.cs index 2b5623b03..a430c22f8 100644 --- a/src/Kafka/test/Eventuous.Tests.Kafka/KafkaFixture.cs +++ b/src/Kafka/test/Eventuous.Tests.Kafka/KafkaFixture.cs @@ -1,14 +1,12 @@ -// Copyright (C) Ubiquitous AS.All rights reserved -// Licensed under the Apache License, Version 2.0. - using Testcontainers.Kafka; +using TUnit.Core.Interfaces; namespace Eventuous.Tests.Kafka; -public class KafkaFixture : IAsyncLifetime { +public class KafkaFixture : IAsyncInitializer, IAsyncDisposable { KafkaContainer _kafkaContainer = null!; - public async ValueTask InitializeAsync() { + public async Task InitializeAsync() { _kafkaContainer = new KafkaBuilder() .WithImage("confluentinc/cp-kafka:7.2.6") .Build(); diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj index 88e36e840..406194be9 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj @@ -1,18 +1,20 @@ - - true - true - true - Exe - - - - - - - - - - - + + true + true + true + Exe + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs index 65b5c28ec..8f3d66416 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs @@ -5,10 +5,11 @@ using MongoDB.Driver; using Testcontainers.EventStoreDb; using Testcontainers.MongoDb; +using TUnit.Core.Interfaces; namespace Eventuous.Tests.Projections.MongoDB.Fixtures; -public sealed class IntegrationFixture : IAsyncLifetime { +public sealed class IntegrationFixture : IAsyncInitializer, IAsyncDisposable { public IEventStore EventStore { get; set; } = null!; public EventStoreClient Client { get; private set; } = null!; public IMongoDatabase Mongo { get; private set; } = null!; @@ -32,7 +33,7 @@ static IntegrationFixture() { EventStoreDbContainer _esdbContainer = null!; MongoDbContainer _mongoContainer = null!; - public async ValueTask InitializeAsync() { + public async Task InitializeAsync() { _esdbContainer = new EventStoreDbBuilder().Build(); await _esdbContainer.StartAsync(); var settings = EventStoreClientSettings.Create(_esdbContainer.GetConnectionString()); diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs index b376c06b6..ef8222241 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs @@ -7,9 +7,9 @@ namespace Eventuous.Tests.Projections.MongoDB; -public class ProjectWithBuilder(IntegrationFixture fixture, ITestOutputHelper output) - : ProjectionTestBase(nameof(ProjectWithBuilder), fixture, output) { - [Fact] +[ClassDataSource] +public class ProjectWithBuilder(IntegrationFixture fixture) : ProjectionTestBase(nameof(ProjectWithBuilder), fixture) { + [Test] public async Task ShouldProjectImported() { var evt = DomainFixture.CreateImportBooking(); var id = new BookingId(CreateId()); diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBulkBuilder.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBulkBuilder.cs index 5010d41fb..2b60a752f 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBulkBuilder.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBulkBuilder.cs @@ -7,9 +7,9 @@ namespace Eventuous.Tests.Projections.MongoDB; -public class ProjectWithBulkBuilder(IntegrationFixture fixture, ITestOutputHelper output) - : ProjectionTestBase(nameof(ProjectWithBulkBuilder), fixture, output) { - [Fact] +[ClassDataSource] +public class ProjectWithBulkBuilder(IntegrationFixture fixture) : ProjectionTestBase(nameof(ProjectWithBulkBuilder), fixture) { + [Test] public async Task ShouldProjectImported() { var evt = DomainFixture.CreateImportBooking(); var id = new BookingId(CreateId()); @@ -60,7 +60,7 @@ public SutBulkProjection(IMongoDatabase database) .AddOperation( x => x.InsertOne .Document( - ctx => new BookingDocument(ctx.Stream.GetId()) { + ctx => new(ctx.Stream.GetId()) { RoomId = ctx.Message.RoomId, CheckInDate = ctx.Message.CheckIn, CheckOutDate = ctx.Message.CheckOut, @@ -77,7 +77,7 @@ public SutBulkProjection(IMongoDatabase database) .AddOperation( x => x.InsertOne .Document( - ctx => new BookingDocument(ctx.Stream.GetId()) { + ctx => new(ctx.Stream.GetId()) { BookingPrice = ctx.Message.Price, Outstanding = ctx.Message.Price } diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs index d65df595b..fdedff881 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs @@ -4,14 +4,14 @@ using Eventuous.Tests.Projections.MongoDB.Fixtures; using MongoDB.Driver; using static Eventuous.Sut.Domain.BookingEvents; -using static Xunit.TestContext; namespace Eventuous.Tests.Projections.MongoDB; -public sealed class ProjectingWithTypedHandlers(IntegrationFixture fixture, ITestOutputHelper output) - : ProjectionTestBase(nameof(ProjectingWithTypedHandlers), fixture, output) { - [Fact] - public async Task ShouldProjectImported() { +[ClassDataSource] +public sealed class ProjectingWithTypedHandlers(IntegrationFixture fixture) + : ProjectionTestBase(nameof(ProjectingWithTypedHandlers), fixture) { + [Test] + public async Task ShouldProjectImported(CancellationToken cancellationToken) { var evt = DomainFixture.CreateImportBooking(); var id = new BookingId(CreateId()); var stream = StreamNameFactory.For(id); @@ -30,7 +30,7 @@ public async Task ShouldProjectImported() { StreamPosition = (ulong)append.NextExpectedVersion }; - var actual = await Fixture.Mongo.LoadDocument(id.ToString(), cancellationToken: Current.CancellationToken); + var actual = await Fixture.Mongo.LoadDocument(id.ToString(), cancellationToken: cancellationToken); actual.Should().Be(expected); } diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs index fee081702..8d4cab5c8 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs @@ -2,29 +2,45 @@ using Eventuous.Projections.MongoDB; using Eventuous.Subscriptions; using Eventuous.Subscriptions.Checkpoints; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using Eventuous.Tests.Projections.MongoDB.Fixtures; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Eventuous.Tests.Projections.MongoDB; -public class ProjectionTestBase : IClassFixture, IAsyncLifetime where TProjection : class, IEventHandler { - protected readonly IntegrationFixture Fixture; - protected readonly IHost Host; +public abstract class ProjectionTestBase { + readonly string _id; + protected IHost Host = null!; + readonly IHostBuilder _builder; - protected ProjectionTestBase(string id, IntegrationFixture fixture, ITestOutputHelper output) { - Fixture = fixture; + protected ProjectionTestBase(string id) { + _id = id; - var builder = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder() - .ConfigureLogging(cfg => cfg.AddXUnit(output).SetMinimumLevel(LogLevel.Trace)) - .ConfigureServices(collection => ConfigureServices(collection, id)); + _builder = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder() + .ConfigureLogging(cfg => cfg.AddTUnit().SetMinimumLevel(LogLevel.Trace)); + } + + protected abstract void ConfigureServices(IServiceCollection services, string id); - Host = builder.Build(); + [Before(Test)] + public async Task InitializeAsync() { + _builder.ConfigureServices(collection => ConfigureServices(collection, _id)); + + Host = _builder.Build(); Host.Services.AddEventuousLogs(); + await Host.StartAsync(); } - void ConfigureServices(IServiceCollection services, string id) + [After(Test)] + public async Task DisposeAsync() => await Host.StopAsync(); +} + +public abstract class ProjectionTestBase(string id, IntegrationFixture fixture) : ProjectionTestBase(id) + where TProjection : class, IEventHandler { + protected readonly IntegrationFixture Fixture = fixture; + + protected override void ConfigureServices(IServiceCollection services, string id) => services .AddSingleton(Fixture.Client) .AddSingleton(Fixture.Mongo) @@ -38,7 +54,7 @@ void ConfigureServices(IServiceCollection services, string id) protected async Task WaitForPosition(ulong position) { var checkpointStore = Host.Services.GetRequiredService(); - var count = 100; + var count = 100; while (count-- > 0) { var checkpoint = await checkpointStore.GetLastCheckpoint(nameof(ProjectWithBuilder), default); @@ -48,8 +64,4 @@ protected async Task WaitForPosition(ulong position) { await Task.Delay(100); } } - - public async ValueTask InitializeAsync() => await Host.StartAsync(); - - public async ValueTask DisposeAsync() => await Host.StopAsync(); } diff --git a/src/Postgres/src/Eventuous.Postgresql/Schema.cs b/src/Postgres/src/Eventuous.Postgresql/Schema.cs index c71611435..72df29800 100644 --- a/src/Postgres/src/Eventuous.Postgresql/Schema.cs +++ b/src/Postgres/src/Eventuous.Postgresql/Schema.cs @@ -13,7 +13,7 @@ namespace Eventuous.Postgresql; public class Schema(string schema = Schema.DefaultSchema) { public const string DefaultSchema = "eventuous"; - public static string GetStreamMessageTypeName(string schema = Schema.DefaultSchema) => $"{schema}.stream_message"; + public static string GetStreamMessageTypeName(string schema = DefaultSchema) => $"{schema}.stream_message"; public string StreamMessage => GetStreamMessageTypeName(schema); public string AppendEvents => $"select * from {schema}.append_events(@_stream_name, @_expected_version, @_created, @_messages)"; diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj b/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj index 1647eda67..84c31fafe 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsTests.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsTests.cs index c7f5179ba..cde246904 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsTests.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsTests.cs @@ -7,6 +7,16 @@ namespace Eventuous.Tests.Postgres.Metrics; -[Collection("Database")] -public class MetricsTests(ITestOutputHelper outputHelper) - : MetricsTestsBase(outputHelper); +public class MetricsTests : MetricsTestsBase { + [Test] + public async Task ShouldMeasureSubscriptionGapCount() { + await ShouldMeasureSubscriptionGapCountBase(); + } + + [Before(Test)] + public async Task Setup() => await InitializeAsync(); + + [After(Test)] + public async Task TearDown() => await DisposeAsync(); +} + diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Projections/ProjectorTests.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Projections/ProjectorTests.cs index 35d29b510..b01c61df1 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Projections/ProjectorTests.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Projections/ProjectorTests.cs @@ -1,6 +1,3 @@ -// Copyright (C) Ubiquitous AS. All rights reserved -// Licensed under the Apache License, Version 2.0. - using Eventuous.Postgresql.Projections; using Eventuous.Postgresql.Subscriptions; using Eventuous.Sut.App; @@ -8,13 +5,12 @@ using Eventuous.Tests.Persistence.Base.Fixtures; using Eventuous.Tests.Postgres.Subscriptions; using Npgsql; -using static Xunit.TestContext; +using Assert = TUnit.Assertions.Assert; namespace Eventuous.Tests.Postgres.Projections; -[Collection("Database")] -public class ProjectorTests(ITestOutputHelper outputHelper) : IAsyncLifetime { - readonly SubscriptionFixture _fixture = new(_ => { }, outputHelper); +public class ProjectorTests() { + readonly SubscriptionFixture _fixture = new(_ => { }); const string Schema = """ create table if not exists __schema__.bookings ( @@ -24,24 +20,24 @@ price numeric(10,2) ); """; - [Fact] - public async Task ProjectImportedBookingsToTable() { + [Test] + public async Task ProjectImportedBookingsToTable(CancellationToken cancellationToken) { await CreateSchema(); var commands = await GenerateAndProduceEvents(100); - await Task.Delay(1000, Current.CancellationToken); + await Task.Delay(1000, cancellationToken); - await using var connection = await _fixture.DataSource.OpenConnectionAsync(Current.CancellationToken); + await using var connection = await _fixture.DataSource.OpenConnectionAsync(cancellationToken); var select = $"select * from {_fixture.SchemaName}.bookings where booking_id = @bookingId"; foreach (var command in commands) { await using var cmd = new NpgsqlCommand(select, connection); cmd.Parameters.AddWithValue("@bookingId", command.BookingId); - await using var reader = await cmd.ExecuteReaderAsync(Current.CancellationToken); - await reader.ReadAsync(Current.CancellationToken); - reader["checkin_date"].Should().Be(command.CheckIn.ToDateTimeUnspecified()); - reader["price"].Should().Be(command.Price); + await using var reader = await cmd.ExecuteReaderAsync(cancellationToken); + await reader.ReadAsync(cancellationToken); + await Assert.That(reader["checkin_date"]).IsEqualTo(command.CheckIn.ToDateTimeUnspecified()); + await Assert.That(reader["price"]).IsEqualTo((decimal)command.Price); } } @@ -72,8 +68,10 @@ async Task CreateSchema() { static BookingEvents.BookingImported ToEvent(Commands.ImportBooking cmd) => new(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut); + [Before(Test)] public async ValueTask InitializeAsync() => await _fixture.InitializeAsync(); + [After(Test)] public async ValueTask DisposeAsync() => await _fixture.DisposeAsync(); } diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Registrations/RegistrationTests.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Registrations/RegistrationTests.cs index d82754bbf..7955298cc 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Registrations/RegistrationTests.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Registrations/RegistrationTests.cs @@ -4,14 +4,15 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Npgsql; +using Assert = TUnit.Assertions.Assert; namespace Eventuous.Tests.Postgres.Registrations; public class RegistrationTests { const string ConnectionString = "Host=localhost;Username=postgres;Password=secret;Database=eventuous;"; - [Fact] - public void Should_resolve_store_with_manual_registration() { + [Test] + public async Task Should_resolve_store_with_manual_registration() { var ds = new NpgsqlDataSourceBuilder(ConnectionString).Build(); var builder = new WebHostBuilder(); builder.Configure(_ => { }); @@ -25,11 +26,11 @@ public void Should_resolve_store_with_manual_registration() { ); var app = builder.Build(); var aggregateStore = app.Services.GetRequiredService(); - aggregateStore.Should().NotBeNull(); + await Assert.That(aggregateStore).IsNotNull(); } - [Fact] - public void Should_resolve_store_with_extensions() { + [Test] + public async Task Should_resolve_store_with_extensions() { var builder = new WebHostBuilder(); var config = new Dictionary { ["postgres:schema"] = "test", @@ -47,7 +48,7 @@ public void Should_resolve_store_with_extensions() { var app = builder.Build(); var reader = app.Services.GetService(); var npgSqlReader = ((reader as TracedEventStore)!).Inner as PostgresStore; - npgSqlReader.Should().NotBeNull(); - npgSqlReader!.Schema.StreamMessage.Should().Be("test.stream_message"); + await Assert.That(npgSqlReader).IsNotNull(); + await Assert.That(npgSqlReader!.Schema.StreamMessage).IsEqualTo("test.stream_message"); } } diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Store/StoreTests.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Store/StoreTests.cs index d11bfc069..a8cf87223 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Store/StoreTests.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Store/StoreTests.cs @@ -4,11 +4,14 @@ namespace Eventuous.Tests.Postgres.Store; -[Collection("Database")] +[InheritsTests] +[ClassDataSource] public class Append(StoreFixture fixture) : StoreAppendTests(fixture); -[Collection("Database")] +[InheritsTests] +[ClassDataSource] public class Read(StoreFixture fixture) : StoreReadTests(fixture); -[Collection("Database")] +[InheritsTests] +[ClassDataSource] public class OtherMethods(StoreFixture fixture) : StoreOtherOpsTests(fixture); \ No newline at end of file diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Store/TieredStoreTests.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Store/TieredStoreTests.cs index ee241deb5..9da0a6e78 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Store/TieredStoreTests.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Store/TieredStoreTests.cs @@ -1,8 +1,6 @@ using Eventuous.Tests.Persistence.Base.Store; -using JetBrains.Annotations; using Testcontainers.PostgreSql; namespace Eventuous.Tests.Postgres.Store; -[UsedImplicitly] -public class TieredStoreTests(StoreFixture storeFixture) : TieredStoreTestsBase(storeFixture), IClassFixture; +public class TieredStoreTests(StoreFixture storeFixture) : TieredStoreTestsBase(storeFixture); diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Subscriptions/SubscribeTests.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Subscriptions/SubscribeTests.cs index 5ce296299..8051d0d27 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Subscriptions/SubscribeTests.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Subscriptions/SubscribeTests.cs @@ -6,53 +6,48 @@ namespace Eventuous.Tests.Postgres.Subscriptions; -[Collection("Database")] -public class SubscribeToAll(ITestOutputHelper outputHelper) +public class SubscribeToAll() : SubscribeToAllBase( - outputHelper, - new SubscriptionFixture(_ => { }, outputHelper, false) + new SubscriptionFixture(_ => { }, false) ) { - [Fact] - public async Task Postgres_ShouldConsumeProducedEvents() { - await ShouldConsumeProducedEvents(); + [Test] + public async Task Postgres_ShouldConsumeProducedEvents(CancellationToken cancellationToken) { + await ShouldConsumeProducedEvents(cancellationToken); } - [Fact] - public async Task Postgres_ShouldConsumeProducedEventsWhenRestarting() { - await ShouldConsumeProducedEventsWhenRestarting(); + [Test] + public async Task Postgres_ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { + await ShouldConsumeProducedEventsWhenRestarting(cancellationToken); } - [Fact] - public async Task Postgres_ShouldUseExistingCheckpoint() { - await ShouldUseExistingCheckpoint(); + [Test] + public async Task Postgres_ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { + await ShouldUseExistingCheckpoint(cancellationToken); } } -[Collection("Database")] -public class SubscribeToStream(ITestOutputHelper outputHelper, StreamNameFixture streamNameFixture) +[ClassDataSource] +public class SubscribeToStream(StreamNameFixture streamNameFixture) : SubscribeToStreamBase( - outputHelper, - streamNameFixture.StreamName, - new SubscriptionFixture( - opt => ConfigureOptions(opt, streamNameFixture), - outputHelper, - false - ) - ), - IClassFixture { - [Fact] - public async Task Postgres_ShouldConsumeProducedEvents() { - await ShouldConsumeProducedEvents(); + streamNameFixture.StreamName, + new SubscriptionFixture( + opt => ConfigureOptions(opt, streamNameFixture), + false + ) + ) { + [Test] + public async Task Postgres_ShouldConsumeProducedEvents(CancellationToken cancellationToken) { + await ShouldConsumeProducedEvents(cancellationToken); } - [Fact] - public async Task Postgres_ShouldConsumeProducedEventsWhenRestarting() { - await ShouldConsumeProducedEventsWhenRestarting(); + [Test] + public async Task Postgres_ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { + await ShouldConsumeProducedEventsWhenRestarting(cancellationToken); } - [Fact] - public async Task Postgres_ShouldUseExistingCheckpoint() { - await ShouldUseExistingCheckpoint(); + [Test] + public async Task Postgres_ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { + await ShouldUseExistingCheckpoint(cancellationToken); } static void ConfigureOptions(PostgresStreamSubscriptionOptions options, StreamNameFixture streamNameFixture) { diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Subscriptions/SubscriptionFixture.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Subscriptions/SubscriptionFixture.cs index 074acb608..33a3549cd 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Subscriptions/SubscriptionFixture.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Subscriptions/SubscriptionFixture.cs @@ -11,13 +11,11 @@ namespace Eventuous.Tests.Postgres.Subscriptions; public class SubscriptionFixture( Action configureOptions, - ITestOutputHelper outputHelper, bool autoStart = true, Action? configureServices = null, LogLevel logLevel = LogLevel.Debug ) : SubscriptionFixtureBase( - outputHelper, autoStart, logLevel ) @@ -26,8 +24,6 @@ public class SubscriptionFixture PostgresContainer.Create(); protected override PostgresCheckpointStore GetCheckpointStore(IServiceProvider sp) @@ -43,7 +39,7 @@ protected override void SetupServices(IServiceCollection services) { services.AddSingleton(new SchemaInfo(SchemaName)); services.AddEventuousPostgres(Container.GetConnectionString(), SchemaName, true); services.AddEventStore(); - services.AddSingleton(new TestEventHandlerOptions(null, _outputHelper)); + services.AddSingleton(new TestEventHandlerOptions()); services.AddPostgresCheckpointStore(); configureServices?.Invoke(services); } diff --git a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj index fc8c59006..aa2febb01 100644 --- a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj +++ b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj @@ -1,12 +1,14 @@ - - + + - + - + + + diff --git a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/RabbitMqFixture.cs b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/RabbitMqFixture.cs index 26222a5eb..a14c2b8a2 100644 --- a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/RabbitMqFixture.cs +++ b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/RabbitMqFixture.cs @@ -1,16 +1,17 @@ using Testcontainers.RabbitMq; +using TUnit.Core.Interfaces; namespace Eventuous.Tests.RabbitMq; -public class RabbitMqFixture : IAsyncLifetime { +public class RabbitMqFixture : IAsyncInitializer, IAsyncDisposable { RabbitMqContainer _rabbitMq = null!; public ConnectionFactory ConnectionFactory { get; private set; } = null!; - public async ValueTask InitializeAsync() { + public async Task InitializeAsync() { _rabbitMq = new RabbitMqBuilder().Build(); await _rabbitMq.StartAsync(); - ConnectionFactory = new ConnectionFactory { Uri = new Uri(_rabbitMq.GetConnectionString()), DispatchConsumersAsync = true }; + ConnectionFactory = new() { Uri = new(_rabbitMq.GetConnectionString()), DispatchConsumersAsync = true }; } public async ValueTask DisposeAsync() => await _rabbitMq.DisposeAsync(); diff --git a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs index bc61e0615..9f27dcbc3 100644 --- a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs +++ b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs @@ -2,14 +2,14 @@ using Eventuous.RabbitMq.Producers; using Eventuous.RabbitMq.Subscriptions; using Eventuous.Subscriptions.Filters; -using Eventuous.TestHelpers; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit; +using Eventuous.TestHelpers.TUnit.Logging; using Eventuous.Tests.Subscriptions.Base; -using static Xunit.TestContext; namespace Eventuous.Tests.RabbitMq; -public class SubscriptionSpec : IAsyncLifetime, IClassFixture { +[ClassDataSource] +public class SubscriptionSpec { static SubscriptionSpec() => TypeMap.Instance.RegisterKnownEventTypes(typeof(TestEvent).Assembly); static readonly Fixture Auto = new(); @@ -23,38 +23,39 @@ public class SubscriptionSpec : IAsyncLifetime, IClassFixture { readonly ILoggerFactory _loggerFactory; readonly RabbitMqFixture _fixture; - public SubscriptionSpec(RabbitMqFixture fixture, ITestOutputHelper outputHelper) { + public SubscriptionSpec(RabbitMqFixture fixture) { _fixture = fixture; - _es = new(outputHelper); + _es = new(); _exchange = new(Auto.Create()); - _loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddXUnit(outputHelper)); + _loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddTUnit()); _log = _loggerFactory.CreateLogger(); } - [Fact] - public async Task SubscribeAndProduce() { + [Test] + public async Task SubscribeAndProduce(CancellationToken cancellationToken) { var testEvent = Auto.Create(); - await _producer.Produce(_exchange, testEvent, new Metadata(), cancellationToken: Current.CancellationToken); - await _handler.AssertThat().Timebox(10.Seconds()).Any().Match(x => x as TestEvent == testEvent).Validate(Current.CancellationToken); + await _producer.Produce(_exchange, testEvent, new(), cancellationToken: cancellationToken); + await _handler.AssertThat().Timebox(10.Seconds()).Any().Match(x => x as TestEvent == testEvent).Validate(cancellationToken); } - [Fact] - public async Task SubscribeAndProduceMany() { + [Test] + public async Task SubscribeAndProduceMany(CancellationToken cancellationToken) { const int count = 10000; var testEvents = Auto.CreateMany(count).ToList(); - await _producer.Produce(_exchange, testEvents, new Metadata(), cancellationToken: Current.CancellationToken); - await _handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(Current.CancellationToken); + await _producer.Produce(_exchange, testEvents, new(), cancellationToken: cancellationToken); + await _handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(cancellationToken); } + [Before(Test)] public async ValueTask InitializeAsync() { _handler = new(); _producer = new(_fixture.ConnectionFactory); var queue = Auto.Create(); - _subscription = new RabbitMqSubscription( + _subscription = new( _fixture.ConnectionFactory, new RabbitMqSubscriptionOptions { ConcurrencyLimit = 10, @@ -69,6 +70,7 @@ public async ValueTask InitializeAsync() { await _producer.StartAsync(); } + [After(Test)] public async ValueTask DisposeAsync() { await _producer.StopAsync(); await _subscription.UnsubscribeWithLog(_log); diff --git a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs index 0910cd540..a4600945d 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs @@ -4,10 +4,11 @@ using Eventuous.Redis; using Eventuous.TestHelpers; using Testcontainers.Redis; +using TUnit.Core.Interfaces; namespace Eventuous.Tests.Redis.Fixtures; -public sealed class IntegrationFixture : IAsyncLifetime { +public sealed class IntegrationFixture : IAsyncInitializer, IAsyncDisposable { public IEventWriter EventWriter { get; private set; } = null!; public IEventReader EventReader { get; private set; } = null!; public GetRedisDatabase GetDatabase { get; private set; } = null!; @@ -17,11 +18,9 @@ public sealed class IntegrationFixture : IAsyncLifetime { IEventSerializer Serializer { get; } = new DefaultEventSerializer(TestPrimitives.DefaultOptions); - public IntegrationFixture() { - DefaultEventSerializer.SetDefaultSerializer(Serializer); - } + public IntegrationFixture() => DefaultEventSerializer.SetDefaultSerializer(Serializer); - public async ValueTask InitializeAsync() { + public async Task InitializeAsync() { _redisContainer = new RedisBuilder().WithImage("redis:7.0.12-alpine").Build(); await _redisContainer.StartAsync(); diff --git a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/SubscriptionFixture.cs b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/SubscriptionFixture.cs index f8b7aba5d..c16843b57 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/SubscriptionFixture.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/SubscriptionFixture.cs @@ -2,49 +2,49 @@ using Eventuous.Redis.Subscriptions; using Eventuous.Subscriptions; using Eventuous.Subscriptions.Filters; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using Eventuous.Tests.Subscriptions.Base; namespace Eventuous.Tests.Redis.Fixtures; -public abstract class SubscriptionFixture : IAsyncLifetime where T : class, IEventHandler { +public class SubscriptionFixture where T : IEventHandler, new() { static SubscriptionFixture() => TypeMap.Instance.RegisterKnownEventTypes(typeof(TestEvent).Assembly); - protected IntegrationFixture IntegrationFixture { get; private set; } = null!; - protected StreamName Stream { get; } - protected T Handler { get; private set; } = null!; + public IntegrationFixture IntegrationFixture { get; private set; } = null!; + public StreamName Stream { get; } protected ILogger Log { get; } - protected RedisCheckpointStore CheckpointStore { get; private set; } = null!; - protected ILoggerFactory LoggerFactory { get; } + public RedisCheckpointStore CheckpointStore { get; private set; } = null!; + public ILoggerFactory LoggerFactory { get; } + public T Handler { get; } IMessageSubscription Subscription { get; set; } = null!; - protected SubscriptionFixture(ITestOutputHelper outputHelper, bool subscribeToAll, bool autoStart = true, LogLevel logLevel = LogLevel.Trace) { + public SubscriptionFixture(bool subscribeToAll, LogLevel logLevel = LogLevel.Trace) { + Handler = new T(); _subscribeToAll = subscribeToAll; - _autoStart = autoStart; - Stream = new StreamName(SharedAutoFixture.Auto.Create()); - LoggerFactory = LoggingExtensions.GetLoggerFactory(outputHelper, logLevel); + Stream = new(SharedAutoFixture.Auto.Create()); + LoggerFactory = LoggingExtensions.GetLoggerFactory(logLevel); SubscriptionId = $"test-{Guid.NewGuid():N}"; Log = LoggerFactory.CreateLogger(GetType()); - _listener = new LoggingEventListener(LoggerFactory); + _listener = new(LoggerFactory); } - protected abstract T GetHandler(); - public string SubscriptionId { get; } - protected ValueTask Start() => Subscription.SubscribeWithLog(Log); + public async Task Start() { + await Subscription.SubscribeWithLog(Log); + } - protected ValueTask Stop() => Subscription.UnsubscribeWithLog(Log); + public async Task Stop() { + await Subscription.UnsubscribeWithLog(Log); + } readonly bool _subscribeToAll; - readonly bool _autoStart; readonly LoggingEventListener _listener; public async ValueTask InitializeAsync() { IntegrationFixture = new(); await IntegrationFixture.InitializeAsync(); - Handler = GetHandler(); - CheckpointStore = new RedisCheckpointStore(IntegrationFixture.GetDatabase, LoggerFactory); + CheckpointStore = new(IntegrationFixture.GetDatabase, LoggerFactory); var pipe = new ConsumePipe(); pipe.AddDefaultConsumer(Handler); @@ -53,23 +53,21 @@ public async ValueTask InitializeAsync() { !_subscribeToAll ? new RedisStreamSubscription( IntegrationFixture.GetDatabase, - new RedisStreamSubscriptionOptions(Stream) { SubscriptionId = SubscriptionId }, + new(Stream) { SubscriptionId = SubscriptionId }, CheckpointStore, pipe, LoggerFactory ) : new RedisAllStreamSubscription( IntegrationFixture.GetDatabase, - new RedisAllStreamSubscriptionOptions { SubscriptionId = SubscriptionId }, + new() { SubscriptionId = SubscriptionId }, CheckpointStore, pipe, LoggerFactory ); - if (_autoStart) await Start(); } public async ValueTask DisposeAsync() { - if (_autoStart) await Stop(); await FlushDb(); _listener.Dispose(); await IntegrationFixture.DisposeAsync(); diff --git a/src/Redis/test/Eventuous.Tests.Redis/Store/Append.cs b/src/Redis/test/Eventuous.Tests.Redis/Store/Append.cs index 491849a53..ef822e19c 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Store/Append.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Store/Append.cs @@ -3,51 +3,52 @@ namespace Eventuous.Tests.Redis.Store; -public class AppendEvents(IntegrationFixture fixture) : IClassFixture { - [Fact] - public async Task ShouldAppendToNoStream() { +[ClassDataSource] +public class AppendEvents(IntegrationFixture fixture) { + [Test] + public async Task ShouldAppendToNoStream(CancellationToken cancellationToken) { var evt = CreateEvent(); var streamName = GetStreamName(); - var result = await fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream); + var result = await fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream, cancellationToken); result.NextExpectedVersion.Should().Be(0); } - [Fact] - public async Task ShouldAppendOneByOne() { + [Test] + public async Task ShouldAppendOneByOne(CancellationToken cancellationToken) { var evt = CreateEvent(); var stream = GetStreamName(); - var result = await fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream); + var result = await fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream, cancellationToken); evt = CreateEvent(); var version = new ExpectedStreamVersion(result.NextExpectedVersion); - result = await fixture.AppendEvent(stream, evt, version); + result = await fixture.AppendEvent(stream, evt, version, cancellationToken); result.NextExpectedVersion.Should().Be(1); } - [Fact] - public async Task ShouldFailOnWrongVersionNoStream() { + [Test] + public async Task ShouldFailOnWrongVersionNoStream(CancellationToken cancellationToken) { var evt = CreateEvent(); var stream = GetStreamName(); - await fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream); + await fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream, cancellationToken); evt = CreateEvent(); - var task = () => fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream); + var task = () => fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream, cancellationToken); await task.Should().ThrowAsync(); } - [Fact] - public async Task ShouldFailOnWrongVersion() { + [Test] + public async Task ShouldFailOnWrongVersion(CancellationToken cancellationToken) { var evt = CreateEvent(); var stream = GetStreamName(); - await fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream); + await fixture.AppendEvent(stream, evt, ExpectedStreamVersion.NoStream, cancellationToken); evt = CreateEvent(); - var task = () => fixture.AppendEvent(stream, evt, new ExpectedStreamVersion(3)); + var task = () => fixture.AppendEvent(stream, evt, new(3), cancellationToken); await task.Should().ThrowAsync(); } } diff --git a/src/Redis/test/Eventuous.Tests.Redis/Store/Helpers.cs b/src/Redis/test/Eventuous.Tests.Redis/Store/Helpers.cs index d4ad88da3..5a17bb584 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Store/Helpers.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Store/Helpers.cs @@ -24,16 +24,17 @@ public static Task AppendEvents( this IntegrationFixture fixture, StreamName stream, object[] evt, - ExpectedStreamVersion version + ExpectedStreamVersion version, + CancellationToken cancellationToken ) { var streamEvents = evt.Select(x => new NewStreamEvent(Guid.NewGuid(), x, new())); - return fixture.EventWriter.AppendEvents(stream, version, streamEvents.ToArray(), default); + return fixture.EventWriter.AppendEvents(stream, version, streamEvents.ToArray(), cancellationToken); } - public static Task AppendEvent(this IntegrationFixture fixture, StreamName stream, object evt, ExpectedStreamVersion version) { + public static Task AppendEvent(this IntegrationFixture fixture, StreamName stream, object evt, ExpectedStreamVersion version, CancellationToken cancellationToken) { var streamEvent = new NewStreamEvent(Guid.NewGuid(), evt, new()); - return fixture.EventWriter.AppendEvents(stream, version, [streamEvent], default); + return fixture.EventWriter.AppendEvents(stream, version, [streamEvent], cancellationToken); } } diff --git a/src/Redis/test/Eventuous.Tests.Redis/Store/Read.cs b/src/Redis/test/Eventuous.Tests.Redis/Store/Read.cs index 5224be7ab..947205123 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Store/Read.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Store/Read.cs @@ -1,61 +1,61 @@ using Eventuous.Tests.Redis.Fixtures; using static Eventuous.Tests.Redis.Store.Helpers; -using static Xunit.TestContext; namespace Eventuous.Tests.Redis.Store; -public class ReadEvents(IntegrationFixture fixture) : IClassFixture { - [Fact] - public async Task ShouldReadOne() { +[ClassDataSource] +public class ReadEvents(IntegrationFixture fixture) { + [Test] + public async Task ShouldReadOne(CancellationToken cancellationToken) { var evt = CreateEvent(); var streamName = GetStreamName(); - await fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream); + await fixture.AppendEvent(streamName, evt, ExpectedStreamVersion.NoStream, cancellationToken); - var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); + var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 100, cancellationToken); result.Length.Should().Be(1); result[0].Payload.Should().BeEquivalentTo(evt); } - [Fact] - public async Task ShouldReadMany() { + [Test] + public async Task ShouldReadMany(CancellationToken cancellationToken) { // ReSharper disable once CoVariantArrayConversion var events = CreateEvents(20).ToArray(); var streamName = GetStreamName(); - await fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); + await fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream, cancellationToken); - var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 100, Current.CancellationToken); + var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 100, cancellationToken); var actual = result.Select(x => x.Payload); actual.Should().BeEquivalentTo(events); } - [Fact] - public async Task ShouldReadTail() { + [Test] + public async Task ShouldReadTail(CancellationToken cancellationToken) { // ReSharper disable once CoVariantArrayConversion var streamName = GetStreamName(); var events1 = CreateEvents(10).ToArray(); - var appended = await fixture.AppendEvents(streamName, events1, ExpectedStreamVersion.NoStream); + var appended = await fixture.AppendEvents(streamName, events1, ExpectedStreamVersion.NoStream, cancellationToken); var position = appended.GlobalPosition; var events2 = CreateEvents(10).ToArray(); - await fixture.AppendEvents(streamName, events2, ExpectedStreamVersion.Any); + await fixture.AppendEvents(streamName, events2, ExpectedStreamVersion.Any, cancellationToken); - var result = await fixture.EventReader.ReadEvents(streamName, new((long)position), 100, Current.CancellationToken); + var result = await fixture.EventReader.ReadEvents(streamName, new((long)position), 100, cancellationToken); var actual = result.Select(x => x.Payload); actual.Should().BeEquivalentTo(events2); } - [Fact] - public async Task ShouldReadHead() { + [Test] + public async Task ShouldReadHead(CancellationToken cancellationToken) { // ReSharper disable once CoVariantArrayConversion var events = CreateEvents(20).ToArray(); var streamName = GetStreamName(); - await fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream); + await fixture.AppendEvents(streamName, events, ExpectedStreamVersion.NoStream, cancellationToken); - var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 10, Current.CancellationToken); + var result = await fixture.EventReader.ReadEvents(streamName, StreamReadPosition.Start, 10, cancellationToken); var expected = events.Take(10); var actual = result.Select(x => x.Payload); diff --git a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs index f54aff673..b1ad3fab8 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs @@ -3,30 +3,43 @@ using Eventuous.Tests.Subscriptions.Base; using static Eventuous.Sut.App.Commands; using static Eventuous.Sut.Domain.BookingEvents; -using static Xunit.TestContext; namespace Eventuous.Tests.Redis.Subscriptions; -public class SubscribeToAll(ITestOutputHelper outputHelper) : SubscriptionFixture(outputHelper, true, false) { - [Fact] - public async Task ShouldConsumeProducedEvents() { +public class SubscribeToAll() { + SubscriptionFixture _fixture = null!; + + [Before(Test)] + public async Task Setup() { + _fixture = new(true); + await _fixture.InitializeAsync(); + } + + [After(Test)] + public async Task TearDown() { + await _fixture.DisposeAsync(); + } + + [Test] + public async Task ShouldConsumeProducedEvents(CancellationToken cancellationToken) { const int count = 10; var (testEvents, _) = await GenerateAndProduceEvents(count); - await Start(); - await Handler.AssertThat().Timebox(2.Seconds()).Exactly(count).Match(x => testEvents.Contains(x)).Validate(Current.CancellationToken); - await Stop(); + await _fixture.Start(); + await _fixture.Handler.AssertThat().Timebox(2.Seconds()).Exactly(count).Match(x => testEvents.Contains(x)).Validate(cancellationToken); + await _fixture.Stop(); - Handler.Count.Should().Be(10); + _fixture.Handler.Count.Should().Be(10); } - [Fact] - public async Task ShouldConsumeProducedEventsWhenRestarting() { + [Test] + public async Task ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { await TestConsumptionOfProducedEvents(); - Handler.Reset(); - await InitializeAsync(); + _fixture.Handler.Reset(); + + await _fixture.InitializeAsync(); await TestConsumptionOfProducedEvents(); @@ -37,28 +50,28 @@ async Task TestConsumptionOfProducedEvents() { var (testEvents, _) = await GenerateAndProduceEvents(count); - await Start(); - await Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(Current.CancellationToken); - await Stop(); + await _fixture.Start(); + await _fixture.Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(cancellationToken); + await _fixture.Stop(); - Handler.Count.Should().Be(10); + _fixture.Handler.Count.Should().Be(10); } } - [Fact] - public async Task ShouldUseExistingCheckpoint() { + [Test] + public async Task ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { const int count = 10; var (_, result) = await GenerateAndProduceEvents(count); - await CheckpointStore.GetLastCheckpoint(SubscriptionId, Current.CancellationToken); - Logger.ConfigureIfNull(SubscriptionId, LoggerFactory); - await CheckpointStore.StoreCheckpoint(new(SubscriptionId, result.GlobalPosition), true, Current.CancellationToken); + await _fixture.CheckpointStore.GetLastCheckpoint(_fixture.SubscriptionId, cancellationToken); + Logger.ConfigureIfNull(_fixture.SubscriptionId, _fixture.LoggerFactory); + await _fixture.CheckpointStore.StoreCheckpoint(new(_fixture.SubscriptionId, result.GlobalPosition), true, cancellationToken); - await Start(); - await Task.Delay(TimeSpan.FromSeconds(1), Current.CancellationToken); - await Stop(); - Handler.Count.Should().Be(0); + await _fixture.Start(); + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); + await _fixture.Stop(); + _fixture.Handler.Count.Should().Be(0); } static BookingImported ToEvent(ImportBooking cmd) @@ -72,10 +85,8 @@ static BookingImported ToEvent(ImportBooking cmd) var events = commands.Select(ToEvent).ToList(); var streamEvents = events.Select(x => new NewStreamEvent(Guid.NewGuid(), x, new())); - var result = await IntegrationFixture.EventWriter.AppendEvents(Stream, ExpectedStreamVersion.Any, streamEvents.ToArray(), default); + var result = await _fixture.IntegrationFixture.EventWriter.AppendEvents(_fixture.Stream, ExpectedStreamVersion.Any, streamEvents.ToArray(), default); return (events, result); } - - protected override TestEventHandler GetHandler() => new(); } diff --git a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs index 75c987b78..b34c8cba7 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs @@ -3,30 +3,42 @@ using Eventuous.Tests.Subscriptions.Base; using static Eventuous.Sut.App.Commands; using static Eventuous.Sut.Domain.BookingEvents; -using static Xunit.TestContext; namespace Eventuous.Tests.Redis.Subscriptions; -public class SubscribeToStream(ITestOutputHelper outputHelper) : SubscriptionFixture(outputHelper, false, false) { - [Fact] - public async Task ShouldConsumeProducedEvents() { +public class SubscribeToStream { + SubscriptionFixture _fixture = null!; + + [Before(Test)] + public async Task Setup() { + _fixture = new(false); + await _fixture.InitializeAsync(); + } + + [After(Test)] + public async Task TearDown() { + await _fixture.DisposeAsync(); + } + + [Test] + public async Task ShouldConsumeProducedEvents(CancellationToken cancellationToken) { const int count = 10; var testEvents = await GenerateAndProduceEvents(count); - await Start(); - await Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(Current.CancellationToken); - await Stop(); + await _fixture.Start(); + await _fixture.Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(cancellationToken); + await _fixture.Stop(); - Handler.Count.Should().Be(10); + _fixture.Handler.Count.Should().Be(10); } - [Fact] - public async Task ShouldConsumeProducedEventsWhenRestarting() { + [Test] + public async Task ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { await TestConsumptionOfProducedEvents(); - Handler.Reset(); - await InitializeAsync(); + _fixture.Handler.Reset(); + await _fixture.InitializeAsync(); await TestConsumptionOfProducedEvents(); @@ -37,29 +49,29 @@ async Task TestConsumptionOfProducedEvents() { var testEvents = await GenerateAndProduceEvents(count); - await Start(); - await Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(Current.CancellationToken); - await Stop(); + await _fixture.Start(); + await _fixture.Handler.AssertCollection(2.Seconds(), [..testEvents]).Validate(cancellationToken); + await _fixture.Stop(); - Handler.Count.Should().Be(10); + _fixture.Handler.Count.Should().Be(10); } } - [Fact] - public async Task ShouldUseExistingCheckpoint() { + [Test] + public async Task ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { const int count = 10; await GenerateAndProduceEvents(count); - await CheckpointStore.GetLastCheckpoint(SubscriptionId, Current.CancellationToken); + await _fixture.CheckpointStore.GetLastCheckpoint(_fixture.SubscriptionId, cancellationToken); var streamPosition = await GetStreamPosition(count); - Logger.ConfigureIfNull(SubscriptionId, LoggerFactory); - await CheckpointStore.StoreCheckpoint(new(SubscriptionId, (ulong)streamPosition), true, Current.CancellationToken); + Logger.ConfigureIfNull(_fixture.SubscriptionId, _fixture.LoggerFactory); + await _fixture.CheckpointStore.StoreCheckpoint(new(_fixture.SubscriptionId, (ulong)streamPosition), true, cancellationToken); - await Start(); - await Task.Delay(TimeSpan.FromSeconds(1), Current.CancellationToken); - await Stop(); - Handler.Count.Should().Be(0); + await _fixture.Start(); + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); + await _fixture.Stop(); + _fixture.Handler.Count.Should().Be(0); } static BookingImported ToEvent(ImportBooking cmd) => new(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut); @@ -73,16 +85,14 @@ async Task> GenerateAndProduceEvents(int count) { var events = commands.Select(ToEvent).ToList(); var streamEvents = events.Select(x => new NewStreamEvent(Guid.NewGuid(), x, new())); - await IntegrationFixture.EventWriter.AppendEvents(Stream, ExpectedStreamVersion.Any, streamEvents.ToArray(), default); + await _fixture.IntegrationFixture.EventWriter.AppendEvents(_fixture.Stream, ExpectedStreamVersion.Any, streamEvents.ToArray(), default); return events; } async Task GetStreamPosition(int count) { - var readEvents = await IntegrationFixture.EventReader.ReadEvents(Stream, StreamReadPosition.Start, count, default); + var readEvents = await _fixture.IntegrationFixture.EventReader.ReadEvents(_fixture.Stream, StreamReadPosition.Start, count, default); return readEvents.Last().Position; } - - protected override TestEventHandler GetHandler() => new(); } diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj b/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj index 3273c70cd..aec64d78a 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj @@ -17,4 +17,8 @@ + + + + \ No newline at end of file diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Metrics/MetricsTests.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Metrics/MetricsTests.cs index 7cd30a397..ffd698ff4 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Metrics/MetricsTests.cs +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Metrics/MetricsTests.cs @@ -7,6 +7,15 @@ namespace Eventuous.Tests.SqlServer.Metrics; -[Collection("Database")] -public class MetricsTests(ITestOutputHelper outputHelper) - : MetricsTestsBase(outputHelper); +public class MetricsTests : MetricsTestsBase { + [Test] + public async Task ShouldMeasureSubscriptionGapCount() { + await ShouldMeasureSubscriptionGapCountBase(); + } + + [Before(Test)] + public async Task Setup() => await InitializeAsync(); + + [After(Test)] + public async Task TearDown() => await DisposeAsync(); +} diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Projections/ProjectorTests.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Projections/ProjectorTests.cs index 890e59b80..0f9b428f5 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Projections/ProjectorTests.cs +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Projections/ProjectorTests.cs @@ -1,7 +1,4 @@ -// Copyright (C) Ubiquitous AS. All rights reserved -// Licensed under the Apache License, Version 2.0. - -using Eventuous.SqlServer; +using Eventuous.SqlServer; using Eventuous.SqlServer.Projections; using Eventuous.SqlServer.Subscriptions; using Eventuous.Sut.App; @@ -9,14 +6,12 @@ using Eventuous.Tests.Persistence.Base.Fixtures; using Eventuous.Tests.SqlServer.Subscriptions; using Microsoft.Data.SqlClient; -using static Xunit.TestContext; namespace Eventuous.Tests.SqlServer.Projections; -[Collection("Database")] -public class ProjectorTests(ITestOutputHelper outputHelper) : IAsyncLifetime { +public class ProjectorTests() { readonly SubscriptionFixture _fixture - = new(_ => { }, outputHelper); + = new(_ => { }); const string Schema = """ IF OBJECT_ID('__schema__.Bookings', 'U') IS NULL @@ -29,14 +24,14 @@ Price NUMERIC(10,2) END """; - [Fact] - public async Task ProjectImportedBookingsToTable() { + [Test] + public async Task ProjectImportedBookingsToTable(CancellationToken cancellationToken) { await CreateSchema(); var commands = await GenerateAndProduceEvents(100); - await Task.Delay(1000, Current.CancellationToken); + await Task.Delay(1000, cancellationToken); - await using var connection = await ConnectionFactory.GetConnection(_fixture.ConnectionString, Current.CancellationToken); + await using var connection = await ConnectionFactory.GetConnection(_fixture.ConnectionString, cancellationToken); var select = $"SELECT * FROM {_fixture.SchemaName}.Bookings where BookingId = @BookingId"; @@ -49,8 +44,8 @@ public async Task ProjectImportedBookingsToTable() { async Task ValidateProjectedObject(SqlConnection conn, Commands.ImportBooking command) { await using var cmd = new SqlCommand(select, conn); cmd.Parameters.AddWithValue("@BookingId", command.BookingId); - await using var reader = await cmd.ExecuteReaderAsync(Current.CancellationToken); - await reader.ReadAsync(Current.CancellationToken); + await using var reader = await cmd.ExecuteReaderAsync(cancellationToken); + await reader.ReadAsync(cancellationToken); reader["CheckinDate"].Should().Be(command.CheckIn.ToDateTimeUnspecified()); reader["Price"].Should().Be(command.Price); } @@ -81,8 +76,10 @@ async Task CreateSchema() { static BookingEvents.BookingImported ToEvent(Commands.ImportBooking cmd) => new(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut); + [Before(Test)] public async ValueTask InitializeAsync() => await _fixture.InitializeAsync(); + [After(Test)] public async ValueTask DisposeAsync() => await _fixture.DisposeAsync(); } diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Registrations/RegistrationTests.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Registrations/RegistrationTests.cs index 9d1c52d9b..b5b582c40 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Registrations/RegistrationTests.cs +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Registrations/RegistrationTests.cs @@ -9,7 +9,7 @@ namespace Eventuous.Tests.SqlServer.Registrations; public class RegistrationTests { const string ConnectionString = "Server=localhost;User Id=sqlserver;Password=secret;Database=eventuous;TrustServerCertificate=True"; - [Fact] + [Test] public void Should_resolve_store_with_manual_registration() { var builder = new WebHostBuilder(); builder.Configure(_ => { }); @@ -27,7 +27,7 @@ public void Should_resolve_store_with_manual_registration() { innerStore.Should().BeOfType(); } - [Fact] + [Test] public void Should_resolve_store_with_extensions() { var builder = new WebHostBuilder(); diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Store/StoreTests.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Store/StoreTests.cs index 54ce965ea..d7578f7cc 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Store/StoreTests.cs +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Store/StoreTests.cs @@ -4,11 +4,14 @@ namespace Eventuous.Tests.SqlServer.Store; -[Collection("Database")] +[InheritsTests] +[ClassDataSource] public class Append(StoreFixture fixture) : StoreAppendTests(fixture); -[Collection("Database")] +[InheritsTests] +[ClassDataSource] public class Read(StoreFixture fixture) : StoreReadTests(fixture); -[Collection("Database")] +[InheritsTests] +[ClassDataSource] public class OtherMethods(StoreFixture fixture) : StoreOtherOpsTests(fixture); diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Store/TieredStoreTests.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Store/TieredStoreTests.cs index 206eb9b67..bb9799fed 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Store/TieredStoreTests.cs +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Store/TieredStoreTests.cs @@ -1,8 +1,12 @@ using Eventuous.Tests.Persistence.Base.Store; -using JetBrains.Annotations; using Testcontainers.SqlEdge; namespace Eventuous.Tests.SqlServer.Store; -[UsedImplicitly] -public class TieredStoreTests(StoreFixture storeFixture) : TieredStoreTestsBase(storeFixture), IClassFixture; +[ClassDataSource(Shared = SharedType.ForClass)] +public class TieredStoreTests(StoreFixture storeFixture) : TieredStoreTestsBase(storeFixture) { + [Test] + public async Task Should_load_hot_and_archive_test() { + await Should_load_hot_and_archive(); + } +} diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscribeTests.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscribeTests.cs index 6f25176b5..f0dc46626 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscribeTests.cs +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscribeTests.cs @@ -6,53 +6,48 @@ namespace Eventuous.Tests.SqlServer.Subscriptions; -[Collection("Database")] -public class SubscribeToAll(ITestOutputHelper outputHelper) +public class SubscribeToAll() : SubscribeToAllBase( - outputHelper, - new SubscriptionFixture(_ => { }, outputHelper, false) + new SubscriptionFixture(_ => { }, false) ) { - [Fact] - public async Task SqlServer_ShouldConsumeProducedEvents() { - await ShouldConsumeProducedEvents(); + [Test] + public async Task SqlServer_ShouldConsumeProducedEvents(CancellationToken cancellationToken) { + await ShouldConsumeProducedEvents(cancellationToken); } - [Fact] - public async Task SqlServer_ShouldConsumeProducedEventsWhenRestarting() { - await ShouldConsumeProducedEventsWhenRestarting(); + [Test] + public async Task SqlServer_ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { + await ShouldConsumeProducedEventsWhenRestarting(cancellationToken); } - [Fact] - public async Task SqlServer_ShouldUseExistingCheckpoint() { - await ShouldUseExistingCheckpoint(); + [Test] + public async Task SqlServer_ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { + await ShouldUseExistingCheckpoint(cancellationToken); } } -[Collection("Database")] -public class SubscribeToStream(ITestOutputHelper outputHelper, StreamNameFixture streamNameFixture) +[ClassDataSource(Shared = SharedType.None)] +public class SubscribeToStream(StreamNameFixture streamNameFixture) : SubscribeToStreamBase( - outputHelper, - streamNameFixture.StreamName, - new SubscriptionFixture( - opt => ConfigureOptions(opt, streamNameFixture), - outputHelper, - false - ) - ), - IClassFixture { - [Fact] - public async Task SqlServer_ShouldConsumeProducedEvents() { - await ShouldConsumeProducedEvents(); + streamNameFixture.StreamName, + new SubscriptionFixture( + opt => ConfigureOptions(opt, streamNameFixture), + false + ) + ) { + [Test] + public async Task SqlServer_ShouldConsumeProducedEvents(CancellationToken cancellationToken) { + await ShouldConsumeProducedEvents(cancellationToken); } - [Fact] - public async Task SqlServer_ShouldConsumeProducedEventsWhenRestarting() { - await ShouldConsumeProducedEventsWhenRestarting(); + [Test] + public async Task SqlServer_ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { + await ShouldConsumeProducedEventsWhenRestarting(cancellationToken); } - [Fact] - public async Task SqlServer_ShouldUseExistingCheckpoint() { - await ShouldUseExistingCheckpoint(); + [Test] + public async Task SqlServer_ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { + await ShouldUseExistingCheckpoint(cancellationToken); } static void ConfigureOptions(SqlServerStreamSubscriptionOptions options, StreamNameFixture streamNameFixture) { diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscriptionFixture.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscriptionFixture.cs index 6da865349..487ae9adf 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscriptionFixture.cs +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Subscriptions/SubscriptionFixture.cs @@ -11,13 +11,11 @@ namespace Eventuous.Tests.SqlServer.Subscriptions; public class SubscriptionFixture( Action configureOptions, - ITestOutputHelper outputHelper, bool autoStart = true, Action? configureServices = null, LogLevel logLevel = LogLevel.Debug ) : SubscriptionFixtureBase( - outputHelper, autoStart, logLevel ) @@ -26,8 +24,6 @@ public class SubscriptionFixture SqlContainer.Create(); protected override SqlServerCheckpointStore GetCheckpointStore(IServiceProvider sp) @@ -45,7 +41,7 @@ protected override void SetupServices(IServiceCollection services) { services.AddEventuousSqlServer(Container.GetConnectionString(), SchemaName, true); services.AddEventStore(); services.AddSqlServerCheckpointStore(); - services.AddSingleton(new TestEventHandlerOptions(null, _outputHelper)); + services.AddSingleton(new TestEventHandlerOptions()); configureServices?.Invoke(services); } diff --git a/test/Eventuous.TestHelpers.TUnit/Eventuous.TestHelpers.TUnit.csproj b/test/Eventuous.TestHelpers.TUnit/Eventuous.TestHelpers.TUnit.csproj new file mode 100644 index 000000000..0c19ddd67 --- /dev/null +++ b/test/Eventuous.TestHelpers.TUnit/Eventuous.TestHelpers.TUnit.csproj @@ -0,0 +1,10 @@ + + + false + enable + + + + + + diff --git a/test/Eventuous.TestHelpers.TUnit/Logging/LoggingExtensions.cs b/test/Eventuous.TestHelpers.TUnit/Logging/LoggingExtensions.cs new file mode 100644 index 000000000..e9d6feeb7 --- /dev/null +++ b/test/Eventuous.TestHelpers.TUnit/Logging/LoggingExtensions.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.Logging; + +namespace Eventuous.TestHelpers.TUnit.Logging; + +public static class LoggingExtensions { + public static ILoggerFactory GetLoggerFactory(LogLevel logLevel = LogLevel.Debug) + => LoggerFactory.Create( + builder => builder + .SetMinimumLevel(logLevel) + .AddFilter("Microsoft", LogLevel.Warning) + .AddFilter("Grpc.Net.Client", LogLevel.Warning) + .AddTUnit() + ); + + public static ILoggerFactory AddTUnit(this ILoggerFactory factory) { + factory.AddProvider(new TUnitLoggerProvider()); + + return factory; + } + + public static ILoggingBuilder AddTUnit(this ILoggingBuilder builder) => builder.AddProvider(new TUnitLoggerProvider()); +} + +public sealed class TUnitLoggerProvider() : ILoggerProvider { + private readonly LoggerExternalScopeProvider _scopeProvider = new(); + + public ILogger CreateLogger(string categoryName) => new TUnitLog(_scopeProvider, categoryName); + + public void Dispose() { } +} diff --git a/test/Eventuous.TestHelpers.TUnit/Logging/TUnitLogger.cs b/test/Eventuous.TestHelpers.TUnit/Logging/TUnitLogger.cs new file mode 100644 index 000000000..755a0c5e4 --- /dev/null +++ b/test/Eventuous.TestHelpers.TUnit/Logging/TUnitLogger.cs @@ -0,0 +1,43 @@ +// Copyright (C) Ubiquitous AS.All rights reserved +// Licensed under the Apache License, Version 2.0. + +using Microsoft.Extensions.Logging; +using ILogger = Microsoft.Extensions.Logging.ILogger; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; + +namespace Eventuous.TestHelpers.TUnit.Logging; + +public sealed class TUnitLog(LoggerExternalScopeProvider scopeProvider) : TUnitLog(scopeProvider, typeof(T).FullName), Microsoft.Extensions.Logging.ILogger; + +public class TUnitLog : ILogger { + private readonly string? _categoryName; + private readonly LoggerExternalScopeProvider _scopeProvider; + + public static ILogger CreateLogger() => new TUnitLog(new(), ""); + + public static Microsoft.Extensions.Logging.ILogger CreateLogger() => new TUnitLog(new()); + + public TUnitLog(LoggerExternalScopeProvider scopeProvider, string? categoryName) { + _scopeProvider = scopeProvider; + _categoryName = categoryName; + } + + public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None; + + public IDisposable? BeginScope(TState state) where TState : notnull => _scopeProvider.Push(state); + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { + if (TestContext.Current == null) return; + + var level = logLevel switch { + LogLevel.Trace => global::TUnit.Core.Logging.LogLevel.Trace, + LogLevel.Debug => global::TUnit.Core.Logging.LogLevel.Debug, + LogLevel.Information => global::TUnit.Core.Logging.LogLevel.Information, + LogLevel.Warning => global::TUnit.Core.Logging.LogLevel.Warning, + LogLevel.Error => global::TUnit.Core.Logging.LogLevel.Error, + LogLevel.Critical => global::TUnit.Core.Logging.LogLevel.Critical, + _ => throw new ArgumentOutOfRangeException(nameof(logLevel)) + }; + TestContext.Current.GetDefaultLogger().Log(level, state, exception, formatter); + } +} diff --git a/test/Eventuous.TestHelpers.TUnit/TestEventListener.cs b/test/Eventuous.TestHelpers.TUnit/TestEventListener.cs new file mode 100644 index 000000000..a12c4370e --- /dev/null +++ b/test/Eventuous.TestHelpers.TUnit/TestEventListener.cs @@ -0,0 +1,38 @@ +using System.Diagnostics.Tracing; + +namespace Eventuous.TestHelpers.TUnit; + +public sealed class TestEventListener(Action? act = null, params string[] prefixes) : EventListener { + readonly string[] _prefixes = prefixes.Length > 0 ? prefixes : ["OpenTelemetry", "eventuous"]; + readonly List _eventSources = []; + + protected override void OnEventSourceCreated(EventSource? eventSource) { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (_prefixes == null || eventSource?.Name == null) { + return; + } + + if (_prefixes.Any(x => eventSource.Name.StartsWith(x))) { + _eventSources.Add(eventSource); + EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All); + } + + base.OnEventSourceCreated(eventSource); + } + +#nullable disable + protected override void OnEventWritten(EventWrittenEventArgs evt) { + var message = evt.Message != null && (evt.Payload?.Count ?? 0) > 0 ? string.Format(evt.Message, evt.Payload.ToArray()) : evt.Message; + TestContext.Current?.OutputWriter.WriteLine($"{evt.EventSource.Name} - EventId: [{evt.EventId}], EventName: [{evt.EventName}], Message: [{message}]"); + act?.Invoke(evt); + } +#nullable enable + + public override void Dispose() { + foreach (var eventSource in _eventSources) { + DisableEvents(eventSource); + } + + base.Dispose(); + } +} diff --git a/test/Eventuous.TestHelpers.XUnit/Eventuous.TestHelpers.XUnit.csproj b/test/Eventuous.TestHelpers.XUnit/Eventuous.TestHelpers.XUnit.csproj new file mode 100644 index 000000000..7716080ef --- /dev/null +++ b/test/Eventuous.TestHelpers.XUnit/Eventuous.TestHelpers.XUnit.csproj @@ -0,0 +1,13 @@ + + + false + + + + + + + + + + diff --git a/test/Eventuous.TestHelpers/Logging/LoggingExtensions.cs b/test/Eventuous.TestHelpers.XUnit/Logging/LoggingExtensions.cs similarity index 100% rename from test/Eventuous.TestHelpers/Logging/LoggingExtensions.cs rename to test/Eventuous.TestHelpers.XUnit/Logging/LoggingExtensions.cs diff --git a/test/Eventuous.TestHelpers/Logging/xUnitLogger.cs b/test/Eventuous.TestHelpers.XUnit/Logging/xUnitLogger.cs similarity index 100% rename from test/Eventuous.TestHelpers/Logging/xUnitLogger.cs rename to test/Eventuous.TestHelpers.XUnit/Logging/xUnitLogger.cs diff --git a/test/Eventuous.TestHelpers/Logging/xUnitLoggerOptions.cs b/test/Eventuous.TestHelpers.XUnit/Logging/xUnitLoggerOptions.cs similarity index 100% rename from test/Eventuous.TestHelpers/Logging/xUnitLoggerOptions.cs rename to test/Eventuous.TestHelpers.XUnit/Logging/xUnitLoggerOptions.cs diff --git a/test/Eventuous.TestHelpers/TestEventListener.cs b/test/Eventuous.TestHelpers.XUnit/TestEventListener.cs similarity index 100% rename from test/Eventuous.TestHelpers/TestEventListener.cs rename to test/Eventuous.TestHelpers.XUnit/TestEventListener.cs diff --git a/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj b/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj index a9942088e..c1c0df14a 100644 --- a/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj +++ b/test/Eventuous.TestHelpers/Eventuous.TestHelpers.csproj @@ -5,15 +5,6 @@ - - - - - - - - - From 0ff404d59ad4692b424507dd4275af576e8675f8 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Wed, 6 Nov 2024 16:45:41 +0100 Subject: [PATCH 08/17] Try limiting parallel tests --- .../Fixtures/EsdbContainer.cs | 4 ++-- .../test/Eventuous.Tests.EventStore/Limiter.cs | 10 ++++++++++ .../test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs | 2 +- .../test/Eventuous.Tests.SqlServer/Limiter.cs | 10 ++++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 src/EventStore/test/Eventuous.Tests.EventStore/Limiter.cs create mode 100644 src/SqlServer/test/Eventuous.Tests.SqlServer/Limiter.cs diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/EsdbContainer.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/EsdbContainer.cs index d904e850d..7b02ed3eb 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/EsdbContainer.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Fixtures/EsdbContainer.cs @@ -6,8 +6,8 @@ namespace Eventuous.Tests.EventStore.Fixtures; public static class EsdbContainer { public static EventStoreDbContainer Create() { var image = RuntimeInformation.ProcessArchitecture == Architecture.Arm64 - ? "eventstore/eventstore:24.2.0-alpha-arm64v8" - : "eventstore/eventstore:24.2"; + ? "eventstore/eventstore:24.6.0-alpha-arm64v8" + : "eventstore/eventstore:24.6"; return new EventStoreDbBuilder() .WithImage(image) diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Limiter.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Limiter.cs new file mode 100644 index 000000000..7a9625204 --- /dev/null +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Limiter.cs @@ -0,0 +1,10 @@ +using Eventuous.Tests.EventStore; +using TUnit.Core.Interfaces; + +[assembly: ParallelLimiter] + +namespace Eventuous.Tests.EventStore; + +public class Limiter : IParallelLimit { + public int Limit => 4; +} diff --git a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs index 9f27dcbc3..120a2d8ec 100644 --- a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs +++ b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs @@ -45,7 +45,7 @@ public async Task SubscribeAndProduceMany(CancellationToken cancellationToken) { var testEvents = Auto.CreateMany(count).ToList(); await _producer.Produce(_exchange, testEvents, new(), cancellationToken: cancellationToken); - await _handler.AssertCollection(10.Seconds(), [..testEvents]).Validate(cancellationToken); + await _handler.AssertCollection(30.Seconds(), [..testEvents]).Validate(cancellationToken); } [Before(Test)] diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Limiter.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Limiter.cs new file mode 100644 index 000000000..c029df3a0 --- /dev/null +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Limiter.cs @@ -0,0 +1,10 @@ +using Eventuous.Tests.SqlServer; +using TUnit.Core.Interfaces; + +[assembly: ParallelLimiter] + +namespace Eventuous.Tests.SqlServer; + +public class Limiter : IParallelLimit { + public int Limit => 2; +} From caf6686323c08df1dba4f67fb451f3fd52b13350 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Wed, 6 Nov 2024 17:30:37 +0100 Subject: [PATCH 09/17] Flaky pub sub tests --- .../Producers/GooglePubSubProducer.cs | 5 ++--- .../PubSubFixture.cs | 8 +++---- .../PubSubTests.cs | 22 ++++++++++--------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/GooglePubSub/src/Eventuous.GooglePubSub/Producers/GooglePubSubProducer.cs b/src/GooglePubSub/src/Eventuous.GooglePubSub/Producers/GooglePubSubProducer.cs index 715c6edc8..270ee0a12 100644 --- a/src/GooglePubSub/src/Eventuous.GooglePubSub/Producers/GooglePubSubProducer.cs +++ b/src/GooglePubSub/src/Eventuous.GooglePubSub/Producers/GooglePubSubProducer.cs @@ -39,12 +39,11 @@ public GooglePubSubProducer( /// Producer options /// Optional event serializer. Will use the default instance if missing. /// Optional logger instance - public GooglePubSubProducer(PubSubProducerOptions options, IEventSerializer? serializer = null, ILogger? log = null) - : base(TracingOptions) { + public GooglePubSubProducer(PubSubProducerOptions options, IEventSerializer? serializer = null, ILogger? log = null) : base(TracingOptions) { Ensure.NotNull(options); _serializer = serializer ?? DefaultEventSerializer.Instance; - _clientCache = new ClientCache(options, log); + _clientCache = new(options, log); _attributes = options.Attributes; _log = log; } diff --git a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs index fd2b15a39..5d596c151 100644 --- a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs +++ b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubFixture.cs @@ -8,16 +8,16 @@ namespace Eventuous.Tests.GooglePubSub; public class PubSubFixture : IAsyncInitializer, IAsyncDisposable { public static string PubsubProjectId => "test-id"; - public static async Task DeleteSubscription(string subscriptionId) { + public static async Task DeleteSubscription(string subscriptionId, CancellationToken cancellationToken) { var builder = new SubscriberServiceApiClientBuilder { EmulatorDetection = EmulatorDetection.EmulatorOnly }; - var subscriber = await builder.BuildAsync(); + var subscriber = await builder.BuildAsync(cancellationToken); var subscriptionName = SubscriptionName.FromProjectSubscription(PubsubProjectId, subscriptionId); await subscriber.DeleteSubscriptionAsync(subscriptionName); } - public static async Task DeleteTopic(string topicId) { + public static async Task DeleteTopic(string topicId, CancellationToken cancellationToken) { var builder = new PublisherServiceApiClientBuilder { EmulatorDetection = EmulatorDetection.EmulatorOnly }; - var publisher = await builder.BuildAsync(); + var publisher = await builder.BuildAsync(cancellationToken); var topicName = TopicName.FromProjectTopic(PubsubProjectId, topicId); await publisher.DeleteTopicAsync(topicName); } diff --git a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs index 1dda5fc97..136ed9db9 100644 --- a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs +++ b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs @@ -8,7 +8,7 @@ namespace Eventuous.Tests.GooglePubSub; -[ClassDataSource] +[ClassDataSource(Shared = SharedType.ForClass)] public class PubSubTests { static PubSubTests() => TypeMap.Instance.RegisterKnownEventTypes(typeof(TestEvent).Assembly); @@ -48,6 +48,7 @@ public PubSubTests(PubSubFixture _) { } [Test] + [Retry(3)] public async Task SubscribeAndProduce(CancellationToken cancellationToken) { var testEvent = Auto.Create(); @@ -57,27 +58,28 @@ public async Task SubscribeAndProduce(CancellationToken cancellationToken) { } [Test] + [Retry(3)] public async Task SubscribeAndProduceMany(CancellationToken cancellationToken) { const int count = 10000; var testEvents = Auto.CreateMany(count).ToList(); await _producer.Produce(_pubsubTopic, testEvents, null, cancellationToken: cancellationToken); - await _handler.AssertCollection(20.Seconds(), [..testEvents]).Validate(cancellationToken); + await _handler.AssertCollection(40.Seconds(), [..testEvents]).Validate(cancellationToken); } [Before(Test)] - public async Task InitializeAsync() { - await _producer.StartAsync(); - await _subscription.SubscribeWithLog(_log); + public async Task InitializeAsync(CancellationToken cancellationToken) { + await _producer.StartAsync(cancellationToken); + await _subscription.SubscribeWithLog(_log, cancellationToken); } [After(Test)] - public async Task DisposeAsync() { - await _producer.StopAsync(); - await _subscription.UnsubscribeWithLog(_log); + public async Task DisposeAsync(CancellationToken cancellationToken) { + await _producer.StopAsync(cancellationToken); + await _subscription.UnsubscribeWithLog(_log, cancellationToken); - await PubSubFixture.DeleteSubscription(_pubsubSubscription); - await PubSubFixture.DeleteTopic(_pubsubTopic); + await PubSubFixture.DeleteSubscription(_pubsubSubscription, cancellationToken); + await PubSubFixture.DeleteTopic(_pubsubTopic, cancellationToken); } } From 60f53a5ba33ac67b8185f94a00501424cda75c9a Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Wed, 6 Nov 2024 18:10:51 +0100 Subject: [PATCH 10/17] Convert the last project --- Directory.Packages.props | 13 ++- Eventuous.sln | 9 -- .../Fixtures/TestEventHandler.cs | 2 +- ...teContractToCommandExplicitly.verified.txt | 27 ------ ...CommandExplicitlyWithoutRoute.verified.txt | 27 ------ ...lyWithoutRouteWithGenericAttr.verified.txt | 27 ------ ...licitly_tResult=BookingResult.verified.txt | 27 ------ ...mandExplicitly_tResult=Result.verified.txt | 27 ------ ...licitly_tResult=BookingResult.verified.txt | 27 ------ ...mandExplicitly_tResult=Result.verified.txt | 27 ------ ...mandsTests.MapEnrichedCommand.verified.txt | 28 ------ .../AggregateCommandsTests.cs | 28 +++--- .../ControllerTests.cs | 19 ++--- ...ts.CallDiscoveredCommandRoute.verified.txt | 28 ------ .../DiscoveredCommandsTests.cs | 9 +- ...entuous.Tests.Extensions.AspNetCore.csproj | 4 +- .../Fixture/ServerFixture.cs | 5 +- .../Fixture/TestBaseWithLogs.cs | 6 +- .../Eventuous.TestHelpers.XUnit.csproj | 13 --- .../Logging/LoggingExtensions.cs | 34 -------- .../Logging/xUnitLogger.cs | 85 ------------------- .../Logging/xUnitLoggerOptions.cs | 31 ------- .../TestEventListener.cs | 38 --------- 23 files changed, 35 insertions(+), 506 deletions(-) delete mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly.verified.txt delete mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRoute.verified.txt delete mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRouteWithGenericAttr.verified.txt delete mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_tResult=BookingResult.verified.txt delete mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_tResult=Result.verified.txt delete mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapContractToCommandExplicitly_tResult=BookingResult.verified.txt delete mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapContractToCommandExplicitly_tResult=Result.verified.txt delete mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapEnrichedCommand.verified.txt delete mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.CallDiscoveredCommandRoute.verified.txt delete mode 100644 test/Eventuous.TestHelpers.XUnit/Eventuous.TestHelpers.XUnit.csproj delete mode 100644 test/Eventuous.TestHelpers.XUnit/Logging/LoggingExtensions.cs delete mode 100644 test/Eventuous.TestHelpers.XUnit/Logging/xUnitLogger.cs delete mode 100644 test/Eventuous.TestHelpers.XUnit/Logging/xUnitLoggerOptions.cs delete mode 100644 test/Eventuous.TestHelpers.XUnit/TestEventListener.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 3797554ca..064af7c8d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -50,11 +50,6 @@ - - - - - @@ -85,10 +80,12 @@ - - - + + + + + diff --git a/Eventuous.sln b/Eventuous.sln index e935424f4..3668bfbb7 100644 --- a/Eventuous.sln +++ b/Eventuous.sln @@ -215,8 +215,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Brokers", "Brokers", "{86D9 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.Extensions.Logging", "src\Extensions\src\Eventuous.Extensions.Logging\Eventuous.Extensions.Logging.csproj", "{E1F6CDD8-D37E-487B-A429-25A06C590FE4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.TestHelpers.XUnit", "test\Eventuous.TestHelpers.XUnit\Eventuous.TestHelpers.XUnit.csproj", "{8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eventuous.TestHelpers.TUnit", "test\Eventuous.TestHelpers.TUnit\Eventuous.TestHelpers.TUnit.csproj", "{2A816CFD-5D05-4F64-8222-F7214B229EBC}" EndProject Global @@ -554,12 +552,6 @@ Global {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Release|Any CPU.Build.0 = Release|Any CPU {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Debug CI|Any CPU.ActiveCfg = Debug CI|Any CPU {E1F6CDD8-D37E-487B-A429-25A06C590FE4}.Debug CI|Any CPU.Build.0 = Debug CI|Any CPU - {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Release|Any CPU.Build.0 = Release|Any CPU - {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Debug CI|Any CPU.ActiveCfg = Debug CI|Any CPU - {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26}.Debug CI|Any CPU.Build.0 = Debug CI|Any CPU {2A816CFD-5D05-4F64-8222-F7214B229EBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2A816CFD-5D05-4F64-8222-F7214B229EBC}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A816CFD-5D05-4F64-8222-F7214B229EBC}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -664,7 +656,6 @@ Global {6E545DFE-FE70-4486-92E0-E47E86E66210} = {86D92758-EBB6-4B8C-94B7-BD91AF1E31D2} {2E59C5F8-3E5A-4450-B902-7648AD7ECC0F} = {86D92758-EBB6-4B8C-94B7-BD91AF1E31D2} {E1F6CDD8-D37E-487B-A429-25A06C590FE4} = {2B7F84B7-C0E5-408F-ABAF-BF23C8305486} - {8ACC0B10-85A5-43ED-97F1-43E2C67D5F26} = {C60C6094-2A03-45B6-AB33-C514C35DF823} {2A816CFD-5D05-4F64-8222-F7214B229EBC} = {C60C6094-2A03-45B6-AB33-C514C35DF823} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs index 4a812f30e..8f0e29762 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs @@ -29,7 +29,7 @@ public Hypothesis AssertCollection(TimeSpan deadline, List colle => Hypothesis.On(_observer).Timebox(deadline).Exactly(collection.Count).Match(collection.Contains); public override async ValueTask HandleEvent(IMessageConsumeContext context) { - TestContext.Current?.OutputWriter.WriteLine(context.Message!.ToString() ?? string.Empty); + TestContext.Current?.OutputWriter.WriteLine(context.Message!.ToString() ?? "Unknown"); await Task.Delay(_delay); await _observer.Add(context.Message!, context.CancellationToken); Count++; diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly.verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly.verified.txt deleted file mode 100644 index 94228fd7f..000000000 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly.verified.txt +++ /dev/null @@ -1,27 +0,0 @@ -{ - state: { - price: { - amount: 100, - currency: EUR - }, - amountPaid: { - amount: 0, - currency: EUR - }, - id: { - value: Guid_1 - } - }, - changes: [ - { - event: { - roomId: Guid_2, - price: 100, - checkIn: 2023-10-01, - checkOut: 2023-10-02 - }, - eventType: V1.BookingImported - } - ], - streamPosition: 0 -} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRoute.verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRoute.verified.txt deleted file mode 100644 index 94228fd7f..000000000 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRoute.verified.txt +++ /dev/null @@ -1,27 +0,0 @@ -{ - state: { - price: { - amount: 100, - currency: EUR - }, - amountPaid: { - amount: 0, - currency: EUR - }, - id: { - value: Guid_1 - } - }, - changes: [ - { - event: { - roomId: Guid_2, - price: 100, - checkIn: 2023-10-01, - checkOut: 2023-10-02 - }, - eventType: V1.BookingImported - } - ], - streamPosition: 0 -} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRouteWithGenericAttr.verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRouteWithGenericAttr.verified.txt deleted file mode 100644 index 94228fd7f..000000000 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRouteWithGenericAttr.verified.txt +++ /dev/null @@ -1,27 +0,0 @@ -{ - state: { - price: { - amount: 100, - currency: EUR - }, - amountPaid: { - amount: 0, - currency: EUR - }, - id: { - value: Guid_1 - } - }, - changes: [ - { - event: { - roomId: Guid_2, - price: 100, - checkIn: 2023-10-01, - checkOut: 2023-10-02 - }, - eventType: V1.BookingImported - } - ], - streamPosition: 0 -} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_tResult=BookingResult.verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_tResult=BookingResult.verified.txt deleted file mode 100644 index c3dc7761f..000000000 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_tResult=BookingResult.verified.txt +++ /dev/null @@ -1,27 +0,0 @@ -{ - state: { - price: { - amount: 100, - currency: EUR - }, - amountPaid: { - amount: 0, - currency: EUR - }, - id: { - value: Guid_1 - } - }, - success: true, - changes: [ - { - event: { - roomId: Guid_2, - price: 100, - checkIn: 2023-10-01, - checkOut: 2023-10-02 - }, - eventType: V1.BookingImported - } - ] -} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_tResult=Result.verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_tResult=Result.verified.txt deleted file mode 100644 index c3dc7761f..000000000 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_tResult=Result.verified.txt +++ /dev/null @@ -1,27 +0,0 @@ -{ - state: { - price: { - amount: 100, - currency: EUR - }, - amountPaid: { - amount: 0, - currency: EUR - }, - id: { - value: Guid_1 - } - }, - success: true, - changes: [ - { - event: { - roomId: Guid_2, - price: 100, - checkIn: 2023-10-01, - checkOut: 2023-10-02 - }, - eventType: V1.BookingImported - } - ] -} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapContractToCommandExplicitly_tResult=BookingResult.verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapContractToCommandExplicitly_tResult=BookingResult.verified.txt deleted file mode 100644 index c3dc7761f..000000000 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapContractToCommandExplicitly_tResult=BookingResult.verified.txt +++ /dev/null @@ -1,27 +0,0 @@ -{ - state: { - price: { - amount: 100, - currency: EUR - }, - amountPaid: { - amount: 0, - currency: EUR - }, - id: { - value: Guid_1 - } - }, - success: true, - changes: [ - { - event: { - roomId: Guid_2, - price: 100, - checkIn: 2023-10-01, - checkOut: 2023-10-02 - }, - eventType: V1.BookingImported - } - ] -} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapContractToCommandExplicitly_tResult=Result.verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapContractToCommandExplicitly_tResult=Result.verified.txt deleted file mode 100644 index c3dc7761f..000000000 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapContractToCommandExplicitly_tResult=Result.verified.txt +++ /dev/null @@ -1,27 +0,0 @@ -{ - state: { - price: { - amount: 100, - currency: EUR - }, - amountPaid: { - amount: 0, - currency: EUR - }, - id: { - value: Guid_1 - } - }, - success: true, - changes: [ - { - event: { - roomId: Guid_2, - price: 100, - checkIn: 2023-10-01, - checkOut: 2023-10-02 - }, - eventType: V1.BookingImported - } - ] -} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapEnrichedCommand.verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapEnrichedCommand.verified.txt deleted file mode 100644 index ff1590fa7..000000000 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapEnrichedCommand.verified.txt +++ /dev/null @@ -1,28 +0,0 @@ -{ - state: { - price: { - amount: 100, - currency: EUR - }, - amountPaid: { - amount: 0, - currency: EUR - }, - id: { - value: Guid_1 - } - }, - changes: [ - { - event: { - roomId: Guid_2, - checkIn: 2023-10-01, - checkOut: 2023-10-02, - price: 100, - guestId: test guest - }, - eventType: V1.RoomBooked - } - ], - streamPosition: 0 -} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.cs b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.cs index 82b08d664..1477f4b41 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.cs +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.cs @@ -6,11 +6,9 @@ namespace Eventuous.Tests.Extensions.AspNetCore; using static SutBookingCommands; using static Fixture.TestCommands; -public class AggregateCommandsTests(ITestOutputHelper output, WebApplicationFactory factory) - : TestBaseWithLogs(output), IClassFixture> { - readonly ITestOutputHelper _output = output; - - [Fact] +[ClassDataSource>] +public class AggregateCommandsTests(WebApplicationFactory factory) : TestBaseWithLogs() { + [Test] public void RegisterAggregateCommands() { var builder = WebApplication.CreateBuilder(); @@ -21,7 +19,7 @@ public void RegisterAggregateCommands() { b.DataSources.First().Endpoints[0].DisplayName.Should().Be("HTTP: POST book"); } - [Fact] + [Test] public void RegisterAggregatesCommands() { var builder = WebApplication.CreateBuilder(); @@ -32,11 +30,10 @@ public void RegisterAggregatesCommands() { b.DataSources.First().Endpoints[0].DisplayName.Should().Be("HTTP: POST nested-book"); } - [Fact] + [Test] public void MapAggregateContractToCommandExplicitlyWithoutRouteWithWrongGenericAttr() { var act = () => new ServerFixture( factory, - _output, _ => { }, app => app .MapCommands() @@ -46,12 +43,10 @@ public void MapAggregateContractToCommandExplicitlyWithoutRouteWithWrongGenericA act.Should().Throw(); } - - [Fact] + [Test] public async Task MapAggregateContractToCommandExplicitly() { var fixture = new ServerFixture( factory, - _output, _ => { }, app => app .MapCommands() @@ -61,11 +56,10 @@ public async Task MapAggregateContractToCommandExplicitly() { await Execute(fixture, ImportRoute); } - [Fact] + [Test] public async Task MapAggregateContractToCommandExplicitlyWithoutRoute() { var fixture = new ServerFixture( factory, - _output, _ => { }, app => app .MapCommands() @@ -75,11 +69,10 @@ public async Task MapAggregateContractToCommandExplicitlyWithoutRoute() { await Execute(fixture, Import1Route); } - [Fact] + [Test] public async Task MapAggregateContractToCommandExplicitlyWithoutRouteWithGenericAttr() { var fixture = new ServerFixture( factory, - _output, _ => { }, app => app .MapCommands() @@ -89,17 +82,16 @@ public async Task MapAggregateContractToCommandExplicitlyWithoutRouteWithGeneric await Execute(fixture, Import2Route); } - [Fact] + [Test] public async Task MapEnrichedCommand() { var fixture = new ServerFixture( factory, - _output, _ => { }, app => app .MapCommands() .MapCommand((x, _) => x with { GuestId = TestData.GuestId }) ); - var cmd = fixture.GetBookRoom(); + var cmd = fixture.GetBookRoom(); var content = await fixture.ExecuteRequest(cmd, "book", cmd.BookingId); await VerifyJson(content); } diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/ControllerTests.cs b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/ControllerTests.cs index 0738ec123..d22bee757 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/ControllerTests.cs +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/ControllerTests.cs @@ -1,19 +1,19 @@ +using Eventuous.TestHelpers.TUnit; using Microsoft.AspNetCore.Mvc.Testing; using static Eventuous.Sut.App.Commands; using static Eventuous.Sut.AspNetCore.BookingApi; -using static Xunit.TestContext; namespace Eventuous.Tests.Extensions.AspNetCore; -using TestHelpers; using Fixture; using static SutBookingCommands; -public class ControllerTests : IDisposable, IClassFixture> { +[ClassDataSource>] +public class ControllerTests : IDisposable { readonly ServerFixture _fixture; readonly TestEventListener _listener; - public ControllerTests(WebApplicationFactory factory, ITestOutputHelper output) { + public ControllerTests(WebApplicationFactory factory) { var commandMap = new CommandMap() .Add( (x, ctx) => new(new(x.BookingId), x.PaymentId, new(x.Amount), x.PaidAt) { PaidBy = ctx.User.Identity?.Name } @@ -21,7 +21,6 @@ public ControllerTests(WebApplicationFactory factory, ITestOutputHelper _fixture = new( factory, - output, services => { services.AddSingleton(commandMap); services.AddControllers(); @@ -32,21 +31,21 @@ public ControllerTests(WebApplicationFactory factory, ITestOutputHelper } ); - _listener = new(output); + _listener = new(); } - [Fact] - public async Task RecordPaymentUsingMappedCommand() { + [Test] + public async Task RecordPaymentUsingMappedCommand(CancellationToken cancellationToken) { using var client = _fixture.GetClient(); var bookRoom = _fixture.GetBookRoom(); - await client.PostJsonAsync("/book", bookRoom, cancellationToken: Current.CancellationToken); + await client.PostJsonAsync("/book", bookRoom, cancellationToken: cancellationToken); var registerPayment = new RegisterPaymentHttp(bookRoom.BookingId, bookRoom.RoomId, 100, DateTimeOffset.Now); var request = new RestRequest("/v2/pay").AddJsonBody(registerPayment); - var response = await client.ExecutePostAsync.Ok>(request, cancellationToken: Current.CancellationToken); + var response = await client.ExecutePostAsync.Ok>(request, cancellationToken: cancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); var expected = new BookingEvents.BookingFullyPaid(registerPayment.PaidAt); diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.CallDiscoveredCommandRoute.verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.CallDiscoveredCommandRoute.verified.txt deleted file mode 100644 index 23b3a78cd..000000000 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.CallDiscoveredCommandRoute.verified.txt +++ /dev/null @@ -1,28 +0,0 @@ -{ - state: { - price: { - amount: 100, - currency: EUR - }, - amountPaid: { - amount: 0, - currency: EUR - }, - id: { - value: Guid_1 - } - }, - changes: [ - { - event: { - roomId: Guid_2, - checkIn: 2023-10-01, - checkOut: 2023-10-02, - price: 100, - guestId: guest - }, - eventType: V1.RoomBooked - } - ], - streamPosition: 0 -} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.cs b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.cs index 442bb9b0c..05af186aa 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.cs +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.cs @@ -6,15 +6,12 @@ namespace Eventuous.Tests.Extensions.AspNetCore; using static SutBookingCommands; using Fixture; -public class DiscoveredCommandsTests(ITestOutputHelper output, WebApplicationFactory factory) - : TestBaseWithLogs(output), IClassFixture> { - readonly ITestOutputHelper _output = output; - - [Fact] +[ClassDataSource>] +public class DiscoveredCommandsTests(WebApplicationFactory factory) : TestBaseWithLogs() { + [Test] public async Task CallDiscoveredCommandRoute() { var fixture = new ServerFixture( factory, - _output, _ => { }, app => app.MapDiscoveredCommands(typeof(NestedCommands).Assembly) ); diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj index ee86e5929..140f675d7 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj @@ -6,7 +6,7 @@ - + @@ -14,7 +14,7 @@ - + diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs index 4aff5d01a..b8cace6fb 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs @@ -3,7 +3,7 @@ using System.Text.Json; using Eventuous.TestHelpers; -using Eventuous.TestHelpers.Logging; +using Eventuous.TestHelpers.TUnit.Logging; using Microsoft.AspNetCore.Mvc.Testing; using RestSharp.Serializers.Json; @@ -16,7 +16,6 @@ public class ServerFixture { public ServerFixture( WebApplicationFactory factory, - ITestOutputHelper output, Action? register = null, ConfigureWebApplication? configure = null ) { @@ -30,7 +29,7 @@ public ServerFixture( if (configure != null) services.AddSingleton(configure); } ) - .ConfigureLogging(x => x.AddXUnit(output).AddConsole().SetMinimumLevel(LogLevel.Debug)); + .ConfigureLogging(x => x.AddTUnit().AddConsole().SetMinimumLevel(LogLevel.Debug)); } ); builder.Server.PreserveExecutionContext = false; diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/TestBaseWithLogs.cs b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/TestBaseWithLogs.cs index 76dd212e4..d42901083 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/TestBaseWithLogs.cs +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/TestBaseWithLogs.cs @@ -1,9 +1,9 @@ -using Eventuous.TestHelpers; +using Eventuous.TestHelpers.TUnit; namespace Eventuous.Tests.Extensions.AspNetCore.Fixture; -public abstract class TestBaseWithLogs(ITestOutputHelper output) : IDisposable { - readonly TestEventListener _listener = new(output); +public abstract class TestBaseWithLogs : IDisposable { + readonly TestEventListener _listener = new(); public void Dispose() => _listener.Dispose(); } diff --git a/test/Eventuous.TestHelpers.XUnit/Eventuous.TestHelpers.XUnit.csproj b/test/Eventuous.TestHelpers.XUnit/Eventuous.TestHelpers.XUnit.csproj deleted file mode 100644 index 7716080ef..000000000 --- a/test/Eventuous.TestHelpers.XUnit/Eventuous.TestHelpers.XUnit.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - false - - - - - - - - - - diff --git a/test/Eventuous.TestHelpers.XUnit/Logging/LoggingExtensions.cs b/test/Eventuous.TestHelpers.XUnit/Logging/LoggingExtensions.cs deleted file mode 100644 index 45d67efe2..000000000 --- a/test/Eventuous.TestHelpers.XUnit/Logging/LoggingExtensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Eventuous.TestHelpers.Logging; - -public static class LoggingExtensions { - public static ILoggerFactory GetLoggerFactory(ITestOutputHelper outputHelper, LogLevel logLevel = LogLevel.Debug) - => LoggerFactory.Create( - builder => builder - .SetMinimumLevel(logLevel) - .AddFilter("Microsoft", LogLevel.Warning) - .AddFilter("Grpc.Net.Client", LogLevel.Warning) - .AddXUnit(outputHelper) - ); - - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, XUnitLoggerOptions? options = null) { - factory.AddProvider(new XUnitLoggerProvider(outputHelper, options)); - - return factory; - } - - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper, XUnitLoggerOptions? options = null) - => builder.AddProvider(new XUnitLoggerProvider(outputHelper, options)); -} - -public sealed class XUnitLoggerProvider(ITestOutputHelper testOutputHelper, XUnitLoggerOptions? options = null) : ILoggerProvider { - private readonly XUnitLoggerOptions _options = options ?? new XUnitLoggerOptions(); - private readonly LoggerExternalScopeProvider _scopeProvider = new(); - - public XUnitLoggerProvider(ITestOutputHelper testOutputHelper, bool appendScope) : this(testOutputHelper, new XUnitLoggerOptions { IncludeScopes = appendScope }) { } - - public ILogger CreateLogger(string categoryName) => new XUnitLogger(testOutputHelper, _scopeProvider, categoryName, _options); - - public void Dispose() { } -} diff --git a/test/Eventuous.TestHelpers.XUnit/Logging/xUnitLogger.cs b/test/Eventuous.TestHelpers.XUnit/Logging/xUnitLogger.cs deleted file mode 100644 index 24045c152..000000000 --- a/test/Eventuous.TestHelpers.XUnit/Logging/xUnitLogger.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) Ubiquitous AS.All rights reserved -// Licensed under the Apache License, Version 2.0. - -using System.Text; -using Microsoft.Extensions.Logging; - -namespace Eventuous.TestHelpers.Logging; - -public sealed class XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider) : XUnitLogger(testOutputHelper, scopeProvider, typeof(T).FullName), ILogger; - -public class XUnitLogger : ILogger { - private readonly ITestOutputHelper _testOutputHelper; - private readonly string? _categoryName; - private readonly XUnitLoggerOptions _options; - private readonly LoggerExternalScopeProvider _scopeProvider; - - public static ILogger CreateLogger(ITestOutputHelper testOutputHelper) => new XUnitLogger(testOutputHelper, new(), ""); - - public static ILogger CreateLogger(ITestOutputHelper testOutputHelper) => new XUnitLogger(testOutputHelper, new()); - - public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider, string? categoryName, bool appendScope = true) - : this(testOutputHelper, scopeProvider, categoryName, options: new() { IncludeScopes = appendScope }) { } - - public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider, string? categoryName, XUnitLoggerOptions? options) { - _testOutputHelper = testOutputHelper; - _scopeProvider = scopeProvider; - _categoryName = categoryName; - _options = options ?? new(); - } - - public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None; - - public IDisposable? BeginScope(TState state) where TState : notnull => _scopeProvider.Push(state); - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - var sb = new StringBuilder(); - - if (_options.TimestampFormat is not null) { - var now = _options.UseUtcTimestamp ? DateTimeOffset.UtcNow : DateTimeOffset.Now; - var timestamp = now.ToString(_options.TimestampFormat); - sb.Append(timestamp).Append(' '); - } - - if (_options.IncludeLogLevel) { - sb.Append(GetLogLevelString(logLevel)).Append(' '); - } - - if (_options.IncludeCategory) { - sb.Append('[').Append(_categoryName).Append("] "); - } - - sb.Append(formatter(state, exception)); - - if (exception is not null) { - sb.Append('\n').Append(exception); - } - - // Append scopes - if (_options.IncludeScopes) { - _scopeProvider.ForEachScope( - (scope, s) => { - s.Append("\n => "); - s.Append(scope); - }, - sb - ); - } - - try { - _testOutputHelper.WriteLine(sb.ToString()); - } catch { - // This can happen when the test is not active - } - } - - private static string GetLogLevelString(LogLevel logLevel) => logLevel switch { - LogLevel.Trace => "trce", - LogLevel.Debug => "dbug", - LogLevel.Information => "info", - LogLevel.Warning => "warn", - LogLevel.Error => "fail", - LogLevel.Critical => "crit", - _ => throw new ArgumentOutOfRangeException(nameof(logLevel)) - }; -} diff --git a/test/Eventuous.TestHelpers.XUnit/Logging/xUnitLoggerOptions.cs b/test/Eventuous.TestHelpers.XUnit/Logging/xUnitLoggerOptions.cs deleted file mode 100644 index b3f008811..000000000 --- a/test/Eventuous.TestHelpers.XUnit/Logging/xUnitLoggerOptions.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) Ubiquitous AS.All rights reserved -// Licensed under the Apache License, Version 2.0. - -namespace Eventuous.TestHelpers.Logging; - -public record XUnitLoggerOptions { - /// - /// Includes scopes when . - /// - public bool IncludeScopes { get; set; } - - /// - /// Includes category when . - /// - public bool IncludeCategory { get; set; } = true; - - /// - /// Includes log level when . - /// - public bool IncludeLogLevel { get; set; } = true; - - /// - /// Gets or sets format string used to format timestamp in logging messages. Defaults to . - /// - public string? TimestampFormat { get; set; } - - /// - /// Gets or sets indication whether UTC timezone should be used to format timestamps in logging messages. Defaults to . - /// - public bool UseUtcTimestamp { get; set; } -} diff --git a/test/Eventuous.TestHelpers.XUnit/TestEventListener.cs b/test/Eventuous.TestHelpers.XUnit/TestEventListener.cs deleted file mode 100644 index 9732199d9..000000000 --- a/test/Eventuous.TestHelpers.XUnit/TestEventListener.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Diagnostics.Tracing; - -namespace Eventuous.TestHelpers; - -public sealed class TestEventListener(ITestOutputHelper outputHelper, Action? act = null, params string[] prefixes) : EventListener { - readonly string[] _prefixes = prefixes.Length > 0 ? prefixes : ["OpenTelemetry", "eventuous"]; - readonly List _eventSources = []; - - protected override void OnEventSourceCreated(EventSource? eventSource) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (_prefixes == null || eventSource?.Name == null) { - return; - } - - if (_prefixes.Any(x => eventSource.Name.StartsWith(x))) { - _eventSources.Add(eventSource); - EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All); - } - - base.OnEventSourceCreated(eventSource); - } - -#nullable disable - protected override void OnEventWritten(EventWrittenEventArgs evt) { - var message = evt.Message != null && (evt.Payload?.Count ?? 0) > 0 ? string.Format(evt.Message, evt.Payload.ToArray()) : evt.Message; - outputHelper.WriteLine($"{evt.EventSource.Name} - EventId: [{evt.EventId}], EventName: [{evt.EventName}], Message: [{message}]"); - act?.Invoke(evt); - } -#nullable enable - - public override void Dispose() { - foreach (var eventSource in _eventSources) { - DisableEvents(eventSource); - } - - base.Dispose(); - } -} From 8448ac8d288be195c3ddc6a465ac21bb2bb6e219 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Wed, 6 Nov 2024 18:13:53 +0100 Subject: [PATCH 11/17] Remove matrix --- .github/workflows/pull-request.yml | 11 ++++---- ...ApplicationFactory`1[Program].verified.txt | 27 ++++++++++++++++++ ...ApplicationFactory`1[Program].verified.txt | 27 ++++++++++++++++++ ...ApplicationFactory`1[Program].verified.txt | 27 ++++++++++++++++++ ...ApplicationFactory`1[Program].verified.txt | 28 +++++++++++++++++++ ...ApplicationFactory`1[Program].verified.txt | 28 +++++++++++++++++++ 6 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRouteWithGenericAttr_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt create mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRoute_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt create mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt create mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapEnrichedCommand_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt create mode 100644 src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.CallDiscoveredCommandRoute_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index eb2d4b862..8e8fb4d23 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -18,9 +18,6 @@ jobs: build-and-test: name: "Build and test" runs-on: ubuntu-latest - strategy: - matrix: - dotnet: [ 'net9.0', 'net8.0' ] env: NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages TC_CLOUD_TOKEN: ${{ secrets.TC_TOKEN }} @@ -32,12 +29,14 @@ jobs: name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: '9.0' + dotnet-version: | + '8.0' + '9.0' dotnet-quality: 'preview' - name: Build run: | - dotnet build -c "Debug CI" -f ${{ matrix.dotnet }} + dotnet build -c "Debug CI" - name: Prepare Testcontainers Cloud agent if: env.TC_CLOUD_TOKEN != '' @@ -45,7 +44,7 @@ jobs: - name: Run tests run: | - dotnet test -c "Debug CI" --no-build -f ${{ matrix.dotnet }} + dotnet test -c "Debug CI" --no-build - name: Upload Test Results if: always() diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRouteWithGenericAttr_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRouteWithGenericAttr_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt new file mode 100644 index 000000000..94228fd7f --- /dev/null +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRouteWithGenericAttr_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt @@ -0,0 +1,27 @@ +{ + state: { + price: { + amount: 100, + currency: EUR + }, + amountPaid: { + amount: 0, + currency: EUR + }, + id: { + value: Guid_1 + } + }, + changes: [ + { + event: { + roomId: Guid_2, + price: 100, + checkIn: 2023-10-01, + checkOut: 2023-10-02 + }, + eventType: V1.BookingImported + } + ], + streamPosition: 0 +} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRoute_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRoute_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt new file mode 100644 index 000000000..94228fd7f --- /dev/null +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitlyWithoutRoute_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt @@ -0,0 +1,27 @@ +{ + state: { + price: { + amount: 100, + currency: EUR + }, + amountPaid: { + amount: 0, + currency: EUR + }, + id: { + value: Guid_1 + } + }, + changes: [ + { + event: { + roomId: Guid_2, + price: 100, + checkIn: 2023-10-01, + checkOut: 2023-10-02 + }, + eventType: V1.BookingImported + } + ], + streamPosition: 0 +} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt new file mode 100644 index 000000000..94228fd7f --- /dev/null +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapAggregateContractToCommandExplicitly_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt @@ -0,0 +1,27 @@ +{ + state: { + price: { + amount: 100, + currency: EUR + }, + amountPaid: { + amount: 0, + currency: EUR + }, + id: { + value: Guid_1 + } + }, + changes: [ + { + event: { + roomId: Guid_2, + price: 100, + checkIn: 2023-10-01, + checkOut: 2023-10-02 + }, + eventType: V1.BookingImported + } + ], + streamPosition: 0 +} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapEnrichedCommand_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapEnrichedCommand_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt new file mode 100644 index 000000000..ff1590fa7 --- /dev/null +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/AggregateCommandsTests.MapEnrichedCommand_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt @@ -0,0 +1,28 @@ +{ + state: { + price: { + amount: 100, + currency: EUR + }, + amountPaid: { + amount: 0, + currency: EUR + }, + id: { + value: Guid_1 + } + }, + changes: [ + { + event: { + roomId: Guid_2, + checkIn: 2023-10-01, + checkOut: 2023-10-02, + price: 100, + guestId: test guest + }, + eventType: V1.RoomBooked + } + ], + streamPosition: 0 +} \ No newline at end of file diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.CallDiscoveredCommandRoute_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.CallDiscoveredCommandRoute_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt new file mode 100644 index 000000000..23b3a78cd --- /dev/null +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/DiscoveredCommandsTests.CallDiscoveredCommandRoute_factory=Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1[Program].verified.txt @@ -0,0 +1,28 @@ +{ + state: { + price: { + amount: 100, + currency: EUR + }, + amountPaid: { + amount: 0, + currency: EUR + }, + id: { + value: Guid_1 + } + }, + changes: [ + { + event: { + roomId: Guid_2, + checkIn: 2023-10-01, + checkOut: 2023-10-02, + price: 100, + guestId: guest + }, + eventType: V1.RoomBooked + } + ], + streamPosition: 0 +} \ No newline at end of file From 8a146e7771e4b2bef95e5e58eb2590ccafabd08e Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Wed, 6 Nov 2024 18:16:22 +0100 Subject: [PATCH 12/17] Fix the version --- .github/workflows/pull-request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 8e8fb4d23..687ffcdaa 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -30,8 +30,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - '8.0' - '9.0' + '8.0.X' + '9.0.X' dotnet-quality: 'preview' - name: Build From 72b397768f039fef21c630a14db5a6060e41b866 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Wed, 6 Nov 2024 18:35:00 +0100 Subject: [PATCH 13/17] Fix the version AGAIN --- .github/workflows/pull-request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 687ffcdaa..01831f7e9 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -30,8 +30,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - '8.0.X' - '9.0.X' + '8.0.x' + '9.0.x' dotnet-quality: 'preview' - name: Build From c55b9d4c0c3be6436c7effc72a3422a8cd716462 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Wed, 6 Nov 2024 18:36:32 +0100 Subject: [PATCH 14/17] One more try to fix the version --- .github/workflows/pull-request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 01831f7e9..91de2b1d8 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -30,8 +30,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - '8.0.x' - '9.0.x' + 8.0.x + 9.0.x dotnet-quality: 'preview' - name: Build From bdc67b4e37bcd5f26c480610d894014f2c01042b Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Thu, 7 Nov 2024 15:17:17 +0100 Subject: [PATCH 15/17] Improving logging for tests --- .../Fixtures/StoreFixtureBase.cs | 3 +-- .../Fixtures/SubscriptionFixtureBase.cs | 2 +- .../Store/AggregateStoreTests.cs | 4 ++-- .../Store/EventStoreAggregateTests.cs | 4 ++-- .../Subscriptions/StreamSubscriptionDeletedEventsTests.cs | 4 ++-- .../Fixture/ServerFixture.cs | 2 +- .../test/Eventuous.Tests.GooglePubSub/PubSubTests.cs | 2 +- .../ProjectionTestBase.cs | 7 ++----- .../test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs | 5 ++--- .../Logging/LoggingExtensions.cs | 5 ++++- 10 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs index 172b19bcc..1cf669f01 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs @@ -7,7 +7,6 @@ using MicroElements.AutoFixture.NodaTime; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; using TUnit.Core.Interfaces; namespace Eventuous.Tests.Persistence.Base.Fixtures; @@ -28,7 +27,7 @@ public virtual async Task InitializeAsync() { var services = new ServiceCollection(); - services.AddLogging(cfg => cfg.AddTUnit().SetMinimumLevel(LogLevel.Debug)); + services.AddLogging(cfg => cfg.ForTests()); Serializer = new DefaultEventSerializer(TestPrimitives.DefaultOptions, TypeMapper); services.AddSingleton(Serializer); diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs index 37b905c61..c5f936653 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs @@ -64,7 +64,7 @@ protected override void SetupServices(IServiceCollection services) { var host = services.First(x => !x.IsKeyedService && x.ImplementationFactory?.GetType() == typeof(Func)); services.Remove(host); - services.AddLogging(b => ConfigureLogging(b.AddTUnit().SetMinimumLevel(_logLevel))); + services.AddLogging(b => ConfigureLogging(b.ForTests(_logLevel))); } protected override void GetDependencies(IServiceProvider provider) { diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs index fbc38e961..b574f4102 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Store/AggregateStoreTests.cs @@ -1,7 +1,7 @@ using System.Collections.Immutable; -using Eventuous.TestHelpers.TUnit.Logging; using JetBrains.Annotations; using static Eventuous.AggregateFactoryRegistry; +using LoggingExtensions = Eventuous.TestHelpers.TUnit.Logging.LoggingExtensions; namespace Eventuous.Tests.EventStore.Store; @@ -13,7 +13,7 @@ public class AggregateStoreTests { public AggregateStoreTests(StoreFixture fixture) { _fixture = fixture; _fixture.TypeMapper.AddType("testAggregateEvent"); - var loggerFactory = LoggerFactory.Create(cfg => cfg.AddTUnit().SetMinimumLevel(LogLevel.Debug)); + var loggerFactory = LoggingExtensions.GetLoggerFactory(); _log = loggerFactory.CreateLogger(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs index 99f4cf091..6ff7af8c2 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Store/EventStoreAggregateTests.cs @@ -1,7 +1,7 @@ using System.Collections.Immutable; -using Eventuous.TestHelpers.TUnit.Logging; using JetBrains.Annotations; using static Eventuous.AggregateFactoryRegistry; +using LoggingExtensions = Eventuous.TestHelpers.TUnit.Logging.LoggingExtensions; namespace Eventuous.Tests.EventStore.Store; @@ -14,7 +14,7 @@ public class EventStoreAggregateTests { public EventStoreAggregateTests(StoreFixture fixture) { _fixture = fixture; _fixture.TypeMapper.AddType("testEvent"); - _loggerFactory = LoggerFactory.Create(cfg => cfg.AddTUnit().SetMinimumLevel(LogLevel.Debug)); + _loggerFactory = LoggingExtensions.GetLoggerFactory(); _log = _loggerFactory.CreateLogger(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs index ebfa5e231..b027824aa 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Subscriptions/StreamSubscriptionDeletedEventsTests.cs @@ -6,8 +6,8 @@ using Eventuous.Subscriptions.Filters; using Eventuous.Sut.App; using Eventuous.Sut.Domain; -using Eventuous.TestHelpers.TUnit.Logging; using Eventuous.Tests.Subscriptions.Base; +using LoggingExtensions = Eventuous.TestHelpers.TUnit.Logging.LoggingExtensions; using StreamSubscription = Eventuous.EventStore.Subscriptions.StreamSubscription; namespace Eventuous.Tests.EventStore.Subscriptions; @@ -19,7 +19,7 @@ public sealed class StreamSubscriptionDeletedEventsTests { public StreamSubscriptionDeletedEventsTests() { _fixture = new(); - _loggerFactory = LoggerFactory.Create(cfg => cfg.AddTUnit().AddConsole().SetMinimumLevel(LogLevel.Debug)); + _loggerFactory = LoggingExtensions.GetLoggerFactory(); _listener = new(_loggerFactory); _fixture.TypeMapper.RegisterKnownEventTypes(typeof(BookingEvents.BookingImported).Assembly); } diff --git a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs index b8cace6fb..13ac44e5e 100644 --- a/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs +++ b/src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Fixture/ServerFixture.cs @@ -29,7 +29,7 @@ public ServerFixture( if (configure != null) services.AddSingleton(configure); } ) - .ConfigureLogging(x => x.AddTUnit().AddConsole().SetMinimumLevel(LogLevel.Debug)); + .ConfigureLogging(x => x.ForTests()); } ); builder.Server.PreserveExecutionContext = false; diff --git a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs index 136ed9db9..988c7a28a 100644 --- a/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs +++ b/src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/PubSubTests.cs @@ -23,7 +23,7 @@ public class PubSubTests { // ReSharper disable once UnusedParameter.Local public PubSubTests(PubSubFixture _) { - var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddTUnit()); + var loggerFactory = LoggingExtensions.GetLoggerFactory(); _log = loggerFactory.CreateLogger(); _pubsubTopic = new($"test-{Guid.NewGuid():N}"); diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs index 8d4cab5c8..781624644 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs @@ -15,10 +15,8 @@ public abstract class ProjectionTestBase { readonly IHostBuilder _builder; protected ProjectionTestBase(string id) { - _id = id; - - _builder = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder() - .ConfigureLogging(cfg => cfg.AddTUnit().SetMinimumLevel(LogLevel.Trace)); + _id = id; + _builder = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().ConfigureLogging(cfg => cfg.ForTests()); } protected abstract void ConfigureServices(IServiceCollection services, string id); @@ -26,7 +24,6 @@ protected ProjectionTestBase(string id) { [Before(Test)] public async Task InitializeAsync() { _builder.ConfigureServices(collection => ConfigureServices(collection, _id)); - Host = _builder.Build(); Host.Services.AddEventuousLogs(); await Host.StartAsync(); diff --git a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs index 120a2d8ec..dc47f90b0 100644 --- a/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs +++ b/src/RabbitMq/test/Eventuous.Tests.RabbitMq/SubscriptionSpec.cs @@ -27,9 +27,8 @@ public SubscriptionSpec(RabbitMqFixture fixture) { _fixture = fixture; _es = new(); _exchange = new(Auto.Create()); - _loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug).AddTUnit()); - - _log = _loggerFactory.CreateLogger(); + _loggerFactory = LoggingExtensions.GetLoggerFactory(); + _log = _loggerFactory.CreateLogger(); } [Test] diff --git a/test/Eventuous.TestHelpers.TUnit/Logging/LoggingExtensions.cs b/test/Eventuous.TestHelpers.TUnit/Logging/LoggingExtensions.cs index e9d6feeb7..bde6fc790 100644 --- a/test/Eventuous.TestHelpers.TUnit/Logging/LoggingExtensions.cs +++ b/test/Eventuous.TestHelpers.TUnit/Logging/LoggingExtensions.cs @@ -8,7 +8,7 @@ public static ILoggerFactory GetLoggerFactory(LogLevel logLevel = LogLevel.Debug builder => builder .SetMinimumLevel(logLevel) .AddFilter("Microsoft", LogLevel.Warning) - .AddFilter("Grpc.Net.Client", LogLevel.Warning) + .AddFilter("Grpc", LogLevel.Warning) .AddTUnit() ); @@ -19,6 +19,9 @@ public static ILoggerFactory AddTUnit(this ILoggerFactory factory) { } public static ILoggingBuilder AddTUnit(this ILoggingBuilder builder) => builder.AddProvider(new TUnitLoggerProvider()); + + public static ILoggingBuilder ForTests(this ILoggingBuilder builder, LogLevel logLevel = LogLevel.Debug) + => builder.AddTUnit().SetMinimumLevel(logLevel).AddFilter("Grpc", LogLevel.Warning).AddFilter("Microsoft", LogLevel.Warning); } public sealed class TUnitLoggerProvider() : ILoggerProvider { From 8d965f9f1e347e90fa21bacfbae0c8095345108b Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Thu, 7 Nov 2024 15:20:00 +0100 Subject: [PATCH 16/17] Retry Redis tests --- .../Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs | 5 ++++- .../Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs index b1ad3fab8..e2b89fffd 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToAll.cs @@ -6,7 +6,7 @@ namespace Eventuous.Tests.Redis.Subscriptions; -public class SubscribeToAll() { +public class SubscribeToAll { SubscriptionFixture _fixture = null!; [Before(Test)] @@ -21,6 +21,7 @@ public async Task TearDown() { } [Test] + [Retry(5)] public async Task ShouldConsumeProducedEvents(CancellationToken cancellationToken) { const int count = 10; @@ -34,6 +35,7 @@ public async Task ShouldConsumeProducedEvents(CancellationToken cancellationToke } [Test] + [Retry(5)] public async Task ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { await TestConsumptionOfProducedEvents(); @@ -59,6 +61,7 @@ async Task TestConsumptionOfProducedEvents() { } [Test] + [Retry(5)] public async Task ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { const int count = 10; diff --git a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs index b34c8cba7..40453fc0b 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Subscriptions/SubscribeToStream.cs @@ -21,6 +21,7 @@ public async Task TearDown() { } [Test] + [Retry(5)] public async Task ShouldConsumeProducedEvents(CancellationToken cancellationToken) { const int count = 10; @@ -34,6 +35,7 @@ public async Task ShouldConsumeProducedEvents(CancellationToken cancellationToke } [Test] + [Retry(5)] public async Task ShouldConsumeProducedEventsWhenRestarting(CancellationToken cancellationToken) { await TestConsumptionOfProducedEvents(); @@ -58,6 +60,7 @@ async Task TestConsumptionOfProducedEvents() { } [Test] + [Retry(5)] public async Task ShouldUseExistingCheckpoint(CancellationToken cancellationToken) { const int count = 10; From 84f39c5ce337ea9b3beabb9fbae4551aa0d2b7ad Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Thu, 7 Nov 2024 16:31:29 +0100 Subject: [PATCH 17/17] Streamline metrics tests --- .../Fixtures/StoreFixtureBase.cs | 4 +- .../Fixtures/SubscriptionFixtureBase.cs | 3 -- .../SubscriptionTestBase.cs | 2 + .../MetricsSubscriptionFixtureBase.cs | 20 +++++++-- .../MetricsTests.cs | 43 ++++++++----------- .../Metrics/MetricsTests.cs | 19 ++------ .../Metrics/MetricsFixture.cs | 3 +- .../Metrics/MetricsTests.cs | 18 ++------ .../Metrics/MetricsTests.cs | 18 ++------ 9 files changed, 49 insertions(+), 81 deletions(-) diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs index 1cf669f01..2769af31b 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/StoreFixtureBase.cs @@ -11,6 +11,8 @@ namespace Eventuous.Tests.Persistence.Base.Fixtures; +public interface IStartableFixture : IAsyncInitializer, IAsyncDisposable; + public abstract class StoreFixtureBase { public IEventStore EventStore { get; protected private set; } = null!; public IFixture Auto { get; } = new Fixture().Customize(new NodaTimeCustomization()); @@ -20,7 +22,7 @@ public abstract class StoreFixtureBase { public TypeMapper TypeMapper { get; } = new(); } -public abstract partial class StoreFixtureBase : StoreFixtureBase, IAsyncInitializer, IAsyncDisposable where TContainer : DockerContainer { +public abstract partial class StoreFixtureBase : StoreFixtureBase, IStartableFixture where TContainer : DockerContainer { public virtual async Task InitializeAsync() { Container = CreateContainer(); await Container.StartAsync(); diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs index c5f936653..96f34f43c 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionFixtureBase.cs @@ -7,14 +7,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using TUnit.Core.Interfaces; using ILogger = Microsoft.Extensions.Logging.ILogger; using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace Eventuous.Tests.Subscriptions.Base; -public interface IStartableFixture : IAsyncInitializer, IAsyncDisposable; - public abstract class SubscriptionFixtureBase : StoreFixtureBase, IStartableFixture where TEventHandler : class, IEventHandler diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscriptionTestBase.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscriptionTestBase.cs index 6b778a758..2d0fe3894 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscriptionTestBase.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/SubscriptionTestBase.cs @@ -1,3 +1,5 @@ +using Eventuous.Tests.Persistence.Base.Fixtures; + namespace Eventuous.Tests.Subscriptions.Base; public abstract class SubscriptionTestBase(IStartableFixture fixture) { diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs index 94b0cfc8d..12e8d36ee 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/Fixtures/MetricsSubscriptionFixtureBase.cs @@ -8,13 +8,25 @@ namespace Eventuous.Tests.OpenTelemetry.Fixtures; -public abstract class MetricsSubscriptionFixtureBase : StoreFixtureBase +public interface IMetricsSubscriptionFixtureBase { + StreamName Stream { get; } + MessageCounter Counter { get; } + TestExporter Exporter { get; } + int Count { get; } + public string DefaultTagKey { get; } + public string DefaultTagValue { get; } + string SubscriptionId { get; } + IProducer Producer { get; } + IFixture Auto { get; } +} + +public abstract class MetricsSubscriptionFixtureBase : StoreFixtureBase, IMetricsSubscriptionFixtureBase where TContainer : DockerContainer where TProducer : class, IProducer where TSubscription : EventSubscriptionWithCheckpoint, IMeasuredSubscription where TSubscriptionOptions : SubscriptionWithCheckpointOptions { // ReSharper disable once ConvertToConstant.Global - public readonly int Count = 100; + public int Count => 100; // ReSharper disable once StaticMemberInGenericType static readonly KeyValuePair DefaultTag = new("test", "foo"); @@ -32,7 +44,7 @@ static MetricsSubscriptionFixtureBase() { public string DefaultTagValue => DefaultTag.Value; // ReSharper disable once ConvertToConstant.Global - public readonly string SubscriptionId = "test-sub"; + public string SubscriptionId => "test-sub"; TestListener? _listener; @@ -61,7 +73,7 @@ protected override void GetDependencies(IServiceProvider provider) { Counter = provider.GetRequiredService(); } - public TProducer Producer { get; private set; } = null!; + public IProducer Producer { get; private set; } = null!; public MessageCounter Counter { get; private set; } = null!; public TestExporter Exporter { get; } = new(); diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs index 751529e71..5bf7a10f9 100644 --- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs +++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs @@ -1,32 +1,25 @@ -using DotNet.Testcontainers.Containers; using Eventuous.TestHelpers.TUnit; using Eventuous.Tests.OpenTelemetry.Fakes; using Eventuous.Tests.Subscriptions.Base; -using Assert = TUnit.Assertions.Assert; -// ReSharper disable MethodHasAsyncOverload +// ReSharper disable MethodHasAsyncOverload // ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract namespace Eventuous.Tests.OpenTelemetry; -public abstract class MetricsTestsBase - where T : MetricsSubscriptionFixtureBase, new() - where TContainer : DockerContainer - where TProducer : class, IProducer - where TSubscription : EventSubscriptionWithCheckpoint, IMeasuredSubscription - where TSubscriptionOptions : SubscriptionWithCheckpointOptions { - T Fixture { get; } = new(); - - protected async Task ShouldMeasureSubscriptionGapCountBase() { - TestContext.Current?.OutputWriter.WriteLine($"Stream {Fixture.Stream}"); +public abstract class MetricsTestsBase(IMetricsSubscriptionFixtureBase fixture) { + [Test] + [Retry(3)] + public async Task ShouldMeasureSubscriptionGapCountBase() { + TestContext.Current?.OutputWriter.WriteLine($"Stream {fixture.Stream}"); await Assert.That(_values).IsNotNull(); var gapCount = GetValue(_values!, SubscriptionMetrics.GapCountMetricName)!; - var expectedGap = Fixture.Count - Fixture.Counter.Count; + var expectedGap = fixture.Count - fixture.Counter.Count; gapCount.Should().NotBeNull(); gapCount.Value.Should().BeInRange(expectedGap - 20, expectedGap + 20); - gapCount.CheckTag(SubscriptionMetrics.SubscriptionIdTag, Fixture.SubscriptionId); - gapCount.CheckTag(Fixture.DefaultTagKey, Fixture.DefaultTagValue); + gapCount.CheckTag(SubscriptionMetrics.SubscriptionIdTag, fixture.SubscriptionId); + gapCount.CheckTag(fixture.DefaultTagKey, fixture.DefaultTagValue); } // [Fact] @@ -45,25 +38,25 @@ protected async Task ShouldMeasureSubscriptionGapCountBase() { static MetricValue? GetValue(MetricValue[] values, string metric) => values.FirstOrDefault(x => x.Name == metric); - public async ValueTask InitializeAsync() { - await Fixture.InitializeAsync(); - var testEvents = Fixture.Auto.CreateMany(Fixture.Count).ToList(); - await Fixture.Producer.Produce(Fixture.Stream, testEvents, new Metadata()); + [Before(Test)] + public async Task InitializeAsync() { + var testEvents = fixture.Auto.CreateMany(fixture.Count).ToList(); + await fixture.Producer.Produce(fixture.Stream, testEvents, new Metadata()); - while (Fixture.Counter.Count < Fixture.Count / 2) { + while (fixture.Counter.Count < fixture.Count / 2) { await Task.Delay(100); } - Fixture.Exporter.Collect(Timeout.Infinite); - _values = Fixture.Exporter.CollectValues(); + fixture.Exporter.Collect(Timeout.Infinite); + _values = fixture.Exporter.CollectValues(); foreach (var value in _values) { TestContext.Current?.OutputWriter.WriteLine(value.ToString()); } } - public async ValueTask DisposeAsync() { - await Fixture.DisposeAsync(); + [After(Test)] + public void Teardown() { _es.Dispose(); } diff --git a/src/EventStore/test/Eventuous.Tests.EventStore/Metrics/MetricsTests.cs b/src/EventStore/test/Eventuous.Tests.EventStore/Metrics/MetricsTests.cs index 7ad238440..ac1415af3 100644 --- a/src/EventStore/test/Eventuous.Tests.EventStore/Metrics/MetricsTests.cs +++ b/src/EventStore/test/Eventuous.Tests.EventStore/Metrics/MetricsTests.cs @@ -1,20 +1,7 @@ -using Eventuous.EventStore.Producers; -using Eventuous.EventStore.Subscriptions; using Eventuous.Tests.OpenTelemetry; -using Testcontainers.EventStoreDb; -// ReSharper disable UnusedType.Global namespace Eventuous.Tests.EventStore.Metrics; -public class MetricsTests : MetricsTestsBase { - [Test] - public async Task ShouldMeasureSubscriptionGapCount() { - await ShouldMeasureSubscriptionGapCountBase(); - } - - [Before(Test)] - public async Task Setup() => await InitializeAsync(); - - [After(Test)] - public async Task TearDown() => await DisposeAsync(); -} +[ClassDataSource] +[InheritsTests] +public class MetricsTests(MetricsFixture fixture) : MetricsTestsBase(fixture); diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsFixture.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsFixture.cs index 912e2c5d4..7f8e426b2 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsFixture.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsFixture.cs @@ -8,8 +8,7 @@ namespace Eventuous.Tests.Postgres.Metrics; -public class MetricsFixture - : MetricsSubscriptionFixtureBase { +public class MetricsFixture : MetricsSubscriptionFixtureBase { readonly string _schemaName = GetSchemaName(); protected override PostgreSqlContainer CreateContainer() => PostgresContainer.Create(); diff --git a/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsTests.cs b/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsTests.cs index cde246904..b7553cf13 100644 --- a/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsTests.cs +++ b/src/Postgres/test/Eventuous.Tests.Postgres/Metrics/MetricsTests.cs @@ -1,22 +1,10 @@ -using Eventuous.Postgresql.Subscriptions; -using Eventuous.Sql.Base.Producers; using Eventuous.Tests.OpenTelemetry; -using Testcontainers.PostgreSql; // ReSharper disable UnusedType.Global namespace Eventuous.Tests.Postgres.Metrics; -public class MetricsTests : MetricsTestsBase { - [Test] - public async Task ShouldMeasureSubscriptionGapCount() { - await ShouldMeasureSubscriptionGapCountBase(); - } - - [Before(Test)] - public async Task Setup() => await InitializeAsync(); - - [After(Test)] - public async Task TearDown() => await DisposeAsync(); -} +[ClassDataSource] +[InheritsTests] +public class MetricsTests(MetricsFixture fixture) : MetricsTestsBase(fixture); diff --git a/src/SqlServer/test/Eventuous.Tests.SqlServer/Metrics/MetricsTests.cs b/src/SqlServer/test/Eventuous.Tests.SqlServer/Metrics/MetricsTests.cs index ffd698ff4..8eb321205 100644 --- a/src/SqlServer/test/Eventuous.Tests.SqlServer/Metrics/MetricsTests.cs +++ b/src/SqlServer/test/Eventuous.Tests.SqlServer/Metrics/MetricsTests.cs @@ -1,21 +1,9 @@ -using Eventuous.Sql.Base.Producers; -using Eventuous.SqlServer.Subscriptions; using Eventuous.Tests.OpenTelemetry; -using Testcontainers.SqlEdge; // ReSharper disable UnusedType.Global namespace Eventuous.Tests.SqlServer.Metrics; -public class MetricsTests : MetricsTestsBase { - [Test] - public async Task ShouldMeasureSubscriptionGapCount() { - await ShouldMeasureSubscriptionGapCountBase(); - } - - [Before(Test)] - public async Task Setup() => await InitializeAsync(); - - [After(Test)] - public async Task TearDown() => await DisposeAsync(); -} +[ClassDataSource] +[InheritsTests] +public class MetricsTests(MetricsFixture fixture) : MetricsTestsBase(fixture);