diff --git a/.bot/README.md b/.bot/README.md index f7e13f00..9f9afdab 100644 --- a/.bot/README.md +++ b/.bot/README.md @@ -7,4 +7,4 @@ This folder is reserved for local-only AI working material such as: - design alternatives - temporary agent state -Keep this folder out of source control. Move only finalized, non-confidential guidance into `AGENTS.md`. +The `.bot/README.md` file itself is intentionally tracked in source control. All other `.bot/` working material remains local-only and excluded from source control. Move only finalized, non-confidential guidance into `AGENTS.md`. diff --git a/.docfx/BuildDocfxImage.ps1 b/.docfx/BuildDocfxImage.ps1 index 5d9f365a..861021cd 100644 --- a/.docfx/BuildDocfxImage.ps1 +++ b/.docfx/BuildDocfxImage.ps1 @@ -1,4 +1,4 @@ -[CmdletBinding()] +[CmdletBinding()] param() $ErrorActionPreference = 'Stop' diff --git a/.docfx/api/dotnet/index.md b/.docfx/api/dotnet/index.md index c54e3a90..62e7c764 100644 --- a/.docfx/api/dotnet/index.md +++ b/.docfx/api/dotnet/index.md @@ -1,4 +1,4 @@ ---- +--- uid: dotnet-md title: Savvy I/O API --- diff --git a/.docfx/api/extensions/dotnet/index.md b/.docfx/api/extensions/dotnet/index.md index 1b8a42b7..57bf3feb 100644 --- a/.docfx/api/extensions/dotnet/index.md +++ b/.docfx/api/extensions/dotnet/index.md @@ -1,4 +1,4 @@ ---- +--- uid: extensions-dotnet-md title: Extensions for Savvy I/O API --- diff --git a/.docfx/api/types/Savvyio.AsyncEventHandler`1.md b/.docfx/api/types/Savvyio.AsyncEventHandler`1.md index 3ac34e7b..bc29ed52 100644 --- a/.docfx/api/types/Savvyio.AsyncEventHandler`1.md +++ b/.docfx/api/types/Savvyio.AsyncEventHandler`1.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.AsyncEventHandler`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Commands.CommandDispatcher.md b/.docfx/api/types/Savvyio.Commands.CommandDispatcher.md index d36e3c0f..236ebfd0 100644 --- a/.docfx/api/types/Savvyio.Commands.CommandDispatcher.md +++ b/.docfx/api/types/Savvyio.Commands.CommandDispatcher.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Commands.CommandDispatcher example: - *content diff --git a/.docfx/api/types/Savvyio.Commands.Messaging.InMemoryCommandQueue.md b/.docfx/api/types/Savvyio.Commands.Messaging.InMemoryCommandQueue.md index d30a0c50..d1fdbff4 100644 --- a/.docfx/api/types/Savvyio.Commands.Messaging.InMemoryCommandQueue.md +++ b/.docfx/api/types/Savvyio.Commands.Messaging.InMemoryCommandQueue.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Commands.Messaging.InMemoryCommandQueue example: - *content diff --git a/.docfx/api/types/Savvyio.Dispatchers.ServiceLocator.md b/.docfx/api/types/Savvyio.Dispatchers.ServiceLocator.md index efbab22e..6f8bdd81 100644 --- a/.docfx/api/types/Savvyio.Dispatchers.ServiceLocator.md +++ b/.docfx/api/types/Savvyio.Dispatchers.ServiceLocator.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Dispatchers.ServiceLocator example: - *content diff --git a/.docfx/api/types/Savvyio.Domain.DomainEventDispatcher.md b/.docfx/api/types/Savvyio.Domain.DomainEventDispatcher.md index 0a5bfbd4..446b5321 100644 --- a/.docfx/api/types/Savvyio.Domain.DomainEventDispatcher.md +++ b/.docfx/api/types/Savvyio.Domain.DomainEventDispatcher.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Domain.DomainEventDispatcher example: - *content diff --git a/.docfx/api/types/Savvyio.Domain.DomainEventDispatcherExtensions.md b/.docfx/api/types/Savvyio.Domain.DomainEventDispatcherExtensions.md index 7d377f85..8836c97a 100644 --- a/.docfx/api/types/Savvyio.Domain.DomainEventDispatcherExtensions.md +++ b/.docfx/api/types/Savvyio.Domain.DomainEventDispatcherExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Domain.DomainEventDispatcherExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.Dapper.DapperDataSource.md b/.docfx/api/types/Savvyio.Extensions.Dapper.DapperDataSource.md index 09d9d207..3153fe1f 100644 --- a/.docfx/api/types/Savvyio.Extensions.Dapper.DapperDataSource.md +++ b/.docfx/api/types/Savvyio.Extensions.Dapper.DapperDataSource.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.Dapper.DapperDataSource example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.Dapper.DapperDataSourceOptions.md b/.docfx/api/types/Savvyio.Extensions.Dapper.DapperDataSourceOptions.md index c323090a..13ee8c1d 100644 --- a/.docfx/api/types/Savvyio.Extensions.Dapper.DapperDataSourceOptions.md +++ b/.docfx/api/types/Savvyio.Extensions.Dapper.DapperDataSourceOptions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.Dapper.DapperDataSourceOptions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DapperExtensions.DapperExtensionsDataStore`1.md b/.docfx/api/types/Savvyio.Extensions.DapperExtensions.DapperExtensionsDataStore`1.md index c36b7547..86c9796d 100644 --- a/.docfx/api/types/Savvyio.Extensions.DapperExtensions.DapperExtensionsDataStore`1.md +++ b/.docfx/api/types/Savvyio.Extensions.DapperExtensions.DapperExtensionsDataStore`1.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DapperExtensions.DapperExtensionsDataStore`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSourceOptions`1.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSourceOptions`1.md index 225fd9d7..1cf70d31 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSourceOptions`1.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSourceOptions`1.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSourceOptions`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSource`1.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSource`1.md index 90248c4c..0bfa39df 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSource`1.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSource`1.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.Dapper.DapperDataSource`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.DapperExtensions.DapperExtensionsDataStore`2.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.DapperExtensions.DapperExtensionsDataStore`2.md index e65ac74f..bca7808b 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.DapperExtensions.DapperExtensionsDataStore`2.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.DapperExtensions.DapperExtensionsDataStore`2.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.DapperExtensions.DapperExtensionsDataStore`2 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.DapperExtensions.ServiceCollectionExtensions.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.DapperExtensions.ServiceCollectionExtensions.md index 11309ab3..5295e6e8 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.DapperExtensions.ServiceCollectionExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.DapperExtensions.ServiceCollectionExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.DapperExtensions.ServiceCollectionExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateDataSource`1.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateDataSource`1.md index 4e4f2517..fb368961 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateDataSource`1.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateDataSource`1.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateDataSource`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateRepository`3.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateRepository`3.md index a496cd43..16b1bbde 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateRepository`3.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateRepository`3.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.Domain.EfCoreAggregateRepository`3 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`3.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`3.md index 05389353..c398c232 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`3.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`3.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`3 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.ServiceCollectionExtensions.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.ServiceCollectionExtensions.md index b08c73c4..0a80eeb2 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.ServiceCollectionExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.ServiceCollectionExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.ServiceCollectionExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.ServiceCollectionExtensions.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.ServiceCollectionExtensions.md index 01df2407..cf1a9492 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.ServiceCollectionExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.Domain.ServiceCollectionExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.Domain.ServiceCollectionExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSourceOptions`1.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSourceOptions`1.md index 1f4894e4..1b0c9ce4 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSourceOptions`1.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSourceOptions`1.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSourceOptions`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSource`1.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSource`1.md index 9b49dc9a..5261137a 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSource`1.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSource`1.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataSource`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataStore`2.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataStore`2.md index ba29154a..948bec25 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataStore`2.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataStore`2.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.EfCoreDataStore`2 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreRepository`3.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreRepository`3.md index d0a1a3b6..46f7ec7b 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreRepository`3.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.EfCoreRepository`3.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.EfCoreRepository`3 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.ServiceCollectionExtensions.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.ServiceCollectionExtensions.md index 7cc7bca3..b1f8e16a 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.ServiceCollectionExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.EFCore.ServiceCollectionExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.EFCore.ServiceCollectionExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Messaging.ServiceCollectionExtensions.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Messaging.ServiceCollectionExtensions.md index 15d14380..1e096364 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Messaging.ServiceCollectionExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.Messaging.ServiceCollectionExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.Messaging.ServiceCollectionExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueue.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueue.md index c547923f..5016a642 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueue.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueue.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueue`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueueOptions.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueueOptions.md index 52ad703c..f5937651 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueueOptions.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueueOptions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.NATS.Commands.NatsCommandQueueOptions`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.ServiceCollectionExtensions.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.ServiceCollectionExtensions.md index 0ac40834..604271fc 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.ServiceCollectionExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.NATS.ServiceCollectionExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.NATS.ServiceCollectionExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.RabbitMQ.ServiceCollectionExtensions.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.RabbitMQ.ServiceCollectionExtensions.md index 7c9a6a0c..e6e9e0e7 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.RabbitMQ.ServiceCollectionExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.RabbitMQ.ServiceCollectionExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.RabbitMQ.ServiceCollectionExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.SimpleQueueService.ServiceCollectionExtensions.md b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.SimpleQueueService.ServiceCollectionExtensions.md index 1ecdd8c8..def7d775 100644 --- a/.docfx/api/types/Savvyio.Extensions.DependencyInjection.SimpleQueueService.ServiceCollectionExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.DependencyInjection.SimpleQueueService.ServiceCollectionExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.DependencyInjection.SimpleQueueService.ServiceCollectionExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.DomainEventDispatcherExtensions.md b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.DomainEventDispatcherExtensions.md index 42898d32..6025ff4e 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.DomainEventDispatcherExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.DomainEventDispatcherExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.Domain.DomainEventDispatcherExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EfCoreAggregateDataSource.md b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EfCoreAggregateDataSource.md index 02b6b7e8..307db654 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EfCoreAggregateDataSource.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EfCoreAggregateDataSource.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.Domain.EfCoreAggregateDataSource example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EfCoreAggregateRepository`2.md b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EfCoreAggregateRepository`2.md index 1b57e519..0a849f15 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EfCoreAggregateRepository`2.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EfCoreAggregateRepository`2.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.Domain.EfCoreAggregateRepository`2 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityExtensions.md b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityExtensions.md index d036ae39..4642dda6 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityOptions.md b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityOptions.md index ad51ce50..be5e9430 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityOptions.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityOptions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntityOptions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntity`2.md b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntity`2.md index a1de460a..564d29ce 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntity`2.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntity`2.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateEntity`2 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`2.md b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`2.md index b5357827..16e3c579 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`2.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`2.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.Domain.EventSourcing.EfCoreTracedAggregateRepository`2 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.ModelBuilderExtensions.md b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.ModelBuilderExtensions.md index 11051a6c..e2188d53 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.ModelBuilderExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.ModelBuilderExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.Domain.EventSourcing.ModelBuilderExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.TracedDomainEventExtensions.md b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.TracedDomainEventExtensions.md index 9dfd6b74..0d59a696 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.TracedDomainEventExtensions.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.Domain.EventSourcing.TracedDomainEventExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.Domain.EventSourcing.TracedDomainEventExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreDataStore`1.md b/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreDataStore`1.md index 68bf111f..bb86795c 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreDataStore`1.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreDataStore`1.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.EfCoreDataStore`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreDbContext.md b/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreDbContext.md index 6f155954..c364b802 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreDbContext.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreDbContext.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.EfCoreDbContext example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreQueryOptions`1.md b/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreQueryOptions`1.md index 16376496..e50f56cd 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreQueryOptions`1.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreQueryOptions`1.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.EfCoreQueryOptions`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreRepository`2.md b/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreRepository`2.md index 45529072..53948509 100644 --- a/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreRepository`2.md +++ b/.docfx/api/types/Savvyio.Extensions.EFCore.EfCoreRepository`2.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.EFCore.EfCoreRepository`2 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.Newtonsoft.Json.Converters.AggregateRootConverter.md b/.docfx/api/types/Savvyio.Extensions.Newtonsoft.Json.Converters.AggregateRootConverter.md index 50ba6c6a..5d28d3c3 100644 --- a/.docfx/api/types/Savvyio.Extensions.Newtonsoft.Json.Converters.AggregateRootConverter.md +++ b/.docfx/api/types/Savvyio.Extensions.Newtonsoft.Json.Converters.AggregateRootConverter.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.Newtonsoft.Json.Converters.AggregateRootConverter`1 example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.Text.Json.Converters.MetadataDictionaryConverter.md b/.docfx/api/types/Savvyio.Extensions.Text.Json.Converters.MetadataDictionaryConverter.md index f85b97eb..7e7f5d6e 100644 --- a/.docfx/api/types/Savvyio.Extensions.Text.Json.Converters.MetadataDictionaryConverter.md +++ b/.docfx/api/types/Savvyio.Extensions.Text.Json.Converters.MetadataDictionaryConverter.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.Text.Json.Converters.MetadataDictionaryConverter example: - *content diff --git a/.docfx/api/types/Savvyio.Extensions.Text.Json.Converters.SingleValueObjectConverter.md b/.docfx/api/types/Savvyio.Extensions.Text.Json.Converters.SingleValueObjectConverter.md index 320d5118..3b2938c3 100644 --- a/.docfx/api/types/Savvyio.Extensions.Text.Json.Converters.SingleValueObjectConverter.md +++ b/.docfx/api/types/Savvyio.Extensions.Text.Json.Converters.SingleValueObjectConverter.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Extensions.Text.Json.Converters.SingleValueObjectConverter example: - *content diff --git a/.docfx/api/types/Savvyio.HandlerDiscoveryModel.md b/.docfx/api/types/Savvyio.HandlerDiscoveryModel.md index e5b57e5b..fb3c285b 100644 --- a/.docfx/api/types/Savvyio.HandlerDiscoveryModel.md +++ b/.docfx/api/types/Savvyio.HandlerDiscoveryModel.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.HandlerDiscoveryModel example: - *content diff --git a/.docfx/api/types/Savvyio.HandlerFactory.md b/.docfx/api/types/Savvyio.HandlerFactory.md index 9dfb94cf..c1702bd7 100644 --- a/.docfx/api/types/Savvyio.HandlerFactory.md +++ b/.docfx/api/types/Savvyio.HandlerFactory.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.HandlerFactory example: - *content diff --git a/.docfx/api/types/Savvyio.HandlerServiceAssemblyModel.md b/.docfx/api/types/Savvyio.HandlerServiceAssemblyModel.md index 01c271c1..8c9bc633 100644 --- a/.docfx/api/types/Savvyio.HandlerServiceAssemblyModel.md +++ b/.docfx/api/types/Savvyio.HandlerServiceAssemblyModel.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.HandlerServiceAssemblyModel example: - *content diff --git a/.docfx/api/types/Savvyio.HandlerServiceTypeImplementationModel.md b/.docfx/api/types/Savvyio.HandlerServiceTypeImplementationModel.md index 26e92450..4c7c532b 100644 --- a/.docfx/api/types/Savvyio.HandlerServiceTypeImplementationModel.md +++ b/.docfx/api/types/Savvyio.HandlerServiceTypeImplementationModel.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.HandlerServiceTypeImplementationModel example: - *content diff --git a/.docfx/api/types/Savvyio.Handlers.FireForgetRegistryExtensions.md b/.docfx/api/types/Savvyio.Handlers.FireForgetRegistryExtensions.md index 75b10d30..c5b70a62 100644 --- a/.docfx/api/types/Savvyio.Handlers.FireForgetRegistryExtensions.md +++ b/.docfx/api/types/Savvyio.Handlers.FireForgetRegistryExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Handlers.FireForgetRegistryExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Handlers.RequestReplyRegistryExtensions.md b/.docfx/api/types/Savvyio.Handlers.RequestReplyRegistryExtensions.md index 3ad24664..dee19f07 100644 --- a/.docfx/api/types/Savvyio.Handlers.RequestReplyRegistryExtensions.md +++ b/.docfx/api/types/Savvyio.Handlers.RequestReplyRegistryExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Handlers.RequestReplyRegistryExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Messaging.AcknowledgedEventArgs.md b/.docfx/api/types/Savvyio.Messaging.AcknowledgedEventArgs.md index c8239b2c..10e46128 100644 --- a/.docfx/api/types/Savvyio.Messaging.AcknowledgedEventArgs.md +++ b/.docfx/api/types/Savvyio.Messaging.AcknowledgedEventArgs.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Messaging.AcknowledgedEventArgs example: - *content diff --git a/.docfx/api/types/Savvyio.Messaging.Cryptography.MessageExtensions.md b/.docfx/api/types/Savvyio.Messaging.Cryptography.MessageExtensions.md index 0f1fd64e..6485ee51 100644 --- a/.docfx/api/types/Savvyio.Messaging.Cryptography.MessageExtensions.md +++ b/.docfx/api/types/Savvyio.Messaging.Cryptography.MessageExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Messaging.Cryptography.MessageExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Messaging.Cryptography.SignedMessageExtensions.md b/.docfx/api/types/Savvyio.Messaging.Cryptography.SignedMessageExtensions.md index 0329b3e2..1e6cbd7f 100644 --- a/.docfx/api/types/Savvyio.Messaging.Cryptography.SignedMessageExtensions.md +++ b/.docfx/api/types/Savvyio.Messaging.Cryptography.SignedMessageExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Messaging.Cryptography.SignedMessageExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.Messaging.MessageExtensions.md b/.docfx/api/types/Savvyio.Messaging.MessageExtensions.md index fb603d8c..85412ba7 100644 --- a/.docfx/api/types/Savvyio.Messaging.MessageExtensions.md +++ b/.docfx/api/types/Savvyio.Messaging.MessageExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.Messaging.MessageExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.MetadataExtensions.md b/.docfx/api/types/Savvyio.MetadataExtensions.md index 37cf6905..1ea7b828 100644 --- a/.docfx/api/types/Savvyio.MetadataExtensions.md +++ b/.docfx/api/types/Savvyio.MetadataExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.MetadataExtensions example: - *content diff --git a/.docfx/api/types/Savvyio.MetadataFactory.md b/.docfx/api/types/Savvyio.MetadataFactory.md index 9db0a481..767b506f 100644 --- a/.docfx/api/types/Savvyio.MetadataFactory.md +++ b/.docfx/api/types/Savvyio.MetadataFactory.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.MetadataFactory example: - *content diff --git a/.docfx/api/types/Savvyio.TaskExtensions.md b/.docfx/api/types/Savvyio.TaskExtensions.md index 67e1a3b0..8772baa7 100644 --- a/.docfx/api/types/Savvyio.TaskExtensions.md +++ b/.docfx/api/types/Savvyio.TaskExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Savvyio.TaskExtensions example: - *content diff --git a/.docfx/filterConfig.yml b/.docfx/filterConfig.yml index 837050d4..bab37623 100644 --- a/.docfx/filterConfig.yml +++ b/.docfx/filterConfig.yml @@ -1,4 +1,4 @@ -apiRules: +apiRules: - exclude: uidRegex: ^System\.Object - type: Type \ No newline at end of file + type: Type diff --git a/.docfx/includes/availability-modern.md b/.docfx/includes/availability-modern.md index af5cf4b9..a13a3f63 100644 --- a/.docfx/includes/availability-modern.md +++ b/.docfx/includes/availability-modern.md @@ -1 +1 @@ -Availability: .NET 10 and .NET 9 \ No newline at end of file +Availability: .NET 11 and .NET 10 diff --git a/.docfx/templates/savvyio/layout/_master.tmpl b/.docfx/templates/savvyio/layout/_master.tmpl index bea28ce5..c987bb72 100644 --- a/.docfx/templates/savvyio/layout/_master.tmpl +++ b/.docfx/templates/savvyio/layout/_master.tmpl @@ -152,4 +152,4 @@ {{/redirect_url}} - \ No newline at end of file + diff --git a/.docfx/templates/savvyio/partials/namespace.tmpl.partial b/.docfx/templates/savvyio/partials/namespace.tmpl.partial index 173b006c..53bd779d 100644 --- a/.docfx/templates/savvyio/partials/namespace.tmpl.partial +++ b/.docfx/templates/savvyio/partials/namespace.tmpl.partial @@ -8,7 +8,9 @@ {{#children}}

{{>partials/namespaceSubtitle}}

{{#children}} -

-
{{{summary}}}
+
+
+
{{{summary}}}
+
{{/children}} -{{/children}} \ No newline at end of file +{{/children}} diff --git a/.docfx/templates/savvyio/public/main.js b/.docfx/templates/savvyio/public/main.js index 801e9b2d..438e0797 100644 --- a/.docfx/templates/savvyio/public/main.js +++ b/.docfx/templates/savvyio/public/main.js @@ -6,4 +6,4 @@ export default { title: 'GitHub' } ] - } \ No newline at end of file + } diff --git a/.editorconfig b/.editorconfig index a083db5c..886ea153 100644 --- a/.editorconfig +++ b/.editorconfig @@ -37,7 +37,7 @@ dotnet_diagnostic.IDE0063.severity = none dotnet_diagnostic.IDE0071.severity = none # S3267: Loops should be simplified with "LINQ" expressions -# https://rules.sonarsource.com/csharp/RSPEC-3267 +# https://sonarcloud.io/organizations/sonarcloud/rules?open=csharpsquid:S3267&rule_key=csharpsquid:S3267 dotnet_diagnostic.S3267.severity = none # CA1859: Use concrete types when possible for improved performance @@ -57,9 +57,10 @@ indent_size = 4 # IDE0161: Namespace declaration preferences # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0161 -# Prefer file-scoped namespaces for new files; existing block-scoped files should not be converted unless explicitly asked +# File-scoped namespaces are mandatory for new and existing C# files. [*.cs] -csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_namespace_declarations = file_scoped:error +dotnet_diagnostic.IDE0161.severity = error # Top-level statements: DO NOT USE # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0210-ide0211 diff --git a/.gitattributes b/.gitattributes index 1ff0c423..0ea8d1bd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,7 @@ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### -* text=auto +* text=auto eol=lf ############################################################################### # Set default behavior for command prompt diff. @@ -17,7 +17,7 @@ # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following +# the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below @@ -46,9 +46,9 @@ ############################################################################### # diff behavior for common document formats -# +# # Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the +# is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index e27edb40..1fa464ec 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ -# Contributor Covenant Code of Conduct +# Contributor Covenant Code of Conduct This document is adapted from the Contributor Covenant which is used by many open source projects, including those under the [.NET Foundation](https://dotnetfoundation.org/code-of-conduct). @@ -132,4 +132,4 @@ at [https://www.contributor-covenant.org/translations][translations]. [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations \ No newline at end of file +[translations]: https://www.contributor-covenant.org/translations diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a4506aca..013ff856 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -5,7 +5,7 @@ This repository is part of the Codebelt .NET library estate. The instructions be ## Before you start - Read the repository `README.md` and open an issue before starting a non-trivial feature or behavioral change. -- Use an installed .NET SDK that can build the target frameworks listed below. This repository currently targets: **net10.0, net9.0**. +- Use an installed .NET SDK that can build the target frameworks listed below. This repository currently targets: **net10.0, net11.0**. - The solution is `Savvyio.slnx`. Central package versions are maintained in `Directory.Packages.props`. - The shared build behavior is in `Directory.Build.props` and `Directory.Build.targets`; repository-specific TFMs, package references and metadata remain local to this library. @@ -15,7 +15,7 @@ This repository is part of the Codebelt .NET library estate. The instructions be - `test/` contains xUnit v3 test projects. - `Savvyio.slnx` is the solution used for local development. - `.github/workflows/ci-pipeline.yml` is the CI workflow and the authority for the test matrix. -- `testenvironments.json` declares the supported `WSL-Ubuntu` and `Docker-Ubuntu` test environments. +- `testenvironments.json` declares the supported `WSL-Ubuntu`, `Docker-Ubuntu (net10.0)`, and `Docker-Ubuntu (net11.0)` test environments. ## Build @@ -40,12 +40,13 @@ foreach ($project in $testProjects) { } ``` -The CI test plan currently runs **28** project(s) and excludes **8** project(s). The workflow also has an optional macOS test job. +The CI test plan currently runs **28** project(s) and excludes **8** project(s). The workflow also has an optional macOS test job. Run cloud integration projects one framework at a time with `--framework` because their queues are shared across targets. ## Integration and infrastructure -- `WSL-Ubuntu` — WSL distribution `Ubuntu-24.04`. -- `Docker-Ubuntu` — Docker image `codebeltnet/ubuntu-testrunner:8-9-10-11`. +- `WSL-Ubuntu` — WSL distribution `Ubuntu-26.04`. +- `Docker-Ubuntu (net10.0)` — Docker image `codebeltnet/ubuntu-testrunner:10`. +- `Docker-Ubuntu (net11.0)` — Docker image `codebeltnet/ubuntu-testrunner:11`. This repository has `docker-compose.yml` with these services: `awscli, localstack, nats, rabbitmq`. diff --git a/.github/codecov.yml b/.github/codecov.yml index c0a00374..b52d0c49 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,2 +1,2 @@ ignore: - - "test" \ No newline at end of file + - "test" diff --git a/.github/prompts/benchmark.prompt.md b/.github/prompts/benchmark.prompt.md deleted file mode 100644 index 22d1c9db..00000000 --- a/.github/prompts/benchmark.prompt.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -mode: agent -description: 'Writing Performance Benchmarks' ---- - -# Benchmark Fixture Prompt (Tuning Benchmarks) - -This prompt defines how to generate performance tests (“benchmarks”) for a project/solution using BenchmarkDotNet. -Benchmarks are *not* unit tests — they are micro- or component-level performance measurements that belong under the `tuning/` directory and follow strict conventions. - -Copilot must follow these guidelines when generating benchmark fixtures. - ---- - -## 1. Naming and Placement - -- All benchmark projects live under the `tuning/` folder. - Examples: - - `tuning/.Benchmarks/` - - `tuning/.Console.Benchmarks/` - -- **Namespaces must NOT end with `.Benchmarks`.** - They must mirror the production assembly’s namespace. - - Example: - If benchmarking a type inside `YourProject.Console`, then: - - ```csharp - namespace YourProject.Console - { - public class Sha512256Benchmark { … } - } - ``` - -* **Benchmark class names must end with `Benchmark`.** - Example: `DateSpanBenchmark`, `FowlerNollVoBenchmark`. - -* Benchmark files should be located in the matching benchmark project - (e.g., benchmarks for `YourProject.Console` go in `YourProject.Console.Benchmarks.csproj`). - -* In the `.csproj` for each benchmark project, set the root namespace to the production namespace, for example: - - ```xml - YourProject.Console - ``` - ---- - -## 2. Attributes and Configuration - -Each benchmark class should use: - -```csharp -[MemoryDiagnoser] -[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] -``` - -Optional but strongly recommended where meaningful: - -* `[Params(...)]` — define small, medium, large input sizes. -* `[GlobalSetup]` — deterministic initialization of benchmark data. -* `[Benchmark(Description = "...")]` — always add descriptions. -* `[Benchmark(Baseline = true)]` — when comparing two implementations. - -Avoid complex global configs; prefer explicit attributes inside the class. - ---- - -## 3. Structure and Best Practices - -A benchmark fixture must: - -* Measure a **single logical operation** per benchmark method. -* Avoid I/O, networking, disk access, logging, or side effects. -* Avoid expensive setup inside `[Benchmark]` methods. -* Use deterministic data (e.g., seeded RNG or predefined constants). -* Use `[GlobalSetup]` to allocate buffers, random payloads, or reusable test data only once. -* Avoid shared mutable state unless reset per iteration. - -Use representative input sizes such as: - -```csharp -[Params(8, 256, 4096)] -public int Count { get; set; } -``` - -BenchmarkDotNet will run each benchmark for each parameter value. - ---- - -## 4. Method Naming Conventions - -Use descriptive names that communicate intent: - -* `Parse_Short` -* `Parse_Long` -* `ComputeHash_Small` -* `ComputeHash_Large` -* `Serialize_Optimized` -* `Serialize_Baseline` - -When comparing approaches, always list them clearly and tag one as the baseline. - ---- - -## 5. Example Benchmark Fixture - -```csharp -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; - -namespace YourProject -{ - [MemoryDiagnoser] - [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] - public class SampleOperationBenchmark - { - [Params(8, 256, 4096)] - public int Count { get; set; } - - private byte[] _payload; - - [GlobalSetup] - public void Setup() - { - _payload = new byte[Count]; - // deterministic initialization - } - - [Benchmark(Baseline = true, Description = "Operation - baseline")] - public int Operation_Baseline() => SampleOperation.Process(_payload); - - [Benchmark(Description = "Operation - optimized")] - public int Operation_Optimized() => SampleOperation.ProcessOptimized(_payload); - } -} -``` - ---- - -## 6. Reporting and CI - -* Benchmark projects live exclusively under `tuning/`. They must not affect production builds. -* Heavy BenchmarkDotNet runs should *not* run in CI unless explicitly configured. -* Reports are produced by the benchmark runner and stored under the configured artifacts directory. - ---- - -## 7. Additional Guidelines - -* Keep benchmark fixtures focused and readable. -* Document non-obvious reasoning in short comments. -* Prefer realistic but deterministic data sets. -* When benchmarks reveal regressions or improvements, reference the associated PR or issue in a comment. -* Shared benchmark helpers belong in `tuning/` projects, not in production code. - ---- - -## Final Notes - -* Benchmarks are performance tests, not unit tests. -* Use `[Benchmark]` only for pure performance measurement. -* Avoid `MethodImplOptions.NoInlining` unless absolutely necessary. -* Use small sets of meaningful benchmark scenarios — avoid combinatorial explosion. diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 2cfd30bf..82a25009 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -23,7 +23,7 @@ permissions: jobs: init: name: initialize - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 outputs: run-mac-tests: ${{ steps.vars.outputs.run-mac-tests }} run-privileged-jobs: ${{ steps.vars.outputs.run-privileged-jobs }} @@ -50,9 +50,26 @@ jobs: echo "build-switches=" >> "$GITHUB_OUTPUT" fi + line_endings: + name: 📏 Line Endings + runs-on: ubuntu-26.04 + steps: + - name: Checkout + uses: codebeltnet/git-checkout@v1 + + - name: Require LF line endings for tracked text files + shell: bash + run: | + violations=$(git ls-files --eol | awk '$1 != "i/lf" && $1 != "i/-text" && $1 != "i/none" { print }') + if [[ -n "$violations" ]]; then + echo "$violations" + echo "::error::Tracked text files must use LF line endings (see .editorconfig and .gitattributes)." + exit 1 + fi + prepare_test: name: 📜 Prepare Test - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 outputs: json: ${{ steps.test-projects.outputs.result }} steps: @@ -86,7 +103,8 @@ jobs: configuration: [Debug, Release] uses: codebeltnet/jobs-dotnet-build/.github/workflows/default.yml@v3 with: - runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + include-preview: true + runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} configuration: ${{ matrix.configuration }} strong-name-key-filename: ${{ needs.init.outputs.strong-name-key-filename }} build-switches: ${{ needs.init.outputs.build-switches }} @@ -101,6 +119,7 @@ jobs: configuration: [Debug, Release] uses: codebeltnet/jobs-dotnet-pack/.github/workflows/default.yml@v3 with: + include-preview: true configuration: ${{ matrix.configuration }} version: ${{ needs.build.outputs.version }} download-build-artifact-pattern: build-${{ matrix.configuration }}-X64 @@ -116,7 +135,8 @@ jobs: project: ${{ fromJson(needs.prepare_test.outputs.json) }} uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: - runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + include-preview: true + runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} configuration: ${{ matrix.configuration }} projects: ${{ matrix.project }} build-switches: -p:SkipSignAssembly=true @@ -135,6 +155,7 @@ jobs: project: ${{ fromJson(needs.prepare_test.outputs.json) }} uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: + include-preview: true runs-on: ${{ matrix.arch == 'ARM64' && 'windows-11-arm' || 'windows-2025' }} configuration: ${{ matrix.configuration }} projects: ${{ matrix.project }} @@ -155,6 +176,7 @@ jobs: project: ${{ fromJson(needs.prepare_test.outputs.json) }} uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: + include-preview: true runs-on: ${{ matrix.arch == 'ARM64' && 'macos-26' || 'macos-26-intel' }} configuration: ${{ matrix.configuration }} projects: ${{ matrix.project }} @@ -166,13 +188,14 @@ jobs: test_qualitygate: if: ${{ always() }} name: test-qualitygate - needs: [init, test_linux, test_windows, test_mac] - runs-on: ubuntu-24.04 + needs: [init, line_endings, test_linux, test_windows, test_mac] + runs-on: ubuntu-26.04 steps: - name: Evaluate test results shell: bash env: RUN_MAC_TESTS: ${{ needs.init.outputs.run-mac-tests }} + LINE_ENDINGS_RESULT: ${{ needs.line_endings.result }} TEST_LINUX_RESULT: ${{ needs.test_linux.result }} TEST_WINDOWS_RESULT: ${{ needs.test_windows.result }} TEST_MAC_RESULT: ${{ needs.test_mac.result }} @@ -203,6 +226,7 @@ jobs: fi } + require_success "line_endings" "$LINE_ENDINGS_RESULT" require_success "test_linux" "$TEST_LINUX_RESULT" require_success "test_windows" "$TEST_WINDOWS_RESULT" require_success_or_skip "test_mac" "$RUN_MAC_TESTS" "$TEST_MAC_RESULT" @@ -214,7 +238,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-24.04, windows-2025] + os: [ubuntu-26.04, windows-2025] configuration: [Release] project: [ test/**/Savvyio.FunctionalTests.csproj, test/**/Savvyio.Extensions.SimpleQueueService.FunctionalTests.csproj, test/**/Savvyio.Extensions.QueueStorage.FunctionalTests.csproj ] runs-on: ${{ matrix.os }} @@ -224,6 +248,8 @@ jobs: - name: Install .NET uses: codebeltnet/install-dotnet@v3 + with: + includePreview: true - name: Install .NET Tool - Report Generator uses: codebeltnet/dotnet-tool-install-reportgenerator@v1 @@ -254,7 +280,7 @@ jobs: integration_test_rabbitmq: name: ⚗️ Integration Test - RabbitMQ needs: [build] - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 timeout-minutes: 15 steps: - name: Checkout @@ -262,6 +288,8 @@ jobs: - name: Install .NET uses: codebeltnet/install-dotnet@v3 + with: + includePreview: true - name: Install .NET Tool - Report Generator uses: codebeltnet/dotnet-tool-install-reportgenerator@v1 @@ -303,7 +331,7 @@ jobs: integration_test_nats: name: ⚗️ Integration Test - NATS needs: [build] - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 timeout-minutes: 15 steps: - name: Checkout @@ -311,6 +339,8 @@ jobs: - name: Install .NET uses: codebeltnet/install-dotnet@v3 + with: + includePreview: true - name: Install .NET Tool - Report Generator uses: codebeltnet/dotnet-tool-install-reportgenerator@v1 @@ -351,6 +381,7 @@ jobs: needs: [init, build, test_qualitygate, integration_test, integration_test_rabbitmq, integration_test_nats] uses: codebeltnet/jobs-sonarcloud/.github/workflows/default.yml@v3 with: + include-preview: true organization: geekle projectKey: savvyio version: ${{ needs.build.outputs.version }} @@ -370,6 +401,8 @@ jobs: name: call-codeql needs: [init, build, test_qualitygate, integration_test, integration_test_rabbitmq, integration_test_nats] uses: codebeltnet/jobs-codeql/.github/workflows/default.yml@v3 + with: + include-preview: true permissions: security-events: write diff --git a/.github/workflows/service-update.yml b/.github/workflows/service-update.yml index 1f695860..2dbfe5b7 100644 --- a/.github/workflows/service-update.yml +++ b/.github/workflows/service-update.yml @@ -50,6 +50,7 @@ jobs: import re import subprocess import sys + from datetime import date tag_pattern = re.compile(r"^v(\d+)\.(\d+)\.(\d+)$") tags = subprocess.check_output(["git", "tag", "--list"], text=True).splitlines() @@ -72,28 +73,37 @@ jobs: with open("CHANGELOG.md", encoding="utf-8") as changelog_file: changelog = changelog_file.read() - heading = re.search(r"(?m)^## \[(\d+\.\d+\.\d+)\] - ([^\r\n]+)[ \t]*$", changelog) + heading = re.search(r"(?m)^## \[(\d+\.\d+\.\d+)\]([^\r\n]*)$", changelog) if not heading: print("::error::CHANGELOG.md has no versioned heading to compare with Git tags.") sys.exit(1) - current_version, current_status = heading.groups() - current_status = current_status.strip() - if current_status.upper() != "TBD" and not re.fullmatch(r"\d{4}-\d{2}-\d{2}", current_status): - print(f"::error::Unsupported CHANGELOG.md version status: {current_status}") + current_version = heading.group(1) + status_match = re.fullmatch(r" - (TBD|\d{4}-\d{2}-\d{2})[ \t]*", heading.group(2), re.IGNORECASE) + if not status_match: + print(f"::error::Unsupported or missing CHANGELOG.md version status: {heading.group(2).strip()}") sys.exit(1) - - if current_version == next_version and current_status.upper() == "TBD": - # Reuse an already pending next release, such as [5.0.13] - TBD. - new_version = next_version - elif current_version == latest_version and current_status.upper() != "TBD": + current_status = status_match.group(1) + if current_status.upper() != "TBD": + try: + date.fromisoformat(current_status) + except ValueError: + print(f"::error::Invalid CHANGELOG.md release date: {current_status}") + sys.exit(1) + + pending_version = tuple(int(part) for part in current_version.split(".")) + released_version = (major, minor, patch) + if pending_version > released_version: + # Preserve an explicitly planned patch, minor, or major release. + new_version = current_version + elif pending_version == released_version and current_status.upper() != "TBD": # The changelog is at the latest released tag; start the next patch release. new_version = next_version else: print( f"::error::CHANGELOG.md starts at {current_version} ({current_status}), " f"but the latest stable Git tag is {latest_tag}. Expected either " - f"[{latest_version}] with a release date or [{next_version}] - TBD." + f"[{latest_version}] with a release date or a newer version with a date or TBD." ) sys.exit(1) @@ -105,6 +115,7 @@ jobs: output.write(f"current={current_version}\n") output.write(f"new={new_version}\n") output.write(f"branch={branch}\n") + PY - name: Generate codebelt-aicia token id: app-token @@ -125,7 +136,9 @@ jobs: NEW="${{ steps.newver.outputs.new }}" for f in .nuget/*/PackageReleaseNotes.txt; do [ -f "$f" ] || continue - TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //' || echo ".NET 10, .NET 9 and .NET Standard 2.0") + # Preserve an existing pending release block instead of duplicating it. + if [ "$(head -n1 "$f" | tr -d '\r')" = "Version: ${NEW}" ]; then continue; fi + TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //' || echo ".NET 11 and .NET 10") ENTRY="Version: ${NEW}\nAvailability: ${TFM}\n\n# ALM\n- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)\n\n" { printf "$ENTRY"; cat "$f"; } > "$f.tmp" && mv "$f.tmp" "$f" done @@ -140,9 +153,9 @@ jobs: service_note = "This release also includes a service update focused on package dependencies." with open("CHANGELOG.md", encoding="utf-8") as f: content = f.read() - heading = re.search(r"(?m)^## \[(\d+\.\d+\.\d+)\] - (TBD|\d{4}-\d{2}-\d{2})[ \t]*$", content) + heading = re.search(r"(?m)^## \[(\d+\.\d+\.\d+)\] - (TBD|\d{4}-\d{2}-\d{2})[ \t]*$", content, re.IGNORECASE) if heading and heading.group(1) == new_ver: - if heading.group(2) == "TBD": + if heading.group(2).upper() == "TBD": content = content[:heading.start(2)] + today + content[heading.end(2):] heading = re.search(r"(?m)^## \[(\d+\.\d+\.\d+)\] - \d{4}-\d{2}-\d{2}[ \t]*$", content) section_end = re.search(r"(?m)^## \[", content[heading.end():]) diff --git a/.nuget/Savvyio.App/PackageReleaseNotes.txt b/.nuget/Savvyio.App/PackageReleaseNotes.txt index 06429e8d..f1b4ba23 100644 --- a/.nuget/Savvyio.App/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.App/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.App/README.md b/.nuget/Savvyio.App/README.md index b4ab6516..f1f265bd 100644 --- a/.nuget/Savvyio.App/README.md +++ b/.nuget/Savvyio.App/README.md @@ -6,7 +6,7 @@ Provides a complete and convenient set of API additions for building a DDD, CQRS An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Commands.Messaging/PackageReleaseNotes.txt b/.nuget/Savvyio.Commands.Messaging/PackageReleaseNotes.txt index 0d457acb..de1ca3d6 100644 --- a/.nuget/Savvyio.Commands.Messaging/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Commands.Messaging/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Commands.Messaging/README.md b/.nuget/Savvyio.Commands.Messaging/README.md index 756c5395..bad40645 100644 --- a/.nuget/Savvyio.Commands.Messaging/README.md +++ b/.nuget/Savvyio.Commands.Messaging/README.md @@ -6,7 +6,7 @@ Provides ways to scale out Commands using distributed subsystems. An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Commands/PackageReleaseNotes.txt b/.nuget/Savvyio.Commands/PackageReleaseNotes.txt index 6107ea05..a0b74ec5 100644 --- a/.nuget/Savvyio.Commands/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Commands/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Commands/README.md b/.nuget/Savvyio.Commands/README.md index 1de46c47..8397652d 100644 --- a/.nuget/Savvyio.Commands/README.md +++ b/.nuget/Savvyio.Commands/README.md @@ -6,7 +6,7 @@ Provides the fundamental classes to work with the Command part of the CQRS patte An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Core/PackageReleaseNotes.txt b/.nuget/Savvyio.Core/PackageReleaseNotes.txt index c34b63cd..0f0aab1a 100644 --- a/.nuget/Savvyio.Core/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Core/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Core/README.md b/.nuget/Savvyio.Core/README.md index fb0d9634..e0605f47 100644 --- a/.nuget/Savvyio.Core/README.md +++ b/.nuget/Savvyio.Core/README.md @@ -6,7 +6,7 @@ Provides the fundamental abstractions and classes for supporting a complete flow An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Domain.EventSourcing/PackageReleaseNotes.txt b/.nuget/Savvyio.Domain.EventSourcing/PackageReleaseNotes.txt index 96ec7730..66c86bc4 100644 --- a/.nuget/Savvyio.Domain.EventSourcing/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Domain.EventSourcing/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Domain.EventSourcing/README.md b/.nuget/Savvyio.Domain.EventSourcing/README.md index 03da228a..066fd146 100644 --- a/.nuget/Savvyio.Domain.EventSourcing/README.md +++ b/.nuget/Savvyio.Domain.EventSourcing/README.md @@ -6,7 +6,7 @@ Provides the default implementation of an Aggregate Root that is Event Sourced e An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Domain/PackageReleaseNotes.txt b/.nuget/Savvyio.Domain/PackageReleaseNotes.txt index ac7af682..e75f2bc6 100644 --- a/.nuget/Savvyio.Domain/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Domain/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Domain/README.md b/.nuget/Savvyio.Domain/README.md index 87c63fbd..7d293032 100644 --- a/.nuget/Savvyio.Domain/README.md +++ b/.nuget/Savvyio.Domain/README.md @@ -6,7 +6,7 @@ Provides the fundamental classes and interfaces to work with the concepts of DDD An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.EventDriven.Messaging/PackageReleaseNotes.txt b/.nuget/Savvyio.EventDriven.Messaging/PackageReleaseNotes.txt index 98cec1d2..33894e0b 100644 --- a/.nuget/Savvyio.EventDriven.Messaging/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.EventDriven.Messaging/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.EventDriven.Messaging/README.md b/.nuget/Savvyio.EventDriven.Messaging/README.md index 9b4b9e2b..d6216d17 100644 --- a/.nuget/Savvyio.EventDriven.Messaging/README.md +++ b/.nuget/Savvyio.EventDriven.Messaging/README.md @@ -6,7 +6,7 @@ Provides ways to scale out Integration Events for a distributed architecture. An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.EventDriven/PackageReleaseNotes.txt b/.nuget/Savvyio.EventDriven/PackageReleaseNotes.txt index 21e05e49..54a0904a 100644 --- a/.nuget/Savvyio.EventDriven/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.EventDriven/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.EventDriven/README.md b/.nuget/Savvyio.EventDriven/README.md index 04eb69a6..5502a25c 100644 --- a/.nuget/Savvyio.EventDriven/README.md +++ b/.nuget/Savvyio.EventDriven/README.md @@ -6,7 +6,7 @@ Provides the fundamental classes to work with the concepts of Integration Events An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.Dapper/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.Dapper/PackageReleaseNotes.txt index 6ba8ad2c..b78d1555 100644 --- a/.nuget/Savvyio.Extensions.Dapper/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.Dapper/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.Dapper/README.md b/.nuget/Savvyio.Extensions.Dapper/README.md index 0517da3b..2c1eff2c 100644 --- a/.nuget/Savvyio.Extensions.Dapper/README.md +++ b/.nuget/Savvyio.Extensions.Dapper/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for [Dapper](https://github.co An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DapperExtensions/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DapperExtensions/PackageReleaseNotes.txt index 8ea9bf9c..54ad54e6 100644 --- a/.nuget/Savvyio.Extensions.DapperExtensions/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DapperExtensions/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DapperExtensions/README.md b/.nuget/Savvyio.Extensions.DapperExtensions/README.md index 9608fe03..809945ed 100644 --- a/.nuget/Savvyio.Extensions.DapperExtensions/README.md +++ b/.nuget/Savvyio.Extensions.DapperExtensions/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for [Dapper](https://github.co An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.Dapper/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.Dapper/PackageReleaseNotes.txt index d885004e..5e2d6b84 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.Dapper/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.Dapper/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.Dapper/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.Dapper/README.md index c0147411..5fb6ae52 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.Dapper/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.Dapper/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with [Dapper](ht An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.DapperExtensions/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.DapperExtensions/PackageReleaseNotes.txt index 75e8ca63..230e946c 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.DapperExtensions/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.DapperExtensions/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.DapperExtensions/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.DapperExtensions/README.md index f0851129..cf35db3f 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.DapperExtensions/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.DapperExtensions/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with [DapperExte An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.Domain/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.Domain/PackageReleaseNotes.txt index b3766401..c875694e 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.Domain/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.Domain/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.Domain/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.Domain/README.md index b7e65e91..9dbe6309 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.Domain/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.Domain/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection - optimized for An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/PackageReleaseNotes.txt index 99b26e3b..4318d76d 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/README.md index 73dba87a..f9b4505d 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with [Microsoft An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain/PackageReleaseNotes.txt index 3002b88d..205a21e3 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain/README.md index 9ce63f34..ae7ed71f 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore.Domain/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with [Microsoft An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore/PackageReleaseNotes.txt index cad7c222..438d7d51 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore/README.md index 483fbba4..0954bfdb 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.EFCore/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.EFCore/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with [Microsoft An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.NATS/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.NATS/PackageReleaseNotes.txt index f5236bae..0c5f7722 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.NATS/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.NATS/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.NATS/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.NATS/README.md index b9901ff6..eece543a 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.NATS/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.NATS/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with NATS Work-Q An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/PackageReleaseNotes.txt index f45c4e3b..d555ef0c 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/README.md index a7887e59..58afa44c 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with Newtonsoft An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.QueueStorage/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.QueueStorage/PackageReleaseNotes.txt index 256a1e61..995b45b9 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.QueueStorage/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.QueueStorage/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.QueueStorage/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.QueueStorage/README.md index 6146bd60..d93ec92d 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.QueueStorage/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.QueueStorage/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with Azure Queue An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.RabbitMQ/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.RabbitMQ/PackageReleaseNotes.txt index b41f3958..68edd787 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.RabbitMQ/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.RabbitMQ/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.RabbitMQ/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.RabbitMQ/README.md index 9937e37c..b02833bd 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.RabbitMQ/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.RabbitMQ/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with RabbitMQ Wo An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.SimpleQueueService/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.SimpleQueueService/PackageReleaseNotes.txt index e428c4e6..3a25a7a8 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.SimpleQueueService/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.SimpleQueueService/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.SimpleQueueService/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.SimpleQueueService/README.md index 029736ae..ae89c6d5 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.SimpleQueueService/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.SimpleQueueService/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with Amazon Simp An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.Text.Json/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection.Text.Json/PackageReleaseNotes.txt index aad94f70..de5d5f6a 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.Text.Json/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection.Text.Json/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection.Text.Json/README.md b/.nuget/Savvyio.Extensions.DependencyInjection.Text.Json/README.md index 6eaf64f6..1fb50281 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection.Text.Json/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection.Text.Json/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for Microsoft Dependency Injection with official bu An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.DependencyInjection/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.DependencyInjection/PackageReleaseNotes.txt index ada4f33d..c5bea99e 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.DependencyInjection/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.DependencyInjection/README.md b/.nuget/Savvyio.Extensions.DependencyInjection/README.md index 8b7bd9f2..5a0297bc 100644 --- a/.nuget/Savvyio.Extensions.DependencyInjection/README.md +++ b/.nuget/Savvyio.Extensions.DependencyInjection/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for Microsoft Dependency Injec An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.Dispatchers/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.Dispatchers/PackageReleaseNotes.txt index c35cc40a..b8833ecb 100644 --- a/.nuget/Savvyio.Extensions.Dispatchers/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.Dispatchers/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.Dispatchers/README.md b/.nuget/Savvyio.Extensions.Dispatchers/README.md index 6a7e5500..c640f36a 100644 --- a/.nuget/Savvyio.Extensions.Dispatchers/README.md +++ b/.nuget/Savvyio.Extensions.Dispatchers/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with Mediator support to provide a uniform An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.EFCore.Domain.EventSourcing/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.EFCore.Domain.EventSourcing/PackageReleaseNotes.txt index 609cf14c..a00e3bcd 100644 --- a/.nuget/Savvyio.Extensions.EFCore.Domain.EventSourcing/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.EFCore.Domain.EventSourcing/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.EFCore.Domain.EventSourcing/README.md b/.nuget/Savvyio.Extensions.EFCore.Domain.EventSourcing/README.md index 024a71b0..66c99632 100644 --- a/.nuget/Savvyio.Extensions.EFCore.Domain.EventSourcing/README.md +++ b/.nuget/Savvyio.Extensions.EFCore.Domain.EventSourcing/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for [Microsoft Entity Framework Core](https://githu An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.EFCore.Domain/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.EFCore.Domain/PackageReleaseNotes.txt index 49b842d8..30fab542 100644 --- a/.nuget/Savvyio.Extensions.EFCore.Domain/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.EFCore.Domain/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.EFCore.Domain/README.md b/.nuget/Savvyio.Extensions.EFCore.Domain/README.md index 932c393d..3d2790a4 100644 --- a/.nuget/Savvyio.Extensions.EFCore.Domain/README.md +++ b/.nuget/Savvyio.Extensions.EFCore.Domain/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O support for [Microsoft Entity Framework Core](https://githu An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.EFCore/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.EFCore/PackageReleaseNotes.txt index 858e0aa4..2e6aaaa0 100644 --- a/.nuget/Savvyio.Extensions.EFCore/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.EFCore/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.EFCore/README.md b/.nuget/Savvyio.Extensions.EFCore/README.md index 7b1d9ed3..4edba0f7 100644 --- a/.nuget/Savvyio.Extensions.EFCore/README.md +++ b/.nuget/Savvyio.Extensions.EFCore/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for [Microsoft Entity Framewor An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.NATS/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.NATS/PackageReleaseNotes.txt index e1ae9091..e412c5e1 100644 --- a/.nuget/Savvyio.Extensions.NATS/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.NATS/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.NATS/README.md b/.nuget/Savvyio.Extensions.NATS/README.md index f2b6ff77..937117c7 100644 --- a/.nuget/Savvyio.Extensions.NATS/README.md +++ b/.nuget/Savvyio.Extensions.NATS/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for NATS Work-Queue Stream and An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.Newtonsoft.Json/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.Newtonsoft.Json/PackageReleaseNotes.txt index cc90732d..173236fe 100644 --- a/.nuget/Savvyio.Extensions.Newtonsoft.Json/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.Newtonsoft.Json/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.Newtonsoft.Json/README.md b/.nuget/Savvyio.Extensions.Newtonsoft.Json/README.md index 0b0472dc..ed23d917 100644 --- a/.nuget/Savvyio.Extensions.Newtonsoft.Json/README.md +++ b/.nuget/Savvyio.Extensions.Newtonsoft.Json/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for Newtonsoft JSON. An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.QueueStorage/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.QueueStorage/PackageReleaseNotes.txt index 26ec1111..2aa6cbc9 100644 --- a/.nuget/Savvyio.Extensions.QueueStorage/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.QueueStorage/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.QueueStorage/README.md b/.nuget/Savvyio.Extensions.QueueStorage/README.md index 5777dc2f..bf1b9605 100644 --- a/.nuget/Savvyio.Extensions.QueueStorage/README.md +++ b/.nuget/Savvyio.Extensions.QueueStorage/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for Azure Queue Storage paired An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.RabbitMQ/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.RabbitMQ/PackageReleaseNotes.txt index 9038c93c..c43c3f61 100644 --- a/.nuget/Savvyio.Extensions.RabbitMQ/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.RabbitMQ/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.RabbitMQ/README.md b/.nuget/Savvyio.Extensions.RabbitMQ/README.md index cd33162a..8f8227ac 100644 --- a/.nuget/Savvyio.Extensions.RabbitMQ/README.md +++ b/.nuget/Savvyio.Extensions.RabbitMQ/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for RabbitMQ Work Queue and Ra An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.SimpleQueueService/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.SimpleQueueService/PackageReleaseNotes.txt index dacd111e..b0125890 100644 --- a/.nuget/Savvyio.Extensions.SimpleQueueService/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.SimpleQueueService/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.SimpleQueueService/README.md b/.nuget/Savvyio.Extensions.SimpleQueueService/README.md index bb537325..438c1df5 100644 --- a/.nuget/Savvyio.Extensions.SimpleQueueService/README.md +++ b/.nuget/Savvyio.Extensions.SimpleQueueService/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for Amazon Simple Queue Servic An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Extensions.Text.Json/PackageReleaseNotes.txt b/.nuget/Savvyio.Extensions.Text.Json/PackageReleaseNotes.txt index 9d9062e0..fcfa76f0 100644 --- a/.nuget/Savvyio.Extensions.Text.Json/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Extensions.Text.Json/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Extensions.Text.Json/README.md b/.nuget/Savvyio.Extensions.Text.Json/README.md index 518222c8..a114a261 100644 --- a/.nuget/Savvyio.Extensions.Text.Json/README.md +++ b/.nuget/Savvyio.Extensions.Text.Json/README.md @@ -6,7 +6,7 @@ Extend the Savvy I/O core assemblies with support for official built-in JSON. An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Messaging/PackageReleaseNotes.txt b/.nuget/Savvyio.Messaging/PackageReleaseNotes.txt index b5464532..8fcc4e67 100644 --- a/.nuget/Savvyio.Messaging/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Messaging/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Messaging/README.md b/.nuget/Savvyio.Messaging/README.md index a1024a65..a75d08a4 100644 --- a/.nuget/Savvyio.Messaging/README.md +++ b/.nuget/Savvyio.Messaging/README.md @@ -6,7 +6,7 @@ Provides the fundamental classes to work with Messaging in a distributed archite An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/.nuget/Savvyio.Queries/PackageReleaseNotes.txt b/.nuget/Savvyio.Queries/PackageReleaseNotes.txt index f53efd74..924b3641 100644 --- a/.nuget/Savvyio.Queries/PackageReleaseNotes.txt +++ b/.nuget/Savvyio.Queries/PackageReleaseNotes.txt @@ -1,3 +1,12 @@ +Version: 6.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + +# Breaking Changes +- REMOVED .NET 9 support; consumers must target .NET 10 or later + Version: 5.0.13 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Savvyio.Queries/README.md b/.nuget/Savvyio.Queries/README.md index 747c9d53..769acc05 100644 --- a/.nuget/Savvyio.Queries/README.md +++ b/.nuget/Savvyio.Queries/README.md @@ -6,7 +6,7 @@ Provides the fundamental classes and interfaces to work with the Query part of t An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/AGENTS.md b/AGENTS.md index 32523016..b23aba2c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ This document provides guidance for AI agents working in the Savvy I/O repositor **Savvy I/O** is an open-source .NET class library (MIT license) providing a SOLID and clean foundation for writing DDD (Domain-Driven Design), CQRS (Command Query Responsibility Segregation), and Event Sourcing applications. It is designed to be intuitive and removes the complexity normally associated with these architectural patterns. -The solution targets .NET 10.0 and .NET 9.0. It comprises: +The solution targets .NET 11.0 and .NET 10.0. It comprises: - **Core Libraries** — DDD, CQRS, Commands, Queries, Event-Driven, and Domain Event Sourcing - **Data Access Extensions** — Support for Dapper, Entity Framework Core diff --git a/CHANGELOG.md b/CHANGELOG.md index 72312a73..e05bf1e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), For more details, please refer to `PackageReleaseNotes.txt` on a per assembly basis in the `.nuget` folder. +## [6.0.0] - 2026-11-10 + +This major release adds .NET 11 support, removes .NET 9 support, and aligns configured options with Microsoft Options integration so direct options and `IOptions.Value` share one configuration and validation pipeline. + +### Added + +- .NET 11 support alongside .NET 10 across packages and tests, with matching .NET 11 Entity Framework Core, SQLite, logging abstractions, and cryptography dependencies for net11.0. + +### Changed + +- CI provisions preview SDKs and tests .NET 11, preserving the single-target CI policy; local tests cover both supported targets, +- Updated Codebelt.Coverlet.MTP from 10.1.0 to 10.1.1, +- Updated framework availability in contributor guidance, package READMEs, and DocFX includes; retained .NET 10 for DocFX metadata generation, +- Service-update automation preserves pending major/minor release versions and uses the modern framework availability fallback, +- Configured options now use Cuemon's `AddConfiguredOptions`: direct `TOptions` and the default `IOptions.Value` share one instance and the Microsoft configuration, post-configuration, and validation pipeline, avoiding the previous separate eager instance and repeated setup execution for these two consumption forms, +- Options configuration and validation are deferred until materialization; recoverable parameter-object validation failures surface as `OptionsValidationException` instead of `ArgumentException` during registration. The first `AddConfiguredOptions` registration per options type is retained; use `Configure` for additional composition. Injectable `Action` remains the original setup delegate and does not include the Microsoft pipeline. + +### Removed + +- .NET 9 support. Consumers must target .NET 10 or later, +- The .NET 9-only System.Linq.Async reference; both supported frameworks provide async LINQ, +- `Savvyio.Reflection.AssemblyContext`; dispatcher, handler, and message converter discovery now calls `AssemblyContext.GetCurrentDomainAssemblies` from `Cuemon.Reflection`, +- `Savvyio.Extensions.DependencyInjection.ServiceCollectionExtensions.AddConfiguredOptions`; import `Cuemon.Extensions.DependencyInjection` or update fully qualified calls to use the Cuemon implementation. + ## [5.0.13] - 2026-10-18 This is a patch release focused on DocFX publishing pipeline hardening, expanded test coverage for configured options, discovery, and JSON converters, shared dependency and Entity Framework Core updates, documentation sitemap correction, and consolidated repository agent guidance. @@ -1171,6 +1195,7 @@ Noticeable highlights: - QueryHandler class in the Savvyio.Queries namespace that defines a generic and consistent way of handling Query objects that implements the IQuery interface - SavvyioOptionsExtensions class in the Savvyio.Queries namespace that consist of extension methods for the SavvyioOptions class: AddQueryHandler, AddQueryDispatcher +[6.0.0]: https://github.com/codebeltnet/savvyio/compare/v5.0.13...v6.0.0 [5.0.13]: https://github.com/codebeltnet/savvyio/compare/v5.0.12...v5.0.13 [5.0.12]: https://github.com/codebeltnet/savvyio/compare/v5.0.11...v5.0.12 [5.0.11]: https://github.com/codebeltnet/savvyio/compare/v5.0.10...v5.0.11 diff --git a/Directory.Build.props b/Directory.Build.props index 8ce7f192..17472915 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,7 +13,7 @@ - net10.0;net9.0 + net11.0;net10.0 Copyright © Geekle 2021-2026. All rights reserved. gimlichael Geekle @@ -46,16 +46,17 @@ + - net10.0 + net11.0 - net10.0;net9.0 + net11.0;net10.0 diff --git a/Directory.Build.targets b/Directory.Build.targets index 00bb68a3..f6a6a003 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,4 +1,4 @@ - + $([System.IO.File]::ReadAllText('$(PackageReleaseNotesFile)')) diff --git a/Directory.Packages.props b/Directory.Packages.props index d33419e8..e71523d3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,54 +4,56 @@ true - - + + - - - - - - - + + + + + + + - - + - - - + + - - - + + + + - - - - - + + + + + + + - - - - - + + + + + + - \ No newline at end of file + diff --git a/Dockerfile.tests b/Dockerfile.tests deleted file mode 100644 index 644c40dc..00000000 --- a/Dockerfile.tests +++ /dev/null @@ -1,54 +0,0 @@ -# syntax=docker/dockerfile:1.7 - -ARG DOTNET_VERSION=10.0 -ARG UBUNTU_FLAVOR=noble - -FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-${UBUNTU_FLAVOR} - -ARG DOTNET_VERSION - -ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \ - DOTNET_NOLOGO=1 \ - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 \ - NUGET_XMLDOC_MODE=skip \ - CI=false - -WORKDIR /work - -# The .NET 10 SDK can build net9.0, but it does not include the net9.0 runtime. -# Your local test projects target both net10.0 and net9.0 when CI != true. -RUN if [ "${DOTNET_VERSION}" = "10.0" ]; then \ - apt-get update \ - && apt-get install -y --no-install-recommends curl ca-certificates git \ - && curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \ - && chmod +x /tmp/dotnet-install.sh \ - && /tmp/dotnet-install.sh --channel 9.0 --runtime dotnet --install-dir /usr/share/dotnet \ - && rm -rf /tmp/dotnet-install.sh /var/lib/apt/lists/*; \ - else \ - apt-get update \ - && apt-get install -y --no-install-recommends ca-certificates git \ - && rm -rf /var/lib/apt/lists/*; \ - fi - -COPY . . - -ENTRYPOINT ["/bin/bash", "-lc"] - -CMD [ "\ - set -euo pipefail; \ - project=\"${TEST_PROJECT:-Savvyio.slnx}\"; \ - configuration=\"${CONFIGURATION:-Debug}\"; \ - framework=\"${TEST_FRAMEWORK:-}\"; \ - filter=\"${TEST_FILTER:-}\"; \ - echo \"Architecture: $(uname -m)\"; \ - echo \"Project: $project\"; \ - echo \"Configuration: $configuration\"; \ - echo \"Framework: ${framework:-all}\"; \ - dotnet --info; \ - dotnet restore \"$project\"; \ - args=(dotnet test \"$project\" --no-restore --configuration \"$configuration\" -p:SkipSignAssembly=true --logger \"console;verbosity=normal\"); \ - if [ -n \"$framework\" ]; then args+=(--framework \"$framework\"); fi; \ - if [ -n \"$filter\" ]; then args+=(--filter \"$filter\"); fi; \ - printf 'Command: '; printf '%q ' \"${args[@]}\"; echo; \ - \"${args[@]}\" \ -" ] diff --git a/README.md b/README.md index 8e99610a..95a5c235 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ An open-source project (MIT license) that provides a SOLID and clean .NET class library for writing DDD, CQRS and Event Sourcing applications. -Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 9` and `.NET 10`. +Your versatile DDD, CQRS and Event Sourcing companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/src/Savvyio.App/Savvyio.App.csproj b/src/Savvyio.App/Savvyio.App.csproj index 4eaf40d6..d265459d 100644 --- a/src/Savvyio.App/Savvyio.App.csproj +++ b/src/Savvyio.App/Savvyio.App.csproj @@ -1,4 +1,4 @@ - + false diff --git a/src/Savvyio.Commands.Messaging/CommandExtensions.cs b/src/Savvyio.Commands.Messaging/CommandExtensions.cs index 4be5a781..2e517912 100644 --- a/src/Savvyio.Commands.Messaging/CommandExtensions.cs +++ b/src/Savvyio.Commands.Messaging/CommandExtensions.cs @@ -1,37 +1,36 @@ -using System; +using System; using Cuemon; using Savvyio.Messaging; -namespace Savvyio.Commands.Messaging +namespace Savvyio.Commands.Messaging; + +/// +/// Extension methods for the interface. +/// +public static class CommandExtensions { /// - /// Extension methods for the interface. + /// Encloses the specified to an instance of . /// - public static class CommandExtensions + /// The type of the payload constraint to the interface. + /// The payload to attach within the message. + /// The context that describes the origin of the message. + /// The type that describes the type of related to the originating occurrence. + /// The which may be configured. + /// An instance of constraint to the interface. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// failed to configure an instance of in a valid state. + /// + public static IMessage ToMessage(this T command, Uri source, string type, Action setup = null) where T : ICommand { - /// - /// Encloses the specified to an instance of . - /// - /// The type of the payload constraint to the interface. - /// The payload to attach within the message. - /// The context that describes the origin of the message. - /// The type that describes the type of related to the originating occurrence. - /// The which may be configured. - /// An instance of constraint to the interface. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// failed to configure an instance of in a valid state. - /// - public static IMessage ToMessage(this T command, Uri source, string type, Action setup = null) where T : ICommand - { - Validator.ThrowIfNull(command); - Validator.ThrowIfNull(source); - Validator.ThrowIfNullOrWhitespace(type); - Validator.ThrowIfInvalidConfigurator(setup, out var options); - return new Message(options.MessageId, source, type, command, options.Time); - } + Validator.ThrowIfNull(command); + Validator.ThrowIfNull(source); + Validator.ThrowIfNullOrWhitespace(type); + Validator.ThrowIfInvalidConfigurator(setup, out var options); + return new Message(options.MessageId, source, type, command, options.Time); } } diff --git a/src/Savvyio.Commands.Messaging/InMemoryCommandQueue.cs b/src/Savvyio.Commands.Messaging/InMemoryCommandQueue.cs index 3aae3ad3..c9728e46 100644 --- a/src/Savvyio.Commands.Messaging/InMemoryCommandQueue.cs +++ b/src/Savvyio.Commands.Messaging/InMemoryCommandQueue.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Channels; using System.Threading.Tasks; @@ -6,43 +6,42 @@ using Cuemon.Threading; using Savvyio.Messaging; -namespace Savvyio.Commands.Messaging +namespace Savvyio.Commands.Messaging; + +/// +/// Provides an in-memory implementation of the interface useful for unit testing and the likes thereof. +/// +public class InMemoryCommandQueue : IPointToPointChannel { + private readonly Channel> _channel = Channel.CreateUnbounded>(); + /// - /// Provides an in-memory implementation of the interface useful for unit testing and the likes thereof. + /// Sends the specified whose generic type argument is asynchronous using Point-to-Point Channel/P2P MEP. /// - public class InMemoryCommandQueue : IPointToPointChannel + /// The enclosed messages to send. + /// The which may be configured. + /// A that represents the asynchronous operation. + public async Task SendAsync(IEnumerable> messages, Action setup = null) { - private readonly Channel> _channel = Channel.CreateUnbounded>(); - - /// - /// Sends the specified whose generic type argument is asynchronous using Point-to-Point Channel/P2P MEP. - /// - /// The enclosed messages to send. - /// The which may be configured. - /// A that represents the asynchronous operation. - public async Task SendAsync(IEnumerable> messages, Action setup = null) + var options = setup.Configure(); + foreach (var command in messages) { - var options = setup.Configure(); - foreach (var command in messages) - { - await _channel.Writer.WriteAsync(command, options.CancellationToken).ConfigureAwait(false); - } + await _channel.Writer.WriteAsync(command, options.CancellationToken).ConfigureAwait(false); } + } - /// - /// Receive one or more command(s) asynchronous using Point-to-Point Channel/P2P MEP. - /// - /// The which may be configured. - /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . - public async IAsyncEnumerable> ReceiveAsync(Action setup = null) + /// + /// Receive one or more command(s) asynchronous using Point-to-Point Channel/P2P MEP. + /// + /// The which may be configured. + /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . + public async IAsyncEnumerable> ReceiveAsync(Action setup = null) + { + var options = setup.Configure(); + await foreach (var message in _channel.Reader.ReadAllAsync(options.CancellationToken).ConfigureAwait(false)) { - var options = setup.Configure(); - await foreach (var message in _channel.Reader.ReadAllAsync(options.CancellationToken).ConfigureAwait(false)) - { - yield return message; - if (_channel.Reader.Count == 0) { yield break; } - } + yield return message; + if (_channel.Reader.Count == 0) { yield break; } } } } diff --git a/src/Savvyio.Commands.Messaging/Savvyio.Commands.Messaging.csproj b/src/Savvyio.Commands.Messaging/Savvyio.Commands.Messaging.csproj index bfd43845..b21967cc 100644 --- a/src/Savvyio.Commands.Messaging/Savvyio.Commands.Messaging.csproj +++ b/src/Savvyio.Commands.Messaging/Savvyio.Commands.Messaging.csproj @@ -1,4 +1,4 @@ - + Provides ways to scale out Commands using distributed subsystems. diff --git a/src/Savvyio.Commands/Command.cs b/src/Savvyio.Commands/Command.cs index f660e90f..80ace916 100644 --- a/src/Savvyio.Commands/Command.cs +++ b/src/Savvyio.Commands/Command.cs @@ -1,22 +1,21 @@ -using System; +using System; -namespace Savvyio.Commands +namespace Savvyio.Commands; + +/// +/// Provides a default implementation of the interface. +/// +/// +/// +public abstract record Command : Request, ICommand { /// - /// Provides a default implementation of the interface. + /// Initializes a new instance of the class. /// - /// - /// - public abstract record Command : Request, ICommand + /// The optional metadata to merge with this instance. + protected Command(IMetadata metadata = null) { - /// - /// Initializes a new instance of the class. - /// - /// The optional metadata to merge with this instance. - protected Command(IMetadata metadata = null) - { - this.SetCorrelationId(Guid.NewGuid().ToString("N")); - this.MergeMetadata(metadata); - } + this.SetCorrelationId(Guid.NewGuid().ToString("N")); + this.MergeMetadata(metadata); } } diff --git a/src/Savvyio.Commands/CommandDispatcher.cs b/src/Savvyio.Commands/CommandDispatcher.cs index 0f986c37..3116bfde 100644 --- a/src/Savvyio.Commands/CommandDispatcher.cs +++ b/src/Savvyio.Commands/CommandDispatcher.cs @@ -1,46 +1,45 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon; using Cuemon.Threading; using Savvyio.Dispatchers; -namespace Savvyio.Commands +namespace Savvyio.Commands; + +/// +/// Provides a default implementation of of the interface. +/// +/// +/// +public class CommandDispatcher : FireForgetDispatcher, ICommandDispatcher { /// - /// Provides a default implementation of of the interface. + /// Initializes a new instance of the class. /// - /// - /// - public class CommandDispatcher : FireForgetDispatcher, ICommandDispatcher + /// The provider of service implementations. + public CommandDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) { - /// - /// Initializes a new instance of the class. - /// - /// The provider of service implementations. - public CommandDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) - { - } + } - /// - /// Commits the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to commit. - public void Commit(ICommand request) - { - Validator.ThrowIfNull(request); - Dispatch(request, handler => handler.Delegates); - } + /// + /// Commits the specified using Fire-and-Forget/In-Only MEP. + /// + /// The to commit. + public void Commit(ICommand request) + { + Validator.ThrowIfNull(request); + Dispatch(request, handler => handler.Delegates); + } - /// - /// Commits the specified asynchronous using Fire-and-Forget/In-Only MEP. - /// - /// The to commit. - /// The which may be configured. - /// A that represents the asynchronous operation. - public Task CommitAsync(ICommand request, Action setup = null) - { - Validator.ThrowIfNull(request); - return DispatchAsync(request, handler => handler.Delegates, setup); - } + /// + /// Commits the specified asynchronous using Fire-and-Forget/In-Only MEP. + /// + /// The to commit. + /// The which may be configured. + /// A that represents the asynchronous operation. + public Task CommitAsync(ICommand request, Action setup = null) + { + Validator.ThrowIfNull(request); + return DispatchAsync(request, handler => handler.Delegates, setup); } } diff --git a/src/Savvyio.Commands/CommandHandler.cs b/src/Savvyio.Commands/CommandHandler.cs index 048d5a19..3c0ad0e2 100644 --- a/src/Savvyio.Commands/CommandHandler.cs +++ b/src/Savvyio.Commands/CommandHandler.cs @@ -1,31 +1,30 @@ -using Savvyio.Handlers; +using Savvyio.Handlers; -namespace Savvyio.Commands +namespace Savvyio.Commands; + +/// +/// Provides a generic and consistent way of handling Command objects that implements the interface. This is an abstract class. +/// +/// +public abstract class CommandHandler : ICommandHandler { /// - /// Provides a generic and consistent way of handling Command objects that implements the interface. This is an abstract class. + /// Initializes a new instance of the class. /// - /// - public abstract class CommandHandler : ICommandHandler + protected CommandHandler() { - /// - /// Initializes a new instance of the class. - /// - protected CommandHandler() - { - Delegates = HandlerFactory.CreateFireForget(RegisterDelegates); - } + Delegates = HandlerFactory.CreateFireForget(RegisterDelegates); + } - /// - /// Registers the delegates responsible for handling types that implements the interface. - /// - /// The registry that store the delegates of type . - protected abstract void RegisterDelegates(IFireForgetRegistry handlers); + /// + /// Registers the delegates responsible for handling types that implements the interface. + /// + /// The registry that store the delegates of type . + protected abstract void RegisterDelegates(IFireForgetRegistry handlers); - /// - /// Gets the activator responsible for invoking delegates that handles . - /// - /// The activator responsible for invoking delegates that handles . - public IFireForgetActivator Delegates { get; } - } + /// + /// Gets the activator responsible for invoking delegates that handles . + /// + /// The activator responsible for invoking delegates that handles . + public IFireForgetActivator Delegates { get; } } diff --git a/src/Savvyio.Commands/Savvyio.Commands.csproj b/src/Savvyio.Commands/Savvyio.Commands.csproj index e5ef97a6..f9d202f7 100644 --- a/src/Savvyio.Commands/Savvyio.Commands.csproj +++ b/src/Savvyio.Commands/Savvyio.Commands.csproj @@ -1,4 +1,4 @@ - + Provides the fundamental classes to work with the Command part of the CQRS pattern (Command and Query Responsibility Segregation). diff --git a/src/Savvyio.Commands/SavvyioOptionsExtensions.cs b/src/Savvyio.Commands/SavvyioOptionsExtensions.cs index 5c89d93a..0f118d64 100644 --- a/src/Savvyio.Commands/SavvyioOptionsExtensions.cs +++ b/src/Savvyio.Commands/SavvyioOptionsExtensions.cs @@ -1,30 +1,29 @@ -namespace Savvyio.Commands +namespace Savvyio.Commands; + +/// +/// Extension methods for the class. +/// +public static class SavvyioOptionsExtensions { /// - /// Extension methods for the class. + /// Adds an implementation of the interface to (if not already registered). /// - public static class SavvyioOptionsExtensions + /// The type that implements the interface. + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + public static SavvyioOptions AddCommandHandler(this SavvyioOptions options) where TImplementation : class, ICommandHandler { - /// - /// Adds an implementation of the interface to (if not already registered). - /// - /// The type that implements the interface. - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - public static SavvyioOptions AddCommandHandler(this SavvyioOptions options) where TImplementation : class, ICommandHandler - { - options.AddHandler(); - return options; - } + options.AddHandler(); + return options; + } - /// - /// Adds a default implementation of the interface. - /// - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - public static SavvyioOptions AddCommandDispatcher(this SavvyioOptions options) - { - return options.AddDispatcher(); - } + /// + /// Adds a default implementation of the interface. + /// + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + public static SavvyioOptions AddCommandDispatcher(this SavvyioOptions options) + { + return options.AddDispatcher(); } } diff --git a/src/Savvyio.Core/AsyncEventHandler.cs b/src/Savvyio.Core/AsyncEventHandler.cs index 01b76aab..3729c2bd 100644 --- a/src/Savvyio.Core/AsyncEventHandler.cs +++ b/src/Savvyio.Core/AsyncEventHandler.cs @@ -1,14 +1,13 @@ -using System; +using System; using System.Threading.Tasks; -namespace Savvyio -{ - /// - /// Represents the method that will handle an event when the event provides data asynchronously. - /// - /// The type of the event data generated by the event. - /// The source of the event. - /// An object that contains the event data. - /// A task that represents the asynchronous operation. - public delegate Task AsyncEventHandler(object sender, TEventArgs e) where TEventArgs : EventArgs; -} +namespace Savvyio; + +/// +/// Represents the method that will handle an event when the event provides data asynchronously. +/// +/// The type of the event data generated by the event. +/// The source of the event. +/// An object that contains the event data. +/// A task that represents the asynchronous operation. +public delegate Task AsyncEventHandler(object sender, TEventArgs e) where TEventArgs : EventArgs; diff --git a/src/Savvyio.Core/Commands/ICommand.cs b/src/Savvyio.Core/Commands/ICommand.cs index 86d4bd90..fd4a09db 100644 --- a/src/Savvyio.Core/Commands/ICommand.cs +++ b/src/Savvyio.Core/Commands/ICommand.cs @@ -1,9 +1,8 @@ -namespace Savvyio.Commands +namespace Savvyio.Commands; + +/// +/// A marker interface that specifies an intention to do something (e.g. change the state). +/// +public interface ICommand : IRequest, IMetadata { - /// - /// A marker interface that specifies an intention to do something (e.g. change the state). - /// - public interface ICommand : IRequest, IMetadata - { - } } diff --git a/src/Savvyio.Core/Commands/ICommandDispatcher.cs b/src/Savvyio.Core/Commands/ICommandDispatcher.cs index 502b5ac9..9ab8aebb 100644 --- a/src/Savvyio.Core/Commands/ICommandDispatcher.cs +++ b/src/Savvyio.Core/Commands/ICommandDispatcher.cs @@ -1,28 +1,27 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; using Savvyio.Dispatchers; -namespace Savvyio.Commands +namespace Savvyio.Commands; + +/// +/// Defines a Command dispatcher that uses Fire-and-Forget/In-Only MEP. +/// +/// +public interface ICommandDispatcher : IDispatcher { /// - /// Defines a Command dispatcher that uses Fire-and-Forget/In-Only MEP. + /// Commits the specified using Fire-and-Forget/In-Only MEP. /// - /// - public interface ICommandDispatcher : IDispatcher - { - /// - /// Commits the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to commit. - void Commit(ICommand request); + /// The to commit. + void Commit(ICommand request); - /// - /// Commits the specified asynchronous using Fire-and-Forget/In-Only MEP. - /// - /// The to commit. - /// The which may be configured. - /// A that represents the asynchronous operation. - Task CommitAsync(ICommand request, Action setup = null); - } + /// + /// Commits the specified asynchronous using Fire-and-Forget/In-Only MEP. + /// + /// The to commit. + /// The which may be configured. + /// A that represents the asynchronous operation. + Task CommitAsync(ICommand request, Action setup = null); } diff --git a/src/Savvyio.Core/Commands/ICommandHandler.cs b/src/Savvyio.Core/Commands/ICommandHandler.cs index 694e4a44..ded321a9 100644 --- a/src/Savvyio.Core/Commands/ICommandHandler.cs +++ b/src/Savvyio.Core/Commands/ICommandHandler.cs @@ -1,12 +1,11 @@ -using Savvyio.Handlers; +using Savvyio.Handlers; -namespace Savvyio.Commands +namespace Savvyio.Commands; + +/// +/// Specifies a handler responsible for objects that implements the interface. +/// +/// +public interface ICommandHandler : IFireForgetHandler { - /// - /// Specifies a handler responsible for objects that implements the interface. - /// - /// - public interface ICommandHandler : IFireForgetHandler - { - } } diff --git a/src/Savvyio.Core/Data/IDataStore.cs b/src/Savvyio.Core/Data/IDataStore.cs index 83114967..331fc59b 100644 --- a/src/Savvyio.Core/Data/IDataStore.cs +++ b/src/Savvyio.Core/Data/IDataStore.cs @@ -1,10 +1,9 @@ -namespace Savvyio.Data +namespace Savvyio.Data; + +/// +/// A marker interface that specifies an abstraction of data persistence based on the Data Access Object pattern aka DAO. +/// +/// The type of the DTO. +public interface IDataStore where T : class { - /// - /// A marker interface that specifies an abstraction of data persistence based on the Data Access Object pattern aka DAO. - /// - /// The type of the DTO. - public interface IDataStore where T : class - { - } } diff --git a/src/Savvyio.Core/Data/IDeletableDataStore.cs b/src/Savvyio.Core/Data/IDeletableDataStore.cs index ec422a50..a2520fb1 100644 --- a/src/Savvyio.Core/Data/IDeletableDataStore.cs +++ b/src/Savvyio.Core/Data/IDeletableDataStore.cs @@ -1,22 +1,21 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; -namespace Savvyio.Data +namespace Savvyio.Data; + +/// +/// Defines a generic way of abstracting deletable data access objects (cruD). +/// +/// The type of the DTO. +/// +public interface IDeletableDataStore : IDataStore where T : class { /// - /// Defines a generic way of abstracting deletable data access objects (cruD). + /// Deletes the specified asynchronous. /// - /// The type of the DTO. - /// - public interface IDeletableDataStore : IDataStore where T : class - { - /// - /// Deletes the specified asynchronous. - /// - /// The object to delete. - /// The which may be configured. - /// A that represents the asynchronous operation. - Task DeleteAsync(T dto, Action setup = null); - } + /// The object to delete. + /// The which may be configured. + /// A that represents the asynchronous operation. + Task DeleteAsync(T dto, Action setup = null); } diff --git a/src/Savvyio.Core/Data/IPersistentDataStore.cs b/src/Savvyio.Core/Data/IPersistentDataStore.cs index 8ffc5168..7a06bbd8 100644 --- a/src/Savvyio.Core/Data/IPersistentDataStore.cs +++ b/src/Savvyio.Core/Data/IPersistentDataStore.cs @@ -1,19 +1,18 @@ -using Cuemon.Threading; +using Cuemon.Threading; -namespace Savvyio.Data +namespace Savvyio.Data; + +/// +/// Defines a generic way of abstracting persistent data access objects (CRUD). +/// +/// The type of the DTO. +/// The type of options associated with this DTO. +/// +/// +/// +/// +public interface IPersistentDataStore : IWritableDataStore, IReadableDataStore, ISearchableDataStore, IDeletableDataStore + where T : class + where TOptions : AsyncOptions, new() { - /// - /// Defines a generic way of abstracting persistent data access objects (CRUD). - /// - /// The type of the DTO. - /// The type of options associated with this DTO. - /// - /// - /// - /// - public interface IPersistentDataStore : IWritableDataStore, IReadableDataStore, ISearchableDataStore, IDeletableDataStore - where T : class - where TOptions : AsyncOptions, new() - { - } } diff --git a/src/Savvyio.Core/Data/IReadableDataStore.cs b/src/Savvyio.Core/Data/IReadableDataStore.cs index 9b24b85d..096b17b9 100644 --- a/src/Savvyio.Core/Data/IReadableDataStore.cs +++ b/src/Savvyio.Core/Data/IReadableDataStore.cs @@ -1,23 +1,21 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; -namespace Savvyio.Data -{ +namespace Savvyio.Data; + +/// +/// Defines a generic way of abstracting readable data access objects (cRud). +/// +/// The type of the DTO. +public interface IReadableDataStore : IDataStore where T : class +{ /// - /// Defines a generic way of abstracting readable data access objects (cRud). + /// Loads the object from the specified asynchronous. /// - /// The type of the DTO. - public interface IReadableDataStore : IDataStore where T : class - - { - /// - /// Loads the object from the specified asynchronous. - /// - /// The key that uniquely identifies the object. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the object of the operation or null if not found. - Task GetByIdAsync(object id, Action setup = null); - } + /// The key that uniquely identifies the object. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the object of the operation or null if not found. + Task GetByIdAsync(object id, Action setup = null); } diff --git a/src/Savvyio.Core/Data/ISearchableDataStore.cs b/src/Savvyio.Core/Data/ISearchableDataStore.cs index b8ec0380..0d833666 100644 --- a/src/Savvyio.Core/Data/ISearchableDataStore.cs +++ b/src/Savvyio.Core/Data/ISearchableDataStore.cs @@ -1,25 +1,24 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon.Threading; -namespace Savvyio.Data +namespace Savvyio.Data; + +/// +/// Defines a generic way of abstracting searchable data access objects (cRud). +/// +/// The type of the DTO. +/// The type of the options associated with this DTO. +/// +public interface ISearchableDataStore : IDataStore + where T : class + where TOptions : AsyncOptions, new() { /// - /// Defines a generic way of abstracting searchable data access objects (cRud). + /// Finds all objects matching the specified asynchronous. /// - /// The type of the DTO. - /// The type of the options associated with this DTO. - /// - public interface ISearchableDataStore : IDataStore - where T : class - where TOptions : AsyncOptions, new() - { - /// - /// Finds all objects matching the specified asynchronous. - /// - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the matching objects of the operation or an empty sequence if no match was found. - Task> FindAllAsync(Action setup = null); - } + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the matching objects of the operation or an empty sequence if no match was found. + Task> FindAllAsync(Action setup = null); } diff --git a/src/Savvyio.Core/Data/IWritableDataStore.cs b/src/Savvyio.Core/Data/IWritableDataStore.cs index c99ff9e6..8fd6b774 100644 --- a/src/Savvyio.Core/Data/IWritableDataStore.cs +++ b/src/Savvyio.Core/Data/IWritableDataStore.cs @@ -1,30 +1,29 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; -namespace Savvyio.Data +namespace Savvyio.Data; + +/// +/// Defines a generic way of abstracting writable data access objects (CrUd). +/// +/// The type of the DTO. +/// +public interface IWritableDataStore : IDataStore where T : class { /// - /// Defines a generic way of abstracting writable data access objects (CrUd). + /// Creates the specified asynchronous. /// - /// The type of the DTO. - /// - public interface IWritableDataStore : IDataStore where T : class - { - /// - /// Creates the specified asynchronous. - /// - /// The object to create. - /// The which may be configured. - /// A that represents the asynchronous operation. - Task CreateAsync(T dto, Action setup = null); + /// The object to create. + /// The which may be configured. + /// A that represents the asynchronous operation. + Task CreateAsync(T dto, Action setup = null); - /// - /// Updates the specified asynchronous. - /// - /// The object to update. - /// The which may be configured. - /// A that represents the asynchronous operation. - Task UpdateAsync(T dto, Action setup = null); - } + /// + /// Updates the specified asynchronous. + /// + /// The object to update. + /// The which may be configured. + /// A that represents the asynchronous operation. + Task UpdateAsync(T dto, Action setup = null); } diff --git a/src/Savvyio.Core/Diagnostics/IAsyncHealthCheckProvider.cs b/src/Savvyio.Core/Diagnostics/IAsyncHealthCheckProvider.cs index f7513a65..adf360ee 100644 --- a/src/Savvyio.Core/Diagnostics/IAsyncHealthCheckProvider.cs +++ b/src/Savvyio.Core/Diagnostics/IAsyncHealthCheckProvider.cs @@ -1,19 +1,18 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; -namespace Savvyio.Diagnostics +namespace Savvyio.Diagnostics; + +/// +/// Defines a contract for asynchronously providing an underlying target that can be probed to assess health status. +/// +/// The type of the underlying target used for health probing. +public interface IAsyncHealthCheckProvider { /// - /// Defines a contract for asynchronously providing an underlying target that can be probed to assess health status. + /// Asynchronously gets the underlying target used for probing health status. /// - /// The type of the underlying target used for health probing. - public interface IAsyncHealthCheckProvider - { - /// - /// Asynchronously gets the underlying target used for probing health status. - /// - /// A that can be used to cancel the asynchronous operation. - /// A task that represents the asynchronous operation. The task result contains the target instance of type used for health probing. - Task GetHealthCheckTargetAsync(CancellationToken cancellationToken); - } + /// A that can be used to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. The task result contains the target instance of type used for health probing. + Task GetHealthCheckTargetAsync(CancellationToken cancellationToken); } diff --git a/src/Savvyio.Core/Diagnostics/IHealthCheckProvider.cs b/src/Savvyio.Core/Diagnostics/IHealthCheckProvider.cs index 6d47fff5..01d23960 100644 --- a/src/Savvyio.Core/Diagnostics/IHealthCheckProvider.cs +++ b/src/Savvyio.Core/Diagnostics/IHealthCheckProvider.cs @@ -1,15 +1,14 @@ -namespace Savvyio.Diagnostics +namespace Savvyio.Diagnostics; + +/// +/// Defines a contract for providing an underlying target that can be probed to assess health status. +/// +/// The type of the underlying target used for health probing. +public interface IHealthCheckProvider { /// - /// Defines a contract for providing an underlying target that can be probed to assess health status. + /// Gets the underlying target used for probing health status. /// - /// The type of the underlying target used for health probing. - public interface IHealthCheckProvider - { - /// - /// Gets the underlying target used for probing health status. - /// - /// The target instance of type used for health probing. - T GetHealthCheckTarget(); - } + /// The target instance of type used for health probing. + T GetHealthCheckTarget(); } diff --git a/src/Savvyio.Core/Dispatchers/Dispatcher.cs b/src/Savvyio.Core/Dispatchers/Dispatcher.cs index 58ea5081..9ce2f370 100644 --- a/src/Savvyio.Core/Dispatchers/Dispatcher.cs +++ b/src/Savvyio.Core/Dispatchers/Dispatcher.cs @@ -1,29 +1,28 @@ -using System; +using System; using System.Collections.Generic; using Cuemon; -namespace Savvyio.Dispatchers +namespace Savvyio.Dispatchers; + +/// +/// Represents the base class from which all implementations of the dispatcher concept should derive. This is an abstract class. +/// +/// +public abstract class Dispatcher : IDispatcher { /// - /// Represents the base class from which all implementations of the dispatcher concept should derive. This is an abstract class. + /// Initializes a new instance of the class. /// - /// - public abstract class Dispatcher : IDispatcher + /// The provider of service implementations. + protected Dispatcher(IServiceLocator serviceLocator) { - /// - /// Initializes a new instance of the class. - /// - /// The provider of service implementations. - protected Dispatcher(IServiceLocator serviceLocator) - { - Validator.ThrowIfNull(serviceLocator); - ServiceFactory = serviceLocator.GetServices; - } - - /// - /// Gets the function delegate that creates the services. - /// - /// The function delegate that creates the services. - protected Func> ServiceFactory { get; } + Validator.ThrowIfNull(serviceLocator); + ServiceFactory = serviceLocator.GetServices; } + + /// + /// Gets the function delegate that creates the services. + /// + /// The function delegate that creates the services. + protected Func> ServiceFactory { get; } } diff --git a/src/Savvyio.Core/Dispatchers/FireForgetDispatcher.cs b/src/Savvyio.Core/Dispatchers/FireForgetDispatcher.cs index 49bd412c..abc3c095 100644 --- a/src/Savvyio.Core/Dispatchers/FireForgetDispatcher.cs +++ b/src/Savvyio.Core/Dispatchers/FireForgetDispatcher.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon; @@ -6,76 +6,75 @@ using Cuemon.Threading; using Savvyio.Handlers; -namespace Savvyio.Dispatchers +namespace Savvyio.Dispatchers; + +/// +/// Provides a generic dispatcher that uses Fire-and-Forget/In-Only MEP. This is an abstract class. +/// +/// +public abstract class FireForgetDispatcher : Dispatcher { /// - /// Provides a generic dispatcher that uses Fire-and-Forget/In-Only MEP. This is an abstract class. + /// Initializes a new instance of the class. /// - /// - public abstract class FireForgetDispatcher : Dispatcher + /// The provider of service implementations. + protected FireForgetDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) { - /// - /// Initializes a new instance of the class. - /// - /// The provider of service implementations. - protected FireForgetDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) - { - } + } - /// - /// Dispatches the specified using Fire-and-Forget/In-Only MEP. - /// - /// The type of the input model registered to a handler. - /// The type of the handler to locate the input model. - /// The model that is being handled by a registered delegate. - /// The function delegate that will locate and invoke the handler registered to the specified . - /// A can be a command, domain event, integration event, query etc. - protected virtual void Dispatch(TRequest request, Func> handlerFactory) - where TRequest : IRequest - where THandler : IHandler + /// + /// Dispatches the specified using Fire-and-Forget/In-Only MEP. + /// + /// The type of the input model registered to a handler. + /// The type of the handler to locate the input model. + /// The model that is being handled by a registered delegate. + /// The function delegate that will locate and invoke the handler registered to the specified . + /// A can be a command, domain event, integration event, query etc. + protected virtual void Dispatch(TRequest request, Func> handlerFactory) + where TRequest : IRequest + where THandler : IHandler + { + Validator.ThrowIfNull(request); + Validator.ThrowIfNull(handlerFactory); + var handlerType = typeof(THandler); + var hasHandler = false; + if (ServiceFactory(handlerType) is IEnumerable handlers) { - Validator.ThrowIfNull(request); - Validator.ThrowIfNull(handlerFactory); - var handlerType = typeof(THandler); - var hasHandler = false; - if (ServiceFactory(handlerType) is IEnumerable handlers) + foreach (var handler in handlers) // allow multiple handlers for same model { - foreach (var handler in handlers) // allow multiple handlers for same model - { - hasHandler |= handlerFactory(handler).TryInvoke(request); - } + hasHandler |= handlerFactory(handler).TryInvoke(request); } - if (!hasHandler) { throw OrphanedHandlerException.Create(request, nameof(request)); } } + if (!hasHandler) { throw OrphanedHandlerException.Create(request, nameof(request)); } + } - /// - /// Dispatches the specified asynchronous using Fire-and-Forget/In-Only MEP. - /// - /// The type of the input model registered to a handler. - /// The type of the handler to locate the input model. - /// The model that is being handled by a registered delegate. - /// The function delegate that will locate and invoke the handler registered to the specified . - /// The which may be configured. - /// A that represents the asynchronous operation. - /// A can be a command, domain event, integration event, query etc. - protected virtual async Task DispatchAsync(TRequest request, Func> handlerFactory, Action setup) - where TRequest : IRequest - where THandler : IHandler + /// + /// Dispatches the specified asynchronous using Fire-and-Forget/In-Only MEP. + /// + /// The type of the input model registered to a handler. + /// The type of the handler to locate the input model. + /// The model that is being handled by a registered delegate. + /// The function delegate that will locate and invoke the handler registered to the specified . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// A can be a command, domain event, integration event, query etc. + protected virtual async Task DispatchAsync(TRequest request, Func> handlerFactory, Action setup) + where TRequest : IRequest + where THandler : IHandler + { + Validator.ThrowIfNull(request); + Validator.ThrowIfNull(handlerFactory); + var options = setup.Configure(); + var handlerType = typeof(THandler); + var hasHandler = false; + if (ServiceFactory(handlerType) is IEnumerable handlers) { - Validator.ThrowIfNull(request); - Validator.ThrowIfNull(handlerFactory); - var options = setup.Configure(); - var handlerType = typeof(THandler); - var hasHandler = false; - if (ServiceFactory(handlerType) is IEnumerable handlers) + foreach (var handler in handlers) // allow multiple handlers for same model { - foreach (var handler in handlers) // allow multiple handlers for same model - { - var operation = await handlerFactory(handler).TryInvokeAsync(request, options.CancellationToken).ConfigureAwait(false); - hasHandler |= operation.Succeeded; - } + var operation = await handlerFactory(handler).TryInvokeAsync(request, options.CancellationToken).ConfigureAwait(false); + hasHandler |= operation.Succeeded; } - if (!hasHandler) { throw OrphanedHandlerException.Create(request, nameof(request)); } } + if (!hasHandler) { throw OrphanedHandlerException.Create(request, nameof(request)); } } } diff --git a/src/Savvyio.Core/Dispatchers/IDispatcher.cs b/src/Savvyio.Core/Dispatchers/IDispatcher.cs index 6e1351b6..a1827302 100644 --- a/src/Savvyio.Core/Dispatchers/IDispatcher.cs +++ b/src/Savvyio.Core/Dispatchers/IDispatcher.cs @@ -1,9 +1,8 @@ -namespace Savvyio.Dispatchers +namespace Savvyio.Dispatchers; + +/// +/// A marker interface that specifies a dispatcher that encapsulates how a set of objects interact. +/// +public interface IDispatcher { - /// - /// A marker interface that specifies a dispatcher that encapsulates how a set of objects interact. - /// - public interface IDispatcher - { - } } diff --git a/src/Savvyio.Core/Dispatchers/IServiceLocator.cs b/src/Savvyio.Core/Dispatchers/IServiceLocator.cs index 9f29b699..e41fd651 100644 --- a/src/Savvyio.Core/Dispatchers/IServiceLocator.cs +++ b/src/Savvyio.Core/Dispatchers/IServiceLocator.cs @@ -1,18 +1,17 @@ -using System; +using System; using System.Collections.Generic; -namespace Savvyio.Dispatchers +namespace Savvyio.Dispatchers; + +/// +/// Provides a generic way to locate implementations of service objects. +/// +public interface IServiceLocator { /// - /// Provides a generic way to locate implementations of service objects. + /// Get an enumeration of services of type . /// - public interface IServiceLocator - { - /// - /// Get an enumeration of services of type . - /// - /// An object that specifies the type of service object to get. - /// An enumeration of services of type . - IEnumerable GetServices(Type serviceType); - } + /// An object that specifies the type of service object to get. + /// An enumeration of services of type . + IEnumerable GetServices(Type serviceType); } diff --git a/src/Savvyio.Core/Dispatchers/RequestReplyDispatcher.cs b/src/Savvyio.Core/Dispatchers/RequestReplyDispatcher.cs index aff8159f..4db4c7a4 100644 --- a/src/Savvyio.Core/Dispatchers/RequestReplyDispatcher.cs +++ b/src/Savvyio.Core/Dispatchers/RequestReplyDispatcher.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon; @@ -6,83 +6,82 @@ using Cuemon.Threading; using Savvyio.Handlers; -namespace Savvyio.Dispatchers +namespace Savvyio.Dispatchers; + +/// +/// Provides a generic dispatcher that uses Request-Reply/In-Out MEP. This is an abstract class. +/// +/// +public abstract class RequestReplyDispatcher : Dispatcher { /// - /// Provides a generic dispatcher that uses Request-Reply/In-Out MEP. This is an abstract class. + /// Initializes a new instance of the class. /// - /// - public abstract class RequestReplyDispatcher : Dispatcher + /// The provider of service implementations. + protected RequestReplyDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) { - /// - /// Initializes a new instance of the class. - /// - /// The provider of service implementations. - protected RequestReplyDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) - { - } + } - /// - /// Dispatches the specified using Request-Reply/In-Out MEP. - /// - /// The type of the input model registered to a handler. - /// The type of the handler to locate the input model. - /// The type of the output model. - /// The model that is being handled by a registered delegate. - /// The function delegate that will locate and invoke the handler registered to the specified . - /// The response of the request. - /// A can be a command, domain event, integration event, query etc. - protected virtual TResponse Dispatch(TRequest request, Func> handlerFactory) - where TRequest : IRequest - where THandler : IHandler + /// + /// Dispatches the specified using Request-Reply/In-Out MEP. + /// + /// The type of the input model registered to a handler. + /// The type of the handler to locate the input model. + /// The type of the output model. + /// The model that is being handled by a registered delegate. + /// The function delegate that will locate and invoke the handler registered to the specified . + /// The response of the request. + /// A can be a command, domain event, integration event, query etc. + protected virtual TResponse Dispatch(TRequest request, Func> handlerFactory) + where TRequest : IRequest + where THandler : IHandler + { + Validator.ThrowIfNull(request); + Validator.ThrowIfNull(handlerFactory); + var handlerType = typeof(THandler); + if (ServiceFactory(handlerType) is IEnumerable handlers) { - Validator.ThrowIfNull(request); - Validator.ThrowIfNull(handlerFactory); - var handlerType = typeof(THandler); - if (ServiceFactory(handlerType) is IEnumerable handlers) + foreach (var handler in handlers) { - foreach (var handler in handlers) + if (handlerFactory(handler).TryInvoke(request, out TResponse result)) { - if (handlerFactory(handler).TryInvoke(request, out TResponse result)) - { - return result; - } + return result; } } - throw OrphanedHandlerException.Create(request, nameof(request)); } + throw OrphanedHandlerException.Create(request, nameof(request)); + } - /// - /// Dispatches the specified asynchronous using Request-Reply/In-Out MEP. - /// - /// The type of the input model registered to a handler. - /// The type of the handler to locate the input model. - /// The type of the output model. - /// The model that is being handled by a registered delegate. - /// The function delegate that will locate and invoke the handler registered to the specified . - /// The which may be configured. - /// A task that represents the asynchronous operation. The task result contains the response of the request. - /// A can be a command, domain event, integration event, query etc. - protected virtual async Task DispatchAsync(TRequest request, Func> handlerFactory, Action setup) - where TRequest : IRequest - where THandler : IHandler + /// + /// Dispatches the specified asynchronous using Request-Reply/In-Out MEP. + /// + /// The type of the input model registered to a handler. + /// The type of the handler to locate the input model. + /// The type of the output model. + /// The model that is being handled by a registered delegate. + /// The function delegate that will locate and invoke the handler registered to the specified . + /// The which may be configured. + /// A task that represents the asynchronous operation. The task result contains the response of the request. + /// A can be a command, domain event, integration event, query etc. + protected virtual async Task DispatchAsync(TRequest request, Func> handlerFactory, Action setup) + where TRequest : IRequest + where THandler : IHandler + { + Validator.ThrowIfNull(request); + Validator.ThrowIfNull(handlerFactory); + var options = setup.Configure(); + var handlerType = typeof(THandler); + if (ServiceFactory(handlerType) is IEnumerable handlers) { - Validator.ThrowIfNull(request); - Validator.ThrowIfNull(handlerFactory); - var options = setup.Configure(); - var handlerType = typeof(THandler); - if (ServiceFactory(handlerType) is IEnumerable handlers) + foreach (var handler in handlers) { - foreach (var handler in handlers) + var operation = await handlerFactory(handler).TryInvokeAsync(request, options.CancellationToken).ConfigureAwait(false); + if (operation.Succeeded) { - var operation = await handlerFactory(handler).TryInvokeAsync(request, options.CancellationToken).ConfigureAwait(false); - if (operation.Succeeded) - { - return operation.Result; - } + return operation.Result; } } - throw OrphanedHandlerException.Create(request, nameof(request)); } + throw OrphanedHandlerException.Create(request, nameof(request)); } } diff --git a/src/Savvyio.Core/Dispatchers/ServiceLocator.cs b/src/Savvyio.Core/Dispatchers/ServiceLocator.cs index 9030150f..68db0937 100644 --- a/src/Savvyio.Core/Dispatchers/ServiceLocator.cs +++ b/src/Savvyio.Core/Dispatchers/ServiceLocator.cs @@ -1,33 +1,32 @@ -using System; +using System; using System.Collections.Generic; -namespace Savvyio.Dispatchers +namespace Savvyio.Dispatchers; + +/// +/// Provides a default implementation of the interface. +/// +/// +public class ServiceLocator : IServiceLocator { + private readonly Func> _serviceFactory; + /// - /// Provides a default implementation of the interface. + /// Initializes a new instance of the class. /// - /// - public class ServiceLocator : IServiceLocator + /// The function delegate that provides the services. + public ServiceLocator(Func> serviceFactory) { - private readonly Func> _serviceFactory; - - /// - /// Initializes a new instance of the class. - /// - /// The function delegate that provides the services. - public ServiceLocator(Func> serviceFactory) - { - _serviceFactory = serviceFactory; - } + _serviceFactory = serviceFactory; + } - /// - /// Get an enumeration of services of type . - /// - /// An object that specifies the type of service object to get. - /// An enumeration of services of type . - public IEnumerable GetServices(Type serviceType) - { - return _serviceFactory?.Invoke(serviceType); - } + /// + /// Get an enumeration of services of type . + /// + /// An object that specifies the type of service object to get. + /// An enumeration of services of type . + public IEnumerable GetServices(Type serviceType) + { + return _serviceFactory?.Invoke(serviceType); } } diff --git a/src/Savvyio.Core/Domain/DomainException.cs b/src/Savvyio.Core/Domain/DomainException.cs index fbb3e2db..801a917a 100644 --- a/src/Savvyio.Core/Domain/DomainException.cs +++ b/src/Savvyio.Core/Domain/DomainException.cs @@ -1,34 +1,33 @@ -using System; +using System; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// The exception that is thrown when a domain model is not in a valid state. +/// +public class DomainException : Exception { /// - /// The exception that is thrown when a domain model is not in a valid state. + /// Initializes a new instance of the class. /// - public class DomainException : Exception + public DomainException() : this(null) { - /// - /// Initializes a new instance of the class. - /// - public DomainException() : this(null) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The exception that is the cause of the current exception. - public DomainException(Exception innerException) : this(default, innerException) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The exception that is the cause of the current exception. + public DomainException(Exception innerException) : this(default, innerException) + { + } - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the invalid state of the domain model. - /// The exception that is the cause of the current exception. - public DomainException(string message, Exception innerException = null) : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the invalid state of the domain model. + /// The exception that is the cause of the current exception. + public DomainException(string message, Exception innerException = null) : base(message, innerException) + { } } diff --git a/src/Savvyio.Core/Domain/EventSourcing/ITracedAggregateRepository.cs b/src/Savvyio.Core/Domain/EventSourcing/ITracedAggregateRepository.cs index 97a5c164..2671ce68 100644 --- a/src/Savvyio.Core/Domain/EventSourcing/ITracedAggregateRepository.cs +++ b/src/Savvyio.Core/Domain/EventSourcing/ITracedAggregateRepository.cs @@ -1,13 +1,12 @@ -namespace Savvyio.Domain.EventSourcing +namespace Savvyio.Domain.EventSourcing; + +/// +/// Defines a generic way of abstracting traced read- and writable repositories (CRud) that is optimized for Domain Driven Design. +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// +/// +public interface ITracedAggregateRepository : IReadableRepository, IWritableRepository where TEntity : class, ITracedAggregateRoot { - /// - /// Defines a generic way of abstracting traced read- and writable repositories (CRud) that is optimized for Domain Driven Design. - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// - /// - public interface ITracedAggregateRepository : IReadableRepository, IWritableRepository where TEntity : class, ITracedAggregateRoot - { - } } diff --git a/src/Savvyio.Core/Domain/EventSourcing/ITracedAggregateRoot.cs b/src/Savvyio.Core/Domain/EventSourcing/ITracedAggregateRoot.cs index ad21befe..285577f9 100644 --- a/src/Savvyio.Core/Domain/EventSourcing/ITracedAggregateRoot.cs +++ b/src/Savvyio.Core/Domain/EventSourcing/ITracedAggregateRoot.cs @@ -1,17 +1,16 @@ -namespace Savvyio.Domain.EventSourcing +namespace Savvyio.Domain.EventSourcing; + +/// +/// Defines an Event Sourcing capable contract of an Aggregate as specified in Domain Driven Design. +/// Implements the +/// +/// The type of the key that uniquely identifies this aggregate. +/// +public interface ITracedAggregateRoot : IAggregateRoot, IEntity { /// - /// Defines an Event Sourcing capable contract of an Aggregate as specified in Domain Driven Design. - /// Implements the + /// Gets the version of the Aggregate. /// - /// The type of the key that uniquely identifies this aggregate. - /// - public interface ITracedAggregateRoot : IAggregateRoot, IEntity - { - /// - /// Gets the version of the Aggregate. - /// - /// The version of the Aggregate. - long Version { get; } - } + /// The version of the Aggregate. + long Version { get; } } diff --git a/src/Savvyio.Core/Domain/EventSourcing/ITracedDomainEvent.cs b/src/Savvyio.Core/Domain/EventSourcing/ITracedDomainEvent.cs index cec62b75..0a868b66 100644 --- a/src/Savvyio.Core/Domain/EventSourcing/ITracedDomainEvent.cs +++ b/src/Savvyio.Core/Domain/EventSourcing/ITracedDomainEvent.cs @@ -1,9 +1,8 @@ -namespace Savvyio.Domain.EventSourcing +namespace Savvyio.Domain.EventSourcing; + +/// +/// Specifies something that happened in the domain that you want other parts of the same domain (in-process/inner-application) to be aware of. +/// +public interface ITracedDomainEvent : IDomainEvent { - /// - /// Specifies something that happened in the domain that you want other parts of the same domain (in-process/inner-application) to be aware of. - /// - public interface ITracedDomainEvent : IDomainEvent - { - } } diff --git a/src/Savvyio.Core/Domain/IAggregateRepository.cs b/src/Savvyio.Core/Domain/IAggregateRepository.cs index c27c29a2..ee222af9 100644 --- a/src/Savvyio.Core/Domain/IAggregateRepository.cs +++ b/src/Savvyio.Core/Domain/IAggregateRepository.cs @@ -1,12 +1,11 @@ -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines a generic way of abstracting persistent repositories (CRUD) that is optimized for Domain Driven Design. +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// +public interface IAggregateRepository : IPersistentRepository where TEntity : class, IAggregateRoot { - /// - /// Defines a generic way of abstracting persistent repositories (CRUD) that is optimized for Domain Driven Design. - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// - public interface IAggregateRepository : IPersistentRepository where TEntity : class, IAggregateRoot - { - } } diff --git a/src/Savvyio.Core/Domain/IAggregateRoot.cs b/src/Savvyio.Core/Domain/IAggregateRoot.cs index bc858784..1e94c9be 100644 --- a/src/Savvyio.Core/Domain/IAggregateRoot.cs +++ b/src/Savvyio.Core/Domain/IAggregateRoot.cs @@ -1,41 +1,40 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines a marker interface of an Aggregate as specified in Domain Driven Design. +/// +public interface IAggregateRoot : IMetadata { - /// - /// Defines a marker interface of an Aggregate as specified in Domain Driven Design. - /// - public interface IAggregateRoot : IMetadata - { - } +} +/// +/// Defines an Event based contract of an Aggregate as specified in Domain Driven Design. +/// +/// The type of the event that implements the interface. +/// +public interface IAggregateRoot : IAggregateRoot +{ /// - /// Defines an Event based contract of an Aggregate as specified in Domain Driven Design. + /// Gets the events that was added to the Aggregate. /// - /// The type of the event that implements the interface. - /// - public interface IAggregateRoot : IAggregateRoot - { - /// - /// Gets the events that was added to the Aggregate. - /// - /// The events added to the Aggregate. - IReadOnlyList Events { get; } - - /// - /// Removes all events from the Aggregate. - /// - void RemoveAllEvents(); - } + /// The events added to the Aggregate. + IReadOnlyList Events { get; } /// - /// Defines an Event and Entity based contract of an Aggregate as specified in Domain Driven Design. + /// Removes all events from the Aggregate. /// - /// The type of the event that implements the interface. - /// The type of the key that uniquely identifies this Aggregate. - /// - /// - public interface IAggregateRoot : IAggregateRoot, IEntity - { - } + void RemoveAllEvents(); +} + +/// +/// Defines an Event and Entity based contract of an Aggregate as specified in Domain Driven Design. +/// +/// The type of the event that implements the interface. +/// The type of the key that uniquely identifies this Aggregate. +/// +/// +public interface IAggregateRoot : IAggregateRoot, IEntity +{ } diff --git a/src/Savvyio.Core/Domain/IDeletableRepository.cs b/src/Savvyio.Core/Domain/IDeletableRepository.cs index 0bc3b92e..097a73f8 100644 --- a/src/Savvyio.Core/Domain/IDeletableRepository.cs +++ b/src/Savvyio.Core/Domain/IDeletableRepository.cs @@ -1,25 +1,24 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines a generic way of abstracting deletable repositories (cruD). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// +public interface IDeletableRepository : IRepository where TEntity : class, IIdentity { /// - /// Defines a generic way of abstracting deletable repositories (cruD). + /// Marks the specified to be removed from the data store when is called. /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// - public interface IDeletableRepository : IRepository where TEntity : class, IIdentity - { - /// - /// Marks the specified to be removed from the data store when is called. - /// - /// The entity to remove. - void Remove(TEntity entity); + /// The entity to remove. + void Remove(TEntity entity); - /// - /// Marks the specified to be removed from the data store when is called. - /// - /// The entities to remove. - void RemoveRange(IEnumerable entities); - } + /// + /// Marks the specified to be removed from the data store when is called. + /// + /// The entities to remove. + void RemoveRange(IEnumerable entities); } diff --git a/src/Savvyio.Core/Domain/IDomainEvent.cs b/src/Savvyio.Core/Domain/IDomainEvent.cs index 6027f046..79155ce5 100644 --- a/src/Savvyio.Core/Domain/IDomainEvent.cs +++ b/src/Savvyio.Core/Domain/IDomainEvent.cs @@ -1,9 +1,8 @@ -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// A marker interface that specifies something that happened in the domain that you want other parts of the same domain (in-process/inner-application) to be made aware of. +/// +public interface IDomainEvent : IRequest, IMetadata { - /// - /// A marker interface that specifies something that happened in the domain that you want other parts of the same domain (in-process/inner-application) to be made aware of. - /// - public interface IDomainEvent : IRequest, IMetadata - { - } } diff --git a/src/Savvyio.Core/Domain/IDomainEventDispatcher.cs b/src/Savvyio.Core/Domain/IDomainEventDispatcher.cs index 493becfd..0f668c62 100644 --- a/src/Savvyio.Core/Domain/IDomainEventDispatcher.cs +++ b/src/Savvyio.Core/Domain/IDomainEventDispatcher.cs @@ -1,28 +1,27 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; using Savvyio.Dispatchers; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines a Domain Event dispatcher that uses Fire-and-Forget/In-Only MEP. +/// +/// +public interface IDomainEventDispatcher : IDispatcher { /// - /// Defines a Domain Event dispatcher that uses Fire-and-Forget/In-Only MEP. + /// Invokes any domain event handlers that is assigned to the specified event. /// - /// - public interface IDomainEventDispatcher : IDispatcher - { - /// - /// Invokes any domain event handlers that is assigned to the specified event. - /// - /// The to raise. - void Raise(IDomainEvent request); + /// The to raise. + void Raise(IDomainEvent request); - /// - /// Invokes any domain event handlers that is assigned to the specified event. - /// - /// The to raise. - /// The which may be configured. - /// A that represents the asynchronous operation. - Task RaiseAsync(IDomainEvent request, Action setup = null); - } + /// + /// Invokes any domain event handlers that is assigned to the specified event. + /// + /// The to raise. + /// The which may be configured. + /// A that represents the asynchronous operation. + Task RaiseAsync(IDomainEvent request, Action setup = null); } diff --git a/src/Savvyio.Core/Domain/IDomainEventHandler.cs b/src/Savvyio.Core/Domain/IDomainEventHandler.cs index 67b12351..f5d546c9 100644 --- a/src/Savvyio.Core/Domain/IDomainEventHandler.cs +++ b/src/Savvyio.Core/Domain/IDomainEventHandler.cs @@ -1,12 +1,11 @@ -using Savvyio.Handlers; +using Savvyio.Handlers; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Specifies a handler responsible for objects that implements the interface. +/// +/// +public interface IDomainEventHandler : IFireForgetHandler { - /// - /// Specifies a handler responsible for objects that implements the interface. - /// - /// - public interface IDomainEventHandler : IFireForgetHandler - { - } } diff --git a/src/Savvyio.Core/Domain/IEntity.cs b/src/Savvyio.Core/Domain/IEntity.cs index 2fd07698..b945e38b 100644 --- a/src/Savvyio.Core/Domain/IEntity.cs +++ b/src/Savvyio.Core/Domain/IEntity.cs @@ -1,11 +1,10 @@ -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines an Entity as specified in Domain Driven Design. +/// +/// The type of the identifier. +/// +public interface IEntity : IIdentity { - /// - /// Defines an Entity as specified in Domain Driven Design. - /// - /// The type of the identifier. - /// - public interface IEntity : IIdentity - { - } } diff --git a/src/Savvyio.Core/Domain/IPersistentRepository.cs b/src/Savvyio.Core/Domain/IPersistentRepository.cs index fe92e468..66865c61 100644 --- a/src/Savvyio.Core/Domain/IPersistentRepository.cs +++ b/src/Savvyio.Core/Domain/IPersistentRepository.cs @@ -1,15 +1,14 @@ -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines a generic way of abstracting persistent repositories (CRUD). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// +/// +/// +/// +public interface IPersistentRepository : IWritableRepository, IReadableRepository, ISearchableRepository, IDeletableRepository where TEntity : class, IIdentity { - /// - /// Defines a generic way of abstracting persistent repositories (CRUD). - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// - /// - /// - /// - public interface IPersistentRepository : IWritableRepository, IReadableRepository, ISearchableRepository, IDeletableRepository where TEntity : class, IIdentity - { - } } diff --git a/src/Savvyio.Core/Domain/IReadableRepository.cs b/src/Savvyio.Core/Domain/IReadableRepository.cs index c4371f5b..b1ab3f97 100644 --- a/src/Savvyio.Core/Domain/IReadableRepository.cs +++ b/src/Savvyio.Core/Domain/IReadableRepository.cs @@ -1,23 +1,22 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines a generic way of abstracting readable repositories (cRud). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// +public interface IReadableRepository : IRepository where TEntity : class, IIdentity { /// - /// Defines a generic way of abstracting readable repositories (cRud). + /// Loads the entity from the specified asynchronous. /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// - public interface IReadableRepository : IRepository where TEntity : class, IIdentity - { - /// - /// Loads the entity from the specified asynchronous. - /// - /// The key that uniquely identifies the entity. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the entity of the operation or null if not found. - Task GetByIdAsync(TKey id, Action setup = null); - } + /// The key that uniquely identifies the entity. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the entity of the operation or null if not found. + Task GetByIdAsync(TKey id, Action setup = null); } diff --git a/src/Savvyio.Core/Domain/IRepository.cs b/src/Savvyio.Core/Domain/IRepository.cs index 34848d89..df3a3279 100644 --- a/src/Savvyio.Core/Domain/IRepository.cs +++ b/src/Savvyio.Core/Domain/IRepository.cs @@ -1,11 +1,10 @@ -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// A marker interface that specifies an abstraction of data persistence based on the Repository pattern. +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +public interface IRepository where TEntity : class, IIdentity { - /// - /// A marker interface that specifies an abstraction of data persistence based on the Repository pattern. - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - public interface IRepository where TEntity : class, IIdentity - { - } } diff --git a/src/Savvyio.Core/Domain/ISearchableRepository.cs b/src/Savvyio.Core/Domain/ISearchableRepository.cs index a15b1847..3bfacce3 100644 --- a/src/Savvyio.Core/Domain/ISearchableRepository.cs +++ b/src/Savvyio.Core/Domain/ISearchableRepository.cs @@ -1,25 +1,24 @@ -using System; +using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Threading.Tasks; using Cuemon.Threading; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines a generic way of abstracting searchable repositories (cRud). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// +public interface ISearchableRepository : IRepository where TEntity : class, IIdentity { /// - /// Defines a generic way of abstracting searchable repositories (cRud). + /// Finds all entities matching the specified asynchronous. /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// - public interface ISearchableRepository : IRepository where TEntity : class, IIdentity - { - /// - /// Finds all entities matching the specified asynchronous. - /// - /// The predicate that matches the entities to retrieve. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the matching entities of the operation or an empty sequence if no match was found. - Task> FindAllAsync(Expression> predicate = null, Action setup = null); - } + /// The predicate that matches the entities to retrieve. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the matching entities of the operation or an empty sequence if no match was found. + Task> FindAllAsync(Expression> predicate = null, Action setup = null); } diff --git a/src/Savvyio.Core/Domain/IUnitOfWork.cs b/src/Savvyio.Core/Domain/IUnitOfWork.cs index d890990c..9188f798 100644 --- a/src/Savvyio.Core/Domain/IUnitOfWork.cs +++ b/src/Savvyio.Core/Domain/IUnitOfWork.cs @@ -1,19 +1,18 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines a transaction that bundles multiple calls into a single unit. +/// +public interface IUnitOfWork { /// - /// Defines a transaction that bundles multiple calls into a single unit. + /// Saves the different implementations as one transaction towards a data store asynchronous. /// - public interface IUnitOfWork - { - /// - /// Saves the different implementations as one transaction towards a data store asynchronous. - /// - /// The which may be configured. - /// A that represents the asynchronous operation. - Task SaveChangesAsync(Action setup = null); - } + /// The which may be configured. + /// A that represents the asynchronous operation. + Task SaveChangesAsync(Action setup = null); } diff --git a/src/Savvyio.Core/Domain/IWritableRepository.cs b/src/Savvyio.Core/Domain/IWritableRepository.cs index 555a640f..0c4bea43 100644 --- a/src/Savvyio.Core/Domain/IWritableRepository.cs +++ b/src/Savvyio.Core/Domain/IWritableRepository.cs @@ -1,25 +1,24 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Defines a generic way of abstracting writable repositories (CrUd). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// +public interface IWritableRepository : IRepository where TEntity : class, IIdentity { /// - /// Defines a generic way of abstracting writable repositories (CrUd). + /// Marks the specified to be added in the data store when is called. /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// - public interface IWritableRepository : IRepository where TEntity : class, IIdentity - { - /// - /// Marks the specified to be added in the data store when is called. - /// - /// The entity to add. - void Add(TEntity entity); + /// The entity to add. + void Add(TEntity entity); - /// - /// Marks the specified to be added in the data store when is called. - /// - /// The entities to add. - void AddRange(IEnumerable entities); - } + /// + /// Marks the specified to be added in the data store when is called. + /// + /// The entities to add. + void AddRange(IEnumerable entities); } diff --git a/src/Savvyio.Core/EventDriven/IIntegrationEvent.cs b/src/Savvyio.Core/EventDriven/IIntegrationEvent.cs index 5a49b309..97573a01 100644 --- a/src/Savvyio.Core/EventDriven/IIntegrationEvent.cs +++ b/src/Savvyio.Core/EventDriven/IIntegrationEvent.cs @@ -1,9 +1,8 @@ -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +/// +/// A marker interface that specifies something that happened when an object was successfully persisted and you want other subsystems (out-process/inter-application) to be made aware of. +/// +public interface IIntegrationEvent : IRequest, IMetadata { - /// - /// A marker interface that specifies something that happened when an object was successfully persisted and you want other subsystems (out-process/inter-application) to be made aware of. - /// - public interface IIntegrationEvent : IRequest, IMetadata - { - } } diff --git a/src/Savvyio.Core/EventDriven/IIntegrationEventDispatcher.cs b/src/Savvyio.Core/EventDriven/IIntegrationEventDispatcher.cs index 5d6733a1..7aafab68 100644 --- a/src/Savvyio.Core/EventDriven/IIntegrationEventDispatcher.cs +++ b/src/Savvyio.Core/EventDriven/IIntegrationEventDispatcher.cs @@ -1,28 +1,27 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; using Savvyio.Dispatchers; -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +/// +/// Defines an Integration Event dispatcher that uses Fire-and-Forget/In-Only MEP. +/// +/// +public interface IIntegrationEventDispatcher : IDispatcher { /// - /// Defines an Integration Event dispatcher that uses Fire-and-Forget/In-Only MEP. + /// Publishes the specified using Fire-and-Forget/In-Only MEP. /// - /// - public interface IIntegrationEventDispatcher : IDispatcher - { - /// - /// Publishes the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to publish. - void Publish(IIntegrationEvent request); + /// The to publish. + void Publish(IIntegrationEvent request); - /// - /// Publishes the specified asynchronous using Fire-and-Forget/In-Only MEP. - /// - /// The to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - Task PublishAsync(IIntegrationEvent request, Action setup = null); - } + /// + /// Publishes the specified asynchronous using Fire-and-Forget/In-Only MEP. + /// + /// The to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + Task PublishAsync(IIntegrationEvent request, Action setup = null); } diff --git a/src/Savvyio.Core/EventDriven/IIntegrationEventHandler.cs b/src/Savvyio.Core/EventDriven/IIntegrationEventHandler.cs index 7e1538a9..c2bc7001 100644 --- a/src/Savvyio.Core/EventDriven/IIntegrationEventHandler.cs +++ b/src/Savvyio.Core/EventDriven/IIntegrationEventHandler.cs @@ -1,12 +1,11 @@ -using Savvyio.Handlers; +using Savvyio.Handlers; -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +/// +/// Specifies a handler responsible for objects that implements the interface. +/// +/// +public interface IIntegrationEventHandler : IFireForgetHandler { - /// - /// Specifies a handler responsible for objects that implements the interface. - /// - /// - public interface IIntegrationEventHandler : IFireForgetHandler - { - } } diff --git a/src/Savvyio.Core/EventDriven/Messaging/CloudEvents/Cryptography/ISignedCloudEvent.cs b/src/Savvyio.Core/EventDriven/Messaging/CloudEvents/Cryptography/ISignedCloudEvent.cs index e647c186..bfc10bc4 100644 --- a/src/Savvyio.Core/EventDriven/Messaging/CloudEvents/Cryptography/ISignedCloudEvent.cs +++ b/src/Savvyio.Core/EventDriven/Messaging/CloudEvents/Cryptography/ISignedCloudEvent.cs @@ -1,14 +1,13 @@ -using Savvyio.Messaging.Cryptography; +using Savvyio.Messaging.Cryptography; -namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography +namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography; + +/// +/// Defines a generic way to wrap an inside a CloudEvents compliant message format. +/// +/// The type of the payload constraint to the interface. +/// +/// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md +public interface ISignedCloudEvent : ICloudEvent, ISignedMessage where T : IIntegrationEvent { - /// - /// Defines a generic way to wrap an inside a CloudEvents compliant message format. - /// - /// The type of the payload constraint to the interface. - /// - /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md - public interface ISignedCloudEvent : ICloudEvent, ISignedMessage where T : IIntegrationEvent - { - } } diff --git a/src/Savvyio.Core/EventDriven/Messaging/CloudEvents/ICloudEvent.cs b/src/Savvyio.Core/EventDriven/Messaging/CloudEvents/ICloudEvent.cs index fccb0882..5a60c663 100644 --- a/src/Savvyio.Core/EventDriven/Messaging/CloudEvents/ICloudEvent.cs +++ b/src/Savvyio.Core/EventDriven/Messaging/CloudEvents/ICloudEvent.cs @@ -1,21 +1,20 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Savvyio.Messaging; -namespace Savvyio.EventDriven.Messaging.CloudEvents +namespace Savvyio.EventDriven.Messaging.CloudEvents; + +/// +/// Defines a generic way to wrap an inside a CloudEvents compliant message format. +/// +/// The type of the payload constraint to the interface. +/// +/// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md +public interface ICloudEvent : IMessage, IDictionary where T : IIntegrationEvent { /// - /// Defines a generic way to wrap an inside a CloudEvents compliant message format. + /// Gets version of the CloudEvents specification which the event uses. /// - /// The type of the payload constraint to the interface. - /// - /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md - public interface ICloudEvent : IMessage, IDictionary where T : IIntegrationEvent - { - /// - /// Gets version of the CloudEvents specification which the event uses. - /// - /// The version of the CloudEvents specification which the event uses. - /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#specversion - string Specversion { get; } - } + /// The version of the CloudEvents specification which the event uses. + /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#specversion + string Specversion { get; } } diff --git a/src/Savvyio.Core/HandlerDiscoveryModel.cs b/src/Savvyio.Core/HandlerDiscoveryModel.cs index f035e7f8..bab5f611 100644 --- a/src/Savvyio.Core/HandlerDiscoveryModel.cs +++ b/src/Savvyio.Core/HandlerDiscoveryModel.cs @@ -1,4 +1,4 @@ -using Cuemon; +using Cuemon; using System; using System.Collections.Generic; using System.Linq; @@ -6,204 +6,203 @@ using Cuemon.Extensions; using Cuemon.Extensions.Runtime; -namespace Savvyio +namespace Savvyio; + +/// +/// Represents a model for handler services discovery. +/// +public record HandlerDiscoveryModel { /// - /// Represents a model for handler services discovery. + /// Initializes a new instance of the class. /// - public record HandlerDiscoveryModel + /// The of the handler abstraction. + /// The of the delegate abstraction. + /// The implementations of the interface. + public HandlerDiscoveryModel(Type handlerAbstractionType, Type delegateAbstractionType, IGrouping>>> groupHandlers) { - /// - /// Initializes a new instance of the class. - /// - /// The of the handler abstraction. - /// The of the delegate abstraction. - /// The implementations of the interface. - public HandlerDiscoveryModel(Type handlerAbstractionType, Type delegateAbstractionType, IGrouping>>> groupHandlers) - { - Validator.ThrowIfNull(handlerAbstractionType); - Validator.ThrowIfNull(delegateAbstractionType); - Validator.ThrowIfNull(groupHandlers); + Validator.ThrowIfNull(handlerAbstractionType); + Validator.ThrowIfNull(delegateAbstractionType); + Validator.ThrowIfNull(groupHandlers); - var handlers = groupHandlers.Where(pair => pair.Key == handlerAbstractionType).SelectMany(pair => pair.Value).ToList(); - if (handlers.Count == 0) { return; } + var handlers = groupHandlers.Where(pair => pair.Key == handlerAbstractionType).SelectMany(pair => pair.Value).ToList(); + if (handlers.Count == 0) { return; } - var handlersDelegatesCount = 0; + var handlersDelegatesCount = 0; - var assemblyModels = new List(); - foreach (var group in handlers.GroupBy(h => new HandlerServiceAssemblyModel(h.Instance.As()))) - { - var delegateCount = 0; + var assemblyModels = new List(); + foreach (var group in handlers.GroupBy(h => new HandlerServiceAssemblyModel(h.Instance.As()))) + { + var delegateCount = 0; - var implementations = new List(); - foreach (var node in group) + var implementations = new List(); + foreach (var node in group) + { + if (node.Instance is Type handlerImplementationType) { - if (node.Instance is Type handlerImplementationType) - { - var implementation = new HandlerServiceTypeImplementationModel(node, handlerImplementationType, delegateAbstractionType); - delegateCount += implementation.DelegatesCount; - implementations.Add(implementation); - } + var implementation = new HandlerServiceTypeImplementationModel(node, handlerImplementationType, delegateAbstractionType); + delegateCount += implementation.DelegatesCount; + implementations.Add(implementation); } - - group.Key.Implementations = implementations; - - handlersDelegatesCount += delegateCount; - assemblyModels.Add(group.Key); } - ImplementationsCount = handlers.Count; - DelegatesCount = handlersDelegatesCount; - AbstractionType = handlerAbstractionType.Name; - DelegateType = delegateAbstractionType.Name; - Assemblies = assemblyModels; + group.Key.Implementations = implementations; + + handlersDelegatesCount += delegateCount; + assemblyModels.Add(group.Key); } - /// - /// Gets the name of the handler abstraction. - /// - public string AbstractionType { get; init; } - - /// - /// Gets the count of handler implementations. - /// - public int ImplementationsCount { get; init; } - - /// - /// Gets the name of the delegate abstraction. - /// - public string DelegateType { get; init; } - - /// - /// Gets the count of handler implementations delegates. - /// - public int DelegatesCount { get; init; } - - /// - /// Gets the assemblies containing the handler implementations. - /// - public IEnumerable Assemblies { get; init; } + ImplementationsCount = handlers.Count; + DelegatesCount = handlersDelegatesCount; + AbstractionType = handlerAbstractionType.Name; + DelegateType = delegateAbstractionType.Name; + Assemblies = assemblyModels; } /// - /// Represents a model for handler service assemblies. + /// Gets the name of the handler abstraction. /// - public record HandlerServiceAssemblyModel - { - /// - /// Initializes a new instance of the class. - /// - /// The of the handler implementation. - public HandlerServiceAssemblyModel(Type handlerImplementationType) - { - Validator.ThrowIfNull(handlerImplementationType); + public string AbstractionType { get; init; } - Name = handlerImplementationType.Assembly.GetName().Name; - Namespace = handlerImplementationType.Namespace; - } + /// + /// Gets the count of handler implementations. + /// + public int ImplementationsCount { get; init; } + + /// + /// Gets the name of the delegate abstraction. + /// + public string DelegateType { get; init; } - /// - /// Gets the name of the assembly. - /// - public string Name { get; init; } + /// + /// Gets the count of handler implementations delegates. + /// + public int DelegatesCount { get; init; } + + /// + /// Gets the assemblies containing the handler implementations. + /// + public IEnumerable Assemblies { get; init; } +} - /// - /// Gets the namespace of the handler implementation. - /// - public string Namespace { get; init; } +/// +/// Represents a model for handler service assemblies. +/// +public record HandlerServiceAssemblyModel +{ + /// + /// Initializes a new instance of the class. + /// + /// The of the handler implementation. + public HandlerServiceAssemblyModel(Type handlerImplementationType) + { + Validator.ThrowIfNull(handlerImplementationType); - /// - /// Gets or sets the implementations of the handler service type. - /// - public IEnumerable Implementations { get; internal set; } + Name = handlerImplementationType.Assembly.GetName().Name; + Namespace = handlerImplementationType.Namespace; } /// - /// Represents a model for handler service type implementations. + /// Gets the name of the assembly. + /// + public string Name { get; init; } + + /// + /// Gets the namespace of the handler implementation. + /// + public string Namespace { get; init; } + + /// + /// Gets or sets the implementations of the handler service type. /// - public record HandlerServiceTypeImplementationModel + public IEnumerable Implementations { get; internal set; } +} + +/// +/// Represents a model for handler service type implementations. +/// +public record HandlerServiceTypeImplementationModel +{ + /// + /// Initializes a new instance of the class. + /// + /// The hierarchy node. + /// The of the handler implementation. + /// The of the delegate abstraction. + public HandlerServiceTypeImplementationModel(IHierarchy node, Type handlerImplementationType, Type delegateAbstractionType) { - /// - /// Initializes a new instance of the class. - /// - /// The hierarchy node. - /// The of the handler implementation. - /// The of the delegate abstraction. - public HandlerServiceTypeImplementationModel(IHierarchy node, Type handlerImplementationType, Type delegateAbstractionType) - { - Validator.ThrowIfNull(node); - Validator.ThrowIfNull(handlerImplementationType); - Validator.ThrowIfNull(delegateAbstractionType); + Validator.ThrowIfNull(node); + Validator.ThrowIfNull(handlerImplementationType); + Validator.ThrowIfNull(delegateAbstractionType); - var delegates = new List(); - var children = node.GetChildren().ToList(); - foreach (var child in children.Select(h => h.Instance).OrderBy(h => h.As()?.Name, StringComparer.OrdinalIgnoreCase)) + var delegates = new List(); + var children = node.GetChildren().ToList(); + foreach (var child in children.Select(h => h.Instance).OrderBy(h => h.As()?.Name, StringComparer.OrdinalIgnoreCase)) + { + if (child is MethodInfo methodInfo) + { + var p = methodInfo.GetParameters().Single(p => p.ParameterType.HasInterfaces(delegateAbstractionType)); + delegates.Add(new HandlerServiceTypeImplementationDelegatesModel(p.ParameterType.Name, methodInfo.Name)); + DelegatesCount++; + } + else if (child is Type runtimeType) // we will get here when no class dependencies is specified (e.g., isolated 'method') { - if (child is MethodInfo methodInfo) + var nestedMethods = runtimeType.GetRuntimeMethods().Where(i => i.GetParameters().Any(p => p.ParameterType.HasInterfaces(delegateAbstractionType))); + foreach (var nested in nestedMethods) { - var p = methodInfo.GetParameters().Single(p => p.ParameterType.HasInterfaces(delegateAbstractionType)); - delegates.Add(new HandlerServiceTypeImplementationDelegatesModel(p.ParameterType.Name, methodInfo.Name)); + var p = nested.GetParameters().Single(); + delegates.Add(new HandlerServiceTypeImplementationDelegatesModel(p.ParameterType.Name, nested.Name)); DelegatesCount++; } - else if (child is Type runtimeType) // we will get here when no class dependencies is specified (e.g., isolated 'method') - { - var nestedMethods = runtimeType.GetRuntimeMethods().Where(i => i.GetParameters().Any(p => p.ParameterType.HasInterfaces(delegateAbstractionType))); - foreach (var nested in nestedMethods) - { - var p = nested.GetParameters().Single(); - delegates.Add(new HandlerServiceTypeImplementationDelegatesModel(p.ParameterType.Name, nested.Name)); - DelegatesCount++; - } - } } - - Name = handlerImplementationType.ToFriendlyName(); - Delegates = delegates; } - /// - /// Gets the name of the handler implementation. - /// - public string Name { get; init; } + Name = handlerImplementationType.ToFriendlyName(); + Delegates = delegates; + } - /// - /// Gets the count of delegates for the handler implementation. - /// - internal int DelegatesCount { get; init; } + /// + /// Gets the name of the handler implementation. + /// + public string Name { get; init; } - /// - /// Gets the delegates of the handler implementation. - /// - public IEnumerable Delegates { get; init; } - } + /// + /// Gets the count of delegates for the handler implementation. + /// + internal int DelegatesCount { get; init; } + + /// + /// Gets the delegates of the handler implementation. + /// + public IEnumerable Delegates { get; init; } +} +/// +/// Represents a model for handler service type implementation delegates. +/// +public record HandlerServiceTypeImplementationDelegatesModel +{ /// - /// Represents a model for handler service type implementation delegates. + /// Initializes a new instance of the class. /// - public record HandlerServiceTypeImplementationDelegatesModel + /// Type of the delegate model. + /// Type of the delegate method. + public HandlerServiceTypeImplementationDelegatesModel(string type, string handler) { - /// - /// Initializes a new instance of the class. - /// - /// Type of the delegate model. - /// Type of the delegate method. - public HandlerServiceTypeImplementationDelegatesModel(string type, string handler) - { - Validator.ThrowIfNull(type); - Validator.ThrowIfNull(handler); + Validator.ThrowIfNull(type); + Validator.ThrowIfNull(handler); - Type = type; - Handler = handler; - } + Type = type; + Handler = handler; + } - /// - /// Gets the name of the delegate model. - /// - public string Type { get; init; } + /// + /// Gets the name of the delegate model. + /// + public string Type { get; init; } - /// - /// Gets the name of the delegate method. - /// - public string Handler { get; init; } - } + /// + /// Gets the name of the delegate method. + /// + public string Handler { get; init; } } diff --git a/src/Savvyio.Core/HandlerFactory.cs b/src/Savvyio.Core/HandlerFactory.cs index 62de0af1..ac73c8eb 100644 --- a/src/Savvyio.Core/HandlerFactory.cs +++ b/src/Savvyio.Core/HandlerFactory.cs @@ -1,40 +1,39 @@ -using System; +using System; using Cuemon; using Savvyio.Handlers; -namespace Savvyio +namespace Savvyio; + +/// +/// Provides access to factory methods for creating and configuring generic handlers that supports MEP. +/// +public static class HandlerFactory { /// - /// Provides access to factory methods for creating and configuring generic handlers that supports MEP. + /// Creates an instance of a Fire-and-Forget/In-Only MEP activator responsible of invoking delegates that handles . /// - public static class HandlerFactory + /// The type of the model to handle. + /// The delegate responsible for registering delegates of type . + /// An implementation of the interface responsible of invoking delegates that handles . + public static IFireForgetActivator CreateFireForget(Action> handlerRegistrar) { - /// - /// Creates an instance of a Fire-and-Forget/In-Only MEP activator responsible of invoking delegates that handles . - /// - /// The type of the model to handle. - /// The delegate responsible for registering delegates of type . - /// An implementation of the interface responsible of invoking delegates that handles . - public static IFireForgetActivator CreateFireForget(Action> handlerRegistrar) - { - Validator.ThrowIfNull(handlerRegistrar); - var handlerManager = new FireForgetManager(); - handlerRegistrar(handlerManager); - return handlerManager; - } + Validator.ThrowIfNull(handlerRegistrar); + var handlerManager = new FireForgetManager(); + handlerRegistrar(handlerManager); + return handlerManager; + } - /// - /// Creates an instance of a Request-Reply/In-Out MEP activator responsible of invoking delegates that handles . - /// - /// The type of the model to handle. - /// The delegate responsible for registering delegates of type . - /// An implementation of the interface responsible of invoking delegates that handles . - public static IRequestReplyActivator CreateRequestReply(Action> handlerRegistrar) - { - Validator.ThrowIfNull(handlerRegistrar); - var handlerManager = new RequestReplyManager(); - handlerRegistrar(handlerManager); - return handlerManager; - } + /// + /// Creates an instance of a Request-Reply/In-Out MEP activator responsible of invoking delegates that handles . + /// + /// The type of the model to handle. + /// The delegate responsible for registering delegates of type . + /// An implementation of the interface responsible of invoking delegates that handles . + public static IRequestReplyActivator CreateRequestReply(Action> handlerRegistrar) + { + Validator.ThrowIfNull(handlerRegistrar); + var handlerManager = new RequestReplyManager(); + handlerRegistrar(handlerManager); + return handlerManager; } } diff --git a/src/Savvyio.Core/HandlerServicesDescriptor.cs b/src/Savvyio.Core/HandlerServicesDescriptor.cs index e07b7f59..66c0e7a7 100644 --- a/src/Savvyio.Core/HandlerServicesDescriptor.cs +++ b/src/Savvyio.Core/HandlerServicesDescriptor.cs @@ -8,121 +8,120 @@ using Cuemon.Extensions; using Cuemon.Extensions.Runtime; -namespace Savvyio +namespace Savvyio; + +/// +/// Provides information, in a developer friendly way, about implementations of the interface such as name, declared members and what type of request they handle. +/// +/// +/// An example of the output available when calling :
+///
+/// Discovered 1 ICommandHandler implementation covering a total of 5 ICommand methods
+///
+///
+/// Assembly: Savvyio.Assets.Tests
+/// Namespace: Savvyio.Assets
+///
+/// <AccountCommandHandler>
+/// *UpdateAccount --> &<RegisterDelegates>b__5_0
+/// *CreateAccount --> &CreateAccountAsync
+///
> +public class HandlerServicesDescriptor : IHandlerServicesDescriptor { + private List _models; + private readonly Lock _locker = new(); + /// - /// Provides information, in a developer friendly way, about implementations of the interface such as name, declared members and what type of request they handle. + /// Initializes a new instance of the class. /// - /// - /// An example of the output available when calling :
- ///
- /// Discovered 1 ICommandHandler implementation covering a total of 5 ICommand methods
- ///
- ///
- /// Assembly: Savvyio.Assets.Tests
- /// Namespace: Savvyio.Assets
- ///
- /// <AccountCommandHandler>
- /// *UpdateAccount --> &<RegisterDelegates>b__5_0
- /// *CreateAccount --> &CreateAccountAsync
- ///
> - public class HandlerServicesDescriptor : IHandlerServicesDescriptor + /// The discovered implementations of the interface. + /// The registered service types. + public HandlerServicesDescriptor(IEnumerable>>>> discoveredServices, IEnumerable serviceTypes) { - private List _models; - private readonly Lock _locker = new(); - - /// - /// Initializes a new instance of the class. - /// - /// The discovered implementations of the interface. - /// The registered service types. - public HandlerServicesDescriptor(IEnumerable>>>> discoveredServices, IEnumerable serviceTypes) - { - DiscoveredServices = new List>>>>(discoveredServices ?? Enumerable.Empty>>>>()); - ServiceTypes = new List(serviceTypes ?? Enumerable.Empty()).OrderBy(type => type.Name).ToList(); - } + DiscoveredServices = new List>>>>(discoveredServices ?? Enumerable.Empty>>>>()); + ServiceTypes = new List(serviceTypes ?? Enumerable.Empty()).OrderBy(type => type.Name).ToList(); + } - private IReadOnlyList>>>> DiscoveredServices { get; } + private IReadOnlyList>>>> DiscoveredServices { get; } - private IReadOnlyList ServiceTypes { get; } + private IReadOnlyList ServiceTypes { get; } - /// - /// Returns a that represents this instance. - /// - /// A that represents this instance. - public override string ToString() + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + var builder = new StringBuilder(); + foreach (var model in GenerateHandlerDiscoveries()) { - var builder = new StringBuilder(); - foreach (var model in GenerateHandlerDiscoveries()) + var discovery = FormatDiscoveryHeader(model); + builder.Append(discovery); + builder.AppendLine(); + foreach (var assembly in model.Assemblies) { - var discovery = FormatDiscoveryHeader(model); - builder.Append(discovery); - builder.AppendLine(); - foreach (var assembly in model.Assemblies) - { - AppendAssembly(builder, assembly); - } - builder.AppendLine(Generate.FixedString('-', discovery.Length.Max(discovery.Length.Max(discovery.Length)))); - builder.AppendLine(); + AppendAssembly(builder, assembly); } - return builder.ToString().TrimEnd(); + builder.AppendLine(Generate.FixedString('-', discovery.Length.Max(discovery.Length.Max(discovery.Length)))); + builder.AppendLine(); } + return builder.ToString().TrimEnd(); + } - private static string FormatDiscoveryHeader(HandlerDiscoveryModel model) - { - return $"Discovered {model.ImplementationsCount} {model.AbstractionType} implementation{(model.ImplementationsCount > 1 ? "s" : "")} covering a total of {model.DelegatesCount} {model.DelegateType} method{(model.DelegatesCount > 1 ? "s" : "")}"; - } + private static string FormatDiscoveryHeader(HandlerDiscoveryModel model) + { + return $"Discovered {model.ImplementationsCount} {model.AbstractionType} implementation{(model.ImplementationsCount > 1 ? "s" : "")} covering a total of {model.DelegatesCount} {model.DelegateType} method{(model.DelegatesCount > 1 ? "s" : "")}"; + } - private static void AppendAssembly(StringBuilder builder, HandlerServiceAssemblyModel assembly) - { - builder.AppendLine(); - builder.AppendLine(CultureInfo.InvariantCulture, $"Assembly: {assembly.Name}"); - builder.AppendLine(CultureInfo.InvariantCulture, $"Namespace: {assembly.Namespace}"); - builder.AppendLine(); + private static void AppendAssembly(StringBuilder builder, HandlerServiceAssemblyModel assembly) + { + builder.AppendLine(); + builder.AppendLine(CultureInfo.InvariantCulture, $"Assembly: {assembly.Name}"); + builder.AppendLine(CultureInfo.InvariantCulture, $"Namespace: {assembly.Namespace}"); + builder.AppendLine(); - foreach (var implementation in assembly.Implementations) + foreach (var implementation in assembly.Implementations) + { + builder.AppendLine(CultureInfo.InvariantCulture, $"<{implementation.Name}>"); + foreach (var @delegate in implementation.Delegates) { - builder.AppendLine(CultureInfo.InvariantCulture, $"<{implementation.Name}>"); - foreach (var @delegate in implementation.Delegates) - { - builder.AppendLine(CultureInfo.InvariantCulture, $"\t*{@delegate.Type} --> &{@delegate.Handler}"); - } - builder.AppendLine(); + builder.AppendLine(CultureInfo.InvariantCulture, $"\t*{@delegate.Type} --> &{@delegate.Handler}"); } + builder.AppendLine(); } + } - /// - /// Generates the handler discoveries. - /// - /// A collection of representing the handler discoveries. - public IEnumerable GenerateHandlerDiscoveries() - { - if (_models != null) { return _models; } + /// + /// Generates the handler discoveries. + /// + /// A collection of representing the handler discoveries. + public IEnumerable GenerateHandlerDiscoveries() + { + if (_models != null) { return _models; } - lock (_locker) - { - _models ??= BuildHandlerDiscoveries(); - } - return _models; + lock (_locker) + { + _models ??= BuildHandlerDiscoveries(); } + return _models; + } - private List BuildHandlerDiscoveries() + private List BuildHandlerDiscoveries() + { + var models = new List(); + foreach (var serviceType in ServiceTypes) { - var models = new List(); - foreach (var serviceType in ServiceTypes) + var serviceRequestType = serviceType.GetInterface("IHandler`1")?.GenericTypeArguments.Single(); + if (serviceRequestType == null) { continue; } + foreach (var discoveredServicesGroup in DiscoveredServices) { - var serviceRequestType = serviceType.GetInterface("IHandler`1")?.GenericTypeArguments.Single(); - if (serviceRequestType == null) { continue; } - foreach (var discoveredServicesGroup in DiscoveredServices) + var model = new HandlerDiscoveryModel(serviceType, serviceRequestType, discoveredServicesGroup); + if (model.ImplementationsCount > 0) { - var model = new HandlerDiscoveryModel(serviceType, serviceRequestType, discoveredServicesGroup); - if (model.ImplementationsCount > 0) - { - models.Add(model); - } + models.Add(model); } } - return models; } + return models; } } diff --git a/src/Savvyio.Core/Handlers/FireForgetManager.cs b/src/Savvyio.Core/Handlers/FireForgetManager.cs index 661dd93c..e99b05ec 100644 --- a/src/Savvyio.Core/Handlers/FireForgetManager.cs +++ b/src/Savvyio.Core/Handlers/FireForgetManager.cs @@ -1,52 +1,51 @@ -using System; +using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; using Cuemon; -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +internal sealed class FireForgetManager : IFireForgetRegistry, IFireForgetActivator { - internal sealed class FireForgetManager : IFireForgetRegistry, IFireForgetActivator - { - private readonly ConcurrentDictionary> _handlers = new(); - private readonly ConcurrentDictionary> _asyncHandlers = new(); + private readonly ConcurrentDictionary> _handlers = new(); + private readonly ConcurrentDictionary> _asyncHandlers = new(); - public FireForgetManager() - { - } + public FireForgetManager() + { + } - public void Register(Action handler) where T : class, TRequest - { - Validator.ThrowIfNull(handler); - _handlers.TryAdd(typeof(T), e => handler(e as T)); - } + public void Register(Action handler) where T : class, TRequest + { + Validator.ThrowIfNull(handler); + _handlers.TryAdd(typeof(T), e => handler(e as T)); + } - public void RegisterAsync(Func handler) where T : class, TRequest - { - Validator.ThrowIfNull(handler); - _asyncHandlers.TryAdd(typeof(T), async (h, t) => await handler(h as T, t)); - } + public void RegisterAsync(Func handler) where T : class, TRequest + { + Validator.ThrowIfNull(handler); + _asyncHandlers.TryAdd(typeof(T), async (h, t) => await handler(h as T, t)); + } - public bool TryInvoke(TRequest request) + public bool TryInvoke(TRequest request) + { + Validator.ThrowIfNull(request); + if (_handlers.TryGetValue(request.GetType(), out var handler)) { - Validator.ThrowIfNull(request); - if (_handlers.TryGetValue(request.GetType(), out var handler)) - { - handler.Invoke(request); - return true; - } - return false; + handler.Invoke(request); + return true; } + return false; + } - public async Task TryInvokeAsync(TRequest request, CancellationToken ct = default) + public async Task TryInvokeAsync(TRequest request, CancellationToken ct = default) + { + Validator.ThrowIfNull(request); + if (_asyncHandlers.TryGetValue(request.GetType(), out var handler)) { - Validator.ThrowIfNull(request); - if (_asyncHandlers.TryGetValue(request.GetType(), out var handler)) - { - await handler(request, ct).ConfigureAwait(false); - return new SuccessfulValue(); - } - return new UnsuccessfulValue(); + await handler(request, ct).ConfigureAwait(false); + return new SuccessfulValue(); } + return new UnsuccessfulValue(); } } diff --git a/src/Savvyio.Core/Handlers/FireForgetRegistryExtensions.cs b/src/Savvyio.Core/Handlers/FireForgetRegistryExtensions.cs index 0f2cd25c..e6e2f588 100644 --- a/src/Savvyio.Core/Handlers/FireForgetRegistryExtensions.cs +++ b/src/Savvyio.Core/Handlers/FireForgetRegistryExtensions.cs @@ -1,22 +1,21 @@ -using System; +using System; using System.Threading.Tasks; -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +/// +/// Extension methods for the interface. +/// +public static class FireForgetRegistryExtensions { /// - /// Extension methods for the interface. + /// Registers the specified function delegate . /// - public static class FireForgetRegistryExtensions + /// The type of the model to store in the registry. + /// The to extend. + /// The function delegate that handles . + public static void RegisterAsync(this IFireForgetRegistry registry, Func handler) where TRequest : class { - /// - /// Registers the specified function delegate . - /// - /// The type of the model to store in the registry. - /// The to extend. - /// The function delegate that handles . - public static void RegisterAsync(this IFireForgetRegistry registry, Func handler) where TRequest : class - { - registry.RegisterAsync((h, _) => handler(h)); - } + registry.RegisterAsync((h, _) => handler(h)); } } diff --git a/src/Savvyio.Core/Handlers/IFireForgetActivator.cs b/src/Savvyio.Core/Handlers/IFireForgetActivator.cs index a114ef37..6e9a5bfe 100644 --- a/src/Savvyio.Core/Handlers/IFireForgetActivator.cs +++ b/src/Savvyio.Core/Handlers/IFireForgetActivator.cs @@ -1,29 +1,28 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Cuemon; -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +/// +/// Specifies a way of invoking Fire-and-Forget/In-Only MEP delegates that handles . +/// +/// The type of the model to invoke on a handler. +/// +public interface IFireForgetActivator { /// - /// Specifies a way of invoking Fire-and-Forget/In-Only MEP delegates that handles . + /// Invokes (if registered) the delegate handling the specified . /// - /// The type of the model to invoke on a handler. - /// - public interface IFireForgetActivator - { - /// - /// Invokes (if registered) the delegate handling the specified . - /// - /// The model that is handled by a delegate. - /// true if the delegate handling the specified was invoked, false otherwise. - bool TryInvoke(TRequest request); + /// The model that is handled by a delegate. + /// true if the delegate handling the specified was invoked, false otherwise. + bool TryInvoke(TRequest request); - /// - /// Invokes (if registered) the function delegate handling the specified . - /// - /// The model that is handled by a function delegate. - /// The cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// true if the function delegate handling the specified was invoked, false otherwise. - Task TryInvokeAsync(TRequest request, CancellationToken ct = default); - } + /// + /// Invokes (if registered) the function delegate handling the specified . + /// + /// The model that is handled by a function delegate. + /// The cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// true if the function delegate handling the specified was invoked, false otherwise. + Task TryInvokeAsync(TRequest request, CancellationToken ct = default); } diff --git a/src/Savvyio.Core/Handlers/IFireForgetHandler.cs b/src/Savvyio.Core/Handlers/IFireForgetHandler.cs index e3703c4a..d7c87b87 100644 --- a/src/Savvyio.Core/Handlers/IFireForgetHandler.cs +++ b/src/Savvyio.Core/Handlers/IFireForgetHandler.cs @@ -1,16 +1,15 @@ -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +/// +/// Defines a generic handler that uses Fire-and-Forget/In-Only MEP. +/// +/// The type of the model to handle. +/// +public interface IFireForgetHandler : IHandler where TRequest : IRequest { /// - /// Defines a generic handler that uses Fire-and-Forget/In-Only MEP. + /// Gets the responsible of invoking delegates that handles the model. /// - /// The type of the model to handle. - /// - public interface IFireForgetHandler : IHandler where TRequest : IRequest - { - /// - /// Gets the responsible of invoking delegates that handles the model. - /// - /// The responsible of invoking delegates of type . - public IFireForgetActivator Delegates { get; } - } + /// The responsible of invoking delegates of type . + public IFireForgetActivator Delegates { get; } } diff --git a/src/Savvyio.Core/Handlers/IFireForgetRegistry.cs b/src/Savvyio.Core/Handlers/IFireForgetRegistry.cs index 8149518d..af67e31d 100644 --- a/src/Savvyio.Core/Handlers/IFireForgetRegistry.cs +++ b/src/Savvyio.Core/Handlers/IFireForgetRegistry.cs @@ -1,28 +1,27 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +/// +/// Specifies a Fire-and-Forget/In-Only MEP registry that store delegates responsible for handling type . +/// +/// The type of the model to store in the registry. +/// +public interface IFireForgetRegistry { /// - /// Specifies a Fire-and-Forget/In-Only MEP registry that store delegates responsible for handling type . + /// Registers the specified delegate . /// - /// The type of the model to store in the registry. - /// - public interface IFireForgetRegistry - { - /// - /// Registers the specified delegate . - /// - /// The type that implements . - /// The delegate that handles . - void Register(Action handler) where T : class, TRequest; + /// The type that implements . + /// The delegate that handles . + void Register(Action handler) where T : class, TRequest; - /// - /// Registers the specified function delegate . - /// - /// The type that implements . - /// The function delegate that handles . - void RegisterAsync(Func handler) where T : class, TRequest; - } + /// + /// Registers the specified function delegate . + /// + /// The type that implements . + /// The function delegate that handles . + void RegisterAsync(Func handler) where T : class, TRequest; } diff --git a/src/Savvyio.Core/Handlers/IRequestReplyActivator.cs b/src/Savvyio.Core/Handlers/IRequestReplyActivator.cs index 599254e6..68648f89 100644 --- a/src/Savvyio.Core/Handlers/IRequestReplyActivator.cs +++ b/src/Savvyio.Core/Handlers/IRequestReplyActivator.cs @@ -1,32 +1,31 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Cuemon; -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +/// +/// Specifies a way of invoking Request-Reply/In-Out MEP delegates that handles . +/// +/// The type of the model to invoke on a handler. +/// +public interface IRequestReplyActivator { /// - /// Specifies a way of invoking Request-Reply/In-Out MEP delegates that handles . + /// Invokes (if registered) the delegate handling the specified . /// - /// The type of the model to invoke on a handler. - /// - public interface IRequestReplyActivator - { - /// - /// Invokes (if registered) the delegate handling the specified . - /// - /// The type of the response. - /// The model that is handled by a delegate. - /// The value returned from the request; otherwise the default value for of the parameter. - /// true if a delegate matching the specified was invoked; otherwise, false. - bool TryInvoke(TRequest request, out TResponse result); + /// The type of the response. + /// The model that is handled by a delegate. + /// The value returned from the request; otherwise the default value for of the parameter. + /// true if a delegate matching the specified was invoked; otherwise, false. + bool TryInvoke(TRequest request, out TResponse result); - /// - /// Invokes (if registered) the function delegate handling the specified . - /// - /// The type of the response. - /// The model that is handled by a function delegate. - /// The cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// A task that represents the asynchronous operation. The task result contains a that indicates if a function delegate matching the specified was invoked and with the potential result hereof. - Task> TryInvokeAsync(TRequest request, CancellationToken ct = default); - } + /// + /// Invokes (if registered) the function delegate handling the specified . + /// + /// The type of the response. + /// The model that is handled by a function delegate. + /// The cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A task that represents the asynchronous operation. The task result contains a that indicates if a function delegate matching the specified was invoked and with the potential result hereof. + Task> TryInvokeAsync(TRequest request, CancellationToken ct = default); } diff --git a/src/Savvyio.Core/Handlers/IRequestReplyHandler.cs b/src/Savvyio.Core/Handlers/IRequestReplyHandler.cs index 75263273..9d058ceb 100644 --- a/src/Savvyio.Core/Handlers/IRequestReplyHandler.cs +++ b/src/Savvyio.Core/Handlers/IRequestReplyHandler.cs @@ -1,16 +1,15 @@ -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +/// +/// Defines a generic Request-Reply/In-Out MEP handler. +/// +/// The type of the model to handle. +/// +public interface IRequestReplyHandler : IHandler where TRequest : IRequest { /// - /// Defines a generic Request-Reply/In-Out MEP handler. + /// Gets the responsible of invoking delegates that handles the model. /// - /// The type of the model to handle. - /// - public interface IRequestReplyHandler : IHandler where TRequest : IRequest - { - /// - /// Gets the responsible of invoking delegates that handles the model. - /// - /// The responsible of invoking delegates of type . - public IRequestReplyActivator Delegates { get; } - } + /// The responsible of invoking delegates of type . + public IRequestReplyActivator Delegates { get; } } diff --git a/src/Savvyio.Core/Handlers/IRequestReplyRegistry.cs b/src/Savvyio.Core/Handlers/IRequestReplyRegistry.cs index 8257df85..462d17ab 100644 --- a/src/Savvyio.Core/Handlers/IRequestReplyRegistry.cs +++ b/src/Savvyio.Core/Handlers/IRequestReplyRegistry.cs @@ -1,30 +1,29 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +/// +/// Specifies a Request-Reply/In-Out MEP registry that store delegates responsible for handling type . +/// +/// The type of the model to store in the registry. +/// +public interface IRequestReplyRegistry { /// - /// Specifies a Request-Reply/In-Out MEP registry that store delegates responsible for handling type . + /// Registers the specified function delegate . /// - /// The type of the model to store in the registry. - /// - public interface IRequestReplyRegistry - { - /// - /// Registers the specified function delegate . - /// - /// The type that implements . - /// The type of the response to return. - /// The function delegate that handles . - void Register(Func handler) where T : class, TRequest; + /// The type that implements . + /// The type of the response to return. + /// The function delegate that handles . + void Register(Func handler) where T : class, TRequest; - /// - /// Registers the specified function delegate . - /// - /// The type that implements . - /// The type of the response to return. - /// The function delegate that handles . - void RegisterAsync(Func> handler) where T : class, TRequest; - } + /// + /// Registers the specified function delegate . + /// + /// The type that implements . + /// The type of the response to return. + /// The function delegate that handles . + void RegisterAsync(Func> handler) where T : class, TRequest; } diff --git a/src/Savvyio.Core/Handlers/OrphanedHandlerException.cs b/src/Savvyio.Core/Handlers/OrphanedHandlerException.cs index 5a7e0bb6..1b571952 100644 --- a/src/Savvyio.Core/Handlers/OrphanedHandlerException.cs +++ b/src/Savvyio.Core/Handlers/OrphanedHandlerException.cs @@ -1,37 +1,36 @@ -using System; +using System; + +namespace Savvyio.Handlers; + +/// +/// The exception that is thrown when an implementation cannot be resolved. +/// +/// +public class OrphanedHandlerException : ArgumentException -namespace Savvyio.Handlers { /// - /// The exception that is thrown when an implementation cannot be resolved. + /// Creates a new instance of using the provided generic type arguments. /// - /// - public class OrphanedHandlerException : ArgumentException - + /// The type of the input model registered to a handler. + /// The type of the handler to locate the input model. + /// The model that is being handled by a registered delegate. + /// The name of the parameter that caused the current exception. + /// A new instance of initialized from the provided generic type arguments. + public static OrphanedHandlerException Create(TRequest request, string paramName) + where TRequest : IRequest + where THandler : IHandler { - /// - /// Creates a new instance of using the provided generic type arguments. - /// - /// The type of the input model registered to a handler. - /// The type of the handler to locate the input model. - /// The model that is being handled by a registered delegate. - /// The name of the parameter that caused the current exception. - /// A new instance of initialized from the provided generic type arguments. - public static OrphanedHandlerException Create(TRequest request, string paramName) - where TRequest : IRequest - where THandler : IHandler - { - var requestType = request.GetType(); - return new OrphanedHandlerException($"Unable to retrieve an {typeof(THandler).Name} for the specified {requestType.Name}: {requestType.FullName}.", paramName); - } + var requestType = request.GetType(); + return new OrphanedHandlerException($"Unable to retrieve an {typeof(THandler).Name} for the specified {requestType.Name}: {requestType.FullName}.", paramName); + } - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The name of the parameter that caused the current exception. - public OrphanedHandlerException(string message, string paramName) : base(message, paramName) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// The name of the parameter that caused the current exception. + public OrphanedHandlerException(string message, string paramName) : base(message, paramName) + { } } diff --git a/src/Savvyio.Core/Handlers/RequestReplyManager.cs b/src/Savvyio.Core/Handlers/RequestReplyManager.cs index b8c19f17..775e2e81 100644 --- a/src/Savvyio.Core/Handlers/RequestReplyManager.cs +++ b/src/Savvyio.Core/Handlers/RequestReplyManager.cs @@ -1,53 +1,52 @@ -using System; +using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; using Cuemon; -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +internal sealed class RequestReplyManager : IRequestReplyRegistry, IRequestReplyActivator { - internal sealed class RequestReplyManager : IRequestReplyRegistry, IRequestReplyActivator - { - private readonly ConcurrentDictionary> _handlers = new(); - private readonly ConcurrentDictionary>> _asyncHandlers = new(); + private readonly ConcurrentDictionary> _handlers = new(); + private readonly ConcurrentDictionary>> _asyncHandlers = new(); - public RequestReplyManager() - { - } + public RequestReplyManager() + { + } - public void Register(Func handler) where T : class, TModel //, IRequestReply - { - Validator.ThrowIfNull(handler); - _handlers.TryAdd(typeof(T), e => handler(e as T)); - } + public void Register(Func handler) where T : class, TModel //, IRequestReply + { + Validator.ThrowIfNull(handler); + _handlers.TryAdd(typeof(T), e => handler(e as T)); + } - public void RegisterAsync(Func> handler) where T : class, TModel //, IRequestReply - { - Validator.ThrowIfNull(handler); - _asyncHandlers.TryAdd(typeof(T), async (h, t) => await handler(h as T, t)); - } + public void RegisterAsync(Func> handler) where T : class, TModel //, IRequestReply + { + Validator.ThrowIfNull(handler); + _asyncHandlers.TryAdd(typeof(T), async (h, t) => await handler(h as T, t)); + } - public bool TryInvoke(TModel request, out TResult result) + public bool TryInvoke(TModel request, out TResult result) + { + Validator.ThrowIfNull(request); + if (_handlers.TryGetValue(request.GetType(), out var handler)) { - Validator.ThrowIfNull(request); - if (_handlers.TryGetValue(request.GetType(), out var handler)) - { - result = (TResult)handler(request); - return true; - } - result = default; - return false; + result = (TResult)handler(request); + return true; } + result = default; + return false; + } - public async Task> TryInvokeAsync(TModel request, CancellationToken ct = default) + public async Task> TryInvokeAsync(TModel request, CancellationToken ct = default) + { + Validator.ThrowIfNull(request); + if (_asyncHandlers.TryGetValue(request.GetType(), out var handler)) { - Validator.ThrowIfNull(request); - if (_asyncHandlers.TryGetValue(request.GetType(), out var handler)) - { - var result = await handler(request, ct).ConfigureAwait(false); - return new SuccessfulValue((TResult)result); - } - return new UnsuccessfulValue(); + var result = await handler(request, ct).ConfigureAwait(false); + return new SuccessfulValue((TResult)result); } + return new UnsuccessfulValue(); } } diff --git a/src/Savvyio.Core/Handlers/RequestReplyRegistryExtensions.cs b/src/Savvyio.Core/Handlers/RequestReplyRegistryExtensions.cs index 9ed35efb..31c5c1a3 100644 --- a/src/Savvyio.Core/Handlers/RequestReplyRegistryExtensions.cs +++ b/src/Savvyio.Core/Handlers/RequestReplyRegistryExtensions.cs @@ -1,23 +1,22 @@ -using System; +using System; using System.Threading.Tasks; -namespace Savvyio.Handlers +namespace Savvyio.Handlers; + +/// +/// Extension methods for the interface. +/// +public static class RequestReplyRegistryExtensions { /// - /// Extension methods for the interface. + /// Registers the asynchronous. /// - public static class RequestReplyRegistryExtensions + /// The type of the model to store in the registry. + /// The type of the response to return. + /// The to extend. + /// The function delegate that handles . + public static void RegisterAsync(this IRequestReplyRegistry registry, Func> handler) where TRequest : class { - /// - /// Registers the asynchronous. - /// - /// The type of the model to store in the registry. - /// The type of the response to return. - /// The to extend. - /// The function delegate that handles . - public static void RegisterAsync(this IRequestReplyRegistry registry, Func> handler) where TRequest : class - { - registry.RegisterAsync((h, _) => handler(h)); - } + registry.RegisterAsync((h, _) => handler(h)); } } diff --git a/src/Savvyio.Core/IDataSource.cs b/src/Savvyio.Core/IDataSource.cs index 7aaf1078..00f5f6cd 100644 --- a/src/Savvyio.Core/IDataSource.cs +++ b/src/Savvyio.Core/IDataSource.cs @@ -1,15 +1,14 @@ -using Savvyio.Data; +using Savvyio.Data; using Savvyio.Domain; -namespace Savvyio +namespace Savvyio; + +/// +/// A marker interface that specifies the actual I/O communication with a source of data. +/// +/// A data source can be anything that holds data, eg. high level DbContext (EFCore) to low level ADO.NET. It could also be as simple as files, services or anything that can expose data. +/// +/// +public interface IDataSource { - /// - /// A marker interface that specifies the actual I/O communication with a source of data. - /// - /// A data source can be anything that holds data, eg. high level DbContext (EFCore) to low level ADO.NET. It could also be as simple as files, services or anything that can expose data. - /// - /// - public interface IDataSource - { - } } diff --git a/src/Savvyio.Core/IHandler.cs b/src/Savvyio.Core/IHandler.cs index a695c4ee..4818d938 100644 --- a/src/Savvyio.Core/IHandler.cs +++ b/src/Savvyio.Core/IHandler.cs @@ -1,17 +1,16 @@ -namespace Savvyio +namespace Savvyio; + +/// +/// A marker interface that specifies a handler. +/// +public interface IHandler { - /// - /// A marker interface that specifies a handler. - /// - public interface IHandler - { - } +} - /// - /// Defines a marker interface for a generic handler. - /// - /// The type of the model to handle. - public interface IHandler : IHandler where TRequest : IRequest - { - } +/// +/// Defines a marker interface for a generic handler. +/// +/// The type of the model to handle. +public interface IHandler : IHandler where TRequest : IRequest +{ } diff --git a/src/Savvyio.Core/IHandlerServicesDescriptor.cs b/src/Savvyio.Core/IHandlerServicesDescriptor.cs index 0d7ae216..220b09fc 100644 --- a/src/Savvyio.Core/IHandlerServicesDescriptor.cs +++ b/src/Savvyio.Core/IHandlerServicesDescriptor.cs @@ -1,16 +1,15 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace Savvyio +namespace Savvyio; + +/// +/// Defines a contract about implementations of the interface such as name, declared members and what type of request they handle. +/// +public interface IHandlerServicesDescriptor { /// - /// Defines a contract about implementations of the interface such as name, declared members and what type of request they handle. + /// Generates the handler discoveries. /// - public interface IHandlerServicesDescriptor - { - /// - /// Generates the handler discoveries. - /// - /// A collection of representing the handler discoveries. - IEnumerable GenerateHandlerDiscoveries(); - } + /// A collection of representing the handler discoveries. + IEnumerable GenerateHandlerDiscoveries(); } diff --git a/src/Savvyio.Core/IIdentity.cs b/src/Savvyio.Core/IIdentity.cs index d8fc56a1..b4edf23a 100644 --- a/src/Savvyio.Core/IIdentity.cs +++ b/src/Savvyio.Core/IIdentity.cs @@ -1,15 +1,14 @@ -namespace Savvyio +namespace Savvyio; + +/// +/// Defines a generic identity typically associated with a storage such as a database. +/// +/// The type of the identifier. +public interface IIdentity { /// - /// Defines a generic identity typically associated with a storage such as a database. + /// Gets the value of the identifier. /// - /// The type of the identifier. - public interface IIdentity - { - /// - /// Gets the value of the identifier. - /// - /// The value of the identifier. - TKey Id { get; } - } + /// The value of the identifier. + TKey Id { get; } } diff --git a/src/Savvyio.Core/IMarshaller.cs b/src/Savvyio.Core/IMarshaller.cs index c8143147..e23255e2 100644 --- a/src/Savvyio.Core/IMarshaller.cs +++ b/src/Savvyio.Core/IMarshaller.cs @@ -1,42 +1,41 @@ -using System; +using System; using System.IO; -namespace Savvyio +namespace Savvyio; + +/// +/// Defines methods for serializing and deserializing objects to and from a . +/// +public interface IMarshaller { /// - /// Defines methods for serializing and deserializing objects to and from a . + /// Serializes the specified to an object of . /// - public interface IMarshaller - { - /// - /// Serializes the specified to an object of . - /// - /// The object to serialize to format. - /// A of the serialized . - Stream Serialize(TValue value); + /// The object to serialize to format. + /// A of the serialized . + Stream Serialize(TValue value); - /// - /// Serializes the specified to an object of . - /// - /// The object to serialize to format. - /// The type of the object to serialize. - /// A of the serialized . - Stream Serialize(object value, Type inputType); + /// + /// Serializes the specified to an object of . + /// + /// The object to serialize to format. + /// The type of the object to serialize. + /// A of the serialized . + Stream Serialize(object value, Type inputType); - /// - /// Deserializes the specified into an object of . - /// - /// The type of the object to return. - /// The object from which to deserialize the object graph. - /// An object of . - TValue Deserialize(Stream data); + /// + /// Deserializes the specified into an object of . + /// + /// The type of the object to return. + /// The object from which to deserialize the object graph. + /// An object of . + TValue Deserialize(Stream data); - /// - /// Deserializes the specified into an object of . - /// - /// The string from which to deserialize the object graph. - /// The type of the deserialized object. - /// An object of . - object Deserialize(Stream data, Type returnType); - } + /// + /// Deserializes the specified into an object of . + /// + /// The string from which to deserialize the object graph. + /// The type of the deserialized object. + /// An object of . + object Deserialize(Stream data, Type returnType); } diff --git a/src/Savvyio.Core/IMetadata.cs b/src/Savvyio.Core/IMetadata.cs index 0ea6583d..4c872ecb 100644 --- a/src/Savvyio.Core/IMetadata.cs +++ b/src/Savvyio.Core/IMetadata.cs @@ -1,14 +1,13 @@ -namespace Savvyio +namespace Savvyio; + +/// +/// Defines a generic way to associate metadata with any type of object. +/// +public interface IMetadata { /// - /// Defines a generic way to associate metadata with any type of object. + /// Gets the associated metadata of this instance. /// - public interface IMetadata - { - /// - /// Gets the associated metadata of this instance. - /// - /// The associated metadata of this instance. - public IMetadataDictionary Metadata { get; } - } + /// The associated metadata of this instance. + public IMetadataDictionary Metadata { get; } } diff --git a/src/Savvyio.Core/IMetadataDictionary.cs b/src/Savvyio.Core/IMetadataDictionary.cs index 0bd346ed..ed133784 100644 --- a/src/Savvyio.Core/IMetadataDictionary.cs +++ b/src/Savvyio.Core/IMetadataDictionary.cs @@ -1,12 +1,11 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace Savvyio +namespace Savvyio; + +/// +/// Defines a generic way to support metadata capabilities. +/// +/// +public interface IMetadataDictionary : IDictionary { - /// - /// Defines a generic way to support metadata capabilities. - /// - /// - public interface IMetadataDictionary : IDictionary - { - } } diff --git a/src/Savvyio.Core/IRequest.cs b/src/Savvyio.Core/IRequest.cs index 900a7cb5..e26dd19d 100644 --- a/src/Savvyio.Core/IRequest.cs +++ b/src/Savvyio.Core/IRequest.cs @@ -1,10 +1,9 @@ -namespace Savvyio +namespace Savvyio; + +/// +/// A marker interface that specifies a request/model/event. +/// +/// A request can be a command, domain event, integration event, query etc. +public interface IRequest { - /// - /// A marker interface that specifies a request/model/event. - /// - /// A request can be a command, domain event, integration event, query etc. - public interface IRequest - { - } } diff --git a/src/Savvyio.Core/Messaging/AcknowledgedEventArgs.cs b/src/Savvyio.Core/Messaging/AcknowledgedEventArgs.cs index 2f4d8542..bf3372ab 100644 --- a/src/Savvyio.Core/Messaging/AcknowledgedEventArgs.cs +++ b/src/Savvyio.Core/Messaging/AcknowledgedEventArgs.cs @@ -1,36 +1,35 @@ -using System; +using System; using System.Collections.Generic; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Provides data for message related operations. +/// +/// +public class AcknowledgedEventArgs : EventArgs { /// - /// Provides data for message related operations. + /// Represents an event with no event data. /// - /// - public class AcknowledgedEventArgs : EventArgs - { - /// - /// Represents an event with no event data. - /// - public new static readonly AcknowledgedEventArgs Empty = new(); - - AcknowledgedEventArgs() : this(null) - { - } + public new static readonly AcknowledgedEventArgs Empty = new(); - /// - /// Initializes a new instance of the class. - /// - /// The > to associate with the event. - public AcknowledgedEventArgs(IDictionary properties) - { - Properties = properties ?? new Dictionary(); - } + AcknowledgedEventArgs() : this(null) + { + } - /// - /// Share state between components during message processing. - /// - /// The state between components during message processing. - public IDictionary Properties { get; } + /// + /// Initializes a new instance of the class. + /// + /// The > to associate with the event. + public AcknowledgedEventArgs(IDictionary properties) + { + Properties = properties ?? new Dictionary(); } + + /// + /// Share state between components during message processing. + /// + /// The state between components during message processing. + public IDictionary Properties { get; } } diff --git a/src/Savvyio.Core/Messaging/Cryptography/ISignedMessage.cs b/src/Savvyio.Core/Messaging/Cryptography/ISignedMessage.cs index a96f5773..fdab9ad8 100644 --- a/src/Savvyio.Core/Messaging/Cryptography/ISignedMessage.cs +++ b/src/Savvyio.Core/Messaging/Cryptography/ISignedMessage.cs @@ -1,15 +1,14 @@ -namespace Savvyio.Messaging.Cryptography +namespace Savvyio.Messaging.Cryptography; + +/// +/// Defines a generic way to wrap an inside a cryptographically signed message. +/// +/// The type of the payload constraint to the interface. +public interface ISignedMessage : IMessage where T : IRequest { /// - /// Defines a generic way to wrap an inside a cryptographically signed message. + /// Gets the cryptographic signature of the message. /// - /// The type of the payload constraint to the interface. - public interface ISignedMessage : IMessage where T : IRequest - { - /// - /// Gets the cryptographic signature of the message. - /// - /// The cryptographic signature of the message. - string Signature { get; } - } + /// The cryptographic signature of the message. + string Signature { get; } } diff --git a/src/Savvyio.Core/Messaging/IAcknowledgeable.cs b/src/Savvyio.Core/Messaging/IAcknowledgeable.cs index b8a80245..d14aa339 100644 --- a/src/Savvyio.Core/Messaging/IAcknowledgeable.cs +++ b/src/Savvyio.Core/Messaging/IAcknowledgeable.cs @@ -1,28 +1,27 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Defines a generic way to make a message acknowledgeable. +/// +public interface IAcknowledgeable { - /// - /// Defines a generic way to make a message acknowledgeable. - /// - public interface IAcknowledgeable - { - /// - /// Occurs when the method is called. - /// - event AsyncEventHandler Acknowledged; + /// + /// Occurs when the method is called. + /// + event AsyncEventHandler Acknowledged; - /// - /// Share state between components during message processing. - /// - /// The state between components during message processing. - IDictionary Properties { get; } + /// + /// Share state between components during message processing. + /// + /// The state between components during message processing. + IDictionary Properties { get; } - /// - /// Acknowledges that this message was processed successfully. - /// - /// A task that represents the asynchronous operation. - Task AcknowledgeAsync(); - } + /// + /// Acknowledges that this message was processed successfully. + /// + /// A task that represents the asynchronous operation. + Task AcknowledgeAsync(); } diff --git a/src/Savvyio.Core/Messaging/IMessage.cs b/src/Savvyio.Core/Messaging/IMessage.cs index f2eb819c..77368a2b 100644 --- a/src/Savvyio.Core/Messaging/IMessage.cs +++ b/src/Savvyio.Core/Messaging/IMessage.cs @@ -1,43 +1,42 @@ -using System; +using System; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Defines a generic way to wrap an inside a message. +/// +/// The type of the payload constraint to the interface. +/// Inspired by cloudevents.io (https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md) default attributes. +/// +public interface IMessage : IAcknowledgeable where T : IRequest { /// - /// Defines a generic way to wrap an inside a message. + /// Gets the identifier of the message. When combined with , this enables deduplication. /// - /// The type of the payload constraint to the interface. - /// Inspired by cloudevents.io (https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md) default attributes. - /// - public interface IMessage : IAcknowledgeable where T : IRequest - { - /// - /// Gets the identifier of the message. When combined with , this enables deduplication. - /// - /// The identifier of the message. - string Id { get; } + /// The identifier of the message. + string Id { get; } - /// - /// Gets the context that describes the origin of the message. When combined with , this enables deduplication. - /// - /// The context that describes the origin of the message. - public string Source { get; } + /// + /// Gets the context that describes the origin of the message. When combined with , this enables deduplication. + /// + /// The context that describes the origin of the message. + public string Source { get; } - /// - /// Gets the type that describes the type of event related to the originating occurrence. - /// - /// The type that describes the type of event related to the originating occurrence. - string Type { get; } + /// + /// Gets the type that describes the type of event related to the originating occurrence. + /// + /// The type that describes the type of event related to the originating occurrence. + string Type { get; } - /// - /// Gets the time, expressed as the Coordinated Universal Time (UTC), at which this message was generated. - /// - /// The time at which this message was generated. - DateTime? Time { get; } + /// + /// Gets the time, expressed as the Coordinated Universal Time (UTC), at which this message was generated. + /// + /// The time at which this message was generated. + DateTime? Time { get; } - /// - /// Gets the payload of the message. - /// - /// The payload of the message. - T Data { get; } - } + /// + /// Gets the payload of the message. + /// + /// The payload of the message. + T Data { get; } } diff --git a/src/Savvyio.Core/Messaging/IPointToPointChannel.cs b/src/Savvyio.Core/Messaging/IPointToPointChannel.cs index 9ae389d7..013f3cf5 100644 --- a/src/Savvyio.Core/Messaging/IPointToPointChannel.cs +++ b/src/Savvyio.Core/Messaging/IPointToPointChannel.cs @@ -1,12 +1,11 @@ -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Specifies an interface for a bus that is used for interacting with other subsystems (out-process/inter-application) to do something (e.g., change the state). +/// +/// The type of the model to invoke on a handler. +/// +/// +public interface IPointToPointChannel : ISender, IReceiver where TRequest : IRequest { - /// - /// Specifies an interface for a bus that is used for interacting with other subsystems (out-process/inter-application) to do something (e.g., change the state). - /// - /// The type of the model to invoke on a handler. - /// - /// - public interface IPointToPointChannel : ISender, IReceiver where TRequest : IRequest - { - } } diff --git a/src/Savvyio.Core/Messaging/IPublishSubscribeChannel.cs b/src/Savvyio.Core/Messaging/IPublishSubscribeChannel.cs index 2caa059a..cce2bfb4 100644 --- a/src/Savvyio.Core/Messaging/IPublishSubscribeChannel.cs +++ b/src/Savvyio.Core/Messaging/IPublishSubscribeChannel.cs @@ -1,12 +1,11 @@ -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Specifies an interface for a bus that is used for interacting with other subsystems (out-process/inter-application) to be notified (e.g., made aware of something that has happened). +/// +/// The type of the model to invoke on a handler. +/// +/// +public interface IPublishSubscribeChannel : IPublisher, ISubscriber where TRequest : IRequest { - /// - /// Specifies an interface for a bus that is used for interacting with other subsystems (out-process/inter-application) to be notified (e.g., made aware of something that has happened). - /// - /// The type of the model to invoke on a handler. - /// - /// - public interface IPublishSubscribeChannel : IPublisher, ISubscriber where TRequest : IRequest - { - } } diff --git a/src/Savvyio.Core/Messaging/IPublisher.cs b/src/Savvyio.Core/Messaging/IPublisher.cs index 3298cb86..4e2fab00 100644 --- a/src/Savvyio.Core/Messaging/IPublisher.cs +++ b/src/Savvyio.Core/Messaging/IPublisher.cs @@ -1,21 +1,20 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Defines a publisher/sender channel for interacting with other subsystems (out-process/inter-application) to be notified (e.g., made aware of something that has happened). +/// +/// The type of the model to invoke on a handler. +public interface IPublisher where TRequest : IRequest { /// - /// Defines a publisher/sender channel for interacting with other subsystems (out-process/inter-application) to be notified (e.g., made aware of something that has happened). + /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. /// - /// The type of the model to invoke on a handler. - public interface IPublisher where TRequest : IRequest - { - /// - /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The message to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - Task PublishAsync(IMessage message, Action setup = null); - } + /// The message to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + Task PublishAsync(IMessage message, Action setup = null); } diff --git a/src/Savvyio.Core/Messaging/IReceiver.cs b/src/Savvyio.Core/Messaging/IReceiver.cs index 4142ffda..bbeae8d6 100644 --- a/src/Savvyio.Core/Messaging/IReceiver.cs +++ b/src/Savvyio.Core/Messaging/IReceiver.cs @@ -1,20 +1,19 @@ -using System; +using System; using System.Collections.Generic; using Cuemon.Threading; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Defines a consumer/receiver channel used by subsystems to receive a command and perform one or more actions (e.g., change the state). +/// +/// The type of the model to invoke on a handler. +public interface IReceiver where TRequest : IRequest { /// - /// Defines a consumer/receiver channel used by subsystems to receive a command and perform one or more actions (e.g., change the state). + /// Receive one or more message(s) asynchronous using Point-to-Point Channel/P2P MEP. /// - /// The type of the model to invoke on a handler. - public interface IReceiver where TRequest : IRequest - { - /// - /// Receive one or more message(s) asynchronous using Point-to-Point Channel/P2P MEP. - /// - /// The which may be configured. - /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . - IAsyncEnumerable> ReceiveAsync(Action setup = null); - } + /// The which may be configured. + /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . + IAsyncEnumerable> ReceiveAsync(Action setup = null); } diff --git a/src/Savvyio.Core/Messaging/ISender.cs b/src/Savvyio.Core/Messaging/ISender.cs index 0823ec24..613033bc 100644 --- a/src/Savvyio.Core/Messaging/ISender.cs +++ b/src/Savvyio.Core/Messaging/ISender.cs @@ -1,22 +1,21 @@ -using Cuemon.Threading; +using Cuemon.Threading; using System.Threading.Tasks; using System; using System.Collections.Generic; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Defines a producer/sender channel used for interacting with other subsystems (out-process/inter-application) to do something (e.g., change the state). +/// +/// The type of the model to invoke on a handler. +public interface ISender where TRequest : IRequest { /// - /// Defines a producer/sender channel used for interacting with other subsystems (out-process/inter-application) to do something (e.g., change the state). + /// Sends the specified asynchronous using Point-to-Point Channel/P2P MEP. /// - /// The type of the model to invoke on a handler. - public interface ISender where TRequest : IRequest - { - /// - /// Sends the specified asynchronous using Point-to-Point Channel/P2P MEP. - /// - /// The messages to send. - /// The which may be configured. - /// A that represents the asynchronous operation. - Task SendAsync(IEnumerable> messages, Action setup = null); - } + /// The messages to send. + /// The which may be configured. + /// A that represents the asynchronous operation. + Task SendAsync(IEnumerable> messages, Action setup = null); } diff --git a/src/Savvyio.Core/Messaging/ISubscriber.cs b/src/Savvyio.Core/Messaging/ISubscriber.cs index 65f9786b..e8a34fe8 100644 --- a/src/Savvyio.Core/Messaging/ISubscriber.cs +++ b/src/Savvyio.Core/Messaging/ISubscriber.cs @@ -1,21 +1,20 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Defines a subscriber/receiver channel used by subsystems to subscribe to messages (typically events) to be made aware of something that has happened. +/// +/// The type of the model to invoke on a handler. +public interface ISubscriber where TRequest : IRequest { /// - /// Defines a subscriber/receiver channel used by subsystems to subscribe to messages (typically events) to be made aware of something that has happened. + /// Subscribe to one or more message(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. /// - /// The type of the model to invoke on a handler. - public interface ISubscriber where TRequest : IRequest - { - /// - /// Subscribe to one or more message(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The function delegate that will handle the message. - /// The which may be configured. - /// A that represents the asynchronous operation. - Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null); - } + /// The function delegate that will handle the message. + /// The which may be configured. + /// A that represents the asynchronous operation. + Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null); } diff --git a/src/Savvyio.Core/Messaging/SubscribeAsyncOptions.cs b/src/Savvyio.Core/Messaging/SubscribeAsyncOptions.cs index 1e7a8900..cba5e958 100644 --- a/src/Savvyio.Core/Messaging/SubscribeAsyncOptions.cs +++ b/src/Savvyio.Core/Messaging/SubscribeAsyncOptions.cs @@ -1,39 +1,38 @@ -using System; +using System; using System.Threading; using Cuemon.Threading; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Configuration options that is related to implementations of the interface. +/// +/// +public class SubscribeAsyncOptions : AsyncOptions { /// - /// Configuration options that is related to implementations of the interface. + /// Initializes a new instance of the class. /// - /// - public class SubscribeAsyncOptions : AsyncOptions + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// false + /// + /// + /// + public SubscribeAsyncOptions() { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// false - /// - /// - /// - public SubscribeAsyncOptions() - { - } - - /// - /// Gets or sets a value indicating whether to throw an (or derived thereof) if a was requested cancelled. - /// - /// true to throw an if a was requested cancelled; otherwise, false. - public bool ThrowIfCancellationWasRequested { get; set; } } + + /// + /// Gets or sets a value indicating whether to throw an (or derived thereof) if a was requested cancelled. + /// + /// true to throw an if a was requested cancelled; otherwise, false. + public bool ThrowIfCancellationWasRequested { get; set; } } diff --git a/src/Savvyio.Core/MetadataDictionary.cs b/src/Savvyio.Core/MetadataDictionary.cs index a362ee17..6df5c9cf 100644 --- a/src/Savvyio.Core/MetadataDictionary.cs +++ b/src/Savvyio.Core/MetadataDictionary.cs @@ -1,216 +1,215 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Cuemon.Collections.Generic; -namespace Savvyio +namespace Savvyio; + +/// +/// Provides a default implementation of the interface. This class cannot be inherited. +/// +/// +public sealed class MetadataDictionary : IMetadataDictionary { + private readonly IDictionary _dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + /// - /// Provides a default implementation of the interface. This class cannot be inherited. + /// Gets a list of the reserved keywords. /// - /// - public sealed class MetadataDictionary : IMetadataDictionary + /// A list of the reserved keywords. + public static IReadOnlyCollection ReservedKeywords { get; } = Arguments.ToEnumerableOf(Timestamp, MemberType, CorrelationId, RequestId, CausationId, EventId, AggregateVersion).ToList(); + + internal static string EnsureReservedKeywordCapitalization(string key) { - private readonly IDictionary _dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + return ReservedKeywords.SingleOrDefault(rk => rk.Equals(key, StringComparison.OrdinalIgnoreCase)) ?? key; + } - /// - /// Gets a list of the reserved keywords. - /// - /// A list of the reserved keywords. - public static IReadOnlyCollection ReservedKeywords { get; } = Arguments.ToEnumerableOf(Timestamp, MemberType, CorrelationId, RequestId, CausationId, EventId, AggregateVersion).ToList(); + /// + /// Timestamp is a reserved keyword. + /// + public const string Timestamp = nameof(Timestamp); - internal static string EnsureReservedKeywordCapitalization(string key) - { - return ReservedKeywords.SingleOrDefault(rk => rk.Equals(key, StringComparison.OrdinalIgnoreCase)) ?? key; - } + /// + /// MemberType is a reserved keyword. + /// + public const string MemberType = nameof(MemberType); - /// - /// Timestamp is a reserved keyword. - /// - public const string Timestamp = nameof(Timestamp); - - /// - /// MemberType is a reserved keyword. - /// - public const string MemberType = nameof(MemberType); - - /// - /// CorrelationId is a reserved keyword. - /// - public const string CorrelationId = nameof(CorrelationId); - - /// - /// RequestId is a reserved keyword. - /// - public const string RequestId = nameof(RequestId); - - /// - /// CausationId is a reserved keyword. - /// - public const string CausationId = nameof(CausationId); - - /// - /// EventId is a reserved keyword. - /// - public const string EventId = nameof(EventId); - - /// - /// AggregateVersion is a reserved keyword. - /// - public const string AggregateVersion = nameof(AggregateVersion); - - /// - /// Initializes a new instance of the class. - /// - public MetadataDictionary() - { - } + /// + /// CorrelationId is a reserved keyword. + /// + public const string CorrelationId = nameof(CorrelationId); - /// - /// Gets or sets the element with the specified key. - /// - /// The key of the value to get or set. - /// The value associated with the specified key. - public object this[string key] - { - get => _dictionary[key]; - set - { - var parsedKey = EnsureReservedKeywordCapitalization(key); - _dictionary[parsedKey] = value; - } - } + /// + /// RequestId is a reserved keyword. + /// + public const string RequestId = nameof(RequestId); - /// - /// Gets the number of elements contained in the . - /// - /// The number of key/value pairs contained in the . - public int Count => _dictionary.Count; - - /// - /// Gets a value indicating whether the is read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly => _dictionary.IsReadOnly; - - /// - /// Gets an containing the keys of the . - /// - /// A containing the keys in the . - public ICollection Keys => _dictionary.Keys; - - /// - /// Gets an containing the values in the . - /// - /// A containing the values in the . - public ICollection Values => _dictionary.Values; - - /// - /// Adds an item to the . - /// - /// The object to add to the . - public void Add(KeyValuePair item) - { - Add(item.Key, item.Value); - } + /// + /// CausationId is a reserved keyword. + /// + public const string CausationId = nameof(CausationId); + + /// + /// EventId is a reserved keyword. + /// + public const string EventId = nameof(EventId); + + /// + /// AggregateVersion is a reserved keyword. + /// + public const string AggregateVersion = nameof(AggregateVersion); - /// - /// Adds an element with the provided key and value to the . - /// - /// The object to use as the key of the element to add. - /// The object to use as the value of the element to add. - public void Add(string key, object value) + /// + /// Initializes a new instance of the class. + /// + public MetadataDictionary() + { + } + + /// + /// Gets or sets the element with the specified key. + /// + /// The key of the value to get or set. + /// The value associated with the specified key. + public object this[string key] + { + get => _dictionary[key]; + set { var parsedKey = EnsureReservedKeywordCapitalization(key); - _dictionary.Add(parsedKey, value); + _dictionary[parsedKey] = value; } + } - /// - /// Removes all items from the . - /// - public void Clear() - { - _dictionary.Clear(); - } + /// + /// Gets the number of elements contained in the . + /// + /// The number of key/value pairs contained in the . + public int Count => _dictionary.Count; - /// - /// Copies the elements of the to an array of type , starting at the specified array index. - /// - /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - /// The zero-based index in at which copying begins. - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - _dictionary.CopyTo(array, arrayIndex); - } + /// + /// Gets a value indicating whether the is read-only. + /// + /// true if this instance is read only; otherwise, false. + public bool IsReadOnly => _dictionary.IsReadOnly; - /// - /// Determines whether the contains a specific value. - /// - /// The object to locate in the . - /// if is found in the ; otherwise, . - public bool Contains(KeyValuePair item) - { - return _dictionary.Contains(item); - } + /// + /// Gets an containing the keys of the . + /// + /// A containing the keys in the . + public ICollection Keys => _dictionary.Keys; - /// - /// Determines whether the contains an element with the specified key. - /// - /// The key to locate in the . - /// if the contains an element with the key; otherwise, . - public bool ContainsKey(string key) - { - return _dictionary.ContainsKey(key); - } + /// + /// Gets an containing the values in the . + /// + /// A containing the values in the . + public ICollection Values => _dictionary.Values; - /// - /// Removes the first occurrence of a specific object from the . - /// - /// The object to remove from the . - /// if was successfully removed from the ; otherwise, . This method also returns if is not found in the original . - public bool Remove(KeyValuePair item) - { - return Remove(item.Key); - } + /// + /// Adds an item to the . + /// + /// The object to add to the . + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } - /// - /// Removes the element with the specified key from the . - /// - /// The key of the element to remove. - /// if the element is successfully removed; otherwise, . This method also returns if was not found in the original . - public bool Remove(string key) - { - return _dictionary.Remove(key); - } + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + public void Add(string key, object value) + { + var parsedKey = EnsureReservedKeywordCapitalization(key); + _dictionary.Add(parsedKey, value); + } - /// - /// Gets the value associated with the specified key. - /// - /// The key whose value to get. - /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. - /// if the object that implements contains an element with the specified key; otherwise, . - public bool TryGetValue(string key, out object value) - { - return _dictionary.TryGetValue(key, out value); - } + /// + /// Removes all items from the . + /// + public void Clear() + { + _dictionary.Clear(); + } - /// - /// Returns an enumerator that iterates through the collection. - /// - /// An enumerator that can be used to iterate through the collection. - public IEnumerator> GetEnumerator() - { - return _dictionary.GetEnumerator(); - } + /// + /// Copies the elements of the to an array of type , starting at the specified array index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + _dictionary.CopyTo(array, arrayIndex); + } - /// - /// Returns an enumerator that iterates through a collection. - /// - /// An object that can be used to iterate through the collection. - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)_dictionary).GetEnumerator(); - } + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// if is found in the ; otherwise, . + public bool Contains(KeyValuePair item) + { + return _dictionary.Contains(item); + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// if the contains an element with the key; otherwise, . + public bool ContainsKey(string key) + { + return _dictionary.ContainsKey(key); + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// if was successfully removed from the ; otherwise, . This method also returns if is not found in the original . + public bool Remove(KeyValuePair item) + { + return Remove(item.Key); + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// if the element is successfully removed; otherwise, . This method also returns if was not found in the original . + public bool Remove(string key) + { + return _dictionary.Remove(key); + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// if the object that implements contains an element with the specified key; otherwise, . + public bool TryGetValue(string key, out object value) + { + return _dictionary.TryGetValue(key, out value); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// An enumerator that can be used to iterate through the collection. + public IEnumerator> GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// An object that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_dictionary).GetEnumerator(); } } diff --git a/src/Savvyio.Core/MetadataExtensions.cs b/src/Savvyio.Core/MetadataExtensions.cs index 7ee3a80a..862a52af 100644 --- a/src/Savvyio.Core/MetadataExtensions.cs +++ b/src/Savvyio.Core/MetadataExtensions.cs @@ -1,188 +1,187 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.Reflection; -namespace Savvyio +namespace Savvyio; + +/// +/// Extension methods for the interface. +/// +public static class MetadataExtensions { /// - /// Extension methods for the interface. + /// Gets the string representation of the causation identifier from the . /// - public static class MetadataExtensions + /// The model that implements the interface. + /// The to extend. + /// The string representation of the causation identifier from the . + public static string GetCausationId(this T request) where T : IMetadata { - /// - /// Gets the string representation of the causation identifier from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The string representation of the causation identifier from the . - public static string GetCausationId(this T request) where T : IMetadata - { - return MetadataFactory.Get(request, MetadataDictionary.CausationId).As(); - } + return MetadataFactory.Get(request, MetadataDictionary.CausationId).As(); + } - /// - /// Gets the string representation of the correlation identifier from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The string representation of the correlation identifier from the . - public static string GetCorrelationId(this T request) where T : IMetadata - { - return MetadataFactory.Get(request, MetadataDictionary.CorrelationId).As(); - } + /// + /// Gets the string representation of the correlation identifier from the . + /// + /// The model that implements the interface. + /// The to extend. + /// The string representation of the correlation identifier from the . + public static string GetCorrelationId(this T request) where T : IMetadata + { + return MetadataFactory.Get(request, MetadataDictionary.CorrelationId).As(); + } - /// - /// Gets the string representation of the request identifier from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The string representation of the request identifier from the . - public static string GetRequestId(this T request) where T : IMetadata - { - return MetadataFactory.Get(request, MetadataDictionary.RequestId).As(); - } + /// + /// Gets the string representation of the request identifier from the . + /// + /// The model that implements the interface. + /// The to extend. + /// The string representation of the request identifier from the . + public static string GetRequestId(this T request) where T : IMetadata + { + return MetadataFactory.Get(request, MetadataDictionary.RequestId).As(); + } - /// - /// Gets the string representation of the underlying member type of . - /// - /// The model that implements the interface. - /// The to extend. - /// The string representation of the member type from the . - /// The underlying of a model expressed as a string representation (fully qualified name of the type, including its namespace, comma delimited with the simple name of the assembly). - public static string GetMemberType(this T request) where T : IMetadata - { - return MetadataFactory.Get(request, MetadataDictionary.MemberType).As(); - } + /// + /// Gets the string representation of the underlying member type of . + /// + /// The model that implements the interface. + /// The to extend. + /// The string representation of the member type from the . + /// The underlying of a model expressed as a string representation (fully qualified name of the type, including its namespace, comma delimited with the simple name of the assembly). + public static string GetMemberType(this T request) where T : IMetadata + { + return MetadataFactory.Get(request, MetadataDictionary.MemberType).As(); + } - /// - /// Assigns a new to the . - /// - /// The model that implements the interface. - /// The to extend. - /// The causation identifier of the model. - /// A reference to after the operation has completed. - public static T SetCausationId(this T request, string causationId) where T : IMetadata - { - MetadataFactory.Set(request, MetadataDictionary.CausationId, causationId); - return request; - } + /// + /// Assigns a new to the . + /// + /// The model that implements the interface. + /// The to extend. + /// The causation identifier of the model. + /// A reference to after the operation has completed. + public static T SetCausationId(this T request, string causationId) where T : IMetadata + { + MetadataFactory.Set(request, MetadataDictionary.CausationId, causationId); + return request; + } - /// - /// Assigns a new to the . - /// - /// The model that implements the interface. - /// The to extend. - /// The correlation identifier of the model. - /// A reference to after the operation has completed. - public static T SetCorrelationId(this T request, string correlationId) where T : IMetadata - { - MetadataFactory.Set(request, MetadataDictionary.CorrelationId, correlationId); - return request; - } + /// + /// Assigns a new to the . + /// + /// The model that implements the interface. + /// The to extend. + /// The correlation identifier of the model. + /// A reference to after the operation has completed. + public static T SetCorrelationId(this T request, string correlationId) where T : IMetadata + { + MetadataFactory.Set(request, MetadataDictionary.CorrelationId, correlationId); + return request; + } - /// - /// Assigns a new to the . - /// - /// The model that implements the interface. - /// The to extend. - /// The request identifier of the model. - /// A reference to after the operation has completed. - public static T SetRequestId(this T request, string requestId) where T : IMetadata - { - MetadataFactory.Set(request, MetadataDictionary.RequestId, requestId); - return request; - } + /// + /// Assigns a new to the . + /// + /// The model that implements the interface. + /// The to extend. + /// The request identifier of the model. + /// A reference to after the operation has completed. + public static T SetRequestId(this T request, string requestId) where T : IMetadata + { + MetadataFactory.Set(request, MetadataDictionary.RequestId, requestId); + return request; + } - /// - /// Assigns a new to the . - /// - /// The model that implements the interface. - /// The to extend. - /// The event identifier of the model. - /// A reference to after the operation has completed. - public static T SetEventId(this T request, string eventId) where T : IMetadata - { - MetadataFactory.Set(request, MetadataDictionary.EventId, eventId); - return request; - } + /// + /// Assigns a new to the . + /// + /// The model that implements the interface. + /// The to extend. + /// The event identifier of the model. + /// A reference to after the operation has completed. + public static T SetEventId(this T request, string eventId) where T : IMetadata + { + MetadataFactory.Set(request, MetadataDictionary.EventId, eventId); + return request; + } - /// - /// Assigns a new timestamp ( value) to the . - /// - /// The model that implements the interface. - /// The to extend. - /// The optional value expressed as the Coordinated Universal Time (UTC). - /// A reference to after the operation has completed. - /// - /// (when set) was not expressed as the Coordinated Universal Time (UTC). - /// - public static T SetTimestamp(this T request, DateTime? utcTimestamp = null) where T : IMetadata - { - Validator.ThrowIfTrue(utcTimestamp.HasValue && utcTimestamp.Value.Kind != DateTimeKind.Utc, nameof(utcTimestamp), "Value must be expressed as the Coordinated Universal Time (UTC)."); - MetadataFactory.Set(request, MetadataDictionary.Timestamp, utcTimestamp ?? DateTime.UtcNow); - return request; - } + /// + /// Assigns a new timestamp ( value) to the . + /// + /// The model that implements the interface. + /// The to extend. + /// The optional value expressed as the Coordinated Universal Time (UTC). + /// A reference to after the operation has completed. + /// + /// (when set) was not expressed as the Coordinated Universal Time (UTC). + /// + public static T SetTimestamp(this T request, DateTime? utcTimestamp = null) where T : IMetadata + { + Validator.ThrowIfTrue(utcTimestamp.HasValue && utcTimestamp.Value.Kind != DateTimeKind.Utc, nameof(utcTimestamp), "Value must be expressed as the Coordinated Universal Time (UTC)."); + MetadataFactory.Set(request, MetadataDictionary.Timestamp, utcTimestamp ?? DateTime.UtcNow); + return request; + } - /// - /// Assigns a new to the . - /// - /// The model that implements the interface. - /// The to extend. - /// The type of the model. - /// A reference to after the operation has completed. - /// The is converted to its equivalent string representation (fully qualified name of the type, including its namespace, comma delimited with the simple name of the assembly). - public static T SetMemberType(this T request, Type type) where T : IMetadata - { - MetadataFactory.Set(request, MetadataDictionary.MemberType, type.ToFullNameIncludingAssemblyName()); - return request; - } + /// + /// Assigns a new to the . + /// + /// The model that implements the interface. + /// The to extend. + /// The type of the model. + /// A reference to after the operation has completed. + /// The is converted to its equivalent string representation (fully qualified name of the type, including its namespace, comma delimited with the simple name of the assembly). + public static T SetMemberType(this T request, Type type) where T : IMetadata + { + MetadataFactory.Set(request, MetadataDictionary.MemberType, type.ToFullNameIncludingAssemblyName()); + return request; + } - /// - /// Add or update a set of model to the . - /// - /// The model that implements the interface. - /// The to extend. - /// The key of the element to add or update. - /// The value of the element to add or update. - /// A reference to after the operation has completed. - /// - /// cannot be null -or- - /// cannot be null. - /// - public static T SaveMetadata(this T request, string key, object value) where T : IMetadata - { - Validator.ThrowIfNull(request); - Validator.ThrowIfNull(key); + /// + /// Add or update a set of model to the . + /// + /// The model that implements the interface. + /// The to extend. + /// The key of the element to add or update. + /// The value of the element to add or update. + /// A reference to after the operation has completed. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static T SaveMetadata(this T request, string key, object value) where T : IMetadata + { + Validator.ThrowIfNull(request); + Validator.ThrowIfNull(key); - var parsedKey = MetadataDictionary.EnsureReservedKeywordCapitalization(key); - MetadataFactory.Set(request, parsedKey, value); - return request; - } + var parsedKey = MetadataDictionary.EnsureReservedKeywordCapitalization(key); + MetadataFactory.Set(request, parsedKey, value); + return request; + } - /// - /// Copies model from the to the if not already existing. - /// - /// The giving type of the model that implements the interface. - /// The receiving type of the model that implements the interface. - /// The to extend. Receives model from . - /// The model that will give metadata to . - /// A reference to after the operation has completed. - public static TDestination MergeMetadata(this TDestination destination, TSource source) - where TSource : IMetadata - where TDestination : IMetadata + /// + /// Copies model from the to the if not already existing. + /// + /// The giving type of the model that implements the interface. + /// The receiving type of the model that implements the interface. + /// The to extend. Receives model from . + /// The model that will give metadata to . + /// A reference to after the operation has completed. + public static TDestination MergeMetadata(this TDestination destination, TSource source) + where TSource : IMetadata + where TDestination : IMetadata + { + if (destination != null && source != null) { - if (destination != null && source != null) + foreach (var entry in source.Metadata) { - foreach (var entry in source.Metadata) + if (!destination.Metadata.ContainsKey(entry.Key)) { - if (!destination.Metadata.ContainsKey(entry.Key)) - { - destination.Metadata.Add(entry.Key, entry.Value); - } + destination.Metadata.Add(entry.Key, entry.Value); } } - return destination; } + return destination; } } diff --git a/src/Savvyio.Core/MetadataFactory.cs b/src/Savvyio.Core/MetadataFactory.cs index 23f6ccda..3476ed5a 100644 --- a/src/Savvyio.Core/MetadataFactory.cs +++ b/src/Savvyio.Core/MetadataFactory.cs @@ -1,55 +1,54 @@ -using System; +using System; using Cuemon; -namespace Savvyio +namespace Savvyio; + +/// +/// Provides access to factory methods for maintaining metadata in models. +/// +public static class MetadataFactory { /// - /// Provides access to factory methods for maintaining metadata in models. + /// Gets the value associates to the from the item with the specified . /// - public static class MetadataFactory + /// The model that implements the interface. + /// The to extend. + /// The key of the element to retrieve. + /// A reference to the value in the that is identified by , if the entry exists; otherwise, null. + public static object Get(T request, string key) where T : IMetadata { - /// - /// Gets the value associates to the from the item with the specified . - /// - /// The model that implements the interface. - /// The to extend. - /// The key of the element to retrieve. - /// A reference to the value in the that is identified by , if the entry exists; otherwise, null. - public static object Get(T request, string key) where T : IMetadata + if (request.Metadata.TryGetValue(key, out var result)) { - if (request.Metadata.TryGetValue(key, out var result)) - { - return result; - } - return null; + return result; } + return null; + } - /// - /// Assigns a new to the on the item with the specified . - /// - /// The model that implements the interface. - /// The to extend. - /// The key of the element to retrieve. - /// The value of the element to change. - /// A reference to after the operation has completed. - /// - /// cannot be null -or- - /// cannot be null. - /// - public static T Set(T request, string key, object value) where T : IMetadata - { - Validator.ThrowIfNull(request); - Validator.ThrowIfNull(key); + /// + /// Assigns a new to the on the item with the specified . + /// + /// The model that implements the interface. + /// The to extend. + /// The key of the element to retrieve. + /// The value of the element to change. + /// A reference to after the operation has completed. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static T Set(T request, string key, object value) where T : IMetadata + { + Validator.ThrowIfNull(request); + Validator.ThrowIfNull(key); - if (request.Metadata.ContainsKey(key)) - { - request.Metadata[key] = value; - } - else - { - request.Metadata.Add(key, value); - } - return request; + if (request.Metadata.ContainsKey(key)) + { + request.Metadata[key] = value; + } + else + { + request.Metadata.Add(key, value); } + return request; } } diff --git a/src/Savvyio.Core/Queries/IQuery.cs b/src/Savvyio.Core/Queries/IQuery.cs index 7821c952..48a156c1 100644 --- a/src/Savvyio.Core/Queries/IQuery.cs +++ b/src/Savvyio.Core/Queries/IQuery.cs @@ -1,17 +1,16 @@ -namespace Savvyio.Queries +namespace Savvyio.Queries; + +/// +/// A marker interface that specifies something that returns data. +/// +public interface IQuery : IRequest, IMetadata { - /// - /// A marker interface that specifies something that returns data. - /// - public interface IQuery : IRequest, IMetadata - { - } +} - /// - /// A marker interface that specifies something that returns data. - /// - /// The type of the result to return. - public interface IQuery : IQuery - { - } +/// +/// A marker interface that specifies something that returns data. +/// +/// The type of the result to return. +public interface IQuery : IQuery +{ } diff --git a/src/Savvyio.Core/Queries/IQueryDispatcher.cs b/src/Savvyio.Core/Queries/IQueryDispatcher.cs index 10c8f097..6fc28a3e 100644 --- a/src/Savvyio.Core/Queries/IQueryDispatcher.cs +++ b/src/Savvyio.Core/Queries/IQueryDispatcher.cs @@ -1,31 +1,30 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; using Savvyio.Dispatchers; -namespace Savvyio.Queries +namespace Savvyio.Queries; + +/// +/// Defines a Query dispatcher that uses Request-Reply/In-Out MEP. +/// +/// +public interface IQueryDispatcher : IDispatcher { /// - /// Defines a Query dispatcher that uses Request-Reply/In-Out MEP. + /// Queries the specified using Request-Reply/In-Out MEP. /// - /// - public interface IQueryDispatcher : IDispatcher - { - /// - /// Queries the specified using Request-Reply/In-Out MEP. - /// - /// The type of the result to return. - /// The to request. - /// The outcome of the query operation. - TResult Query(IQuery request); + /// The type of the result to return. + /// The to request. + /// The outcome of the query operation. + TResult Query(IQuery request); - /// - /// Queries the specified asynchronous using Request-Reply/In-Out MEP. - /// - /// The type of the result to return. - /// The to request. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result contains the outcome of the query operation. - Task QueryAsync(IQuery request, Action setup = null); - } + /// + /// Queries the specified asynchronous using Request-Reply/In-Out MEP. + /// + /// The type of the result to return. + /// The to request. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result contains the outcome of the query operation. + Task QueryAsync(IQuery request, Action setup = null); } diff --git a/src/Savvyio.Core/Queries/IQueryHandler.cs b/src/Savvyio.Core/Queries/IQueryHandler.cs index 4cf5f121..963e438e 100644 --- a/src/Savvyio.Core/Queries/IQueryHandler.cs +++ b/src/Savvyio.Core/Queries/IQueryHandler.cs @@ -1,12 +1,11 @@ -using Savvyio.Handlers; +using Savvyio.Handlers; -namespace Savvyio.Queries +namespace Savvyio.Queries; + +/// +/// Specifies a handler responsible for objects that implements the interface. +/// +/// +public interface IQueryHandler : IRequestReplyHandler { - /// - /// Specifies a handler responsible for objects that implements the interface. - /// - /// - public interface IQueryHandler : IRequestReplyHandler - { - } } diff --git a/src/Savvyio.Core/Reflection/AssemblyContext.cs b/src/Savvyio.Core/Reflection/AssemblyContext.cs deleted file mode 100644 index 4c898375..00000000 --- a/src/Savvyio.Core/Reflection/AssemblyContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Cuemon.Extensions.Collections.Generic; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Cuemon; namespace Savvyio.Reflection { /// /// Provides a set of static methods and properties to manage and filter assemblies in the current application domain. /// public static class AssemblyContext { private static readonly Lazy> AssemblyLoadFactory = new(() => AppDomain .CurrentDomain .GetAssemblies() .Where(AssemblyFilterCallback) .SelectMany(AssemblyDependenciesCallback) .Distinct() .Except(typeof(AssemblyContext).Assembly.Yield()) .ToList() .AsReadOnly()); private static Func _assemblyFilterCallback = DefaultAssemblyFilter; private static Func> _assemblyDependenciesCallback = DefaultAssemblyDependencies; private static Func _assemblyDependenciesFilterCallback = DefaultAssemblyDependenciesFilter; /// /// Gets or sets the function delegate that filters assemblies from the current application domain. /// /// The function delegate that filters assemblies from the current application domain. /// The default implementation filters away assemblies that suggest being part of the .NET runtime themselves. /// /// cannot be null. /// public static Func AssemblyFilterCallback { get => _assemblyFilterCallback; set => _assemblyFilterCallback = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets or sets the function delegate that recursively discover dependencies for an assembly in the current application domain. /// /// The function delegate that recursively discover dependencies for an assembly in the current application domain. /// /// cannot be null. /// public static Func> AssemblyDependenciesCallback { get => _assemblyDependenciesCallback; set => _assemblyDependenciesCallback = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets or sets the function delegate that filters assembly dependencies from the current application domain. /// /// The function delegate that filters assembly dependencies from the current application domain. /// The default implementation filters away assembly dependencies that suggest being part of the .NET runtime themselves. /// /// cannot be null. /// public static Func AssemblyDependenciesFilterCallback { get => _assemblyDependenciesFilterCallback; set => _assemblyDependenciesFilterCallback = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets the qualified assemblies from the current application domain. /// /// The qualified assemblies from the current application domain. /// This result of this property is coupled with and . public static IReadOnlyList CurrentDomainAssemblies => AssemblyLoadFactory.Value; private static bool DefaultAssemblyFilter(Assembly assembly) { return assembly.FullName != null && - !assembly.FullName.StartsWith(nameof(System), StringComparison.Ordinal) && - !assembly.FullName.StartsWith(nameof(Microsoft), StringComparison.Ordinal); - } - - private static bool DefaultAssemblyDependenciesFilter(AssemblyName assemblyName) - { - return !assemblyName.FullName.StartsWith(nameof(System), StringComparison.Ordinal) && - !assemblyName.FullName.StartsWith(nameof(Microsoft), StringComparison.Ordinal); - } private static IEnumerable DefaultAssemblyDependencies(Assembly assembly) { var stack = new Stack(); var guard = new HashSet(); yield return assembly; stack.Push(assembly); guard.Add(assembly.FullName); while (stack.TryPop(out var assemblyToTraverse)) { foreach (var assemblyName in assemblyToTraverse.GetReferencedAssemblies().Where(AssemblyDependenciesFilterCallback)) { if (!guard.Add(assemblyName.FullName)) { continue; } if (Patterns.TryInvoke(() => Assembly.Load(assemblyName), out var referencedAssembly) && referencedAssembly != null) { stack.Push(referencedAssembly); yield return referencedAssembly; } } } } } } \ No newline at end of file diff --git a/src/Savvyio.Core/Request.cs b/src/Savvyio.Core/Request.cs index 05e11227..8d8e5294 100644 --- a/src/Savvyio.Core/Request.cs +++ b/src/Savvyio.Core/Request.cs @@ -1,24 +1,23 @@ -namespace Savvyio +namespace Savvyio; + +/// +/// Represents the base class from which all implementations of should derive. +/// +/// +/// +public abstract record Request : IRequest, IMetadata { /// - /// Represents the base class from which all implementations of should derive. + /// Initializes a new instance of the class. /// - /// - /// - public abstract record Request : IRequest, IMetadata + protected Request() { - /// - /// Initializes a new instance of the class. - /// - protected Request() - { - this.SetMemberType(GetType()); - } - - /// - /// Gets the associated metadata of this model. - /// - /// The associated metadata of this model. - public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); + this.SetMemberType(GetType()); } + + /// + /// Gets the associated metadata of this model. + /// + /// The associated metadata of this model. + public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); } diff --git a/src/Savvyio.Core/Savvyio.Core.csproj b/src/Savvyio.Core/Savvyio.Core.csproj index b69fa2f1..57b8b9f8 100644 --- a/src/Savvyio.Core/Savvyio.Core.csproj +++ b/src/Savvyio.Core/Savvyio.Core.csproj @@ -1,4 +1,4 @@ - + Savvyio diff --git a/src/Savvyio.Core/SavvyioOptions.cs b/src/Savvyio.Core/SavvyioOptions.cs index 35341383..cfee6608 100644 --- a/src/Savvyio.Core/SavvyioOptions.cs +++ b/src/Savvyio.Core/SavvyioOptions.cs @@ -1,203 +1,202 @@ -using System; +using System; using System.Collections.Generic; using Cuemon.Configuration; using Cuemon.Extensions; using Cuemon.Extensions.Collections.Generic; using Savvyio.Dispatchers; -namespace Savvyio +namespace Savvyio; + +/// +/// Specifies options that is related to setting up Savvy I/O services. +/// +public class SavvyioOptions : IParameterObject { + private readonly List _handlerServiceTypes = new(); // the handler service types to tie one or more handler implementation types + private readonly List _handlerImplementationTypes = new(); // the handler implementation types to tie one or more handler service types + + private readonly List _dispatcherServiceTypes = new(); // the dispatchers service types to tie one or more dispatcher implementation types + private readonly List _dispatcherImplementationTypes = new(); // the dispatcher implementation types to tie one or more dispatchers service types + /// - /// Specifies options that is related to setting up Savvy I/O services. + /// Determines if a given pair of types are a valid as a dependency. /// - public class SavvyioOptions : IParameterObject + /// The type of the service to validate. + /// The provider type. + /// The contract type. + /// true if the specified provider is a valid dependency; otherwise, false. + /// Dependency formula: provider.HasInterfaces(contract) && provider.IsClass && !provider.IsAbstract && !provider.IsInterface + public static bool IsValid(Type provider, Type contract) { - private readonly List _handlerServiceTypes = new(); // the handler service types to tie one or more handler implementation types - private readonly List _handlerImplementationTypes = new(); // the handler implementation types to tie one or more handler service types - - private readonly List _dispatcherServiceTypes = new(); // the dispatchers service types to tie one or more dispatcher implementation types - private readonly List _dispatcherImplementationTypes = new(); // the dispatcher implementation types to tie one or more dispatchers service types - - /// - /// Determines if a given pair of types are a valid as a dependency. - /// - /// The type of the service to validate. - /// The provider type. - /// The contract type. - /// true if the specified provider is a valid dependency; otherwise, false. - /// Dependency formula: provider.HasInterfaces(contract) && provider.IsClass && !provider.IsAbstract && !provider.IsInterface - public static bool IsValid(Type provider, Type contract) + if (provider.HasInterfaces(typeof(T))) { - if (provider.HasInterfaces(typeof(T))) - { - return provider.HasInterfaces(contract) && provider.IsClass && !provider.IsAbstract && !provider.IsInterface; - } - return false; + return provider.HasInterfaces(contract) && provider.IsClass && !provider.IsAbstract && !provider.IsInterface; } + return false; + } - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// false - /// - /// - /// - /// false - /// - /// - /// - /// false - /// - /// - /// - public SavvyioOptions() - { - } + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// false + /// + /// + /// + /// false + /// + /// + /// + /// false + /// + /// + /// + public SavvyioOptions() + { + } - /// - /// Gets a value indicating whether discovery of implementations using assembly scanning is enabled. - /// - /// true discovery of implementations using assembly scanning is enabled; otherwise, false. - public bool AllowHandlerDiscovery { get; private set; } - - /// - /// Gets a value indicating whether discovery of implementations using assembly scanning is enabled. - /// - /// true discovery of implementations using assembly scanning is enabled; otherwise, false. - public bool AllowDispatcherDiscovery { get; private set; } - - /// - /// Gets a value indicating whether the is enabled. - /// - /// true if the is enabled; otherwise, false. - public bool IncludeHandlerServicesDescriptor { get; private set; } - - /// - /// Enables discovery of handlers implementing the interface using assembly scanning. - /// - /// true if handlers implementing the interface are allowed to be discovered using assembly scanning; otherwise, false. - /// A reference to this instance so that additional configuration calls can be chained. - public SavvyioOptions EnableHandlerDiscovery(bool allowHandlerDiscovery = true) - { - AllowHandlerDiscovery = allowHandlerDiscovery; - return this; - } + /// + /// Gets a value indicating whether discovery of implementations using assembly scanning is enabled. + /// + /// true discovery of implementations using assembly scanning is enabled; otherwise, false. + public bool AllowHandlerDiscovery { get; private set; } - /// - /// Enables discovery of dispatchers implementing the interface using assembly scanning. - /// - /// true if dispatchers implementing the interface are allowed to be discovered using assembly scanning; otherwise, false. - /// A reference to this instance so that additional configuration calls can be chained. - public SavvyioOptions EnableDispatcherDiscovery(bool allowDispatcherDiscovery = true) - { - AllowDispatcherDiscovery = allowDispatcherDiscovery; - return this; - } + /// + /// Gets a value indicating whether discovery of implementations using assembly scanning is enabled. + /// + /// true discovery of implementations using assembly scanning is enabled; otherwise, false. + public bool AllowDispatcherDiscovery { get; private set; } - /// - /// Enables the inclusion of the that provides an overview of all handlers presented in a detailed and developer friendly way. - /// - /// true enables the inclusion of an implemenation that provides an overview of all handlers presented in a detailed and developer friendly way; otherwise, false. - /// A reference to this instance so that additional configuration calls can be chained. - public SavvyioOptions EnableHandlerServicesDescriptor(bool includeHandlerServicesDescriptor = true) - { - IncludeHandlerServicesDescriptor = includeHandlerServicesDescriptor; - return this; - } + /// + /// Gets a value indicating whether the is enabled. + /// + /// true if the is enabled; otherwise, false. + public bool IncludeHandlerServicesDescriptor { get; private set; } - /// - /// Adds a dispatcher of type to (if not already registered) and (if not already registered). - /// - /// The type of the interface to add. - /// The type of the implementation to use. - /// A reference to this instance so that additional configuration calls can be chained. - public SavvyioOptions AddDispatcher() - where TDispatcher : IDispatcher - where TImplementation : class, TDispatcher - { - AddDispatcher(typeof(TDispatcher), typeof(TImplementation)); - return this; - } + /// + /// Enables discovery of handlers implementing the interface using assembly scanning. + /// + /// true if handlers implementing the interface are allowed to be discovered using assembly scanning; otherwise, false. + /// A reference to this instance so that additional configuration calls can be chained. + public SavvyioOptions EnableHandlerDiscovery(bool allowHandlerDiscovery = true) + { + AllowHandlerDiscovery = allowHandlerDiscovery; + return this; + } - /// - /// Adds a dispatcher of type to (if not already registered) and (if not already registered). - /// - /// The of the interface to add. - /// The type of the implementation to use. - /// A reference to this instance so that additional configuration calls can be chained. - public SavvyioOptions AddDispatcher(Type service, Type implementation) - { - if (IsValid(implementation, service)) - { - _dispatcherServiceTypes.TryAdd(service); - _dispatcherImplementationTypes.TryAdd(implementation); - } - return this; - } + /// + /// Enables discovery of dispatchers implementing the interface using assembly scanning. + /// + /// true if dispatchers implementing the interface are allowed to be discovered using assembly scanning; otherwise, false. + /// A reference to this instance so that additional configuration calls can be chained. + public SavvyioOptions EnableDispatcherDiscovery(bool allowDispatcherDiscovery = true) + { + AllowDispatcherDiscovery = allowDispatcherDiscovery; + return this; + } - /// - /// Adds a handler of type to (if not already registered) and (if not already registered). - /// - /// The type of the service to add. - /// The type of the model to handle. - /// The type of the implementation to use. - /// A reference to this instance so that additional configuration calls can be chained. - /// Type is only added if the following conditions are true: typeof(THandler).HasInterfaces(typeof(IHandler<TRequest>)) && typeof(T).IsClass && !typeof(T).IsAbstract && !typeof(T).IsInterface - public SavvyioOptions AddHandler() - where THandler : IHandler - where TRequest : IRequest - where TImplementation : class, THandler + /// + /// Enables the inclusion of the that provides an overview of all handlers presented in a detailed and developer friendly way. + /// + /// true enables the inclusion of an implemenation that provides an overview of all handlers presented in a detailed and developer friendly way; otherwise, false. + /// A reference to this instance so that additional configuration calls can be chained. + public SavvyioOptions EnableHandlerServicesDescriptor(bool includeHandlerServicesDescriptor = true) + { + IncludeHandlerServicesDescriptor = includeHandlerServicesDescriptor; + return this; + } + + /// + /// Adds a dispatcher of type to (if not already registered) and (if not already registered). + /// + /// The type of the interface to add. + /// The type of the implementation to use. + /// A reference to this instance so that additional configuration calls can be chained. + public SavvyioOptions AddDispatcher() + where TDispatcher : IDispatcher + where TImplementation : class, TDispatcher + { + AddDispatcher(typeof(TDispatcher), typeof(TImplementation)); + return this; + } + + /// + /// Adds a dispatcher of type to (if not already registered) and (if not already registered). + /// + /// The of the interface to add. + /// The type of the implementation to use. + /// A reference to this instance so that additional configuration calls can be chained. + public SavvyioOptions AddDispatcher(Type service, Type implementation) + { + if (IsValid(implementation, service)) { - return AddHandler(typeof(THandler), typeof(TImplementation)); + _dispatcherServiceTypes.TryAdd(service); + _dispatcherImplementationTypes.TryAdd(implementation); } + return this; + } + + /// + /// Adds a handler of type to (if not already registered) and (if not already registered). + /// + /// The type of the service to add. + /// The type of the model to handle. + /// The type of the implementation to use. + /// A reference to this instance so that additional configuration calls can be chained. + /// Type is only added if the following conditions are true: typeof(THandler).HasInterfaces(typeof(IHandler<TRequest>)) && typeof(T).IsClass && !typeof(T).IsAbstract && !typeof(T).IsInterface + public SavvyioOptions AddHandler() + where THandler : IHandler + where TRequest : IRequest + where TImplementation : class, THandler + { + return AddHandler(typeof(THandler), typeof(TImplementation)); + } - /// - /// Adds a dispatcher of type to (if not already registered) and (if not already registered). - /// - /// The of the interface to add. - /// The type of the implementation to use. - /// A reference to this instance so that additional configuration calls can be chained. - public SavvyioOptions AddHandler(Type service, Type implementation) + /// + /// Adds a dispatcher of type to (if not already registered) and (if not already registered). + /// + /// The of the interface to add. + /// The type of the implementation to use. + /// A reference to this instance so that additional configuration calls can be chained. + public SavvyioOptions AddHandler(Type service, Type implementation) + { + if (IsValid(implementation, service)) { - if (IsValid(implementation, service)) - { - _handlerServiceTypes.TryAdd(service); - _handlerImplementationTypes.TryAdd(implementation); - } - return this; + _handlerServiceTypes.TryAdd(service); + _handlerImplementationTypes.TryAdd(implementation); } - - /// - /// Gets the types associated with an service. - /// - /// The types associated with an service. - public IReadOnlyCollection HandlerServiceTypes => _handlerServiceTypes; - - /// - /// Gets the types implementing an service. - /// - /// The types implementing an service. - public IReadOnlyCollection HandlerImplementationTypes => _handlerImplementationTypes; - - /// - /// Gets the types associated with an service. - /// - /// The types dispatcher with an service. - public IReadOnlyCollection DispatcherServiceTypes => _dispatcherServiceTypes; - - /// - /// Gets the types implementing an service. - /// - /// The types implementing an service. - public IReadOnlyCollection DispatcherImplementationTypes => _dispatcherImplementationTypes; + return this; } + + /// + /// Gets the types associated with an service. + /// + /// The types associated with an service. + public IReadOnlyCollection HandlerServiceTypes => _handlerServiceTypes; + + /// + /// Gets the types implementing an service. + /// + /// The types implementing an service. + public IReadOnlyCollection HandlerImplementationTypes => _handlerImplementationTypes; + + /// + /// Gets the types associated with an service. + /// + /// The types dispatcher with an service. + public IReadOnlyCollection DispatcherServiceTypes => _dispatcherServiceTypes; + + /// + /// Gets the types implementing an service. + /// + /// The types implementing an service. + public IReadOnlyCollection DispatcherImplementationTypes => _dispatcherImplementationTypes; } diff --git a/src/Savvyio.Core/SavvyioOptionsExtensions.cs b/src/Savvyio.Core/SavvyioOptionsExtensions.cs index 2e47a3e3..efd9dbfc 100644 --- a/src/Savvyio.Core/SavvyioOptionsExtensions.cs +++ b/src/Savvyio.Core/SavvyioOptionsExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -6,71 +6,70 @@ using Cuemon.Extensions; using Savvyio.Dispatchers; -namespace Savvyio +namespace Savvyio; + +/// +/// Extension methods for the class. +/// +public static class SavvyioOptionsExtensions { /// - /// Extension methods for the class. + /// Adds handlers of type from the specified to the extended . /// - public static class SavvyioOptionsExtensions + /// The to extend. + /// The assemblies to scan for implementations. + /// A reference to so that additional configuration calls can be chained. + /// + /// cannot be null. + /// + public static SavvyioOptions AddDispatchers(this SavvyioOptions options, params Assembly[] assemblies) { - /// - /// Adds handlers of type from the specified to the extended . - /// - /// The to extend. - /// The assemblies to scan for implementations. - /// A reference to so that additional configuration calls can be chained. - /// - /// cannot be null. - /// - public static SavvyioOptions AddDispatchers(this SavvyioOptions options, params Assembly[] assemblies) - { - return AddDependenciesCore(options, (service, implementation) => options.AddDispatcher(service, implementation), assemblies); - } + return AddDependenciesCore(options, (service, implementation) => options.AddDispatcher(service, implementation), assemblies); + } - /// - /// Adds handlers of type from the specified to the extended . - /// - /// The to extend. - /// The assemblies to scan for implementations. - /// A reference to so that additional configuration calls can be chained. - /// - /// cannot be null. - /// - public static SavvyioOptions AddHandlers(this SavvyioOptions options, params Assembly[] assemblies) - { - return AddDependenciesCore(options, (service, implementation) => options.AddHandler(service, implementation), assemblies); - } + /// + /// Adds handlers of type from the specified to the extended . + /// + /// The to extend. + /// The assemblies to scan for implementations. + /// A reference to so that additional configuration calls can be chained. + /// + /// cannot be null. + /// + public static SavvyioOptions AddHandlers(this SavvyioOptions options, params Assembly[] assemblies) + { + return AddDependenciesCore(options, (service, implementation) => options.AddHandler(service, implementation), assemblies); + } - private static SavvyioOptions AddDependenciesCore(SavvyioOptions options, Action servicesManager, IEnumerable assemblies) - { - Validator.ThrowIfNull(assemblies); + private static SavvyioOptions AddDependenciesCore(SavvyioOptions options, Action servicesManager, IEnumerable assemblies) + { + Validator.ThrowIfNull(assemblies); - var self = typeof(SavvyioOptions).Assembly; - var definedTypes = new List(); - var filteredTypes = self.DefinedTypes.Where(type => type.HasInterfaces(typeof(T)) && !type.Namespace!.ContainsAny(nameof(Commands), nameof(Domain), nameof(EventDriven), nameof(Queries))).ToList(); + var self = typeof(SavvyioOptions).Assembly; + var definedTypes = new List(); + var filteredTypes = self.DefinedTypes.Where(type => type.HasInterfaces(typeof(T)) && !type.Namespace!.ContainsAny(nameof(Commands), nameof(Domain), nameof(EventDriven), nameof(Queries))).ToList(); - foreach (var assembly in assemblies.Append(self)) + foreach (var assembly in assemblies.Append(self)) + { + try { - try - { - definedTypes.AddRange(assembly.DefinedTypes.Where(type => type.HasInterfaces(typeof(T))).Except(filteredTypes)); - } - catch (ReflectionTypeLoadException) - { - // by design; ignore potential orphaned dll references causing ReflectionTypeLoadException - } + definedTypes.AddRange(assembly.DefinedTypes.Where(type => type.HasInterfaces(typeof(T))).Except(filteredTypes)); } + catch (ReflectionTypeLoadException) + { + // by design; ignore potential orphaned dll references causing ReflectionTypeLoadException + } + } - foreach (var contract in definedTypes.Where(type => type.IsInterface).OrderBy(ti => ti.FullName)) + foreach (var contract in definedTypes.Where(type => type.IsInterface).OrderBy(ti => ti.FullName)) + { + var filtered = definedTypes.Where(type => SavvyioOptions.IsValid(type, contract)).ToList(); + if (filtered.Count == 0) { continue; } + foreach (var filteredType in filtered) { - var filtered = definedTypes.Where(type => SavvyioOptions.IsValid(type, contract)).ToList(); - if (filtered.Count == 0) { continue; } - foreach (var filteredType in filtered) - { - servicesManager(contract, filteredType); - } + servicesManager(contract, filteredType); } - return options; } + return options; } } diff --git a/src/Savvyio.Core/TaskExtensions.cs b/src/Savvyio.Core/TaskExtensions.cs index f14b7e0d..c6b1199a 100644 --- a/src/Savvyio.Core/TaskExtensions.cs +++ b/src/Savvyio.Core/TaskExtensions.cs @@ -1,24 +1,23 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Savvyio +namespace Savvyio; + +/// +/// Extension methods for the class. +/// +public static class TaskExtensions { /// - /// Extension methods for the class. + /// Asynchronously returns the only element of a sequence, or a default value if the sequence is empty; this method throws an exception if there is more than one element in the sequence. /// - public static class TaskExtensions + /// The type of the elements of . + /// The to return the single element of. + /// A task that represents the asynchronous operation. The task result contains the single element of the input sequence, or default (TSource) if the sequence contains no elements. + public static async Task SingleOrDefaultAsync(this Task> source) { - /// - /// Asynchronously returns the only element of a sequence, or a default value if the sequence is empty; this method throws an exception if there is more than one element in the sequence. - /// - /// The type of the elements of . - /// The to return the single element of. - /// A task that represents the asynchronous operation. The task result contains the single element of the input sequence, or default (TSource) if the sequence contains no elements. - public static async Task SingleOrDefaultAsync(this Task> source) - { - var presult = await source.ConfigureAwait(false); - return presult.SingleOrDefault(); - } + var presult = await source.ConfigureAwait(false); + return presult.SingleOrDefault(); } } diff --git a/src/Savvyio.Domain.EventSourcing/Savvyio.Domain.EventSourcing.csproj b/src/Savvyio.Domain.EventSourcing/Savvyio.Domain.EventSourcing.csproj index a0aee09e..26536ea6 100644 --- a/src/Savvyio.Domain.EventSourcing/Savvyio.Domain.EventSourcing.csproj +++ b/src/Savvyio.Domain.EventSourcing/Savvyio.Domain.EventSourcing.csproj @@ -1,4 +1,4 @@ - + Provides the default implementation of an Aggregate Root that is Event Sourced enabled. Complements the concepts of DDD (Domain Driven Design). diff --git a/src/Savvyio.Domain.EventSourcing/TracedAggregateRoot.cs b/src/Savvyio.Domain.EventSourcing/TracedAggregateRoot.cs index 4475f1e8..2a4c21d7 100644 --- a/src/Savvyio.Domain.EventSourcing/TracedAggregateRoot.cs +++ b/src/Savvyio.Domain.EventSourcing/TracedAggregateRoot.cs @@ -1,93 +1,92 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Savvyio.Handlers; -namespace Savvyio.Domain.EventSourcing +namespace Savvyio.Domain.EventSourcing; + +/// +/// Provides a way to cover the pattern of an Aggregate as specified in Domain Driven Design that is optimized for Event Sourcing. This is an abstract class. +/// +/// The type of the key that uniquely identifies this aggregate. +/// +/// +public abstract class TracedAggregateRoot : Aggregate, ITracedAggregateRoot { + private IFireForgetActivator _handlers; + /// - /// Provides a way to cover the pattern of an Aggregate as specified in Domain Driven Design that is optimized for Event Sourcing. This is an abstract class. + /// Initializes a new instance of the class. /// - /// The type of the key that uniquely identifies this aggregate. - /// - /// - public abstract class TracedAggregateRoot : Aggregate, ITracedAggregateRoot + /// The optional metadata to merge with this instance. + protected TracedAggregateRoot(IMetadata metadata = null) : base(metadata) { - private IFireForgetActivator _handlers; - - /// - /// Initializes a new instance of the class. - /// - /// The optional metadata to merge with this instance. - protected TracedAggregateRoot(IMetadata metadata = null) : base(metadata) - { - Initialize(); - } + Initialize(); + } - /// - /// Initializes a new instance of the class. - /// - /// The identifier of the entity. - /// The events to rehydrate from. - protected TracedAggregateRoot(TKey id, IEnumerable events) : base(id) - { - Initialize(); - ReplayEvents(events); - } + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the entity. + /// The events to rehydrate from. + protected TracedAggregateRoot(TKey id, IEnumerable events) : base(id) + { + Initialize(); + ReplayEvents(events); + } - private void Initialize() - { - _handlers = HandlerFactory.CreateFireForget(RegisterDelegates); - } + private void Initialize() + { + _handlers = HandlerFactory.CreateFireForget(RegisterDelegates); + } - /// - /// Registers the delegates responsible for handling types that implements the interface. - /// - /// The registry that store the delegates of type . - protected abstract void RegisterDelegates(IFireForgetRegistry handler); + /// + /// Registers the delegates responsible for handling types that implements the interface. + /// + /// The registry that store the delegates of type . + protected abstract void RegisterDelegates(IFireForgetRegistry handler); - /// - /// Gets the version of the Aggregate. - /// - /// The version of the Aggregate. - public long Version { get; private set; } + /// + /// Gets the version of the Aggregate. + /// + /// The version of the Aggregate. + public long Version { get; private set; } - private void ReplayEvents(IEnumerable events) + private void ReplayEvents(IEnumerable events) + { + if (events == null) { return; } + foreach (var e in events) { - if (events == null) { return; } - foreach (var e in events) - { - Version = e.GetAggregateVersion(); - ApplyChange(e, false); - } + Version = e.GetAggregateVersion(); + ApplyChange(e, false); } + } - /// - /// Adds an event to the Aggregate. - /// - /// The event to be added to the end of . - protected sealed override void AddEvent(ITracedDomainEvent e) - { - ApplyChange(e); - } + /// + /// Adds an event to the Aggregate. + /// + /// The event to be added to the end of . + protected sealed override void AddEvent(ITracedDomainEvent e) + { + ApplyChange(e); + } - private void ApplyChange(ITracedDomainEvent e, bool isNew = true) + private void ApplyChange(ITracedDomainEvent e, bool isNew = true) + { + if (e == null) { return; } + if (isNew) { - if (e == null) { return; } - if (isNew) - { - RaiseEvent(e); - e.SetAggregateVersion(Version + 1); - Version = e.GetAggregateVersion(); - base.AddEvent(e); - } - else - { - RaiseEvent(e); - } + RaiseEvent(e); + e.SetAggregateVersion(Version + 1); + Version = e.GetAggregateVersion(); + base.AddEvent(e); } - - private void RaiseEvent(ITracedDomainEvent @event) + else { - _handlers.TryInvoke(@event); + RaiseEvent(e); } } + + private void RaiseEvent(ITracedDomainEvent @event) + { + _handlers.TryInvoke(@event); + } } diff --git a/src/Savvyio.Domain.EventSourcing/TracedDomainEvent.cs b/src/Savvyio.Domain.EventSourcing/TracedDomainEvent.cs index f2709b86..365bbfbb 100644 --- a/src/Savvyio.Domain.EventSourcing/TracedDomainEvent.cs +++ b/src/Savvyio.Domain.EventSourcing/TracedDomainEvent.cs @@ -1,36 +1,35 @@ -namespace Savvyio.Domain.EventSourcing +namespace Savvyio.Domain.EventSourcing; + +/// +/// Provides a default implementation of something that happened in the domain that you want other parts of the same domain (in-process/inner-application) to be aware of. +/// +/// +/// +public abstract record TracedDomainEvent : DomainEvent, ITracedDomainEvent { /// - /// Provides a default implementation of something that happened in the domain that you want other parts of the same domain (in-process/inner-application) to be aware of. + /// Initializes a new instance of the class. /// - /// - /// - public abstract record TracedDomainEvent : DomainEvent, ITracedDomainEvent + /// The optional identifier of the event. Default is an auto-generated UUID. + /// The optional metadata to merge with this instance. + /// + /// The following table shows the initial metadata values for an instance of . + /// + /// + /// Key + /// Initial Value + /// + /// + /// + /// eventId ?? Guid.NewGuid().ToString("N") + /// + /// + /// + /// DateTime.UtcNow + /// + /// + /// + protected TracedDomainEvent(string eventId = null, IMetadata metadata = null) : base(eventId, metadata) { - /// - /// Initializes a new instance of the class. - /// - /// The optional identifier of the event. Default is an auto-generated UUID. - /// The optional metadata to merge with this instance. - /// - /// The following table shows the initial metadata values for an instance of . - /// - /// - /// Key - /// Initial Value - /// - /// - /// - /// eventId ?? Guid.NewGuid().ToString("N") - /// - /// - /// - /// DateTime.UtcNow - /// - /// - /// - protected TracedDomainEvent(string eventId = null, IMetadata metadata = null) : base(eventId, metadata) - { - } } } diff --git a/src/Savvyio.Domain.EventSourcing/TracedDomainEventExtensions.cs b/src/Savvyio.Domain.EventSourcing/TracedDomainEventExtensions.cs index 39288358..1d05507b 100644 --- a/src/Savvyio.Domain.EventSourcing/TracedDomainEventExtensions.cs +++ b/src/Savvyio.Domain.EventSourcing/TracedDomainEventExtensions.cs @@ -1,44 +1,43 @@ -using Cuemon.Extensions; +using Cuemon.Extensions; -namespace Savvyio.Domain.EventSourcing +namespace Savvyio.Domain.EventSourcing; + +/// +/// Extension methods for the interface. +/// +public static class TracedDomainEventExtensions { /// - /// Extension methods for the interface. + /// Assigns a new to the . /// - public static class TracedDomainEventExtensions + /// The model that implements the interface. + /// The to extend. + /// The aggregate version of the model. + /// A reference to after the operation has completed. + public static T SetAggregateVersion(this T request, long version) where T : ITracedDomainEvent { - /// - /// Assigns a new to the . - /// - /// The model that implements the interface. - /// The to extend. - /// The aggregate version of the model. - /// A reference to after the operation has completed. - public static T SetAggregateVersion(this T request, long version) where T : ITracedDomainEvent - { - return MetadataFactory.Set(request, MetadataDictionary.AggregateVersion, version); - } + return MetadataFactory.Set(request, MetadataDictionary.AggregateVersion, version); + } - /// - /// Gets the aggregate version from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The version of the associated . - public static long GetAggregateVersion(this T request) where T : ITracedDomainEvent - { - return MetadataFactory.Get(request, MetadataDictionary.AggregateVersion).As(); - } + /// + /// Gets the aggregate version from the . + /// + /// The model that implements the interface. + /// The to extend. + /// The version of the associated . + public static long GetAggregateVersion(this T request) where T : ITracedDomainEvent + { + return MetadataFactory.Get(request, MetadataDictionary.AggregateVersion).As(); + } - /// - /// Gets the string representation of the type from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The string representation of the type from the . - public static string GetMemberType(this T request) where T : ITracedDomainEvent - { - return MetadataFactory.Get(request, MetadataDictionary.MemberType).As(); - } + /// + /// Gets the string representation of the type from the . + /// + /// The model that implements the interface. + /// The to extend. + /// The string representation of the type from the . + public static string GetMemberType(this T request) where T : ITracedDomainEvent + { + return MetadataFactory.Get(request, MetadataDictionary.MemberType).As(); } } diff --git a/src/Savvyio.Domain/Aggregate.cs b/src/Savvyio.Domain/Aggregate.cs index d7e74ee6..15471dd0 100644 --- a/src/Savvyio.Domain/Aggregate.cs +++ b/src/Savvyio.Domain/Aggregate.cs @@ -1,61 +1,60 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Represents the base class from which all implementations of an Aggregate Root (as specified in Domain Driven Design) should derive. +/// +/// The type of the key that uniquely identifies this aggregate. +/// The type of the event that implements the interface. +/// +public abstract class Aggregate : Entity, IMetadata { + private readonly List _events = new(); + + /// + /// Initializes a new instance of the class. + /// + /// The optional metadata to merge with this instance. + protected Aggregate(IMetadata metadata = null) + { + this.MergeMetadata(metadata); + } + + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the entity. + protected Aggregate(TKey id) : base(id) + { + } + + /// + /// Gets the events that was added to the Aggregate. + /// + /// The events added to the Aggregate. + public IReadOnlyList Events => _events; + + /// + /// Gets the associated metadata of this Aggregate. + /// + /// The associated metadata of this Aggregate. + public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); + + /// + /// Adds an event to the Aggregate. + /// + /// The event to be added to the end of . + protected virtual void AddEvent(TEvent e) + { + _events.Add(e); + } + /// - /// Represents the base class from which all implementations of an Aggregate Root (as specified in Domain Driven Design) should derive. + /// Removes all events from the Aggregate. /// - /// The type of the key that uniquely identifies this aggregate. - /// The type of the event that implements the interface. - /// - public abstract class Aggregate : Entity, IMetadata + public void RemoveAllEvents() { - private readonly List _events = new(); - - /// - /// Initializes a new instance of the class. - /// - /// The optional metadata to merge with this instance. - protected Aggregate(IMetadata metadata = null) - { - this.MergeMetadata(metadata); - } - - /// - /// Initializes a new instance of the class. - /// - /// The identifier of the entity. - protected Aggregate(TKey id) : base(id) - { - } - - /// - /// Gets the events that was added to the Aggregate. - /// - /// The events added to the Aggregate. - public IReadOnlyList Events => _events; - - /// - /// Gets the associated metadata of this Aggregate. - /// - /// The associated metadata of this Aggregate. - public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); - - /// - /// Adds an event to the Aggregate. - /// - /// The event to be added to the end of . - protected virtual void AddEvent(TEvent e) - { - _events.Add(e); - } - - /// - /// Removes all events from the Aggregate. - /// - public void RemoveAllEvents() - { - _events.Clear(); - } + _events.Clear(); } } diff --git a/src/Savvyio.Domain/AggregateRoot.cs b/src/Savvyio.Domain/AggregateRoot.cs index 38ea0d99..bc368d3c 100644 --- a/src/Savvyio.Domain/AggregateRoot.cs +++ b/src/Savvyio.Domain/AggregateRoot.cs @@ -1,26 +1,25 @@ -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Provides a way to cover the pattern of an Aggregate Root as specified in Domain Driven Design. This is an abstract class. +/// +/// The type of the key that uniquely identifies this aggregate. +/// +public abstract class AggregateRoot : Aggregate, IAggregateRoot { /// - /// Provides a way to cover the pattern of an Aggregate Root as specified in Domain Driven Design. This is an abstract class. + /// Initializes a new instance of the class. /// - /// The type of the key that uniquely identifies this aggregate. - /// - public abstract class AggregateRoot : Aggregate, IAggregateRoot + /// The optional metadata to merge with this instance. + protected AggregateRoot(IMetadata metadata = null) : base(metadata) { - /// - /// Initializes a new instance of the class. - /// - /// The optional metadata to merge with this instance. - protected AggregateRoot(IMetadata metadata = null) : base(metadata) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The identifier of the entity. - protected AggregateRoot(TKey id) : base(id) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the entity. + protected AggregateRoot(TKey id) : base(id) + { } } diff --git a/src/Savvyio.Domain/DomainEvent.cs b/src/Savvyio.Domain/DomainEvent.cs index d3858c44..5de48e8d 100644 --- a/src/Savvyio.Domain/DomainEvent.cs +++ b/src/Savvyio.Domain/DomainEvent.cs @@ -1,41 +1,40 @@ -using System; +using System; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Provides a default implementation of the interface. +/// +/// +/// +public abstract record DomainEvent : Request, IDomainEvent { /// - /// Provides a default implementation of the interface. + /// Initializes a new instance of the class. /// - /// - /// - public abstract record DomainEvent : Request, IDomainEvent + /// The optional identifier of the event. Default is an auto-generated UUID. + /// The optional metadata to merge with this instance. + /// + /// The following table shows the initial metadata values for an instance of . + /// + /// + /// Key + /// Initial Value + /// + /// + /// + /// eventId ?? Guid.NewGuid().ToString("N") + /// + /// + /// + /// DateTime.UtcNow + /// + /// + /// + protected DomainEvent(string eventId = null, IMetadata metadata = null) { - /// - /// Initializes a new instance of the class. - /// - /// The optional identifier of the event. Default is an auto-generated UUID. - /// The optional metadata to merge with this instance. - /// - /// The following table shows the initial metadata values for an instance of . - /// - /// - /// Key - /// Initial Value - /// - /// - /// - /// eventId ?? Guid.NewGuid().ToString("N") - /// - /// - /// - /// DateTime.UtcNow - /// - /// - /// - protected DomainEvent(string eventId = null, IMetadata metadata = null) - { - this.SetEventId(eventId ?? Guid.NewGuid().ToString("N")); - this.SetTimestamp(); - this.MergeMetadata(metadata); - } + this.SetEventId(eventId ?? Guid.NewGuid().ToString("N")); + this.SetTimestamp(); + this.MergeMetadata(metadata); } } diff --git a/src/Savvyio.Domain/DomainEventDispatcher.cs b/src/Savvyio.Domain/DomainEventDispatcher.cs index a57e9e7c..162c8eda 100644 --- a/src/Savvyio.Domain/DomainEventDispatcher.cs +++ b/src/Savvyio.Domain/DomainEventDispatcher.cs @@ -1,46 +1,45 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon; using Cuemon.Threading; using Savvyio.Dispatchers; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Provides a default implementation of of the interface. +/// +/// +/// +public class DomainEventDispatcher : FireForgetDispatcher, IDomainEventDispatcher { /// - /// Provides a default implementation of of the interface. + /// Initializes a new instance of the class. /// - /// - /// - public class DomainEventDispatcher : FireForgetDispatcher, IDomainEventDispatcher + /// The provider of service implementations. + public DomainEventDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) { - /// - /// Initializes a new instance of the class. - /// - /// The provider of service implementations. - public DomainEventDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) - { - } + } - /// - /// Raises the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to raise. - public void Raise(IDomainEvent request) - { - Validator.ThrowIfNull(request); - Dispatch(request, handler => handler.Delegates); - } + /// + /// Raises the specified using Fire-and-Forget/In-Only MEP. + /// + /// The to raise. + public void Raise(IDomainEvent request) + { + Validator.ThrowIfNull(request); + Dispatch(request, handler => handler.Delegates); + } - /// - /// Raises the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to raise. - /// The which may be configured. - /// A that represents the asynchronous operation. - public Task RaiseAsync(IDomainEvent request, Action setup = null) - { - Validator.ThrowIfNull(request); - return DispatchAsync(request, handler => handler.Delegates, setup); - } + /// + /// Raises the specified using Fire-and-Forget/In-Only MEP. + /// + /// The to raise. + /// The which may be configured. + /// A that represents the asynchronous operation. + public Task RaiseAsync(IDomainEvent request, Action setup = null) + { + Validator.ThrowIfNull(request); + return DispatchAsync(request, handler => handler.Delegates, setup); } } diff --git a/src/Savvyio.Domain/DomainEventDispatcherExtensions.cs b/src/Savvyio.Domain/DomainEventDispatcherExtensions.cs index baf43454..2cfcc705 100644 --- a/src/Savvyio.Domain/DomainEventDispatcherExtensions.cs +++ b/src/Savvyio.Domain/DomainEventDispatcherExtensions.cs @@ -1,48 +1,47 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using Cuemon.Threading; using Savvyio.Domain.EventSourcing; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Extension methods for the interface. +/// +public static class DomainEventDispatcherExtensions { /// - /// Extension methods for the interface. + /// Raises domain events from the specified to handlers that implements the interface. /// - public static class DomainEventDispatcherExtensions + /// The type that implements the interface. + /// The to extend. + /// The aggregate to publish domain events from. + public static void RaiseMany(this IDomainEventDispatcher dispatcher, IAggregateRoot aggregate) where T : IDomainEvent { - /// - /// Raises domain events from the specified to handlers that implements the interface. - /// - /// The type that implements the interface. - /// The to extend. - /// The aggregate to publish domain events from. - public static void RaiseMany(this IDomainEventDispatcher dispatcher, IAggregateRoot aggregate) where T : IDomainEvent + var events = aggregate.Events.ToList(); + if (!typeof(ITracedDomainEvent).IsAssignableFrom(typeof(T))) { aggregate.RemoveAllEvents(); } + foreach (var @event in events) { - var events = aggregate.Events.ToList(); - if (!typeof(ITracedDomainEvent).IsAssignableFrom(typeof(T))) { aggregate.RemoveAllEvents(); } - foreach (var @event in events) - { - dispatcher.Raise(@event.MergeMetadata(aggregate)); - } + dispatcher.Raise(@event.MergeMetadata(aggregate)); } + } - /// - /// Asynchronously raises domain events from the specified to handlers that implements the interface. - /// - /// The type that implements the interface. - /// The to extend. - /// The aggregate to publish domain events from. - /// The which may be configured. - /// A that represents the asynchronous operation. - public static async Task RaiseManyAsync(this IDomainEventDispatcher dispatcher, IAggregateRoot aggregate, Action setup = null) where T : IDomainEvent + /// + /// Asynchronously raises domain events from the specified to handlers that implements the interface. + /// + /// The type that implements the interface. + /// The to extend. + /// The aggregate to publish domain events from. + /// The which may be configured. + /// A that represents the asynchronous operation. + public static async Task RaiseManyAsync(this IDomainEventDispatcher dispatcher, IAggregateRoot aggregate, Action setup = null) where T : IDomainEvent + { + var events = aggregate.Events.ToList(); + if (!typeof(ITracedDomainEvent).IsAssignableFrom(typeof(T))) { aggregate.RemoveAllEvents(); } + foreach (var @event in events) { - var events = aggregate.Events.ToList(); - if (!typeof(ITracedDomainEvent).IsAssignableFrom(typeof(T))) { aggregate.RemoveAllEvents(); } - foreach (var @event in events) - { - await dispatcher.RaiseAsync(@event.MergeMetadata(aggregate), setup).ConfigureAwait(false); - } + await dispatcher.RaiseAsync(@event.MergeMetadata(aggregate), setup).ConfigureAwait(false); } } } diff --git a/src/Savvyio.Domain/DomainEventExtensions.cs b/src/Savvyio.Domain/DomainEventExtensions.cs index cc78d551..cc06acf7 100644 --- a/src/Savvyio.Domain/DomainEventExtensions.cs +++ b/src/Savvyio.Domain/DomainEventExtensions.cs @@ -1,34 +1,33 @@ -using System; +using System; using Cuemon.Extensions; using Savvyio.Domain.EventSourcing; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Extension methods for the interface. +/// +public static class DomainEventExtensions { /// - /// Extension methods for the interface. + /// Gets the string representation of the event identifier from the . /// - public static class DomainEventExtensions + /// The model that implements the interface. + /// The to extend. + /// The string representation of the event identifier from the . + public static string GetEventId(this T request) where T : IDomainEvent { - /// - /// Gets the string representation of the event identifier from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The string representation of the event identifier from the . - public static string GetEventId(this T request) where T : IDomainEvent - { - return MetadataFactory.Get(request, MetadataDictionary.EventId).As(); - } + return MetadataFactory.Get(request, MetadataDictionary.EventId).As(); + } - /// - /// Gets the value of the timestamp from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The value of the timestamp from the . - public static DateTime GetTimestamp(this T request) where T : IDomainEvent - { - return MetadataFactory.Get(request, MetadataDictionary.Timestamp).As(); - } + /// + /// Gets the value of the timestamp from the . + /// + /// The model that implements the interface. + /// The to extend. + /// The value of the timestamp from the . + public static DateTime GetTimestamp(this T request) where T : IDomainEvent + { + return MetadataFactory.Get(request, MetadataDictionary.Timestamp).As(); } } diff --git a/src/Savvyio.Domain/DomainEventHandler.cs b/src/Savvyio.Domain/DomainEventHandler.cs index 3041e0db..de0f4f1f 100644 --- a/src/Savvyio.Domain/DomainEventHandler.cs +++ b/src/Savvyio.Domain/DomainEventHandler.cs @@ -1,31 +1,30 @@ -using Savvyio.Handlers; +using Savvyio.Handlers; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Provides a generic and consistent way of handling Domain Event (as specified in Domain Driven Design) objects that implements the interface. This is an abstract class. +/// +/// +public abstract class DomainEventHandler : IDomainEventHandler { /// - /// Provides a generic and consistent way of handling Domain Event (as specified in Domain Driven Design) objects that implements the interface. This is an abstract class. + /// Initializes a new instance of the class. /// - /// - public abstract class DomainEventHandler : IDomainEventHandler + protected DomainEventHandler() { - /// - /// Initializes a new instance of the class. - /// - protected DomainEventHandler() - { - Delegates = HandlerFactory.CreateFireForget(RegisterDelegates); - } + Delegates = HandlerFactory.CreateFireForget(RegisterDelegates); + } - /// - /// Registers the delegates responsible of handling types that implements the interface. - /// - /// The registry that store the delegates of type . - protected abstract void RegisterDelegates(IFireForgetRegistry handlers); + /// + /// Registers the delegates responsible of handling types that implements the interface. + /// + /// The registry that store the delegates of type . + protected abstract void RegisterDelegates(IFireForgetRegistry handlers); - /// - /// Gets the activator responsible of invoking delegates that handles . - /// - /// The activator responsible of invoking delegates that handles . - public IFireForgetActivator Delegates { get; } - } + /// + /// Gets the activator responsible of invoking delegates that handles . + /// + /// The activator responsible of invoking delegates that handles . + public IFireForgetActivator Delegates { get; } } diff --git a/src/Savvyio.Domain/Entity.cs b/src/Savvyio.Domain/Entity.cs index b293e710..ce56f6e6 100644 --- a/src/Savvyio.Domain/Entity.cs +++ b/src/Savvyio.Domain/Entity.cs @@ -1,40 +1,39 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Provides a way to cover the pattern of an Entity as specified in Domain Driven Design. This is an abstract class. +/// +/// The type of the identifier. +/// +public abstract class Entity : IEntity { /// - /// Provides a way to cover the pattern of an Entity as specified in Domain Driven Design. This is an abstract class. + /// Initializes a new instance of the class. /// - /// The type of the identifier. - /// - public abstract class Entity : IEntity + protected Entity() { - /// - /// Initializes a new instance of the class. - /// - protected Entity() - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The identifier of the entity. - protected Entity(TKey id) - { - Id = id; - } + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the entity. + protected Entity(TKey id) + { + Id = id; + } - /// - /// Gets or sets the value of the identifier. - /// - /// The value of the identifier. - public TKey Id { get; protected set; } + /// + /// Gets or sets the value of the identifier. + /// + /// The value of the identifier. + public TKey Id { get; protected set; } - /// - /// Gets a value indicating whether this instance is transient. - /// - /// true if this instance is transient; otherwise, false. - public virtual bool IsTransient => EqualityComparer.Default.Equals(Id, default); - } + /// + /// Gets a value indicating whether this instance is transient. + /// + /// true if this instance is transient; otherwise, false. + public virtual bool IsTransient => EqualityComparer.Default.Equals(Id, default); } diff --git a/src/Savvyio.Domain/SavvyioOptionsExtensions.cs b/src/Savvyio.Domain/SavvyioOptionsExtensions.cs index 36bb560e..b675d41c 100644 --- a/src/Savvyio.Domain/SavvyioOptionsExtensions.cs +++ b/src/Savvyio.Domain/SavvyioOptionsExtensions.cs @@ -1,29 +1,28 @@ -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Extension methods for the class. +/// +public static class SavvyioOptionsExtensions { /// - /// Extension methods for the class. + /// Adds an implementation of the interface to (if not already registered). /// - public static class SavvyioOptionsExtensions + /// The type that implements the interface. + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + public static SavvyioOptions AddDomainEventHandler(this SavvyioOptions options) where TImplementation : class, IDomainEventHandler { - /// - /// Adds an implementation of the interface to (if not already registered). - /// - /// The type that implements the interface. - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - public static SavvyioOptions AddDomainEventHandler(this SavvyioOptions options) where TImplementation : class, IDomainEventHandler - { - return options.AddHandler(); - } + return options.AddHandler(); + } - /// - /// Adds a default implementation of the interface. - /// - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - public static SavvyioOptions AddDomainEventDispatcher(this SavvyioOptions options) - { - return options.AddDispatcher(); - } + /// + /// Adds a default implementation of the interface. + /// + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + public static SavvyioOptions AddDomainEventDispatcher(this SavvyioOptions options) + { + return options.AddDispatcher(); } } diff --git a/src/Savvyio.Domain/SingleValueObject.cs b/src/Savvyio.Domain/SingleValueObject.cs index 2158ce1d..aef6e6e6 100644 --- a/src/Savvyio.Domain/SingleValueObject.cs +++ b/src/Savvyio.Domain/SingleValueObject.cs @@ -1,46 +1,45 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Provides an implementation of tailored for handling a single value. +/// +/// The type of the object. +/// +public abstract record SingleValueObject : ValueObject { /// - /// Provides an implementation of tailored for handling a single value. + /// Performs an implicit conversion from to . /// - /// The type of the object. - /// - public abstract record SingleValueObject : ValueObject + /// The value to convert. + /// that is equivalent to . + public static implicit operator T(SingleValueObject value) { - /// - /// Performs an implicit conversion from to . - /// - /// The value to convert. - /// that is equivalent to . - public static implicit operator T(SingleValueObject value) - { - return value.Value; - } + return value.Value; + } - /// - /// Initializes a new instance of the class. - /// - /// The value to associate to . - protected SingleValueObject(T value) - { - Value = value; - } + /// + /// Initializes a new instance of the class. + /// + /// The value to associate to . + protected SingleValueObject(T value) + { + Value = value; + } - /// - /// Gets the value of this instance. - /// - /// The value of this instance. - public T Value { get; } + /// + /// Gets the value of this instance. + /// + /// The value of this instance. + public T Value { get; } - /// - /// Gets the equality components of this instance. - /// - /// An that contains the equality components of this instance. - protected sealed override IEnumerable GetEqualityComponents() - { - yield return Value; - } + /// + /// Gets the equality components of this instance. + /// + /// An that contains the equality components of this instance. + protected sealed override IEnumerable GetEqualityComponents() + { + yield return Value; } } diff --git a/src/Savvyio.Domain/ValueObject.cs b/src/Savvyio.Domain/ValueObject.cs index a2ef8dcd..d4ea34ad 100644 --- a/src/Savvyio.Domain/ValueObject.cs +++ b/src/Savvyio.Domain/ValueObject.cs @@ -5,78 +5,77 @@ using Cuemon.Extensions; using Cuemon.Reflection; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +/// +/// Represents an object whose equality is based on the value rather than identity as specified in Domain Driven Design. +/// +/// +public abstract record ValueObject { + private const int NullHashCode = 472074819; + private readonly Lock _locker = new(); + private IEnumerable _equalityComponents; + /// - /// Represents an object whose equality is based on the value rather than identity as specified in Domain Driven Design. + /// Initializes a new instance of the class. /// - /// - public abstract record ValueObject + protected ValueObject() { - private const int NullHashCode = 472074819; - private readonly Lock _locker = new(); - private IEnumerable _equalityComponents; - - /// - /// Initializes a new instance of the class. - /// - protected ValueObject() - { - } + } - /// - /// Gets the equality components of this instance. Default is all public properties having a simple signature. - /// - /// An that contains the equality components of this instance. - protected virtual IEnumerable GetEqualityComponents() + /// + /// Gets the equality components of this instance. Default is all public properties having a simple signature. + /// + /// An that contains the equality components of this instance. + protected virtual IEnumerable GetEqualityComponents() + { + if (_equalityComponents == null) { - if (_equalityComponents == null) + lock (_locker) { - lock (_locker) + if (_equalityComponents == null) { - if (_equalityComponents == null) + var equalityComponents = new List(); + var properties = GetType().GetProperties(new MemberReflection(true, true)).Where(pi => (pi.PropertyType.IsSimple() || pi.PropertyType.IsAssignableTo(typeof(ValueObject))) && pi.CanRead); + foreach (var property in properties) { - var equalityComponents = new List(); - var properties = GetType().GetProperties(new MemberReflection(true, true)).Where(pi => (pi.PropertyType.IsSimple() || pi.PropertyType.IsAssignableTo(typeof(ValueObject))) && pi.CanRead); - foreach (var property in properties) - { - equalityComponents.Add(property.GetValue(this)); - } - _equalityComponents = equalityComponents; + equalityComponents.Add(property.GetValue(this)); } + _equalityComponents = equalityComponents; } } - return _equalityComponents; } + return _equalityComponents; + } - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// if the current object is equal to the parameter; otherwise, . - public virtual bool Equals(ValueObject other) - { - if (ReferenceEquals(this, other)) { return true; } - if (ReferenceEquals(null, other)) { return false; } - return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents()); - } + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// if the current object is equal to the parameter; otherwise, . + public virtual bool Equals(ValueObject other) + { + if (ReferenceEquals(this, other)) { return true; } + if (ReferenceEquals(null, other)) { return false; } + return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents()); + } - /// - /// Returns a hash code for this instance. - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - public override int GetHashCode() - { - return GetEqualityComponents().Select(x => x != null ? x.GetHashCode() : NullHashCode).GetHashCode32(); - } + /// + /// Returns a hash code for this instance. + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + public override int GetHashCode() + { + return GetEqualityComponents().Select(x => x != null ? x.GetHashCode() : NullHashCode).GetHashCode32(); + } - /// - /// Returns a that represents this instance. - /// - /// A that represents this instance. - public override string ToString() - { - return DelimitedString.Create(GetEqualityComponents()); - } + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return DelimitedString.Create(GetEqualityComponents()); } } diff --git a/src/Savvyio.EventDriven.Messaging/CloudEvents/CloudEvent.cs b/src/Savvyio.EventDriven.Messaging/CloudEvents/CloudEvent.cs index 6a55b0f8..7fdf6361 100644 --- a/src/Savvyio.EventDriven.Messaging/CloudEvents/CloudEvent.cs +++ b/src/Savvyio.EventDriven.Messaging/CloudEvents/CloudEvent.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -6,208 +6,207 @@ using Cuemon.Collections.Generic; using Savvyio.Messaging; -namespace Savvyio.EventDriven.Messaging.CloudEvents +namespace Savvyio.EventDriven.Messaging.CloudEvents; + +/// +/// Provides the base class for an implementation of the interface. +/// +public abstract record CloudEvent : Acknowledgeable { /// - /// Provides the base class for an implementation of the interface. + /// Gets a list of the reserved keywords. /// - public abstract record CloudEvent : Acknowledgeable - { - /// - /// Gets a list of the reserved keywords. - /// - /// A list of the reserved keywords. - protected static IReadOnlyCollection ReservedKeywords { get; } = Arguments.ToEnumerableOf(nameof(Id), nameof(Source), nameof(Type), nameof(Time), nameof(Data), nameof(Specversion)).ToList(); - - /// - /// Initializes a new instance of the class. - /// - protected CloudEvent() - { - } + /// A list of the reserved keywords. + protected static IReadOnlyCollection ReservedKeywords { get; } = Arguments.ToEnumerableOf(nameof(Id), nameof(Source), nameof(Type), nameof(Time), nameof(Data), nameof(Specversion)).ToList(); - /// - /// Gets the identifier of the event. When combined with , this enables deduplication. - /// - /// The identifier of the event. - /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#id - public string Id { get; protected set; } - - /// - /// Gets the context in which an event happened. When combined with , this enables deduplication. - /// - /// The context in which an event happened. - /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#source-1 - public string Source { get; protected set; } - - - /// - /// Gets the value describing the type of event related to the originating occurrence. - /// - /// The value describing the type of event related to the originating occurrence. - /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#type - public string Type { get; protected set; } - - /// - /// Gets the time, expressed as the Coordinated Universal Time (UTC), of when the occurrence happened. - /// - /// The timestamp of when the occurrence happened. - /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#time - public DateTime? Time { get; protected set; } - - /// - /// Gets version of the CloudEvents specification which the event uses. - /// - /// The version of the CloudEvents specification which the event uses. - /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#specversion - public string Specversion { get; protected set; } + /// + /// Initializes a new instance of the class. + /// + protected CloudEvent() + { } /// - /// Provides a default implementation of the interface. + /// Gets the identifier of the event. When combined with , this enables deduplication. /// - /// The type of the payload constraint to the interface. - /// - public record CloudEvent : CloudEvent, ICloudEvent where T : IIntegrationEvent - { - private readonly IDictionary _extensionAttributes = new Dictionary(StringComparer.OrdinalIgnoreCase); + /// The identifier of the event. + /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#id + public string Id { get; protected set; } - private static string EnsureReservedKeywordCompliance(string key) // https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#attribute-naming-convention - { - Validator.ThrowIfNullOrWhitespace(key); - Validator.ThrowIfContainsReservedKeyword(key, ReservedKeywords); - return key.ToLowerInvariant(); - } + /// + /// Gets the context in which an event happened. When combined with , this enables deduplication. + /// + /// The context in which an event happened. + /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#source-1 + public string Source { get; protected set; } - /// - /// Initializes a new instance of the class. - /// - /// The message to elevate to an compliance. - /// The version of the CloudEvents specification which the event uses. - /// - /// cannot be null. - /// - public CloudEvent(IMessage message, string specversion = null) - { - Validator.ThrowIfNull(message); - Id = message.Id; - Source = message.Source; - Type = message.Type; - Time = message.Time; - Data = message.Data; - Specversion = specversion ?? "1.0"; - } - /// - /// Gets the event payload. - /// - /// The event payload. - /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#event-data - public T Data { get; } + /// + /// Gets the value describing the type of event related to the originating occurrence. + /// + /// The value describing the type of event related to the originating occurrence. + /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#type + public string Type { get; protected set; } + /// + /// Gets the time, expressed as the Coordinated Universal Time (UTC), of when the occurrence happened. + /// + /// The timestamp of when the occurrence happened. + /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#time + public DateTime? Time { get; protected set; } + /// + /// Gets version of the CloudEvents specification which the event uses. + /// + /// The version of the CloudEvents specification which the event uses. + /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#specversion + public string Specversion { get; protected set; } +} - IEnumerator> IEnumerable>.GetEnumerator() - { - return _extensionAttributes.GetEnumerator(); - } +/// +/// Provides a default implementation of the interface. +/// +/// The type of the payload constraint to the interface. +/// +public record CloudEvent : CloudEvent, ICloudEvent where T : IIntegrationEvent +{ + private readonly IDictionary _extensionAttributes = new Dictionary(StringComparer.OrdinalIgnoreCase); - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)_extensionAttributes).GetEnumerator(); - } + private static string EnsureReservedKeywordCompliance(string key) // https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#attribute-naming-convention + { + Validator.ThrowIfNullOrWhitespace(key); + Validator.ThrowIfContainsReservedKeyword(key, ReservedKeywords); + return key.ToLowerInvariant(); + } - void ICollection>.Add(KeyValuePair item) - { - Add(item.Key, item.Value); - } + /// + /// Initializes a new instance of the class. + /// + /// The message to elevate to an compliance. + /// The version of the CloudEvents specification which the event uses. + /// + /// cannot be null. + /// + public CloudEvent(IMessage message, string specversion = null) + { + Validator.ThrowIfNull(message); + Id = message.Id; + Source = message.Source; + Type = message.Type; + Time = message.Time; + Data = message.Data; + Specversion = specversion ?? "1.0"; + } - /// - /// Removes all items from the . - /// - public void Clear() - { - _extensionAttributes.Clear(); - } + /// + /// Gets the event payload. + /// + /// The event payload. + /// https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#event-data + public T Data { get; } - bool ICollection>.Contains(KeyValuePair item) - { - return _extensionAttributes.Contains(item); - } - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) - { - _extensionAttributes.CopyTo(array, arrayIndex); - } - bool ICollection>.Remove(KeyValuePair item) - { - return Remove(item.Key); - } + IEnumerator> IEnumerable>.GetEnumerator() + { + return _extensionAttributes.GetEnumerator(); + } - int ICollection>.Count => _extensionAttributes.Count; + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_extensionAttributes).GetEnumerator(); + } - bool ICollection>.IsReadOnly => _extensionAttributes.IsReadOnly; + void ICollection>.Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } - /// - /// Adds an element with the provided key and value to the . - /// - /// The object to use as the key of the element to add. - /// The object to use as the value of the element to add. - public void Add(string key, object value) - { - var parsedKey = EnsureReservedKeywordCompliance(key); - _extensionAttributes.Add(parsedKey, value); - } + /// + /// Removes all items from the . + /// + public void Clear() + { + _extensionAttributes.Clear(); + } - /// - /// Determines whether the contains an element with the specified key. - /// - /// The key to locate in the . - /// if the contains an element with the key; otherwise, . - public bool ContainsKey(string key) - { - return _extensionAttributes.ContainsKey(key); - } + bool ICollection>.Contains(KeyValuePair item) + { + return _extensionAttributes.Contains(item); + } - /// - /// Removes the element with the specified key from the . - /// - /// The key of the element to remove. - /// if the element is successfully removed; otherwise, . This method also returns if was not found in the original . - public bool Remove(string key) - { - return _extensionAttributes.Remove(key); - } + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + _extensionAttributes.CopyTo(array, arrayIndex); + } - /// - /// Gets the value associated with the specified key. - /// - /// The key whose value to get. - /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. - /// if the object that implements contains an element with the specified key; otherwise, . - public bool TryGetValue(string key, out object value) - { - return _extensionAttributes.TryGetValue(key, out value); - } + bool ICollection>.Remove(KeyValuePair item) + { + return Remove(item.Key); + } + + int ICollection>.Count => _extensionAttributes.Count; + + bool ICollection>.IsReadOnly => _extensionAttributes.IsReadOnly; + + /// + /// Adds an element with the provided key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + public void Add(string key, object value) + { + var parsedKey = EnsureReservedKeywordCompliance(key); + _extensionAttributes.Add(parsedKey, value); + } + + /// + /// Determines whether the contains an element with the specified key. + /// + /// The key to locate in the . + /// if the contains an element with the key; otherwise, . + public bool ContainsKey(string key) + { + return _extensionAttributes.ContainsKey(key); + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// if the element is successfully removed; otherwise, . This method also returns if was not found in the original . + public bool Remove(string key) + { + return _extensionAttributes.Remove(key); + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// if the object that implements contains an element with the specified key; otherwise, . + public bool TryGetValue(string key, out object value) + { + return _extensionAttributes.TryGetValue(key, out value); + } - /// - /// Gets or sets the element with the specified key. - /// - /// The key of the value to get or set. - /// The value associated with the specified key. - public object this[string key] + /// + /// Gets or sets the element with the specified key. + /// + /// The key of the value to get or set. + /// The value associated with the specified key. + public object this[string key] + { + get => _extensionAttributes[key]; + set { - get => _extensionAttributes[key]; - set - { - var parsedKey = EnsureReservedKeywordCompliance(key); - _extensionAttributes[parsedKey] = value; - } + var parsedKey = EnsureReservedKeywordCompliance(key); + _extensionAttributes[parsedKey] = value; } + } - ICollection IDictionary.Keys => _extensionAttributes.Keys; + ICollection IDictionary.Keys => _extensionAttributes.Keys; - ICollection IDictionary.Values => _extensionAttributes.Values; - } + ICollection IDictionary.Values => _extensionAttributes.Values; } diff --git a/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/CloudEventExtensions.cs b/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/CloudEventExtensions.cs index d28d17ca..731066f5 100644 --- a/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/CloudEventExtensions.cs +++ b/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/CloudEventExtensions.cs @@ -1,38 +1,37 @@ -using System; +using System; using Cuemon; using Cuemon.Security.Cryptography; using Savvyio.Messaging.Cryptography; -namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography +namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography; + +/// +/// Extension methods for the interface. +/// +public static class CloudEventExtensions { /// - /// Extension methods for the interface. + /// Converts the specified to an equivalent. /// - public static class CloudEventExtensions + /// The type of the payload constraint to the interface. + /// The payload to attach within the message. + /// The that is used when converting the into an . + /// The which may be configured. + /// An instance of constraint to the interface. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// failed to configure an instance of in a valid state. + /// + public static ISignedCloudEvent SignCloudEvent(this ICloudEvent cloudEvent, IMarshaller marshaller, Action setup = null) where T : IIntegrationEvent { - /// - /// Converts the specified to an equivalent. - /// - /// The type of the payload constraint to the interface. - /// The payload to attach within the message. - /// The that is used when converting the into an . - /// The which may be configured. - /// An instance of constraint to the interface. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// failed to configure an instance of in a valid state. - /// - public static ISignedCloudEvent SignCloudEvent(this ICloudEvent cloudEvent, IMarshaller marshaller, Action setup = null) where T : IIntegrationEvent - { - Validator.ThrowIfNull(cloudEvent); - Validator.ThrowIfNull(marshaller); - Validator.ThrowIfInvalidConfigurator(setup, out var options); - var data = marshaller.Serialize(cloudEvent); - var signature = KeyedHashFactory.CreateHmacCrypto(options.SignatureSecret, options.SignatureAlgorithm).ComputeHash(data).ToHexadecimalString(); - return new SignedCloudEvent(cloudEvent, signature); - } + Validator.ThrowIfNull(cloudEvent); + Validator.ThrowIfNull(marshaller); + Validator.ThrowIfInvalidConfigurator(setup, out var options); + var data = marshaller.Serialize(cloudEvent); + var signature = KeyedHashFactory.CreateHmacCrypto(options.SignatureSecret, options.SignatureAlgorithm).ComputeHash(data).ToHexadecimalString(); + return new SignedCloudEvent(cloudEvent, signature); } } diff --git a/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/SignedCloudEvent.cs b/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/SignedCloudEvent.cs index f37795aa..cfb39202 100644 --- a/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/SignedCloudEvent.cs +++ b/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/SignedCloudEvent.cs @@ -1,34 +1,33 @@ -using System; +using System; using Cuemon; -namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography +namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography; + +/// +/// Provides a default implementation of the interface. +/// +/// The type of the payload constraint to the interface. +/// +public record SignedCloudEvent : CloudEvent, ISignedCloudEvent where T : IIntegrationEvent { /// - /// Provides a default implementation of the interface. + /// Initializes a new instance of the class. /// - /// The type of the payload constraint to the interface. - /// - public record SignedCloudEvent : CloudEvent, ISignedCloudEvent where T : IIntegrationEvent + /// The cloud event to sign. + /// The cryptographic signature of the . + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters. + /// + public SignedCloudEvent(ICloudEvent message, string signature) : base(message, message.Specversion) { - /// - /// Initializes a new instance of the class. - /// - /// The cloud event to sign. - /// The cryptographic signature of the . - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// cannot be empty or consist only of white-space characters. - /// - public SignedCloudEvent(ICloudEvent message, string signature) : base(message, message.Specversion) - { - Validator.ThrowIfNullOrWhitespace(signature); - Signature = signature; - } - - /// - public string Signature { get; } + Validator.ThrowIfNullOrWhitespace(signature); + Signature = signature; } + + /// + public string Signature { get; } } diff --git a/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/SignedCloudEventExtensions.cs b/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/SignedCloudEventExtensions.cs index cb18d358..00d5d6bb 100644 --- a/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/SignedCloudEventExtensions.cs +++ b/src/Savvyio.EventDriven.Messaging/CloudEvents/Cryptography/SignedCloudEventExtensions.cs @@ -3,42 +3,41 @@ using Savvyio.Messaging; using Savvyio.Messaging.Cryptography; -namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography +namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography; + +/// +/// Extension methods for the interface. +/// +public static class SignedCloudEventExtensions { + /// - /// Extension methods for the interface. + /// Verifies the digital signature of the message. /// - public static class SignedCloudEventExtensions + /// The type of the payload constraint to the interface. + /// The message to cryptographically verify. + /// The that is used when converting the into an . + /// The that must be configured. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// failed to configure an instance of in a valid state. + /// + /// + /// signature did not match the cryptographically calculated value. + /// + /// This method throws an if the verification of the digital signature fails. + public static void CheckCloudEventSignature(this ISignedCloudEvent cloudEvent, IMarshaller marshaller, Action setup) where T : IIntegrationEvent { - - /// - /// Verifies the digital signature of the message. - /// - /// The type of the payload constraint to the interface. - /// The message to cryptographically verify. - /// The that is used when converting the into an . - /// The that must be configured. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// failed to configure an instance of in a valid state. - /// - /// - /// signature did not match the cryptographically calculated value. - /// - /// This method throws an if the verification of the digital signature fails. - public static void CheckCloudEventSignature(this ISignedCloudEvent cloudEvent, IMarshaller marshaller, Action setup) where T : IIntegrationEvent + Validator.ThrowIfNull(cloudEvent); + Validator.ThrowIfNull(marshaller); + Validator.ThrowIfInvalidConfigurator(setup, out _); + var baseCloudEvent = (cloudEvent.Clone(() => new CloudEvent(cloudEvent, cloudEvent.Specversion)) as ICloudEvent).SignCloudEvent(marshaller, setup); + if (!cloudEvent.Signature.Equals(baseCloudEvent.Signature, StringComparison.Ordinal)) { - Validator.ThrowIfNull(cloudEvent); - Validator.ThrowIfNull(marshaller); - Validator.ThrowIfInvalidConfigurator(setup, out _); - var baseCloudEvent = (cloudEvent.Clone(() => new CloudEvent(cloudEvent, cloudEvent.Specversion)) as ICloudEvent).SignCloudEvent(marshaller, setup); - if (!cloudEvent.Signature.Equals(baseCloudEvent.Signature, StringComparison.Ordinal)) - { - throw new ArgumentOutOfRangeException(nameof(cloudEvent), cloudEvent.Signature, "The signature of the cloud event does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with."); - } + throw new ArgumentOutOfRangeException(nameof(cloudEvent), cloudEvent.Signature, "The signature of the cloud event does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with."); } } } diff --git a/src/Savvyio.EventDriven.Messaging/CloudEvents/MessageExtensions.cs b/src/Savvyio.EventDriven.Messaging/CloudEvents/MessageExtensions.cs index 8077747a..359661f9 100644 --- a/src/Savvyio.EventDriven.Messaging/CloudEvents/MessageExtensions.cs +++ b/src/Savvyio.EventDriven.Messaging/CloudEvents/MessageExtensions.cs @@ -1,28 +1,27 @@ -using System; +using System; using Cuemon; using Savvyio.Messaging; -namespace Savvyio.EventDriven.Messaging.CloudEvents +namespace Savvyio.EventDriven.Messaging.CloudEvents; + +/// +/// Extension methods for the interface. +/// +public static class MessageExtensions { /// - /// Extension methods for the interface. + /// Converts the specified to an equivalent. /// - public static class MessageExtensions + /// The type of the payload constraint to the interface. + /// The payload to attach within the message. + /// The version of the CloudEvents specification which the event uses. + /// An instance of constraint to the interface. + /// + /// cannot be null. + /// + public static ICloudEvent ToCloudEvent(this IMessage message, string specversion = null) where T : IIntegrationEvent { - /// - /// Converts the specified to an equivalent. - /// - /// The type of the payload constraint to the interface. - /// The payload to attach within the message. - /// The version of the CloudEvents specification which the event uses. - /// An instance of constraint to the interface. - /// - /// cannot be null. - /// - public static ICloudEvent ToCloudEvent(this IMessage message, string specversion = null) where T : IIntegrationEvent - { - Validator.ThrowIfNull(message); - return new CloudEvent(message, specversion); - } + Validator.ThrowIfNull(message); + return new CloudEvent(message, specversion); } } diff --git a/src/Savvyio.EventDriven.Messaging/InMemoryEventBus.cs b/src/Savvyio.EventDriven.Messaging/InMemoryEventBus.cs index 16e21f0a..c8a8c64e 100644 --- a/src/Savvyio.EventDriven.Messaging/InMemoryEventBus.cs +++ b/src/Savvyio.EventDriven.Messaging/InMemoryEventBus.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; @@ -7,62 +7,61 @@ using Cuemon.Threading; using Savvyio.Messaging; -namespace Savvyio.EventDriven.Messaging +namespace Savvyio.EventDriven.Messaging; + +/// +/// Provides an in-memory implementation of the interface useful for unit testing and the likes thereof. +/// +public class InMemoryEventBus : IPublishSubscribeChannel { + private readonly Channel> _channel = Channel.CreateUnbounded>(); + /// - /// Provides an in-memory implementation of the interface useful for unit testing and the likes thereof. + /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. /// - public class InMemoryEventBus : IPublishSubscribeChannel + /// The to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + public async Task PublishAsync(IMessage @event, Action setup = null) { - private readonly Channel> _channel = Channel.CreateUnbounded>(); - - /// - /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - public async Task PublishAsync(IMessage @event, Action setup = null) - { - var options = setup.Configure(); - await _channel.Writer.WriteAsync(@event, options.CancellationToken).ConfigureAwait(false); - } + var options = setup.Configure(); + await _channel.Writer.WriteAsync(@event, options.CancellationToken).ConfigureAwait(false); + } - /// - /// Subscribe to one or more event(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The delegate that will handle the event. - /// The which may be configured. - /// A that represents the asynchronous operation. - public async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) + /// + /// Subscribe to one or more event(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. + /// + /// The delegate that will handle the event. + /// The which may be configured. + /// A that represents the asynchronous operation. + public async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) + { + var options = setup.Configure(); + await Condition.FlipFlopAsync(options.ThrowIfCancellationWasRequested, () => WaitToReadAsync(asyncHandler, options.CancellationToken), async () => { - var options = setup.Configure(); - await Condition.FlipFlopAsync(options.ThrowIfCancellationWasRequested, () => WaitToReadAsync(asyncHandler, options.CancellationToken), async () => + try { - try - { - await WaitToReadAsync(asyncHandler, options.CancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) when (options.CancellationToken.IsCancellationRequested) - { - // ThrowIfCancellationWasRequested was set to false (default) - } - }); - } + await WaitToReadAsync(asyncHandler, options.CancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) when (options.CancellationToken.IsCancellationRequested) + { + // ThrowIfCancellationWasRequested was set to false (default) + } + }); + } - private async Task WaitToReadAsync(Func, CancellationToken, Task> asyncHandler, CancellationToken ct) + private async Task WaitToReadAsync(Func, CancellationToken, Task> asyncHandler, CancellationToken ct) + { + var currentSize = Math.Clamp(_channel.Reader.Count, 0, byte.MaxValue); + while (currentSize > 0) { - var currentSize = Math.Clamp(_channel.Reader.Count, 0, byte.MaxValue); - while (currentSize > 0) + for (var i = 0; i < currentSize; i++) { - for (var i = 0; i < currentSize; i++) - { - var message = await _channel.Reader.ReadAsync(ct).ConfigureAwait(false); - if (message == null) { break; } - await asyncHandler.Invoke(message, ct).ConfigureAwait(false); - } - currentSize = _channel.Reader.Count; + var message = await _channel.Reader.ReadAsync(ct).ConfigureAwait(false); + if (message == null) { break; } + await asyncHandler.Invoke(message, ct).ConfigureAwait(false); } + currentSize = _channel.Reader.Count; } } } diff --git a/src/Savvyio.EventDriven.Messaging/IntegrationEventExtensions.cs b/src/Savvyio.EventDriven.Messaging/IntegrationEventExtensions.cs index 31019e3d..5510b2d9 100644 --- a/src/Savvyio.EventDriven.Messaging/IntegrationEventExtensions.cs +++ b/src/Savvyio.EventDriven.Messaging/IntegrationEventExtensions.cs @@ -1,37 +1,36 @@ -using System; +using System; using Cuemon; using Savvyio.Messaging; -namespace Savvyio.EventDriven.Messaging +namespace Savvyio.EventDriven.Messaging; + +/// +/// Extension methods for the interface. +/// +public static class IntegrationEventExtensions { /// - /// Extension methods for the interface. + /// Encloses the specified to an instance of . /// - public static class IntegrationEventExtensions + /// The type of the payload constraint to the interface. + /// The payload to attach within the message. + /// The context that describes the origin of the message. + /// The type that describes the type of related to the originating occurrence. + /// The which may be configured. + /// An instance of constraint to the interface. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// failed to configure an instance of in a valid state. + /// + public static IMessage ToMessage(this T @event, Uri source, string type, Action setup = null) where T : IIntegrationEvent { - /// - /// Encloses the specified to an instance of . - /// - /// The type of the payload constraint to the interface. - /// The payload to attach within the message. - /// The context that describes the origin of the message. - /// The type that describes the type of related to the originating occurrence. - /// The which may be configured. - /// An instance of constraint to the interface. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// failed to configure an instance of in a valid state. - /// - public static IMessage ToMessage(this T @event, Uri source, string type, Action setup = null) where T : IIntegrationEvent - { - Validator.ThrowIfNull(@event); - Validator.ThrowIfNull(source); - Validator.ThrowIfNullOrWhitespace(type); - Validator.ThrowIfInvalidConfigurator(setup, out var options); - return new Message(options.MessageId, source, type, @event, options.Time); - } + Validator.ThrowIfNull(@event); + Validator.ThrowIfNull(source); + Validator.ThrowIfNullOrWhitespace(type); + Validator.ThrowIfInvalidConfigurator(setup, out var options); + return new Message(options.MessageId, source, type, @event, options.Time); } } diff --git a/src/Savvyio.EventDriven.Messaging/Savvyio.EventDriven.Messaging.csproj b/src/Savvyio.EventDriven.Messaging/Savvyio.EventDriven.Messaging.csproj index f4be9bcb..0d41e495 100644 --- a/src/Savvyio.EventDriven.Messaging/Savvyio.EventDriven.Messaging.csproj +++ b/src/Savvyio.EventDriven.Messaging/Savvyio.EventDriven.Messaging.csproj @@ -1,4 +1,4 @@ - + Provides ways to scale out Integration Events for a distributed architecture. diff --git a/src/Savvyio.EventDriven/IntegrationEvent.cs b/src/Savvyio.EventDriven/IntegrationEvent.cs index f0b5e402..59ab68b0 100644 --- a/src/Savvyio.EventDriven/IntegrationEvent.cs +++ b/src/Savvyio.EventDriven/IntegrationEvent.cs @@ -1,41 +1,40 @@ -using System; +using System; -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +/// +/// Provides a default implementation of of the interface. +/// +/// +/// +public abstract record IntegrationEvent : Request, IIntegrationEvent { /// - /// Provides a default implementation of of the interface. + /// Initializes a new instance of the class. /// - /// - /// - public abstract record IntegrationEvent : Request, IIntegrationEvent + /// The optional identifier of the event. Default is an auto-generated UUID. + /// The optional metadata to merge with this instance. + /// + /// The following table shows the initial metadata values for an instance of . + /// + /// + /// Key + /// Initial Value + /// + /// + /// + /// eventId ?? Guid.NewGuid().ToString("N") + /// + /// + /// + /// DateTime.UtcNow + /// + /// + /// + protected IntegrationEvent(string eventId = null, IMetadata metadata = null) { - /// - /// Initializes a new instance of the class. - /// - /// The optional identifier of the event. Default is an auto-generated UUID. - /// The optional metadata to merge with this instance. - /// - /// The following table shows the initial metadata values for an instance of . - /// - /// - /// Key - /// Initial Value - /// - /// - /// - /// eventId ?? Guid.NewGuid().ToString("N") - /// - /// - /// - /// DateTime.UtcNow - /// - /// - /// - protected IntegrationEvent(string eventId = null, IMetadata metadata = null) - { - this.SetEventId(eventId ?? Guid.NewGuid().ToString("N")); - this.SetTimestamp(); - this.MergeMetadata(metadata); - } + this.SetEventId(eventId ?? Guid.NewGuid().ToString("N")); + this.SetTimestamp(); + this.MergeMetadata(metadata); } } diff --git a/src/Savvyio.EventDriven/IntegrationEventDispatcher.cs b/src/Savvyio.EventDriven/IntegrationEventDispatcher.cs index 0d8b2ae1..965d98cc 100644 --- a/src/Savvyio.EventDriven/IntegrationEventDispatcher.cs +++ b/src/Savvyio.EventDriven/IntegrationEventDispatcher.cs @@ -1,46 +1,45 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon; using Cuemon.Threading; using Savvyio.Dispatchers; -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +/// +/// Provides a default implementation of of the interface. +/// +/// +/// +public class IntegrationEventDispatcher : FireForgetDispatcher, IIntegrationEventDispatcher { /// - /// Provides a default implementation of of the interface. + /// Initializes a new instance of the class. /// - /// - /// - public class IntegrationEventDispatcher : FireForgetDispatcher, IIntegrationEventDispatcher + /// The provider of service implementations. + public IntegrationEventDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) { - /// - /// Initializes a new instance of the class. - /// - /// The provider of service implementations. - public IntegrationEventDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) - { - } + } - /// - /// Publishes the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to publish. - public void Publish(IIntegrationEvent request) - { - Validator.ThrowIfNull(request); - Dispatch(request, handler => handler.Delegates); - } + /// + /// Publishes the specified using Fire-and-Forget/In-Only MEP. + /// + /// The to publish. + public void Publish(IIntegrationEvent request) + { + Validator.ThrowIfNull(request); + Dispatch(request, handler => handler.Delegates); + } - /// - /// Publishes the specified asynchronous using Fire-and-Forget/In-Only MEP. - /// - /// The to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - public Task PublishAsync(IIntegrationEvent request, Action setup = null) - { - Validator.ThrowIfNull(request); - return DispatchAsync(request, handler => handler.Delegates, setup); - } + /// + /// Publishes the specified asynchronous using Fire-and-Forget/In-Only MEP. + /// + /// The to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + public Task PublishAsync(IIntegrationEvent request, Action setup = null) + { + Validator.ThrowIfNull(request); + return DispatchAsync(request, handler => handler.Delegates, setup); } } diff --git a/src/Savvyio.EventDriven/IntegrationEventExtensions.cs b/src/Savvyio.EventDriven/IntegrationEventExtensions.cs index 780f57d1..aaa082c4 100644 --- a/src/Savvyio.EventDriven/IntegrationEventExtensions.cs +++ b/src/Savvyio.EventDriven/IntegrationEventExtensions.cs @@ -1,44 +1,43 @@ -using System; +using System; using Cuemon.Extensions; -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +/// +/// Extension methods for the interface. +/// +public static class IntegrationEventExtensions { /// - /// Extension methods for the interface. + /// Gets the string representation of the event identifier from the . /// - public static class IntegrationEventExtensions + /// The model that implements the interface. + /// The to extend. + /// The string representation of the event identifier from the . + public static string GetEventId(this T request) where T : IIntegrationEvent { - /// - /// Gets the string representation of the event identifier from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The string representation of the event identifier from the . - public static string GetEventId(this T request) where T : IIntegrationEvent - { - return MetadataFactory.Get(request, MetadataDictionary.EventId).As(); - } + return MetadataFactory.Get(request, MetadataDictionary.EventId).As(); + } - /// - /// Gets the value of the timestamp from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The value of the timestamp from the . - public static DateTime GetTimestamp(this T request) where T : IIntegrationEvent - { - return MetadataFactory.Get(request, MetadataDictionary.Timestamp).As(); - } + /// + /// Gets the value of the timestamp from the . + /// + /// The model that implements the interface. + /// The to extend. + /// The value of the timestamp from the . + public static DateTime GetTimestamp(this T request) where T : IIntegrationEvent + { + return MetadataFactory.Get(request, MetadataDictionary.Timestamp).As(); + } - /// - /// Gets the string representation of the type from the . - /// - /// The model that implements the interface. - /// The to extend. - /// The string representation of the type from the . - public static string GetMemberType(this T request) where T : IIntegrationEvent - { - return MetadataFactory.Get(request, MetadataDictionary.MemberType).As(); - } + /// + /// Gets the string representation of the type from the . + /// + /// The model that implements the interface. + /// The to extend. + /// The string representation of the type from the . + public static string GetMemberType(this T request) where T : IIntegrationEvent + { + return MetadataFactory.Get(request, MetadataDictionary.MemberType).As(); } } diff --git a/src/Savvyio.EventDriven/IntegrationEventHandler.cs b/src/Savvyio.EventDriven/IntegrationEventHandler.cs index 87223947..72e53156 100644 --- a/src/Savvyio.EventDriven/IntegrationEventHandler.cs +++ b/src/Savvyio.EventDriven/IntegrationEventHandler.cs @@ -1,31 +1,30 @@ -using Savvyio.Handlers; +using Savvyio.Handlers; -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +/// +/// Provides a generic and consistent way of handling Integration Event objects that implements the interface. This is an abstract class. +/// +/// +public abstract class IntegrationEventHandler : IIntegrationEventHandler { /// - /// Provides a generic and consistent way of handling Integration Event objects that implements the interface. This is an abstract class. + /// Initializes a new instance of the class. /// - /// - public abstract class IntegrationEventHandler : IIntegrationEventHandler + protected IntegrationEventHandler() { - /// - /// Initializes a new instance of the class. - /// - protected IntegrationEventHandler() - { - Delegates = HandlerFactory.CreateFireForget(RegisterDelegates); - } + Delegates = HandlerFactory.CreateFireForget(RegisterDelegates); + } - /// - /// Registers the delegates responsible of handling types that implements the interface. - /// - /// The registry that store the delegates of type . - protected abstract void RegisterDelegates(IFireForgetRegistry handlers); + /// + /// Registers the delegates responsible of handling types that implements the interface. + /// + /// The registry that store the delegates of type . + protected abstract void RegisterDelegates(IFireForgetRegistry handlers); - /// - /// Gets the activator responsible of invoking delegates that handles . - /// - /// The activator responsible of invoking delegates that handles . - public IFireForgetActivator Delegates { get; } - } + /// + /// Gets the activator responsible of invoking delegates that handles . + /// + /// The activator responsible of invoking delegates that handles . + public IFireForgetActivator Delegates { get; } } diff --git a/src/Savvyio.EventDriven/Savvyio.EventDriven.csproj b/src/Savvyio.EventDriven/Savvyio.EventDriven.csproj index c6341fb9..a7ca6666 100644 --- a/src/Savvyio.EventDriven/Savvyio.EventDriven.csproj +++ b/src/Savvyio.EventDriven/Savvyio.EventDriven.csproj @@ -1,4 +1,4 @@ - + Provides the fundamental classes to work with the concepts of Integration Events that complements both CQRS (Command and Query Responsibility Segregation) and DDD (Domain Driven Design). diff --git a/src/Savvyio.EventDriven/SavvyioOptionsExtensions.cs b/src/Savvyio.EventDriven/SavvyioOptionsExtensions.cs index af7b4d92..b4d8220f 100644 --- a/src/Savvyio.EventDriven/SavvyioOptionsExtensions.cs +++ b/src/Savvyio.EventDriven/SavvyioOptionsExtensions.cs @@ -1,30 +1,29 @@ -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +/// +/// Extension methods for the class. +/// +public static class SavvyioOptionsExtensions { /// - /// Extension methods for the class. + /// Adds an implementation of the interface to (if not already registered). /// - public static class SavvyioOptionsExtensions + /// The type that implements the interface. + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + public static SavvyioOptions AddIntegrationEventHandler(this SavvyioOptions options) where TImplementation : class, IIntegrationEventHandler { - /// - /// Adds an implementation of the interface to (if not already registered). - /// - /// The type that implements the interface. - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - public static SavvyioOptions AddIntegrationEventHandler(this SavvyioOptions options) where TImplementation : class, IIntegrationEventHandler - { - options.AddHandler(); - return options; - } + options.AddHandler(); + return options; + } - /// - /// Adds a default implementation of the interface. - /// - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - public static SavvyioOptions AddIntegrationEventDispatcher(this SavvyioOptions options) - { - return options.AddDispatcher(); - } + /// + /// Adds a default implementation of the interface. + /// + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + public static SavvyioOptions AddIntegrationEventDispatcher(this SavvyioOptions options) + { + return options.AddDispatcher(); } } diff --git a/src/Savvyio.Extensions.Dapper/DapperDataSource.cs b/src/Savvyio.Extensions.Dapper/DapperDataSource.cs index 0ed92766..a2dd7bf0 100644 --- a/src/Savvyio.Extensions.Dapper/DapperDataSource.cs +++ b/src/Savvyio.Extensions.Dapper/DapperDataSource.cs @@ -1,123 +1,122 @@ -using System; +using System; using System.Data; using Cuemon; -namespace Savvyio.Extensions.Dapper +namespace Savvyio.Extensions.Dapper; + +/// +/// Provides a default implementation of the interface to support the actual I/O communication with a source of data using Dapper. +/// +/// +/// +public class DapperDataSource : Disposable, IDapperDataSource { + private readonly IDbConnection _dbConnection; + /// - /// Provides a default implementation of the interface to support the actual I/O communication with a source of data using Dapper. + /// Initializes a new instance of the class. /// - /// - /// - public class DapperDataSource : Disposable, IDapperDataSource + /// The used to configure this instance. + /// + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public DapperDataSource(DapperDataSourceOptions options) { - private readonly IDbConnection _dbConnection; - - /// - /// Initializes a new instance of the class. - /// - /// The used to configure this instance. - /// - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public DapperDataSource(DapperDataSourceOptions options) - { - Validator.ThrowIfInvalidOptions(options); - _dbConnection = options.ConnectionFactory.Invoke(); - _dbConnection.Open(); - } + Validator.ThrowIfInvalidOptions(options); + _dbConnection = options.ConnectionFactory.Invoke(); + _dbConnection.Open(); + } - /// - /// Called when this object is being disposed by either or having disposing set to true and is false. - /// - protected override void OnDisposeManagedResources() - { - _dbConnection?.Close(); - _dbConnection?.Dispose(); - } + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected override void OnDisposeManagedResources() + { + _dbConnection?.Close(); + _dbConnection?.Dispose(); + } - /// - /// Begins a database transaction. - /// - /// An object representing the new transaction. - public IDbTransaction BeginTransaction() - { - return _dbConnection.BeginTransaction(); - } + /// + /// Begins a database transaction. + /// + /// An object representing the new transaction. + public IDbTransaction BeginTransaction() + { + return _dbConnection.BeginTransaction(); + } - /// - /// Begins a database transaction. - /// - /// One of the values. - /// An object representing the new transaction. - public IDbTransaction BeginTransaction(IsolationLevel il) - { - return _dbConnection.BeginTransaction(il); - } + /// + /// Begins a database transaction. + /// + /// One of the values. + /// An object representing the new transaction. + public IDbTransaction BeginTransaction(IsolationLevel il) + { + return _dbConnection.BeginTransaction(il); + } - /// - /// Changes the current database for an open object. - /// - /// The name of the database to use in place of the current database. - public void ChangeDatabase(string databaseName) - { - _dbConnection.ChangeDatabase(databaseName); - } + /// + /// Changes the current database for an open object. + /// + /// The name of the database to use in place of the current database. + public void ChangeDatabase(string databaseName) + { + _dbConnection.ChangeDatabase(databaseName); + } - /// - /// Closes the connection to the database. - /// - public void Close() - { - _dbConnection.Close(); - } + /// + /// Closes the connection to the database. + /// + public void Close() + { + _dbConnection.Close(); + } - /// - /// Creates and returns a Command object associated with the connection. - /// - /// A Command object associated with the connection. - public IDbCommand CreateCommand() - { - return _dbConnection.CreateCommand(); - } + /// + /// Creates and returns a Command object associated with the connection. + /// + /// A Command object associated with the connection. + public IDbCommand CreateCommand() + { + return _dbConnection.CreateCommand(); + } - /// - /// Opens a database connection with the settings specified by the property of the provider-specific Connection object. - /// - public void Open() - { - _dbConnection.Open(); - } + /// + /// Opens a database connection with the settings specified by the property of the provider-specific Connection object. + /// + public void Open() + { + _dbConnection.Open(); + } - /// - /// Gets or sets the string used to open a database. - /// - /// A string containing connection settings. - public string ConnectionString - { - get => _dbConnection.ConnectionString; - set => _dbConnection.ConnectionString = value; - } + /// + /// Gets or sets the string used to open a database. + /// + /// A string containing connection settings. + public string ConnectionString + { + get => _dbConnection.ConnectionString; + set => _dbConnection.ConnectionString = value; + } - /// - /// Gets the time to wait (in seconds) while trying to establish a connection before terminating the attempt and generating an error. - /// - /// The time (in seconds) to wait for a connection to open. The default value is 15 seconds. - public int ConnectionTimeout => _dbConnection.ConnectionTimeout; + /// + /// Gets the time to wait (in seconds) while trying to establish a connection before terminating the attempt and generating an error. + /// + /// The time (in seconds) to wait for a connection to open. The default value is 15 seconds. + public int ConnectionTimeout => _dbConnection.ConnectionTimeout; - /// - /// Gets the name of the current database or the database to be used after a connection is opened. - /// - /// The name of the current database or the name of the database to be used once a connection is open. The default value is an empty string. - public string Database => _dbConnection.Database; + /// + /// Gets the name of the current database or the database to be used after a connection is opened. + /// + /// The name of the current database or the name of the database to be used once a connection is open. The default value is an empty string. + public string Database => _dbConnection.Database; - /// - /// Gets the current state of the connection. - /// - /// One of the values. - public ConnectionState State => _dbConnection.State; - } + /// + /// Gets the current state of the connection. + /// + /// One of the values. + public ConnectionState State => _dbConnection.State; } diff --git a/src/Savvyio.Extensions.Dapper/DapperDataSourceOptions.cs b/src/Savvyio.Extensions.Dapper/DapperDataSourceOptions.cs index e047105c..6d3e9b10 100644 --- a/src/Savvyio.Extensions.Dapper/DapperDataSourceOptions.cs +++ b/src/Savvyio.Extensions.Dapper/DapperDataSourceOptions.cs @@ -1,38 +1,37 @@ -using System; +using System; using System.Data; using Cuemon; using Cuemon.Configuration; -namespace Savvyio.Extensions.Dapper +namespace Savvyio.Extensions.Dapper; + +/// +/// Configuration options for . +/// +public class DapperDataSourceOptions : IValidatableParameterObject { /// - /// Configuration options for . + /// Initializes a new instance of the class. /// - public class DapperDataSourceOptions : IValidatableParameterObject + public DapperDataSourceOptions() { - /// - /// Initializes a new instance of the class. - /// - public DapperDataSourceOptions() - { - } + } - /// - /// Gets or sets the function delegate that provides the . - /// - /// The function delegate that provides the . - public Func ConnectionFactory { get; set; } + /// + /// Gets or sets the function delegate that provides the . + /// + /// The function delegate that provides the . + public Func ConnectionFactory { get; set; } - /// - /// Determines whether the public read-write properties of this instance are in a valid state. - /// - /// - /// cannot be null. - /// - /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. - public void ValidateOptions() - { - Validator.ThrowIfInvalidState(ConnectionFactory == null); - } + /// + /// Determines whether the public read-write properties of this instance are in a valid state. + /// + /// + /// cannot be null. + /// + /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. + public void ValidateOptions() + { + Validator.ThrowIfInvalidState(ConnectionFactory == null); } } diff --git a/src/Savvyio.Extensions.Dapper/DapperDataStore.cs b/src/Savvyio.Extensions.Dapper/DapperDataStore.cs index 353130d3..6dbd769e 100644 --- a/src/Savvyio.Extensions.Dapper/DapperDataStore.cs +++ b/src/Savvyio.Extensions.Dapper/DapperDataStore.cs @@ -1,83 +1,82 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon; using Cuemon.Threading; using Savvyio.Data; -namespace Savvyio.Extensions.Dapper +namespace Savvyio.Extensions.Dapper; + +/// +/// Represents the base class from which all implementations of should derive. This is an abstract class to serve as an abstraction layer before the actual I/O communication with a source of data using Dapper. +/// +/// The type of the DTO. +/// The type of options associated with this DTO. +/// +/// +public abstract class DapperDataStore : Disposable, IPersistentDataStore + where T : class + where TOptions : AsyncOptions, new() { /// - /// Represents the base class from which all implementations of should derive. This is an abstract class to serve as an abstraction layer before the actual I/O communication with a source of data using Dapper. + /// Initializes a new instance of the class. /// - /// The type of the DTO. - /// The type of options associated with this DTO. - /// - /// - public abstract class DapperDataStore : Disposable, IPersistentDataStore - where T : class - where TOptions : AsyncOptions, new() + /// The that handles actual I/O communication with a source of data. + protected DapperDataStore(IDapperDataSource source) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - protected DapperDataStore(IDapperDataSource source) - { - Source = source; - } + Source = source; + } - /// - /// Gets the that handles actual I/O communication with a source of data. - /// - /// The that handles actual I/O communication with a source of data. - protected IDapperDataSource Source { get; } + /// + /// Gets the that handles actual I/O communication with a source of data. + /// + /// The that handles actual I/O communication with a source of data. + protected IDapperDataSource Source { get; } - /// - /// Creates the specified asynchronous in the associated . - /// - /// The object to create in the associated . - /// The which may be configured. - /// A that represents the asynchronous operation. - public abstract Task CreateAsync(T dto, Action setup = null); + /// + /// Creates the specified asynchronous in the associated . + /// + /// The object to create in the associated . + /// The which may be configured. + /// A that represents the asynchronous operation. + public abstract Task CreateAsync(T dto, Action setup = null); - /// - /// Updates the specified asynchronous in the associated . - /// - /// The object to update in the associated . - /// The which may be configured. - /// A that represents the asynchronous operation. - public abstract Task UpdateAsync(T dto, Action setup = null); + /// + /// Updates the specified asynchronous in the associated . + /// + /// The object to update in the associated . + /// The which may be configured. + /// A that represents the asynchronous operation. + public abstract Task UpdateAsync(T dto, Action setup = null); - /// - /// Loads the object from the specified asynchronous. - /// - /// The key that uniquely identifies the object. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the object of the operation or null if not found. - public abstract Task GetByIdAsync(object id, Action setup = null); + /// + /// Loads the object from the specified asynchronous. + /// + /// The key that uniquely identifies the object. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the object of the operation or null if not found. + public abstract Task GetByIdAsync(object id, Action setup = null); - /// - /// Finds all objects matching the specified asynchronous in the associated . - /// - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the matching objects of the operation or an empty sequence if no match was found. - public abstract Task> FindAllAsync(Action setup = null); + /// + /// Finds all objects matching the specified asynchronous in the associated . + /// + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the matching objects of the operation or an empty sequence if no match was found. + public abstract Task> FindAllAsync(Action setup = null); - /// - /// Deletes the specified asynchronous in the associated . - /// - /// The object to delete in the associated . - /// The which may be configured. - /// A that represents the asynchronous operation. - public abstract Task DeleteAsync(T dto, Action setup = null); + /// + /// Deletes the specified asynchronous in the associated . + /// + /// The object to delete in the associated . + /// The which may be configured. + /// A that represents the asynchronous operation. + public abstract Task DeleteAsync(T dto, Action setup = null); - /// - /// Called when this object is being disposed by either or having disposing set to true and is false. - /// - protected override void OnDisposeManagedResources() - { - Source?.Dispose(); - } + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected override void OnDisposeManagedResources() + { + Source?.Dispose(); } } diff --git a/src/Savvyio.Extensions.Dapper/DapperQueryOptions.cs b/src/Savvyio.Extensions.Dapper/DapperQueryOptions.cs index 36164e08..889adc20 100644 --- a/src/Savvyio.Extensions.Dapper/DapperQueryOptions.cs +++ b/src/Savvyio.Extensions.Dapper/DapperQueryOptions.cs @@ -1,96 +1,95 @@ -using System; +using System; using System.Data; using Cuemon.Threading; using Dapper; -namespace Savvyio.Extensions.Dapper +namespace Savvyio.Extensions.Dapper; + +/// +/// Specifies options that is related to . +/// +/// +public class DapperQueryOptions : AsyncOptions { /// - /// Specifies options that is related to . + /// Performs an implicit conversion from to . /// - /// - public class DapperQueryOptions : AsyncOptions + /// The to convert. + /// A that is equivalent to . + public static implicit operator CommandDefinition(DapperQueryOptions value) { - /// - /// Performs an implicit conversion from to . - /// - /// The to convert. - /// A that is equivalent to . - public static implicit operator CommandDefinition(DapperQueryOptions value) - { - return new CommandDefinition(value.CommandText, commandTimeout: (int)value.CommandTimeout.TotalSeconds, cancellationToken: value.CancellationToken, flags: value.CommandFlags, commandType: value.CommandType, transaction: value.Transaction, parameters: value.Parameters); - } + return new CommandDefinition(value.CommandText, commandTimeout: (int)value.CommandTimeout.TotalSeconds, cancellationToken: value.CancellationToken, flags: value.CommandFlags, commandType: value.CommandType, transaction: value.Transaction, parameters: value.Parameters); + } - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// default - /// - /// - /// - /// TimeSpan.FromSeconds(30) - /// - /// - /// - /// CommandFlags.Buffered - /// - /// - /// - /// CommandType.Text - /// - /// - /// - public DapperQueryOptions() - { - CommandTimeout = TimeSpan.FromSeconds(30); - CommandFlags = CommandFlags.Buffered; - CommandType = CommandType.Text; - } + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// default + /// + /// + /// + /// TimeSpan.FromSeconds(30) + /// + /// + /// + /// CommandFlags.Buffered + /// + /// + /// + /// CommandType.Text + /// + /// + /// + public DapperQueryOptions() + { + CommandTimeout = TimeSpan.FromSeconds(30); + CommandFlags = CommandFlags.Buffered; + CommandType = CommandType.Text; + } - /// - /// Gets or sets the parameters associated with the command. - /// - /// The parameters associated with the command. - /// This is typically the DTO. - public object Parameters { get; set; } + /// + /// Gets or sets the parameters associated with the command. + /// + /// The parameters associated with the command. + /// This is typically the DTO. + public object Parameters { get; set; } - /// - /// Gets or sets the command (sql or a stored-procedure name) to execute. - /// - /// The command (sql or a stored-procedure name) to execute. - public string CommandText { get; set; } + /// + /// Gets or sets the command (sql or a stored-procedure name) to execute. + /// + /// The command (sql or a stored-procedure name) to execute. + public string CommandText { get; set; } - /// - /// Gets or sets the timeout for the command. - /// - /// The timeout for the command. - public TimeSpan CommandTimeout { get; set; } + /// + /// Gets or sets the timeout for the command. + /// + /// The timeout for the command. + public TimeSpan CommandTimeout { get; set; } - /// - /// Gets or sets the state flags against the command. - /// - /// The state flags against the command. - public CommandFlags CommandFlags { get; set; } + /// + /// Gets or sets the state flags against the command. + /// + /// The state flags against the command. + public CommandFlags CommandFlags { get; set; } - /// - /// Gets or sets the type of command that the command-text represents. - /// - /// The type of command that the command-text represents. - public CommandType CommandType { get; set; } + /// + /// Gets or sets the type of command that the command-text represents. + /// + /// The type of command that the command-text represents. + public CommandType CommandType { get; set; } - /// - /// Gets or sets the active transaction for the command. - /// - /// The active transaction for the command. - public IDbTransaction Transaction { get; set; } - } + /// + /// Gets or sets the active transaction for the command. + /// + /// The active transaction for the command. + public IDbTransaction Transaction { get; set; } } diff --git a/src/Savvyio.Extensions.Dapper/IDapperDataSource.cs b/src/Savvyio.Extensions.Dapper/IDapperDataSource.cs index 9bad4c02..baa0045b 100644 --- a/src/Savvyio.Extensions.Dapper/IDapperDataSource.cs +++ b/src/Savvyio.Extensions.Dapper/IDapperDataSource.cs @@ -1,12 +1,11 @@ -using System.Data; +using System.Data; -namespace Savvyio.Extensions.Dapper +namespace Savvyio.Extensions.Dapper; + +/// +/// Defines a generic way to support the actual I/O communication with a source of data - tailored to Dapper. +/// +/// +public interface IDapperDataSource : IDataSource, IDbConnection { - /// - /// Defines a generic way to support the actual I/O communication with a source of data - tailored to Dapper. - /// - /// - public interface IDapperDataSource : IDataSource, IDbConnection - { - } } diff --git a/src/Savvyio.Extensions.Dapper/Savvyio.Extensions.Dapper.csproj b/src/Savvyio.Extensions.Dapper/Savvyio.Extensions.Dapper.csproj index 1cc9a018..fa723051 100644 --- a/src/Savvyio.Extensions.Dapper/Savvyio.Extensions.Dapper.csproj +++ b/src/Savvyio.Extensions.Dapper/Savvyio.Extensions.Dapper.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O core assemblies with support for Dapper. diff --git a/src/Savvyio.Extensions.DapperExtensions/DapperExtensionsDataStore.cs b/src/Savvyio.Extensions.DapperExtensions/DapperExtensionsDataStore.cs index 70a8ce93..54365b32 100644 --- a/src/Savvyio.Extensions.DapperExtensions/DapperExtensionsDataStore.cs +++ b/src/Savvyio.Extensions.DapperExtensions/DapperExtensionsDataStore.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon.Extensions; @@ -6,77 +6,76 @@ using DapperExtensions; using Savvyio.Extensions.Dapper; -namespace Savvyio.Extensions.DapperExtensions +namespace Savvyio.Extensions.DapperExtensions; + +/// +/// Provides a default implementation of the class that is tailored for Plain Old CLR Objects (POCO) usage by DapperExtensions. +/// +/// The type of the DTO. +/// +public class DapperExtensionsDataStore : DapperDataStore> where T : class { /// - /// Provides a default implementation of the class that is tailored for Plain Old CLR Objects (POCO) usage by DapperExtensions. + /// Initializes a new instance of the class. /// - /// The type of the DTO. - /// - public class DapperExtensionsDataStore : DapperDataStore> where T : class + /// The that handles actual I/O communication with a source of data. + public DapperExtensionsDataStore(IDapperDataSource source) : base(source) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - public DapperExtensionsDataStore(IDapperDataSource source) : base(source) - { - } + } - /// - /// Creates the specified asynchronous in the associated . - /// - /// The object to create in the associated . - /// The that needs to be configured. - /// A that represents the asynchronous operation. - public override Task CreateAsync(T dto, Action setup = null) - { - return Source.InsertAsync(dto); + /// + /// Creates the specified asynchronous in the associated . + /// + /// The object to create in the associated . + /// The that needs to be configured. + /// A that represents the asynchronous operation. + public override Task CreateAsync(T dto, Action setup = null) + { + return Source.InsertAsync(dto); - } + } - /// - /// Deletes the specified asynchronous in the associated . - /// - /// The object to delete in the associated . - /// The that needs to be configured. - /// A that represents the asynchronous operation. - public override Task DeleteAsync(T dto, Action setup = null) - { - return Source.DeleteAsync(dto); - } + /// + /// Deletes the specified asynchronous in the associated . + /// + /// The object to delete in the associated . + /// The that needs to be configured. + /// A that represents the asynchronous operation. + public override Task DeleteAsync(T dto, Action setup = null) + { + return Source.DeleteAsync(dto); + } - /// - /// Loads the object from the specified asynchronous. - /// - /// The key that uniquely identifies the object. - /// The that needs to be configured. - /// A that represents the asynchronous operation. The task result either contains the object of the operation or null if not found. - public override Task GetByIdAsync(object id, Action setup = null) - { - return Source.GetAsync(id); - } + /// + /// Loads the object from the specified asynchronous. + /// + /// The key that uniquely identifies the object. + /// The that needs to be configured. + /// A that represents the asynchronous operation. The task result either contains the object of the operation or null if not found. + public override Task GetByIdAsync(object id, Action setup = null) + { + return Source.GetAsync(id); + } - /// - /// Finds all objects matching the specified asynchronous in the associated . - /// - /// The that needs to be configured. - /// A that represents the asynchronous operation. The task result either contains the matching objects of the operation or an empty sequence if no match was found. - public override Task> FindAllAsync(Action> setup = null) - { - var options = setup.Configure(); - return options.Predicate == null ? Source.GetListAsync() : Source.GetListAsync(Predicates.Field(options.Predicate, options.Op, options.Value, options.Not, options.UseColumnPrefix, options.Function, options.FunctionParameters)); - } + /// + /// Finds all objects matching the specified asynchronous in the associated . + /// + /// The that needs to be configured. + /// A that represents the asynchronous operation. The task result either contains the matching objects of the operation or an empty sequence if no match was found. + public override Task> FindAllAsync(Action> setup = null) + { + var options = setup.Configure(); + return options.Predicate == null ? Source.GetListAsync() : Source.GetListAsync(Predicates.Field(options.Predicate, options.Op, options.Value, options.Not, options.UseColumnPrefix, options.Function, options.FunctionParameters)); + } - /// - /// Updates the specified asynchronous in the associated . - /// - /// The object to update in the associated . - /// The that needs to be configured. - /// A that represents the asynchronous operation. - public override Task UpdateAsync(T dto, Action setup = null) - { - return Source.UpdateAsync(dto); - } + /// + /// Updates the specified asynchronous in the associated . + /// + /// The object to update in the associated . + /// The that needs to be configured. + /// A that represents the asynchronous operation. + public override Task UpdateAsync(T dto, Action setup = null) + { + return Source.UpdateAsync(dto); } } diff --git a/src/Savvyio.Extensions.DapperExtensions/DapperExtensionsQueryOptions.cs b/src/Savvyio.Extensions.DapperExtensions/DapperExtensionsQueryOptions.cs index 7d0dfa4f..e4818e58 100644 --- a/src/Savvyio.Extensions.DapperExtensions/DapperExtensionsQueryOptions.cs +++ b/src/Savvyio.Extensions.DapperExtensions/DapperExtensionsQueryOptions.cs @@ -1,109 +1,108 @@ -using System; +using System; using System.Linq.Expressions; using Cuemon.Threading; using DapperExtensions; using DapperExtensions.Predicate; -namespace Savvyio.Extensions.DapperExtensions +namespace Savvyio.Extensions.DapperExtensions; + +/// +/// Specifies options that is related to . +/// +/// +public class DapperExtensionsQueryOptions : AsyncOptions { /// - /// Specifies options that is related to . + /// Initializes a new instance of the class. /// - /// - public class DapperExtensionsQueryOptions : AsyncOptions + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// null + /// + /// + /// + /// + /// + /// + /// + /// null + /// + /// + /// + /// false + /// + /// + /// + /// true + /// + /// + /// + /// + /// + /// + /// + /// null + /// + /// + /// + /// default + /// + /// + /// + public DapperExtensionsQueryOptions() { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// null - /// - /// - /// - /// - /// - /// - /// - /// null - /// - /// - /// - /// false - /// - /// - /// - /// true - /// - /// - /// - /// - /// - /// - /// - /// null - /// - /// - /// - /// default - /// - /// - /// - public DapperExtensionsQueryOptions() - { - Op = Operator.Eq; - Not = false; - UseColumnPrefix = true; - Function = DatabaseFunction.None; - } + Op = Operator.Eq; + Not = false; + UseColumnPrefix = true; + Function = DatabaseFunction.None; + } - /// - /// Gets or sets the predicate of the . - /// - /// The predicate of the .. - public Expression> Predicate { get; set; } + /// + /// Gets or sets the predicate of the . + /// + /// The predicate of the .. + public Expression> Predicate { get; set; } - /// - /// Gets or sets the comparison operator of the . - /// - /// The comparison operator of the . - public Operator Op { get; set; } + /// + /// Gets or sets the comparison operator of the . + /// + /// The comparison operator of the . + public Operator Op { get; set; } - /// - /// Gets or sets the value of the . - /// - /// The value of the . - public object Value { get; set; } + /// + /// Gets or sets the value of the . + /// + /// The value of the . + public object Value { get; set; } - /// - /// Gets or sets a value that inverts the comparision operator of the . - /// - /// true to invert the value of ; otherwise, false. - public bool Not { get; set; } + /// + /// Gets or sets a value that inverts the comparision operator of the . + /// + /// true to invert the value of ; otherwise, false. + public bool Not { get; set; } - /// - /// Gets or sets a value indicating whether generated SQL should have column prefix of the . - /// - /// true if to include column prefix on generated SQL; otherwise, false. - public bool UseColumnPrefix { get; set; } + /// + /// Gets or sets a value indicating whether generated SQL should have column prefix of the . + /// + /// true if to include column prefix on generated SQL; otherwise, false. + public bool UseColumnPrefix { get; set; } - /// - /// Gets or sets the database function of the . - /// - /// The database function of the . - public DatabaseFunction Function { get; set; } + /// + /// Gets or sets the database function of the . + /// + /// The database function of the . + public DatabaseFunction Function { get; set; } - /// - /// Gets or sets the parameters to the of the . - /// - /// The parameters to the of the . - public string FunctionParameters { get; set; } - } + /// + /// Gets or sets the parameters to the of the . + /// + /// The parameters to the of the . + public string FunctionParameters { get; set; } } diff --git a/src/Savvyio.Extensions.DapperExtensions/Savvyio.Extensions.DapperExtensions.csproj b/src/Savvyio.Extensions.DapperExtensions/Savvyio.Extensions.DapperExtensions.csproj index 9124fb6a..c046bfdc 100644 --- a/src/Savvyio.Extensions.DapperExtensions/Savvyio.Extensions.DapperExtensions.csproj +++ b/src/Savvyio.Extensions.DapperExtensions/Savvyio.Extensions.DapperExtensions.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O core assemblies with support for Dapper and automapping of Plain Old CLR Objects (POCO) by DapperExtensions. diff --git a/src/Savvyio.Extensions.DependencyInjection.Dapper/DapperDataSource.cs b/src/Savvyio.Extensions.DependencyInjection.Dapper/DapperDataSource.cs index 22e103e4..64d8acc0 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Dapper/DapperDataSource.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Dapper/DapperDataSource.cs @@ -1,28 +1,27 @@ -using System; +using System; using Savvyio.Extensions.Dapper; -namespace Savvyio.Extensions.DependencyInjection.Dapper +namespace Savvyio.Extensions.DependencyInjection.Dapper; + +/// +/// Provides a default implementation of the interface to support the actual I/O communication with a source of data using Dapper. +/// +/// The type used to mark the implementation that this data store represents. Optimized for Microsoft Dependency Injection. +/// +/// +public class DapperDataSource : DapperDataSource, IDapperDataSource { /// - /// Provides a default implementation of the interface to support the actual I/O communication with a source of data using Dapper. + /// Initializes a new instance of the class. /// - /// The type used to mark the implementation that this data store represents. Optimized for Microsoft Dependency Injection. - /// - /// - public class DapperDataSource : DapperDataSource, IDapperDataSource + /// The used to configure this instance. + /// + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public DapperDataSource(DapperDataSourceOptions options) : base(options) { - /// - /// Initializes a new instance of the class. - /// - /// The used to configure this instance. - /// - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public DapperDataSource(DapperDataSourceOptions options) : base(options) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.Dapper/DapperDataSourceOptions.cs b/src/Savvyio.Extensions.DependencyInjection.Dapper/DapperDataSourceOptions.cs index 79edb7df..3be666a0 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Dapper/DapperDataSourceOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Dapper/DapperDataSourceOptions.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.Dapper; -namespace Savvyio.Extensions.DependencyInjection.Dapper +namespace Savvyio.Extensions.DependencyInjection.Dapper; + +/// +/// Configuration options for . +/// +/// +/// +public class DapperDataSourceOptions : DapperDataSourceOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// - /// - public class DapperDataSourceOptions : DapperDataSourceOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.Dapper/IDapperDataSource.cs b/src/Savvyio.Extensions.DependencyInjection.Dapper/IDapperDataSource.cs index 4851f58d..e22335ca 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Dapper/IDapperDataSource.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Dapper/IDapperDataSource.cs @@ -1,14 +1,13 @@ -using Savvyio.Extensions.Dapper; +using Savvyio.Extensions.Dapper; -namespace Savvyio.Extensions.DependencyInjection.Dapper +namespace Savvyio.Extensions.DependencyInjection.Dapper; + +/// +/// Defines a generic way to support multiple implementations that does the actual I/O communication with a source of data optimized for Dapper. +/// +/// The type used to mark the implementation that this data store represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IDapperDataSource : IDapperDataSource, IDataSource { - /// - /// Defines a generic way to support multiple implementations that does the actual I/O communication with a source of data optimized for Dapper. - /// - /// The type used to mark the implementation that this data store represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IDapperDataSource : IDapperDataSource, IDataSource - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.Dapper/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.Dapper/ServiceCollectionExtensions.cs index 853d00b3..30ee3575 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Dapper/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Dapper/ServiceCollectionExtensions.cs @@ -1,91 +1,90 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Savvyio.Extensions.Dapper; using Savvyio.Extensions.DependencyInjection.Data; -namespace Savvyio.Extensions.DependencyInjection.Dapper +namespace Savvyio.Extensions.DependencyInjection.Dapper; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation to the specified . /// - public static class ServiceCollectionExtensions + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddDapperDataSource(this IServiceCollection services, Action dataSourceSetup, Action serviceSetup = null) { - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddDapperDataSource(this IServiceCollection services, Action dataSourceSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddDapperDataSource(serviceSetup) - .AddConfiguredOptions(dataSourceSetup); - } + Validator.ThrowIfNull(services); + return services + .AddDapperDataSource(serviceSetup) + .AddConfiguredOptions(dataSourceSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddDapperDataSource(this IServiceCollection services, Action> dataSourceSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddDapperDataSource>(serviceSetup) - .AddConfiguredOptions(dataSourceSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddDapperDataSource(this IServiceCollection services, Action> dataSourceSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddDapperDataSource>(serviceSetup) + .AddConfiguredOptions(dataSourceSetup); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the interface to add. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional configuration calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - /// - /// - /// cannot be null. - /// - public static IServiceCollection AddDapperDataSource(this IServiceCollection services, Action setup = null) where TService : class, IDapperDataSource - { - Validator.ThrowIfNull(services); - return services.AddDataSource(setup); - } + /// + /// Adds an implementation of to the specified . + /// + /// The type of the interface to add. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional configuration calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + /// + /// + /// cannot be null. + /// + public static IServiceCollection AddDapperDataSource(this IServiceCollection services, Action setup = null) where TService : class, IDapperDataSource + { + Validator.ThrowIfNull(services); + return services.AddDataSource(setup); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the abstraction to add. - /// The type of the DTO to use. - /// The to add the service to. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddDapperDataStore(this IServiceCollection services) - where TService : DapperDataStore - where T : class - { - Validator.ThrowIfNull(services); - return services.AddDataStore(); - } + /// + /// Adds an implementation of to the specified . + /// + /// The type of the abstraction to add. + /// The type of the DTO to use. + /// The to add the service to. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddDapperDataStore(this IServiceCollection services) + where TService : DapperDataStore + where T : class + { + Validator.ThrowIfNull(services); + return services.AddDataStore(); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.DapperExtensions/DapperExtensionsDataStore.cs b/src/Savvyio.Extensions.DependencyInjection.DapperExtensions/DapperExtensionsDataStore.cs index 5fb8e1ee..731dab29 100644 --- a/src/Savvyio.Extensions.DependencyInjection.DapperExtensions/DapperExtensionsDataStore.cs +++ b/src/Savvyio.Extensions.DependencyInjection.DapperExtensions/DapperExtensionsDataStore.cs @@ -1,25 +1,24 @@ -using Savvyio.Data; +using Savvyio.Data; using Savvyio.Extensions.DapperExtensions; using Savvyio.Extensions.DependencyInjection.Dapper; using Savvyio.Extensions.DependencyInjection.Data; -namespace Savvyio.Extensions.DependencyInjection.DapperExtensions +namespace Savvyio.Extensions.DependencyInjection.DapperExtensions; + +/// +/// Provides a default implementation of the interface to support multiple implementations that is tailored for Plain Old CLR Objects (POCO) usage by DapperExtensions. +/// +/// The type of the DTO. +/// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. +/// +/// +public class DapperExtensionsDataStore : DapperExtensionsDataStore, IPersistentDataStore, TMarker> where T : class { /// - /// Provides a default implementation of the interface to support multiple implementations that is tailored for Plain Old CLR Objects (POCO) usage by DapperExtensions. + /// Initializes a new instance of the class. /// - /// The type of the DTO. - /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. - /// - /// - public class DapperExtensionsDataStore : DapperExtensionsDataStore, IPersistentDataStore, TMarker> where T : class + /// The that handles actual I/O communication with a source of data. + public DapperExtensionsDataStore(IDapperDataSource source) : base(source) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - public DapperExtensionsDataStore(IDapperDataSource source) : base(source) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.DapperExtensions/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.DapperExtensions/ServiceCollectionExtensions.cs index 59bb291f..03d60f9f 100644 --- a/src/Savvyio.Extensions.DependencyInjection.DapperExtensions/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.DapperExtensions/ServiceCollectionExtensions.cs @@ -1,45 +1,44 @@ -using System; +using System; using Cuemon; using Microsoft.Extensions.DependencyInjection; using Savvyio.Extensions.DapperExtensions; using Savvyio.Extensions.DependencyInjection.Data; -namespace Savvyio.Extensions.DependencyInjection.DapperExtensions +namespace Savvyio.Extensions.DependencyInjection.DapperExtensions; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation of to the specified . /// - public static class ServiceCollectionExtensions + /// The type of the DTO to use. + /// The to add the service to. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddDapperExtensionsDataStore(this IServiceCollection services) where T : class { - /// - /// Adds an implementation of to the specified . - /// - /// The type of the DTO to use. - /// The to add the service to. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddDapperExtensionsDataStore(this IServiceCollection services) where T : class - { - Validator.ThrowIfNull(services); - return services.AddDataStore, T, DapperExtensionsQueryOptions>(); - } + Validator.ThrowIfNull(services); + return services.AddDataStore, T, DapperExtensionsQueryOptions>(); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the DTO to use. - /// The type used to mark the implementation that this data access object represents. Optimized for DapperExtensions. - /// The to add the service to. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddDapperExtensionsDataStore(this IServiceCollection services) where T : class - { - Validator.ThrowIfNull(services); - return services.AddDataStore, T, DapperExtensionsQueryOptions>(); - } + /// + /// Adds an implementation of to the specified . + /// + /// The type of the DTO to use. + /// The type used to mark the implementation that this data access object represents. Optimized for DapperExtensions. + /// The to add the service to. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddDapperExtensionsDataStore(this IServiceCollection services) where T : class + { + Validator.ThrowIfNull(services); + return services.AddDataStore, T, DapperExtensionsQueryOptions>(); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.Domain/EventSourcing/ITracedAggregateRepository.cs b/src/Savvyio.Extensions.DependencyInjection.Domain/EventSourcing/ITracedAggregateRepository.cs index 25fceb42..ffcc37e1 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Domain/EventSourcing/ITracedAggregateRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Domain/EventSourcing/ITracedAggregateRepository.cs @@ -1,17 +1,16 @@ -using Savvyio.Domain.EventSourcing; +using Savvyio.Domain.EventSourcing; -namespace Savvyio.Extensions.DependencyInjection.Domain.EventSourcing +namespace Savvyio.Extensions.DependencyInjection.Domain.EventSourcing; + +/// +/// Defines a generic way to support multiple implementations traced read- and writable repositories (CRud) that is optimized for Domain Driven Design. +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +/// +public interface ITracedAggregateRepository : ITracedAggregateRepository, IReadableRepository, IWritableRepository where TEntity : class, ITracedAggregateRoot { - /// - /// Defines a generic way to support multiple implementations traced read- and writable repositories (CRud) that is optimized for Domain Driven Design. - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - /// - public interface ITracedAggregateRepository : ITracedAggregateRepository, IReadableRepository, IWritableRepository where TEntity : class, ITracedAggregateRoot - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.Domain/EventSourcing/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.Domain/EventSourcing/ServiceCollectionExtensions.cs index 47c0ca94..d01bf000 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Domain/EventSourcing/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Domain/EventSourcing/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.DependencyInjection; @@ -6,41 +6,40 @@ using Savvyio.Domain; using Savvyio.Domain.EventSourcing; -namespace Savvyio.Extensions.DependencyInjection.Domain.EventSourcing +namespace Savvyio.Extensions.DependencyInjection.Domain.EventSourcing; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation of to the specified . /// - public static class ServiceCollectionExtensions + /// The type of the interface to add. + /// The type of the entity. + /// The type of the key that uniquely identifies the entity. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional configuration calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddTracedAggregateRepository(this IServiceCollection services, Action setup = null) + where TService : class, ITracedAggregateRepository + where TEntity : class, ITracedAggregateRoot { - /// - /// Adds an implementation of to the specified . - /// - /// The type of the interface to add. - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional configuration calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - /// - /// - /// - /// - /// - public static IServiceCollection AddTracedAggregateRepository(this IServiceCollection services, Action setup = null) - where TService : class, ITracedAggregateRepository - where TEntity : class, ITracedAggregateRoot + Validator.ThrowIfNull(services); + var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Scoped)).Configure(); + return services.Add(o => { - Validator.ThrowIfNull(services); - var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Scoped)).Configure(); - return services.Add(o => - { - o.Lifetime = options.Lifetime; - o.NestedTypePredicate = type => type.HasInterfaces(typeof(ITracedAggregateRepository<,>), typeof(IWritableRepository<,>), typeof(IReadableRepository<,>)); - }); - } + o.Lifetime = options.Lifetime; + o.NestedTypePredicate = type => type.HasInterfaces(typeof(ITracedAggregateRepository<,>), typeof(IWritableRepository<,>), typeof(IReadableRepository<,>)); + }); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.Domain/GlobalSuppressions.cs b/src/Savvyio.Extensions.DependencyInjection.Domain/GlobalSuppressions.cs index c3f38bce..0e40e233 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Domain/GlobalSuppressions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Domain/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. diff --git a/src/Savvyio.Extensions.DependencyInjection.Domain/IAggregateRepository.cs b/src/Savvyio.Extensions.DependencyInjection.Domain/IAggregateRepository.cs index 2c33f6b9..a805f2c9 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Domain/IAggregateRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Domain/IAggregateRepository.cs @@ -1,16 +1,15 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Defines a generic way to support multiple implementations of persistent repositories (CRUD) that is optimized for Domain Driven Design. +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IAggregateRepository : IAggregateRepository, IPersistentRepository where TEntity : class, IAggregateRoot { - /// - /// Defines a generic way to support multiple implementations of persistent repositories (CRUD) that is optimized for Domain Driven Design. - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IAggregateRepository : IAggregateRepository, IPersistentRepository where TEntity : class, IAggregateRoot - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.Domain/Savvyio.Extensions.DependencyInjection.Domain.csproj b/src/Savvyio.Extensions.DependencyInjection.Domain/Savvyio.Extensions.DependencyInjection.Domain.csproj index 876601a6..d12a265a 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Domain/Savvyio.Extensions.DependencyInjection.Domain.csproj +++ b/src/Savvyio.Extensions.DependencyInjection.Domain/Savvyio.Extensions.DependencyInjection.Domain.csproj @@ -1,5 +1,5 @@ - - + + Extend the Savvy I/O support for Microsoft Dependency Injection - optimized for DDD (Domain Driven Design). ddd domain-driven-design event-sourcing es aggregate-repository traced-aggregate-repository di depedency-injection marker data-store dao data-access-object diff --git a/src/Savvyio.Extensions.DependencyInjection.Domain/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.Domain/ServiceCollectionExtensions.cs index c4ba05d1..23120110 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Domain/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Domain/ServiceCollectionExtensions.cs @@ -1,46 +1,45 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation of to the specified . /// - public static class ServiceCollectionExtensions + /// The type of the to add. + /// The type of the entity. + /// The type of the key that uniquely identifies the entity. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional configuration calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + /// + /// + /// + /// + /// cannot be null. + /// + public static IServiceCollection AddAggregateRepository(this IServiceCollection services, Action setup = null) + where TService : class, IAggregateRepository + where TEntity : class, IAggregateRoot { - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional configuration calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - /// - /// - /// - /// - /// cannot be null. - /// - public static IServiceCollection AddAggregateRepository(this IServiceCollection services, Action setup = null) - where TService : class, IAggregateRepository - where TEntity : class, IAggregateRoot + Validator.ThrowIfNull(services); + var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Scoped)).Configure(); + return services.Add(o => { - Validator.ThrowIfNull(services); - var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Scoped)).Configure(); - return services.Add(o => - { - o.Lifetime = options.Lifetime; - o.NestedTypePredicate = type => type.HasInterfaces(typeof(IAggregateRepository<,>)); - }).AddRepository(setup); - } + o.Lifetime = options.Lifetime; + o.NestedTypePredicate = type => type.HasInterfaces(typeof(IAggregateRepository<,>)); + }).AddRepository(setup); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/EfCoreTracedAggregateRepository.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/EfCoreTracedAggregateRepository.cs index 3680ffa6..5e638a8c 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/EfCoreTracedAggregateRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/EfCoreTracedAggregateRepository.cs @@ -1,29 +1,28 @@ -using Savvyio.Domain.EventSourcing; +using Savvyio.Domain.EventSourcing; using Savvyio.Extensions.DependencyInjection.Domain; using Savvyio.Extensions.DependencyInjection.Domain.EventSourcing; using Savvyio.Extensions.EFCore.Domain.EventSourcing; -namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing; + +/// +/// Provides an implementation of the that is optimized for Domain Driven Design and Event Sourcing. +/// +/// The type of the entity that implements the interface. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +/// +public class EfCoreTracedAggregateRepository : EfCoreTracedAggregateRepository, ITracedAggregateRepository + where TEntity : class, ITracedAggregateRoot { /// - /// Provides an implementation of the that is optimized for Domain Driven Design and Event Sourcing. + /// Initializes a new instance of the class. /// - /// The type of the entity that implements the interface. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - /// - public class EfCoreTracedAggregateRepository : EfCoreTracedAggregateRepository, ITracedAggregateRepository - where TEntity : class, ITracedAggregateRoot + /// The that handles actual I/O communication with a source of data. + /// The that is used when converting between and arbitrary data. + public EfCoreTracedAggregateRepository(IEfCoreDataSource source, IMarshaller marshaller) : base(source, marshaller) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - /// The that is used when converting between and arbitrary data. - public EfCoreTracedAggregateRepository(IEfCoreDataSource source, IMarshaller marshaller) : base(source, marshaller) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/GlobalSuppressions.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/GlobalSuppressions.cs index 5fab405b..afb154ee 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/GlobalSuppressions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.csproj b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.csproj index 8feb7577..29177d02 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.csproj +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.csproj @@ -1,5 +1,5 @@ - + Extend the Savvy I/O support for Microsoft Dependency Injection with Microsoft Entity Framework Core implementation - optimized for Domain Driven Design and Event Sourcing. event-sourcing es ddd domain-driven-design traced-aggregate-repository efcore entity-framework-core di depedency-injection marker data-store dao data-access-object repository diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/ServiceCollectionExtensions.cs index 92871961..0404fb15 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; @@ -7,50 +7,49 @@ using Savvyio.Extensions.EFCore.Domain; using Savvyio.Extensions.EFCore.Domain.EventSourcing; -namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an to the specified . /// - public static class ServiceCollectionExtensions + /// The type of the entity. + /// The type of the key that uniquely identifies the entity. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The will be type forwarded accordingly. + /// + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreTracedAggregateRepository(this IServiceCollection services, Action setup = null) where TEntity : class, ITracedAggregateRoot { - /// - /// Adds an to the specified . - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The will be type forwarded accordingly. - /// - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreTracedAggregateRepository(this IServiceCollection services, Action setup = null) where TEntity : class, ITracedAggregateRoot - { - Validator.ThrowIfNull(services); - return services.AddTracedAggregateRepository, TEntity, TKey>(setup); - } + Validator.ThrowIfNull(services); + return services.AddTracedAggregateRepository, TEntity, TKey>(setup); + } - /// - /// Adds an to the specified . - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The will be type forwarded accordingly. - /// - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreTracedAggregateRepository(this IServiceCollection services, Action setup = null) where TEntity : class, ITracedAggregateRoot - { - Validator.ThrowIfNull(services); - return services.AddTracedAggregateRepository, TEntity, TKey>(setup); - } + /// + /// Adds an to the specified . + /// + /// The type of the entity. + /// The type of the key that uniquely identifies the entity. + /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The will be type forwarded accordingly. + /// + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreTracedAggregateRepository(this IServiceCollection services, Action setup = null) where TEntity : class, ITracedAggregateRoot + { + Validator.ThrowIfNull(services); + return services.AddTracedAggregateRepository, TEntity, TKey>(setup); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/EfCoreAggregateDataSource.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/EfCoreAggregateDataSource.cs index f8c901f0..40ce16aa 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/EfCoreAggregateDataSource.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/EfCoreAggregateDataSource.cs @@ -1,22 +1,21 @@ -using Savvyio.Domain; +using Savvyio.Domain; using Savvyio.Extensions.EFCore; using Savvyio.Extensions.EFCore.Domain; -namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain +namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain; + +/// +/// Provides an implementation of the that is optimized for Domain Driven Design and the concept of Aggregate Root. +/// +/// +public class EfCoreAggregateDataSource : EfCoreAggregateDataSource, IEfCoreDataSource { /// - /// Provides an implementation of the that is optimized for Domain Driven Design and the concept of Aggregate Root. + /// Initializes a new instance of the class. /// - /// - public class EfCoreAggregateDataSource : EfCoreAggregateDataSource, IEfCoreDataSource + /// The that are responsible for raising domain events. + /// The used to configure this instance. + public EfCoreAggregateDataSource(IDomainEventDispatcher dispatcher, EfCoreDataSourceOptions options) : base(dispatcher, new EfCoreDbContext(options)) { - /// - /// Initializes a new instance of the class. - /// - /// The that are responsible for raising domain events. - /// The used to configure this instance. - public EfCoreAggregateDataSource(IDomainEventDispatcher dispatcher, EfCoreDataSourceOptions options) : base(dispatcher, new EfCoreDbContext(options)) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/EfCoreAggregateRepository.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/EfCoreAggregateRepository.cs index 3e621298..81341c52 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/EfCoreAggregateRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/EfCoreAggregateRepository.cs @@ -1,25 +1,24 @@ -using Savvyio.Domain; +using Savvyio.Domain; using Savvyio.Extensions.DependencyInjection.Domain; using Savvyio.Extensions.EFCore; -namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain +namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain; + +/// +/// Provides an implementation of the that is optimized for Domain Driven Design. +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +public class EfCoreAggregateRepository : EfCoreRepository, IAggregateRepository where TEntity : class, IAggregateRoot { /// - /// Provides an implementation of the that is optimized for Domain Driven Design. + /// Initializes a new instance of the class. /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - public class EfCoreAggregateRepository : EfCoreRepository, IAggregateRepository where TEntity : class, IAggregateRoot + /// The that handles actual I/O communication with a source of data. + public EfCoreAggregateRepository(IEfCoreDataSource source) : base(source) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - public EfCoreAggregateRepository(IEfCoreDataSource source) : base(source) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/GlobalSuppressions.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/GlobalSuppressions.cs index 75d8b0da..73f5da6b 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/GlobalSuppressions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/Savvyio.Extensions.DependencyInjection.EFCore.Domain.csproj b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/Savvyio.Extensions.DependencyInjection.EFCore.Domain.csproj index a54261c7..1aff82c1 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/Savvyio.Extensions.DependencyInjection.EFCore.Domain.csproj +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/Savvyio.Extensions.DependencyInjection.EFCore.Domain.csproj @@ -1,10 +1,10 @@ - + Extend the Savvy I/O support for Microsoft Dependency Injection with Microsoft Entity Framework Core implementation - optimized for DDD (Domain Driven Design). ddd domain-driven-design aggregate-repository efcore entity-framework-core di depedency-injection marker data-store dao data-access-object repository - + diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/ServiceCollectionExtensions.cs index d7b37061..b88f89c7 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore.Domain/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; @@ -7,88 +7,87 @@ using Savvyio.Extensions.EFCore; using Savvyio.Extensions.EFCore.Domain; -namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain +namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation to the specified . /// - public static class ServiceCollectionExtensions + /// The to extend. + /// The which need to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreAggregateDataSource(this IServiceCollection services, Action dataSourceSetup, Action serviceSetup = null) { - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The which need to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreAggregateDataSource(this IServiceCollection services, Action dataSourceSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services.AddDataSource(serviceSetup) - .AddUnitOfWork(serviceSetup) - .AddConfiguredOptions(dataSourceSetup); - } + Validator.ThrowIfNull(services); + return services.AddDataSource(serviceSetup) + .AddUnitOfWork(serviceSetup) + .AddConfiguredOptions(dataSourceSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The which need to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreAggregateDataSource(this IServiceCollection services, Action> dataSourceSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services.AddDataSource>(serviceSetup) - .AddUnitOfWork>(serviceSetup) - .AddConfiguredOptions(dataSourceSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The which need to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreAggregateDataSource(this IServiceCollection services, Action> dataSourceSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services.AddDataSource>(serviceSetup) + .AddUnitOfWork>(serviceSetup) + .AddConfiguredOptions(dataSourceSetup); + } - /// - /// Adds an to the specified . - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The to extend. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The will be type forwarded accordingly. - /// - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreAggregateRepository(this IServiceCollection services, Action setup = null) where TEntity : class, IAggregateRoot - { - Validator.ThrowIfNull(services); - return services.AddAggregateRepository, TEntity, TKey>(setup); - } + /// + /// Adds an to the specified . + /// + /// The type of the entity. + /// The type of the key that uniquely identifies the entity. + /// The to extend. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The will be type forwarded accordingly. + /// + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreAggregateRepository(this IServiceCollection services, Action setup = null) where TEntity : class, IAggregateRoot + { + Validator.ThrowIfNull(services); + return services.AddAggregateRepository, TEntity, TKey>(setup); + } - /// - /// Adds an to the specified . - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// The to extend. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The will be type forwarded accordingly. - /// - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreAggregateRepository(this IServiceCollection services, Action setup = null) where TEntity : class, IAggregateRoot - { - Validator.ThrowIfNull(services); - return services.AddAggregateRepository, TEntity, TKey>(setup); - } + /// + /// Adds an to the specified . + /// + /// The type of the entity. + /// The type of the key that uniquely identifies the entity. + /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. + /// The to extend. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The will be type forwarded accordingly. + /// + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreAggregateRepository(this IServiceCollection services, Action setup = null) where TEntity : class, IAggregateRoot + { + Validator.ThrowIfNull(services); + return services.AddAggregateRepository, TEntity, TKey>(setup); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataSource.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataSource.cs index a27c0b27..591377ed 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataSource.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataSource.cs @@ -1,21 +1,20 @@ -using Savvyio.Extensions.EFCore; +using Savvyio.Extensions.EFCore; -namespace Savvyio.Extensions.DependencyInjection.EFCore +namespace Savvyio.Extensions.DependencyInjection.EFCore; + +/// +/// Provides a default implementation of the interface to support multiple implementations that does the actual I/O communication with a source of data using Microsoft Entity Framework Core. +/// +/// The type used to mark the implementation that this data store represents. Optimized for Microsoft Dependency Injection. +/// +/// +public class EfCoreDataSource : EfCoreDataSource, IEfCoreDataSource { /// - /// Provides a default implementation of the interface to support multiple implementations that does the actual I/O communication with a source of data using Microsoft Entity Framework Core. + /// Initializes a new instance of the class. /// - /// The type used to mark the implementation that this data store represents. Optimized for Microsoft Dependency Injection. - /// - /// - public class EfCoreDataSource : EfCoreDataSource, IEfCoreDataSource + /// The used to configure this instance. + public EfCoreDataSource(EfCoreDataSourceOptions options) : base(new EfCoreDbContext(options)) { - /// - /// Initializes a new instance of the class. - /// - /// The used to configure this instance. - public EfCoreDataSource(EfCoreDataSourceOptions options) : base(new EfCoreDbContext(options)) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataSourceOptions.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataSourceOptions.cs index 8befa7c7..60495b45 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataSourceOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataSourceOptions.cs @@ -1,15 +1,14 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.EFCore; -namespace Savvyio.Extensions.DependencyInjection.EFCore +namespace Savvyio.Extensions.DependencyInjection.EFCore; + +/// +/// Configuration options for . +/// +/// The type used to mark the implementation that these options represents. Optimized for Microsoft Dependency Injection. +/// +/// +public class EfCoreDataSourceOptions : EfCoreDataSourceOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// The type used to mark the implementation that these options represents. Optimized for Microsoft Dependency Injection. - /// - /// - public class EfCoreDataSourceOptions : EfCoreDataSourceOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataStore.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataStore.cs index e4d9a4c4..be187f52 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataStore.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDataStore.cs @@ -1,23 +1,22 @@ -using Savvyio.Extensions.DependencyInjection.Data; +using Savvyio.Extensions.DependencyInjection.Data; using Savvyio.Extensions.EFCore; -namespace Savvyio.Extensions.DependencyInjection.EFCore +namespace Savvyio.Extensions.DependencyInjection.EFCore; + +/// +/// Provides a default implementation of the interface to support multiple implementations that serves as an abstraction layer before the actual I/O communication with a source of data using Microsoft Entity Framework Core. +/// +/// The type of the DTO. +/// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. +/// +/// +public class EfCoreDataStore : EfCoreDataStore, IPersistentDataStore, TMarker> where T : class { /// - /// Provides a default implementation of the interface to support multiple implementations that serves as an abstraction layer before the actual I/O communication with a source of data using Microsoft Entity Framework Core. + /// Initializes a new instance of the class. /// - /// The type of the DTO. - /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. - /// - /// - public class EfCoreDataStore : EfCoreDataStore, IPersistentDataStore, TMarker> where T : class + /// The that handles actual I/O communication with a source of data. + public EfCoreDataStore(IEfCoreDataSource source) : base(source) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - public EfCoreDataStore(IEfCoreDataSource source) : base(source) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDbContext.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDbContext.cs index 8ef9e914..75a06349 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDbContext.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreDbContext.cs @@ -1,23 +1,22 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using Savvyio.Extensions.EFCore; -namespace Savvyio.Extensions.DependencyInjection.EFCore +namespace Savvyio.Extensions.DependencyInjection.EFCore; + +/// +/// Provides a default implementation of the class to support Savvy I/O extensions of Microsoft Entity Framework Core in multiple implementations. +/// +/// The type used to mark the implementation that this context represents. Optimized for Microsoft Dependency Injection. +/// +/// +public class EfCoreDbContext : EfCoreDbContext, IDependencyInjectionMarker { /// - /// Provides a default implementation of the class to support Savvy I/O extensions of Microsoft Entity Framework Core in multiple implementations. + /// Initializes a new instance of the class. /// - /// The type used to mark the implementation that this context represents. Optimized for Microsoft Dependency Injection. - /// - /// - public class EfCoreDbContext : EfCoreDbContext, IDependencyInjectionMarker + /// The used to configure this instance. + public EfCoreDbContext(EfCoreDataSourceOptions options) : base(options) { - /// - /// Initializes a new instance of the class. - /// - /// The used to configure this instance. - public EfCoreDbContext(EfCoreDataSourceOptions options) : base(options) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreRepository.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreRepository.cs index d20191c7..2db17f71 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore/EfCoreRepository.cs @@ -1,24 +1,23 @@ -using Savvyio.Extensions.DependencyInjection.Domain; +using Savvyio.Extensions.DependencyInjection.Domain; using Savvyio.Extensions.EFCore; -namespace Savvyio.Extensions.DependencyInjection.EFCore +namespace Savvyio.Extensions.DependencyInjection.EFCore; + +/// +/// Provides a default implementation of the interface to support multiple implementations that serves as an abstraction layer before the actual I/O communication with a source of data using Microsoft Entity Framework Core. +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +public class EfCoreRepository : EfCoreRepository, IPersistentRepository where TEntity : class, IIdentity { /// - /// Provides a default implementation of the interface to support multiple implementations that serves as an abstraction layer before the actual I/O communication with a source of data using Microsoft Entity Framework Core. + /// Initializes a new instance of the class. /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - public class EfCoreRepository : EfCoreRepository, IPersistentRepository where TEntity : class, IIdentity + /// The that handles actual I/O communication with a source of data. + public EfCoreRepository(IEfCoreDataSource source) : base(source) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - public EfCoreRepository(IEfCoreDataSource source) : base(source) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore/GlobalSuppressions.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore/GlobalSuppressions.cs index 9561458e..4b516b15 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore/GlobalSuppressions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore/IEfCoreDataSource.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore/IEfCoreDataSource.cs index 5772af4f..ad8d0adb 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore/IEfCoreDataSource.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore/IEfCoreDataSource.cs @@ -1,15 +1,14 @@ -using Savvyio.Extensions.DependencyInjection.Domain; +using Savvyio.Extensions.DependencyInjection.Domain; using Savvyio.Extensions.EFCore; -namespace Savvyio.Extensions.DependencyInjection.EFCore +namespace Savvyio.Extensions.DependencyInjection.EFCore; + +/// +/// Defines a generic way to support multiple implementations that does the actual I/O communication with a source of data using Microsoft Entity Framework Core. +/// +/// The type used to mark the implementation that this data store represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IEfCoreDataSource : IEfCoreDataSource, IDataSource, IUnitOfWork { - /// - /// Defines a generic way to support multiple implementations that does the actual I/O communication with a source of data using Microsoft Entity Framework Core. - /// - /// The type used to mark the implementation that this data store represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IEfCoreDataSource : IEfCoreDataSource, IDataSource, IUnitOfWork - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore/Savvyio.Extensions.DependencyInjection.EFCore.csproj b/src/Savvyio.Extensions.DependencyInjection.EFCore/Savvyio.Extensions.DependencyInjection.EFCore.csproj index 50f8276d..35fcd197 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore/Savvyio.Extensions.DependencyInjection.EFCore.csproj +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore/Savvyio.Extensions.DependencyInjection.EFCore.csproj @@ -1,5 +1,5 @@ - + Extend the Savvy I/O support for Microsoft Dependency Injection with Microsoft Entity Framework Core implementation. efcore entity-framework-core di depedency-injection marker data-store dao data-access-object repository diff --git a/src/Savvyio.Extensions.DependencyInjection.EFCore/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.EFCore/ServiceCollectionExtensions.cs index 2025e245..d6097672 100644 --- a/src/Savvyio.Extensions.DependencyInjection.EFCore/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.EFCore/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; @@ -7,122 +7,121 @@ using Savvyio.Extensions.DependencyInjection.Domain; using Savvyio.Extensions.EFCore; -namespace Savvyio.Extensions.DependencyInjection.EFCore +namespace Savvyio.Extensions.DependencyInjection.EFCore; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation to the specified . /// - public static class ServiceCollectionExtensions + /// The to extend. + /// The which need to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreDataSource(this IServiceCollection services, Action dataSourceSetup, Action serviceSetup = null) { - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The which need to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreDataSource(this IServiceCollection services, Action dataSourceSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services.AddDataSource(serviceSetup) - .AddUnitOfWork(serviceSetup) - .AddConfiguredOptions(dataSourceSetup); - } + Validator.ThrowIfNull(services); + return services.AddDataSource(serviceSetup) + .AddUnitOfWork(serviceSetup) + .AddConfiguredOptions(dataSourceSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The type used to mark the implementation that this data source represents. Optimized for Microsoft Dependency Injection. - /// The to extend. - /// The which need to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreDataSource(this IServiceCollection services, Action> dataSourceSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services.AddDataSource>(serviceSetup) - .AddUnitOfWork>(serviceSetup) - .AddConfiguredOptions(dataSourceSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The type used to mark the implementation that this data source represents. Optimized for Microsoft Dependency Injection. + /// The to extend. + /// The which need to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreDataSource(this IServiceCollection services, Action> dataSourceSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services.AddDataSource>(serviceSetup) + .AddUnitOfWork>(serviceSetup) + .AddConfiguredOptions(dataSourceSetup); + } - /// - /// Adds an to the specified . - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The will be type forwarded to: , , and . - /// - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreRepository(this IServiceCollection services, Action setup = null) where TEntity : class, IIdentity - { - Validator.ThrowIfNull(services); - return services.AddRepository, TEntity, TKey>(setup); - } + /// + /// Adds an to the specified . + /// + /// The type of the entity. + /// The type of the key that uniquely identifies the entity. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The will be type forwarded to: , , and . + /// + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreRepository(this IServiceCollection services, Action setup = null) where TEntity : class, IIdentity + { + Validator.ThrowIfNull(services); + return services.AddRepository, TEntity, TKey>(setup); + } - /// - /// Adds an to the specified . - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The will be type forwarded to: , , and . - /// - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreRepository(this IServiceCollection services, Action setup = null) where TEntity : class, IIdentity - { - Validator.ThrowIfNull(services); - return services.AddRepository, TEntity, TKey>(setup); - } + /// + /// Adds an to the specified . + /// + /// The type of the entity. + /// The type of the key that uniquely identifies the entity. + /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The will be type forwarded to: , , and . + /// + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreRepository(this IServiceCollection services, Action setup = null) where TEntity : class, IIdentity + { + Validator.ThrowIfNull(services); + return services.AddRepository, TEntity, TKey>(setup); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the DTO. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreDataStore(this IServiceCollection services, Action setup = null) where T : class - { - Validator.ThrowIfNull(services); - return services.AddDataStore, T, EfCoreQueryOptions>(setup); - } + /// + /// Adds an implementation of to the specified . + /// + /// The type of the DTO. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreDataStore(this IServiceCollection services, Action setup = null) where T : class + { + Validator.ThrowIfNull(services); + return services.AddDataStore, T, EfCoreQueryOptions>(setup); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the DTO. - /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddEfCoreDataStore(this IServiceCollection services, Action setup = null) where T : class - { - Validator.ThrowIfNull(services); - return services.AddDataStore, T, EfCoreQueryOptions>(setup); - } + /// + /// Adds an implementation of to the specified . + /// + /// The type of the DTO. + /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddEfCoreDataStore(this IServiceCollection services, Action setup = null) where T : class + { + Validator.ThrowIfNull(services); + return services.AddDataStore, T, EfCoreQueryOptions>(setup); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.NATS/Commands/NatsCommandQueue.cs b/src/Savvyio.Extensions.DependencyInjection.NATS/Commands/NatsCommandQueue.cs index 7b8580f1..950bf441 100644 --- a/src/Savvyio.Extensions.DependencyInjection.NATS/Commands/NatsCommandQueue.cs +++ b/src/Savvyio.Extensions.DependencyInjection.NATS/Commands/NatsCommandQueue.cs @@ -1,32 +1,31 @@ -using System; +using System; using Savvyio.Commands; using Savvyio.Extensions.DependencyInjection.Messaging; using Savvyio.Extensions.NATS; using Savvyio.Extensions.NATS.Commands; -namespace Savvyio.Extensions.DependencyInjection.NATS.Commands +namespace Savvyio.Extensions.DependencyInjection.NATS.Commands; + +/// +/// Provides a default implementation of the class for messages holding an implementation. +/// +/// +/// +public class NatsCommandQueue : NatsCommandQueue, IPointToPointChannel { /// - /// Provides a default implementation of the class for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - /// - public class NatsCommandQueue : NatsCommandQueue, IPointToPointChannel + /// The marshaller used for serializing and deserializing messages. + /// The options used to configure the NATS command queue. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public NatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The options used to configure the NATS command queue. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public NatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options) : base(marshaller, options) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.NATS/Commands/NatsCommandQueueOptions.cs b/src/Savvyio.Extensions.DependencyInjection.NATS/Commands/NatsCommandQueueOptions.cs index d92ae34b..63dfbe5e 100644 --- a/src/Savvyio.Extensions.DependencyInjection.NATS/Commands/NatsCommandQueueOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.NATS/Commands/NatsCommandQueueOptions.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.NATS.Commands; -namespace Savvyio.Extensions.DependencyInjection.NATS.Commands +namespace Savvyio.Extensions.DependencyInjection.NATS.Commands; + +/// +/// Configuration options for . +/// +/// +/// +public class NatsCommandQueueOptions : NatsCommandQueueOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// - /// - public class NatsCommandQueueOptions : NatsCommandQueueOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.NATS/EventDriven/NatsEventBus.cs b/src/Savvyio.Extensions.DependencyInjection.NATS/EventDriven/NatsEventBus.cs index 42495450..4737e3e1 100644 --- a/src/Savvyio.Extensions.DependencyInjection.NATS/EventDriven/NatsEventBus.cs +++ b/src/Savvyio.Extensions.DependencyInjection.NATS/EventDriven/NatsEventBus.cs @@ -1,33 +1,32 @@ -using System; +using System; using Savvyio.EventDriven; using Savvyio.Extensions.DependencyInjection.Messaging; using Savvyio.Extensions.NATS; using Savvyio.Extensions.NATS.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.NATS.EventDriven +namespace Savvyio.Extensions.DependencyInjection.NATS.EventDriven; + +/// +/// Provides a default implementation of the class for messages holding an implementation. +/// +/// +/// +public class NatsEventBus : NatsEventBus, IPublishSubscribeChannel { + /// - /// Provides a default implementation of the class for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - /// - public class NatsEventBus : NatsEventBus, IPublishSubscribeChannel + /// The marshaller used for serializing and deserializing messages. + /// The options used to configure the NATS event bus. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public NatsEventBus(IMarshaller marshaller, NatsEventBusOptions options) : base(marshaller, options) { - - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The options used to configure the NATS event bus. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public NatsEventBus(IMarshaller marshaller, NatsEventBusOptions options) : base(marshaller, options) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.NATS/EventDriven/NatsEventBusOptions.cs b/src/Savvyio.Extensions.DependencyInjection.NATS/EventDriven/NatsEventBusOptions.cs index 12a6f123..572c90c7 100644 --- a/src/Savvyio.Extensions.DependencyInjection.NATS/EventDriven/NatsEventBusOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.NATS/EventDriven/NatsEventBusOptions.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.NATS.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.NATS.EventDriven +namespace Savvyio.Extensions.DependencyInjection.NATS.EventDriven; + +/// +/// Configuration options for . +/// +/// +/// +public class NatsEventBusOptions : NatsEventBusOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// - /// - public class NatsEventBusOptions : NatsEventBusOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.NATS/Savvyio.Extensions.DependencyInjection.NATS.csproj b/src/Savvyio.Extensions.DependencyInjection.NATS/Savvyio.Extensions.DependencyInjection.NATS.csproj index 9ed94a20..c5b1bdcd 100644 --- a/src/Savvyio.Extensions.DependencyInjection.NATS/Savvyio.Extensions.DependencyInjection.NATS.csproj +++ b/src/Savvyio.Extensions.DependencyInjection.NATS/Savvyio.Extensions.DependencyInjection.NATS.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O support for Microsoft Dependency Injection with NATS Work-Queue Stream and NATS Core Publish-Subscribe implementations. diff --git a/src/Savvyio.Extensions.DependencyInjection.NATS/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.NATS/ServiceCollectionExtensions.cs index 9dbb1066..f9c59709 100644 --- a/src/Savvyio.Extensions.DependencyInjection.NATS/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.NATS/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Savvyio.Extensions.DependencyInjection.Messaging; using System; @@ -10,87 +10,86 @@ using Savvyio.Extensions.NATS.Commands; using Savvyio.Extensions.NATS.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.NATS +namespace Savvyio.Extensions.DependencyInjection.NATS; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation to the specified . /// - public static class ServiceCollectionExtensions + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddNatsCommandQueue(this IServiceCollection services, Action natsSetup, Action serviceSetup = null) { - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddNatsCommandQueue(this IServiceCollection services, Action natsSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageQueue(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(natsSetup); - } + Validator.ThrowIfNull(services); + return services + .AddMessageQueue(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(natsSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddNatsCommandQueue(this IServiceCollection services, Action> natsSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageQueue, ICommand>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(natsSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddNatsCommandQueue(this IServiceCollection services, Action> natsSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageQueue, ICommand>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(natsSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddNatsEventBus(this IServiceCollection services, Action natsSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageBus(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(natsSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddNatsEventBus(this IServiceCollection services, Action natsSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageBus(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(natsSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddNatsEventBus(this IServiceCollection services, Action> natsSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageBus, IIntegrationEvent>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(natsSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddNatsEventBus(this IServiceCollection services, Action> natsSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageBus, IIntegrationEvent>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(natsSetup); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.csproj b/src/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.csproj index a2fffda6..070f2d5d 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.csproj +++ b/src/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O support for Microsoft Dependency Injection with support for Newtonsoft JSON implementations. diff --git a/src/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/ServiceCollectionExtensions.cs index fdec1af0..dcd4a101 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Codebelt.Extensions.Newtonsoft.Json.Formatters; using Cuemon; using Cuemon.Extensions.DependencyInjection; @@ -6,30 +6,29 @@ using Newtonsoft.Json; using Savvyio.Extensions.Newtonsoft.Json; -namespace Savvyio.Extensions.DependencyInjection.Newtonsoft.Json +namespace Savvyio.Extensions.DependencyInjection.Newtonsoft.Json; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation to the specified . /// - public static class ServiceCollectionExtensions + /// The to extend. + /// The which may be configured. Default is optimized for messaging. + /// The which may be configured. Default is . + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddNewtonsoftJsonMarshaller(this IServiceCollection services, Action jsonSetup = null, Action serviceSetup = null) { - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The which may be configured. Default is optimized for messaging. - /// The which may be configured. Default is . - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddNewtonsoftJsonMarshaller(this IServiceCollection services, Action jsonSetup = null, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMarshaller(serviceSetup) - .AddConfiguredOptions(jsonSetup ?? (o => o.Settings.Formatting = Formatting.None)); - } + Validator.ThrowIfNull(services); + return services + .AddMarshaller(serviceSetup) + .AddConfiguredOptions(jsonSetup ?? (o => o.Settings.Formatting = Formatting.None)); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/AzureQueueOptions.cs b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/AzureQueueOptions.cs index a6808ab0..2dcbcce4 100644 --- a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/AzureQueueOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/AzureQueueOptions.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.QueueStorage; -namespace Savvyio.Extensions.DependencyInjection.QueueStorage +namespace Savvyio.Extensions.DependencyInjection.QueueStorage; + +/// +/// Configuration options for . +/// +/// +/// +public class AzureQueueOptions : AzureQueueOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// - /// - public class AzureQueueOptions : AzureQueueOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/Commands/AzureCommandQueue.cs b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/Commands/AzureCommandQueue.cs index f54cc126..6a80a320 100644 --- a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/Commands/AzureCommandQueue.cs +++ b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/Commands/AzureCommandQueue.cs @@ -1,31 +1,30 @@ -using System; +using System; using Savvyio.Commands; using Savvyio.Extensions.DependencyInjection.Messaging; using Savvyio.Extensions.QueueStorage.Commands; -namespace Savvyio.Extensions.DependencyInjection.QueueStorage.Commands +namespace Savvyio.Extensions.DependencyInjection.QueueStorage.Commands; + +/// +/// Provides a default implementation of the class that is optimized for Dependency Injection. +/// +/// +/// +public class AzureCommandQueue : AzureCommandQueue, IPointToPointChannel { /// - /// Provides a default implementation of the class that is optimized for Dependency Injection. + /// Initializes a new instance of the class. /// - /// - /// - public class AzureCommandQueue : AzureCommandQueue, IPointToPointChannel + /// The that is used when converting implementations to messages. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public AzureCommandQueue(IMarshaller marshaller, AzureQueueOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The that is used when converting implementations to messages. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public AzureCommandQueue(IMarshaller marshaller, AzureQueueOptions options) : base(marshaller, options) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/EventDriven/AzureEventBus.cs b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/EventDriven/AzureEventBus.cs index c2a81d87..48682df0 100644 --- a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/EventDriven/AzureEventBus.cs +++ b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/EventDriven/AzureEventBus.cs @@ -1,32 +1,31 @@ -using System; +using System; using Savvyio.EventDriven; using Savvyio.Extensions.DependencyInjection.Messaging; using Savvyio.Extensions.QueueStorage.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.QueueStorage.EventDriven +namespace Savvyio.Extensions.DependencyInjection.QueueStorage.EventDriven; + +/// +/// Provides a default implementation of the class that is optimized for Dependency Injection. +/// +/// +/// +public class AzureEventBus : AzureEventBus, IPublishSubscribeChannel { /// - /// Provides a default implementation of the class that is optimized for Dependency Injection. + /// Initializes a new instance of the class. /// - /// - /// - public class AzureEventBus : AzureEventBus, IPublishSubscribeChannel + /// The that is used when converting implementations to messages. + /// The used to configure this instance. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public AzureEventBus(IMarshaller marshaller, AzureQueueOptions azureQueueOptions, AzureEventBusOptions azureEventBusOptions) : base(marshaller, azureQueueOptions, azureEventBusOptions) { - /// - /// Initializes a new instance of the class. - /// - /// The that is used when converting implementations to messages. - /// The used to configure this instance. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public AzureEventBus(IMarshaller marshaller, AzureQueueOptions azureQueueOptions, AzureEventBusOptions azureEventBusOptions) : base(marshaller, azureQueueOptions, azureEventBusOptions) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/EventDriven/AzureEventBusOptions.cs b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/EventDriven/AzureEventBusOptions.cs index d3b9c04a..4461b54e 100644 --- a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/EventDriven/AzureEventBusOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/EventDriven/AzureEventBusOptions.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.QueueStorage.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.QueueStorage.EventDriven +namespace Savvyio.Extensions.DependencyInjection.QueueStorage.EventDriven; + +/// +/// Configuration options for . +/// +/// +/// +public class AzureEventBusOptions : AzureEventBusOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// - /// - public class AzureEventBusOptions : AzureEventBusOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/Savvyio.Extensions.DependencyInjection.QueueStorage.csproj b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/Savvyio.Extensions.DependencyInjection.QueueStorage.csproj index f615606a..e28a5f2a 100644 --- a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/Savvyio.Extensions.DependencyInjection.QueueStorage.csproj +++ b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/Savvyio.Extensions.DependencyInjection.QueueStorage.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O support for Microsoft Dependency Injection with Azure Queue Storage paired with Azure Event Grid implementations. diff --git a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/ServiceCollectionExtensions.cs index 89ad7028..121a70f8 100644 --- a/src/Savvyio.Extensions.DependencyInjection.QueueStorage/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.QueueStorage/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; @@ -11,91 +11,90 @@ using Savvyio.Extensions.QueueStorage.Commands; using Savvyio.Extensions.QueueStorage.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.QueueStorage +namespace Savvyio.Extensions.DependencyInjection.QueueStorage; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation to the specified . /// - public static class ServiceCollectionExtensions + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddAzureCommandQueue(this IServiceCollection services, Action azureQueueSetup, Action serviceSetup = null) { - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddAzureCommandQueue(this IServiceCollection services, Action azureQueueSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageQueue(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(azureQueueSetup); - } + Validator.ThrowIfNull(services); + return services + .AddMessageQueue(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(azureQueueSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddAzureCommandQueue(this IServiceCollection services, Action> azureQueueSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageQueue, ICommand>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(azureQueueSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddAzureCommandQueue(this IServiceCollection services, Action> azureQueueSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageQueue, ICommand>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(azureQueueSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddAzureEventBus(this IServiceCollection services, Action azureQueueSetup, Action azureEventBusSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageBus(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(azureQueueSetup) - .AddConfiguredOptions(azureEventBusSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddAzureEventBus(this IServiceCollection services, Action azureQueueSetup, Action azureEventBusSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageBus(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(azureQueueSetup) + .AddConfiguredOptions(azureEventBusSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddAzureEventBus(this IServiceCollection services, Action> azureQueueSetup, Action> azureEventBusSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageBus, IIntegrationEvent>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(azureQueueSetup) - .AddConfiguredOptions(azureEventBusSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddAzureEventBus(this IServiceCollection services, Action> azureQueueSetup, Action> azureEventBusSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageBus, IIntegrationEvent>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(azureQueueSetup) + .AddConfiguredOptions(azureEventBusSetup); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Commands/RabbitMqCommandQueue.cs b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Commands/RabbitMqCommandQueue.cs index 5dc12e57..a82f9fcf 100644 --- a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Commands/RabbitMqCommandQueue.cs +++ b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Commands/RabbitMqCommandQueue.cs @@ -1,32 +1,31 @@ -using System; +using System; using Savvyio.Commands; using Savvyio.Extensions.DependencyInjection.Messaging; using Savvyio.Extensions.RabbitMQ; using Savvyio.Extensions.RabbitMQ.Commands; -namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.Commands +namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.Commands; + +/// +/// Provides a default implementation of the class for messages holding an implementation. +/// +/// +/// +public class RabbitMqCommandQueue : RabbitMqCommandQueue, IPointToPointChannel { /// - /// Provides a default implementation of the class for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - /// - public class RabbitMqCommandQueue : RabbitMqCommandQueue, IPointToPointChannel + /// The marshaller used for serializing and deserializing messages. + /// The options used to configure the RabbitMQ command queue. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public RabbitMqCommandQueue(IMarshaller marshaller, RabbitMqCommandQueueOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The options used to configure the RabbitMQ command queue. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public RabbitMqCommandQueue(IMarshaller marshaller, RabbitMqCommandQueueOptions options) : base(marshaller, options) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Commands/RabbitMqCommandQueueOptions.cs b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Commands/RabbitMqCommandQueueOptions.cs index 0c7acf79..ff8c13a8 100644 --- a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Commands/RabbitMqCommandQueueOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Commands/RabbitMqCommandQueueOptions.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.RabbitMQ.Commands; -namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.Commands +namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.Commands; + +/// +/// Configuration options for . +/// +/// +/// +public class RabbitMqCommandQueueOptions : RabbitMqCommandQueueOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// - /// - public class RabbitMqCommandQueueOptions : RabbitMqCommandQueueOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/EventDriven/RabbitMqEventBus.cs b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/EventDriven/RabbitMqEventBus.cs index 89acf907..18913050 100644 --- a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/EventDriven/RabbitMqEventBus.cs +++ b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/EventDriven/RabbitMqEventBus.cs @@ -1,33 +1,32 @@ -using Savvyio.Extensions.RabbitMQ.EventDriven; +using Savvyio.Extensions.RabbitMQ.EventDriven; using System; using Savvyio.EventDriven; using Savvyio.Extensions.DependencyInjection.Messaging; using Savvyio.Extensions.RabbitMQ; -namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.EventDriven +namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.EventDriven; + +/// +/// Provides a default implementation of the class for messages holding an implementation. +/// +/// +/// +public class RabbitMqEventBus : RabbitMqEventBus, IPublishSubscribeChannel { + /// - /// Provides a default implementation of the class for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - /// - public class RabbitMqEventBus : RabbitMqEventBus, IPublishSubscribeChannel + /// The marshaller used for serializing and deserializing messages. + /// The options used to configure the RabbitMQ event bus. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public RabbitMqEventBus(IMarshaller marshaller, RabbitMqEventBusOptions options) : base(marshaller, options) { - - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The options used to configure the RabbitMQ event bus. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public RabbitMqEventBus(IMarshaller marshaller, RabbitMqEventBusOptions options) : base(marshaller, options) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/EventDriven/RabbitMqEventBusOptions.cs b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/EventDriven/RabbitMqEventBusOptions.cs index 45ee9611..3d683ff3 100644 --- a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/EventDriven/RabbitMqEventBusOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/EventDriven/RabbitMqEventBusOptions.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.RabbitMQ.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.EventDriven +namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.EventDriven; + +/// +/// Configuration options for . +/// +/// +/// +public class RabbitMqEventBusOptions : RabbitMqEventBusOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// - /// - public class RabbitMqEventBusOptions : RabbitMqEventBusOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Savvyio.Extensions.DependencyInjection.RabbitMQ.csproj b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Savvyio.Extensions.DependencyInjection.RabbitMQ.csproj index 23f83cbb..15762055 100644 --- a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Savvyio.Extensions.DependencyInjection.RabbitMQ.csproj +++ b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/Savvyio.Extensions.DependencyInjection.RabbitMQ.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O support for Microsoft Dependency Injection with RabbitMQ Worker Queue and Publish/Subscribe implementations. diff --git a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/ServiceCollectionExtensions.cs index 20569427..ce8367ca 100644 --- a/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.RabbitMQ/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Savvyio.Extensions.DependencyInjection.Messaging; using Savvyio.Extensions.DependencyInjection.RabbitMQ.Commands; @@ -10,87 +10,86 @@ using Savvyio.Extensions.RabbitMQ.Commands; using Savvyio.Extensions.RabbitMQ.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.RabbitMQ +namespace Savvyio.Extensions.DependencyInjection.RabbitMQ; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation to the specified . /// - public static class ServiceCollectionExtensions + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddRabbitMqCommandQueue(this IServiceCollection services, Action rabbitMqSetup, Action serviceSetup = null) { - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddRabbitMqCommandQueue(this IServiceCollection services, Action rabbitMqSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageQueue(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(rabbitMqSetup); - } + Validator.ThrowIfNull(services); + return services + .AddMessageQueue(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(rabbitMqSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddRabbitMqCommandQueue(this IServiceCollection services, Action> rabbitMqSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageQueue, ICommand>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(rabbitMqSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddRabbitMqCommandQueue(this IServiceCollection services, Action> rabbitMqSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageQueue, ICommand>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(rabbitMqSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddRabbitMqEventBus(this IServiceCollection services, Action rabbitMqSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageBus(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(rabbitMqSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddRabbitMqEventBus(this IServiceCollection services, Action rabbitMqSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageBus(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(rabbitMqSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddRabbitMqEventBus(this IServiceCollection services, Action> rabbitMqSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageBus, IIntegrationEvent>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(rabbitMqSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddRabbitMqEventBus(this IServiceCollection services, Action> rabbitMqSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageBus, IIntegrationEvent>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(rabbitMqSetup); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/Commands/AmazonCommandQueue.cs b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/Commands/AmazonCommandQueue.cs index 7391234d..1c6e4235 100644 --- a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/Commands/AmazonCommandQueue.cs +++ b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/Commands/AmazonCommandQueue.cs @@ -1,32 +1,31 @@ -using System; +using System; using Savvyio.Commands; using Savvyio.Extensions.DependencyInjection.Messaging; using Savvyio.Extensions.SimpleQueueService; using Savvyio.Extensions.SimpleQueueService.Commands; -namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.Commands +namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.Commands; + +/// +/// Provides a default implementation of the class tailored for messages holding an implementation. +/// +/// +/// +public class AmazonCommandQueue : AmazonCommandQueue, IPointToPointChannel { /// - /// Provides a default implementation of the class tailored for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - /// - public class AmazonCommandQueue : AmazonCommandQueue, IPointToPointChannel + /// The that is used when converting implementations to messages. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public AmazonCommandQueue(IMarshaller marshaller, AmazonCommandQueueOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The that is used when converting implementations to messages. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public AmazonCommandQueue(IMarshaller marshaller, AmazonCommandQueueOptions options) : base(marshaller, options) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/Commands/AmazonCommandQueueOptions.cs b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/Commands/AmazonCommandQueueOptions.cs index f295e0a2..d71d84a5 100644 --- a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/Commands/AmazonCommandQueueOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/Commands/AmazonCommandQueueOptions.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.SimpleQueueService.Commands; -namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.Commands +namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.Commands; + +/// +/// Configuration options for . +/// +/// +/// +public class AmazonCommandQueueOptions : AmazonCommandQueueOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// - /// - public class AmazonCommandQueueOptions : AmazonCommandQueueOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/EventDriven/AmazonEventBus.cs b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/EventDriven/AmazonEventBus.cs index 38026c7f..14d38ee0 100644 --- a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/EventDriven/AmazonEventBus.cs +++ b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/EventDriven/AmazonEventBus.cs @@ -1,32 +1,31 @@ -using System; +using System; using Savvyio.EventDriven; using Savvyio.Extensions.DependencyInjection.Messaging; using Savvyio.Extensions.SimpleQueueService; using Savvyio.Extensions.SimpleQueueService.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.EventDriven +namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.EventDriven; + +/// +/// Provides a default implementation of the class tailored for messages holding an implementation. +/// +/// +/// +public class AmazonEventBus : AmazonEventBus, IPublishSubscribeChannel { /// - /// Provides a default implementation of the class tailored for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - /// - public class AmazonEventBus : AmazonEventBus, IPublishSubscribeChannel + /// The that is used when converting implementations to messages. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public AmazonEventBus(IMarshaller marshaller, AmazonEventBusOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The that is used when converting implementations to messages. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public AmazonEventBus(IMarshaller marshaller, AmazonEventBusOptions options) : base(marshaller, options) - { - } } } diff --git a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/EventDriven/AmazonEventBusOptions.cs b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/EventDriven/AmazonEventBusOptions.cs index 037e6aec..e53382d9 100644 --- a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/EventDriven/AmazonEventBusOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/EventDriven/AmazonEventBusOptions.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Extensions.SimpleQueueService.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.EventDriven +namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.EventDriven; + +/// +/// Configuration options for . +/// +/// +/// +public class AmazonEventBusOptions : AmazonEventBusOptions, IDependencyInjectionMarker { - /// - /// Configuration options for . - /// - /// - /// - public class AmazonEventBusOptions : AmazonEventBusOptions, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/ServiceCollectionExtensions.cs index 91dc80c2..b203c2e8 100644 --- a/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.SimpleQueueService/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; @@ -10,87 +10,86 @@ using Savvyio.Extensions.SimpleQueueService.Commands; using Savvyio.Extensions.SimpleQueueService.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService +namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation to the specified . /// - public static class ServiceCollectionExtensions + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddAmazonCommandQueue(this IServiceCollection services, Action awsSetup, Action serviceSetup = null) { - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddAmazonCommandQueue(this IServiceCollection services, Action awsSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageQueue(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(awsSetup); - } + Validator.ThrowIfNull(services); + return services + .AddMessageQueue(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(awsSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddAmazonCommandQueue(this IServiceCollection services, Action> awsSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageQueue, ICommand>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(awsSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddAmazonCommandQueue(this IServiceCollection services, Action> awsSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageQueue, ICommand>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(awsSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddAmazonEventBus(this IServiceCollection services, Action awsSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageBus(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(awsSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddAmazonEventBus(this IServiceCollection services, Action awsSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageBus(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(awsSetup); + } - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The that needs to be configured. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddAmazonEventBus(this IServiceCollection services, Action> awsSetup, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMessageBus, IIntegrationEvent>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) - .AddConfiguredOptions(awsSetup); - } + /// + /// Adds an implementation to the specified . + /// + /// The to extend. + /// The that needs to be configured. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddAmazonEventBus(this IServiceCollection services, Action> awsSetup, Action serviceSetup = null) + { + Validator.ThrowIfNull(services); + return services + .AddMessageBus, IIntegrationEvent>(serviceSetup ?? (o => o.Lifetime = ServiceLifetime.Singleton)) + .AddConfiguredOptions(awsSetup); } } diff --git a/src/Savvyio.Extensions.DependencyInjection.Text.Json/Savvyio.Extensions.DependencyInjection.Text.Json.csproj b/src/Savvyio.Extensions.DependencyInjection.Text.Json/Savvyio.Extensions.DependencyInjection.Text.Json.csproj index 1901465b..e2897d3a 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Text.Json/Savvyio.Extensions.DependencyInjection.Text.Json.csproj +++ b/src/Savvyio.Extensions.DependencyInjection.Text.Json/Savvyio.Extensions.DependencyInjection.Text.Json.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O support for Microsoft Dependency Injection with support for official built-in JSON implementations. diff --git a/src/Savvyio.Extensions.DependencyInjection.Text.Json/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection.Text.Json/ServiceCollectionExtensions.cs index 96aa6cfd..ad4bf8c6 100644 --- a/src/Savvyio.Extensions.DependencyInjection.Text.Json/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection.Text.Json/ServiceCollectionExtensions.cs @@ -1,34 +1,33 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Cuemon.Extensions.Text.Json.Formatters; using Microsoft.Extensions.DependencyInjection; using System; using Cuemon; using Savvyio.Extensions.Text.Json; -namespace Savvyio.Extensions.DependencyInjection.Text.Json +namespace Savvyio.Extensions.DependencyInjection.Text.Json; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation to the specified . /// - public static class ServiceCollectionExtensions + /// The to extend. + /// The which may be configured. Default is optimized for messaging. + /// The which may be configured. Default is . + /// A reference to so that additional calls can be chained. + /// The implementation will be type forwarded accordingly. + /// + /// cannot be null. + /// + public static IServiceCollection AddJsonMarshaller(this IServiceCollection services, Action jsonSetup = null, Action serviceSetup = null) { - /// - /// Adds an implementation to the specified . - /// - /// The to extend. - /// The which may be configured. Default is optimized for messaging. - /// The which may be configured. Default is . - /// A reference to so that additional calls can be chained. - /// The implementation will be type forwarded accordingly. - /// - /// cannot be null. - /// - public static IServiceCollection AddJsonMarshaller(this IServiceCollection services, Action jsonSetup = null, Action serviceSetup = null) - { - Validator.ThrowIfNull(services); - return services - .AddMarshaller(serviceSetup) - .AddConfiguredOptions(jsonSetup ?? (o => o.Settings.WriteIndented = false)); - } + Validator.ThrowIfNull(services); + return services + .AddMarshaller(serviceSetup) + .AddConfiguredOptions(jsonSetup ?? (o => o.Settings.WriteIndented = false)); } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Data/IDataStore.cs b/src/Savvyio.Extensions.DependencyInjection/Data/IDataStore.cs index 5a43a23d..8d09068a 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Data/IDataStore.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Data/IDataStore.cs @@ -1,16 +1,15 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Data; -namespace Savvyio.Extensions.DependencyInjection.Data +namespace Savvyio.Extensions.DependencyInjection.Data; + +/// +/// Defines a generic way to support multiple implementations of data persistence based on the Data Access Object pattern aka DAO. +/// +/// The type of the DTO. +/// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IDataStore : IDataStore, IDependencyInjectionMarker where T : class { - /// - /// Defines a generic way to support multiple implementations of data persistence based on the Data Access Object pattern aka DAO. - /// - /// The type of the DTO. - /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IDataStore : IDataStore, IDependencyInjectionMarker where T : class - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Data/IDeletableDataStore.cs b/src/Savvyio.Extensions.DependencyInjection/Data/IDeletableDataStore.cs index 46db0440..33c3888c 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Data/IDeletableDataStore.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Data/IDeletableDataStore.cs @@ -1,15 +1,14 @@ -using Savvyio.Data; +using Savvyio.Data; -namespace Savvyio.Extensions.DependencyInjection.Data +namespace Savvyio.Extensions.DependencyInjection.Data; + +/// +/// Defines a generic way to support multiple implementations of deletable data access objects (cruD). +/// +/// The type of the DTO. +/// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IDeletableDataStore : IDataStore, IDeletableDataStore where T : class { - /// - /// Defines a generic way to support multiple implementations of deletable data access objects (cruD). - /// - /// The type of the DTO. - /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IDeletableDataStore : IDataStore, IDeletableDataStore where T : class - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Data/IPersistentDataStore.cs b/src/Savvyio.Extensions.DependencyInjection/Data/IPersistentDataStore.cs index 753e3440..f7ada902 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Data/IPersistentDataStore.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Data/IPersistentDataStore.cs @@ -1,22 +1,21 @@ -using Cuemon.Threading; +using Cuemon.Threading; using Savvyio.Data; -namespace Savvyio.Extensions.DependencyInjection.Data +namespace Savvyio.Extensions.DependencyInjection.Data; + +/// +/// Defines a generic way to support multiple implementations of the actual I/O communication with a data store that is responsible of persisting data (CRUD). +/// +/// The type of the DTO. +/// The type of the options associated with this DTO. +/// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. +/// +/// +/// +/// +/// +public interface IPersistentDataStore : IPersistentDataStore, IWritableDataStore, IReadableDataStore, ISearchableDataStore, IDeletableDataStore + where T : class + where TOptions : AsyncOptions, new() { - /// - /// Defines a generic way to support multiple implementations of the actual I/O communication with a data store that is responsible of persisting data (CRUD). - /// - /// The type of the DTO. - /// The type of the options associated with this DTO. - /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. - /// - /// - /// - /// - /// - public interface IPersistentDataStore : IPersistentDataStore, IWritableDataStore, IReadableDataStore, ISearchableDataStore, IDeletableDataStore - where T : class - where TOptions : AsyncOptions, new() - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Data/IReadableDataStore.cs b/src/Savvyio.Extensions.DependencyInjection/Data/IReadableDataStore.cs index 91857352..7fd812da 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Data/IReadableDataStore.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Data/IReadableDataStore.cs @@ -1,15 +1,14 @@ -using Savvyio.Data; +using Savvyio.Data; -namespace Savvyio.Extensions.DependencyInjection.Data +namespace Savvyio.Extensions.DependencyInjection.Data; + +/// +/// Defines a generic way to support multiple implementations of readable data access objects (cRud). +/// +/// The type of the DTO. +/// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IReadableDataStore : IDataStore, IReadableDataStore where T : class { - /// - /// Defines a generic way to support multiple implementations of readable data access objects (cRud). - /// - /// The type of the DTO. - /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IReadableDataStore : IDataStore, IReadableDataStore where T : class - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Data/ISearchableDataStore.cs b/src/Savvyio.Extensions.DependencyInjection/Data/ISearchableDataStore.cs index d388074a..8d4d85d4 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Data/ISearchableDataStore.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Data/ISearchableDataStore.cs @@ -1,19 +1,18 @@ -using Cuemon.Threading; +using Cuemon.Threading; using Savvyio.Data; -namespace Savvyio.Extensions.DependencyInjection.Data +namespace Savvyio.Extensions.DependencyInjection.Data; + +/// +/// Defines a generic way to support multiple implementations of searchable data access objects (cRud). +/// +/// The type of the DTO. +/// The type of the options associated with this DTO. +/// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface ISearchableDataStore : IDataStore, ISearchableDataStore + where T : class + where TOptions : AsyncOptions, new() { - /// - /// Defines a generic way to support multiple implementations of searchable data access objects (cRud). - /// - /// The type of the DTO. - /// The type of the options associated with this DTO. - /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface ISearchableDataStore : IDataStore, ISearchableDataStore - where T : class - where TOptions : AsyncOptions, new() - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Data/IWritableDataStore.cs b/src/Savvyio.Extensions.DependencyInjection/Data/IWritableDataStore.cs index 8a4d61a2..2c367b01 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Data/IWritableDataStore.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Data/IWritableDataStore.cs @@ -1,15 +1,14 @@ -using Savvyio.Data; +using Savvyio.Data; -namespace Savvyio.Extensions.DependencyInjection.Data +namespace Savvyio.Extensions.DependencyInjection.Data; + +/// +/// Defines a generic way to support multiple implementations of writable data access objects (CrUd). +/// +/// The type of the DTO. +/// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IWritableDataStore : IDataStore, IWritableDataStore where T : class { - /// - /// Defines a generic way to support multiple implementations of writable data access objects (CrUd). - /// - /// The type of the DTO. - /// The type used to mark the implementation that this data access object represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IWritableDataStore : IDataStore, IWritableDataStore where T : class - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Data/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection/Data/ServiceCollectionExtensions.cs index dc08e3c6..ced2f918 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Data/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Data/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.DependencyInjection; @@ -6,69 +6,68 @@ using Microsoft.Extensions.DependencyInjection; using Savvyio.Data; -namespace Savvyio.Extensions.DependencyInjection.Data +namespace Savvyio.Extensions.DependencyInjection.Data; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation of to the specified . /// - public static class ServiceCollectionExtensions + /// The type of the to add. + /// The type of the DTO to use. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddDataStore(this IServiceCollection services, Action setup = null) + where TService : class, IPersistentDataStore + where T : class { - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The type of the DTO to use. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static IServiceCollection AddDataStore(this IServiceCollection services, Action setup = null) - where TService : class, IPersistentDataStore - where T : class - { - return AddDataStore(services, setup); - } + return AddDataStore(services, setup); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The type of the DTO to use. - /// The type of the options associated with . - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static IServiceCollection AddDataStore(this IServiceCollection services, Action setup = null) - where TService : class, IPersistentDataStore - where T : class - where TOptions : AsyncOptions, new() + /// + /// Adds an implementation of to the specified . + /// + /// The type of the to add. + /// The type of the DTO to use. + /// The type of the options associated with . + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddDataStore(this IServiceCollection services, Action setup = null) + where TService : class, IPersistentDataStore + where T : class + where TOptions : AsyncOptions, new() + { + Validator.ThrowIfNull(services); + var options = Patterns.Configure(setup ?? (o => o.Lifetime = ServiceLifetime.Singleton)); + return services.Add(o => { - Validator.ThrowIfNull(services); - var options = Patterns.Configure(setup ?? (o => o.Lifetime = ServiceLifetime.Singleton)); - return services.Add(o => - { - o.Lifetime = options.Lifetime; - o.NestedTypePredicate = type => type.HasInterfaces(typeof(IDataStore<>)); - }); - } + o.Lifetime = options.Lifetime; + o.NestedTypePredicate = type => type.HasInterfaces(typeof(IDataStore<>)); + }); } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Domain/IDeletableRepository.cs b/src/Savvyio.Extensions.DependencyInjection/Domain/IDeletableRepository.cs index 97770d55..d694b7a3 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Domain/IDeletableRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Domain/IDeletableRepository.cs @@ -1,16 +1,15 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Defines a generic way to support multiple implementations of deletable repositories (cruD). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IDeletableRepository : IRepository, IDeletableRepository where TEntity : class, IIdentity { - /// - /// Defines a generic way to support multiple implementations of deletable repositories (cruD). - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IDeletableRepository : IRepository, IDeletableRepository where TEntity : class, IIdentity - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Domain/IPersistentRepository.cs b/src/Savvyio.Extensions.DependencyInjection/Domain/IPersistentRepository.cs index c398a2f0..fb8eb3d3 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Domain/IPersistentRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Domain/IPersistentRepository.cs @@ -1,18 +1,17 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Defines a generic way to support multiple implementations of persistent repositories (CRUD). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +/// +/// +public interface IPersistentRepository : IPersistentRepository, IWritableRepository, IReadableRepository, ISearchableRepository, IDeletableRepository where TEntity : class, IIdentity { - /// - /// Defines a generic way to support multiple implementations of persistent repositories (CRUD). - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - /// - /// - public interface IPersistentRepository : IPersistentRepository, IWritableRepository, IReadableRepository, ISearchableRepository, IDeletableRepository where TEntity : class, IIdentity - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Domain/IReadableRepository.cs b/src/Savvyio.Extensions.DependencyInjection/Domain/IReadableRepository.cs index b2482edc..5fca806a 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Domain/IReadableRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Domain/IReadableRepository.cs @@ -1,16 +1,15 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Defines a generic way to support multiple implementations of readable repositories (cRud). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IReadableRepository : IRepository, IReadableRepository where TEntity : class, IIdentity { - /// - /// Defines a generic way to support multiple implementations of readable repositories (cRud). - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IReadableRepository : IRepository, IReadableRepository where TEntity : class, IIdentity - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Domain/IRepository.cs b/src/Savvyio.Extensions.DependencyInjection/Domain/IRepository.cs index 1ea0c9e1..cc971fa5 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Domain/IRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Domain/IRepository.cs @@ -1,17 +1,16 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Defines a generic way to support multiple implementations of persistent data access based on the Repository pattern. +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IRepository : IRepository, IDependencyInjectionMarker where TEntity : class, IIdentity { - /// - /// Defines a generic way to support multiple implementations of persistent data access based on the Repository pattern. - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IRepository : IRepository, IDependencyInjectionMarker where TEntity : class, IIdentity - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Domain/ISearchableRepository.cs b/src/Savvyio.Extensions.DependencyInjection/Domain/ISearchableRepository.cs index 851db2fd..108cd0d6 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Domain/ISearchableRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Domain/ISearchableRepository.cs @@ -1,16 +1,15 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Defines a generic way to support multiple implementations of searchable repositories (cRud). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface ISearchableRepository : IRepository, ISearchableRepository where TEntity : class, IIdentity { - /// - /// Defines a generic way to support multiple implementations of searchable repositories (cRud). - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface ISearchableRepository : IRepository, ISearchableRepository where TEntity : class, IIdentity - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Domain/IUnitOfWork.cs b/src/Savvyio.Extensions.DependencyInjection/Domain/IUnitOfWork.cs index 4f0fb72d..d7559851 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Domain/IUnitOfWork.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Domain/IUnitOfWork.cs @@ -1,15 +1,14 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Defines a generic way to support multiple implementations that bundles transactions from multiple calls into a single unit. +/// +/// The type used to mark the implementation that this UoW represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IUnitOfWork : IUnitOfWork, IDependencyInjectionMarker { - /// - /// Defines a generic way to support multiple implementations that bundles transactions from multiple calls into a single unit. - /// - /// The type used to mark the implementation that this UoW represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IUnitOfWork : IUnitOfWork, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Domain/IWritableRepository.cs b/src/Savvyio.Extensions.DependencyInjection/Domain/IWritableRepository.cs index fffec0b2..ab846dcb 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Domain/IWritableRepository.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Domain/IWritableRepository.cs @@ -1,16 +1,15 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Defines a generic way to support multiple implementations of writable repositories (CrUd). +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IWritableRepository : IRepository, IWritableRepository where TEntity : class, IIdentity { - /// - /// Defines a generic way to support multiple implementations of writable repositories (CrUd). - /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The type used to mark the implementation that this repository represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IWritableRepository : IRepository, IWritableRepository where TEntity : class, IIdentity - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Domain/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection/Domain/ServiceCollectionExtensions.cs index 71e50062..ed6ae404 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Domain/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Domain/ServiceCollectionExtensions.cs @@ -1,72 +1,71 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Domain +namespace Savvyio.Extensions.DependencyInjection.Domain; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation of to the specified . /// - public static class ServiceCollectionExtensions + /// The type of the to add. + /// The type of the entity. + /// The type of the key that uniquely identifies the entity. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional configuration calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddRepository(this IServiceCollection services, Action setup = null) + where TService : class, IPersistentRepository + where TEntity : class, IIdentity { - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional configuration calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static IServiceCollection AddRepository(this IServiceCollection services, Action setup = null) - where TService : class, IPersistentRepository - where TEntity : class, IIdentity + Validator.ThrowIfNull(services); + var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Scoped)).Configure(); + return services.Add(o => { - Validator.ThrowIfNull(services); - var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Scoped)).Configure(); - return services.Add(o => - { - o.Lifetime = options.Lifetime; - o.NestedTypePredicate = type => type.HasInterfaces(typeof(IRepository<,>)); - }); - } + o.Lifetime = options.Lifetime; + o.NestedTypePredicate = type => type.HasInterfaces(typeof(IRepository<,>)); + }); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional configuration calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - /// - public static IServiceCollection AddUnitOfWork(this IServiceCollection services, Action setup = null) where TService : class, IUnitOfWork + /// + /// Adds an implementation of to the specified . + /// + /// The type of the to add. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional configuration calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + /// + public static IServiceCollection AddUnitOfWork(this IServiceCollection services, Action setup = null) where TService : class, IUnitOfWork + { + Validator.ThrowIfNull(services); + var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Scoped)).Configure(); + var unitOfWorkType = typeof(IUnitOfWork); + if (typeof(TService).TryGetDependencyInjectionMarker(out var markerType)) { - Validator.ThrowIfNull(services); - var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Scoped)).Configure(); - var unitOfWorkType = typeof(IUnitOfWork); - if (typeof(TService).TryGetDependencyInjectionMarker(out var markerType)) - { - unitOfWorkType = typeof(IUnitOfWork<>).MakeGenericType(markerType); - } - return services.Add(unitOfWorkType, p => p.GetRequiredService(), options.Lifetime); + unitOfWorkType = typeof(IUnitOfWork<>).MakeGenericType(markerType); } + return services.Add(unitOfWorkType, p => p.GetRequiredService(), options.Lifetime); } } diff --git a/src/Savvyio.Extensions.DependencyInjection/GlobalSuppressions.cs b/src/Savvyio.Extensions.DependencyInjection/GlobalSuppressions.cs index 81a3e44d..b3b50dbc 100644 --- a/src/Savvyio.Extensions.DependencyInjection/GlobalSuppressions.cs +++ b/src/Savvyio.Extensions.DependencyInjection/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. diff --git a/src/Savvyio.Extensions.DependencyInjection/IDataSource.cs b/src/Savvyio.Extensions.DependencyInjection/IDataSource.cs index 8e781ad8..6c8acd8b 100644 --- a/src/Savvyio.Extensions.DependencyInjection/IDataSource.cs +++ b/src/Savvyio.Extensions.DependencyInjection/IDataSource.cs @@ -1,14 +1,13 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; -namespace Savvyio.Extensions.DependencyInjection +namespace Savvyio.Extensions.DependencyInjection; + +/// +/// Defines a generic way to support multiple implementations that does the actual I/O communication with a source of data. +/// +/// The type used to mark the implementation that this data source represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IDataSource : IDataSource, IDependencyInjectionMarker { - /// - /// Defines a generic way to support multiple implementations that does the actual I/O communication with a source of data. - /// - /// The type used to mark the implementation that this data source represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IDataSource : IDataSource, IDependencyInjectionMarker - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Messaging/IPointToPointChannel.cs b/src/Savvyio.Extensions.DependencyInjection/Messaging/IPointToPointChannel.cs index f8baef5c..76495032 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Messaging/IPointToPointChannel.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Messaging/IPointToPointChannel.cs @@ -1,16 +1,15 @@ -using Savvyio.Messaging; +using Savvyio.Messaging; -namespace Savvyio.Extensions.DependencyInjection.Messaging +namespace Savvyio.Extensions.DependencyInjection.Messaging; + +/// +/// Defines a generic way to support multiple implementations of a bus that is used for interacting with other subsystems (out-process/inter-application) to do something (e.g., change the state). +/// +/// The type of the model to invoke on a handler. +/// The type used to mark the implementation that this queue represents. Optimized for Microsoft Dependency Injection. +/// +/// +/// +public interface IPointToPointChannel : IPointToPointChannel, ISender, IReceiver where TRequest : IRequest { - /// - /// Defines a generic way to support multiple implementations of a bus that is used for interacting with other subsystems (out-process/inter-application) to do something (e.g., change the state). - /// - /// The type of the model to invoke on a handler. - /// The type used to mark the implementation that this queue represents. Optimized for Microsoft Dependency Injection. - /// - /// - /// - public interface IPointToPointChannel : IPointToPointChannel, ISender, IReceiver where TRequest : IRequest - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Messaging/IPublishSubscribeChannel.cs b/src/Savvyio.Extensions.DependencyInjection/Messaging/IPublishSubscribeChannel.cs index 5ff86d52..5278f72e 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Messaging/IPublishSubscribeChannel.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Messaging/IPublishSubscribeChannel.cs @@ -1,16 +1,15 @@ -using Savvyio.Messaging; +using Savvyio.Messaging; -namespace Savvyio.Extensions.DependencyInjection.Messaging +namespace Savvyio.Extensions.DependencyInjection.Messaging; + +/// +/// Defines a generic way to support multiple implementations of a bus that is used for interacting with other subsystems (out-process/inter-application) to be notified (e.g., made aware of something that has happened). +/// +/// The type of the model to invoke on a handler. +/// The type used to mark the implementation that this bus represents. Optimized for Microsoft Dependency Injection. +/// +/// +/// +public interface IPublishSubscribeChannel : IPublishSubscribeChannel, IPublisher, ISubscriber where TRequest : IRequest { - /// - /// Defines a generic way to support multiple implementations of a bus that is used for interacting with other subsystems (out-process/inter-application) to be notified (e.g., made aware of something that has happened). - /// - /// The type of the model to invoke on a handler. - /// The type used to mark the implementation that this bus represents. Optimized for Microsoft Dependency Injection. - /// - /// - /// - public interface IPublishSubscribeChannel : IPublishSubscribeChannel, IPublisher, ISubscriber where TRequest : IRequest - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Messaging/IPublisher.cs b/src/Savvyio.Extensions.DependencyInjection/Messaging/IPublisher.cs index 21b4dbe2..d09ba272 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Messaging/IPublisher.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Messaging/IPublisher.cs @@ -1,16 +1,15 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Messaging; -namespace Savvyio.Extensions.DependencyInjection.Messaging +namespace Savvyio.Extensions.DependencyInjection.Messaging; + +/// +/// Defines a generic way to support multiple implementations of a publisher/sender channel for interacting with other subsystems (out-process/inter-application) to be notified (e.g., made aware of something that has happened). +/// +/// The type of the model to invoke on a handler. +/// The type used to mark the implementation that this channel represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IPublisher : IPublisher, IDependencyInjectionMarker where TRequest : IRequest { - /// - /// Defines a generic way to support multiple implementations of a publisher/sender channel for interacting with other subsystems (out-process/inter-application) to be notified (e.g., made aware of something that has happened). - /// - /// The type of the model to invoke on a handler. - /// The type used to mark the implementation that this channel represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IPublisher : IPublisher, IDependencyInjectionMarker where TRequest : IRequest - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Messaging/IReceiver.cs b/src/Savvyio.Extensions.DependencyInjection/Messaging/IReceiver.cs index 75b9cb4e..7b6dc957 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Messaging/IReceiver.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Messaging/IReceiver.cs @@ -1,16 +1,15 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Messaging; -namespace Savvyio.Extensions.DependencyInjection.Messaging +namespace Savvyio.Extensions.DependencyInjection.Messaging; + +/// +/// Defines a generic way to support multiple implementations of a consumer/receiver channel used by subsystems to receive a command and perform one or more actions (e.g., change the state). +/// +/// The type of the model to invoke on a handler. +/// The type used to mark the implementation that this channel represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface IReceiver : IReceiver, IDependencyInjectionMarker where TRequest : IRequest { - /// - /// Defines a generic way to support multiple implementations of a consumer/receiver channel used by subsystems to receive a command and perform one or more actions (e.g., change the state). - /// - /// The type of the model to invoke on a handler. - /// The type used to mark the implementation that this channel represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface IReceiver : IReceiver, IDependencyInjectionMarker where TRequest : IRequest - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Messaging/ISender.cs b/src/Savvyio.Extensions.DependencyInjection/Messaging/ISender.cs index 99695fd3..fdde4b36 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Messaging/ISender.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Messaging/ISender.cs @@ -1,16 +1,15 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Messaging; -namespace Savvyio.Extensions.DependencyInjection.Messaging +namespace Savvyio.Extensions.DependencyInjection.Messaging; + +/// +/// Defines a generic way to support multiple implementations of a producer/sender channel used for interacting with other subsystems (out-process/inter-application) to do something (e.g., change the state). +/// +/// The type of the model to invoke on a handler. +/// The type used to mark the implementation that this channel represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface ISender : ISender, IDependencyInjectionMarker where TRequest : IRequest { - /// - /// Defines a generic way to support multiple implementations of a producer/sender channel used for interacting with other subsystems (out-process/inter-application) to do something (e.g., change the state). - /// - /// The type of the model to invoke on a handler. - /// The type used to mark the implementation that this channel represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface ISender : ISender, IDependencyInjectionMarker where TRequest : IRequest - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Messaging/ISubscriber.cs b/src/Savvyio.Extensions.DependencyInjection/Messaging/ISubscriber.cs index 20da3812..6db9d289 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Messaging/ISubscriber.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Messaging/ISubscriber.cs @@ -1,16 +1,15 @@ -using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.DependencyInjection; using Savvyio.Messaging; -namespace Savvyio.Extensions.DependencyInjection.Messaging +namespace Savvyio.Extensions.DependencyInjection.Messaging; + +/// +/// Defines a generic way to support multiple implementations of a subscriber/receiver channel used by subsystems to subscribe to messages (typically events) to be made aware of something that has happened. +/// +/// The type of the model to invoke on a handler. +/// The type used to mark the implementation that this channel represents. Optimized for Microsoft Dependency Injection. +/// +/// +public interface ISubscriber : ISubscriber, IDependencyInjectionMarker where TRequest : IRequest { - /// - /// Defines a generic way to support multiple implementations of a subscriber/receiver channel used by subsystems to subscribe to messages (typically events) to be made aware of something that has happened. - /// - /// The type of the model to invoke on a handler. - /// The type used to mark the implementation that this channel represents. Optimized for Microsoft Dependency Injection. - /// - /// - public interface ISubscriber : ISubscriber, IDependencyInjectionMarker where TRequest : IRequest - { - } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Messaging/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection/Messaging/ServiceCollectionExtensions.cs index 3ccab36b..e390c745 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Messaging/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection/Messaging/ServiceCollectionExtensions.cs @@ -1,59 +1,58 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Savvyio.Messaging; -namespace Savvyio.Extensions.DependencyInjection.Messaging +namespace Savvyio.Extensions.DependencyInjection.Messaging; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation of to the specified . /// - public static class ServiceCollectionExtensions + /// The type of the to add. + /// The type of the model to invoke on a handler. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional configuration calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + public static IServiceCollection AddMessageQueue(this IServiceCollection services, Action setup = null) where TService : class, IPointToPointChannel where TRequest : IRequest { - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The type of the model to invoke on a handler. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional configuration calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - public static IServiceCollection AddMessageQueue(this IServiceCollection services, Action setup = null) where TService : class, IPointToPointChannel where TRequest : IRequest + Validator.ThrowIfNull(services); + var options = Patterns.Configure(setup, o => o.Lifetime = ServiceLifetime.Singleton); + return services.Add(o => { - Validator.ThrowIfNull(services); - var options = Patterns.Configure(setup, o => o.Lifetime = ServiceLifetime.Singleton); - return services.Add(o => - { - o.Lifetime = options.Lifetime; - o.NestedTypePredicate = type => type.HasInterfaces(typeof(IPointToPointChannel<>), typeof(IReceiver<>), typeof(ISender<>)); - }); - } + o.Lifetime = options.Lifetime; + o.NestedTypePredicate = type => type.HasInterfaces(typeof(IPointToPointChannel<>), typeof(IReceiver<>), typeof(ISender<>)); + }); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The type of the model to invoke on a handler. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional configuration calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - public static IServiceCollection AddMessageBus(this IServiceCollection services, Action setup = null) where TService : class, IPublishSubscribeChannel where TRequest : IRequest + /// + /// Adds an implementation of to the specified . + /// + /// The type of the to add. + /// The type of the model to invoke on a handler. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional configuration calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + public static IServiceCollection AddMessageBus(this IServiceCollection services, Action setup = null) where TService : class, IPublishSubscribeChannel where TRequest : IRequest + { + Validator.ThrowIfNull(services); + var options = Patterns.Configure(setup, o => o.Lifetime = ServiceLifetime.Singleton); + return services.Add(o => { - Validator.ThrowIfNull(services); - var options = Patterns.Configure(setup, o => o.Lifetime = ServiceLifetime.Singleton); - return services.Add(o => - { - o.Lifetime = options.Lifetime; - o.NestedTypePredicate = type => type.HasInterfaces(typeof(IPublishSubscribeChannel<>), typeof(ISubscriber<>), typeof(IPublisher<>)); - }); - } + o.Lifetime = options.Lifetime; + o.NestedTypePredicate = type => type.HasInterfaces(typeof(IPublishSubscribeChannel<>), typeof(ISubscriber<>), typeof(IPublisher<>)); + }); } } diff --git a/src/Savvyio.Extensions.DependencyInjection/Savvyio.Extensions.DependencyInjection.csproj b/src/Savvyio.Extensions.DependencyInjection/Savvyio.Extensions.DependencyInjection.csproj index 84597f35..ba4f3e73 100644 --- a/src/Savvyio.Extensions.DependencyInjection/Savvyio.Extensions.DependencyInjection.csproj +++ b/src/Savvyio.Extensions.DependencyInjection/Savvyio.Extensions.DependencyInjection.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O core assemblies with support for Microsoft Dependency Injection. diff --git a/src/Savvyio.Extensions.DependencyInjection/SavvyioDependencyInjectionOptions.cs b/src/Savvyio.Extensions.DependencyInjection/SavvyioDependencyInjectionOptions.cs index 0a3af6dd..699a5ea3 100644 --- a/src/Savvyio.Extensions.DependencyInjection/SavvyioDependencyInjectionOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection/SavvyioDependencyInjectionOptions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -6,118 +6,117 @@ using Microsoft.Extensions.DependencyInjection; using Savvyio.Dispatchers; -namespace Savvyio.Extensions.DependencyInjection +namespace Savvyio.Extensions.DependencyInjection; + +/// +/// Specifies options that is related to setting up Savvy I/O services. +/// +public class SavvyioDependencyInjectionOptions : SavvyioOptions { + private readonly IList _assemblies = new List(); + private readonly ServiceLocatorOptions _serviceLocatorOptions = new(); + /// - /// Specifies options that is related to setting up Savvy I/O services. + /// Initializes a new instance of the class. /// - public class SavvyioDependencyInjectionOptions : SavvyioOptions + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// false + /// + /// + /// + /// false + /// + /// + /// + /// false + /// + /// + /// + /// + /// + /// + /// + /// p => new ServiceLocator(p.GetServices) + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// null + /// + /// + /// + public SavvyioDependencyInjectionOptions() { - private readonly IList _assemblies = new List(); - private readonly ServiceLocatorOptions _serviceLocatorOptions = new(); - - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// false - /// - /// - /// - /// false - /// - /// - /// - /// false - /// - /// - /// - /// - /// - /// - /// - /// p => new ServiceLocator(p.GetServices) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// null - /// - /// - /// - public SavvyioDependencyInjectionOptions() - { - HandlerServicesLifetime = ServiceLifetime.Transient; - DispatcherServicesLifetime = ServiceLifetime.Transient; - ServiceLocatorImplementationFactory = _serviceLocatorOptions.ImplementationFactory; - ServiceLocatorLifetime = _serviceLocatorOptions.Lifetime; - } + HandlerServicesLifetime = ServiceLifetime.Transient; + DispatcherServicesLifetime = ServiceLifetime.Transient; + ServiceLocatorImplementationFactory = _serviceLocatorOptions.ImplementationFactory; + ServiceLocatorLifetime = _serviceLocatorOptions.Lifetime; + } - /// - /// Gets or sets the function delegate that creates an instance of an implementation. - /// - /// The function delegate that creates an instance of an implementation. - public Func ServiceLocatorImplementationFactory { get; set; } + /// + /// Gets or sets the function delegate that creates an instance of an implementation. + /// + /// The function delegate that creates an instance of an implementation. + public Func ServiceLocatorImplementationFactory { get; set; } - /// - /// Gets or sets the lifetime of the . - /// - /// The lifetime of the . - public ServiceLifetime ServiceLocatorLifetime { get; set; } + /// + /// Gets or sets the lifetime of the . + /// + /// The lifetime of the . + public ServiceLifetime ServiceLocatorLifetime { get; set; } - /// - /// Gets or sets the lifetime of the handler services. - /// - /// The lifetime of the handler services. - public ServiceLifetime HandlerServicesLifetime { get; set; } + /// + /// Gets or sets the lifetime of the handler services. + /// + /// The lifetime of the handler services. + public ServiceLifetime HandlerServicesLifetime { get; set; } - /// - /// Gets or sets the lifetime of the dispatcher services. - /// - /// The lifetime of the dispatcher services. - public ServiceLifetime DispatcherServicesLifetime { get; set; } + /// + /// Gets or sets the lifetime of the dispatcher services. + /// + /// The lifetime of the dispatcher services. + public ServiceLifetime DispatcherServicesLifetime { get; set; } - /// - /// Gets the assemblies to scan for dispatcher- and handler- types. - /// - /// The assemblies to scan for dispatcher- and handler- types. - public IEnumerable AssembliesToScan => _assemblies.Count == 0 ? Enumerable.Empty() : _assemblies; + /// + /// Gets the assemblies to scan for dispatcher- and handler- types. + /// + /// The assemblies to scan for dispatcher- and handler- types. + public IEnumerable AssembliesToScan => _assemblies.Count == 0 ? Enumerable.Empty() : _assemblies; - /// - /// Adds the specified range of to be included when scanning for dispatcher- and handler- types. - /// - /// The assemblies to include in the scan. - /// A reference to this instance so that additional configuration calls can be chained. - public SavvyioDependencyInjectionOptions AddAssemblyRangeToScan(params Assembly[] assemblies) - { - _assemblies.AddRange(assemblies); - return this; - } + /// + /// Adds the specified range of to be included when scanning for dispatcher- and handler- types. + /// + /// The assemblies to include in the scan. + /// A reference to this instance so that additional configuration calls can be chained. + public SavvyioDependencyInjectionOptions AddAssemblyRangeToScan(params Assembly[] assemblies) + { + _assemblies.AddRange(assemblies); + return this; + } - /// - /// Adds the specified to be included when scanning for dispatcher- and handler- types. - /// - /// The assembly to include in the scan. - /// A reference to this instance so that additional configuration calls can be chained. - public SavvyioDependencyInjectionOptions AddAssemblyToScan(Assembly assembly) - { - _assemblies.Add(assembly); - return this; - } + /// + /// Adds the specified to be included when scanning for dispatcher- and handler- types. + /// + /// The assembly to include in the scan. + /// A reference to this instance so that additional configuration calls can be chained. + public SavvyioDependencyInjectionOptions AddAssemblyToScan(Assembly assembly) + { + _assemblies.Add(assembly); + return this; } } diff --git a/src/Savvyio.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Savvyio.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index c34b64ce..eb670138 100644 --- a/src/Savvyio.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -10,257 +10,232 @@ using Microsoft.Extensions.DependencyInjection; using Savvyio.Dispatchers; -namespace Savvyio.Extensions.DependencyInjection +namespace Savvyio.Extensions.DependencyInjection; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions { /// - /// Extension methods for the interface. + /// Adds an implementation of to the specified . /// - public static class ServiceCollectionExtensions + /// The type of the to add. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddMarshaller(this IServiceCollection services, Action setup = null) where TService : class, IMarshaller { - /// - /// Registers the specified as a triple-configuration for both compatibility with (and outside the confines of) Microsoft Dependency Injection e.g., IOptions<TOptions>, Action<TOptions> and TOptions. - /// - /// The options type to be configured. - /// The to add the services to. - /// The which need to be configured by the delegate. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public static IServiceCollection AddConfiguredOptions(this IServiceCollection services, Action setup) where TOptions : class, IParameterObject, new() + Validator.ThrowIfNull(services); + var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Singleton)).Configure(); + return services.Add(o => { - Validator.ThrowIfNull(services); - Validator.ThrowIfInvalidConfigurator(setup, out var options); - return services - .Configure(setup) // support for IOptions - .AddSingleton(setup) // support for Action - .AddSingleton(options); // support for TOptions - } + o.Lifetime = options.Lifetime; + o.NestedTypePredicate = type => type.HasInterfaces(typeof(IMarshaller)); + }); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddMarshaller(this IServiceCollection services, Action setup = null) where TService : class, IMarshaller + /// + /// Adds an implementation of to the specified . + /// + /// The type of the to add. + /// The to add the service to. + /// The function delegate that creates the service. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IServiceCollection AddMarshaller(this IServiceCollection services, Func implementationFactory, Action setup = null) where TService : class, IMarshaller + { + Validator.ThrowIfNull(services); + Validator.ThrowIfNull(implementationFactory); + var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Singleton)).Configure(); + return services.Add(implementationFactory, o => { - Validator.ThrowIfNull(services); - var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Singleton)).Configure(); - return services.Add(o => - { - o.Lifetime = options.Lifetime; - o.NestedTypePredicate = type => type.HasInterfaces(typeof(IMarshaller)); - }); - } + o.Lifetime = options.Lifetime; + o.NestedTypePredicate = type => type.HasInterfaces(typeof(IMarshaller)); + }); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The to add the service to. - /// The function delegate that creates the service. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null -or- - /// cannot be null. - /// - public static IServiceCollection AddMarshaller(this IServiceCollection services, Func implementationFactory, Action setup = null) where TService : class, IMarshaller + /// + /// Adds an implementation of to the specified . + /// + /// The type of the to add. + /// The to add the service to. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. + /// + /// + /// + /// + /// cannot be null. + /// + public static IServiceCollection AddDataSource(this IServiceCollection services, Action setup = null) where TService : class, IDataSource + { + Validator.ThrowIfNull(services); + var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Singleton)).Configure(); + return services.Add(o => { - Validator.ThrowIfNull(services); - Validator.ThrowIfNull(implementationFactory); - var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Singleton)).Configure(); - return services.Add(implementationFactory, o => - { - o.Lifetime = options.Lifetime; - o.NestedTypePredicate = type => type.HasInterfaces(typeof(IMarshaller)); - }); - } + o.Lifetime = options.Lifetime; + o.NestedTypePredicate = type => type.HasInterfaces(typeof(IDataSource)); + }); + } - /// - /// Adds an implementation of to the specified . - /// - /// The type of the to add. - /// The to add the service to. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// If the underlying type of implements interface then this is automatically handled. Also, the implementation will be type forwarded accordingly. - /// - /// - /// - /// - /// cannot be null. - /// - public static IServiceCollection AddDataSource(this IServiceCollection services, Action setup = null) where TService : class, IDataSource + /// + /// Adds Savvy I/O service locator used to resolve necessary dependencies. + /// + /// The to extend. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddServiceLocator(this IServiceCollection services, Action setup = null) + { + Validator.ThrowIfNull(services); + var options = setup.Configure(); + services.TryAdd(typeof(IServiceLocator), options.ImplementationFactory, options.Lifetime); + return services; + } + + /// + /// Adds the to the specified if it was allowed included from . + /// + /// The to add the service to. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddHandlerServicesDescriptor(this IServiceCollection services) + { + Validator.ThrowIfNull(services); + if (services.Any(sd => sd.ServiceType == typeof(HandlerServicesDescriptor))) { - Validator.ThrowIfNull(services); - var options = (setup ?? (o => o.Lifetime = ServiceLifetime.Singleton)).Configure(); - return services.Add(o => - { - o.Lifetime = options.Lifetime; - o.NestedTypePredicate = type => type.HasInterfaces(typeof(IDataSource)); - }); + services.AddSingleton(provider => provider.GetRequiredService()); } + return services; + } + + /// + /// Adds Savvy I/O related dispatcher- and handler- types to the specified . + /// + /// The to extend. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddSavvyIO(this IServiceCollection services, Action setup = null) + { + Validator.ThrowIfNull(services); + var options = setup.Configure(); - /// - /// Adds Savvy I/O service locator used to resolve necessary dependencies. - /// - /// The to extend. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddServiceLocator(this IServiceCollection services, Action setup = null) + if (options.AssembliesToScan != null) { - Validator.ThrowIfNull(services); - var options = setup.Configure(); - services.TryAdd(typeof(IServiceLocator), options.ImplementationFactory, options.Lifetime); - return services; + if (options.AllowDispatcherDiscovery) { options.AddDispatchers(options.AssembliesToScan.ToArray()); } + if (options.AllowHandlerDiscovery) { options.AddHandlers(options.AssembliesToScan.ToArray()); } } - /// - /// Adds the to the specified if it was allowed included from . - /// - /// The to add the service to. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddHandlerServicesDescriptor(this IServiceCollection services) + var descriptors = new Dictionary>>(); + foreach (var handlerType in options.HandlerImplementationTypes) { - Validator.ThrowIfNull(services); - if (services.Any(sd => sd.ServiceType == typeof(HandlerServicesDescriptor))) - { - services.AddSingleton(provider => provider.GetRequiredService()); - } - return services; + var handlerTypeServices = handlerType.GetInterfaces().Where(type => type.HasInterfaces(options.HandlerServiceTypes.ToArray())); + AddHandlersWithOptionalDescriptors(services, handlerType, handlerTypeServices, descriptors, options); } - /// - /// Adds Savvy I/O related dispatcher- and handler- types to the specified . - /// - /// The to extend. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddSavvyIO(this IServiceCollection services, Action setup = null) + if (options.IncludeHandlerServicesDescriptor) { - Validator.ThrowIfNull(services); - var options = setup.Configure(); - - if (options.AssembliesToScan != null) - { - if (options.AllowDispatcherDiscovery) { options.AddDispatchers(options.AssembliesToScan.ToArray()); } - if (options.AllowHandlerDiscovery) { options.AddHandlers(options.AssembliesToScan.ToArray()); } - } - - var descriptors = new Dictionary>>(); - foreach (var handlerType in options.HandlerImplementationTypes) - { - var handlerTypeServices = handlerType.GetInterfaces().Where(type => type.HasInterfaces(options.HandlerServiceTypes.ToArray())); - AddHandlersWithOptionalDescriptors(services, handlerType, handlerTypeServices, descriptors, options); - } - - if (options.IncludeHandlerServicesDescriptor) - { - services.AddSingleton(new HandlerServicesDescriptor(descriptors.Where(pair => pair.Key.HasInterfaces(options.HandlerServiceTypes.ToArray())).GroupBy(pair => pair.Key), options.HandlerServiceTypes)); - } + services.AddSingleton(new HandlerServicesDescriptor(descriptors.Where(pair => pair.Key.HasInterfaces(options.HandlerServiceTypes.ToArray())).GroupBy(pair => pair.Key), options.HandlerServiceTypes)); + } - AddDispatchers(services, options); + AddDispatchers(services, options); - return services.AddServiceLocator(o => - { - o.ImplementationFactory = options.ServiceLocatorImplementationFactory; - o.Lifetime = options.ServiceLocatorLifetime; - }); - } + return services.AddServiceLocator(o => + { + o.ImplementationFactory = options.ServiceLocatorImplementationFactory; + o.Lifetime = options.ServiceLocatorLifetime; + }); + } - private static void AddHandlersWithOptionalDescriptors(IServiceCollection services, Type handlerType, IEnumerable handlerTypeServices, Dictionary>> descriptors, SavvyioDependencyInjectionOptions options) + private static void AddHandlersWithOptionalDescriptors(IServiceCollection services, Type handlerType, IEnumerable handlerTypeServices, Dictionary>> descriptors, SavvyioDependencyInjectionOptions options) + { + foreach (var handlerTypeService in handlerTypeServices) { - foreach (var handlerTypeService in handlerTypeServices) - { - var handlers = new Hierarchy(); - if (options.IncludeHandlerServicesDescriptor) { handlers.Add(handlerType); } + var handlers = new Hierarchy(); + if (options.IncludeHandlerServicesDescriptor) { handlers.Add(handlerType); } - var handlerTypeInterfaceModel = handlerTypeService.GetInterface("IHandler`1")?.GenericTypeArguments.SingleOrDefault(); - foreach (var method in handlerType.GetTypeInfo().DeclaredMembers.Where(m => MemberIsMethodOrLambdaWithHandlerTypeInterface(m, handlerTypeInterfaceModel))) + var handlerTypeInterfaceModel = handlerTypeService.GetInterface("IHandler`1")?.GenericTypeArguments.SingleOrDefault(); + foreach (var method in handlerType.GetTypeInfo().DeclaredMembers.Where(m => MemberIsMethodOrLambdaWithHandlerTypeInterface(m, handlerTypeInterfaceModel))) + { + if (options.IncludeHandlerServicesDescriptor) { handlers.Add(method); } + if (!services.Any(sd => sd.ServiceType == handlerTypeService && sd.ImplementationType == handlerType)) { - if (options.IncludeHandlerServicesDescriptor) { handlers.Add(method); } - if (!services.Any(sd => sd.ServiceType == handlerTypeService && sd.ImplementationType == handlerType)) - { - services.Add(handlerTypeService, handlerType, options.HandlerServicesLifetime); - } + services.Add(handlerTypeService, handlerType, options.HandlerServicesLifetime); } - - AddDescriptors(handlers, handlerTypeService, descriptors, options); } + + AddDescriptors(handlers, handlerTypeService, descriptors, options); } + } - private static void AddDescriptors(Hierarchy handlers, Type handlerTypeService, Dictionary>> descriptors, SavvyioDependencyInjectionOptions options) + private static void AddDescriptors(Hierarchy handlers, Type handlerTypeService, Dictionary>> descriptors, SavvyioDependencyInjectionOptions options) + { + if (options.IncludeHandlerServicesDescriptor) { - if (options.IncludeHandlerServicesDescriptor) + if (descriptors.TryGetValue(handlerTypeService, out var list)) { - if (descriptors.TryGetValue(handlerTypeService, out var list)) - { - list.Add(handlers); - } - else - { - descriptors.Add(handlerTypeService, new List>() { handlers }); - } + list.Add(handlers); + } + else + { + descriptors.Add(handlerTypeService, new List>() { handlers }); } } + } - private static void AddDispatchers(IServiceCollection services, SavvyioDependencyInjectionOptions options) + private static void AddDispatchers(IServiceCollection services, SavvyioDependencyInjectionOptions options) + { + foreach (var dispatcherType in options.DispatcherImplementationTypes) { - foreach (var dispatcherType in options.DispatcherImplementationTypes) + var dispatcherTypeServices = dispatcherType.GetInterfaces().Where(type => type.HasInterfaces(options.DispatcherServiceTypes.ToArray())); + foreach (var dispatcherTypeService in dispatcherTypeServices) { - var dispatcherTypeServices = dispatcherType.GetInterfaces().Where(type => type.HasInterfaces(options.DispatcherServiceTypes.ToArray())); - foreach (var dispatcherTypeService in dispatcherTypeServices) - { - services.TryAdd(dispatcherTypeService, dispatcherType, options.DispatcherServicesLifetime); - } + services.TryAdd(dispatcherTypeService, dispatcherType, options.DispatcherServicesLifetime); } } + } - private static bool MemberIsMethodOrLambdaWithHandlerTypeInterface(MemberInfo m, Type handlerTypeInterfaceModel) + private static bool MemberIsMethodOrLambdaWithHandlerTypeInterface(MemberInfo m, Type handlerTypeInterfaceModel) + { + switch (m.MemberType) { - switch (m.MemberType) - { - case MemberTypes.Method: - if (m is MethodInfo mi) - { - var parameters = mi.GetParameters(); - return parameters.Any(p => p.ParameterType.HasInterfaces(handlerTypeInterfaceModel)); - } - break; - case MemberTypes.NestedType: - if (m is Type nt) + case MemberTypes.Method: + if (m is MethodInfo mi) + { + var parameters = mi.GetParameters(); + return parameters.Any(p => p.ParameterType.HasInterfaces(handlerTypeInterfaceModel)); + } + break; + case MemberTypes.NestedType: + if (m is Type nt) + { + foreach (var nestedMethod in nt.GetRuntimeMethods()) { - foreach (var nestedMethod in nt.GetRuntimeMethods()) + var parameters = nestedMethod.GetParameters(); + if (parameters.Any(p => p.ParameterType.HasInterfaces(handlerTypeInterfaceModel))) { - var parameters = nestedMethod.GetParameters(); - if (parameters.Any(p => p.ParameterType.HasInterfaces(handlerTypeInterfaceModel))) - { - return true; - } + return true; } } - break; - } - return false; + } + break; } + return false; } } diff --git a/src/Savvyio.Extensions.DependencyInjection/ServiceLocatorOptions.cs b/src/Savvyio.Extensions.DependencyInjection/ServiceLocatorOptions.cs index 2f0e755f..65fc0d45 100644 --- a/src/Savvyio.Extensions.DependencyInjection/ServiceLocatorOptions.cs +++ b/src/Savvyio.Extensions.DependencyInjection/ServiceLocatorOptions.cs @@ -1,41 +1,40 @@ -using System; +using System; using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Savvyio.Dispatchers; -namespace Savvyio.Extensions.DependencyInjection +namespace Savvyio.Extensions.DependencyInjection; + +/// +/// Configuration options for . +/// +/// +public class ServiceLocatorOptions : ServiceOptions { /// - /// Configuration options for . + /// Initializes a new instance of the class. /// - /// - public class ServiceLocatorOptions : ServiceOptions + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// p => new ServiceLocator(p.GetServices) + /// + /// + /// + public ServiceLocatorOptions() { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// p => new ServiceLocator(p.GetServices) - /// - /// - /// - public ServiceLocatorOptions() - { - ImplementationFactory = p => new ServiceLocator(p.GetServices); - } - - /// - /// Gets or sets the function delegate that creates an instance of an implementation. - /// - /// The function delegate that creates an instance of an implementation. - public Func ImplementationFactory { get; set; } + ImplementationFactory = p => new ServiceLocator(p.GetServices); } + + /// + /// Gets or sets the function delegate that creates an instance of an implementation. + /// + /// The function delegate that creates an instance of an implementation. + public Func ImplementationFactory { get; set; } } diff --git a/src/Savvyio.Extensions.DependencyInjection/ServiceProviderExtensions.cs b/src/Savvyio.Extensions.DependencyInjection/ServiceProviderExtensions.cs index d0685379..ae771882 100644 --- a/src/Savvyio.Extensions.DependencyInjection/ServiceProviderExtensions.cs +++ b/src/Savvyio.Extensions.DependencyInjection/ServiceProviderExtensions.cs @@ -1,24 +1,23 @@ -using System; +using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Savvyio.Extensions.DependencyInjection +namespace Savvyio.Extensions.DependencyInjection; + +/// +/// Extension methods for the interface. +/// +public static class ServiceProviderExtensions { /// - /// Extension methods for the interface. + /// Writes the handler discoveries to with . /// - public static class ServiceProviderExtensions + /// The type used to categorize the log messages. + /// The service provider used to resolve services. + public static void WriteHandlerDiscoveriesToLog(this IServiceProvider provider) { - /// - /// Writes the handler discoveries to with . - /// - /// The type used to categorize the log messages. - /// The service provider used to resolve services. - public static void WriteHandlerDiscoveriesToLog(this IServiceProvider provider) - { - var descriptor = provider.GetRequiredService(); - var logger = provider.GetRequiredService>(); - logger.LogInformation("{HandlerDiscoveries}", descriptor.ToString()); - } + var descriptor = provider.GetRequiredService(); + var logger = provider.GetRequiredService>(); + logger.LogInformation("{HandlerDiscoveries}", descriptor.ToString()); } } diff --git a/src/Savvyio.Extensions.Dispatchers/IMediator.cs b/src/Savvyio.Extensions.Dispatchers/IMediator.cs index a091353b..ddf6bdb5 100644 --- a/src/Savvyio.Extensions.Dispatchers/IMediator.cs +++ b/src/Savvyio.Extensions.Dispatchers/IMediator.cs @@ -1,18 +1,17 @@ -using Savvyio.Commands; +using Savvyio.Commands; using Savvyio.Domain; using Savvyio.EventDriven; using Savvyio.Queries; -namespace Savvyio.Extensions +namespace Savvyio.Extensions; + +/// +/// Defines a mediator to encapsulate requests (Fire-and-Forget/In-Only) and request/response (Request-Reply/In-Out) message exchange patterns. +/// +/// +/// +/// +/// +public interface IMediator : ICommandDispatcher, IDomainEventDispatcher, IIntegrationEventDispatcher, IQueryDispatcher { - /// - /// Defines a mediator to encapsulate requests (Fire-and-Forget/In-Only) and request/response (Request-Reply/In-Out) message exchange patterns. - /// - /// - /// - /// - /// - public interface IMediator : ICommandDispatcher, IDomainEventDispatcher, IIntegrationEventDispatcher, IQueryDispatcher - { - } } diff --git a/src/Savvyio.Extensions.Dispatchers/Mediator.cs b/src/Savvyio.Extensions.Dispatchers/Mediator.cs index 435245cd..98936419 100644 --- a/src/Savvyio.Extensions.Dispatchers/Mediator.cs +++ b/src/Savvyio.Extensions.Dispatchers/Mediator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon.Threading; using Savvyio.Commands; @@ -7,112 +7,111 @@ using Savvyio.EventDriven; using Savvyio.Queries; -namespace Savvyio.Extensions +namespace Savvyio.Extensions; + +/// +/// Provides a default implementation of the interface. +/// +/// +public class Mediator : IMediator { + private readonly CommandDispatcher _commandDispatcher; + private readonly DomainEventDispatcher _domainEventDispatcher; + private readonly IntegrationEventDispatcher _integrationEventDispatcher; + private readonly QueryDispatcher _queryDispatcher; + /// - /// Provides a default implementation of the interface. + /// Initializes a new instance of the class. /// - /// - public class Mediator : IMediator + /// The provider of service implementations. + public Mediator(IServiceLocator serviceLocator) { - private readonly CommandDispatcher _commandDispatcher; - private readonly DomainEventDispatcher _domainEventDispatcher; - private readonly IntegrationEventDispatcher _integrationEventDispatcher; - private readonly QueryDispatcher _queryDispatcher; - - /// - /// Initializes a new instance of the class. - /// - /// The provider of service implementations. - public Mediator(IServiceLocator serviceLocator) - { - _commandDispatcher = new CommandDispatcher(serviceLocator); - _domainEventDispatcher = new DomainEventDispatcher(serviceLocator); - _integrationEventDispatcher = new IntegrationEventDispatcher(serviceLocator); - _queryDispatcher = new QueryDispatcher(serviceLocator); - } + _commandDispatcher = new CommandDispatcher(serviceLocator); + _domainEventDispatcher = new DomainEventDispatcher(serviceLocator); + _integrationEventDispatcher = new IntegrationEventDispatcher(serviceLocator); + _queryDispatcher = new QueryDispatcher(serviceLocator); + } - /// - /// Commits the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to commit. - public void Commit(ICommand request) - { - _commandDispatcher.Commit(request); - } + /// + /// Commits the specified using Fire-and-Forget/In-Only MEP. + /// + /// The to commit. + public void Commit(ICommand request) + { + _commandDispatcher.Commit(request); + } - /// - /// Commits the specified asynchronous using Fire-and-Forget/In-Only MEP. - /// - /// The to commit. - /// The which may be configured. - /// A that represents the asynchronous operation. - public Task CommitAsync(ICommand request, Action setup = null) - { - return _commandDispatcher.CommitAsync(request, setup); - } + /// + /// Commits the specified asynchronous using Fire-and-Forget/In-Only MEP. + /// + /// The to commit. + /// The which may be configured. + /// A that represents the asynchronous operation. + public Task CommitAsync(ICommand request, Action setup = null) + { + return _commandDispatcher.CommitAsync(request, setup); + } - /// - /// Raises the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to raise. - public void Raise(IDomainEvent request) - { - _domainEventDispatcher.Raise(request); - } + /// + /// Raises the specified using Fire-and-Forget/In-Only MEP. + /// + /// The to raise. + public void Raise(IDomainEvent request) + { + _domainEventDispatcher.Raise(request); + } - /// - /// Raises the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to raise. - /// The which may be configured. - /// A that represents the asynchronous operation. - public Task RaiseAsync(IDomainEvent request, Action setup = null) - { - return _domainEventDispatcher.RaiseAsync(request, setup); - } + /// + /// Raises the specified using Fire-and-Forget/In-Only MEP. + /// + /// The to raise. + /// The which may be configured. + /// A that represents the asynchronous operation. + public Task RaiseAsync(IDomainEvent request, Action setup = null) + { + return _domainEventDispatcher.RaiseAsync(request, setup); + } - /// - /// Publishes the specified using Fire-and-Forget/In-Only MEP. - /// - /// The to publish. - public void Publish(IIntegrationEvent request) - { - _integrationEventDispatcher.Publish(request); - } + /// + /// Publishes the specified using Fire-and-Forget/In-Only MEP. + /// + /// The to publish. + public void Publish(IIntegrationEvent request) + { + _integrationEventDispatcher.Publish(request); + } - /// - /// Publishes the specified asynchronous using Fire-and-Forget/In-Only MEP. - /// - /// The to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - public Task PublishAsync(IIntegrationEvent request, Action setup = null) - { - return _integrationEventDispatcher.PublishAsync(request, setup); - } + /// + /// Publishes the specified asynchronous using Fire-and-Forget/In-Only MEP. + /// + /// The to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + public Task PublishAsync(IIntegrationEvent request, Action setup = null) + { + return _integrationEventDispatcher.PublishAsync(request, setup); + } - /// - /// Queries the specified using Request-Reply/In-Out MEP. - /// - /// The type of the result to return. - /// The to request. - /// The outcome of the query operation. - public TResult Query(IQuery request) - { - return _queryDispatcher.Query(request); - } + /// + /// Queries the specified using Request-Reply/In-Out MEP. + /// + /// The type of the result to return. + /// The to request. + /// The outcome of the query operation. + public TResult Query(IQuery request) + { + return _queryDispatcher.Query(request); + } - /// - /// Queries the specified asynchronous using Request-Reply/In-Out MEP. - /// - /// The type of the result to return. - /// The to request. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result contains the outcome of the query operation. - public Task QueryAsync(IQuery request, Action setup = null) - { - return _queryDispatcher.QueryAsync(request, setup); - } + /// + /// Queries the specified asynchronous using Request-Reply/In-Out MEP. + /// + /// The type of the result to return. + /// The to request. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result contains the outcome of the query operation. + public Task QueryAsync(IQuery request, Action setup = null) + { + return _queryDispatcher.QueryAsync(request, setup); } } diff --git a/src/Savvyio.Extensions.Dispatchers/SavvyioOptionsExtensions.cs b/src/Savvyio.Extensions.Dispatchers/SavvyioOptionsExtensions.cs index dd49abf1..e1d90c70 100644 --- a/src/Savvyio.Extensions.Dispatchers/SavvyioOptionsExtensions.cs +++ b/src/Savvyio.Extensions.Dispatchers/SavvyioOptionsExtensions.cs @@ -1,76 +1,75 @@ -using Savvyio.Commands; +using Savvyio.Commands; using Savvyio.Dispatchers; using Savvyio.Domain; using Savvyio.EventDriven; using Savvyio.Queries; using System.Linq; using System.Reflection; -using Savvyio.Reflection; using System.Runtime.CompilerServices; +using Cuemon.Reflection; -namespace Savvyio.Extensions +namespace Savvyio.Extensions; + +/// +/// Extension methods for the class. +/// +public static class SavvyioOptionsExtensions { /// - /// Extension methods for the class. + /// Adds an implementation of the interface. + /// + /// The type of the implementation to use. + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + /// The implementation will be type forwarded to: , , and . + public static SavvyioOptions AddMediator(this SavvyioOptions options) where TImplementation : class, IMediator + { + options.AddDispatcher(); + options.AddDispatcher(); + options.AddDispatcher(); + options.AddDispatcher(); + options.AddDispatcher(); + return options; + } + + /// + /// Enforce automatic discovery of handlers implementing the interface using either or lightweight . /// - public static class SavvyioOptionsExtensions + /// The to extend. + /// true to use brute assembly scanning for all interface implementations throughout the application domain; otherwise, false. + /// A reference to so that additional configuration calls can be chained. + [MethodImpl(MethodImplOptions.NoInlining)] + public static SavvyioOptions UseAutomaticDispatcherDiscovery(this SavvyioOptions options, bool bruteAssemblyScanning = false) { - /// - /// Adds an implementation of the interface. - /// - /// The type of the implementation to use. - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - /// The implementation will be type forwarded to: , , and . - public static SavvyioOptions AddMediator(this SavvyioOptions options) where TImplementation : class, IMediator + if (bruteAssemblyScanning) { - options.AddDispatcher(); - options.AddDispatcher(); - options.AddDispatcher(); - options.AddDispatcher(); - options.AddDispatcher(); - return options; + options.AddDispatchers(AssemblyContext.GetCurrentDomainAssemblies(o => o.ExcludedAssemblies.Add(typeof(SavvyioOptions).Assembly)).ToArray()); } - - /// - /// Enforce automatic discovery of handlers implementing the interface using either or lightweight . - /// - /// The to extend. - /// true to use brute assembly scanning for all interface implementations throughout the application domain; otherwise, false. - /// A reference to so that additional configuration calls can be chained. - [MethodImpl(MethodImplOptions.NoInlining)] - public static SavvyioOptions UseAutomaticDispatcherDiscovery(this SavvyioOptions options, bool bruteAssemblyScanning = false) + else { - if (bruteAssemblyScanning) - { - options.AddDispatchers(AssemblyContext.CurrentDomainAssemblies.ToArray()); - } - else - { - options.AddDispatchers(Assembly.GetCallingAssembly()); - } - return options; + options.AddDispatchers(Assembly.GetCallingAssembly()); } + return options; + } - /// - /// Enforce automatic discovery of dispatchers implementing the interface using either or lightweight . - /// - /// The to extend. - /// true to use brute assembly scanning for all interface implementations throughout the application domain; otherwise, false. - /// A reference to so that additional configuration calls can be chained. - [MethodImpl(MethodImplOptions.NoInlining)] - public static SavvyioOptions UseAutomaticHandlerDiscovery(this SavvyioOptions options, bool bruteAssemblyScanning = false) + /// + /// Enforce automatic discovery of dispatchers implementing the interface using either or lightweight . + /// + /// The to extend. + /// true to use brute assembly scanning for all interface implementations throughout the application domain; otherwise, false. + /// A reference to so that additional configuration calls can be chained. + [MethodImpl(MethodImplOptions.NoInlining)] + public static SavvyioOptions UseAutomaticHandlerDiscovery(this SavvyioOptions options, bool bruteAssemblyScanning = false) + { + if (bruteAssemblyScanning) { - if (bruteAssemblyScanning) - { - options.AddHandlers(AssemblyContext.CurrentDomainAssemblies.ToArray()); - } - else - { - options.AddHandlers(Assembly.GetCallingAssembly()); - } - return options; - + options.AddHandlers(AssemblyContext.GetCurrentDomainAssemblies(o => o.ExcludedAssemblies.Add(typeof(SavvyioOptions).Assembly)).ToArray()); } + else + { + options.AddHandlers(Assembly.GetCallingAssembly()); + } + return options; + } } diff --git a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntity.cs b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntity.cs index d35a4cce..2649e432 100644 --- a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntity.cs +++ b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntity.cs @@ -1,86 +1,85 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Cuemon.Extensions.Collections.Generic; using Savvyio.Domain; using Savvyio.Domain.EventSourcing; -namespace Savvyio.Extensions.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.EFCore.Domain.EventSourcing; + +/// +/// Provides a generic way for EF Core to surrogate and support an implementation of . +/// +/// The type of the entity that implements the interface. +/// The type of the key that uniquely identifies the entity. +/// +public class EfCoreTracedAggregateEntity : ITracedAggregateRoot where TEntity : class, IEntity, ITracedAggregateRoot { + private readonly IMetadataDictionary _metadata; + private readonly IReadOnlyList _events; + private readonly TKey _id; + private readonly DateTime _timestamp; + private readonly long _version; + private readonly string _type; + private readonly byte[] _payload; + + EfCoreTracedAggregateEntity() // efcore + { + } + /// - /// Provides a generic way for EF Core to surrogate and support an implementation of . + /// Initializes a new instance of the class. /// - /// The type of the entity that implements the interface. - /// The type of the key that uniquely identifies the entity. - /// - public class EfCoreTracedAggregateEntity : ITracedAggregateRoot where TEntity : class, IEntity, ITracedAggregateRoot + /// The traced aggregate to convert into this EF Core compatible instance. + /// The traced domain event to convert into this EF Core compatible instance. + /// The that is used when converting into a serialized format. + public EfCoreTracedAggregateEntity(TEntity aggregate, ITracedDomainEvent domainEvent, IMarshaller marshaller) { - private readonly IMetadataDictionary _metadata; - private readonly IReadOnlyList _events; - private readonly TKey _id; - private readonly DateTime _timestamp; - private readonly long _version; - private readonly string _type; - private readonly byte[] _payload; - - EfCoreTracedAggregateEntity() // efcore - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The traced aggregate to convert into this EF Core compatible instance. - /// The traced domain event to convert into this EF Core compatible instance. - /// The that is used when converting into a serialized format. - public EfCoreTracedAggregateEntity(TEntity aggregate, ITracedDomainEvent domainEvent, IMarshaller marshaller) - { - _id = aggregate.Id; - _version = domainEvent.GetAggregateVersion(); - _type = domainEvent.GetMemberType(); - _timestamp = domainEvent.GetTimestamp(); - _payload = domainEvent.ToByteArray(marshaller); - _metadata = aggregate.Metadata; - _events = domainEvent.Yield().ToList(); - } + _id = aggregate.Id; + _version = domainEvent.GetAggregateVersion(); + _type = domainEvent.GetMemberType(); + _timestamp = domainEvent.GetTimestamp(); + _payload = domainEvent.ToByteArray(marshaller); + _metadata = aggregate.Metadata; + _events = domainEvent.Yield().ToList(); + } - /// - /// Gets the value of the identifier associated with an aggregate. - /// - /// The value of the identifier associated with an aggregate. - public TKey Id => _id; + /// + /// Gets the value of the identifier associated with an aggregate. + /// + /// The value of the identifier associated with an aggregate. + public TKey Id => _id; - /// - /// Gets the timestamp of the traced domain event. - /// - /// The timestamp of the traced domain event. - public DateTime Timestamp => _timestamp; + /// + /// Gets the timestamp of the traced domain event. + /// + /// The timestamp of the traced domain event. + public DateTime Timestamp => _timestamp; - /// - /// Gets the version of the aggregate from the traced domain event. - /// - /// The version of the aggregate from the traced domain event. - public long Version => _version; + /// + /// Gets the version of the aggregate from the traced domain event. + /// + /// The version of the aggregate from the traced domain event. + public long Version => _version; - /// - /// Gets the CLR type of the traced domain event. - /// - /// The CLR type of the traced domain event. - public string Type => _type; + /// + /// Gets the CLR type of the traced domain event. + /// + /// The CLR type of the traced domain event. + public string Type => _type; - /// - /// Gets the payload of the traced domain event. - /// - /// The payload of the traced domain event. - public byte[] Payload => _payload; + /// + /// Gets the payload of the traced domain event. + /// + /// The payload of the traced domain event. + public byte[] Payload => _payload; - IMetadataDictionary IMetadata.Metadata => _metadata; + IMetadataDictionary IMetadata.Metadata => _metadata; - IReadOnlyList IAggregateRoot.Events => _events; + IReadOnlyList IAggregateRoot.Events => _events; - void IAggregateRoot.RemoveAllEvents() - { - // empty as we do not want to clear events due to dehydrate/rehydrate - } + void IAggregateRoot.RemoveAllEvents() + { + // empty as we do not want to clear events due to dehydrate/rehydrate } } diff --git a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntityExtensions.cs b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntityExtensions.cs index b53bf85e..2f8f8439 100644 --- a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntityExtensions.cs +++ b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntityExtensions.cs @@ -1,26 +1,25 @@ -using System; +using System; using Cuemon.Extensions.IO; using Savvyio.Domain.EventSourcing; -namespace Savvyio.Extensions.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.EFCore.Domain.EventSourcing; + +/// +/// Extension methods for the class. +/// +public static class EfCoreTracedAggregateEntityExtensions { /// - /// Extension methods for the class. + /// Converts the specified into an implementation of interface equivalent to . /// - public static class EfCoreTracedAggregateEntityExtensions + /// The type of the entity that implements the interface. + /// The type of the key that uniquely identifies the entity. + /// The entity to convert. + /// The originating type of . + /// The that is used when converting the specified into a deserialized version of . + /// A new instance of an implementation. + public static ITracedDomainEvent ToTracedDomainEvent(this EfCoreTracedAggregateEntity entity, Type tracedDomainEventType, IMarshaller marshaller) where TEntity : class, ITracedAggregateRoot { - /// - /// Converts the specified into an implementation of interface equivalent to . - /// - /// The type of the entity that implements the interface. - /// The type of the key that uniquely identifies the entity. - /// The entity to convert. - /// The originating type of . - /// The that is used when converting the specified into a deserialized version of . - /// A new instance of an implementation. - public static ITracedDomainEvent ToTracedDomainEvent(this EfCoreTracedAggregateEntity entity, Type tracedDomainEventType, IMarshaller marshaller) where TEntity : class, ITracedAggregateRoot - { - return (ITracedDomainEvent)marshaller.Deserialize(entity.Payload.ToStream(), tracedDomainEventType); - } + return (ITracedDomainEvent)marshaller.Deserialize(entity.Payload.ToStream(), tracedDomainEventType); } } diff --git a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntityOptions.cs b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntityOptions.cs index 99189a4b..e4feadef 100644 --- a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntityOptions.cs +++ b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateEntityOptions.cs @@ -1,248 +1,247 @@ -using Cuemon; +using Cuemon; using Cuemon.Configuration; -namespace Savvyio.Extensions.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.EFCore.Domain.EventSourcing; + +/// +/// Configuration options for . +/// +public class EfCoreTracedAggregateEntityOptions : IParameterObject { + private string _payloadColumnType; + private string _payloadColumnName; + private string _typeColumnType; + private string _typeColumnName; + private string _timestampColumnType; + private string _timestampColumnName; + private string _compositePrimaryKeyVersionColumnType; + private string _compositePrimaryKeyVersionColumnName; + private string _compositePrimaryKeyIdColumnType; + private string _compositePrimaryKeyIdColumnName; + private string _tableName; + /// - /// Configuration options for . + /// Initializes a new instance of the class. /// - public class EfCoreTracedAggregateEntityOptions : IParameterObject + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// DomainEvents + /// + /// + /// + /// id + /// + /// + /// + /// uniqueidentifier + /// + /// + /// + /// version + /// + /// + /// + /// int + /// + /// + /// + /// timestamp + /// + /// + /// + /// datetime + /// + /// + /// + /// type + /// + /// + /// + /// varchar(1024) + /// + /// + /// + /// payload + /// + /// + /// + /// varchar(max) + /// + /// + /// + public EfCoreTracedAggregateEntityOptions() { - private string _payloadColumnType; - private string _payloadColumnName; - private string _typeColumnType; - private string _typeColumnName; - private string _timestampColumnType; - private string _timestampColumnName; - private string _compositePrimaryKeyVersionColumnType; - private string _compositePrimaryKeyVersionColumnName; - private string _compositePrimaryKeyIdColumnType; - private string _compositePrimaryKeyIdColumnName; - private string _tableName; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// DomainEvents - /// - /// - /// - /// id - /// - /// - /// - /// uniqueidentifier - /// - /// - /// - /// version - /// - /// - /// - /// int - /// - /// - /// - /// timestamp - /// - /// - /// - /// datetime - /// - /// - /// - /// type - /// - /// - /// - /// varchar(1024) - /// - /// - /// - /// payload - /// - /// - /// - /// varchar(max) - /// - /// - /// - public EfCoreTracedAggregateEntityOptions() - { - TableName = "DomainEvents"; - CompositePrimaryKeyIdColumnName = "id"; - CompositePrimaryKeyIdColumnType = "uniqueidentifier"; - CompositePrimaryKeyVersionColumnName = "version"; - CompositePrimaryKeyVersionColumnType = "int"; - TimestampColumnName = "timestamp"; - TimestampColumnType = "datetime"; - TypeColumnName = "clrtype"; - TypeColumnType = "varchar(1024)"; - PayloadColumnName = "payload"; - PayloadColumnType = "varchar(max)"; - } + TableName = "DomainEvents"; + CompositePrimaryKeyIdColumnName = "id"; + CompositePrimaryKeyIdColumnType = "uniqueidentifier"; + CompositePrimaryKeyVersionColumnName = "version"; + CompositePrimaryKeyVersionColumnType = "int"; + TimestampColumnName = "timestamp"; + TimestampColumnType = "datetime"; + TypeColumnName = "clrtype"; + TypeColumnType = "varchar(1024)"; + PayloadColumnName = "payload"; + PayloadColumnType = "varchar(max)"; + } - /// - /// Gets or sets the table name of the Event Sourcing schema. - /// - /// The table name of the Event Sourcing schema. - public string TableName + /// + /// Gets or sets the table name of the Event Sourcing schema. + /// + /// The table name of the Event Sourcing schema. + public string TableName + { + get => _tableName; + set { - get => _tableName; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _tableName = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _tableName = value; } + } - /// - /// Gets or sets the identifier part of the composite PK column name of the Event Sourcing schema. - /// - /// The identifier part of the composite PK column name of the Event Sourcing schema. - public string CompositePrimaryKeyIdColumnName + /// + /// Gets or sets the identifier part of the composite PK column name of the Event Sourcing schema. + /// + /// The identifier part of the composite PK column name of the Event Sourcing schema. + public string CompositePrimaryKeyIdColumnName + { + get => _compositePrimaryKeyIdColumnName; + set { - get => _compositePrimaryKeyIdColumnName; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _compositePrimaryKeyIdColumnName = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _compositePrimaryKeyIdColumnName = value; } + } - /// - /// Gets or sets the identifier part composite PK column type of the Event Sourcing schema. - /// - /// The identifier part of the composite PK column type of the Event Sourcing schema. - public string CompositePrimaryKeyIdColumnType + /// + /// Gets or sets the identifier part composite PK column type of the Event Sourcing schema. + /// + /// The identifier part of the composite PK column type of the Event Sourcing schema. + public string CompositePrimaryKeyIdColumnType + { + get => _compositePrimaryKeyIdColumnType; + set { - get => _compositePrimaryKeyIdColumnType; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _compositePrimaryKeyIdColumnType = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _compositePrimaryKeyIdColumnType = value; } + } - /// - /// Gets or sets the version part of the composite PK column name of the Event Sourcing schema. - /// - /// The version part of the composite PK column name of the Event Sourcing schema. - public string CompositePrimaryKeyVersionColumnName + /// + /// Gets or sets the version part of the composite PK column name of the Event Sourcing schema. + /// + /// The version part of the composite PK column name of the Event Sourcing schema. + public string CompositePrimaryKeyVersionColumnName + { + get => _compositePrimaryKeyVersionColumnName; + set { - get => _compositePrimaryKeyVersionColumnName; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _compositePrimaryKeyVersionColumnName = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _compositePrimaryKeyVersionColumnName = value; } + } - /// - /// Gets or sets the version part composite PK column type of the Event Sourcing schema. - /// - /// The version part of the composite PK column type of the Event Sourcing schema. - public string CompositePrimaryKeyVersionColumnType + /// + /// Gets or sets the version part composite PK column type of the Event Sourcing schema. + /// + /// The version part of the composite PK column type of the Event Sourcing schema. + public string CompositePrimaryKeyVersionColumnType + { + get => _compositePrimaryKeyVersionColumnType; + set { - get => _compositePrimaryKeyVersionColumnType; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _compositePrimaryKeyVersionColumnType = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _compositePrimaryKeyVersionColumnType = value; } + } - /// - /// Gets or sets the timestamp column name of the Event Sourcing schema. - /// - /// The timestamp column name of the Event Sourcing schema. - public string TimestampColumnName + /// + /// Gets or sets the timestamp column name of the Event Sourcing schema. + /// + /// The timestamp column name of the Event Sourcing schema. + public string TimestampColumnName + { + get => _timestampColumnName; + set { - get => _timestampColumnName; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _timestampColumnName = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _timestampColumnName = value; } + } - /// - /// Gets or sets the timestamp column type of the Event Sourcing schema. - /// - /// The timestamp column type of the Event Sourcing schema. - public string TimestampColumnType + /// + /// Gets or sets the timestamp column type of the Event Sourcing schema. + /// + /// The timestamp column type of the Event Sourcing schema. + public string TimestampColumnType + { + get => _timestampColumnType; + set { - get => _timestampColumnType; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _timestampColumnType = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _timestampColumnType = value; } + } - /// - /// Gets or sets the CLR type column name of the Event Sourcing schema. - /// - /// The CLR type column name of the Event Sourcing schema. - public string TypeColumnName + /// + /// Gets or sets the CLR type column name of the Event Sourcing schema. + /// + /// The CLR type column name of the Event Sourcing schema. + public string TypeColumnName + { + get => _typeColumnName; + set { - get => _typeColumnName; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _typeColumnName = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _typeColumnName = value; } + } - /// - /// Gets or sets the CLR type column type of the Event Sourcing schema. - /// - /// The CLR type column type of the Event Sourcing schema. - public string TypeColumnType + /// + /// Gets or sets the CLR type column type of the Event Sourcing schema. + /// + /// The CLR type column type of the Event Sourcing schema. + public string TypeColumnType + { + get => _typeColumnType; + set { - get => _typeColumnType; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _typeColumnType = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _typeColumnType = value; } + } - /// - /// Gets or sets the payload (serialized data) column name of the Event Sourcing schema. - /// - /// The payload (serialized data) column name of the Event Sourcing schema. - public string PayloadColumnName + /// + /// Gets or sets the payload (serialized data) column name of the Event Sourcing schema. + /// + /// The payload (serialized data) column name of the Event Sourcing schema. + public string PayloadColumnName + { + get => _payloadColumnName; + set { - get => _payloadColumnName; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _payloadColumnName = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _payloadColumnName = value; } + } - /// - /// Gets or sets the payload (serialized data) column type of the Event Sourcing schema. - /// - /// The payload (serialized data) column type of the Event Sourcing schema. - public string PayloadColumnType + /// + /// Gets or sets the payload (serialized data) column type of the Event Sourcing schema. + /// + /// The payload (serialized data) column type of the Event Sourcing schema. + public string PayloadColumnType + { + get => _payloadColumnType; + set { - get => _payloadColumnType; - set - { - Validator.ThrowIfNullOrWhitespace(value); - _payloadColumnType = value; - } + Validator.ThrowIfNullOrWhitespace(value); + _payloadColumnType = value; } } } diff --git a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateRepository.cs b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateRepository.cs index 7f3e07c6..7a125c2f 100644 --- a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateRepository.cs +++ b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/EfCoreTracedAggregateRepository.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -8,78 +8,77 @@ using Savvyio.Domain; using Savvyio.Domain.EventSourcing; -namespace Savvyio.Extensions.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.EFCore.Domain.EventSourcing; + +/// +/// Provides an implementation of the that is optimized for Domain Driven Design and Event Sourcing. +/// +/// The type of the entity that implements the interface. +/// The type of the key that uniquely identifies the entity. +/// +/// +public class EfCoreTracedAggregateRepository : ITracedAggregateRepository + where TEntity : class, IEntity, ITracedAggregateRoot { + private readonly EfCoreRepository, TKey> _repository; + private readonly IMarshaller _marshaller; + /// - /// Provides an implementation of the that is optimized for Domain Driven Design and Event Sourcing. + /// Initializes a new instance of the class. /// - /// The type of the entity that implements the interface. - /// The type of the key that uniquely identifies the entity. - /// - /// - public class EfCoreTracedAggregateRepository : ITracedAggregateRepository - where TEntity : class, IEntity, ITracedAggregateRoot + /// The that handles actual I/O communication with a source of data. + /// The that is used when converting between and arbitrary data. + public EfCoreTracedAggregateRepository(IEfCoreDataSource source, IMarshaller marshaller) { - private readonly EfCoreRepository, TKey> _repository; - private readonly IMarshaller _marshaller; + _repository = new EfCoreRepository, TKey>(source); + _marshaller = marshaller; + } - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - /// The that is used when converting between and arbitrary data. - public EfCoreTracedAggregateRepository(IEfCoreDataSource source, IMarshaller marshaller) + /// + /// Loads an aggregate from the specified asynchronous. + /// + /// The key that uniquely identifies the aggregate. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the entity of the operation or null if not found. + /// + /// does not have a suitable constructor. + /// + public async Task GetByIdAsync(TKey id, Action setup = null) + { + var entities = await _repository.FindAllAsync(entity => entity.Id.Equals(id), setup).ConfigureAwait(false); + var events = entities.OrderBy(entity => entity.Version).Select(entity => entity.ToTracedDomainEvent(Type.GetType(entity.Type), _marshaller)); + try { - _repository = new EfCoreRepository, TKey>(source); - _marshaller = marshaller; + return ActivatorFactory.CreateInstance, TEntity>(id, events, o => o.Flags = new MemberReflection()); } - - /// - /// Loads an aggregate from the specified asynchronous. - /// - /// The key that uniquely identifies the aggregate. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the entity of the operation or null if not found. - /// - /// does not have a suitable constructor. - /// - public async Task GetByIdAsync(TKey id, Action setup = null) + catch (MissingMethodException e) { - var entities = await _repository.FindAllAsync(entity => entity.Id.Equals(id), setup).ConfigureAwait(false); - var events = entities.OrderBy(entity => entity.Version).Select(entity => entity.ToTracedDomainEvent(Type.GetType(entity.Type), _marshaller)); - try - { - return ActivatorFactory.CreateInstance, TEntity>(id, events, o => o.Flags = new MemberReflection()); - } - catch (MissingMethodException e) - { - throw new MissingMethodException($"Constructor on type '{typeof(TEntity).Name}' not found. Please add a private constructor with this signature: '{typeof(TEntity).Name}({typeof(TKey).Name}, {typeof(IEnumerable).ToFriendlyName()})'.", e); - } + throw new MissingMethodException($"Constructor on type '{typeof(TEntity).Name}' not found. Please add a private constructor with this signature: '{typeof(TEntity).Name}({typeof(TKey).Name}, {typeof(IEnumerable).ToFriendlyName()})'.", e); } + } - /// - /// Marks the specified to be added in the data store when is called. - /// - /// The aggregate to add. - public void Add(TEntity entity) + /// + /// Marks the specified to be added in the data store when is called. + /// + /// The aggregate to add. + public void Add(TEntity entity) + { + foreach (var current in entity.Events) { - foreach (var current in entity.Events) - { - _repository.Add(new EfCoreTracedAggregateEntity(entity, current, _marshaller)); - } - entity.RemoveAllEvents(); + _repository.Add(new EfCoreTracedAggregateEntity(entity, current, _marshaller)); } + entity.RemoveAllEvents(); + } - /// - /// Marks the specified to be added in the data store when is called. - /// - /// The aggregates to add. - public void AddRange(IEnumerable entities) + /// + /// Marks the specified to be added in the data store when is called. + /// + /// The aggregates to add. + public void AddRange(IEnumerable entities) + { + foreach (var entity in entities) { - foreach (var entity in entities) - { - Add(entity); - } + Add(entity); } } } diff --git a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/ModelBuilderExtensions.cs b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/ModelBuilderExtensions.cs index d54cd593..57661c78 100644 --- a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/ModelBuilderExtensions.cs +++ b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/ModelBuilderExtensions.cs @@ -1,46 +1,45 @@ -using System; +using System; using Cuemon.Extensions; using Microsoft.EntityFrameworkCore; using Savvyio.Domain.EventSourcing; -namespace Savvyio.Extensions.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.EFCore.Domain.EventSourcing; + +/// +/// Extension methods for the class. +/// +public static class ModelBuilderExtensions { /// - /// Extension methods for the class. + /// Adds a default implementation of an EF Core compatible Event Sourcing model. /// - public static class ModelBuilderExtensions + /// The type of the entity that implements the interface. + /// The type of the key that uniquely identifies the entity. + /// The to extend. + /// The which may be configured. + /// A reference to so that additional configuration calls can be chained. + public static ModelBuilder AddEventSourcing(this ModelBuilder mb, Action setup = null) where TEntity : class, ITracedAggregateRoot { - /// - /// Adds a default implementation of an EF Core compatible Event Sourcing model. - /// - /// The type of the entity that implements the interface. - /// The type of the key that uniquely identifies the entity. - /// The to extend. - /// The which may be configured. - /// A reference to so that additional configuration calls can be chained. - public static ModelBuilder AddEventSourcing(this ModelBuilder mb, Action setup = null) where TEntity : class, ITracedAggregateRoot + var options = setup.Configure(); + return mb.Entity>(entity => { - var options = setup.Configure(); - return mb.Entity>(entity => - { - entity.ToTable(options.TableName); - entity.HasKey(es => new { es.Id, es.Version }); - entity.Property(es => es.Id) - .HasColumnName(options.CompositePrimaryKeyIdColumnName) - .HasColumnType(options.CompositePrimaryKeyIdColumnType); - entity.Property(es => es.Version) - .HasColumnName(options.CompositePrimaryKeyVersionColumnName) - .HasColumnType(options.CompositePrimaryKeyVersionColumnType); - entity.Property(es => es.Timestamp) - .HasColumnName(options.TimestampColumnName) - .HasColumnType(options.TimestampColumnType); - entity.Property(es => es.Type) - .HasColumnName(options.TypeColumnName) - .HasColumnType(options.TypeColumnType); - entity.Property(es => es.Payload) - .HasColumnName(options.PayloadColumnName) - .HasColumnType(options.PayloadColumnType); - }); - } + entity.ToTable(options.TableName); + entity.HasKey(es => new { es.Id, es.Version }); + entity.Property(es => es.Id) + .HasColumnName(options.CompositePrimaryKeyIdColumnName) + .HasColumnType(options.CompositePrimaryKeyIdColumnType); + entity.Property(es => es.Version) + .HasColumnName(options.CompositePrimaryKeyVersionColumnName) + .HasColumnType(options.CompositePrimaryKeyVersionColumnType); + entity.Property(es => es.Timestamp) + .HasColumnName(options.TimestampColumnName) + .HasColumnType(options.TimestampColumnType); + entity.Property(es => es.Type) + .HasColumnName(options.TypeColumnName) + .HasColumnType(options.TypeColumnType); + entity.Property(es => es.Payload) + .HasColumnName(options.PayloadColumnName) + .HasColumnType(options.PayloadColumnType); + }); } } diff --git a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/Savvyio.Extensions.EFCore.Domain.EventSourcing.csproj b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/Savvyio.Extensions.EFCore.Domain.EventSourcing.csproj index 71b9faaa..d5759c30 100644 --- a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/Savvyio.Extensions.EFCore.Domain.EventSourcing.csproj +++ b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/Savvyio.Extensions.EFCore.Domain.EventSourcing.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O support for Microsoft Entity Framework Core - optimized for Domain Driven Design and Event Sourcing. diff --git a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/TracedDomainEventExtensions.cs b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/TracedDomainEventExtensions.cs index 521c58f2..18e1dc69 100644 --- a/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/TracedDomainEventExtensions.cs +++ b/src/Savvyio.Extensions.EFCore.Domain.EventSourcing/TracedDomainEventExtensions.cs @@ -1,23 +1,22 @@ -using Cuemon.Extensions.IO; +using Cuemon.Extensions.IO; using Savvyio.Domain.EventSourcing; -namespace Savvyio.Extensions.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.EFCore.Domain.EventSourcing; + +/// +/// Extension methods for the interface. +/// +public static class TracedDomainEventExtensions { /// - /// Extension methods for the interface. + /// Converts the specified into an array of bytes. /// - public static class TracedDomainEventExtensions + /// The domain event to convert. + /// The that is used when converting into an array of bytes. + /// A byte[] that is equivalent to . + public static byte[] ToByteArray(this ITracedDomainEvent domainEvent, IMarshaller marshaller) { - /// - /// Converts the specified into an array of bytes. - /// - /// The domain event to convert. - /// The that is used when converting into an array of bytes. - /// A byte[] that is equivalent to . - public static byte[] ToByteArray(this ITracedDomainEvent domainEvent, IMarshaller marshaller) - { - var bytes = marshaller.Serialize(domainEvent, typeof(ITracedDomainEvent)).ToByteArray(); - return bytes; - } + var bytes = marshaller.Serialize(domainEvent, typeof(ITracedDomainEvent)).ToByteArray(); + return bytes; } } diff --git a/src/Savvyio.Extensions.EFCore.Domain/DomainEventDispatcherExtensions.cs b/src/Savvyio.Extensions.EFCore.Domain/DomainEventDispatcherExtensions.cs index 9d210f74..0680033a 100644 --- a/src/Savvyio.Extensions.EFCore.Domain/DomainEventDispatcherExtensions.cs +++ b/src/Savvyio.Extensions.EFCore.Domain/DomainEventDispatcherExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -7,55 +7,54 @@ using Microsoft.EntityFrameworkCore; using Savvyio.Domain; -namespace Savvyio.Extensions.EFCore.Domain +namespace Savvyio.Extensions.EFCore.Domain; + +/// +/// Extension methods for the interface. +/// +public static class DomainEventDispatcherExtensions { /// - /// Extension methods for the interface. + /// Raises domain events from the specified to handlers that implements the interface. /// - public static class DomainEventDispatcherExtensions + /// The to extend. + /// The to extract aggregate(s) and publish domain events from. + public static void RaiseMany(this IDomainEventDispatcher dispatcher, DbContext context) { - /// - /// Raises domain events from the specified to handlers that implements the interface. - /// - /// The to extend. - /// The to extract aggregate(s) and publish domain events from. - public static void RaiseMany(this IDomainEventDispatcher dispatcher, DbContext context) + var domainEvents = ExtractDomainEvents(context); + foreach (var de in domainEvents) { - var domainEvents = ExtractDomainEvents(context); - foreach (var de in domainEvents) - { - dispatcher.Raise(de); - } + dispatcher.Raise(de); } + } - /// - /// Asynchronously raises domain events from the specified to handlers that implements the interface. - /// - /// The to extend. - /// The to extract aggregate(s) and publish domain events from. - /// The which may be configured. - /// A that represents the asynchronous operation. - public static async Task RaiseManyAsync(this IDomainEventDispatcher dispatcher, DbContext context, Action setup = null) + /// + /// Asynchronously raises domain events from the specified to handlers that implements the interface. + /// + /// The to extend. + /// The to extract aggregate(s) and publish domain events from. + /// The which may be configured. + /// A that represents the asynchronous operation. + public static async Task RaiseManyAsync(this IDomainEventDispatcher dispatcher, DbContext context, Action setup = null) + { + var domainEvents = ExtractDomainEvents(context); + foreach (var de in domainEvents) { - var domainEvents = ExtractDomainEvents(context); - foreach (var de in domainEvents) - { - await dispatcher.RaiseAsync(de, setup).ConfigureAwait(false); - } + await dispatcher.RaiseAsync(de, setup).ConfigureAwait(false); } + } - private static IEnumerable ExtractDomainEvents(DbContext context) + private static IEnumerable ExtractDomainEvents(DbContext context) + { + var entries = context.ChangeTracker.Entries>().Where(entry => entry.Entity.Events.Any()).ToList(); + foreach (var aggregate in entries.Select(entry => entry.Entity)) { - var entries = context.ChangeTracker.Entries>().Where(entry => entry.Entity.Events.Any()).ToList(); - foreach (var aggregate in entries.Select(entry => entry.Entity)) + var events = aggregate.Events.ToList(); + var eventsType = events.First().GetType(); + if (eventsType.HasInterfaces(typeof(IDomainEvent))) { aggregate.RemoveAllEvents(); } + foreach (var e in events) { - var events = aggregate.Events.ToList(); - var eventsType = events.First().GetType(); - if (eventsType.HasInterfaces(typeof(IDomainEvent))) { aggregate.RemoveAllEvents(); } - foreach (var e in events) - { - yield return e.MergeMetadata(aggregate); - } + yield return e.MergeMetadata(aggregate); } } } diff --git a/src/Savvyio.Extensions.EFCore.Domain/EfCoreAggregateDataSource.cs b/src/Savvyio.Extensions.EFCore.Domain/EfCoreAggregateDataSource.cs index c1533ed1..468d8271 100644 --- a/src/Savvyio.Extensions.EFCore.Domain/EfCoreAggregateDataSource.cs +++ b/src/Savvyio.Extensions.EFCore.Domain/EfCoreAggregateDataSource.cs @@ -1,53 +1,52 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Cuemon.Threading; using Microsoft.EntityFrameworkCore; using Savvyio.Domain; -namespace Savvyio.Extensions.EFCore.Domain +namespace Savvyio.Extensions.EFCore.Domain; + +/// +/// Provides an implementation of the that is optimized for Domain Driven Design and the concept of Aggregate Root. +/// +/// +public class EfCoreAggregateDataSource : EfCoreDataSource { + private readonly IDomainEventDispatcher _dispatcher; + /// - /// Provides an implementation of the that is optimized for Domain Driven Design and the concept of Aggregate Root. + /// Initializes a new instance of the class. /// - /// - public class EfCoreAggregateDataSource : EfCoreDataSource + /// The that are responsible for raising domain events. + /// The to associate with this data store. + protected EfCoreAggregateDataSource(IDomainEventDispatcher dispatcher, DbContext dbContext) : base(dbContext) { - private readonly IDomainEventDispatcher _dispatcher; - - /// - /// Initializes a new instance of the class. - /// - /// The that are responsible for raising domain events. - /// The to associate with this data store. - protected EfCoreAggregateDataSource(IDomainEventDispatcher dispatcher, DbContext dbContext) : base(dbContext) - { - _dispatcher = dispatcher; - } + _dispatcher = dispatcher; + } - /// - /// Initializes a new instance of the class. - /// - /// The that are responsible for raising domain events. - /// The used to configure this instance. - public EfCoreAggregateDataSource(IDomainEventDispatcher dispatcher, EfCoreDataSourceOptions options) : base(options) - { - _dispatcher = dispatcher; - } + /// + /// Initializes a new instance of the class. + /// + /// The that are responsible for raising domain events. + /// The used to configure this instance. + public EfCoreAggregateDataSource(IDomainEventDispatcher dispatcher, EfCoreDataSourceOptions options) : base(options) + { + _dispatcher = dispatcher; + } - /// - /// Saves the different implementations as one transaction towards a data store asynchronous. - /// - /// The which may be configured. - /// A that represents the asynchronous operation. - /// Will, just before is called, extract and call for all aggregate roots contained within the backing . - public override async Task SaveChangesAsync(Action setup = null) + /// + /// Saves the different implementations as one transaction towards a data store asynchronous. + /// + /// The which may be configured. + /// A that represents the asynchronous operation. + /// Will, just before is called, extract and call for all aggregate roots contained within the backing . + public override async Task SaveChangesAsync(Action setup = null) + { + if (_dispatcher != null) { - if (_dispatcher != null) - { - await _dispatcher.RaiseManyAsync(DbContext, setup).ConfigureAwait(false); - } - await base.SaveChangesAsync(setup).ConfigureAwait(false); + await _dispatcher.RaiseManyAsync(DbContext, setup).ConfigureAwait(false); } + await base.SaveChangesAsync(setup).ConfigureAwait(false); } } diff --git a/src/Savvyio.Extensions.EFCore.Domain/EfCoreAggregateRepository.cs b/src/Savvyio.Extensions.EFCore.Domain/EfCoreAggregateRepository.cs index 2a3dad65..1d5ed494 100644 --- a/src/Savvyio.Extensions.EFCore.Domain/EfCoreAggregateRepository.cs +++ b/src/Savvyio.Extensions.EFCore.Domain/EfCoreAggregateRepository.cs @@ -1,21 +1,20 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Extensions.EFCore.Domain +namespace Savvyio.Extensions.EFCore.Domain; + +/// +/// Provides an implementation of the that is optimized for Domain Driven Design. +/// +/// The type of the entity that implements the interface. +/// The type of the key that uniquely identifies the entity. +/// +public class EfCoreAggregateRepository : EfCoreRepository, IAggregateRepository where TEntity : class, IAggregateRoot { /// - /// Provides an implementation of the that is optimized for Domain Driven Design. + /// Initializes a new instance of the class. /// - /// The type of the entity that implements the interface. - /// The type of the key that uniquely identifies the entity. - /// - public class EfCoreAggregateRepository : EfCoreRepository, IAggregateRepository where TEntity : class, IAggregateRoot + /// The that handles actual I/O communication with a source of data. + public EfCoreAggregateRepository(IEfCoreDataSource source) : base(source) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - public EfCoreAggregateRepository(IEfCoreDataSource source) : base(source) - { - } } } diff --git a/src/Savvyio.Extensions.EFCore.Domain/Savvyio.Extensions.EFCore.Domain.csproj b/src/Savvyio.Extensions.EFCore.Domain/Savvyio.Extensions.EFCore.Domain.csproj index c2b0440f..d0e3857b 100644 --- a/src/Savvyio.Extensions.EFCore.Domain/Savvyio.Extensions.EFCore.Domain.csproj +++ b/src/Savvyio.Extensions.EFCore.Domain/Savvyio.Extensions.EFCore.Domain.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O support for Microsoft Entity Framework Core - optimized for DDD (Domain Driven Design). diff --git a/src/Savvyio.Extensions.EFCore/EfCoreDataSource.cs b/src/Savvyio.Extensions.EFCore/EfCoreDataSource.cs index 1f6a7c89..f4f211cd 100644 --- a/src/Savvyio.Extensions.EFCore/EfCoreDataSource.cs +++ b/src/Savvyio.Extensions.EFCore/EfCoreDataSource.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon; using Cuemon.Extensions; @@ -6,63 +6,62 @@ using Microsoft.EntityFrameworkCore; using Savvyio.Domain; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +/// +/// Provides a default implementation of the interface to support the actual I/O communication with a source of data using Microsoft Entity Framework Core. +/// +/// +/// +public class EfCoreDataSource : Disposable, IEfCoreDataSource { /// - /// Provides a default implementation of the interface to support the actual I/O communication with a source of data using Microsoft Entity Framework Core. + /// Initializes a new instance of the class. /// - /// - /// - public class EfCoreDataSource : Disposable, IEfCoreDataSource + /// The to associate with this data store. + protected EfCoreDataSource(DbContext dbContext) { - /// - /// Initializes a new instance of the class. - /// - /// The to associate with this data store. - protected EfCoreDataSource(DbContext dbContext) - { - DbContext = dbContext; - } + DbContext = dbContext; + } - /// - /// Initializes a new instance of the class. - /// - /// The used to configure this instance. - public EfCoreDataSource(EfCoreDataSourceOptions options) - { - DbContext = new EfCoreDbContext(options); - } + /// + /// Initializes a new instance of the class. + /// + /// The used to configure this instance. + public EfCoreDataSource(EfCoreDataSourceOptions options) + { + DbContext = new EfCoreDbContext(options); + } - /// - /// Gets the session of this data source. - /// - /// The session of this data source. - public DbContext DbContext { get; } + /// + /// Gets the session of this data source. + /// + /// The session of this data source. + public DbContext DbContext { get; } - /// - /// Creates a that can be used to query and save instances of . - /// - /// The type of entity for which a set should be returned. - /// A set for the given entity type. - public DbSet Set() where TEntity : class => DbContext.Set(); + /// + /// Creates a that can be used to query and save instances of . + /// + /// The type of entity for which a set should be returned. + /// A set for the given entity type. + public DbSet Set() where TEntity : class => DbContext.Set(); - /// - /// Saves the different implementations as one transaction towards a data store asynchronous. - /// - /// The which may be configured. - /// A that represents the asynchronous operation. - public virtual async Task SaveChangesAsync(Action setup = null) - { - var options = setup.Configure(); - await DbContext.SaveChangesAsync(options.CancellationToken).ConfigureAwait(false); - } + /// + /// Saves the different implementations as one transaction towards a data store asynchronous. + /// + /// The which may be configured. + /// A that represents the asynchronous operation. + public virtual async Task SaveChangesAsync(Action setup = null) + { + var options = setup.Configure(); + await DbContext.SaveChangesAsync(options.CancellationToken).ConfigureAwait(false); + } - /// - /// Called when this object is being disposed by either or having disposing set to true and is false. - /// - protected override void OnDisposeManagedResources() - { - DbContext?.Dispose(); - } + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected override void OnDisposeManagedResources() + { + DbContext?.Dispose(); } } diff --git a/src/Savvyio.Extensions.EFCore/EfCoreDataSourceOptions.cs b/src/Savvyio.Extensions.EFCore/EfCoreDataSourceOptions.cs index 685199a1..13d07975 100644 --- a/src/Savvyio.Extensions.EFCore/EfCoreDataSourceOptions.cs +++ b/src/Savvyio.Extensions.EFCore/EfCoreDataSourceOptions.cs @@ -1,37 +1,36 @@ -using System; +using System; using Cuemon.Configuration; using Microsoft.EntityFrameworkCore; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +/// +/// Configuration options for . +/// +public class EfCoreDataSourceOptions : IParameterObject { /// - /// Configuration options for . + /// Initializes a new instance of the class. /// - public class EfCoreDataSourceOptions : IParameterObject + public EfCoreDataSourceOptions() { - /// - /// Initializes a new instance of the class. - /// - public EfCoreDataSourceOptions() - { - } + } - /// - /// Gets or sets the delegate responsible for configuring the model of the . - /// - /// The delegate responsible for configuring the model of the . - public Action ModelConstructor { get; set; } + /// + /// Gets or sets the delegate responsible for configuring the model of the . + /// + /// The delegate responsible for configuring the model of the . + public Action ModelConstructor { get; set; } - /// - /// Gets or sets the delegate responsible for configuring defaults and conventions of the . - /// - /// The delegate responsible for configuring defaults and conventions of the . - public Action ConventionsConfigurator { get; set; } + /// + /// Gets or sets the delegate responsible for configuring defaults and conventions of the . + /// + /// The delegate responsible for configuring defaults and conventions of the . + public Action ConventionsConfigurator { get; set; } - /// - /// Gets or sets the delegate responsible for configuring the . - /// - /// The delegate responsible for configuring the . - public Action ContextConfigurator { get; set; } - } + /// + /// Gets or sets the delegate responsible for configuring the . + /// + /// The delegate responsible for configuring the . + public Action ContextConfigurator { get; set; } } diff --git a/src/Savvyio.Extensions.EFCore/EfCoreDataStore.cs b/src/Savvyio.Extensions.EFCore/EfCoreDataStore.cs index 7c63fa6b..003971ec 100644 --- a/src/Savvyio.Extensions.EFCore/EfCoreDataStore.cs +++ b/src/Savvyio.Extensions.EFCore/EfCoreDataStore.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -9,96 +9,95 @@ using Savvyio.Data; using Savvyio.Domain; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +/// +/// Provides a default implementation of the interface to serve as an abstraction layer before the actual I/O communication with a source of data using Microsoft Entity Framework Core. +/// +/// The type of the DTO. +/// +/// +public class EfCoreDataStore : IPersistentDataStore> where T : class { /// - /// Provides a default implementation of the interface to serve as an abstraction layer before the actual I/O communication with a source of data using Microsoft Entity Framework Core. + /// Initializes a new instance of the class. /// - /// The type of the DTO. - /// - /// - public class EfCoreDataStore : IPersistentDataStore> where T : class + /// The that handles actual I/O communication with a source of data. + public EfCoreDataStore(IEfCoreDataSource source) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - public EfCoreDataStore(IEfCoreDataSource source) - { - Validator.ThrowIfNull(source); - Set = source.Set(); - UnitOfWork = source; - } + Validator.ThrowIfNull(source); + Set = source.Set(); + UnitOfWork = source; + } - /// - /// Gets a that can be used to query and save instances of . - /// - /// The that can be used to query and save instances of . - protected DbSet Set { get; } + /// + /// Gets a that can be used to query and save instances of . + /// + /// The that can be used to query and save instances of . + protected DbSet Set { get; } - /// - /// Gets a that can be used to save instances of . - /// - /// The that can be used to save instances of . - protected IUnitOfWork UnitOfWork { get; } + /// + /// Gets a that can be used to save instances of . + /// + /// The that can be used to save instances of . + protected IUnitOfWork UnitOfWork { get; } - /// - /// Creates the specified asynchronous in the associated . - /// - /// The object to create in the associated . - /// The which may be configured. - /// A that represents the asynchronous operation. - public virtual async Task CreateAsync(T dto, Action setup = null) - { - Set.Add(dto); - await UnitOfWork.SaveChangesAsync(setup).ConfigureAwait(false); - } + /// + /// Creates the specified asynchronous in the associated . + /// + /// The object to create in the associated . + /// The which may be configured. + /// A that represents the asynchronous operation. + public virtual async Task CreateAsync(T dto, Action setup = null) + { + Set.Add(dto); + await UnitOfWork.SaveChangesAsync(setup).ConfigureAwait(false); + } - /// - /// Updates the specified asynchronous in the associated . - /// - /// The object to update in the associated . - /// The which may be configured. - /// A that represents the asynchronous operation. - public virtual async Task UpdateAsync(T dto, Action setup = null) - { - Set.Update(dto); - await UnitOfWork.SaveChangesAsync(setup).ConfigureAwait(false); - } + /// + /// Updates the specified asynchronous in the associated . + /// + /// The object to update in the associated . + /// The which may be configured. + /// A that represents the asynchronous operation. + public virtual async Task UpdateAsync(T dto, Action setup = null) + { + Set.Update(dto); + await UnitOfWork.SaveChangesAsync(setup).ConfigureAwait(false); + } - /// - /// Loads the object from the specified asynchronous. - /// - /// The key that uniquely identifies the object. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the object of the operation or null if not found. - public virtual Task GetByIdAsync(object id, Action setup = null) - { - var options = setup.Configure(); - return Set.FindAsync(new[] { id }, options.CancellationToken).AsTask(); - } + /// + /// Loads the object from the specified asynchronous. + /// + /// The key that uniquely identifies the object. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the object of the operation or null if not found. + public virtual Task GetByIdAsync(object id, Action setup = null) + { + var options = setup.Configure(); + return Set.FindAsync(new[] { id }, options.CancellationToken).AsTask(); + } - /// - /// Finds all objects matching the specified asynchronous in the associated . - /// - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the matching objects of the operation or an empty sequence if no match was found. - public virtual async Task> FindAllAsync(Action> setup = null) - { - var options = setup.Configure(); - return await (options.Predicate == null ? Set.ToListAsync(options.CancellationToken).ConfigureAwait(false) : Set.Where(options.Predicate).ToListAsync(options.CancellationToken).ConfigureAwait(false)); - } + /// + /// Finds all objects matching the specified asynchronous in the associated . + /// + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the matching objects of the operation or an empty sequence if no match was found. + public virtual async Task> FindAllAsync(Action> setup = null) + { + var options = setup.Configure(); + return await (options.Predicate == null ? Set.ToListAsync(options.CancellationToken).ConfigureAwait(false) : Set.Where(options.Predicate).ToListAsync(options.CancellationToken).ConfigureAwait(false)); + } - /// - /// Deletes the specified asynchronous in the associated . - /// - /// The object to delete in the associated . - /// The which may be configured. - /// A that represents the asynchronous operation. - public virtual async Task DeleteAsync(T dto, Action setup = null) - { - Set.Remove(dto); - await UnitOfWork.SaveChangesAsync(setup).ConfigureAwait(false); - } + /// + /// Deletes the specified asynchronous in the associated . + /// + /// The object to delete in the associated . + /// The which may be configured. + /// A that represents the asynchronous operation. + public virtual async Task DeleteAsync(T dto, Action setup = null) + { + Set.Remove(dto); + await UnitOfWork.SaveChangesAsync(setup).ConfigureAwait(false); } } diff --git a/src/Savvyio.Extensions.EFCore/EfCoreDbContext.cs b/src/Savvyio.Extensions.EFCore/EfCoreDbContext.cs index 32201fab..324eeb10 100644 --- a/src/Savvyio.Extensions.EFCore/EfCoreDbContext.cs +++ b/src/Savvyio.Extensions.EFCore/EfCoreDbContext.cs @@ -1,85 +1,84 @@ -using Cuemon.Configuration; +using Cuemon.Configuration; using Microsoft.EntityFrameworkCore; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +/// +/// Provides a default implementation of the class to support Savvy I/O extensions of Microsoft Entity Framework Core. +/// +/// +/// +public class EfCoreDbContext : DbContext, IConfigurable { /// - /// Provides a default implementation of the class to support Savvy I/O extensions of Microsoft Entity Framework Core. + /// Initializes a new instance of the class. /// - /// - /// - public class EfCoreDbContext : DbContext, IConfigurable + /// The used to configure this instance. + public EfCoreDbContext(EfCoreDataSourceOptions options) { - /// - /// Initializes a new instance of the class. - /// - /// The used to configure this instance. - public EfCoreDbContext(EfCoreDataSourceOptions options) - { - Options = options ?? new EfCoreDataSourceOptions(); - } - - /// - /// - /// Override this method to configure the database (and other options) to be used for this context. - /// This method is called for each instance of the context that is created. - /// The base implementation does nothing. - /// - /// - /// In situations where an instance of may or may not have been passed - /// to the constructor, you can use to determine if - /// the options have already been set, and skip some or all of the logic in - /// . - /// - /// - /// A builder used to create or modify options for this context. Databases (and other extensions) - /// typically define extension methods on this object that allow you to configure the context. - /// See DbContext lifetime, configuration, and initialization - /// for more information. - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - base.OnConfiguring(optionsBuilder); - Options.ContextConfigurator?.Invoke(optionsBuilder); - } + Options = options ?? new EfCoreDataSourceOptions(); + } - /// - /// Override this method to set defaults and configure conventions before they run. This method is invoked before - /// . - /// - /// The builder being used to set defaults and configure conventions that will be used to build the model for this context. - /// If a model is explicitly set on the options for this context (via ) - /// then this method will not be run. - protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) - { - base.ConfigureConventions(configurationBuilder); - Options.ConventionsConfigurator?.Invoke(configurationBuilder); - } + /// + /// + /// Override this method to configure the database (and other options) to be used for this context. + /// This method is called for each instance of the context that is created. + /// The base implementation does nothing. + /// + /// + /// In situations where an instance of may or may not have been passed + /// to the constructor, you can use to determine if + /// the options have already been set, and skip some or all of the logic in + /// . + /// + /// + /// A builder used to create or modify options for this context. Databases (and other extensions) + /// typically define extension methods on this object that allow you to configure the context. + /// See DbContext lifetime, configuration, and initialization + /// for more information. + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + Options.ContextConfigurator?.Invoke(optionsBuilder); + } - /// - /// Override this method to further configure the model that was discovered by convention from the entity types - /// exposed in properties on your derived context. The resulting model may be cached - /// and re-used for subsequent instances of your derived context. - /// - /// The builder being used to construct the model for this context. Databases (and other extensions) typically - /// define extension methods on this object that allow you to configure aspects of the model that are specific - /// to a given database. - /// - /// If a model is explicitly set on the options for this context (via ) - /// then this method will not be run. - /// - /// - /// See Modeling entity types and relationships for more information. - /// - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - Options.ModelConstructor?.Invoke(modelBuilder); - } + /// + /// Override this method to set defaults and configure conventions before they run. This method is invoked before + /// . + /// + /// The builder being used to set defaults and configure conventions that will be used to build the model for this context. + /// If a model is explicitly set on the options for this context (via ) + /// then this method will not be run. + protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) + { + base.ConfigureConventions(configurationBuilder); + Options.ConventionsConfigurator?.Invoke(configurationBuilder); + } - /// - /// Gets the configured options of this instance. - /// - /// The configured options of this instance. - public EfCoreDataSourceOptions Options { get; } + /// + /// Override this method to further configure the model that was discovered by convention from the entity types + /// exposed in properties on your derived context. The resulting model may be cached + /// and re-used for subsequent instances of your derived context. + /// + /// The builder being used to construct the model for this context. Databases (and other extensions) typically + /// define extension methods on this object that allow you to configure aspects of the model that are specific + /// to a given database. + /// + /// If a model is explicitly set on the options for this context (via ) + /// then this method will not be run. + /// + /// + /// See Modeling entity types and relationships for more information. + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + Options.ModelConstructor?.Invoke(modelBuilder); } + + /// + /// Gets the configured options of this instance. + /// + /// The configured options of this instance. + public EfCoreDataSourceOptions Options { get; } } diff --git a/src/Savvyio.Extensions.EFCore/EfCoreQueryOptions.cs b/src/Savvyio.Extensions.EFCore/EfCoreQueryOptions.cs index 6f2fc628..4c904d42 100644 --- a/src/Savvyio.Extensions.EFCore/EfCoreQueryOptions.cs +++ b/src/Savvyio.Extensions.EFCore/EfCoreQueryOptions.cs @@ -1,43 +1,42 @@ -using System; +using System; using System.Linq.Expressions; using Cuemon.Threading; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +/// +/// Specifies options that is related to . +/// +/// +public class EfCoreQueryOptions : AsyncOptions { /// - /// Specifies options that is related to . + /// Initializes a new instance of the class. /// - /// - public class EfCoreQueryOptions : AsyncOptions + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// null + /// + /// + /// + /// default + /// + /// + /// + public EfCoreQueryOptions() { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// null - /// - /// - /// - /// default - /// - /// - /// - public EfCoreQueryOptions() - { - } - - /// - /// Gets or sets the predicate that matches one or more objects to retrieve in the associated . - /// - /// The predicate that matches one or more objects to retrieve in the associated . - public Expression> Predicate { get; set; } } + + /// + /// Gets or sets the predicate that matches one or more objects to retrieve in the associated . + /// + /// The predicate that matches one or more objects to retrieve in the associated . + public Expression> Predicate { get; set; } } diff --git a/src/Savvyio.Extensions.EFCore/EfCoreRepository.cs b/src/Savvyio.Extensions.EFCore/EfCoreRepository.cs index 094ced42..37733e40 100644 --- a/src/Savvyio.Extensions.EFCore/EfCoreRepository.cs +++ b/src/Savvyio.Extensions.EFCore/EfCoreRepository.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -9,90 +9,89 @@ using Microsoft.EntityFrameworkCore; using Savvyio.Domain; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +/// +/// Provides a default implementation of the interface to serve as an abstraction layer before the actual I/O communication with a source of data using Microsoft Entity Framework Core. +/// +/// The type of the entity. +/// The type of the key that uniquely identifies the entity. +/// +public class EfCoreRepository : IPersistentRepository where TEntity : class, IIdentity { /// - /// Provides a default implementation of the interface to serve as an abstraction layer before the actual I/O communication with a source of data using Microsoft Entity Framework Core. + /// Initializes a new instance of the class. /// - /// The type of the entity. - /// The type of the key that uniquely identifies the entity. - /// - public class EfCoreRepository : IPersistentRepository where TEntity : class, IIdentity + /// The that handles actual I/O communication with a source of data. + public EfCoreRepository(IEfCoreDataSource source) { - /// - /// Initializes a new instance of the class. - /// - /// The that handles actual I/O communication with a source of data. - public EfCoreRepository(IEfCoreDataSource source) - { - Validator.ThrowIfNull(source); - Set = source.Set(); - } + Validator.ThrowIfNull(source); + Set = source.Set(); + } - /// - /// Gets the associated for this repository. - /// - /// The associated for this repository. - protected DbSet Set { get; } + /// + /// Gets the associated for this repository. + /// + /// The associated for this repository. + protected DbSet Set { get; } - /// - /// Marks the specified to be added in the data store when is called. - /// - /// The entity to add. - public virtual void Add(TEntity entity) - { - Set.Add(entity); - } + /// + /// Marks the specified to be added in the data store when is called. + /// + /// The entity to add. + public virtual void Add(TEntity entity) + { + Set.Add(entity); + } - /// - /// Marks the specified to be added in the data store when is called. - /// - /// The entities to add. - public virtual void AddRange(IEnumerable entities) - { - Set.AddRange(entities); - } + /// + /// Marks the specified to be added in the data store when is called. + /// + /// The entities to add. + public virtual void AddRange(IEnumerable entities) + { + Set.AddRange(entities); + } - /// - /// Loads the entity from the specified asynchronous. - /// - /// The key that uniquely identifies the entity. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the entity of the operation or null if not found. - public virtual Task GetByIdAsync(TKey id, Action setup = null) - { - var options = setup.Configure(); - return Set.FindAsync(new object[] { id }, options.CancellationToken).AsTask(); - } + /// + /// Loads the entity from the specified asynchronous. + /// + /// The key that uniquely identifies the entity. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the entity of the operation or null if not found. + public virtual Task GetByIdAsync(TKey id, Action setup = null) + { + var options = setup.Configure(); + return Set.FindAsync(new object[] { id }, options.CancellationToken).AsTask(); + } - /// - /// Finds all entities matching the specified asynchronous. - /// - /// The predicate that matches the entities to retrieve. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result either contains the matching entities of the operation or an empty sequence if no match was found. - public virtual async Task> FindAllAsync(Expression> predicate = null, Action setup = null) - { - var options = setup.Configure(); - return await (predicate == null ? Set.ToListAsync(options.CancellationToken).ConfigureAwait(false) : Set.Where(predicate).ToListAsync(options.CancellationToken).ConfigureAwait(false)); - } + /// + /// Finds all entities matching the specified asynchronous. + /// + /// The predicate that matches the entities to retrieve. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result either contains the matching entities of the operation or an empty sequence if no match was found. + public virtual async Task> FindAllAsync(Expression> predicate = null, Action setup = null) + { + var options = setup.Configure(); + return await (predicate == null ? Set.ToListAsync(options.CancellationToken).ConfigureAwait(false) : Set.Where(predicate).ToListAsync(options.CancellationToken).ConfigureAwait(false)); + } - /// - /// Marks the specified to be removed from the data store when is called. - /// - /// The entity to remove. - public virtual void Remove(TEntity entity) - { - Set.Remove(entity); - } + /// + /// Marks the specified to be removed from the data store when is called. + /// + /// The entity to remove. + public virtual void Remove(TEntity entity) + { + Set.Remove(entity); + } - /// - /// Marks the specified to be removed from the data store when is called. - /// - /// The entities to remove. - public virtual void RemoveRange(IEnumerable entities) - { - Set.RemoveRange(entities); - } + /// + /// Marks the specified to be removed from the data store when is called. + /// + /// The entities to remove. + public virtual void RemoveRange(IEnumerable entities) + { + Set.RemoveRange(entities); } } diff --git a/src/Savvyio.Extensions.EFCore/IEfCoreDataSource.cs b/src/Savvyio.Extensions.EFCore/IEfCoreDataSource.cs index ed1967b3..b2e8cfef 100644 --- a/src/Savvyio.Extensions.EFCore/IEfCoreDataSource.cs +++ b/src/Savvyio.Extensions.EFCore/IEfCoreDataSource.cs @@ -1,19 +1,18 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Savvyio.Domain; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +/// +/// Defines a generic way to support the actual I/O communication with a source of data - tailored to Microsoft Entity Framework Core. +/// +/// +public interface IEfCoreDataSource : IDataSource, IUnitOfWork { /// - /// Defines a generic way to support the actual I/O communication with a source of data - tailored to Microsoft Entity Framework Core. + /// Creates a that can be used to query and save instances of . /// - /// - public interface IEfCoreDataSource : IDataSource, IUnitOfWork - { - /// - /// Creates a that can be used to query and save instances of . - /// - /// The type of entity for which a set should be returned. - /// A set for the given entity type. - DbSet Set() where TEntity : class; - } + /// The type of entity for which a set should be returned. + /// A set for the given entity type. + DbSet Set() where TEntity : class; } diff --git a/src/Savvyio.Extensions.EFCore/Savvyio.Extensions.EFCore.csproj b/src/Savvyio.Extensions.EFCore/Savvyio.Extensions.EFCore.csproj index d9964525..022abddb 100644 --- a/src/Savvyio.Extensions.EFCore/Savvyio.Extensions.EFCore.csproj +++ b/src/Savvyio.Extensions.EFCore/Savvyio.Extensions.EFCore.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O core assemblies with support for Microsoft Entity Framework Core. diff --git a/src/Savvyio.Extensions.NATS/Commands/NatsCommandQueue.cs b/src/Savvyio.Extensions.NATS/Commands/NatsCommandQueue.cs index 965da06c..d9404f05 100644 --- a/src/Savvyio.Extensions.NATS/Commands/NatsCommandQueue.cs +++ b/src/Savvyio.Extensions.NATS/Commands/NatsCommandQueue.cs @@ -1,4 +1,4 @@ -using Cuemon; +using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.IO; using Cuemon.Extensions.Reflection; @@ -14,199 +14,198 @@ using System.Threading; using System.Threading.Tasks; -namespace Savvyio.Extensions.NATS.Commands +namespace Savvyio.Extensions.NATS.Commands; + +/// +/// Provides a NATS JetStream implementation of the for command messages. +/// +/// +/// +public class NatsCommandQueue : NatsMessage, IPointToPointChannel { + private readonly NatsCommandQueueOptions _options; + /// - /// Provides a NATS JetStream implementation of the for command messages. + /// Initializes a new instance of the class. /// - /// - /// - public class NatsCommandQueue : NatsMessage, IPointToPointChannel + /// The marshaller used for serializing and deserializing messages. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public NatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options) : base(marshaller, options) { - private readonly NatsCommandQueueOptions _options; + Validator.ThrowIfInvalidOptions(options); + _options = options; + } - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public NatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options) : base(marshaller, options) + /// + /// Sends the specified command messages asynchronously to the configured NATS JetStream subject. + /// + /// The messages to send. + /// The which may be configured. + /// A that represents the asynchronous operation. + public async Task SendAsync(IEnumerable> messages, Action setup = null) + { + var context = CreateJetStreamContext(); + foreach (var message in messages) { - Validator.ThrowIfInvalidOptions(options); - _options = options; + await PublishMessageAsync(context, _options.Subject, (await Marshaller.Serialize(message).ToByteArrayAsync().ConfigureAwait(false)).ToBase64String(), new NatsHeaders() + { + { "type", message.GetType().ToFullNameIncludingAssemblyName() } + }).ConfigureAwait(false); } + } - /// - /// Sends the specified command messages asynchronously to the configured NATS JetStream subject. - /// - /// The messages to send. - /// The which may be configured. - /// A that represents the asynchronous operation. - public async Task SendAsync(IEnumerable> messages, Action setup = null) + /// + /// Receives command messages asynchronously from the configured NATS JetStream consumer. + /// + /// The which may be configured. + /// + /// An that yields instances as they are received. + /// + public async IAsyncEnumerable> ReceiveAsync(Action setup = null) + { + Validator.ThrowIfInvalidConfigurator(setup, out var options); + + var streamName = _options.StreamName; + var consumerName = _options.ConsumerName; + + var consumer = await CreateConsumerAsync(new StreamConfig(streamName, [_options.Subject]) { - var context = CreateJetStreamContext(); - foreach (var message in messages) - { - await PublishMessageAsync(context, _options.Subject, (await Marshaller.Serialize(message).ToByteArrayAsync().ConfigureAwait(false)).ToBase64String(), new NatsHeaders() - { - { "type", message.GetType().ToFullNameIncludingAssemblyName() } - }).ConfigureAwait(false); - } - } + Retention = StreamConfigRetention.Workqueue + }, new ConsumerConfig(consumerName) + { + }, options.CancellationToken).ConfigureAwait(false); - /// - /// Receives command messages asynchronously from the configured NATS JetStream consumer. - /// - /// The which may be configured. - /// - /// An that yields instances as they are received. - /// - public async IAsyncEnumerable> ReceiveAsync(Action setup = null) + await foreach (var message in FetchMessagesAsync(consumer, new NatsJSFetchOpts() { - Validator.ThrowIfInvalidConfigurator(setup, out var options); - - var streamName = _options.StreamName; - var consumerName = _options.ConsumerName; - - var consumer = await CreateConsumerAsync(new StreamConfig(streamName, [_options.Subject]) - { - Retention = StreamConfigRetention.Workqueue - }, new ConsumerConfig(consumerName) - { - }, options.CancellationToken).ConfigureAwait(false); - - await foreach (var message in FetchMessagesAsync(consumer, new NatsJSFetchOpts() - { - Expires = _options.Expires == TimeSpan.Zero ? null : _options.Expires, - MaxMsgs = _options.MaxMessages, - IdleHeartbeat = _options.Heartbeat == TimeSpan.Zero ? null : _options.Heartbeat - }, cancellationToken: options.CancellationToken)) + Expires = _options.Expires == TimeSpan.Zero ? null : _options.Expires, + MaxMsgs = _options.MaxMessages, + IdleHeartbeat = _options.Heartbeat == TimeSpan.Zero ? null : _options.Heartbeat + }, cancellationToken: options.CancellationToken)) + { + options.CancellationToken.ThrowIfCancellationRequested(); + var messageType = Type.GetType(message.Headers["type"]); + var deserialized = Marshaller.Deserialize(await message.Data.FromBase64().ToStreamAsync().ConfigureAwait(false), messageType) as IMessage; + deserialized!.Properties.Add(nameof(CancellationToken), options.CancellationToken); + deserialized.Properties.Add(nameof(ReceivedNatsMessage), message); + deserialized.Acknowledged += OnMessageAcknowledgedAsync; + if (_options.AutoAcknowledge) { - options.CancellationToken.ThrowIfCancellationRequested(); - var messageType = Type.GetType(message.Headers["type"]); - var deserialized = Marshaller.Deserialize(await message.Data.FromBase64().ToStreamAsync().ConfigureAwait(false), messageType) as IMessage; - deserialized!.Properties.Add(nameof(CancellationToken), options.CancellationToken); - deserialized.Properties.Add(nameof(ReceivedNatsMessage), message); - deserialized.Acknowledged += OnMessageAcknowledgedAsync; - if (_options.AutoAcknowledge) - { - await deserialized.AcknowledgeAsync().ConfigureAwait(false); - } - yield return deserialized; + await deserialized.AcknowledgeAsync().ConfigureAwait(false); } + yield return deserialized; } + } - /// - /// Handles the acknowledgment of a message by sending an acknowledgment to the NATS JetStream server. - /// - /// The message that was acknowledged. - /// The event arguments containing properties of the acknowledged message. - /// A that represents the asynchronous operation. - private async Task OnMessageAcknowledgedAsync(object sender, AcknowledgedEventArgs e) - { - var ct = (CancellationToken)e.Properties[nameof(CancellationToken)]; - var message = (ReceivedNatsMessage)e.Properties[nameof(ReceivedNatsMessage)]; - await message.AcknowledgeAsync(ct).ConfigureAwait(false); - if (sender is IAcknowledgeable ack) { ack.Acknowledged -= OnMessageAcknowledgedAsync; } - } + /// + /// Handles the acknowledgment of a message by sending an acknowledgment to the NATS JetStream server. + /// + /// The message that was acknowledged. + /// The event arguments containing properties of the acknowledged message. + /// A that represents the asynchronous operation. + private async Task OnMessageAcknowledgedAsync(object sender, AcknowledgedEventArgs e) + { + var ct = (CancellationToken)e.Properties[nameof(CancellationToken)]; + var message = (ReceivedNatsMessage)e.Properties[nameof(ReceivedNatsMessage)]; + await message.AcknowledgeAsync(ct).ConfigureAwait(false); + if (sender is IAcknowledgeable ack) { ack.Acknowledged -= OnMessageAcknowledgedAsync; } + } - /// - /// Publishes a serialized message to NATS JetStream. - /// - /// The JetStream context to use for publishing. - /// The subject to publish to. - /// The serialized message payload. - /// The message headers. - /// A task that represents the asynchronous operation. - protected virtual async Task PublishMessageAsync(INatsJSContext context, string subject, string message, NatsHeaders headers) + /// + /// Publishes a serialized message to NATS JetStream. + /// + /// The JetStream context to use for publishing. + /// The subject to publish to. + /// The serialized message payload. + /// The message headers. + /// A task that represents the asynchronous operation. + protected virtual async Task PublishMessageAsync(INatsJSContext context, string subject, string message, NatsHeaders headers) + { + await context.PublishAsync(subject, message, headers: headers).ConfigureAwait(false); + } + + /// + /// Creates or updates the stream and consumer used by receive operations. + /// + /// The stream configuration. + /// The consumer configuration. + /// The cancellation token of the asynchronous operation. + /// A consumer that can fetch messages. + protected virtual async Task CreateConsumerAsync(StreamConfig streamConfig, ConsumerConfig consumerConfig, CancellationToken cancellationToken) + { + var context = CreateJetStreamContext(); + await context.CreateOrUpdateStreamAsync(streamConfig, cancellationToken).ConfigureAwait(false); + return await context.CreateOrUpdateConsumerAsync(streamConfig.Name, consumerConfig, cancellationToken).ConfigureAwait(false); + } + + /// + /// Creates a NATS JetStream context used by send and receive operations. + /// + /// An for JetStream operations. + protected virtual INatsJSContext CreateJetStreamContext() + { + return NatsClient.CreateJetStreamContext(); + } + + /// + /// Fetches messages from the specified NATS JetStream consumer. + /// + /// The consumer to fetch messages from. + /// The fetch options. + /// The cancellation token of the asynchronous operation. + /// An asynchronous sequence of received NATS messages. + protected virtual async IAsyncEnumerable FetchMessagesAsync(INatsJSConsumer consumer, NatsJSFetchOpts options, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) + { + await foreach (var message in consumer.FetchAsync(opts: options, cancellationToken: cancellationToken).ConfigureAwait(false)) { - await context.PublishAsync(subject, message, headers: headers).ConfigureAwait(false); + yield return new ReceivedNatsMessage(message.Headers, message.Data, ct => message.AckAsync(cancellationToken: ct).AsTask()); } + } + + /// + /// Represents a NATS message received from JetStream. + /// + protected sealed class ReceivedNatsMessage + { + private readonly Func _acknowledgeAsync; /// - /// Creates or updates the stream and consumer used by receive operations. + /// Initializes a new instance of the class. /// - /// The stream configuration. - /// The consumer configuration. - /// The cancellation token of the asynchronous operation. - /// A consumer that can fetch messages. - protected virtual async Task CreateConsumerAsync(StreamConfig streamConfig, ConsumerConfig consumerConfig, CancellationToken cancellationToken) + /// The NATS message headers. + /// The NATS message payload. + /// The delegate used to acknowledge the message. + public ReceivedNatsMessage(NatsHeaders headers, string data, Func acknowledgeAsync) { - var context = CreateJetStreamContext(); - await context.CreateOrUpdateStreamAsync(streamConfig, cancellationToken).ConfigureAwait(false); - return await context.CreateOrUpdateConsumerAsync(streamConfig.Name, consumerConfig, cancellationToken).ConfigureAwait(false); + Headers = headers; + Data = data; + _acknowledgeAsync = acknowledgeAsync; } /// - /// Creates a NATS JetStream context used by send and receive operations. + /// Gets the NATS message headers. /// - /// An for JetStream operations. - protected virtual INatsJSContext CreateJetStreamContext() - { - return NatsClient.CreateJetStreamContext(); - } + public NatsHeaders Headers { get; } /// - /// Fetches messages from the specified NATS JetStream consumer. + /// Gets the NATS message payload. /// - /// The consumer to fetch messages from. - /// The fetch options. - /// The cancellation token of the asynchronous operation. - /// An asynchronous sequence of received NATS messages. - protected virtual async IAsyncEnumerable FetchMessagesAsync(INatsJSConsumer consumer, NatsJSFetchOpts options, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) - { - await foreach (var message in consumer.FetchAsync(opts: options, cancellationToken: cancellationToken).ConfigureAwait(false)) - { - yield return new ReceivedNatsMessage(message.Headers, message.Data, ct => message.AckAsync(cancellationToken: ct).AsTask()); - } - } + public string Data { get; } /// - /// Represents a NATS message received from JetStream. + /// Acknowledges the message. /// - protected sealed class ReceivedNatsMessage + /// The cancellation token of the asynchronous operation. + /// A task that represents the asynchronous operation. + public Task AcknowledgeAsync(CancellationToken cancellationToken) { - private readonly Func _acknowledgeAsync; - - /// - /// Initializes a new instance of the class. - /// - /// The NATS message headers. - /// The NATS message payload. - /// The delegate used to acknowledge the message. - public ReceivedNatsMessage(NatsHeaders headers, string data, Func acknowledgeAsync) - { - Headers = headers; - Data = data; - _acknowledgeAsync = acknowledgeAsync; - } - - /// - /// Gets the NATS message headers. - /// - public NatsHeaders Headers { get; } - - /// - /// Gets the NATS message payload. - /// - public string Data { get; } - - /// - /// Acknowledges the message. - /// - /// The cancellation token of the asynchronous operation. - /// A task that represents the asynchronous operation. - public Task AcknowledgeAsync(CancellationToken cancellationToken) - { - return _acknowledgeAsync(cancellationToken); - } + return _acknowledgeAsync(cancellationToken); } } } diff --git a/src/Savvyio.Extensions.NATS/Commands/NatsCommandQueueOptions.cs b/src/Savvyio.Extensions.NATS/Commands/NatsCommandQueueOptions.cs index 7b636e1c..11828e6b 100644 --- a/src/Savvyio.Extensions.NATS/Commands/NatsCommandQueueOptions.cs +++ b/src/Savvyio.Extensions.NATS/Commands/NatsCommandQueueOptions.cs @@ -1,118 +1,117 @@ -using System; +using System; using Cuemon; -namespace Savvyio.Extensions.NATS.Commands +namespace Savvyio.Extensions.NATS.Commands; + +/// +/// Configuration options for . +/// +/// +public class NatsCommandQueueOptions : NatsMessageOptions { + private int _maxMessages; + private TimeSpan _expires; + /// - /// Configuration options for . + /// Initializes a new instance of the class. /// - /// - public class NatsCommandQueueOptions : NatsMessageOptions + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// 100 + /// + /// + /// + /// false + /// + /// + /// + /// TimeSpan.Zero + /// + /// + /// + /// TimeSpan.Zero + /// + /// + /// + /// null + /// + /// + /// + /// null + /// + /// + /// + public NatsCommandQueueOptions() { - private int _maxMessages; - private TimeSpan _expires; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// 100 - /// - /// - /// - /// false - /// - /// - /// - /// TimeSpan.Zero - /// - /// - /// - /// TimeSpan.Zero - /// - /// - /// - /// null - /// - /// - /// - /// null - /// - /// - /// - public NatsCommandQueueOptions() - { - MaxMessages = 25; - Heartbeat = TimeSpan.Zero; - Expires = TimeSpan.Zero; - } + MaxMessages = 25; + Heartbeat = TimeSpan.Zero; + Expires = TimeSpan.Zero; + } - /// - /// Gets or sets the maximum number of messages to process. - /// - public int MaxMessages - { - get => _maxMessages; - set => _maxMessages = Math.Clamp(value, 1, short.MaxValue); - } + /// + /// Gets or sets the maximum number of messages to process. + /// + public int MaxMessages + { + get => _maxMessages; + set => _maxMessages = Math.Clamp(value, 1, short.MaxValue); + } - /// - /// Gets or sets the expiration time for messages in the queue. - /// - /// If set to a value greater than or equal to 30 seconds, the property is automatically set to 5 seconds. - public TimeSpan Expires + /// + /// Gets or sets the expiration time for messages in the queue. + /// + /// If set to a value greater than or equal to 30 seconds, the property is automatically set to 5 seconds. + public TimeSpan Expires + { + get => _expires; + set { - get => _expires; - set + _expires = value; + if (_expires >= TimeSpan.FromSeconds(30)) { - _expires = value; - if (_expires >= TimeSpan.FromSeconds(30)) - { - Heartbeat = TimeSpan.FromSeconds(5); - } + Heartbeat = TimeSpan.FromSeconds(5); } } + } - /// - /// Gets or sets the interval for the heartbeat signal sent from the NATS server. - /// - /// If is set to a value greater than or equal to 30 seconds, this property is automatically set to 5 seconds. - public TimeSpan Heartbeat { get; set; } + /// + /// Gets or sets the interval for the heartbeat signal sent from the NATS server. + /// + /// If is set to a value greater than or equal to 30 seconds, this property is automatically set to 5 seconds. + public TimeSpan Heartbeat { get; set; } - /// - /// Gets or sets the name of the NATS stream. - /// - public string StreamName { get; set; } + /// + /// Gets or sets the name of the NATS stream. + /// + public string StreamName { get; set; } - /// - /// Gets or sets the name of the NATS consumer. - /// - public string ConsumerName { get; set; } + /// + /// Gets or sets the name of the NATS consumer. + /// + public string ConsumerName { get; set; } - /// - /// Gets or sets a value indicating whether messages should be automatically acknowledged. - /// - public bool AutoAcknowledge { get; set; } + /// + /// Gets or sets a value indicating whether messages should be automatically acknowledged. + /// + public bool AutoAcknowledge { get; set; } - /// - /// Validates the options for the NATS command queue. - /// - /// - /// or is null or whitespace. - /// - public override void ValidateOptions() - { - Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(StreamName)); - Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(ConsumerName)); - base.ValidateOptions(); - } + /// + /// Validates the options for the NATS command queue. + /// + /// + /// or is null or whitespace. + /// + public override void ValidateOptions() + { + Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(StreamName)); + Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(ConsumerName)); + base.ValidateOptions(); } } diff --git a/src/Savvyio.Extensions.NATS/EventDriven/NatsEventBus.cs b/src/Savvyio.Extensions.NATS/EventDriven/NatsEventBus.cs index ed03bfd2..f0e27aef 100644 --- a/src/Savvyio.Extensions.NATS/EventDriven/NatsEventBus.cs +++ b/src/Savvyio.Extensions.NATS/EventDriven/NatsEventBus.cs @@ -1,4 +1,4 @@ -using Cuemon; +using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.IO; using Cuemon.Extensions.Reflection; @@ -11,122 +11,122 @@ using System.Threading; using System.Threading.Tasks; -namespace Savvyio.Extensions.NATS.EventDriven +namespace Savvyio.Extensions.NATS.EventDriven; + +/// +/// Provides a NATS implementation of the for integration event messages. +/// +/// +/// +public class NatsEventBus : NatsMessage, IPublishSubscribeChannel { + private readonly NatsEventBusOptions _options; + /// - /// Provides a NATS implementation of the for integration event messages. + /// Initializes a new instance of the class. /// - /// - /// - public class NatsEventBus : NatsMessage, IPublishSubscribeChannel + /// The marshaller used for serializing and deserializing messages. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public NatsEventBus(IMarshaller marshaller, NatsEventBusOptions options) : base(marshaller, options) { - private readonly NatsEventBusOptions _options; + Validator.ThrowIfInvalidOptions(options); + _options = options; + } - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public NatsEventBus(IMarshaller marshaller, NatsEventBusOptions options) : base(marshaller, options) - { - Validator.ThrowIfInvalidOptions(options); - _options = options; - } + /// + /// Publishes the specified integration event message asynchronously to the configured NATS subject. + /// + /// The message to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + public async Task PublishAsync(IMessage message, Action setup = null) + { + Validator.ThrowIfInvalidConfigurator(setup, out var options); + options.CancellationToken.ThrowIfCancellationRequested(); + await PublishMessageAsync(_options.Subject, (await Marshaller.Serialize(message).ToByteArrayAsync().ConfigureAwait(false)).ToBase64String(), new NatsHeaders() + { + { "type", message.GetType().ToFullNameIncludingAssemblyName() } + }, options.CancellationToken).ConfigureAwait(false); + } - /// - /// Publishes the specified integration event message asynchronously to the configured NATS subject. - /// - /// The message to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - public async Task PublishAsync(IMessage message, Action setup = null) + /// + /// Subscribes to integration event messages from the configured NATS subject and invokes the specified asynchronous handler for each received message. + /// + /// The function delegate that will handle the message. + /// The which may be configured. + /// A that represents the asynchronous operation. + public async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) + { + Validator.ThrowIfInvalidConfigurator(setup, out var options); + await foreach (var message in SubscribeMessagesAsync(_options.Subject, new NatsSubOpts() + { + }, cancellationToken: options.CancellationToken)) { - Validator.ThrowIfInvalidConfigurator(setup, out var options); - await PublishMessageAsync(_options.Subject, (await Marshaller.Serialize(message).ToByteArrayAsync().ConfigureAwait(false)).ToBase64String(), new NatsHeaders() - { - { "type", message.GetType().ToFullNameIncludingAssemblyName() } - }, options.CancellationToken).ConfigureAwait(false); + var messageType = Type.GetType(message.Headers["type"]); + var deserialized = Marshaller.Deserialize(await message.Data.FromBase64().ToStreamAsync().ConfigureAwait(false), messageType) as IMessage; + await asyncHandler.Invoke(deserialized, options.CancellationToken).ConfigureAwait(false); } + } - /// - /// Subscribes to integration event messages from the configured NATS subject and invokes the specified asynchronous handler for each received message. - /// - /// The function delegate that will handle the message. - /// The which may be configured. - /// A that represents the asynchronous operation. - public async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) + /// + /// Publishes a serialized message to NATS. + /// + /// The subject to publish to. + /// The serialized message payload. + /// The message headers. + /// The cancellation token of the asynchronous operation. + /// A task that represents the asynchronous operation. + protected virtual async Task PublishMessageAsync(string subject, string message, NatsHeaders headers, CancellationToken cancellationToken) + { + await NatsClient.PublishAsync(subject, message, headers: headers, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Subscribes to NATS messages. + /// + /// The subject to subscribe to. + /// The subscription options. + /// The cancellation token of the asynchronous operation. + /// An asynchronous sequence of received NATS messages. + protected virtual async IAsyncEnumerable SubscribeMessagesAsync(string subject, NatsSubOpts options, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) + { + await foreach (var message in NatsClient.SubscribeAsync(subject, opts: options, cancellationToken: cancellationToken).ConfigureAwait(false)) { - Validator.ThrowIfInvalidConfigurator(setup, out var options); - await foreach (var message in SubscribeMessagesAsync(_options.Subject, new NatsSubOpts() - { - }, cancellationToken: options.CancellationToken)) - { - var messageType = Type.GetType(message.Headers["type"]); - var deserialized = Marshaller.Deserialize(await message.Data.FromBase64().ToStreamAsync().ConfigureAwait(false), messageType) as IMessage; - await asyncHandler.Invoke(deserialized, options.CancellationToken).ConfigureAwait(false); - } + yield return new ReceivedNatsMessage(message.Headers, message.Data); } + } + /// + /// Represents a NATS message received from a subscription. + /// + protected sealed class ReceivedNatsMessage + { /// - /// Publishes a serialized message to NATS. + /// Initializes a new instance of the class. /// - /// The subject to publish to. - /// The serialized message payload. - /// The message headers. - /// The cancellation token of the asynchronous operation. - /// A task that represents the asynchronous operation. - protected virtual async Task PublishMessageAsync(string subject, string message, NatsHeaders headers, CancellationToken cancellationToken) + /// The NATS message headers. + /// The NATS message payload. + public ReceivedNatsMessage(NatsHeaders headers, string data) { - await NatsClient.PublishAsync(subject, message, headers: headers, cancellationToken: cancellationToken).ConfigureAwait(false); + Headers = headers; + Data = data; } /// - /// Subscribes to NATS messages. + /// Gets the NATS message headers. /// - /// The subject to subscribe to. - /// The subscription options. - /// The cancellation token of the asynchronous operation. - /// An asynchronous sequence of received NATS messages. - protected virtual async IAsyncEnumerable SubscribeMessagesAsync(string subject, NatsSubOpts options, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) - { - await foreach (var message in NatsClient.SubscribeAsync(subject, opts: options, cancellationToken: cancellationToken).ConfigureAwait(false)) - { - yield return new ReceivedNatsMessage(message.Headers, message.Data); - } - } + public NatsHeaders Headers { get; } /// - /// Represents a NATS message received from a subscription. + /// Gets the NATS message payload. /// - protected sealed class ReceivedNatsMessage - { - /// - /// Initializes a new instance of the class. - /// - /// The NATS message headers. - /// The NATS message payload. - public ReceivedNatsMessage(NatsHeaders headers, string data) - { - Headers = headers; - Data = data; - } - - /// - /// Gets the NATS message headers. - /// - public NatsHeaders Headers { get; } - - /// - /// Gets the NATS message payload. - /// - public string Data { get; } - } + public string Data { get; } } } diff --git a/src/Savvyio.Extensions.NATS/EventDriven/NatsEventBusOptions.cs b/src/Savvyio.Extensions.NATS/EventDriven/NatsEventBusOptions.cs index 9174ae42..9f147632 100644 --- a/src/Savvyio.Extensions.NATS/EventDriven/NatsEventBusOptions.cs +++ b/src/Savvyio.Extensions.NATS/EventDriven/NatsEventBusOptions.cs @@ -1,10 +1,9 @@ -namespace Savvyio.Extensions.NATS.EventDriven +namespace Savvyio.Extensions.NATS.EventDriven; + +/// +/// Configuration options for . +/// +/// +public class NatsEventBusOptions : NatsMessageOptions { - /// - /// Configuration options for . - /// - /// - public class NatsEventBusOptions : NatsMessageOptions - { - } } diff --git a/src/Savvyio.Extensions.NATS/GlobalSuppressions.cs b/src/Savvyio.Extensions.NATS/GlobalSuppressions.cs index 5a435af8..e68a23fe 100644 --- a/src/Savvyio.Extensions.NATS/GlobalSuppressions.cs +++ b/src/Savvyio.Extensions.NATS/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. diff --git a/src/Savvyio.Extensions.NATS/NatsMessage.cs b/src/Savvyio.Extensions.NATS/NatsMessage.cs index 87112765..994bb5a3 100644 --- a/src/Savvyio.Extensions.NATS/NatsMessage.cs +++ b/src/Savvyio.Extensions.NATS/NatsMessage.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon.Extensions; using NATS.Net; using System.Threading.Tasks; @@ -6,59 +6,58 @@ using NATS.Client.Core; using Savvyio.Diagnostics; -namespace Savvyio.Extensions.NATS +namespace Savvyio.Extensions.NATS; + +/// +/// Provides a base class for NATS message operations, supporting asynchronous disposal and message serialization. +/// +public abstract class NatsMessage : AsyncDisposable, IHealthCheckProvider { /// - /// Provides a base class for NATS message operations, supporting asynchronous disposal and message serialization. + /// Initializes a new instance of the class with the specified marshaller and options. /// - public abstract class NatsMessage : AsyncDisposable, IHealthCheckProvider + /// The marshaller used for serializing and deserializing messages. + /// The configuration options for the NATS message. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + protected NatsMessage(IMarshaller marshaller, NatsMessageOptions options) { - /// - /// Initializes a new instance of the class with the specified marshaller and options. - /// - /// The marshaller used for serializing and deserializing messages. - /// The configuration options for the NATS message. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - protected NatsMessage(IMarshaller marshaller, NatsMessageOptions options) - { - Validator.ThrowIfNull(marshaller); - Validator.ThrowIfInvalidOptions(options); - Marshaller = marshaller; - NatsClient = new NatsClient(options.NatsUrl.OriginalString); - } + Validator.ThrowIfNull(marshaller); + Validator.ThrowIfInvalidOptions(options); + Marshaller = marshaller; + NatsClient = new NatsClient(options.NatsUrl.OriginalString); + } - /// - /// Gets the NATS client provided by the constructor for communication with the NATS server. - /// - protected NatsClient NatsClient { get; } + /// + /// Gets the NATS client provided by the constructor for communication with the NATS server. + /// + protected NatsClient NatsClient { get; } - /// - /// Gets the marshaller provided by the constructor for serializing and deserializing messages. - /// - /// The marshaller used for message serialization and deserialization. - protected IMarshaller Marshaller { get; } + /// + /// Gets the marshaller provided by the constructor for serializing and deserializing messages. + /// + /// The marshaller used for message serialization and deserialization. + protected IMarshaller Marshaller { get; } - /// - /// Releases the managed resources used by the asynchronously. - /// - /// A that represents the asynchronous dispose operation. - protected override async ValueTask OnDisposeManagedResourcesAsync() - { - if (NatsClient != null) { await NatsClient.DisposeAsync().ConfigureAwait(false); } - } - /// - /// Gets the instance used for probing the health status of the NATS server. - /// - /// instance representing the active connection to the NATS server. - public INatsConnection GetHealthCheckTarget() - { - return NatsClient.Connection; - } + /// + /// Releases the managed resources used by the asynchronously. + /// + /// A that represents the asynchronous dispose operation. + protected override async ValueTask OnDisposeManagedResourcesAsync() + { + if (NatsClient != null) { await NatsClient.DisposeAsync().ConfigureAwait(false); } + } + /// + /// Gets the instance used for probing the health status of the NATS server. + /// + /// instance representing the active connection to the NATS server. + public INatsConnection GetHealthCheckTarget() + { + return NatsClient.Connection; } } diff --git a/src/Savvyio.Extensions.NATS/NatsMessageOptions.cs b/src/Savvyio.Extensions.NATS/NatsMessageOptions.cs index 7e52f359..5c55caa9 100644 --- a/src/Savvyio.Extensions.NATS/NatsMessageOptions.cs +++ b/src/Savvyio.Extensions.NATS/NatsMessageOptions.cs @@ -1,61 +1,60 @@ -using System; +using System; using Cuemon; using Cuemon.Configuration; -namespace Savvyio.Extensions.NATS +namespace Savvyio.Extensions.NATS; + +/// +/// Configuration options that is related to NATS. +/// +/// +public class NatsMessageOptions : IValidatableParameterObject { /// - /// Configuration options that is related to NATS. + /// Initializes a new instance of the class with default values. /// - /// - public class NatsMessageOptions : IValidatableParameterObject + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// new Uri("nats://127.0.0.1:4222") + /// + /// + /// + /// null + /// + /// + /// + public NatsMessageOptions() { - /// - /// Initializes a new instance of the class with default values. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// new Uri("nats://127.0.0.1:4222") - /// - /// - /// - /// null - /// - /// - /// - public NatsMessageOptions() - { - NatsUrl = new Uri("nats://127.0.0.1:4222"); - } + NatsUrl = new Uri("nats://127.0.0.1:4222"); + } - /// - /// Gets or sets the URI of the NATS server. - /// - public Uri NatsUrl { get; set; } + /// + /// Gets or sets the URI of the NATS server. + /// + public Uri NatsUrl { get; set; } - /// - /// Gets or sets the subject to publish or subscribe to in NATS. - /// - public string Subject { get; set; } + /// + /// Gets or sets the subject to publish or subscribe to in NATS. + /// + public string Subject { get; set; } - /// - /// Validates the current options and throws an exception if the state is invalid. - /// - /// - /// is null or whitespace - or - - /// is null. - /// - public virtual void ValidateOptions() - { - Validator.ThrowIfInvalidState(NatsUrl == null); - Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(Subject)); - } + /// + /// Validates the current options and throws an exception if the state is invalid. + /// + /// + /// is null or whitespace - or - + /// is null. + /// + public virtual void ValidateOptions() + { + Validator.ThrowIfInvalidState(NatsUrl == null); + Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(Subject)); } } diff --git a/src/Savvyio.Extensions.NATS/Savvyio.Extensions.NATS.csproj b/src/Savvyio.Extensions.NATS/Savvyio.Extensions.NATS.csproj index 0dcd2a3b..034518e9 100644 --- a/src/Savvyio.Extensions.NATS/Savvyio.Extensions.NATS.csproj +++ b/src/Savvyio.Extensions.NATS/Savvyio.Extensions.NATS.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O core assemblies with support for NATS Work-Queue Stream and NATS Core Publish-Subscribe. diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/Bootstrapper.cs b/src/Savvyio.Extensions.Newtonsoft.Json/Bootstrapper.cs index 44d2a3fe..5b2b4956 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/Bootstrapper.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/Bootstrapper.cs @@ -1,30 +1,29 @@ using Codebelt.Extensions.Newtonsoft.Json.Formatters; using System.Threading; -namespace Savvyio.Extensions.Newtonsoft.Json +namespace Savvyio.Extensions.Newtonsoft.Json; + +internal static class Bootstrapper { - internal static class Bootstrapper - { - private static readonly Lock PadLock = new(); - private static bool _initialized; + private static readonly Lock PadLock = new(); + private static bool _initialized; - internal static void Initialize() + internal static void Initialize() + { + if (!_initialized) { - if (!_initialized) + lock (PadLock) { - lock (PadLock) + if (!_initialized) { - if (!_initialized) + _initialized = true; + NewtonsoftJsonFormatterOptions.DefaultConverters += list => { - _initialized = true; - NewtonsoftJsonFormatterOptions.DefaultConverters += list => - { - list.AddRequestConverter(); - list.AddMetadataDictionaryConverter(); - list.AddMessageConverter(); - list.AddSingleValueObjectConverter(); - }; - } + list.AddRequestConverter(); + list.AddMetadataDictionaryConverter(); + list.AddMessageConverter(); + list.AddSingleValueObjectConverter(); + }; } } } diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/AggregateRootConverter.cs b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/AggregateRootConverter.cs index 003c02c3..6a0860e7 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/AggregateRootConverter.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/AggregateRootConverter.cs @@ -12,143 +12,142 @@ using Newtonsoft.Json; using Savvyio.Domain; -namespace Savvyio.Extensions.Newtonsoft.Json.Converters +namespace Savvyio.Extensions.Newtonsoft.Json.Converters; + +/// +/// Converts an to or from JSON. +/// +/// The type of the key that uniquely identifies an aggregate. +/// +public class AggregateRootConverter : JsonConverter> { + private readonly ActivatorOptions _options; + /// - /// Converts an to or from JSON. + /// Initializes a new instance of the class. /// - /// The type of the key that uniquely identifies an aggregate. - /// - public class AggregateRootConverter : JsonConverter> + /// The which may be configured. + public AggregateRootConverter(Action setup = null) { - private readonly ActivatorOptions _options; + _options = Patterns.Configure(setup); + } - /// - /// Initializes a new instance of the class. - /// - /// The which may be configured. - public AggregateRootConverter(Action setup = null) - { - _options = Patterns.Configure(setup); - } + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, AggregateRoot value, JsonSerializer serializer) + { + var properties = value.GetType().GetRuntimePropertiesExceptOf>().Where(pi => pi.CanRead).ToList(); + writer.WriteStartObject(); + writer.WritePropertyName(nameof(value.Id), serializer); + writer.WriteValue(value.Id); - /// - /// Writes the JSON representation of the object. - /// - /// The to write to. - /// The value. - /// The calling serializer. - public override void WriteJson(JsonWriter writer, AggregateRoot value, JsonSerializer serializer) + foreach (var property in properties) { - var properties = value.GetType().GetRuntimePropertiesExceptOf>().Where(pi => pi.CanRead).ToList(); - writer.WriteStartObject(); - writer.WritePropertyName(nameof(value.Id), serializer); - writer.WriteValue(value.Id); - - foreach (var property in properties) - { - writer.WritePropertyName(property.Name, serializer); - writer.WriteObject(property.GetValue(value), serializer); - } - - writer.WriteEndObject(); + writer.WritePropertyName(property.Name, serializer); + writer.WriteObject(property.GetValue(value), serializer); } - /// - /// Reads the JSON representation of the object. - /// - /// The to read from. - /// Type of the object. - /// The existing value of object being read. If there is no existing value then null will be used. - /// The existing value has a value. - /// The calling serializer. - /// The object value. - public override AggregateRoot ReadJson(JsonReader reader, Type objectType, AggregateRoot existingValue, bool hasExistingValue, JsonSerializer serializer) - { - var idProperty = objectType.GetProperty(nameof(IEntity.Id), _options.Flags); - var properties = objectType.GetRuntimePropertiesExceptOf>().Where(pi => pi.CanRead).ToList(); - if (idProperty != null) { properties.Insert(0, idProperty); } + writer.WriteEndObject(); + } - var propertyData = ReadPropertyData(reader, properties, serializer); + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. If there is no existing value then null will be used. + /// The existing value has a value. + /// The calling serializer. + /// The object value. + public override AggregateRoot ReadJson(JsonReader reader, Type objectType, AggregateRoot existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var idProperty = objectType.GetProperty(nameof(IEntity.Id), _options.Flags); + var properties = objectType.GetRuntimePropertiesExceptOf>().Where(pi => pi.CanRead).ToList(); + if (idProperty != null) { properties.Insert(0, idProperty); } - var result = CreateAggregateRoot(objectType, properties, propertyData); - if (result != null) { return result; } + var propertyData = ReadPropertyData(reader, properties, serializer); - throw ExceptionInsights.Embed(new InvalidOperationException($"Unable to deserialize {objectType.FullName}; consider adding a custom converter for this type."), MethodBase.GetCurrentMethod(), Arguments.ToArray(reader, objectType, existingValue, hasExistingValue, serializer)); - } + var result = CreateAggregateRoot(objectType, properties, propertyData); + if (result != null) { return result; } - private List ReadPropertyData(JsonReader reader, List properties, JsonSerializer serializer) - { - var propertyData = new List(); - if (reader.TokenType != JsonToken.StartObject) { return propertyData; } - - var depth = reader.Depth; - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonToken.PropertyName: - var propertyName = (string)reader.Value; - var matchingProperty = properties.FirstOrDefault(pi => pi.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); - if (matchingProperty != null) - { - reader.Read(); - propertyData.Add(ReadDataPair(reader, matchingProperty, serializer)); - } - break; - } - - if (reader.Depth == depth && reader.TokenType == JsonToken.EndObject) { break; } - } + throw ExceptionInsights.Embed(new InvalidOperationException($"Unable to deserialize {objectType.FullName}; consider adding a custom converter for this type."), MethodBase.GetCurrentMethod(), Arguments.ToArray(reader, objectType, existingValue, hasExistingValue, serializer)); + } - return propertyData; - } + private List ReadPropertyData(JsonReader reader, List properties, JsonSerializer serializer) + { + var propertyData = new List(); + if (reader.TokenType != JsonToken.StartObject) { return propertyData; } - private DataPair ReadDataPair(JsonReader reader, PropertyInfo matchingProperty, JsonSerializer serializer) + var depth = reader.Depth; + while (reader.Read()) { - if (matchingProperty.PropertyType.HasTypes(typeof(SingleValueObject<>))) + switch (reader.TokenType) { - return new DataPair(matchingProperty.Name, serializer.Deserialize(reader, matchingProperty.PropertyType), matchingProperty.PropertyType); + case JsonToken.PropertyName: + var propertyName = (string)reader.Value; + var matchingProperty = properties.FirstOrDefault(pi => pi.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); + if (matchingProperty != null) + { + reader.Read(); + propertyData.Add(ReadDataPair(reader, matchingProperty, serializer)); + } + break; } - return new DataPair(matchingProperty.Name, ParserFactory.FromObject().Parse(reader.Value?.ToString(), matchingProperty.PropertyType, o => o.FormatProvider = _options.FormatProvider) ?? serializer.Deserialize(reader, matchingProperty.PropertyType), matchingProperty.PropertyType); + if (reader.Depth == depth && reader.TokenType == JsonToken.EndObject) { break; } } - private AggregateRoot CreateAggregateRoot(Type objectType, List properties, List propertyData) - { - var ctors = objectType.GetConstructors(_options.Flags).ToList(); - if (ctors.Count == 0) { return null; } + return propertyData; + } - var matchingCtor = ctors.SingleOrDefault(info => MatchesConstructor(info, propertyData)); - if (matchingCtor != null) - { - return matchingCtor.Invoke(propertyData.Select(pair => pair.Value).ToArray()) as AggregateRoot; - } + private DataPair ReadDataPair(JsonReader reader, PropertyInfo matchingProperty, JsonSerializer serializer) + { + if (matchingProperty.PropertyType.HasTypes(typeof(SingleValueObject<>))) + { + return new DataPair(matchingProperty.Name, serializer.Deserialize(reader, matchingProperty.PropertyType), matchingProperty.PropertyType); + } - var defaultCtor = ctors.SingleOrDefault(ci => ci.GetParameters().Length == 0); - if (defaultCtor == null) { return null; } + return new DataPair(matchingProperty.Name, ParserFactory.FromObject().Parse(reader.Value?.ToString(), matchingProperty.PropertyType, o => o.FormatProvider = _options.FormatProvider) ?? serializer.Deserialize(reader, matchingProperty.PropertyType), matchingProperty.PropertyType); + } - return PopulateDefaultConstructed(defaultCtor, properties, propertyData); - } + private AggregateRoot CreateAggregateRoot(Type objectType, List properties, List propertyData) + { + var ctors = objectType.GetConstructors(_options.Flags).ToList(); + if (ctors.Count == 0) { return null; } - private static bool MatchesConstructor(ConstructorInfo info, List propertyData) + var matchingCtor = ctors.SingleOrDefault(info => MatchesConstructor(info, propertyData)); + if (matchingCtor != null) { - var paramters = info.GetParameters().ToList(); - return paramters.Count == propertyData.Count && paramters.Select(pi => pi.ParameterType).SequenceEqual(propertyData.Select(pair => pair.Type)); + return matchingCtor.Invoke(propertyData.Select(pair => pair.Value).ToArray()) as AggregateRoot; } - private static AggregateRoot PopulateDefaultConstructed(ConstructorInfo defaultCtor, List properties, List propertyData) + var defaultCtor = ctors.SingleOrDefault(ci => ci.GetParameters().Length == 0); + if (defaultCtor == null) { return null; } + + return PopulateDefaultConstructed(defaultCtor, properties, propertyData); + } + + private static bool MatchesConstructor(ConstructorInfo info, List propertyData) + { + var paramters = info.GetParameters().ToList(); + return paramters.Count == propertyData.Count && paramters.Select(pi => pi.ParameterType).SequenceEqual(propertyData.Select(pair => pair.Type)); + } + + private static AggregateRoot PopulateDefaultConstructed(ConstructorInfo defaultCtor, List properties, List propertyData) + { + var ar = defaultCtor.Invoke(Array.Empty()) as AggregateRoot; + foreach (var property in properties) { - var ar = defaultCtor.Invoke(Array.Empty()) as AggregateRoot; - foreach (var property in properties) + if (property.CanWrite) { - if (property.CanWrite) - { - property.SetValue(ar, propertyData.SingleOrDefault(pair => pair.Name.Equals(property.Name, StringComparison.OrdinalIgnoreCase))?.Value); - } + property.SetValue(ar, propertyData.SingleOrDefault(pair => pair.Name.Equals(property.Name, StringComparison.OrdinalIgnoreCase))?.Value); } - - return ar; } + + return ar; } } diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/MessageConverter.cs b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/MessageConverter.cs index 516abe48..c4bae6ea 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/MessageConverter.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/MessageConverter.cs @@ -1,9 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Cuemon.Extensions; using Codebelt.Extensions.Newtonsoft.Json; +using Cuemon.Reflection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Savvyio.EventDriven; @@ -11,174 +12,172 @@ using Savvyio.EventDriven.Messaging.CloudEvents.Cryptography; using Savvyio.Messaging; using Savvyio.Messaging.Cryptography; -using Savvyio.Reflection; -namespace Savvyio.Extensions.Newtonsoft.Json.Converters +namespace Savvyio.Extensions.Newtonsoft.Json.Converters; + +/// +/// Converts an (or derived) to or from JSON. +/// +/// +public class MessageConverter : JsonConverter { + internal static readonly Lazy> CloudEventTypes = new(() => AssemblyContext.GetCurrentDomainAssemblies(o => o.ExcludedAssemblies.Add(typeof(IMessage<>).Assembly)).SelectMany(a => a.DefinedTypes.Where(ti => ti.HasInterfaces(typeof(ICloudEvent<>)) && + ti is { IsAbstract: false, IsInterface: false })).ToList()); + /// - /// Converts an (or derived) to or from JSON. + /// Writes the JSON representation of the object. /// - /// - public class MessageConverter : JsonConverter + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - internal static readonly Lazy> CloudEventTypes = new(() => AssemblyContext.CurrentDomainAssemblies.SelectMany(a => a.DefinedTypes.Where(ti => ti.HasInterfaces(typeof(ICloudEvent<>)) && - ti is { IsAbstract: false, IsInterface: false })).ToList()); - - /// - /// Writes the JSON representation of the object. - /// - /// The to write to. - /// The value. - /// The calling serializer. - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - if (value == null) { return; } - var objectType = value.GetType(); - var requestType = objectType.GetGenericArguments()[0]; - var converter = (JsonConverter)Activator.CreateInstance( - typeof(MessageConverter<>).MakeGenericType(requestType), - BindingFlags.Instance | BindingFlags.Public, - binder: null, - args: null, - culture: null)!; - converter.WriteJson(writer, value, serializer); - } + if (value == null) { return; } + var objectType = value.GetType(); + var requestType = objectType.GetGenericArguments()[0]; + var converter = (JsonConverter)Activator.CreateInstance( + typeof(MessageConverter<>).MakeGenericType(requestType), + BindingFlags.Instance | BindingFlags.Public, + binder: null, + args: null, + culture: null)!; + converter.WriteJson(writer, value, serializer); + } - /// - /// Reads the JSON representation of the object. - /// - /// The to read from. - /// Type of the object. - /// The existing value of object being read. - /// The calling serializer. - /// The object value. - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var requestType = objectType.GetGenericArguments()[0]; - var converter = (JsonConverter)Activator.CreateInstance( - typeof(MessageConverter<>).MakeGenericType(requestType), - BindingFlags.Instance | BindingFlags.Public, - binder: null, - args: null, - culture: null)!; - return converter.ReadJson(reader, objectType, existingValue, serializer); - } + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var requestType = objectType.GetGenericArguments()[0]; + var converter = (JsonConverter)Activator.CreateInstance( + typeof(MessageConverter<>).MakeGenericType(requestType), + BindingFlags.Instance | BindingFlags.Public, + binder: null, + args: null, + culture: null)!; + return converter.ReadJson(reader, objectType, existingValue, serializer); + } - /// - /// Determines whether this instance can convert the specified object type. - /// - /// Type of the object. - /// true if this instance can convert the specified object type; otherwise, false. - public override bool CanConvert(Type objectType) - { - return (objectType.IsGenericType && - objectType.GetGenericTypeDefinition().HasInterfaces(typeof(IMessage<>))); - } + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// true if this instance can convert the specified object type; otherwise, false. + public override bool CanConvert(Type objectType) + { + return (objectType.IsGenericType && + objectType.GetGenericTypeDefinition().HasInterfaces(typeof(IMessage<>))); } +} - internal sealed class MessageConverter : JsonConverter> where T : IRequest +internal sealed class MessageConverter : JsonConverter> where T : IRequest +{ + public MessageConverter() { - public MessageConverter() + } + + public override void WriteJson(JsonWriter writer, IMessage value, JsonSerializer serializer) + { + writer.WriteStartObject(); + writer.WritePropertyName(nameof(value.Id), serializer); + writer.WriteValue(value.Id); + writer.WritePropertyName(nameof(value.Source), serializer); + writer.WriteValue(value.Source); + writer.WritePropertyName(nameof(value.Type), serializer); + writer.WriteValue(value.Type); + writer.WritePropertyName(nameof(value.Time), serializer); + writer.WriteObject(value.Time, serializer); + writer.WritePropertyName(nameof(value.Data), serializer); + writer.WriteObject(value.Data, serializer); + + if (value.GetType().HasInterfaces(typeof(ICloudEvent<>))) { + dynamic ce = value; + writer.WritePropertyName(nameof(ICloudEvent.Specversion), serializer); + writer.WriteValue(ce.Specversion); } - public override void WriteJson(JsonWriter writer, IMessage value, JsonSerializer serializer) + if (value is ISignedMessage sm) { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(value.Id), serializer); - writer.WriteValue(value.Id); - writer.WritePropertyName(nameof(value.Source), serializer); - writer.WriteValue(value.Source); - writer.WritePropertyName(nameof(value.Type), serializer); - writer.WriteValue(value.Type); - writer.WritePropertyName(nameof(value.Time), serializer); - writer.WriteObject(value.Time, serializer); - writer.WritePropertyName(nameof(value.Data), serializer); - writer.WriteObject(value.Data, serializer); - - if (value.GetType().HasInterfaces(typeof(ICloudEvent<>))) - { - dynamic ce = value; - writer.WritePropertyName(nameof(ICloudEvent.Specversion), serializer); - writer.WriteValue(ce.Specversion); - } - - if (value is ISignedMessage sm) - { - writer.WritePropertyName(nameof(sm.Signature), serializer); - writer.WriteValue(sm.Signature); - } + writer.WritePropertyName(nameof(sm.Signature), serializer); + writer.WriteValue(sm.Signature); + } - if (value is IDictionary dictionary) + if (value is IDictionary dictionary) + { + foreach (var kvp in dictionary) { - foreach (var kvp in dictionary) - { - writer.WritePropertyName(kvp.Key); - writer.WriteObject(kvp.Value, serializer); - } + writer.WritePropertyName(kvp.Key); + writer.WriteObject(kvp.Value, serializer); } - - writer.WriteEndObject(); } - public override IMessage ReadJson(JsonReader reader, Type objectType, IMessage existingValue, bool hasExistingValue, JsonSerializer serializer) + writer.WriteEndObject(); + } + + public override IMessage ReadJson(JsonReader reader, Type objectType, IMessage existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var idKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Id)); + var sourceKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Source)); + var timeKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Time)); + var typeKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Type)); + var dataKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Data)); + var metadataKey = serializer.ResolvePropertyKeyByConvention(nameof(IMetadata.Metadata)); + var memberTypeKey = serializer.ResolveDictionaryKeyByConvention(nameof(MetadataDictionary.MemberType)); + var signatureKey = serializer.ResolvePropertyKeyByConvention(nameof(ISignedMessage.Signature)); + + var document = JObject.Load(reader); + var id = document.Root[idKey]!.Value(); + var source = document.Root[sourceKey]!.Value().ToUri(); + var type = document.Root[typeKey]!.Value(); + var memberType = Type.GetType(document.Root[dataKey]![metadataKey]![memberTypeKey]!.Value()); + var time = document.Root[timeKey]!.Value().ToUniversalTime(); + var data = (T)serializer.Deserialize(document.Root[dataKey].CreateReader(), memberType); + + var message = new Message(id, source, type, data, time); + + if (objectType.HasInterfaces(typeof(ICloudEvent<>))) { - var idKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Id)); - var sourceKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Source)); - var timeKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Time)); - var typeKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Type)); - var dataKey = serializer.ResolvePropertyKeyByConvention(nameof(IMessage.Data)); - var metadataKey = serializer.ResolvePropertyKeyByConvention(nameof(IMetadata.Metadata)); - var memberTypeKey = serializer.ResolveDictionaryKeyByConvention(nameof(MetadataDictionary.MemberType)); - var signatureKey = serializer.ResolvePropertyKeyByConvention(nameof(ISignedMessage.Signature)); - - var document = JObject.Load(reader); - var id = document.Root[idKey]!.Value(); - var source = document.Root[sourceKey]!.Value().ToUri(); - var type = document.Root[typeKey]!.Value(); - var memberType = Type.GetType(document.Root[dataKey]![metadataKey]![memberTypeKey]!.Value()); - var time = document.Root[timeKey]!.Value().ToUniversalTime(); - var data = (T)serializer.Deserialize(document.Root[dataKey].CreateReader(), memberType); - - var message = new Message(id, source, type, data, time); - - if (objectType.HasInterfaces(typeof(ICloudEvent<>))) - { - var specVersionKey = serializer.ResolvePropertyKeyByConvention(nameof(ICloudEvent.Specversion)); + var specVersionKey = serializer.ResolvePropertyKeyByConvention(nameof(ICloudEvent.Specversion)); - var requestType = objectType.GetGenericArguments()[0]; - var cloudEventType = MessageConverter.CloudEventTypes.Value.Single(ti => ti.FullName!.StartsWith("Savvyio.EventDriven.Messaging.CloudEvents.CloudEvent", StringComparison.Ordinal)); - var specVersion = document.Root[specVersionKey]!.Value(); - var cloudEvent = Activator.CreateInstance(cloudEventType.MakeGenericType(requestType), [message, specVersion]) as IMessage; + var requestType = objectType.GetGenericArguments()[0]; + var cloudEventType = MessageConverter.CloudEventTypes.Value.Single(ti => ti.FullName!.StartsWith("Savvyio.EventDriven.Messaging.CloudEvents.CloudEvent", StringComparison.Ordinal)); + var specVersion = document.Root[specVersionKey]!.Value(); + var cloudEvent = Activator.CreateInstance(cloudEventType.MakeGenericType(requestType), [message, specVersion]) as IMessage; - if (objectType.HasInterfaces(typeof(ISignedCloudEvent<>))) - { - var signedCloudEventType = MessageConverter.CloudEventTypes.Value.Single(ti => ti.FullName!.StartsWith("Savvyio.EventDriven.Messaging.CloudEvents.Cryptography.SignedCloudEvent", StringComparison.Ordinal)); - var signature = document.Root[signatureKey]!.Value(); + if (objectType.HasInterfaces(typeof(ISignedCloudEvent<>))) + { + var signedCloudEventType = MessageConverter.CloudEventTypes.Value.Single(ti => ti.FullName!.StartsWith("Savvyio.EventDriven.Messaging.CloudEvents.Cryptography.SignedCloudEvent", StringComparison.Ordinal)); + var signature = document.Root[signatureKey]!.Value(); - return Activator.CreateInstance(signedCloudEventType.MakeGenericType(requestType), [cloudEvent, signature]) as IMessage; - } + return Activator.CreateInstance(signedCloudEventType.MakeGenericType(requestType), [cloudEvent, signature]) as IMessage; + } - var reservedKeys = new[] { idKey, sourceKey, timeKey, typeKey, dataKey, metadataKey, signatureKey, specVersionKey }; + var reservedKeys = new[] { idKey, sourceKey, timeKey, typeKey, dataKey, metadataKey, signatureKey, specVersionKey }; - if (cloudEvent is IDictionary dictionary) + if (cloudEvent is IDictionary dictionary) + { + foreach (var property in document.Properties().Where(jp => !reservedKeys.Contains(jp.Name))) { - foreach (var property in document.Properties().Where(jp => !reservedKeys.Contains(jp.Name))) - { - dictionary.Add(property.Name, property.Value.ToObject(null, serializer)); - } + dictionary.Add(property.Name, property.Value.ToObject(null, serializer)); } - - return cloudEvent; } - if (objectType.HasInterfaces(typeof(ISignedMessage<>))) - { - var signature = document.Root[signatureKey]!.Value(); - return new SignedMessage(message, signature); - } + return cloudEvent; + } - return message; + if (objectType.HasInterfaces(typeof(ISignedMessage<>))) + { + var signature = document.Root[signatureKey]!.Value(); + return new SignedMessage(message, signature); } + + return message; } } diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/RequestConverter.cs b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/RequestConverter.cs index ea4c41d8..60a9a79f 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/RequestConverter.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/RequestConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -6,99 +6,98 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace Savvyio.Extensions.Newtonsoft.Json.Converters +namespace Savvyio.Extensions.Newtonsoft.Json.Converters; + +/// +/// Converts an to or from JSON. +/// +/// +public class RequestConverter : JsonConverter { /// - /// Converts an to or from JSON. + /// Initializes a new instance of the class. /// - /// - public class RequestConverter : JsonConverter + public RequestConverter() { - /// - /// Initializes a new instance of the class. - /// - public RequestConverter() - { - } + } - /// - /// Writes the JSON representation of the object. - /// - /// The to write to. - /// The value. - /// The calling serializer. - /// - /// This method is not implemented and should not be used. - /// - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - throw new NotImplementedException(); - } + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + /// + /// This method is not implemented and should not be used. + /// + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } - /// - /// Gets a value indicating whether this can write JSON. - /// - /// true if this can write JSON; otherwise, false. - public override bool CanWrite { get; } = false; + /// + /// Gets a value indicating whether this can write JSON. + /// + /// true if this can write JSON; otherwise, false. + public override bool CanWrite { get; } = false; - /// - /// Reads the JSON representation of the object. - /// - /// The to read from. - /// Type of the object. - /// The existing value of object being read. - /// The calling serializer. - /// The object value. - /// - /// This deserializer only supports rehydration of implementations that either use auto-properties or have a naming convention that makes it possible to tie non-writable properties with the backing field equivalent. - /// - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + /// + /// This deserializer only supports rehydration of implementations that either use auto-properties or have a naming convention that makes it possible to tie non-writable properties with the backing field equivalent. + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var document = JObject.Load(reader); + var instance = RuntimeHelpers.GetUninitializedObject(objectType); + var properties = objectType.GetAllProperties(); + var jProperties = document.Properties().ToList(); + foreach (var property in properties.Where(pi => jProperties.Exists(jp => jp.Name.Equals(serializer.ResolvePropertyKeyByConvention(pi.Name), StringComparison.OrdinalIgnoreCase)))) { - var document = JObject.Load(reader); - var instance = RuntimeHelpers.GetUninitializedObject(objectType); - var properties = objectType.GetAllProperties(); - var jProperties = document.Properties().ToList(); - foreach (var property in properties.Where(pi => jProperties.Exists(jp => jp.Name.Equals(serializer.ResolvePropertyKeyByConvention(pi.Name), StringComparison.OrdinalIgnoreCase)))) + var jProperty = jProperties.Single(jp => jp.Name.Equals(serializer.ResolvePropertyKeyByConvention(property.Name), StringComparison.OrdinalIgnoreCase)); + if (document.Root[jProperty.Name] != null) { - var jProperty = jProperties.Single(jp => jp.Name.Equals(serializer.ResolvePropertyKeyByConvention(property.Name), StringComparison.OrdinalIgnoreCase)); - if (document.Root[jProperty.Name] != null) - { - var value = serializer.Deserialize(document.Root[jProperty.Name].CreateReader(), property.PropertyType); - SetPropertyOrField(instance, objectType, property, value); - } + var value = serializer.Deserialize(document.Root[jProperty.Name].CreateReader(), property.PropertyType); + SetPropertyOrField(instance, objectType, property, value); } - return instance as IRequest; - } + return instance as IRequest; - private static void SetPropertyOrField(object instance, Type objectType, PropertyInfo property, object value) - { - if (property.CanWrite) - { - property.SetValue(instance, value); - return; - } - - var field = property.IsAutoProperty() - ? objectType.GetAllFields().SingleOrDefault(fi => fi.Name.StartsWith($"<{property.Name}>", StringComparison.Ordinal)) - : objectType.GetAllFields().SingleOrDefault(fi => fi.Name.Equals($"_{property.Name}>", StringComparison.OrdinalIgnoreCase)); - if (field == null) - { - throw new NotSupportedException($"This deserializer only supports rehydration of {nameof(IRequest)} implementations that either use auto-properties or have a naming convention that makes it possible to tie non-writable properties with the backing field equivalent."); - } + } - field.SetValue(instance, value); + private static void SetPropertyOrField(object instance, Type objectType, PropertyInfo property, object value) + { + if (property.CanWrite) + { + property.SetValue(instance, value); + return; } - /// - /// Determines whether this instance can convert the specified object type. - /// - /// Type of the object. - /// true if this instance can convert the specified object type; otherwise, false. - public override bool CanConvert(Type objectType) + var field = property.IsAutoProperty() + ? objectType.GetAllFields().SingleOrDefault(fi => fi.Name.StartsWith($"<{property.Name}>", StringComparison.Ordinal)) + : objectType.GetAllFields().SingleOrDefault(fi => fi.Name.Equals($"_{property.Name}>", StringComparison.OrdinalIgnoreCase)); + if (field == null) { - return typeof(IRequest).IsAssignableFrom(objectType); + throw new NotSupportedException($"This deserializer only supports rehydration of {nameof(IRequest)} implementations that either use auto-properties or have a naming convention that makes it possible to tie non-writable properties with the backing field equivalent."); } + + field.SetValue(instance, value); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// true if this instance can convert the specified object type; otherwise, false. + public override bool CanConvert(Type objectType) + { + return typeof(IRequest).IsAssignableFrom(objectType); } } diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/SingleValueObjectConverter.cs b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/SingleValueObjectConverter.cs index 18b7a226..d9c9d916 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/SingleValueObjectConverter.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/SingleValueObjectConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using Cuemon.Extensions; @@ -7,89 +7,88 @@ using Newtonsoft.Json; using Savvyio.Domain; -namespace Savvyio.Extensions.Newtonsoft.Json.Converters +namespace Savvyio.Extensions.Newtonsoft.Json.Converters; + +/// +/// Converts a to or from JSON. +/// +/// +public class SingleValueObjectConverter : JsonConverter { /// - /// Converts a to or from JSON. + /// Initializes a new instance of the class. /// - /// - public class SingleValueObjectConverter : JsonConverter + public SingleValueObjectConverter() { - /// - /// Initializes a new instance of the class. - /// - public SingleValueObjectConverter() - { - } + } - /// - /// Writes the JSON representation of the object. - /// - /// The to write to. - /// The value. - /// The calling serializer. - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - if (value == null) { return; } - var objectType = value.GetType(); - var valueType = objectType.GetInheritedTypes().Single(type => type.IsGenericType - && type.GetGenericTypeDefinition() == typeof(SingleValueObject<>)).GetGenericArguments()[0]; - var converter = (JsonConverter)Activator.CreateInstance( - typeof(SingleValueObjectConverter<>).MakeGenericType(valueType), - BindingFlags.Instance | BindingFlags.Public, - binder: null, - args: null, - culture: null)!; - converter.WriteJson(writer, value, serializer); - } + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) { return; } + var objectType = value.GetType(); + var valueType = objectType.GetInheritedTypes().Single(type => type.IsGenericType + && type.GetGenericTypeDefinition() == typeof(SingleValueObject<>)).GetGenericArguments()[0]; + var converter = (JsonConverter)Activator.CreateInstance( + typeof(SingleValueObjectConverter<>).MakeGenericType(valueType), + BindingFlags.Instance | BindingFlags.Public, + binder: null, + args: null, + culture: null)!; + converter.WriteJson(writer, value, serializer); + } - /// - /// Reads the JSON representation of the object. - /// - /// The to read from. - /// Type of the object. - /// The existing value of object being read. - /// The calling serializer. - /// The object value. - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var valueType = objectType.GetInheritedTypes().Single(type => type.IsGenericType - && type.GetGenericTypeDefinition() == typeof(SingleValueObject<>)).GetGenericArguments()[0]; - var converter = (JsonConverter)Activator.CreateInstance( - typeof(SingleValueObjectConverter<>).MakeGenericType(valueType), - BindingFlags.Instance | BindingFlags.Public, - binder: null, - args: null, - culture: null)!; - return converter.ReadJson(reader, objectType, existingValue, serializer); - } + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var valueType = objectType.GetInheritedTypes().Single(type => type.IsGenericType + && type.GetGenericTypeDefinition() == typeof(SingleValueObject<>)).GetGenericArguments()[0]; + var converter = (JsonConverter)Activator.CreateInstance( + typeof(SingleValueObjectConverter<>).MakeGenericType(valueType), + BindingFlags.Instance | BindingFlags.Public, + binder: null, + args: null, + culture: null)!; + return converter.ReadJson(reader, objectType, existingValue, serializer); + } - /// - /// Determines whether this instance can convert the specified object type. - /// - /// Type of the object. - /// true if this instance can convert the specified object type; otherwise, false. - public override bool CanConvert(Type objectType) - { - return objectType.HasTypes(typeof(SingleValueObject<>)); - } + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// true if this instance can convert the specified object type; otherwise, false. + public override bool CanConvert(Type objectType) + { + return objectType.HasTypes(typeof(SingleValueObject<>)); } +} - internal sealed class SingleValueObjectConverter : JsonConverter> +internal sealed class SingleValueObjectConverter : JsonConverter> +{ + public SingleValueObjectConverter() { - public SingleValueObjectConverter() - { - } + } - public override void WriteJson(JsonWriter writer, SingleValueObject value, JsonSerializer serializer) - { - writer.WriteObject(value.Value, serializer); - } + public override void WriteJson(JsonWriter writer, SingleValueObject value, JsonSerializer serializer) + { + writer.WriteObject(value.Value, serializer); + } - public override SingleValueObject ReadJson(JsonReader reader, Type objectType, SingleValueObject existingValue, bool hasExistingValue, JsonSerializer serializer) - { - var valueType = typeof(T); - return Activator.CreateInstance(objectType, serializer.Deserialize(reader, valueType)) as SingleValueObject; - } + public override SingleValueObject ReadJson(JsonReader reader, Type objectType, SingleValueObject existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var valueType = typeof(T); + return Activator.CreateInstance(objectType, serializer.Deserialize(reader, valueType)) as SingleValueObject; } } diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/ValueObjectConverter.cs b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/ValueObjectConverter.cs index 757a49eb..7274a11c 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/Converters/ValueObjectConverter.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/Converters/ValueObjectConverter.cs @@ -13,154 +13,153 @@ using Newtonsoft.Json; using Savvyio.Domain; -namespace Savvyio.Extensions.Newtonsoft.Json.Converters +namespace Savvyio.Extensions.Newtonsoft.Json.Converters; + +/// +/// Converts a to or from JSON. +/// +/// +public class ValueObjectConverter : JsonConverter { + private readonly ActivatorOptions _options; + /// - /// Converts a to or from JSON. + /// Initializes a new instance of the class. /// - /// - public class ValueObjectConverter : JsonConverter + /// The which may be configured. + public ValueObjectConverter(Action setup = null) { - private readonly ActivatorOptions _options; + _options = Patterns.Configure(setup); + } - /// - /// Initializes a new instance of the class. - /// - /// The which may be configured. - public ValueObjectConverter(Action setup = null) - { - _options = Patterns.Configure(setup); - } + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, ValueObject value, JsonSerializer serializer) + { + var properties = value.GetType().GetRuntimePropertiesExceptOf().Where(pi => pi.CanRead).ToList(); + writer.WriteStartObject(); - /// - /// Writes the JSON representation of the object. - /// - /// The to write to. - /// The value. - /// The calling serializer. - public override void WriteJson(JsonWriter writer, ValueObject value, JsonSerializer serializer) + foreach (var property in properties) { - var properties = value.GetType().GetRuntimePropertiesExceptOf().Where(pi => pi.CanRead).ToList(); - writer.WriteStartObject(); - - foreach (var property in properties) + writer.WritePropertyName(property.Name, serializer); + if (property.PropertyType.HasTypes(typeof(SingleValueObject<>))) { - writer.WritePropertyName(property.Name, serializer); - if (property.PropertyType.HasTypes(typeof(SingleValueObject<>))) - { - var svo = property.GetValue(value); - writer.WriteValue(svo.GetType().GetProperty("Value").GetValue(svo)); - } - else - { - writer.WriteObject(property.GetValue(value), serializer); - } + var svo = property.GetValue(value); + writer.WriteValue(svo.GetType().GetProperty("Value").GetValue(svo)); + } + else + { + writer.WriteObject(property.GetValue(value), serializer); } - - writer.WriteEndObject(); } - /// - /// Reads the JSON representation of the object. - /// - /// The to read from. - /// Type of the object. - /// The existing value of object being read. If there is no existing value then null will be used. - /// The existing value has a value. - /// The calling serializer. - /// The object value. - public override ValueObject ReadJson(JsonReader reader, Type objectType, ValueObject existingValue, bool hasExistingValue, JsonSerializer serializer) + writer.WriteEndObject(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. If there is no existing value then null will be used. + /// The existing value has a value. + /// The calling serializer. + /// The object value. + public override ValueObject ReadJson(JsonReader reader, Type objectType, ValueObject existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var properties = objectType.GetRuntimePropertiesExceptOf().Where(pi => pi.CanRead).ToList(); + if (properties.Count == 1 && objectType.HasTypes(typeof(SingleValueObject<>))) { - var properties = objectType.GetRuntimePropertiesExceptOf().Where(pi => pi.CanRead).ToList(); - if (properties.Count == 1 && objectType.HasTypes(typeof(SingleValueObject<>))) + var property = properties.Single(); + if (property.DeclaringType?.IsAssignableFrom(objectType) ?? false) { - var property = properties.Single(); - if (property.DeclaringType?.IsAssignableFrom(objectType) ?? false) - { - return Activator.CreateInstance(objectType, _options.Flags, _options.Binder, new[] { Decorator.Enclose(reader.Value).ChangeType(property.PropertyType) }, _options.FormatProvider as CultureInfo) as ValueObject; - } + return Activator.CreateInstance(objectType, _options.Flags, _options.Binder, new[] { Decorator.Enclose(reader.Value).ChangeType(property.PropertyType) }, _options.FormatProvider as CultureInfo) as ValueObject; } - - var propertyData = ReadPropertyData(reader, properties, serializer); - - var result = CreateValueObject(objectType, properties, propertyData); - if (result != null) { return result; } - - throw ExceptionInsights.Embed(new InvalidOperationException($"Unable to deserialize {objectType.FullName}; consider adding a custom converter for this type."), MethodBase.GetCurrentMethod(), Arguments.ToArray(reader, objectType, existingValue, hasExistingValue, serializer)); } - private List ReadPropertyData(JsonReader reader, List properties, JsonSerializer serializer) - { - var propertyData = new List(); - if (reader.TokenType != JsonToken.StartObject) { return propertyData; } + var propertyData = ReadPropertyData(reader, properties, serializer); - var depth = reader.Depth; - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonToken.PropertyName: - var propertyName = (string)reader.Value; - var matchingProperty = properties.FirstOrDefault(pi => pi.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); - if (matchingProperty != null) - { - reader.Read(); - propertyData.Add(ReadDataPair(reader, matchingProperty, serializer)); - } - break; - } - - if (reader.Depth == depth && reader.TokenType == JsonToken.EndObject) { break; } - } + var result = CreateValueObject(objectType, properties, propertyData); + if (result != null) { return result; } - return propertyData; - } + throw ExceptionInsights.Embed(new InvalidOperationException($"Unable to deserialize {objectType.FullName}; consider adding a custom converter for this type."), MethodBase.GetCurrentMethod(), Arguments.ToArray(reader, objectType, existingValue, hasExistingValue, serializer)); + } + + private List ReadPropertyData(JsonReader reader, List properties, JsonSerializer serializer) + { + var propertyData = new List(); + if (reader.TokenType != JsonToken.StartObject) { return propertyData; } - private DataPair ReadDataPair(JsonReader reader, PropertyInfo matchingProperty, JsonSerializer serializer) + var depth = reader.Depth; + while (reader.Read()) { - if (matchingProperty.PropertyType.HasTypes(typeof(SingleValueObject<>))) + switch (reader.TokenType) { - return new DataPair(matchingProperty.Name, serializer.Deserialize(reader, matchingProperty.PropertyType), matchingProperty.PropertyType); + case JsonToken.PropertyName: + var propertyName = (string)reader.Value; + var matchingProperty = properties.FirstOrDefault(pi => pi.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); + if (matchingProperty != null) + { + reader.Read(); + propertyData.Add(ReadDataPair(reader, matchingProperty, serializer)); + } + break; } - return new DataPair(matchingProperty.Name, ParserFactory.FromObject().Parse(reader.Value?.ToString(), matchingProperty.PropertyType, o => o.FormatProvider = _options.FormatProvider) ?? serializer.Deserialize(reader, matchingProperty.PropertyType), matchingProperty.PropertyType); + if (reader.Depth == depth && reader.TokenType == JsonToken.EndObject) { break; } } - private ValueObject CreateValueObject(Type objectType, List properties, List propertyData) - { - var ctors = objectType.GetConstructors(_options.Flags).ToList(); - if (ctors.Count == 0) { return null; } + return propertyData; + } - var matchingCtor = ctors.SingleOrDefault(info => MatchesConstructor(info, propertyData)); - if (matchingCtor != null) - { - return matchingCtor.Invoke(propertyData.Select(pair => pair.Value).ToArray()) as ValueObject; - } + private DataPair ReadDataPair(JsonReader reader, PropertyInfo matchingProperty, JsonSerializer serializer) + { + if (matchingProperty.PropertyType.HasTypes(typeof(SingleValueObject<>))) + { + return new DataPair(matchingProperty.Name, serializer.Deserialize(reader, matchingProperty.PropertyType), matchingProperty.PropertyType); + } - var defaultCtor = ctors.SingleOrDefault(ci => ci.GetParameters().Length == 0); - if (defaultCtor == null) { return null; } + return new DataPair(matchingProperty.Name, ParserFactory.FromObject().Parse(reader.Value?.ToString(), matchingProperty.PropertyType, o => o.FormatProvider = _options.FormatProvider) ?? serializer.Deserialize(reader, matchingProperty.PropertyType), matchingProperty.PropertyType); + } - return PopulateDefaultConstructed(defaultCtor, properties, propertyData); - } + private ValueObject CreateValueObject(Type objectType, List properties, List propertyData) + { + var ctors = objectType.GetConstructors(_options.Flags).ToList(); + if (ctors.Count == 0) { return null; } - private static bool MatchesConstructor(ConstructorInfo info, List propertyData) + var matchingCtor = ctors.SingleOrDefault(info => MatchesConstructor(info, propertyData)); + if (matchingCtor != null) { - var paramters = info.GetParameters().ToList(); - return paramters.Count == propertyData.Count && paramters.Select(pi => pi.ParameterType).SequenceEqual(propertyData.Select(pair => pair.Type)); + return matchingCtor.Invoke(propertyData.Select(pair => pair.Value).ToArray()) as ValueObject; } - private static ValueObject PopulateDefaultConstructed(ConstructorInfo defaultCtor, List properties, List propertyData) + var defaultCtor = ctors.SingleOrDefault(ci => ci.GetParameters().Length == 0); + if (defaultCtor == null) { return null; } + + return PopulateDefaultConstructed(defaultCtor, properties, propertyData); + } + + private static bool MatchesConstructor(ConstructorInfo info, List propertyData) + { + var paramters = info.GetParameters().ToList(); + return paramters.Count == propertyData.Count && paramters.Select(pi => pi.ParameterType).SequenceEqual(propertyData.Select(pair => pair.Type)); + } + + private static ValueObject PopulateDefaultConstructed(ConstructorInfo defaultCtor, List properties, List propertyData) + { + var vo = defaultCtor.Invoke(Array.Empty()) as ValueObject; + foreach (var property in properties) { - var vo = defaultCtor.Invoke(Array.Empty()) as ValueObject; - foreach (var property in properties) + if (property.CanWrite) { - if (property.CanWrite) - { - property.SetValue(vo, propertyData.SingleOrDefault(pair => pair.Name.Equals(property.Name, StringComparison.OrdinalIgnoreCase))?.Value); - } + property.SetValue(vo, propertyData.SingleOrDefault(pair => pair.Name.Equals(property.Name, StringComparison.OrdinalIgnoreCase))?.Value); } - - return vo; } + + return vo; } } diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/GlobalSuppressions.cs b/src/Savvyio.Extensions.Newtonsoft.Json/GlobalSuppressions.cs index c648670e..f2047b79 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/GlobalSuppressions.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/JsonConverterExtensions.cs b/src/Savvyio.Extensions.Newtonsoft.Json/JsonConverterExtensions.cs index 4c071e5e..7f916fb2 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/JsonConverterExtensions.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/JsonConverterExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Cuemon; using Codebelt.Extensions.Newtonsoft.Json; @@ -9,112 +9,111 @@ using Savvyio.Messaging; using Savvyio.Messaging.Cryptography; -namespace Savvyio.Extensions.Newtonsoft.Json +namespace Savvyio.Extensions.Newtonsoft.Json; + +/// +/// Extension methods for the class. +/// +public static class JsonConverterExtensions { /// - /// Extension methods for the class. + /// Adds a converter to the collection. /// - public static class JsonConverterExtensions + /// The collection to extend. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddValueObjectConverter(this ICollection converters, Action setup = null) { - /// - /// Adds a converter to the collection. - /// - /// The collection to extend. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddValueObjectConverter(this ICollection converters, Action setup = null) - { - Validator.ThrowIfNull(converters); - converters.Add(new ValueObjectConverter(setup)); - return converters; - } + Validator.ThrowIfNull(converters); + converters.Add(new ValueObjectConverter(setup)); + return converters; + } - /// - /// Adds a converter to the collection. - /// - /// The collection to extend. - /// The which may be configured. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddAggregateRootConverter(this ICollection converters, Action setup = null) - { - Validator.ThrowIfNull(converters); - converters.Add(new AggregateRootConverter()); - return converters; - } + /// + /// Adds a converter to the collection. + /// + /// The collection to extend. + /// The which may be configured. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddAggregateRootConverter(this ICollection converters, Action setup = null) + { + Validator.ThrowIfNull(converters); + converters.Add(new AggregateRootConverter()); + return converters; + } - /// - /// Adds a converter to the collection. - /// - /// The collection to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddMetadataDictionaryConverter(this ICollection converters) + /// + /// Adds a converter to the collection. + /// + /// The collection to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddMetadataDictionaryConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(JsonConverterFactory.Create(null, (reader, _, _, _) => { - Validator.ThrowIfNull(converters); - converters.Add(JsonConverterFactory.Create(null, (reader, _, _, _) => + var md = new MetadataDictionary(); + var result = JData.ReadAll(reader); + foreach (var entry in result) { - var md = new MetadataDictionary(); - var result = JData.ReadAll(reader); - foreach (var entry in result) - { - md.Add(entry.PropertyName, entry.Value); - } - return md; - })); - return converters; - } + md.Add(entry.PropertyName, entry.Value); + } + return md; + })); + return converters; + } - /// - /// Adds an converter to the collection. - /// - /// The collection to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddRequestConverter(this ICollection converters) - { - Validator.ThrowIfNull(converters); - converters.Add(new RequestConverter()); - return converters; - } + /// + /// Adds an converter to the collection. + /// + /// The collection to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddRequestConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(new RequestConverter()); + return converters; + } - /// - /// Adds an / converter to the collection. - /// - /// The collection to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddMessageConverter(this ICollection converters) - { - Validator.ThrowIfNull(converters); - converters.Add(new MessageConverter()); - return converters; - } + /// + /// Adds an / converter to the collection. + /// + /// The collection to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddMessageConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(new MessageConverter()); + return converters; + } - /// - /// Adds a converter to the collection. - /// - /// The collection to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddSingleValueObjectConverter(this ICollection converters) - { - Validator.ThrowIfNull(converters); - converters.Add(new SingleValueObjectConverter()); - return converters; - } + /// + /// Adds a converter to the collection. + /// + /// The collection to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddSingleValueObjectConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(new SingleValueObjectConverter()); + return converters; } } diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/JsonSerializerExtensions.cs b/src/Savvyio.Extensions.Newtonsoft.Json/JsonSerializerExtensions.cs index aa6cfde9..af96d467 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/JsonSerializerExtensions.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/JsonSerializerExtensions.cs @@ -1,35 +1,34 @@ -using Codebelt.Extensions.Newtonsoft.Json.Serialization; +using Codebelt.Extensions.Newtonsoft.Json.Serialization; using Newtonsoft.Json; -namespace Savvyio.Extensions.Newtonsoft.Json +namespace Savvyio.Extensions.Newtonsoft.Json; + +/// +/// Provides extension methods for the class. +/// +public static class JsonSerializerExtensions { /// - /// Provides extension methods for the class. + /// Resolves the property key by convention using the specified . /// - public static class JsonSerializerExtensions + /// The to use for resolving the property key. + /// The name of the property to resolve. + /// The resolved property key. + public static string ResolvePropertyKeyByConvention(this JsonSerializer serializer, string name) { - /// - /// Resolves the property key by convention using the specified . - /// - /// The to use for resolving the property key. - /// The name of the property to resolve. - /// The resolved property key. - public static string ResolvePropertyKeyByConvention(this JsonSerializer serializer, string name) - { - var ns = serializer.ContractResolver.ResolveNamingStrategyOrDefault(); - return ns.GetPropertyName(name, false); - } + var ns = serializer.ContractResolver.ResolveNamingStrategyOrDefault(); + return ns.GetPropertyName(name, false); + } - /// - /// Resolves the dictionary key by convention using the specified . - /// - /// The to use for resolving the dictionary key. - /// The name of the dictionary key to resolve. - /// The resolved dictionary key. - public static string ResolveDictionaryKeyByConvention(this JsonSerializer serializer, string name) - { - var ns = serializer.ContractResolver.ResolveNamingStrategyOrDefault(); - return ns.GetDictionaryKey(name); - } + /// + /// Resolves the dictionary key by convention using the specified . + /// + /// The to use for resolving the dictionary key. + /// The name of the dictionary key to resolve. + /// The resolved dictionary key. + public static string ResolveDictionaryKeyByConvention(this JsonSerializer serializer, string name) + { + var ns = serializer.ContractResolver.ResolveNamingStrategyOrDefault(); + return ns.GetDictionaryKey(name); } } diff --git a/src/Savvyio.Extensions.Newtonsoft.Json/NewtonsoftJsonMarshaller.cs b/src/Savvyio.Extensions.Newtonsoft.Json/NewtonsoftJsonMarshaller.cs index ba4db167..de638f86 100644 --- a/src/Savvyio.Extensions.Newtonsoft.Json/NewtonsoftJsonMarshaller.cs +++ b/src/Savvyio.Extensions.Newtonsoft.Json/NewtonsoftJsonMarshaller.cs @@ -1,67 +1,66 @@ -using System; +using System; using System.IO; using Codebelt.Extensions.Newtonsoft.Json.Formatters; using Newtonsoft.Json; -namespace Savvyio.Extensions.Newtonsoft.Json +namespace Savvyio.Extensions.Newtonsoft.Json; + +/// +/// Provides a class for marshalling data using the Newtonsoft JSON library. +/// +/// +public class NewtonsoftJsonMarshaller : IMarshaller { + private readonly Action _setup; + /// - /// Provides a class for marshalling data using the Newtonsoft JSON library. + /// Provides a default instance of the class optimized for messaging. /// - /// - public class NewtonsoftJsonMarshaller : IMarshaller - { - private readonly Action _setup; + /// The default instance of the class optimized for messaging. + public static NewtonsoftJsonMarshaller Default { get; } = new(o => o.Settings.Formatting = Formatting.None); - /// - /// Provides a default instance of the class optimized for messaging. - /// - /// The default instance of the class optimized for messaging. - public static NewtonsoftJsonMarshaller Default { get; } = new(o => o.Settings.Formatting = Formatting.None); - - /// - /// Creates a new instance of the class. - /// - /// The which may be configured. - /// JsonMarshaller. - public static NewtonsoftJsonMarshaller Create(Action setup = null) => new(setup); + /// + /// Creates a new instance of the class. + /// + /// The which may be configured. + /// JsonMarshaller. + public static NewtonsoftJsonMarshaller Create(Action setup = null) => new(setup); - static NewtonsoftJsonMarshaller() - { - Bootstrapper.Initialize(); - } + static NewtonsoftJsonMarshaller() + { + Bootstrapper.Initialize(); + } - /// - /// Initializes a new instance of the class. - /// - /// The which need to be configured. - public NewtonsoftJsonMarshaller(Action setup = null) - { - _setup = setup; - } + /// + /// Initializes a new instance of the class. + /// + /// The which need to be configured. + public NewtonsoftJsonMarshaller(Action setup = null) + { + _setup = setup; + } - /// - public Stream Serialize(TValue value) - { - return NewtonsoftJsonFormatter.SerializeObject(value, _setup); - } + /// + public Stream Serialize(TValue value) + { + return NewtonsoftJsonFormatter.SerializeObject(value, _setup); + } - /// - public Stream Serialize(object value, Type inputType) - { - return NewtonsoftJsonFormatter.SerializeObject(value, inputType, _setup); - } + /// + public Stream Serialize(object value, Type inputType) + { + return NewtonsoftJsonFormatter.SerializeObject(value, inputType, _setup); + } - /// - public TValue Deserialize(Stream data) - { - return NewtonsoftJsonFormatter.DeserializeObject(data, _setup); - } + /// + public TValue Deserialize(Stream data) + { + return NewtonsoftJsonFormatter.DeserializeObject(data, _setup); + } - /// - public object Deserialize(Stream data, Type returnType) - { - return NewtonsoftJsonFormatter.DeserializeObject(data, returnType, _setup); - } + /// + public object Deserialize(Stream data, Type returnType) + { + return NewtonsoftJsonFormatter.DeserializeObject(data, returnType, _setup); } } diff --git a/src/Savvyio.Extensions.QueueStorage/AzureQueue.cs b/src/Savvyio.Extensions.QueueStorage/AzureQueue.cs index 4e35b5d8..7f19d7b3 100644 --- a/src/Savvyio.Extensions.QueueStorage/AzureQueue.cs +++ b/src/Savvyio.Extensions.QueueStorage/AzureQueue.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -11,232 +11,231 @@ using Cuemon.Threading; using Savvyio.Messaging; -namespace Savvyio.Extensions.QueueStorage +namespace Savvyio.Extensions.QueueStorage; + +/// +/// Represents the base class from which all implementations of Azure Storage Queue should derive. +/// +/// The type of the request. +public abstract class AzureQueue where TRequest : IRequest { - /// - /// Represents the base class from which all implementations of Azure Storage Queue should derive. - /// - /// The type of the request. - public abstract class AzureQueue where TRequest : IRequest + private readonly IMarshaller _marshaller; + private readonly AzureQueueOptions _options; + private readonly QueueServiceClient _serviceClient; + private readonly QueueClient _client; + + private readonly Func, IMarshaller, string> _sendMessageFormatter = (message, marshaller) => { - private readonly IMarshaller _marshaller; - private readonly AzureQueueOptions _options; - private readonly QueueServiceClient _serviceClient; - private readonly QueueClient _client; + var base64Type = message.GetType() + .ToFullNameIncludingAssemblyName() + .ToByteArray() + .ToBase64String(); - private readonly Func, IMarshaller, string> _sendMessageFormatter = (message, marshaller) => - { - var base64Type = message.GetType() - .ToFullNameIncludingAssemblyName() - .ToByteArray() - .ToBase64String(); + var base64Message = marshaller.Serialize(message) + .ToByteArray() + .ToBase64String(); - var base64Message = marshaller.Serialize(message) - .ToByteArray() - .ToBase64String(); + return $"{base64Type}.{base64Message}"; + }; - return $"{base64Type}.{base64Message}"; - }; + private readonly Func> _receiveMessageFormatter = (rawMessage, marshaller) => + { + var base64Components = rawMessage.MessageText.Split('.'); + var type = Type.GetType(base64Components[0].FromBase64().ToEncodedString()); + return marshaller.Deserialize(base64Components[1].FromBase64().ToStream(), type) as IMessage; + }; - private readonly Func> _receiveMessageFormatter = (rawMessage, marshaller) => - { - var base64Components = rawMessage.MessageText.Split('.'); - var type = Type.GetType(base64Components[0].FromBase64().ToEncodedString()); - return marshaller.Deserialize(base64Components[1].FromBase64().ToStream(), type) as IMessage; - }; - - /// - /// Initializes a new instance of the class with testable Azure Storage Queue clients. - /// - /// The marshaller used for serializing and deserializing messages. - /// The used to configure this instance. - /// The queue service client to use. - /// The queue client to use. - /// The function delegate to format messages for sending. If null, a default formatter is used. - /// The function delegate to format messages for receiving. If null, a default formatter is used. - /// - /// cannot be null - or - - /// cannot be null - or - - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - protected AzureQueue(IMarshaller marshaller, AzureQueueOptions options, QueueServiceClient serviceClient, QueueClient client, Func, IMarshaller, string> sendMessageFormatter = null, Func> receiveMessageFormatter = null) - { - Validator.ThrowIfNull(marshaller); - Validator.ThrowIfInvalidOptions(options); - Validator.ThrowIfNull(serviceClient); - Validator.ThrowIfNull(client); - - _marshaller = marshaller; - _options = options; - _serviceClient = serviceClient; - _client = client; - _sendMessageFormatter = sendMessageFormatter ?? _sendMessageFormatter; - _receiveMessageFormatter = receiveMessageFormatter ?? _receiveMessageFormatter; - - options.SetConfiguredClient(_client); - } + /// + /// Initializes a new instance of the class with testable Azure Storage Queue clients. + /// + /// The marshaller used for serializing and deserializing messages. + /// The used to configure this instance. + /// The queue service client to use. + /// The queue client to use. + /// The function delegate to format messages for sending. If null, a default formatter is used. + /// The function delegate to format messages for receiving. If null, a default formatter is used. + /// + /// cannot be null - or - + /// cannot be null - or - + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + protected AzureQueue(IMarshaller marshaller, AzureQueueOptions options, QueueServiceClient serviceClient, QueueClient client, Func, IMarshaller, string> sendMessageFormatter = null, Func> receiveMessageFormatter = null) + { + Validator.ThrowIfNull(marshaller); + Validator.ThrowIfInvalidOptions(options); + Validator.ThrowIfNull(serviceClient); + Validator.ThrowIfNull(client); + + _marshaller = marshaller; + _options = options; + _serviceClient = serviceClient; + _client = client; + _sendMessageFormatter = sendMessageFormatter ?? _sendMessageFormatter; + _receiveMessageFormatter = receiveMessageFormatter ?? _receiveMessageFormatter; + + options.SetConfiguredClient(_client); + } - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The used to configure this instance. - /// The function delegate to format messages for sending. If null, a default formatter is used. - /// The function delegate to format messages for receiving. If null, a default formatter is used. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - /// - /// The following table shows the initial field values for an instance of . - /// - /// - /// Field - /// Initial Value - /// - /// - /// - /// - /// (message, marshaller) => - /// { - /// var base64Type = message.GetType() - /// .ToFullNameIncludingAssemblyName() - /// .ToByteArray() - /// .ToBase64String(); - /// var base64Message = marshaller.Serialize(message) - /// .ToByteArray() - /// .ToBase64String(); - /// return $"{base64Type}.{base64Message}"; - /// }; - /// - /// - /// - /// - /// - /// (rawMessage, marshaller) => - /// { - /// var base64Components = rawMessage.MessageText.Split('.'); - /// var type = Type.GetType(base64Components[0].FromBase64().ToEncodedString()); - /// return marshaller.Deserialize(base64Components[1].FromBase64().ToStream(), type) as IMessage<TRequest>; - /// }; - /// - /// - /// - /// - protected AzureQueue(IMarshaller marshaller, AzureQueueOptions options, Func, IMarshaller, string> sendMessageFormatter = null, Func> receiveMessageFormatter = null) - { - Validator.ThrowIfNull(marshaller); - Validator.ThrowIfInvalidOptions(options); + /// + /// Initializes a new instance of the class. + /// + /// The marshaller used for serializing and deserializing messages. + /// The used to configure this instance. + /// The function delegate to format messages for sending. If null, a default formatter is used. + /// The function delegate to format messages for receiving. If null, a default formatter is used. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + /// + /// The following table shows the initial field values for an instance of . + /// + /// + /// Field + /// Initial Value + /// + /// + /// + /// + /// (message, marshaller) => + /// { + /// var base64Type = message.GetType() + /// .ToFullNameIncludingAssemblyName() + /// .ToByteArray() + /// .ToBase64String(); + /// var base64Message = marshaller.Serialize(message) + /// .ToByteArray() + /// .ToBase64String(); + /// return $"{base64Type}.{base64Message}"; + /// }; + /// + /// + /// + /// + /// + /// (rawMessage, marshaller) => + /// { + /// var base64Components = rawMessage.MessageText.Split('.'); + /// var type = Type.GetType(base64Components[0].FromBase64().ToEncodedString()); + /// return marshaller.Deserialize(base64Components[1].FromBase64().ToStream(), type) as IMessage<TRequest>; + /// }; + /// + /// + /// + /// + protected AzureQueue(IMarshaller marshaller, AzureQueueOptions options, Func, IMarshaller, string> sendMessageFormatter = null, Func> receiveMessageFormatter = null) + { + Validator.ThrowIfNull(marshaller); + Validator.ThrowIfInvalidOptions(options); - _marshaller = marshaller; - _options = options; - _sendMessageFormatter = sendMessageFormatter ?? _sendMessageFormatter; - _receiveMessageFormatter = receiveMessageFormatter ?? _receiveMessageFormatter; + _marshaller = marshaller; + _options = options; + _sendMessageFormatter = sendMessageFormatter ?? _sendMessageFormatter; + _receiveMessageFormatter = receiveMessageFormatter ?? _receiveMessageFormatter; - if (string.IsNullOrWhiteSpace(options.ConnectionString)) + if (string.IsNullOrWhiteSpace(options.ConnectionString)) + { + var queueUri = $"https://{options.StorageAccountName}.queue.core.windows.net/{options.QueueName}".ToUri(); + if (options.Credential != null) + { + _serviceClient = new QueueServiceClient(queueUri, options.Credential, options.Settings); + } + else if (options.SasCredential != null) { - var queueUri = $"https://{options.StorageAccountName}.queue.core.windows.net/{options.QueueName}".ToUri(); - if (options.Credential != null) - { - _serviceClient = new QueueServiceClient(queueUri, options.Credential, options.Settings); - } - else if (options.SasCredential != null) - { - _serviceClient = new QueueServiceClient(queueUri, options.SasCredential, options.Settings); - } - else if (options.StorageKeyCredential != null) - { - _serviceClient = new QueueServiceClient(queueUri, options.StorageKeyCredential, options.Settings); - } + _serviceClient = new QueueServiceClient(queueUri, options.SasCredential, options.Settings); } - else + else if (options.StorageKeyCredential != null) { - _serviceClient = new QueueServiceClient(options.ConnectionString, options.Settings); + _serviceClient = new QueueServiceClient(queueUri, options.StorageKeyCredential, options.Settings); } + } + else + { + _serviceClient = new QueueServiceClient(options.ConnectionString, options.Settings); + } - _client = _serviceClient!.GetQueueClient(options.QueueName); + _client = _serviceClient!.GetQueueClient(options.QueueName); - options.SetConfiguredClient(_client); - } + options.SetConfiguredClient(_client); + } - /// - /// Sends the specified messages to the Azure Storage Queue. - /// - /// The messages to send. - /// The which may be configured. - /// A task that represents the asynchronous operation. - /// - /// cannot be null. - /// - protected async Task SendMessageAsync(IEnumerable> messages, Action setup = null) - { - Validator.ThrowIfNull(messages); - Validator.ThrowIfInvalidConfigurator(setup, out var options); + /// + /// Sends the specified messages to the Azure Storage Queue. + /// + /// The messages to send. + /// The which may be configured. + /// A task that represents the asynchronous operation. + /// + /// cannot be null. + /// + protected async Task SendMessageAsync(IEnumerable> messages, Action setup = null) + { + Validator.ThrowIfNull(messages); + Validator.ThrowIfInvalidConfigurator(setup, out var options); - foreach (var message in messages) - { - await _client.SendMessageAsync(_sendMessageFormatter(message, _marshaller), - _options.SendContext.VisibilityTimeout, - _options.SendContext.TimeToLive, - options.CancellationToken).ConfigureAwait(false); - } + foreach (var message in messages) + { + await _client.SendMessageAsync(_sendMessageFormatter(message, _marshaller), + _options.SendContext.VisibilityTimeout, + _options.SendContext.TimeToLive, + options.CancellationToken).ConfigureAwait(false); } + } - /// - /// Receives messages from the Azure Storage Queue. - /// - /// The which may be configured. - /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . - protected async IAsyncEnumerable> ReceiveMessagesAsync(Action setup = null) - { - Validator.ThrowIfInvalidConfigurator(setup, out var options); + /// + /// Receives messages from the Azure Storage Queue. + /// + /// The which may be configured. + /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . + protected async IAsyncEnumerable> ReceiveMessagesAsync(Action setup = null) + { + Validator.ThrowIfInvalidConfigurator(setup, out var options); - var rawMessages = await _client.ReceiveMessagesAsync(_options.ReceiveContext.NumberOfMessagesToTakePerRequest, - _options.ReceiveContext.VisibilityTimeout, - options.CancellationToken).ConfigureAwait(false); + var rawMessages = await _client.ReceiveMessagesAsync(_options.ReceiveContext.NumberOfMessagesToTakePerRequest, + _options.ReceiveContext.VisibilityTimeout, + options.CancellationToken).ConfigureAwait(false); - if (rawMessages.HasValue && rawMessages.Value.Length > 0) + if (rawMessages.HasValue && rawMessages.Value.Length > 0) + { + foreach (var rawMessage in rawMessages.Value) { - foreach (var rawMessage in rawMessages.Value) - { - options.CancellationToken.ThrowIfCancellationRequested(); - - var message = _receiveMessageFormatter(rawMessage, _marshaller); - message!.Properties.Add(nameof(QueueMessage.MessageId), rawMessage.MessageId); - message.Properties.Add(nameof(QueueMessage.PopReceipt), rawMessage.PopReceipt); - message.Properties.Add(nameof(CancellationToken), options.CancellationToken); - message.Acknowledged += OnAcknowledgedAsync; - yield return message; - } + options.CancellationToken.ThrowIfCancellationRequested(); + + var message = _receiveMessageFormatter(rawMessage, _marshaller); + message!.Properties.Add(nameof(QueueMessage.MessageId), rawMessage.MessageId); + message.Properties.Add(nameof(QueueMessage.PopReceipt), rawMessage.PopReceipt); + message.Properties.Add(nameof(CancellationToken), options.CancellationToken); + message.Acknowledged += OnAcknowledgedAsync; + yield return message; } } + } - /// - /// Handles the acknowledgment of a message by deleting it from the queue. - /// - /// The message that was acknowledged. - /// The event arguments containing properties of the acknowledged message. - private async Task OnAcknowledgedAsync(object sender, AcknowledgedEventArgs args) - { - var messageId = (string)args.Properties[nameof(QueueMessage.MessageId)]; - var popReceipt = (string)args.Properties[nameof(QueueMessage.PopReceipt)]; - var cancellationToken = (CancellationToken)args.Properties[nameof(CancellationToken)]; + /// + /// Handles the acknowledgment of a message by deleting it from the queue. + /// + /// The message that was acknowledged. + /// The event arguments containing properties of the acknowledged message. + private async Task OnAcknowledgedAsync(object sender, AcknowledgedEventArgs args) + { + var messageId = (string)args.Properties[nameof(QueueMessage.MessageId)]; + var popReceipt = (string)args.Properties[nameof(QueueMessage.PopReceipt)]; + var cancellationToken = (CancellationToken)args.Properties[nameof(CancellationToken)]; - await _client.DeleteMessageAsync(messageId, popReceipt, cancellationToken).ConfigureAwait(false); + await _client.DeleteMessageAsync(messageId, popReceipt, cancellationToken).ConfigureAwait(false); - if (sender is IAcknowledgeable message) { message.Acknowledged -= OnAcknowledgedAsync; } - } + if (sender is IAcknowledgeable message) { message.Acknowledged -= OnAcknowledgedAsync; } + } - internal QueueServiceClient GetQueueServiceClient() - { - return _serviceClient; - } + internal QueueServiceClient GetQueueServiceClient() + { + return _serviceClient; } } diff --git a/src/Savvyio.Extensions.QueueStorage/AzureQueueOptions.cs b/src/Savvyio.Extensions.QueueStorage/AzureQueueOptions.cs index c84027e9..2d8c548b 100644 --- a/src/Savvyio.Extensions.QueueStorage/AzureQueueOptions.cs +++ b/src/Savvyio.Extensions.QueueStorage/AzureQueueOptions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Azure; using Azure.Core; using Azure.Identity; @@ -7,192 +7,191 @@ using Cuemon; using Cuemon.Configuration; -namespace Savvyio.Extensions.QueueStorage +namespace Savvyio.Extensions.QueueStorage; + +/// +/// Configuration options for . +/// +public class AzureQueueOptions : IValidatableParameterObject { - /// - /// Configuration options for . - /// - public class AzureQueueOptions : IValidatableParameterObject + /// + /// The maximum number of messages that Azure Queue Storage supports when retrieving. + /// + public const int MaxNumberOfMessages = 32; + + /// + /// The maximum visibility timeout for messages in Azure Queue Storage. + /// + public static readonly TimeSpan MaxVisibilityTimeout = TimeSpan.FromDays(7); + + private Action _clientCallback; + private StorageSharedKeyCredential _storageKeyCredential; + private AzureSasCredential _sasCredential; + private TokenCredential _credential; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// new DefaultAzureCredential() + /// + /// + /// + /// null + /// + /// + /// + /// new QueueClientOptions() + /// + /// + /// + /// new AzureQueueReceiveOptions() + /// + /// + /// + /// new AzureQueueSendOptions() + /// + /// + /// + /// null + /// + /// + /// + /// null + /// + /// + /// + /// null + /// + /// + /// + /// null + /// + /// + /// + public AzureQueueOptions() { - /// - /// The maximum number of messages that Azure Queue Storage supports when retrieving. - /// - public const int MaxNumberOfMessages = 32; - - /// - /// The maximum visibility timeout for messages in Azure Queue Storage. - /// - public static readonly TimeSpan MaxVisibilityTimeout = TimeSpan.FromDays(7); - - private Action _clientCallback; - private StorageSharedKeyCredential _storageKeyCredential; - private AzureSasCredential _sasCredential; - private TokenCredential _credential; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// new DefaultAzureCredential() - /// - /// - /// - /// null - /// - /// - /// - /// new QueueClientOptions() - /// - /// - /// - /// new AzureQueueReceiveOptions() - /// - /// - /// - /// new AzureQueueSendOptions() - /// - /// - /// - /// null - /// - /// - /// - /// null - /// - /// - /// - /// null - /// - /// - /// - /// null - /// - /// - /// - public AzureQueueOptions() - { - Credential = new DefaultAzureCredential(); - Settings = new QueueClientOptions(); - ReceiveContext = new AzureQueueReceiveOptions(); - SendContext = new AzureQueueSendOptions(); - } + Credential = new DefaultAzureCredential(); + Settings = new QueueClientOptions(); + ReceiveContext = new AzureQueueReceiveOptions(); + SendContext = new AzureQueueSendOptions(); + } - /// - /// Gets the options for receiving messages from the queue. - /// - public AzureQueueReceiveOptions ReceiveContext { get; } - - /// - /// Gets the options for sending messages to the queue. - /// - public AzureQueueSendOptions SendContext { get; } - - /// - /// Gets or sets the name of the storage account. - /// - public string StorageAccountName { get; set; } - - /// - /// Gets or sets the name of the queue. - /// - public string QueueName { get; set; } - - /// - /// Gets or sets the credential used to authenticate with Azure. - /// - /// Setting this property will nullify and as they are mutually exclusive. - public TokenCredential Credential + /// + /// Gets the options for receiving messages from the queue. + /// + public AzureQueueReceiveOptions ReceiveContext { get; } + + /// + /// Gets the options for sending messages to the queue. + /// + public AzureQueueSendOptions SendContext { get; } + + /// + /// Gets or sets the name of the storage account. + /// + public string StorageAccountName { get; set; } + + /// + /// Gets or sets the name of the queue. + /// + public string QueueName { get; set; } + + /// + /// Gets or sets the credential used to authenticate with Azure. + /// + /// Setting this property will nullify and as they are mutually exclusive. + public TokenCredential Credential + { + get => _credential; + set { - get => _credential; - set - { - _credential = value; - _sasCredential = null; - _storageKeyCredential = null; - } + _credential = value; + _sasCredential = null; + _storageKeyCredential = null; } + } - /// - /// Gets or sets the SAS credential used to authenticate with Azure. - /// - /// Setting this property will nullify and as they are mutually exclusive. - public AzureSasCredential SasCredential + /// + /// Gets or sets the SAS credential used to authenticate with Azure. + /// + /// Setting this property will nullify and as they are mutually exclusive. + public AzureSasCredential SasCredential + { + get => _sasCredential; + set { - get => _sasCredential; - set - { - _sasCredential = value; - _credential = null; - _storageKeyCredential = null; - } + _sasCredential = value; + _credential = null; + _storageKeyCredential = null; } + } - /// - /// Gets or sets the storage key credential used to authenticate with Azure. - /// - /// Setting this property will nullify and as they are mutually exclusive. - public StorageSharedKeyCredential StorageKeyCredential + /// + /// Gets or sets the storage key credential used to authenticate with Azure. + /// + /// Setting this property will nullify and as they are mutually exclusive. + public StorageSharedKeyCredential StorageKeyCredential + { + get => _storageKeyCredential; + set { - get => _storageKeyCredential; - set - { - _storageKeyCredential = value; - _credential = null; - _sasCredential = null; - } + _storageKeyCredential = value; + _credential = null; + _sasCredential = null; } + } - /// - /// Gets or sets the connection string used to connect to the Azure storage account. - /// - public string ConnectionString { get; set; } - - /// - /// Gets the settings for the >. - /// - public QueueClientOptions Settings { get; } - - /// - /// Provides a way to post-configure the client after it has been created. - /// - /// The delegate to post-configure the client. - /// A reference to this instance. - /// This was added to support invoking methods of interest: https://learn.microsoft.com/en-us/dotnet/api/azure.storage.queues.queueclient?view=azure-dotnet#methods - public AzureQueueOptions PostConfigureClient(Action factory) - { - _clientCallback = factory; - return this; - } + /// + /// Gets or sets the connection string used to connect to the Azure storage account. + /// + public string ConnectionString { get; set; } + + /// + /// Gets the settings for the >. + /// + public QueueClientOptions Settings { get; } + + /// + /// Provides a way to post-configure the client after it has been created. + /// + /// The delegate to post-configure the client. + /// A reference to this instance. + /// This was added to support invoking methods of interest: https://learn.microsoft.com/en-us/dotnet/api/azure.storage.queues.queueclient?view=azure-dotnet#methods + public AzureQueueOptions PostConfigureClient(Action factory) + { + _clientCallback = factory; + return this; + } - internal void SetConfiguredClient(QueueClient client) - { - _clientCallback?.Invoke(client); - } + internal void SetConfiguredClient(QueueClient client) + { + _clientCallback?.Invoke(client); + } - /// - /// Validates the options to ensure they are in a valid state. - /// - /// - /// cannot be null, empty, or consists only of white-space characters when was specified -or- - /// and and cannot be null -or- - /// and cannot be null, empty, or consists only of white-space characters. - /// - public void ValidateOptions() - { - Validator.ThrowIfInvalidState(!string.IsNullOrWhiteSpace(ConnectionString) && string.IsNullOrWhiteSpace(QueueName), $"{nameof(QueueName)} cannot be null, empty, or consists only of white-space when a {nameof(ConnectionString)} has been specified."); + /// + /// Validates the options to ensure they are in a valid state. + /// + /// + /// cannot be null, empty, or consists only of white-space characters when was specified -or- + /// and and cannot be null -or- + /// and cannot be null, empty, or consists only of white-space characters. + /// + public void ValidateOptions() + { + Validator.ThrowIfInvalidState(!string.IsNullOrWhiteSpace(ConnectionString) && string.IsNullOrWhiteSpace(QueueName), $"{nameof(QueueName)} cannot be null, empty, or consists only of white-space when a {nameof(ConnectionString)} has been specified."); - Validator.ThrowIfInvalidState(Credential == null && SasCredential == null && StorageKeyCredential == null, "A credential type has to be specified for Azure authentication."); + Validator.ThrowIfInvalidState(Credential == null && SasCredential == null && StorageKeyCredential == null, "A credential type has to be specified for Azure authentication."); - Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(StorageAccountName) && string.IsNullOrWhiteSpace(QueueName), $"{nameof(StorageAccountName)} and {nameof(QueueName)} cannot be null, empty, or consists only of white-space characters."); + Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(StorageAccountName) && string.IsNullOrWhiteSpace(QueueName), $"{nameof(StorageAccountName)} and {nameof(QueueName)} cannot be null, empty, or consists only of white-space characters."); - } } } diff --git a/src/Savvyio.Extensions.QueueStorage/AzureQueueReceiveOptions.cs b/src/Savvyio.Extensions.QueueStorage/AzureQueueReceiveOptions.cs index 1baa0610..0b92d733 100644 --- a/src/Savvyio.Extensions.QueueStorage/AzureQueueReceiveOptions.cs +++ b/src/Savvyio.Extensions.QueueStorage/AzureQueueReceiveOptions.cs @@ -1,54 +1,53 @@ -using System; +using System; using Cuemon.Configuration; -namespace Savvyio.Extensions.QueueStorage +namespace Savvyio.Extensions.QueueStorage; + +/// +/// Configuration options that is related to receiving messages from an Azure Storage Queue. +/// +public class AzureQueueReceiveOptions : IParameterObject { + private int _numberOfMessagesToTakePerRequest; + private TimeSpan _visibilityTimeout; + /// - /// Configuration options that is related to receiving messages from an Azure Storage Queue. + /// Initializes a new instance of the class. /// - public class AzureQueueReceiveOptions : IParameterObject + internal AzureQueueReceiveOptions() { - private int _numberOfMessagesToTakePerRequest; - private TimeSpan _visibilityTimeout; - - /// - /// Initializes a new instance of the class. - /// - internal AzureQueueReceiveOptions() - { - NumberOfMessagesToTakePerRequest = 10; - VisibilityTimeout = TimeSpan.FromSeconds(30); - } + NumberOfMessagesToTakePerRequest = 10; + VisibilityTimeout = TimeSpan.FromSeconds(30); + } - /// - /// Gets or sets the number of messages to return per request. Default is 10. - /// - /// The number of messages to return per request. - /// Max. allowed value is limited to . - public int NumberOfMessagesToTakePerRequest - { - get => _numberOfMessagesToTakePerRequest; - set => _numberOfMessagesToTakePerRequest = Math.Clamp(value, 1, AzureQueueOptions.MaxNumberOfMessages); - } + /// + /// Gets or sets the number of messages to return per request. Default is 10. + /// + /// The number of messages to return per request. + /// Max. allowed value is limited to . + public int NumberOfMessagesToTakePerRequest + { + get => _numberOfMessagesToTakePerRequest; + set => _numberOfMessagesToTakePerRequest = Math.Clamp(value, 1, AzureQueueOptions.MaxNumberOfMessages); + } - /// - /// Gets or sets the visibility timeout for the messages. Default is 30 seconds. - /// - /// The visibility timeout for the messages. - /// - /// The minimum allowed value is 1 second, and the maximum allowed value is limited to . - ///
- /// Reference: https://learn.microsoft.com/en-us/rest/api/storageservices/get-messages#uri-parameters - ///
- public TimeSpan VisibilityTimeout + /// + /// Gets or sets the visibility timeout for the messages. Default is 30 seconds. + /// + /// The visibility timeout for the messages. + /// + /// The minimum allowed value is 1 second, and the maximum allowed value is limited to . + ///
+ /// Reference: https://learn.microsoft.com/en-us/rest/api/storageservices/get-messages#uri-parameters + ///
+ public TimeSpan VisibilityTimeout + { + get => _visibilityTimeout; + set { - get => _visibilityTimeout; - set - { - if (value < TimeSpan.FromSeconds(1)) { value = TimeSpan.FromSeconds(1); } - if (value > AzureQueueOptions.MaxVisibilityTimeout) { value = AzureQueueOptions.MaxVisibilityTimeout; } - _visibilityTimeout = value; - } + if (value < TimeSpan.FromSeconds(1)) { value = TimeSpan.FromSeconds(1); } + if (value > AzureQueueOptions.MaxVisibilityTimeout) { value = AzureQueueOptions.MaxVisibilityTimeout; } + _visibilityTimeout = value; } } } diff --git a/src/Savvyio.Extensions.QueueStorage/AzureQueueSendOptions.cs b/src/Savvyio.Extensions.QueueStorage/AzureQueueSendOptions.cs index 7468bc87..00f91454 100644 --- a/src/Savvyio.Extensions.QueueStorage/AzureQueueSendOptions.cs +++ b/src/Savvyio.Extensions.QueueStorage/AzureQueueSendOptions.cs @@ -1,48 +1,47 @@ -using System; +using System; using Cuemon.Configuration; -namespace Savvyio.Extensions.QueueStorage +namespace Savvyio.Extensions.QueueStorage; + +/// +/// Configuration options that is related to sending messages from an Azure Storage Queue. +/// +public class AzureQueueSendOptions : IParameterObject { + private TimeSpan _visibilityTimeout; + /// - /// Configuration options that is related to sending messages from an Azure Storage Queue. + /// Initializes a new instance of the class. /// - public class AzureQueueSendOptions : IParameterObject + internal AzureQueueSendOptions() { - private TimeSpan _visibilityTimeout; - - /// - /// Initializes a new instance of the class. - /// - internal AzureQueueSendOptions() - { - VisibilityTimeout = TimeSpan.Zero; - TimeToLive = TimeSpan.FromDays(7); - } + VisibilityTimeout = TimeSpan.Zero; + TimeToLive = TimeSpan.FromDays(7); + } - /// - /// Gets or sets the time-to-live for the messages. Default is 7 days. - /// - /// The time-to-live for the messages. - public TimeSpan TimeToLive { get; set; } + /// + /// Gets or sets the time-to-live for the messages. Default is 7 days. + /// + /// The time-to-live for the messages. + public TimeSpan TimeToLive { get; set; } - /// - /// Gets or sets the visibility timeout for the messages. Default is . - /// - /// The visibility timeout for the messages. - /// - /// The minimum allowed value is , and the maximum allowed value is limited to . - ///
- /// Reference: https://learn.microsoft.com/en-us/rest/api/storageservices/put-message#uri-parameters - ///
- public TimeSpan VisibilityTimeout + /// + /// Gets or sets the visibility timeout for the messages. Default is . + /// + /// The visibility timeout for the messages. + /// + /// The minimum allowed value is , and the maximum allowed value is limited to . + ///
+ /// Reference: https://learn.microsoft.com/en-us/rest/api/storageservices/put-message#uri-parameters + ///
+ public TimeSpan VisibilityTimeout + { + get => _visibilityTimeout; + set { - get => _visibilityTimeout; - set - { - if (value < TimeSpan.Zero) { value = TimeSpan.Zero; } - if (value > AzureQueueOptions.MaxVisibilityTimeout) { value = AzureQueueOptions.MaxVisibilityTimeout; } - _visibilityTimeout = value; - } + if (value < TimeSpan.Zero) { value = TimeSpan.Zero; } + if (value > AzureQueueOptions.MaxVisibilityTimeout) { value = AzureQueueOptions.MaxVisibilityTimeout; } + _visibilityTimeout = value; } } } diff --git a/src/Savvyio.Extensions.QueueStorage/Commands/AzureCommandQueue.cs b/src/Savvyio.Extensions.QueueStorage/Commands/AzureCommandQueue.cs index 885570d9..e227b994 100644 --- a/src/Savvyio.Extensions.QueueStorage/Commands/AzureCommandQueue.cs +++ b/src/Savvyio.Extensions.QueueStorage/Commands/AzureCommandQueue.cs @@ -1,4 +1,4 @@ -using Azure.Storage.Queues; +using Azure.Storage.Queues; using Cuemon.Threading; using Savvyio.Commands; using Savvyio.Diagnostics; @@ -7,60 +7,59 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace Savvyio.Extensions.QueueStorage.Commands +namespace Savvyio.Extensions.QueueStorage.Commands; + +/// +/// Provides an Azure Storage Queue implementation of the . +/// +public class AzureCommandQueue : AzureQueue, IPointToPointChannel, IHealthCheckProvider { /// - /// Provides an Azure Storage Queue implementation of the . + /// Initializes a new instance of the class. /// - public class AzureCommandQueue : AzureQueue, IPointToPointChannel, IHealthCheckProvider + /// The marshaller used for serializing and deserializing messages. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public AzureCommandQueue(IMarshaller marshaller, AzureQueueOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public AzureCommandQueue(IMarshaller marshaller, AzureQueueOptions options) : base(marshaller, options) - { - } + } - /// - /// Sends the specified messages to the Azure Storage Queue. - /// - /// The messages to send. - /// The which may be configured. - /// A task that represents the asynchronous operation. - /// - /// cannot be null. - /// - public Task SendAsync(IEnumerable> messages, Action setup = null) - { - return SendMessageAsync(messages, setup); - } + /// + /// Sends the specified messages to the Azure Storage Queue. + /// + /// The messages to send. + /// The which may be configured. + /// A task that represents the asynchronous operation. + /// + /// cannot be null. + /// + public Task SendAsync(IEnumerable> messages, Action setup = null) + { + return SendMessageAsync(messages, setup); + } - /// - /// Receives messages from the Azure Storage Queue. - /// - /// The which may be configured. - /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . - public IAsyncEnumerable> ReceiveAsync(Action setup = null) - { - return ReceiveMessagesAsync(setup); - } + /// + /// Receives messages from the Azure Storage Queue. + /// + /// The which may be configured. + /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . + public IAsyncEnumerable> ReceiveAsync(Action setup = null) + { + return ReceiveMessagesAsync(setup); + } - /// - /// Gets the instance used for probing the health status of the Azure Storage Queue service. - /// - /// The instance representing the client connection to the Azure Storage Queue service. - public QueueServiceClient GetHealthCheckTarget() - { - return GetQueueServiceClient(); // fetch from base class - } + /// + /// Gets the instance used for probing the health status of the Azure Storage Queue service. + /// + /// The instance representing the client connection to the Azure Storage Queue service. + public QueueServiceClient GetHealthCheckTarget() + { + return GetQueueServiceClient(); // fetch from base class } } diff --git a/src/Savvyio.Extensions.QueueStorage/EventDriven/AzureEventBus.cs b/src/Savvyio.Extensions.QueueStorage/EventDriven/AzureEventBus.cs index 7c4a5aea..92e6dd3d 100644 --- a/src/Savvyio.Extensions.QueueStorage/EventDriven/AzureEventBus.cs +++ b/src/Savvyio.Extensions.QueueStorage/EventDriven/AzureEventBus.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; @@ -15,148 +15,147 @@ using Savvyio.Messaging; using Savvyio.Messaging.Cryptography; -namespace Savvyio.Extensions.QueueStorage.EventDriven +namespace Savvyio.Extensions.QueueStorage.EventDriven; + +/// +/// Provides a combined Azure Event Grid/Azure Storage Queue implementation of the . +/// +public class AzureEventBus : AzureQueue, IPublishSubscribeChannel, IHealthCheckProvider { + private readonly EventGridPublisherClient _client; + private readonly IMarshaller _marshaller; + private readonly AzureEventBusOptions _options; + /// - /// Provides a combined Azure Event Grid/Azure Storage Queue implementation of the . + /// The attribute name for the CloudEvent type extension. /// - public class AzureEventBus : AzureQueue, IPublishSubscribeChannel, IHealthCheckProvider - { - private readonly EventGridPublisherClient _client; - private readonly IMarshaller _marshaller; - private readonly AzureEventBusOptions _options; - - /// - /// The attribute name for the CloudEvent type extension. - /// - public const string CloudEventTypeExtensionAttribute = "typeext"; - - /// - /// Initializes a new instance of the class. - /// - /// The to use for serializing and deserializing implementations to messages. - /// The for configuring . - /// The used to configure this instance. - public AzureEventBus(IMarshaller marshaller, AzureQueueOptions azureQueueOptions, AzureEventBusOptions azureEventBusOptions) : base(marshaller, azureQueueOptions, receiveMessageFormatter: FormatCloudEventQueueMessage) - { - Validator.ThrowIfInvalidOptions(azureEventBusOptions); + public const string CloudEventTypeExtensionAttribute = "typeext"; - _marshaller = marshaller; + /// + /// Initializes a new instance of the class. + /// + /// The to use for serializing and deserializing implementations to messages. + /// The for configuring . + /// The used to configure this instance. + public AzureEventBus(IMarshaller marshaller, AzureQueueOptions azureQueueOptions, AzureEventBusOptions azureEventBusOptions) : base(marshaller, azureQueueOptions, receiveMessageFormatter: FormatCloudEventQueueMessage) + { + Validator.ThrowIfInvalidOptions(azureEventBusOptions); - if (azureEventBusOptions.Credential != null) - { - _client = new EventGridPublisherClient(azureEventBusOptions.TopicEndpoint, azureEventBusOptions.Credential, azureEventBusOptions.Settings); - } - else if (azureEventBusOptions.SasCredential != null) - { - _client = new EventGridPublisherClient(azureEventBusOptions.TopicEndpoint, azureEventBusOptions.SasCredential, azureEventBusOptions.Settings); - } - else if (azureEventBusOptions.KeyCredential != null) - { - _client = new EventGridPublisherClient(azureEventBusOptions.TopicEndpoint, azureEventBusOptions.KeyCredential, azureEventBusOptions.Settings); - } + _marshaller = marshaller; - _options = azureEventBusOptions; + if (azureEventBusOptions.Credential != null) + { + _client = new EventGridPublisherClient(azureEventBusOptions.TopicEndpoint, azureEventBusOptions.Credential, azureEventBusOptions.Settings); } - - /// - /// Initializes a new instance of the class with testable Azure clients. - /// - /// The to use for serializing and deserializing implementations to messages. - /// The for configuring . - /// The used to configure this instance. - /// The queue service client to use. - /// The queue client to use. - /// The event grid publisher client to use. - protected AzureEventBus(IMarshaller marshaller, AzureQueueOptions azureQueueOptions, AzureEventBusOptions azureEventBusOptions, QueueServiceClient queueServiceClient, QueueClient queueClient, EventGridPublisherClient eventGridClient) : base(marshaller, azureQueueOptions, queueServiceClient, queueClient, receiveMessageFormatter: FormatCloudEventQueueMessage) + else if (azureEventBusOptions.SasCredential != null) { - Validator.ThrowIfInvalidOptions(azureEventBusOptions); - - _marshaller = marshaller; - _client = eventGridClient; - _options = azureEventBusOptions; + _client = new EventGridPublisherClient(azureEventBusOptions.TopicEndpoint, azureEventBusOptions.SasCredential, azureEventBusOptions.Settings); } - - /// - /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The message to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - /// - /// cannot be null. - /// - public async Task PublishAsync(IMessage message, Action setup = null) + else if (azureEventBusOptions.KeyCredential != null) { - Validator.ThrowIfNull(message); - Validator.ThrowIfInvalidConfigurator(setup, out var options); + _client = new EventGridPublisherClient(azureEventBusOptions.TopicEndpoint, azureEventBusOptions.KeyCredential, azureEventBusOptions.Settings); + } - var ce = message.ToCloudEvent(); + _options = azureEventBusOptions; + } - if (message is ISignedMessage signedMessage) { ce.Add(nameof(ISignedMessage.Signature), signedMessage.Signature); } + /// + /// Initializes a new instance of the class with testable Azure clients. + /// + /// The to use for serializing and deserializing implementations to messages. + /// The for configuring . + /// The used to configure this instance. + /// The queue service client to use. + /// The queue client to use. + /// The event grid publisher client to use. + protected AzureEventBus(IMarshaller marshaller, AzureQueueOptions azureQueueOptions, AzureEventBusOptions azureEventBusOptions, QueueServiceClient queueServiceClient, QueueClient queueClient, EventGridPublisherClient eventGridClient) : base(marshaller, azureQueueOptions, queueServiceClient, queueClient, receiveMessageFormatter: FormatCloudEventQueueMessage) + { + Validator.ThrowIfInvalidOptions(azureEventBusOptions); - ce.Add(CloudEventTypeExtensionAttribute, message.GetType().ToFullNameIncludingAssemblyName()); + _marshaller = marshaller; + _client = eventGridClient; + _options = azureEventBusOptions; + } - var json = _marshaller.Serialize(ce).ToEncodedString(); - await _client.SendEventAsync(Azure.Messaging.CloudEvent.Parse(new BinaryData(json)), options.CancellationToken).ConfigureAwait(false); - } + /// + /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. + /// + /// The message to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + /// + /// cannot be null. + /// + public async Task PublishAsync(IMessage message, Action setup = null) + { + Validator.ThrowIfNull(message); + Validator.ThrowIfInvalidConfigurator(setup, out var options); - /// - /// Subscribe to one or more message(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The function delegate that will handle received messages. - /// The which may be configured. - /// A that represents the asynchronous operation. - /// - /// cannot be null. - /// - public async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) - { - Validator.ThrowIfNull(asyncHandler); - Validator.ThrowIfInvalidConfigurator(setup, out var options); + var ce = message.ToCloudEvent(); - await Condition.FlipFlopAsync(options.ThrowIfCancellationWasRequested, () => InvokeHandlerAsync(asyncHandler, options.CancellationToken), async () => - { - try - { - await InvokeHandlerAsync(asyncHandler, options.CancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) when (options.CancellationToken.IsCancellationRequested) - { - // ThrowIfCancellationWasRequested was set to false (default) - } - }).ConfigureAwait(false); - } + if (message is ISignedMessage signedMessage) { ce.Add(nameof(ISignedMessage.Signature), signedMessage.Signature); } + + ce.Add(CloudEventTypeExtensionAttribute, message.GetType().ToFullNameIncludingAssemblyName()); + + var json = _marshaller.Serialize(ce).ToEncodedString(); + await _client.SendEventAsync(Azure.Messaging.CloudEvent.Parse(new BinaryData(json)), options.CancellationToken).ConfigureAwait(false); + } + + /// + /// Subscribe to one or more message(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. + /// + /// The function delegate that will handle received messages. + /// The which may be configured. + /// A that represents the asynchronous operation. + /// + /// cannot be null. + /// + public async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) + { + Validator.ThrowIfNull(asyncHandler); + Validator.ThrowIfInvalidConfigurator(setup, out var options); - private async Task InvokeHandlerAsync(Func, CancellationToken, Task> asyncHandler, CancellationToken cancellationToken) + await Condition.FlipFlopAsync(options.ThrowIfCancellationWasRequested, () => InvokeHandlerAsync(asyncHandler, options.CancellationToken), async () => { - var hasMessages = true; - while (hasMessages) + try { - hasMessages = false; - await foreach (var message in ReceiveMessagesAsync(o => o.CancellationToken = cancellationToken).ConfigureAwait(false)) - { - hasMessages = true; - await asyncHandler.Invoke(message, cancellationToken).ConfigureAwait(false); - } + await InvokeHandlerAsync(asyncHandler, options.CancellationToken).ConfigureAwait(false); } - } - - /// - /// Gets the representing the health check endpoint for the configured Azure Event Grid topic. - /// - /// A instance pointing to the health check endpoint of the Azure Event Grid topic. - public Uri GetHealthCheckTarget() - { - return new Uri(_options.TopicEndpoint, "/api/health"); - } + catch (OperationCanceledException) when (options.CancellationToken.IsCancellationRequested) + { + // ThrowIfCancellationWasRequested was set to false (default) + } + }).ConfigureAwait(false); + } - private static IMessage FormatCloudEventQueueMessage(Azure.Storage.Queues.Models.QueueMessage rawMessage, IMarshaller serializer) + private async Task InvokeHandlerAsync(Func, CancellationToken, Task> asyncHandler, CancellationToken cancellationToken) + { + var hasMessages = true; + while (hasMessages) { - var json = rawMessage.MessageText; - var jsonStream = json.FromBase64().ToStream(); - var type = JsonNode.Parse(jsonStream.ToEncodedString(o => o.LeaveOpen = true))!.Root[CloudEventTypeExtensionAttribute]!.GetValue(); - return serializer.Deserialize(jsonStream, Type.GetType(type)!) as IMessage; + hasMessages = false; + await foreach (var message in ReceiveMessagesAsync(o => o.CancellationToken = cancellationToken).ConfigureAwait(false)) + { + hasMessages = true; + await asyncHandler.Invoke(message, cancellationToken).ConfigureAwait(false); + } } } + + /// + /// Gets the representing the health check endpoint for the configured Azure Event Grid topic. + /// + /// A instance pointing to the health check endpoint of the Azure Event Grid topic. + public Uri GetHealthCheckTarget() + { + return new Uri(_options.TopicEndpoint, "/api/health"); + } + + private static IMessage FormatCloudEventQueueMessage(Azure.Storage.Queues.Models.QueueMessage rawMessage, IMarshaller serializer) + { + var json = rawMessage.MessageText; + var jsonStream = json.FromBase64().ToStream(); + var type = JsonNode.Parse(jsonStream.ToEncodedString(o => o.LeaveOpen = true))!.Root[CloudEventTypeExtensionAttribute]!.GetValue(); + return serializer.Deserialize(jsonStream, Type.GetType(type)!) as IMessage; + } } diff --git a/src/Savvyio.Extensions.QueueStorage/EventDriven/AzureEventBusOptions.cs b/src/Savvyio.Extensions.QueueStorage/EventDriven/AzureEventBusOptions.cs index 6e891c9b..5f364ca8 100644 --- a/src/Savvyio.Extensions.QueueStorage/EventDriven/AzureEventBusOptions.cs +++ b/src/Savvyio.Extensions.QueueStorage/EventDriven/AzureEventBusOptions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Azure; using Azure.Core; using Azure.Identity; @@ -6,120 +6,119 @@ using Cuemon; using Cuemon.Configuration; -namespace Savvyio.Extensions.QueueStorage.EventDriven +namespace Savvyio.Extensions.QueueStorage.EventDriven; + +/// +/// Configuration options for . +/// +public class AzureEventBusOptions : IValidatableParameterObject { - /// - /// Configuration options for . - /// - public class AzureEventBusOptions : IValidatableParameterObject - { - private TokenCredential _credential; - private AzureKeyCredential _keyCredential; - private AzureSasCredential _sasCredential; + private TokenCredential _credential; + private AzureKeyCredential _keyCredential; + private AzureSasCredential _sasCredential; - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// new DefaultAzureCredential() - /// - /// - /// - /// new QueueClientOptions() - /// - /// - /// - /// null - /// - /// - /// - /// null - /// - /// - /// - public AzureEventBusOptions() - { - Credential = new DefaultAzureCredential(); - Settings = new EventGridPublisherClientOptions(); - } + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// new DefaultAzureCredential() + /// + /// + /// + /// new QueueClientOptions() + /// + /// + /// + /// null + /// + /// + /// + /// null + /// + /// + /// + public AzureEventBusOptions() + { + Credential = new DefaultAzureCredential(); + Settings = new EventGridPublisherClientOptions(); + } - /// - /// Gets or sets the topic endpoint URI. - /// - /// The topic endpoint URI. - public Uri TopicEndpoint { get; set; } + /// + /// Gets or sets the topic endpoint URI. + /// + /// The topic endpoint URI. + public Uri TopicEndpoint { get; set; } - /// - /// Gets or sets the token credential for Azure authentication. - /// - /// The token credential. - /// Setting this property will nullify and as they are mutually exclusive. - public TokenCredential Credential + /// + /// Gets or sets the token credential for Azure authentication. + /// + /// The token credential. + /// Setting this property will nullify and as they are mutually exclusive. + public TokenCredential Credential + { + get => _credential; + set { - get => _credential; - set - { - _credential = value; - _sasCredential = null; - _keyCredential = null; - } + _credential = value; + _sasCredential = null; + _keyCredential = null; } + } - /// - /// Gets or sets the key credential for Azure authentication. - /// - /// The key credential. - /// Setting this property will nullify and as they are mutually exclusive. - public AzureKeyCredential KeyCredential + /// + /// Gets or sets the key credential for Azure authentication. + /// + /// The key credential. + /// Setting this property will nullify and as they are mutually exclusive. + public AzureKeyCredential KeyCredential + { + get => _keyCredential; + set { - get => _keyCredential; - set - { - _keyCredential = value; - _credential = null; - _sasCredential = null; - } + _keyCredential = value; + _credential = null; + _sasCredential = null; } + } - /// - /// Gets or sets the SAS credential for Azure authentication. - /// - /// The SAS credential. - /// Setting this property will nullify and as they are mutually exclusive. - public AzureSasCredential SasCredential + /// + /// Gets or sets the SAS credential for Azure authentication. + /// + /// The SAS credential. + /// Setting this property will nullify and as they are mutually exclusive. + public AzureSasCredential SasCredential + { + get => _sasCredential; + set { - get => _sasCredential; - set - { - _sasCredential = value; - _credential = null; - _keyCredential = null; - } + _sasCredential = value; + _credential = null; + _keyCredential = null; } + } - /// - /// Gets the settings for the Event Grid publisher client. - /// - /// The Event Grid publisher client options. - public EventGridPublisherClientOptions Settings { get; } + /// + /// Gets the settings for the Event Grid publisher client. + /// + /// The Event Grid publisher client options. + public EventGridPublisherClientOptions Settings { get; } - /// - /// Validates the options to ensure they are in a valid state. - /// - /// Thrown when the options are in an invalid state. - public void ValidateOptions() - { - Validator.ThrowIfInvalidState(TopicEndpoint == null, $"A {nameof(TopicEndpoint)} is required."); + /// + /// Validates the options to ensure they are in a valid state. + /// + /// Thrown when the options are in an invalid state. + public void ValidateOptions() + { + Validator.ThrowIfInvalidState(TopicEndpoint == null, $"A {nameof(TopicEndpoint)} is required."); - Validator.ThrowIfInvalidState(Credential == null && SasCredential == null && KeyCredential == null, "A credential type has to be specified for Azure authentication."); - } + Validator.ThrowIfInvalidState(Credential == null && SasCredential == null && KeyCredential == null, "A credential type has to be specified for Azure authentication."); } } diff --git a/src/Savvyio.Extensions.QueueStorage/GlobalSuppressions.cs b/src/Savvyio.Extensions.QueueStorage/GlobalSuppressions.cs index 6431c1a6..59067b15 100644 --- a/src/Savvyio.Extensions.QueueStorage/GlobalSuppressions.cs +++ b/src/Savvyio.Extensions.QueueStorage/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. diff --git a/src/Savvyio.Extensions.QueueStorage/Savvyio.Extensions.QueueStorage.csproj b/src/Savvyio.Extensions.QueueStorage/Savvyio.Extensions.QueueStorage.csproj index 714680aa..f8a9de89 100644 --- a/src/Savvyio.Extensions.QueueStorage/Savvyio.Extensions.QueueStorage.csproj +++ b/src/Savvyio.Extensions.QueueStorage/Savvyio.Extensions.QueueStorage.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O core assemblies with support for Azure Queue Storage paired with Azure Event Grid. diff --git a/src/Savvyio.Extensions.RabbitMQ/Commands/RabbitMqCommandQueue.cs b/src/Savvyio.Extensions.RabbitMQ/Commands/RabbitMqCommandQueue.cs index 806351ea..a70579fd 100644 --- a/src/Savvyio.Extensions.RabbitMQ/Commands/RabbitMqCommandQueue.cs +++ b/src/Savvyio.Extensions.RabbitMQ/Commands/RabbitMqCommandQueue.cs @@ -1,4 +1,4 @@ -using Cuemon; +using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.IO; using Cuemon.Extensions.Reflection; @@ -13,112 +13,111 @@ using System.Threading.Channels; using System.Threading.Tasks; -namespace Savvyio.Extensions.RabbitMQ.Commands +namespace Savvyio.Extensions.RabbitMQ.Commands; + +/// +/// Provides a default implementation of the class for messages holding an implementation. +/// +/// +/// +public class RabbitMqCommandQueue : RabbitMqMessage, IPointToPointChannel { + private readonly RabbitMqCommandQueueOptions _options; + /// - /// Provides a default implementation of the class for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - /// - public class RabbitMqCommandQueue : RabbitMqMessage, IPointToPointChannel + /// The marshaller used for serializing and deserializing messages. + /// The options used to configure the RabbitMQ command queue. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public RabbitMqCommandQueue(IMarshaller marshaller, RabbitMqCommandQueueOptions options) : base(marshaller, options) { - private readonly RabbitMqCommandQueueOptions _options; - - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The options used to configure the RabbitMQ command queue. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public RabbitMqCommandQueue(IMarshaller marshaller, RabbitMqCommandQueueOptions options) : base(marshaller, options) - { - Validator.ThrowIfInvalidOptions(options); - _options = options; - } + Validator.ThrowIfInvalidOptions(options); + _options = options; + } - /// - /// Sends the specified command messages asynchronously to the configured RabbitMQ queue. - /// - /// The messages to send. - /// The which may be configured. - /// A that represents the asynchronous operation. - public async Task SendAsync(IEnumerable> messages, Action setup = null) - { - Validator.ThrowIfInvalidConfigurator(setup, out var options); + /// + /// Sends the specified command messages asynchronously to the configured RabbitMQ queue. + /// + /// The messages to send. + /// The which may be configured. + /// A that represents the asynchronous operation. + public async Task SendAsync(IEnumerable> messages, Action setup = null) + { + Validator.ThrowIfInvalidConfigurator(setup, out var options); - await EnsureConnectivityAsync(options.CancellationToken).ConfigureAwait(false); + await EnsureConnectivityAsync(options.CancellationToken).ConfigureAwait(false); - await RabbitMqChannel.QueueDeclareAsync(_options.QueueName, _options.Durable, _options.Exclusive, _options.AutoDelete, cancellationToken: options.CancellationToken).ConfigureAwait(false); + await RabbitMqChannel.QueueDeclareAsync(_options.QueueName, _options.Durable, _options.Exclusive, _options.AutoDelete, cancellationToken: options.CancellationToken).ConfigureAwait(false); - foreach (var message in messages) + foreach (var message in messages) + { + await RabbitMqChannel.BasicPublishAsync("", _options.QueueName, true, basicProperties: new BasicProperties() { - await RabbitMqChannel.BasicPublishAsync("", _options.QueueName, true, basicProperties: new BasicProperties() + Persistent = _options.Persistent, + Headers = new Dictionary() { - Persistent = _options.Persistent, - Headers = new Dictionary() - { - { MessageType, message.GetType().ToFullNameIncludingAssemblyName() } - } - }, body: await Marshaller.Serialize(message).ToByteArrayAsync(o => o.CancellationToken = options.CancellationToken).ConfigureAwait(false), options.CancellationToken).ConfigureAwait(false); - } + { MessageType, message.GetType().ToFullNameIncludingAssemblyName() } + } + }, body: await Marshaller.Serialize(message).ToByteArrayAsync(o => o.CancellationToken = options.CancellationToken).ConfigureAwait(false), options.CancellationToken).ConfigureAwait(false); } + } - /// - /// Receives command messages asynchronously from the configured RabbitMQ queue. - /// - /// The which may be configured. - /// - /// An that yields instances as they are received. - /// - public async IAsyncEnumerable> ReceiveAsync(Action setup = null) - { - Validator.ThrowIfInvalidConfigurator(setup, out var options); + /// + /// Receives command messages asynchronously from the configured RabbitMQ queue. + /// + /// The which may be configured. + /// + /// An that yields instances as they are received. + /// + public async IAsyncEnumerable> ReceiveAsync(Action setup = null) + { + Validator.ThrowIfInvalidConfigurator(setup, out var options); - await EnsureConnectivityAsync(options.CancellationToken).ConfigureAwait(false); + await EnsureConnectivityAsync(options.CancellationToken).ConfigureAwait(false); - await RabbitMqChannel.QueueDeclareAsync(_options.QueueName, _options.Durable, _options.Exclusive, _options.AutoDelete, cancellationToken: options.CancellationToken).ConfigureAwait(false); + await RabbitMqChannel.QueueDeclareAsync(_options.QueueName, _options.Durable, _options.Exclusive, _options.AutoDelete, cancellationToken: options.CancellationToken).ConfigureAwait(false); - var buffer = Channel.CreateUnbounded>(); - var consumer = new AsyncEventingBasicConsumer(RabbitMqChannel); - consumer.ReceivedAsync += async (_, e) => - { - var messageType = Type.GetType((e.BasicProperties.Headers[MessageType] as byte[]).ToEncodedString()); - var deserialized = Marshaller.Deserialize(e.Body.ToArray().ToStream(), messageType) as IMessage; - deserialized!.Properties.Add(nameof(BasicDeliverEventArgs.DeliveryTag), e.DeliveryTag); - deserialized.Properties.Add(nameof(CancellationToken), options.CancellationToken); - deserialized.Properties.Add(nameof(QueueDeclareOk.QueueName), _options.QueueName); - await buffer.Writer.WriteAsync(deserialized, options.CancellationToken).ConfigureAwait(false); - }; + var buffer = Channel.CreateUnbounded>(); + var consumer = new AsyncEventingBasicConsumer(RabbitMqChannel); + consumer.ReceivedAsync += async (_, e) => + { + var messageType = Type.GetType((e.BasicProperties.Headers[MessageType] as byte[]).ToEncodedString()); + var deserialized = Marshaller.Deserialize(e.Body.ToArray().ToStream(), messageType) as IMessage; + deserialized!.Properties.Add(nameof(BasicDeliverEventArgs.DeliveryTag), e.DeliveryTag); + deserialized.Properties.Add(nameof(CancellationToken), options.CancellationToken); + deserialized.Properties.Add(nameof(QueueDeclareOk.QueueName), _options.QueueName); + await buffer.Writer.WriteAsync(deserialized, options.CancellationToken).ConfigureAwait(false); + }; - await RabbitMqChannel.BasicConsumeAsync(_options.QueueName, autoAck: false, consumer: consumer, cancellationToken: options.CancellationToken).ConfigureAwait(false); + await RabbitMqChannel.BasicConsumeAsync(_options.QueueName, autoAck: false, consumer: consumer, cancellationToken: options.CancellationToken).ConfigureAwait(false); - while (await buffer.Reader.WaitToReadAsync(options.CancellationToken).ConfigureAwait(false)) + while (await buffer.Reader.WaitToReadAsync(options.CancellationToken).ConfigureAwait(false)) + { + while (buffer.Reader.TryRead(out var message)) { - while (buffer.Reader.TryRead(out var message)) + message.Acknowledged += OnMessageAcknowledgedAsync; + if (_options.AutoAcknowledge) { - message.Acknowledged += OnMessageAcknowledgedAsync; - if (_options.AutoAcknowledge) - { - await message.AcknowledgeAsync().ConfigureAwait(false); - } - yield return message; + await message.AcknowledgeAsync().ConfigureAwait(false); } + yield return message; } } + } - private async Task OnMessageAcknowledgedAsync(object sender, AcknowledgedEventArgs e) - { - var ct = (CancellationToken)e.Properties[nameof(CancellationToken)]; - var queueName = e.Properties[nameof(QueueDeclareOk.QueueName)] as string; - await RabbitMqChannel.QueueDeclareAsync(queueName, _options.Durable, _options.Exclusive, _options.AutoDelete, cancellationToken: ct).ConfigureAwait(false); - await RabbitMqChannel.BasicAckAsync((ulong)e.Properties[nameof(BasicDeliverEventArgs.DeliveryTag)], false, ct).ConfigureAwait(false); - if (sender is IAcknowledgeable ack) { ack.Acknowledged -= OnMessageAcknowledgedAsync; } - } + private async Task OnMessageAcknowledgedAsync(object sender, AcknowledgedEventArgs e) + { + var ct = (CancellationToken)e.Properties[nameof(CancellationToken)]; + var queueName = e.Properties[nameof(QueueDeclareOk.QueueName)] as string; + await RabbitMqChannel.QueueDeclareAsync(queueName, _options.Durable, _options.Exclusive, _options.AutoDelete, cancellationToken: ct).ConfigureAwait(false); + await RabbitMqChannel.BasicAckAsync((ulong)e.Properties[nameof(BasicDeliverEventArgs.DeliveryTag)], false, ct).ConfigureAwait(false); + if (sender is IAcknowledgeable ack) { ack.Acknowledged -= OnMessageAcknowledgedAsync; } } } diff --git a/src/Savvyio.Extensions.RabbitMQ/Commands/RabbitMqCommandQueueOptions.cs b/src/Savvyio.Extensions.RabbitMQ/Commands/RabbitMqCommandQueueOptions.cs index f9386fd6..ce6fde7a 100644 --- a/src/Savvyio.Extensions.RabbitMQ/Commands/RabbitMqCommandQueueOptions.cs +++ b/src/Savvyio.Extensions.RabbitMQ/Commands/RabbitMqCommandQueueOptions.cs @@ -1,105 +1,104 @@ -using System; +using System; using Cuemon; -namespace Savvyio.Extensions.RabbitMQ.Commands +namespace Savvyio.Extensions.RabbitMQ.Commands; + +/// +/// Configuration options for . +/// +/// +public class RabbitMqCommandQueueOptions : RabbitMqMessageOptions { /// - /// Configuration options for . + /// Initializes a new instance of the class with default values. /// - /// - public class RabbitMqCommandQueueOptions : RabbitMqMessageOptions + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// null + /// + /// + /// + /// false + /// + /// + /// + /// true + /// + /// + /// + /// false + /// + /// + /// + /// false + /// + /// + /// + public RabbitMqCommandQueueOptions() { - /// - /// Initializes a new instance of the class with default values. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// null - /// - /// - /// - /// false - /// - /// - /// - /// true - /// - /// - /// - /// false - /// - /// - /// - /// false - /// - /// - /// - public RabbitMqCommandQueueOptions() - { - Durable = true; - } + Durable = true; + } - /// - /// Gets or sets the name of the queue. - /// - /// - /// The name of the RabbitMQ queue to be used for command messages. - /// - public string QueueName { get; set; } + /// + /// Gets or sets the name of the queue. + /// + /// + /// The name of the RabbitMQ queue to be used for command messages. + /// + public string QueueName { get; set; } - /// - /// Gets or sets a value indicating whether messages should be automatically acknowledged. - /// - /// - /// true if messages are automatically acknowledged; otherwise, false. - /// - public bool AutoAcknowledge { get; set; } + /// + /// Gets or sets a value indicating whether messages should be automatically acknowledged. + /// + /// + /// true if messages are automatically acknowledged; otherwise, false. + /// + public bool AutoAcknowledge { get; set; } - /// - /// Gets or sets a value indicating whether the queue should be durable. - /// - /// true if the queue should survive broker restarts; otherwise, false. - /// - /// Durable queues remain declared after a RabbitMQ broker restart, but messages in non-persistent queues will be lost. - /// - public bool Durable { get; set; } + /// + /// Gets or sets a value indicating whether the queue should be durable. + /// + /// true if the queue should survive broker restarts; otherwise, false. + /// + /// Durable queues remain declared after a RabbitMQ broker restart, but messages in non-persistent queues will be lost. + /// + public bool Durable { get; set; } - /// - /// Gets or sets a value indicating whether the queue should be exclusive. - /// - /// true if the queue is used by only one connection and deleted when that connection closes; otherwise, false. - /// - /// Exclusive queues are only accessible by the connection that declares them and are deleted when that connection closes. - /// - public bool Exclusive { get; set; } + /// + /// Gets or sets a value indicating whether the queue should be exclusive. + /// + /// true if the queue is used by only one connection and deleted when that connection closes; otherwise, false. + /// + /// Exclusive queues are only accessible by the connection that declares them and are deleted when that connection closes. + /// + public bool Exclusive { get; set; } - /// - /// Gets or sets a value indicating whether the queue should be automatically deleted when no longer in use. - /// - /// true if the queue is automatically deleted when the last consumer unsubscribes; otherwise, false. - /// - /// Auto-delete queues are deleted when the last consumer unsubscribes. If there are no consumers, the queue is not deleted. - /// - public bool AutoDelete { get; set; } + /// + /// Gets or sets a value indicating whether the queue should be automatically deleted when no longer in use. + /// + /// true if the queue is automatically deleted when the last consumer unsubscribes; otherwise, false. + /// + /// Auto-delete queues are deleted when the last consumer unsubscribes. If there are no consumers, the queue is not deleted. + /// + public bool AutoDelete { get; set; } - /// - /// Determines whether the public read-write properties of this instance are in a valid state. - /// - /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. - /// - /// cannot be null or empty. - /// - public override void ValidateOptions() - { - Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(QueueName)); - base.ValidateOptions(); - } + /// + /// Determines whether the public read-write properties of this instance are in a valid state. + /// + /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. + /// + /// cannot be null or empty. + /// + public override void ValidateOptions() + { + Validator.ThrowIfInvalidState(string.IsNullOrWhiteSpace(QueueName)); + base.ValidateOptions(); } } diff --git a/src/Savvyio.Extensions.RabbitMQ/EventDriven/RabbitMqEventBus.cs b/src/Savvyio.Extensions.RabbitMQ/EventDriven/RabbitMqEventBus.cs index f8d374a1..c483ba85 100644 --- a/src/Savvyio.Extensions.RabbitMQ/EventDriven/RabbitMqEventBus.cs +++ b/src/Savvyio.Extensions.RabbitMQ/EventDriven/RabbitMqEventBus.cs @@ -1,4 +1,4 @@ -using Cuemon; +using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.IO; using Cuemon.Extensions.Reflection; @@ -13,97 +13,96 @@ using System.Threading.Channels; using System.Threading.Tasks; -namespace Savvyio.Extensions.RabbitMQ.EventDriven +namespace Savvyio.Extensions.RabbitMQ.EventDriven; + +/// +/// Provides a default implementation of the class for messages holding an implementation. +/// +/// +/// +public class RabbitMqEventBus : RabbitMqMessage, IPublishSubscribeChannel { + private readonly RabbitMqEventBusOptions _options; + /// - /// Provides a default implementation of the class for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - /// - public class RabbitMqEventBus : RabbitMqMessage, IPublishSubscribeChannel + /// The marshaller used for serializing and deserializing messages. + /// The options used to configure the RabbitMQ event bus. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public RabbitMqEventBus(IMarshaller marshaller, RabbitMqEventBusOptions options) : base(marshaller, options) { - private readonly RabbitMqEventBusOptions _options; + Validator.ThrowIfInvalidOptions(options); + _options = options; + } - /// - /// Initializes a new instance of the class. - /// - /// The marshaller used for serializing and deserializing messages. - /// The options used to configure the RabbitMQ event bus. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public RabbitMqEventBus(IMarshaller marshaller, RabbitMqEventBusOptions options) : base(marshaller, options) - { - Validator.ThrowIfInvalidOptions(options); - _options = options; - } + /// + /// Publishes the specified integration event message asynchronously to the configured RabbitMQ exchange. + /// + /// The message to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + public async Task PublishAsync(IMessage message, Action setup = null) + { + Validator.ThrowIfInvalidConfigurator(setup, out var options); - /// - /// Publishes the specified integration event message asynchronously to the configured RabbitMQ exchange. - /// - /// The message to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - public async Task PublishAsync(IMessage message, Action setup = null) - { - Validator.ThrowIfInvalidConfigurator(setup, out var options); + await EnsureConnectivityAsync(options.CancellationToken).ConfigureAwait(false); - await EnsureConnectivityAsync(options.CancellationToken).ConfigureAwait(false); + await RabbitMqChannel.ExchangeDeclareAsync(_options.ExchangeName, ExchangeType.Fanout, cancellationToken: options.CancellationToken).ConfigureAwait(false); - await RabbitMqChannel.ExchangeDeclareAsync(_options.ExchangeName, ExchangeType.Fanout, cancellationToken: options.CancellationToken).ConfigureAwait(false); + await RabbitMqChannel.BasicPublishAsync(_options.ExchangeName, "", false, basicProperties: new BasicProperties() + { + Persistent = _options.Persistent, + Headers = new Dictionary() + { + { MessageType, message.GetType().ToFullNameIncludingAssemblyName() } + } + }, body: await Marshaller.Serialize(message).ToByteArrayAsync(o => o.CancellationToken = options.CancellationToken).ConfigureAwait(false), options.CancellationToken).ConfigureAwait(false); + } - await RabbitMqChannel.BasicPublishAsync(_options.ExchangeName, "", false, basicProperties: new BasicProperties() - { - Persistent = _options.Persistent, - Headers = new Dictionary() - { - { MessageType, message.GetType().ToFullNameIncludingAssemblyName() } - } - }, body: await Marshaller.Serialize(message).ToByteArrayAsync(o => o.CancellationToken = options.CancellationToken).ConfigureAwait(false), options.CancellationToken).ConfigureAwait(false); - } + /// + /// Subscribes to integration event messages from the configured RabbitMQ exchange and invokes the specified asynchronous handler for each received message. + /// + /// The function delegate that will handle the message. + /// The which may be configured. + /// A that represents the asynchronous operation. + public async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) + { + Validator.ThrowIfInvalidConfigurator(setup, out var options); - /// - /// Subscribes to integration event messages from the configured RabbitMQ exchange and invokes the specified asynchronous handler for each received message. - /// - /// The function delegate that will handle the message. - /// The which may be configured. - /// A that represents the asynchronous operation. - public async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) - { - Validator.ThrowIfInvalidConfigurator(setup, out var options); + await EnsureConnectivityAsync(options.CancellationToken).ConfigureAwait(false); - await EnsureConnectivityAsync(options.CancellationToken).ConfigureAwait(false); + await RabbitMqChannel.ExchangeDeclareAsync(_options.ExchangeName, ExchangeType.Fanout, cancellationToken: options.CancellationToken).ConfigureAwait(false); - await RabbitMqChannel.ExchangeDeclareAsync(_options.ExchangeName, ExchangeType.Fanout, cancellationToken: options.CancellationToken).ConfigureAwait(false); + var queue = await RabbitMqChannel.QueueDeclareAsync(cancellationToken: options.CancellationToken).ConfigureAwait(false); - var queue = await RabbitMqChannel.QueueDeclareAsync(cancellationToken: options.CancellationToken).ConfigureAwait(false); + await RabbitMqChannel.QueueBindAsync(queue.QueueName, _options.ExchangeName, "", cancellationToken: options.CancellationToken).ConfigureAwait(false); - await RabbitMqChannel.QueueBindAsync(queue.QueueName, _options.ExchangeName, "", cancellationToken: options.CancellationToken).ConfigureAwait(false); + var buffer = Channel.CreateUnbounded>(); + var consumer = new AsyncEventingBasicConsumer(RabbitMqChannel); + consumer.ReceivedAsync += async (_, e) => + { + var messageType = Type.GetType((e.BasicProperties.Headers[MessageType] as byte[]).ToEncodedString()); + var deserialized = Marshaller.Deserialize(e.Body.ToArray().ToStream(), messageType) as IMessage; + deserialized!.Properties.Add(nameof(BasicDeliverEventArgs.DeliveryTag), e.DeliveryTag); + deserialized.Properties.Add(nameof(CancellationToken), options.CancellationToken); + deserialized.Properties.Add(nameof(QueueDeclareOk.QueueName), queue.QueueName); + await buffer.Writer.WriteAsync(deserialized, options.CancellationToken).ConfigureAwait(false); + }; - var buffer = Channel.CreateUnbounded>(); - var consumer = new AsyncEventingBasicConsumer(RabbitMqChannel); - consumer.ReceivedAsync += async (_, e) => - { - var messageType = Type.GetType((e.BasicProperties.Headers[MessageType] as byte[]).ToEncodedString()); - var deserialized = Marshaller.Deserialize(e.Body.ToArray().ToStream(), messageType) as IMessage; - deserialized!.Properties.Add(nameof(BasicDeliverEventArgs.DeliveryTag), e.DeliveryTag); - deserialized.Properties.Add(nameof(CancellationToken), options.CancellationToken); - deserialized.Properties.Add(nameof(QueueDeclareOk.QueueName), queue.QueueName); - await buffer.Writer.WriteAsync(deserialized, options.CancellationToken).ConfigureAwait(false); - }; - - await RabbitMqChannel.BasicConsumeAsync(queue.QueueName, autoAck: true, consumer: consumer, options.CancellationToken).ConfigureAwait(false); + await RabbitMqChannel.BasicConsumeAsync(queue.QueueName, autoAck: true, consumer: consumer, options.CancellationToken).ConfigureAwait(false); - while (await buffer.Reader.WaitToReadAsync(options.CancellationToken).ConfigureAwait(false)) + while (await buffer.Reader.WaitToReadAsync(options.CancellationToken).ConfigureAwait(false)) + { + while (buffer.Reader.TryRead(out var message)) { - while (buffer.Reader.TryRead(out var message)) - { - await asyncHandler(message, options.CancellationToken).ConfigureAwait(false); - } + await asyncHandler(message, options.CancellationToken).ConfigureAwait(false); } } } diff --git a/src/Savvyio.Extensions.RabbitMQ/EventDriven/RabbitMqEventBusOptions.cs b/src/Savvyio.Extensions.RabbitMQ/EventDriven/RabbitMqEventBusOptions.cs index bc0bef17..c54fd14b 100644 --- a/src/Savvyio.Extensions.RabbitMQ/EventDriven/RabbitMqEventBusOptions.cs +++ b/src/Savvyio.Extensions.RabbitMQ/EventDriven/RabbitMqEventBusOptions.cs @@ -1,51 +1,50 @@ -using System; +using System; using Cuemon; -namespace Savvyio.Extensions.RabbitMQ.EventDriven +namespace Savvyio.Extensions.RabbitMQ.EventDriven; + +/// +/// Configuration options for . +/// +/// +public class RabbitMqEventBusOptions : RabbitMqMessageOptions { /// - /// Configuration options for . + /// Initializes a new instance of the class. /// - /// - public class RabbitMqEventBusOptions : RabbitMqMessageOptions + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// null + /// + /// + /// + public RabbitMqEventBusOptions() { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// null - /// - /// - /// - public RabbitMqEventBusOptions() - { - } + } - /// - /// Gets or sets the name of the exchange. - /// - /// The name of the exchange. - public string ExchangeName { get; set; } + /// + /// Gets or sets the name of the exchange. + /// + /// The name of the exchange. + public string ExchangeName { get; set; } - /// - /// Determines whether the public read-write properties of this instance are in a valid state. - /// - /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. - /// - /// cannot be null or empty. - /// - public override void ValidateOptions() - { - Validator.ThrowIfInvalidState(string.IsNullOrEmpty(ExchangeName)); - base.ValidateOptions(); - } + /// + /// Determines whether the public read-write properties of this instance are in a valid state. + /// + /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. + /// + /// cannot be null or empty. + /// + public override void ValidateOptions() + { + Validator.ThrowIfInvalidState(string.IsNullOrEmpty(ExchangeName)); + base.ValidateOptions(); } } diff --git a/src/Savvyio.Extensions.RabbitMQ/RabbitMqMessage.cs b/src/Savvyio.Extensions.RabbitMQ/RabbitMqMessage.cs index 08969e46..ab16c74f 100644 --- a/src/Savvyio.Extensions.RabbitMQ/RabbitMqMessage.cs +++ b/src/Savvyio.Extensions.RabbitMQ/RabbitMqMessage.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions; using RabbitMQ.Client; @@ -6,118 +6,117 @@ using System.Threading.Tasks; using Savvyio.Diagnostics; -namespace Savvyio.Extensions.RabbitMQ +namespace Savvyio.Extensions.RabbitMQ; + +/// +/// Provides a base class for RabbitMQ message operations, including connection and channel management, marshalling, and resource disposal. Ensures thread-safe initialization of RabbitMQ connectivity. +/// +public abstract class RabbitMqMessage : AsyncDisposable, IAsyncHealthCheckProvider { /// - /// Provides a base class for RabbitMQ message operations, including connection and channel management, marshalling, and resource disposal. Ensures thread-safe initialization of RabbitMQ connectivity. + /// The header key used to indicate the message type in RabbitMQ properties. /// - public abstract class RabbitMqMessage : AsyncDisposable, IAsyncHealthCheckProvider - { - /// - /// The header key used to indicate the message type in RabbitMQ properties. - /// - protected const string MessageType = "type"; + protected const string MessageType = "type"; - private readonly SemaphoreSlim _asyncLock = new(1, 1); - private bool _initialized; + private readonly SemaphoreSlim _asyncLock = new(1, 1); + private bool _initialized; - /// - /// Initializes a new instance of the class with the specified marshaller and options. - /// - /// The marshaller used for serializing and deserializing messages. - /// The options used to configure the RabbitMQ connection. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - protected RabbitMqMessage(IMarshaller marshaller, RabbitMqMessageOptions options) + /// + /// Initializes a new instance of the class with the specified marshaller and options. + /// + /// The marshaller used for serializing and deserializing messages. + /// The options used to configure the RabbitMQ connection. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + protected RabbitMqMessage(IMarshaller marshaller, RabbitMqMessageOptions options) + { + Validator.ThrowIfInvalidOptions(options); + Validator.ThrowIfNull(marshaller); + Marshaller = marshaller; + RabbitMqFactory = new ConnectionFactory { - Validator.ThrowIfInvalidOptions(options); - Validator.ThrowIfNull(marshaller); - Marshaller = marshaller; - RabbitMqFactory = new ConnectionFactory - { - Uri = options.AmqpUrl - }; - } + Uri = options.AmqpUrl + }; + } - /// - /// Gets the RabbitMQ connection factory used to create connections to the broker. - /// - protected IConnectionFactory RabbitMqFactory { get; private set; } + /// + /// Gets the RabbitMQ connection factory used to create connections to the broker. + /// + protected IConnectionFactory RabbitMqFactory { get; private set; } - /// - /// Ensures that a connection and channel to the RabbitMQ broker are established and initialized. - /// This method is thread-safe and will only initialize the connection and channel once. - /// - /// - /// A that can be used to cancel the asynchronous operation. - /// - /// - /// A representing the asynchronous operation. - /// - /// - /// If the connection and channel are already initialized, this method returns immediately. - /// Otherwise, it acquires an asynchronous lock to ensure only one initialization occurs, - /// then creates the RabbitMQ connection and channel. - /// - protected async Task EnsureConnectivityAsync(CancellationToken ct) + /// + /// Ensures that a connection and channel to the RabbitMQ broker are established and initialized. + /// This method is thread-safe and will only initialize the connection and channel once. + /// + /// + /// A that can be used to cancel the asynchronous operation. + /// + /// + /// A representing the asynchronous operation. + /// + /// + /// If the connection and channel are already initialized, this method returns immediately. + /// Otherwise, it acquires an asynchronous lock to ensure only one initialization occurs, + /// then creates the RabbitMQ connection and channel. + /// + protected async Task EnsureConnectivityAsync(CancellationToken ct) + { + if (_initialized) { return; } + await _asyncLock.WaitAsync(ct).ConfigureAwait(false); + try { - if (_initialized) { return; } - await _asyncLock.WaitAsync(ct).ConfigureAwait(false); - try + if (!_initialized) { - if (!_initialized) - { - RabbitMqConnection = await RabbitMqFactory.CreateConnectionAsync(ct).ConfigureAwait(false); - RabbitMqChannel = await RabbitMqConnection.CreateChannelAsync(cancellationToken: ct).ConfigureAwait(false); - _initialized = true; - } - } - finally - { - _asyncLock.Release(); + RabbitMqConnection = await RabbitMqFactory.CreateConnectionAsync(ct).ConfigureAwait(false); + RabbitMqChannel = await RabbitMqConnection.CreateChannelAsync(cancellationToken: ct).ConfigureAwait(false); + _initialized = true; } } + finally + { + _asyncLock.Release(); + } + } - /// - /// Gets the current RabbitMQ connection instance. - /// - protected IConnection RabbitMqConnection { get; private set; } + /// + /// Gets the current RabbitMQ connection instance. + /// + protected IConnection RabbitMqConnection { get; private set; } - /// - /// Gets the current RabbitMQ channel instance. - /// - protected IChannel RabbitMqChannel { get; private set; } + /// + /// Gets the current RabbitMQ channel instance. + /// + protected IChannel RabbitMqChannel { get; private set; } - /// - /// Gets the by constructor provided serializer context. - /// - /// The by constructor provided serializer context. - protected IMarshaller Marshaller { get; } + /// + /// Gets the by constructor provided serializer context. + /// + /// The by constructor provided serializer context. + protected IMarshaller Marshaller { get; } - /// - /// Called when this object is being disposed by . - /// Disposes the RabbitMQ channel and connection asynchronously if they have been initialized. - /// - /// A representing the asynchronous dispose operation. - protected override async ValueTask OnDisposeManagedResourcesAsync() - { - if (RabbitMqChannel != null) { await RabbitMqChannel.DisposeAsync().ConfigureAwait(false); } - if (RabbitMqConnection != null) { await RabbitMqConnection.DisposeAsync().ConfigureAwait(false); } - } + /// + /// Called when this object is being disposed by . + /// Disposes the RabbitMQ channel and connection asynchronously if they have been initialized. + /// + /// A representing the asynchronous dispose operation. + protected override async ValueTask OnDisposeManagedResourcesAsync() + { + if (RabbitMqChannel != null) { await RabbitMqChannel.DisposeAsync().ConfigureAwait(false); } + if (RabbitMqConnection != null) { await RabbitMqConnection.DisposeAsync().ConfigureAwait(false); } + } - /// - /// Asynchronously returns an instance used for probing the health status of the RabbitMQ broker. - /// - /// A that can be used to cancel the asynchronous operation. - /// A task that represents the asynchronous operation. The task result contains the instance representing the active connection to the RabbitMQ broker, or a newly created connection if not already initialized. - public async Task GetHealthCheckTargetAsync(CancellationToken cancellationToken = default) - { - return RabbitMqConnection ?? await RabbitMqFactory.CreateConnectionAsync(cancellationToken).ConfigureAwait(false); - } + /// + /// Asynchronously returns an instance used for probing the health status of the RabbitMQ broker. + /// + /// A that can be used to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. The task result contains the instance representing the active connection to the RabbitMQ broker, or a newly created connection if not already initialized. + public async Task GetHealthCheckTargetAsync(CancellationToken cancellationToken = default) + { + return RabbitMqConnection ?? await RabbitMqFactory.CreateConnectionAsync(cancellationToken).ConfigureAwait(false); } } diff --git a/src/Savvyio.Extensions.RabbitMQ/RabbitMqMessageOptions.cs b/src/Savvyio.Extensions.RabbitMQ/RabbitMqMessageOptions.cs index 84f6b761..b19f65df 100644 --- a/src/Savvyio.Extensions.RabbitMQ/RabbitMqMessageOptions.cs +++ b/src/Savvyio.Extensions.RabbitMQ/RabbitMqMessageOptions.cs @@ -1,68 +1,67 @@ -using System; +using System; using Cuemon; using Cuemon.Configuration; -namespace Savvyio.Extensions.RabbitMQ +namespace Savvyio.Extensions.RabbitMQ; + +/// +/// Configuration options that is related to RabbitMQ. +/// +/// +public class RabbitMqMessageOptions : IValidatableParameterObject { /// - /// Configuration options that is related to RabbitMQ. + /// Initializes a new instance of the class with default values. /// - /// - public class RabbitMqMessageOptions : IValidatableParameterObject + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// new Uri("amqp://localhost:5672") + /// + /// + /// + /// false + /// + /// + /// + public RabbitMqMessageOptions() { - /// - /// Initializes a new instance of the class with default values. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// new Uri("amqp://localhost:5672") - /// - /// - /// - /// false - /// - /// - /// - public RabbitMqMessageOptions() - { - AmqpUrl = new Uri("amqp://localhost:5672"); - } + AmqpUrl = new Uri("amqp://localhost:5672"); + } - /// - /// Gets or sets the AMQP URL used to connect to the RabbitMQ broker. - /// - /// - /// The representing the AMQP endpoint for the RabbitMQ broker. - /// - public Uri AmqpUrl { get; set; } + /// + /// Gets or sets the AMQP URL used to connect to the RabbitMQ broker. + /// + /// + /// The representing the AMQP endpoint for the RabbitMQ broker. + /// + public Uri AmqpUrl { get; set; } - /// - /// Gets or sets a value indicating whether messages should be published as persistent. - /// - /// true if messages are marked as persistent and survive broker restarts; otherwise, false. - /// - /// When set to true, messages are stored to disk by RabbitMQ, ensuring delivery even if the broker restarts. - /// This corresponds to setting the delivery mode to persistent (2) in RabbitMQ. - /// - public bool Persistent { get; set; } + /// + /// Gets or sets a value indicating whether messages should be published as persistent. + /// + /// true if messages are marked as persistent and survive broker restarts; otherwise, false. + /// + /// When set to true, messages are stored to disk by RabbitMQ, ensuring delivery even if the broker restarts. + /// This corresponds to setting the delivery mode to persistent (2) in RabbitMQ. + /// + public bool Persistent { get; set; } - /// - /// Determines whether the public read-write properties of this instance are in a valid state. - /// - /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. - /// - /// cannot be null. - /// - public virtual void ValidateOptions() - { - Validator.ThrowIfInvalidState(AmqpUrl == null); - } + /// + /// Determines whether the public read-write properties of this instance are in a valid state. + /// + /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. + /// + /// cannot be null. + /// + public virtual void ValidateOptions() + { + Validator.ThrowIfInvalidState(AmqpUrl == null); } } diff --git a/src/Savvyio.Extensions.RabbitMQ/Savvyio.Extensions.RabbitMQ.csproj b/src/Savvyio.Extensions.RabbitMQ/Savvyio.Extensions.RabbitMQ.csproj index 5299f41f..bd077a80 100644 --- a/src/Savvyio.Extensions.RabbitMQ/Savvyio.Extensions.RabbitMQ.csproj +++ b/src/Savvyio.Extensions.RabbitMQ/Savvyio.Extensions.RabbitMQ.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O core assemblies with support for RabbitMQ Work Queue and RabbitMQ Publish/Subscribe. diff --git a/src/Savvyio.Extensions.SimpleQueueService/AmazonBus.cs b/src/Savvyio.Extensions.SimpleQueueService/AmazonBus.cs index 8ba9e237..089d4347 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/AmazonBus.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/AmazonBus.cs @@ -1,49 +1,48 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Cuemon.Threading; using Savvyio.Messaging; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +/// +/// Represents the base class from which all implementations in need of bus capabilities should derive. +/// +/// The type of the model to handle. +/// +/// +public abstract class AmazonBus : AmazonMessage, IPublishSubscribeChannel where TRequest : IRequest { /// - /// Represents the base class from which all implementations in need of bus capabilities should derive. + /// Initializes a new instance of the class. /// - /// The type of the model to handle. - /// - /// - public abstract class AmazonBus : AmazonMessage, IPublishSubscribeChannel where TRequest : IRequest + /// The that is used when converting models to messages. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + protected AmazonBus(IMarshaller marshaller, AmazonMessageOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The that is used when converting models to messages. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - protected AmazonBus(IMarshaller marshaller, AmazonMessageOptions options) : base(marshaller, options) - { - } + } - /// - /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The message to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - public abstract Task PublishAsync(IMessage message, Action setup = null); + /// + /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. + /// + /// The message to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + public abstract Task PublishAsync(IMessage message, Action setup = null); - /// - /// Subscribe to one or more message(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The function delegate that will handle the message. - /// The which may be configured. - /// A that represents the asynchronous operation. - public abstract Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null); - } + /// + /// Subscribe to one or more message(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. + /// + /// The function delegate that will handle the message. + /// The which may be configured. + /// A that represents the asynchronous operation. + public abstract Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null); } diff --git a/src/Savvyio.Extensions.SimpleQueueService/AmazonMessage.cs b/src/Savvyio.Extensions.SimpleQueueService/AmazonMessage.cs index 7067e71f..ef02c42f 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/AmazonMessage.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/AmazonMessage.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Cuemon.Configuration; using Cuemon; using Savvyio.Messaging; @@ -11,179 +11,178 @@ using System.Runtime.CompilerServices; using Cuemon.Extensions.IO; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +/// +/// Represents the base class from which all implementations of AWS SQS should derive. +/// +/// The type of the model to handle. +/// +public abstract class AmazonMessage : IConfigurable where TRequest : IRequest { /// - /// Represents the base class from which all implementations of AWS SQS should derive. + /// The key for the message type attribute. + /// + protected const string MessageTypeAttributeKey = "type"; + + /// + /// Initializes a new instance of the class. /// - /// The type of the model to handle. - /// - public abstract class AmazonMessage : IConfigurable where TRequest : IRequest + /// The that is used when converting models to messages. + /// The used to configure this instance. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + protected AmazonMessage(IMarshaller marshaller, AmazonMessageOptions options) { - /// - /// The key for the message type attribute. - /// - protected const string MessageTypeAttributeKey = "type"; + Validator.ThrowIfNull(marshaller); + Validator.ThrowIfInvalidOptions(options); + Marshaller = marshaller; + Options = options; + UseFirstInFirstOut = options.SourceQueue.OriginalString.Contains(".fifo", StringComparison.OrdinalIgnoreCase); + } - /// - /// Initializes a new instance of the class. - /// - /// The that is used when converting models to messages. - /// The used to configure this instance. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - protected AmazonMessage(IMarshaller marshaller, AmazonMessageOptions options) - { - Validator.ThrowIfNull(marshaller); - Validator.ThrowIfInvalidOptions(options); - Marshaller = marshaller; - Options = options; - UseFirstInFirstOut = options.SourceQueue.OriginalString.Contains(".fifo", StringComparison.OrdinalIgnoreCase); - } + /// + /// Gets a value indicating whether AWS SQS is configured for FIFO. + /// + /// true if AWS SQS is configured for FIFO; otherwise, false. + protected bool UseFirstInFirstOut { get; } - /// - /// Gets a value indicating whether AWS SQS is configured for FIFO. - /// - /// true if AWS SQS is configured for FIFO; otherwise, false. - protected bool UseFirstInFirstOut { get; } + /// + /// Gets the by constructor provided serializer context. + /// + /// The by constructor provided serializer context. + protected IMarshaller Marshaller { get; } - /// - /// Gets the by constructor provided serializer context. - /// - /// The by constructor provided serializer context. - protected IMarshaller Marshaller { get; } + /// + /// Gets the configured of this instance. + /// + /// The configured of this instance. + public AmazonMessageOptions Options { get; } - /// - /// Gets the configured of this instance. - /// - /// The configured of this instance. - public AmazonMessageOptions Options { get; } + /// + /// Receive one or more message(s) asynchronous from AWS SQS. + /// + /// The cancellation token of an asynchronous operations. + /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . + protected virtual async IAsyncEnumerable> RetrieveMessagesAsync([EnumeratorCancellation] CancellationToken cancellationToken) + { + var sqs = CreateSimpleQueueServiceClient(); - /// - /// Receive one or more message(s) asynchronous from AWS SQS. - /// - /// The cancellation token of an asynchronous operations. - /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . - protected virtual async IAsyncEnumerable> RetrieveMessagesAsync([EnumeratorCancellation] CancellationToken cancellationToken) + if (Options.ReceiveContext.UseApproximateNumberOfMessages) { - var sqs = CreateSimpleQueueServiceClient(); - - if (Options.ReceiveContext.UseApproximateNumberOfMessages) - { - var approximateNumberOfMessages = await FetchApproximateNumberOfMessagesAsync(sqs, cancellationToken).ConfigureAwait(false); // cost 1 request - var currentMessagesCount = approximateNumberOfMessages; - if (approximateNumberOfMessages == 0) { yield break; } - while (approximateNumberOfMessages > 0 && currentMessagesCount > 0) - { - var messagesCount = 0; - await foreach (var message in ReceiveMessagesAsync(sqs, cancellationToken).ConfigureAwait(false)) - { - messagesCount++; - yield return message; - } - currentMessagesCount = messagesCount; - approximateNumberOfMessages -= currentMessagesCount; - } - } - else + var approximateNumberOfMessages = await FetchApproximateNumberOfMessagesAsync(sqs, cancellationToken).ConfigureAwait(false); // cost 1 request + var currentMessagesCount = approximateNumberOfMessages; + if (approximateNumberOfMessages == 0) { yield break; } + while (approximateNumberOfMessages > 0 && currentMessagesCount > 0) { + var messagesCount = 0; await foreach (var message in ReceiveMessagesAsync(sqs, cancellationToken).ConfigureAwait(false)) { + messagesCount++; yield return message; } + currentMessagesCount = messagesCount; + approximateNumberOfMessages -= currentMessagesCount; } } - - private async Task FetchApproximateNumberOfMessagesAsync(IAmazonSQS sqs, CancellationToken cancellationToken) + else { - var attributes = await sqs.GetQueueAttributesAsync(new GetQueueAttributesRequest + await foreach (var message in ReceiveMessagesAsync(sqs, cancellationToken).ConfigureAwait(false)) { - QueueUrl = Options.SourceQueue.OriginalString, - AttributeNames = new List() - { - "ApproximateNumberOfMessages" - } - }, cancellationToken).ConfigureAwait(false); - return attributes.ApproximateNumberOfMessages; + yield return message; + } } + } - private async IAsyncEnumerable> ReceiveMessagesAsync(IAmazonSQS sqs, [EnumeratorCancellation] CancellationToken cancellationToken) + private async Task FetchApproximateNumberOfMessagesAsync(IAmazonSQS sqs, CancellationToken cancellationToken) + { + var attributes = await sqs.GetQueueAttributesAsync(new GetQueueAttributesRequest { - var request = new ReceiveMessageRequest + QueueUrl = Options.SourceQueue.OriginalString, + AttributeNames = new List() { - MaxNumberOfMessages = Options.ReceiveContext.NumberOfMessagesToTakePerRequest, - WaitTimeSeconds = (int)Options.ReceiveContext.PollingTimeout.TotalSeconds, - QueueUrl = Options.SourceQueue.OriginalString, - MessageAttributeNames = new List() - { - MessageTypeAttributeKey - }, - VisibilityTimeout = (int)Options.ReceiveContext.VisibilityTimeout.TotalSeconds - }; + "ApproximateNumberOfMessages" + } + }, cancellationToken).ConfigureAwait(false); + return attributes.ApproximateNumberOfMessages; + } - var response = await sqs.ReceiveMessageAsync(request, cancellationToken).ConfigureAwait(false); // cost 1 request with fetching of up to 10 messages - if (response.Messages == null || response.Messages.Count == 0) { yield break; } // v4 requirement as per https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html#net-dg-v4-collections - var deserializedMessages = new List>(); - foreach (var message in response.Messages) + private async IAsyncEnumerable> ReceiveMessagesAsync(IAmazonSQS sqs, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var request = new ReceiveMessageRequest + { + MaxNumberOfMessages = Options.ReceiveContext.NumberOfMessagesToTakePerRequest, + WaitTimeSeconds = (int)Options.ReceiveContext.PollingTimeout.TotalSeconds, + QueueUrl = Options.SourceQueue.OriginalString, + MessageAttributeNames = new List() { - cancellationToken.ThrowIfCancellationRequested(); - var messageType = Type.GetType(message.MessageAttributes[MessageTypeAttributeKey].StringValue); - var deserialized = Marshaller.Deserialize(message.Body.ToStream(), messageType) as IMessage; - deserialized!.Properties.Add(nameof(DeleteMessageBatchRequestEntry.Id), message.MessageId); - deserialized.Properties.Add(nameof(DeleteMessageBatchRequestEntry.ReceiptHandle), message.ReceiptHandle); - deserializedMessages.Add(deserialized); - } + MessageTypeAttributeKey + }, + VisibilityTimeout = (int)Options.ReceiveContext.VisibilityTimeout.TotalSeconds + }; + + var response = await sqs.ReceiveMessageAsync(request, cancellationToken).ConfigureAwait(false); // cost 1 request with fetching of up to 10 messages + if (response.Messages == null || response.Messages.Count == 0) { yield break; } // v4 requirement as per https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html#net-dg-v4-collections + var deserializedMessages = new List>(); + foreach (var message in response.Messages) + { + cancellationToken.ThrowIfCancellationRequested(); + var messageType = Type.GetType(message.MessageAttributes[MessageTypeAttributeKey].StringValue); + var deserialized = Marshaller.Deserialize(message.Body.ToStream(), messageType) as IMessage; + deserialized!.Properties.Add(nameof(DeleteMessageBatchRequestEntry.Id), message.MessageId); + deserialized.Properties.Add(nameof(DeleteMessageBatchRequestEntry.ReceiptHandle), message.ReceiptHandle); + deserializedMessages.Add(deserialized); + } - await foreach (var message in new MessageAsyncEnumerable(deserializedMessages, o => + await foreach (var message in new MessageAsyncEnumerable(deserializedMessages, o => + { + o.MessageCallback = async message => { - o.MessageCallback = async message => + if (Options.ReceiveContext.AssumeMessageProcessed) { - if (Options.ReceiveContext.AssumeMessageProcessed) - { - await message.AcknowledgeAsync().ConfigureAwait(false); - } - }; - o.AcknowledgedPropertiesCallback = async properties => + await message.AcknowledgeAsync().ConfigureAwait(false); + } + }; + o.AcknowledgedPropertiesCallback = async properties => + { + if (Options.ReceiveContext.RemoveProcessedMessages) { - if (Options.ReceiveContext.RemoveProcessedMessages) - { - await RemoveProcessedMessagesAsync(sqs, properties, cancellationToken).ConfigureAwait(false); - } - }; - }).ConfigureAwait(false)) - { - yield return message; - } + await RemoveProcessedMessagesAsync(sqs, properties, cancellationToken).ConfigureAwait(false); + } + }; + }).ConfigureAwait(false)) + { + yield return message; } + } - private async Task RemoveProcessedMessagesAsync(IAmazonSQS sqs, IEnumerable> properties, CancellationToken cancellationToken) + private async Task RemoveProcessedMessagesAsync(IAmazonSQS sqs, IEnumerable> properties, CancellationToken cancellationToken) + { + var batchRequest = new DeleteMessageBatchRequest { - var batchRequest = new DeleteMessageBatchRequest + QueueUrl = Options.SourceQueue.OriginalString, + Entries = new(properties.Select(property => new DeleteMessageBatchRequestEntry { - QueueUrl = Options.SourceQueue.OriginalString, - Entries = new(properties.Select(property => new DeleteMessageBatchRequestEntry - { - Id = (string)property[nameof(DeleteMessageBatchRequestEntry.Id)], - ReceiptHandle = (string)property[nameof(DeleteMessageBatchRequestEntry.ReceiptHandle)] - })) - }; - await sqs.DeleteMessageBatchAsync(batchRequest, cancellationToken).ConfigureAwait(false); // cost 1 request with deletion of up to 10 messages - } + Id = (string)property[nameof(DeleteMessageBatchRequestEntry.Id)], + ReceiptHandle = (string)property[nameof(DeleteMessageBatchRequestEntry.ReceiptHandle)] + })) + }; + await sqs.DeleteMessageBatchAsync(batchRequest, cancellationToken).ConfigureAwait(false); // cost 1 request with deletion of up to 10 messages + } - /// - /// Creates the AWS SQS client used by send and receive operations. - /// - /// An client configured from . - protected virtual IAmazonSQS CreateSimpleQueueServiceClient() - { - return Options.ClientConfigurations.IsValid() - ? new AmazonSQSClient(Options.Credentials, Options.ClientConfigurations.SimpleQueueService()) - : new AmazonSQSClient(Options.Credentials, Options.Endpoint); - } + /// + /// Creates the AWS SQS client used by send and receive operations. + /// + /// An client configured from . + protected virtual IAmazonSQS CreateSimpleQueueServiceClient() + { + return Options.ClientConfigurations.IsValid() + ? new AmazonSQSClient(Options.Credentials, Options.ClientConfigurations.SimpleQueueService()) + : new AmazonSQSClient(Options.Credentials, Options.Endpoint); } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/AmazonMessageOptions.cs b/src/Savvyio.Extensions.SimpleQueueService/AmazonMessageOptions.cs index 48aebafa..a62c9d02 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/AmazonMessageOptions.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/AmazonMessageOptions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Amazon; using Amazon.Runtime; @@ -7,136 +7,135 @@ using Cuemon; using Cuemon.Configuration; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +/// +/// Configuration options that is related to AWS SQS and AWS SNS. +/// +/// +public class AmazonMessageOptions : IValidatableParameterObject { /// - /// Configuration options that is related to AWS SQS and AWS SNS. + /// The maximum number of messages that AWS SQS supports when sending or retrieving. /// - /// - public class AmazonMessageOptions : IValidatableParameterObject - { - /// - /// The maximum number of messages that AWS SQS supports when sending or retrieving. - /// - public const int MaxNumberOfMessages = 10; + public const int MaxNumberOfMessages = 10; - /// - /// The maximum amount of seconds that a call waits for a message to arrive in AWS SQS. - /// - public const int MaxPollingWaitTimeInSeconds = 20; + /// + /// The maximum amount of seconds that a call waits for a message to arrive in AWS SQS. + /// + public const int MaxPollingWaitTimeInSeconds = 20; - /// - /// The maximum amount of seconds that a message can be hidden from other consumers in AWS SQS after it has been received. - /// - public const int MaxVisibilityTimeoutInSeconds = 12 * 60 * 60; + /// + /// The maximum amount of seconds that a message can be hidden from other consumers in AWS SQS after it has been received. + /// + public const int MaxVisibilityTimeoutInSeconds = 12 * 60 * 60; - /// - /// The default amount of seconds that a message can be hidden from other consumers in AWS SQS after it has been received. - /// - public const int DefaultVisibilityTimeoutInSeconds = 30; + /// + /// The default amount of seconds that a message can be hidden from other consumers in AWS SQS after it has been received. + /// + public const int DefaultVisibilityTimeoutInSeconds = 30; - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// null - /// - /// - /// - /// null - /// - /// - /// - /// null - /// - /// - /// - /// Enumerable.Empty<ClientConfig>().ToArray(); - /// - /// - /// - public AmazonMessageOptions() - { - ReceiveContext = new AmazonMessageReceiveOptions(); - ClientConfigurations = Enumerable.Empty().ToArray(); - } + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// null + /// + /// + /// + /// null + /// + /// + /// + /// null + /// + /// + /// + /// Enumerable.Empty<ClientConfig>().ToArray(); + /// + /// + /// + public AmazonMessageOptions() + { + ReceiveContext = new AmazonMessageReceiveOptions(); + ClientConfigurations = Enumerable.Empty().ToArray(); + } - /// - /// Gets or sets the credentials required to connect to AWS SQS/SNS. - /// - /// The credentials required to connect to AWS SQS/SNS. - public AWSCredentials Credentials { get; set; } + /// + /// Gets or sets the credentials required to connect to AWS SQS/SNS. + /// + /// The credentials required to connect to AWS SQS/SNS. + public AWSCredentials Credentials { get; set; } - /// - /// Gets or sets the endpoint required to connect to AWS SQS/SNS. - /// - /// The endpoint required to connect to AWS SQS/SNS. - public RegionEndpoint Endpoint { get; set; } + /// + /// Gets or sets the endpoint required to connect to AWS SQS/SNS. + /// + /// The endpoint required to connect to AWS SQS/SNS. + public RegionEndpoint Endpoint { get; set; } - /// - /// Gets the client configuration for AWS SQS/SNS provided by . - /// - /// The configuration of the AWS SQS/SNS. - /// When this property is set, it will take precedence over the property. Useful for testing with a local SQS/SNS instance such as LocalStack or require full configuration flexibility. - public ClientConfig[] ClientConfigurations { get; private set; } + /// + /// Gets the client configuration for AWS SQS/SNS provided by . + /// + /// The configuration of the AWS SQS/SNS. + /// When this property is set, it will take precedence over the property. Useful for testing with a local SQS/SNS instance such as LocalStack or require full configuration flexibility. + public ClientConfig[] ClientConfigurations { get; private set; } - /// - /// Provides a flexible way to configure client configurations for both AWS SQS/SNS in a single call. - /// - /// The setup. - /// - /// cannot be null. - /// - /// When this method is invoked, it will set the property that take precedence over the property. Useful for testing with a local SQS/SNS instance such as LocalStack or require full configuration flexibility. - public AmazonMessageOptions ConfigureClient(Action setup) - { - Validator.ThrowIfNull(setup); - var sqsConfig = new AmazonSQSConfig(); - var snsConfig = new AmazonSimpleNotificationServiceConfig(); - setup(sqsConfig); - setup(snsConfig); - ClientConfigurations = new ClientConfig[] { sqsConfig, snsConfig }; - return this; - } + /// + /// Provides a flexible way to configure client configurations for both AWS SQS/SNS in a single call. + /// + /// The setup. + /// + /// cannot be null. + /// + /// When this method is invoked, it will set the property that take precedence over the property. Useful for testing with a local SQS/SNS instance such as LocalStack or require full configuration flexibility. + public AmazonMessageOptions ConfigureClient(Action setup) + { + Validator.ThrowIfNull(setup); + var sqsConfig = new AmazonSQSConfig(); + var snsConfig = new AmazonSimpleNotificationServiceConfig(); + setup(sqsConfig); + setup(snsConfig); + ClientConfigurations = new ClientConfig[] { sqsConfig, snsConfig }; + return this; + } - /// - /// Gets or sets the URI that represents an AWS SQS/SNS endpoint. - /// - /// The URI that represents an AWS SQS/SNS endpoint. - public Uri SourceQueue { get; set; } + /// + /// Gets or sets the URI that represents an AWS SQS/SNS endpoint. + /// + /// The URI that represents an AWS SQS/SNS endpoint. + public Uri SourceQueue { get; set; } - /// - /// Gets the options related to receive operations on AWS SQS. - /// - /// The options related to receive operations on AWS SQS. - public AmazonMessageReceiveOptions ReceiveContext { get; } + /// + /// Gets the options related to receive operations on AWS SQS. + /// + /// The options related to receive operations on AWS SQS. + public AmazonMessageReceiveOptions ReceiveContext { get; } - /// - /// Determines whether the public read-write properties of this instance are in a valid state. - /// - /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. - /// - /// cannot be null - or - - /// cannot be null - or - - /// cannot be null - or - - /// cannot be null -or- - /// is initialized but does not have a length of 2 or the expected elements of type or . - /// - public void ValidateOptions() - { - Validator.ThrowIfInvalidState(Credentials == null); - Validator.ThrowIfInvalidState(Endpoint == null); - Validator.ThrowIfInvalidState(SourceQueue == null); - Validator.ThrowIfInvalidState(ReceiveContext == null); - Validator.ThrowIfInvalidState(ClientConfigurations.Length > 0 && (ClientConfigurations.Length != 2 || !(ClientConfigurations[0] is AmazonSQSConfig && ClientConfigurations[1] is AmazonSimpleNotificationServiceConfig))); - } + /// + /// Determines whether the public read-write properties of this instance are in a valid state. + /// + /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. + /// + /// cannot be null - or - + /// cannot be null - or - + /// cannot be null - or - + /// cannot be null -or- + /// is initialized but does not have a length of 2 or the expected elements of type or . + /// + public void ValidateOptions() + { + Validator.ThrowIfInvalidState(Credentials == null); + Validator.ThrowIfInvalidState(Endpoint == null); + Validator.ThrowIfInvalidState(SourceQueue == null); + Validator.ThrowIfInvalidState(ReceiveContext == null); + Validator.ThrowIfInvalidState(ClientConfigurations.Length > 0 && (ClientConfigurations.Length != 2 || !(ClientConfigurations[0] is AmazonSQSConfig && ClientConfigurations[1] is AmazonSimpleNotificationServiceConfig))); } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/AmazonMessageReceiveOptions.cs b/src/Savvyio.Extensions.SimpleQueueService/AmazonMessageReceiveOptions.cs index f1227214..ba56394c 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/AmazonMessageReceiveOptions.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/AmazonMessageReceiveOptions.cs @@ -1,107 +1,106 @@ -using System; +using System; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +/// +/// Configuration options that is related to receive operations on AWS SQS. +/// +public class AmazonMessageReceiveOptions { + private TimeSpan _pollingTimeout; + private TimeSpan _visibilityTimeout; + private int _numberOfMessagesToTakePerRequest; + /// - /// Configuration options that is related to receive operations on AWS SQS. + /// Initializes a new instance of the class. /// - public class AmazonMessageReceiveOptions + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// 10 + /// + /// + /// + /// 20 seconds + /// + /// + /// + /// true + /// + /// + /// + /// true + /// + /// + /// + /// false + /// + /// + /// + public AmazonMessageReceiveOptions() { - private TimeSpan _pollingTimeout; - private TimeSpan _visibilityTimeout; - private int _numberOfMessagesToTakePerRequest; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// 10 - /// - /// - /// - /// 20 seconds - /// - /// - /// - /// true - /// - /// - /// - /// true - /// - /// - /// - /// false - /// - /// - /// - public AmazonMessageReceiveOptions() - { - NumberOfMessagesToTakePerRequest = AmazonMessageOptions.MaxNumberOfMessages; - PollingTimeout = TimeSpan.FromSeconds(AmazonMessageOptions.MaxPollingWaitTimeInSeconds); - VisibilityTimeout = TimeSpan.FromSeconds(AmazonMessageOptions.DefaultVisibilityTimeoutInSeconds); - AssumeMessageProcessed = true; - RemoveProcessedMessages = true; - UseApproximateNumberOfMessages = false; - } + NumberOfMessagesToTakePerRequest = AmazonMessageOptions.MaxNumberOfMessages; + PollingTimeout = TimeSpan.FromSeconds(AmazonMessageOptions.MaxPollingWaitTimeInSeconds); + VisibilityTimeout = TimeSpan.FromSeconds(AmazonMessageOptions.DefaultVisibilityTimeoutInSeconds); + AssumeMessageProcessed = true; + RemoveProcessedMessages = true; + UseApproximateNumberOfMessages = false; + } - /// - /// Gets or sets the number of messages to return per request. Default is 10. - /// - /// The number of messages to return per request. - /// Max. allowed value is limited to . - public int NumberOfMessagesToTakePerRequest - { - get => _numberOfMessagesToTakePerRequest; - set => _numberOfMessagesToTakePerRequest = Math.Clamp(value, 1, AmazonMessageOptions.MaxNumberOfMessages); - } + /// + /// Gets or sets the number of messages to return per request. Default is 10. + /// + /// The number of messages to return per request. + /// Max. allowed value is limited to . + public int NumberOfMessagesToTakePerRequest + { + get => _numberOfMessagesToTakePerRequest; + set => _numberOfMessagesToTakePerRequest = Math.Clamp(value, 1, AmazonMessageOptions.MaxNumberOfMessages); + } - /// - /// Gets or sets the polling timeout per request. Default is 20 seconds. - /// - /// The polling timeout per request. - /// Max. allowed value is limited to . - public TimeSpan PollingTimeout - { - get => _pollingTimeout; - set => _pollingTimeout = TimeSpan.FromSeconds(Math.Clamp(value.TotalSeconds, 0, AmazonMessageOptions.MaxPollingWaitTimeInSeconds)); - } + /// + /// Gets or sets the polling timeout per request. Default is 20 seconds. + /// + /// The polling timeout per request. + /// Max. allowed value is limited to . + public TimeSpan PollingTimeout + { + get => _pollingTimeout; + set => _pollingTimeout = TimeSpan.FromSeconds(Math.Clamp(value.TotalSeconds, 0, AmazonMessageOptions.MaxPollingWaitTimeInSeconds)); + } - /// - /// Gets or sets the visibility timeout per request. Default is 30 seconds. - /// - /// The visibility timeout per request. - /// Max. allowed value is limited to . - public TimeSpan VisibilityTimeout - { - get => _visibilityTimeout; - set => _visibilityTimeout = TimeSpan.FromSeconds(Math.Clamp(value.TotalSeconds, 0, AmazonMessageOptions.MaxVisibilityTimeoutInSeconds)); - } + /// + /// Gets or sets the visibility timeout per request. Default is 30 seconds. + /// + /// The visibility timeout per request. + /// Max. allowed value is limited to . + public TimeSpan VisibilityTimeout + { + get => _visibilityTimeout; + set => _visibilityTimeout = TimeSpan.FromSeconds(Math.Clamp(value.TotalSeconds, 0, AmazonMessageOptions.MaxVisibilityTimeoutInSeconds)); + } - /// - /// Gets or sets a value indicating whether each message should be automatically assumed processed. Default is true. - /// - /// true if each message should be assumed automatically processed; otherwise, false. - public bool AssumeMessageProcessed { get; set; } + /// + /// Gets or sets a value indicating whether each message should be automatically assumed processed. Default is true. + /// + /// true if each message should be assumed automatically processed; otherwise, false. + public bool AssumeMessageProcessed { get; set; } - /// - /// Gets or sets a value indicating whether processed messages should be removed after processing. Default is true. - /// - /// true if processed messages should be removed after processing; otherwise, false. - public bool RemoveProcessedMessages { get; set; } + /// + /// Gets or sets a value indicating whether processed messages should be removed after processing. Default is true. + /// + /// true if processed messages should be removed after processing; otherwise, false. + public bool RemoveProcessedMessages { get; set; } - /// - /// Gets or sets a value indicating whether to retrieve the approximate number of messages for the queue in question. Default is false. - /// - /// true to retrieve the approximate number of messages for the queue in question; otherwise, false. - public bool UseApproximateNumberOfMessages { get; set; } - } + /// + /// Gets or sets a value indicating whether to retrieve the approximate number of messages for the queue in question. Default is false. + /// + /// true to retrieve the approximate number of messages for the queue in question; otherwise, false. + public bool UseApproximateNumberOfMessages { get; set; } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/AmazonQueue.cs b/src/Savvyio.Extensions.SimpleQueueService/AmazonQueue.cs index 8b672370..78c98654 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/AmazonQueue.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/AmazonQueue.cs @@ -1,48 +1,47 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon.Threading; using Savvyio.Messaging; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +/// +/// Represents the base class from which all implementations in need of queue capabilities should derive. +/// +/// The type of the model to handle. +/// +/// +public abstract class AmazonQueue : AmazonMessage, IPointToPointChannel where TRequest : IRequest { /// - /// Represents the base class from which all implementations in need of queue capabilities should derive. + /// Initializes a new instance of the class. /// - /// The type of the model to handle. - /// - /// - public abstract class AmazonQueue : AmazonMessage, IPointToPointChannel where TRequest : IRequest + /// The that is used when converting models to messages. + /// The configured . + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + protected AmazonQueue(IMarshaller marshaller, AmazonMessageOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The that is used when converting models to messages. - /// The configured . - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - protected AmazonQueue(IMarshaller marshaller, AmazonMessageOptions options) : base(marshaller, options) - { - } + } - /// - /// Sends the specified asynchronous using Point-to-Point Channel/P2P MEP. - /// - /// The messages to send. - /// The which may be configured. - /// A task that represents the asynchronous operation. - public abstract Task SendAsync(IEnumerable> messages, Action setup = null); + /// + /// Sends the specified asynchronous using Point-to-Point Channel/P2P MEP. + /// + /// The messages to send. + /// The which may be configured. + /// A task that represents the asynchronous operation. + public abstract Task SendAsync(IEnumerable> messages, Action setup = null); - /// - /// Receive one or more message(s) asynchronous using Point-to-Point Channel/P2P MEP. - /// - /// The which may be configured. - /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . - public abstract IAsyncEnumerable> ReceiveAsync(Action setup = null); - } + /// + /// Receive one or more message(s) asynchronous using Point-to-Point Channel/P2P MEP. + /// + /// The which may be configured. + /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . + public abstract IAsyncEnumerable> ReceiveAsync(Action setup = null); } diff --git a/src/Savvyio.Extensions.SimpleQueueService/AmazonResourceNameOptions.cs b/src/Savvyio.Extensions.SimpleQueueService/AmazonResourceNameOptions.cs index 087ce66e..24cd2caa 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/AmazonResourceNameOptions.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/AmazonResourceNameOptions.cs @@ -1,79 +1,78 @@ -using System; +using System; using System.Globalization; using Amazon; using Cuemon; using Cuemon.Configuration; using Cuemon.Extensions; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +/// +/// Configuration options that is related to Amazon Resource Name (ARN). +/// +/// https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html +public class AmazonResourceNameOptions : IValidatableParameterObject { /// - /// Configuration options that is related to Amazon Resource Name (ARN). + /// Gets or sets the default partition in which the resource is located to embed in the ARN. Default is "aws". /// - /// https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html - public class AmazonResourceNameOptions : IValidatableParameterObject - { - /// - /// Gets or sets the default partition in which the resource is located to embed in the ARN. Default is "aws". - /// - /// The default partition in which the resource is located to embed in the ARN. - public static string DefaultPartition { get; set; } = RegionEndpoint.EUWest1.PartitionName; + /// The default partition in which the resource is located to embed in the ARN. + public static string DefaultPartition { get; set; } = RegionEndpoint.EUWest1.PartitionName; - /// - /// Gets or sets the default region code to embed in the ARN. Default is "eu-west-1". - /// - /// The default region code to embed in the ARN. - public static string DefaultRegion { get; set; } = RegionEndpoint.EUWest1.SystemName; + /// + /// Gets or sets the default region code to embed in the ARN. Default is "eu-west-1". + /// + /// The default region code to embed in the ARN. + public static string DefaultRegion { get; set; } = RegionEndpoint.EUWest1.SystemName; - /// - /// Gets or sets the default ID of the AWS account that owns the resource (without the hyphens) to embed in the ARN. Default is "000000000000". - /// - /// The default ID of the AWS account that owns the resource to embed in the ARN. - /// For example, 123456789012. - public static string DefaultAccountId { get; set; } = "000000000000"; + /// + /// Gets or sets the default ID of the AWS account that owns the resource (without the hyphens) to embed in the ARN. Default is "000000000000". + /// + /// The default ID of the AWS account that owns the resource to embed in the ARN. + /// For example, 123456789012. + public static string DefaultAccountId { get; set; } = "000000000000"; - /// - /// Initializes a new instance of the class. - /// - public AmazonResourceNameOptions() - { - Partition = DefaultPartition; - Region = DefaultRegion; - AccountId = DefaultAccountId; - } + /// + /// Initializes a new instance of the class. + /// + public AmazonResourceNameOptions() + { + Partition = DefaultPartition; + Region = DefaultRegion; + AccountId = DefaultAccountId; + } - /// - /// Gets or sets the partition in which the resource is located to embed in the ARN. - /// - /// The partition to embed in the ARN. - public string Partition { get; set; } + /// + /// Gets or sets the partition in which the resource is located to embed in the ARN. + /// + /// The partition to embed in the ARN. + public string Partition { get; set; } - /// - /// Gets or sets the default region code to embed in the ARN. - /// - /// The region code to embed in the ARN. - public string Region { get; set; } + /// + /// Gets or sets the default region code to embed in the ARN. + /// + /// The region code to embed in the ARN. + public string Region { get; set; } - /// - /// Gets or sets the default ID of the AWS account that owns the resource (without the hyphens) to embed in the ARN. - /// - /// The ID of the AWS account that owns the resource to embed in the ARN. - public string AccountId { get; set; } + /// + /// Gets or sets the default ID of the AWS account that owns the resource (without the hyphens) to embed in the ARN. + /// + /// The ID of the AWS account that owns the resource to embed in the ARN. + public string AccountId { get; set; } - /// - /// Determines whether the public read-write properties of this instance are in a valid state. - /// - /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. - /// - /// cannot be null, empty or consist only of white-space characters - or - - /// cannot be null, empty or consist only of white-space characters - or - - /// cannot be null, have a length different from 12 or not be parseable to a numeric value. - /// - public void ValidateOptions() - { - Validator.ThrowIfInvalidState(Partition.IsNullOrWhiteSpace()); - Validator.ThrowIfInvalidState(Region.IsNullOrWhiteSpace()); - Validator.ThrowIfInvalidState(AccountId.IsNullOrWhiteSpace() || AccountId.Length != 12 || !AccountId.IsNumeric(NumberStyles.Integer, CultureInfo.InvariantCulture)); - } + /// + /// Determines whether the public read-write properties of this instance are in a valid state. + /// + /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. + /// + /// cannot be null, empty or consist only of white-space characters - or - + /// cannot be null, empty or consist only of white-space characters - or - + /// cannot be null, have a length different from 12 or not be parseable to a numeric value. + /// + public void ValidateOptions() + { + Validator.ThrowIfInvalidState(Partition.IsNullOrWhiteSpace()); + Validator.ThrowIfInvalidState(Region.IsNullOrWhiteSpace()); + Validator.ThrowIfInvalidState(AccountId.IsNullOrWhiteSpace() || AccountId.Length != 12 || !AccountId.IsNumeric(NumberStyles.Integer, CultureInfo.InvariantCulture)); } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/ClientConfigExtensions.cs b/src/Savvyio.Extensions.SimpleQueueService/ClientConfigExtensions.cs index daee311c..2bc45221 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/ClientConfigExtensions.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/ClientConfigExtensions.cs @@ -1,45 +1,44 @@ -using System.Linq; +using System.Linq; using Amazon.Runtime; using Amazon.SimpleNotificationService; using Amazon.SQS; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +/// +/// Extension methods for the base class. +/// +public static class ClientConfigExtensions { /// - /// Extension methods for the base class. + /// Evaluates whether the specified > is valid, e.g. if it has a length of 2 and each element is of type or . /// - public static class ClientConfigExtensions + /// The array of to extend. + /// true if the specified configurations is valid; otherwise, false. + public static bool IsValid(this ClientConfig[] configurations) { - /// - /// Evaluates whether the specified > is valid, e.g. if it has a length of 2 and each element is of type or . - /// - /// The array of to extend. - /// true if the specified configurations is valid; otherwise, false. - public static bool IsValid(this ClientConfig[] configurations) - { - return configurations != null && - configurations.Length == 2 && - configurations.All(config => config is AmazonSQSConfig || config is AmazonSimpleNotificationServiceConfig); - } + return configurations != null && + configurations.Length == 2 && + configurations.All(config => config is AmazonSQSConfig || config is AmazonSimpleNotificationServiceConfig); + } - /// - /// Resolves an instance of from the specified . - /// - /// The array of to extend. - /// A configured instance of . - public static AmazonSQSConfig SimpleQueueService(this ClientConfig[] configurations) - { - return configurations.SingleOrDefault(config => config.GetType() == typeof(AmazonSQSConfig)) as AmazonSQSConfig; - } + /// + /// Resolves an instance of from the specified . + /// + /// The array of to extend. + /// A configured instance of . + public static AmazonSQSConfig SimpleQueueService(this ClientConfig[] configurations) + { + return configurations.SingleOrDefault(config => config.GetType() == typeof(AmazonSQSConfig)) as AmazonSQSConfig; + } - /// - /// Resolves an instance of from the specified . - /// - /// The array of to extend. - /// A configured instance of . - public static AmazonSimpleNotificationServiceConfig SimpleNotificationService(this ClientConfig[] configurations) - { - return configurations.SingleOrDefault(config => config.GetType() == typeof(AmazonSimpleNotificationServiceConfig)) as AmazonSimpleNotificationServiceConfig; - } + /// + /// Resolves an instance of from the specified . + /// + /// The array of to extend. + /// A configured instance of . + public static AmazonSimpleNotificationServiceConfig SimpleNotificationService(this ClientConfig[] configurations) + { + return configurations.SingleOrDefault(config => config.GetType() == typeof(AmazonSimpleNotificationServiceConfig)) as AmazonSimpleNotificationServiceConfig; } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/Commands/AmazonCommandQueue.cs b/src/Savvyio.Extensions.SimpleQueueService/Commands/AmazonCommandQueue.cs index 52d1d7c0..51dc798f 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/Commands/AmazonCommandQueue.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/Commands/AmazonCommandQueue.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -14,93 +14,92 @@ using Savvyio.Diagnostics; using Savvyio.Messaging; -namespace Savvyio.Extensions.SimpleQueueService.Commands +namespace Savvyio.Extensions.SimpleQueueService.Commands; + +/// +/// Provides a default implementation of the class tailored for messages holding an implementation. +/// +/// +public class AmazonCommandQueue : AmazonQueue, IHealthCheckProvider { /// - /// Provides a default implementation of the class tailored for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - public class AmazonCommandQueue : AmazonQueue, IHealthCheckProvider + /// The that is used when converting implementations to messages. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public AmazonCommandQueue(IMarshaller marshaller, AmazonCommandQueueOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The that is used when converting implementations to messages. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public AmazonCommandQueue(IMarshaller marshaller, AmazonCommandQueueOptions options) : base(marshaller, options) - { - } + } - /// - /// Sends the specified whose generic type argument is asynchronous using Point-to-Point Channel/P2P MEP. - /// - /// The enclosed messages to send. - /// The which may be configured. - /// A task that represents the asynchronous operation. - /// - /// cannot be null. - /// - public override async Task SendAsync(IEnumerable> messages, Action setup = null) + /// + /// Sends the specified whose generic type argument is asynchronous using Point-to-Point Channel/P2P MEP. + /// + /// The enclosed messages to send. + /// The which may be configured. + /// A task that represents the asynchronous operation. + /// + /// cannot be null. + /// + public override async Task SendAsync(IEnumerable> messages, Action setup = null) + { + Validator.ThrowIfNull(messages); + var options = setup.Configure(); + var batches = messages.ToPartitioner(AmazonMessageOptions.MaxNumberOfMessages); + var tasks = new List(); + while (batches.HasPartitions) { - Validator.ThrowIfNull(messages); - var options = setup.Configure(); - var batches = messages.ToPartitioner(AmazonMessageOptions.MaxNumberOfMessages); - var tasks = new List(); - while (batches.HasPartitions) - { - var sqs = CreateSimpleQueueServiceClient(); + var sqs = CreateSimpleQueueServiceClient(); - var batchRequest = new SendMessageBatchRequest + var batchRequest = new SendMessageBatchRequest + { + QueueUrl = Options.SourceQueue.OriginalString, + Entries = new List(batches.Select(message => new SendMessageBatchRequestEntry { - QueueUrl = Options.SourceQueue.OriginalString, - Entries = new List(batches.Select(message => new SendMessageBatchRequestEntry - { - Id = message.Id, - MessageGroupId = UseFirstInFirstOut ? message.Source : null, - MessageDeduplicationId = UseFirstInFirstOut ? message.Id : null, - MessageBody = Marshaller.Serialize(message).ToEncodedString(), - MessageAttributes = new Dictionary + Id = message.Id, + MessageGroupId = UseFirstInFirstOut ? message.Source : null, + MessageDeduplicationId = UseFirstInFirstOut ? message.Id : null, + MessageBody = Marshaller.Serialize(message).ToEncodedString(), + MessageAttributes = new Dictionary + { { + MessageTypeAttributeKey, new MessageAttributeValue { - MessageTypeAttributeKey, new MessageAttributeValue - { - DataType = "String", - StringValue = message.GetType().ToFullNameIncludingAssemblyName() - } + DataType = "String", + StringValue = message.GetType().ToFullNameIncludingAssemblyName() } } - })) - }; - tasks.Add(sqs.SendMessageBatchAsync(batchRequest, options.CancellationToken)); - } - await Task.WhenAll(tasks).ConfigureAwait(false); + } + })) + }; + tasks.Add(sqs.SendMessageBatchAsync(batchRequest, options.CancellationToken)); } + await Task.WhenAll(tasks).ConfigureAwait(false); + } - /// - /// Receive one or more command(s) asynchronous using Point-to-Point Channel/P2P MEP. - /// - /// The which may be configured. - /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . - public override IAsyncEnumerable> ReceiveAsync(Action setup = null) - { - var options = setup.Configure(); - return RetrieveMessagesAsync(options.CancellationToken); - } + /// + /// Receive one or more command(s) asynchronous using Point-to-Point Channel/P2P MEP. + /// + /// The which may be configured. + /// A task that represents the asynchronous operation. The task result contains a sequence of whose generic type argument is . + public override IAsyncEnumerable> ReceiveAsync(Action setup = null) + { + var options = setup.Configure(); + return RetrieveMessagesAsync(options.CancellationToken); + } - /// - /// Returns a new client instance configured for health probing of the underlying Amazon SQS service. - /// - /// An client instance used to probe the health status of the queue service. - public IAmazonSQS GetHealthCheckTarget() - { - return CreateSimpleQueueServiceClient(); - } + /// + /// Returns a new client instance configured for health probing of the underlying Amazon SQS service. + /// + /// An client instance used to probe the health status of the queue service. + public IAmazonSQS GetHealthCheckTarget() + { + return CreateSimpleQueueServiceClient(); } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/Commands/AmazonCommandQueueOptions.cs b/src/Savvyio.Extensions.SimpleQueueService/Commands/AmazonCommandQueueOptions.cs index 8382f166..dac7f7f5 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/Commands/AmazonCommandQueueOptions.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/Commands/AmazonCommandQueueOptions.cs @@ -1,10 +1,9 @@ -namespace Savvyio.Extensions.SimpleQueueService.Commands +namespace Savvyio.Extensions.SimpleQueueService.Commands; + +/// +/// Configuration options for . +/// +/// +public class AmazonCommandQueueOptions : AmazonMessageOptions { - /// - /// Configuration options for . - /// - /// - public class AmazonCommandQueueOptions : AmazonMessageOptions - { - } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/EventDriven/AmazonEventBus.cs b/src/Savvyio.Extensions.SimpleQueueService/EventDriven/AmazonEventBus.cs index a1bb1263..45dbff80 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/EventDriven/AmazonEventBus.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/EventDriven/AmazonEventBus.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -13,114 +13,113 @@ using Savvyio.EventDriven; using Savvyio.Messaging; -namespace Savvyio.Extensions.SimpleQueueService.EventDriven +namespace Savvyio.Extensions.SimpleQueueService.EventDriven; + +/// +/// Provides a default implementation of the class tailored for messages holding an implementation. +/// +/// +public class AmazonEventBus : AmazonBus, IHealthCheckProvider { /// - /// Provides a default implementation of the class tailored for messages holding an implementation. + /// Initializes a new instance of the class. /// - /// - public class AmazonEventBus : AmazonBus, IHealthCheckProvider + /// The that is used when converting implementations to messages. + /// The used to configure this instance. + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// are not in a valid state. + /// + public AmazonEventBus(IMarshaller marshaller, AmazonEventBusOptions options) : base(marshaller, options) { - /// - /// Initializes a new instance of the class. - /// - /// The that is used when converting implementations to messages. - /// The used to configure this instance. - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// are not in a valid state. - /// - public AmazonEventBus(IMarshaller marshaller, AmazonEventBusOptions options) : base(marshaller, options) - { - } + } - /// - /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The event to publish. - /// The which may be configured. - /// A that represents the asynchronous operation. - public override async Task PublishAsync(IMessage @event, Action setup = null) + /// + /// Publishes the specified asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. + /// + /// The event to publish. + /// The which may be configured. + /// A that represents the asynchronous operation. + public override async Task PublishAsync(IMessage @event, Action setup = null) + { + var options = setup.Configure(); + var sns = CreateSimpleNotificationServiceClient(); + var request = new PublishRequest { - var options = setup.Configure(); - var sns = CreateSimpleNotificationServiceClient(); - var request = new PublishRequest + TopicArn = @event.Source, + Message = await Marshaller.Serialize(@event).ToEncodedStringAsync().ConfigureAwait(false), + MessageGroupId = UseFirstInFirstOut ? @event.Source : null, + MessageDeduplicationId = UseFirstInFirstOut ? @event.Id : null, + MessageAttributes = new Dictionary { - TopicArn = @event.Source, - Message = await Marshaller.Serialize(@event).ToEncodedStringAsync().ConfigureAwait(false), - MessageGroupId = UseFirstInFirstOut ? @event.Source : null, - MessageDeduplicationId = UseFirstInFirstOut ? @event.Id : null, - MessageAttributes = new Dictionary { + MessageTypeAttributeKey, new MessageAttributeValue { - MessageTypeAttributeKey, new MessageAttributeValue - { - DataType = "String", - StringValue = @event.GetType().ToFullNameIncludingAssemblyName() - } + DataType = "String", + StringValue = @event.GetType().ToFullNameIncludingAssemblyName() } } - }; - await sns.PublishAsync(request, options.CancellationToken).ConfigureAwait(false); - } + } + }; + await sns.PublishAsync(request, options.CancellationToken).ConfigureAwait(false); + } - /// - /// Subscribe to one or more message(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. - /// - /// The function delegate that will handle the message. - /// The which may be configured. - /// A that represents the asynchronous operation. - public override async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) + /// + /// Subscribe to one or more message(s) asynchronous using Publish-Subscribe Channel/Pub-Sub MEP. + /// + /// The function delegate that will handle the message. + /// The which may be configured. + /// A that represents the asynchronous operation. + public override async Task SubscribeAsync(Func, CancellationToken, Task> asyncHandler, Action setup = null) + { + var options = setup.Configure(); + await Condition.FlipFlopAsync(options.ThrowIfCancellationWasRequested, () => InvokeHandlerAsync(asyncHandler, options.CancellationToken), async () => { - var options = setup.Configure(); - await Condition.FlipFlopAsync(options.ThrowIfCancellationWasRequested, () => InvokeHandlerAsync(asyncHandler, options.CancellationToken), async () => + try { - try - { - await InvokeHandlerAsync(asyncHandler, options.CancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) when (options.CancellationToken.IsCancellationRequested) - { - // ThrowIfCancellationWasRequested was set to false (default) - } - }).ConfigureAwait(false); - } + await InvokeHandlerAsync(asyncHandler, options.CancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) when (options.CancellationToken.IsCancellationRequested) + { + // ThrowIfCancellationWasRequested was set to false (default) + } + }).ConfigureAwait(false); + } - private async Task InvokeHandlerAsync(Func, CancellationToken, Task> asyncHandler, CancellationToken cancellationToken) + private async Task InvokeHandlerAsync(Func, CancellationToken, Task> asyncHandler, CancellationToken cancellationToken) + { + var hasMessages = true; + while (hasMessages) { - var hasMessages = true; - while (hasMessages) + hasMessages = false; + await foreach (var message in RetrieveMessagesAsync(cancellationToken).ConfigureAwait(false)) { - hasMessages = false; - await foreach (var message in RetrieveMessagesAsync(cancellationToken).ConfigureAwait(false)) - { - hasMessages = true; - await asyncHandler.Invoke(message, cancellationToken).ConfigureAwait(false); - } + hasMessages = true; + await asyncHandler.Invoke(message, cancellationToken).ConfigureAwait(false); } } + } - /// - /// Returns a new client instance configured for health probing of the underlying Amazon SNS service. - /// - /// An client instance used to probe the health status of the notification service. - public IAmazonSimpleNotificationService GetHealthCheckTarget() - { - return CreateSimpleNotificationServiceClient(); - } + /// + /// Returns a new client instance configured for health probing of the underlying Amazon SNS service. + /// + /// An client instance used to probe the health status of the notification service. + public IAmazonSimpleNotificationService GetHealthCheckTarget() + { + return CreateSimpleNotificationServiceClient(); + } - /// - /// Creates the AWS SNS client used by publish operations. - /// - /// An client configured from . - protected virtual IAmazonSimpleNotificationService CreateSimpleNotificationServiceClient() - { - return Options.ClientConfigurations.IsValid() - ? new AmazonSimpleNotificationServiceClient(Options.Credentials, Options.ClientConfigurations.SimpleNotificationService()) - : new AmazonSimpleNotificationServiceClient(Options.Credentials, Options.Endpoint); - } + /// + /// Creates the AWS SNS client used by publish operations. + /// + /// An client configured from . + protected virtual IAmazonSimpleNotificationService CreateSimpleNotificationServiceClient() + { + return Options.ClientConfigurations.IsValid() + ? new AmazonSimpleNotificationServiceClient(Options.Credentials, Options.ClientConfigurations.SimpleNotificationService()) + : new AmazonSimpleNotificationServiceClient(Options.Credentials, Options.Endpoint); } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/EventDriven/AmazonEventBusOptions.cs b/src/Savvyio.Extensions.SimpleQueueService/EventDriven/AmazonEventBusOptions.cs index 3582e78a..43804561 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/EventDriven/AmazonEventBusOptions.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/EventDriven/AmazonEventBusOptions.cs @@ -1,10 +1,9 @@ -namespace Savvyio.Extensions.SimpleQueueService.EventDriven +namespace Savvyio.Extensions.SimpleQueueService.EventDriven; + +/// +/// Configuration options for . +/// +/// +public class AmazonEventBusOptions : AmazonMessageOptions { - /// - /// Configuration options for . - /// - /// - public class AmazonEventBusOptions : AmazonMessageOptions - { - } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/EventDriven/StringExtensions.cs b/src/Savvyio.Extensions.SimpleQueueService/EventDriven/StringExtensions.cs index 512b912e..dc822278 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/EventDriven/StringExtensions.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/EventDriven/StringExtensions.cs @@ -1,26 +1,25 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions; -namespace Savvyio.Extensions.SimpleQueueService.EventDriven +namespace Savvyio.Extensions.SimpleQueueService.EventDriven; + +/// +/// Extension methods for the class. +/// +public static class StringExtensions { /// - /// Extension methods for the class. + /// Converts the specified to its equivalent AWS ARN representation. /// - public static class StringExtensions + /// The to extend. + /// The which may be configured. + /// A that corresponds to and . + /// + public static Uri ToSnsUri(this string source, Action setup = null) { - /// - /// Converts the specified to its equivalent AWS ARN representation. - /// - /// The to extend. - /// The which may be configured. - /// A that corresponds to and . - /// - public static Uri ToSnsUri(this string source, Action setup = null) - { - Validator.ThrowIfInvalidConfigurator(setup, out var options); - var arn = $"arn:{options.Partition}:sns:{options.Region}:{options.AccountId}:{source}"; - return arn.ToUri(); - } + Validator.ThrowIfInvalidConfigurator(setup, out var options); + var arn = $"arn:{options.Partition}:sns:{options.Region}:{options.AccountId}:{source}"; + return arn.ToUri(); } } diff --git a/src/Savvyio.Extensions.SimpleQueueService/GlobalSuppressions.cs b/src/Savvyio.Extensions.SimpleQueueService/GlobalSuppressions.cs index e5e0b43c..e8cf4551 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/GlobalSuppressions.cs +++ b/src/Savvyio.Extensions.SimpleQueueService/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. diff --git a/src/Savvyio.Extensions.SimpleQueueService/Savvyio.Extensions.SimpleQueueService.csproj b/src/Savvyio.Extensions.SimpleQueueService/Savvyio.Extensions.SimpleQueueService.csproj index 538ecff1..78b5961b 100644 --- a/src/Savvyio.Extensions.SimpleQueueService/Savvyio.Extensions.SimpleQueueService.csproj +++ b/src/Savvyio.Extensions.SimpleQueueService/Savvyio.Extensions.SimpleQueueService.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O core assemblies with support for Amazon Simple Queue Service (AWS SQS) paired with Amazon Simple Notification Service (AWS SNS). diff --git a/src/Savvyio.Extensions.Text.Json/Bootstrapper.cs b/src/Savvyio.Extensions.Text.Json/Bootstrapper.cs index 732a75db..b803def8 100644 --- a/src/Savvyio.Extensions.Text.Json/Bootstrapper.cs +++ b/src/Savvyio.Extensions.Text.Json/Bootstrapper.cs @@ -1,32 +1,31 @@ using Cuemon.Extensions.Text.Json.Formatters; using System.Threading; -namespace Savvyio.Extensions.Text.Json +namespace Savvyio.Extensions.Text.Json; + +internal static class Bootstrapper { - internal static class Bootstrapper - { - private static readonly Lock PadLock = new(); - private static bool _initialized; + private static readonly Lock PadLock = new(); + private static bool _initialized; - internal static void Initialize() + internal static void Initialize() + { + if (!_initialized) { - if (!_initialized) + lock (PadLock) { - lock (PadLock) + if (!_initialized) { - if (!_initialized) + _initialized = true; + JsonFormatterOptions.DefaultConverters += list => { - _initialized = true; - JsonFormatterOptions.DefaultConverters += list => - { - list.AddRequestConverter(); - list.AddMetadataDictionaryConverter(); - list.AddMessageConverter(); - list.AddSingleValueObjectConverter(); - list.AddDateTimeConverter(); - list.AddDateTimeOffsetConverter(); - }; - } + list.AddRequestConverter(); + list.AddMetadataDictionaryConverter(); + list.AddMessageConverter(); + list.AddSingleValueObjectConverter(); + list.AddDateTimeConverter(); + list.AddDateTimeOffsetConverter(); + }; } } } diff --git a/src/Savvyio.Extensions.Text.Json/Converters/DateTimeConverter.cs b/src/Savvyio.Extensions.Text.Json/Converters/DateTimeConverter.cs index f07082de..1a33a328 100644 --- a/src/Savvyio.Extensions.Text.Json/Converters/DateTimeConverter.cs +++ b/src/Savvyio.Extensions.Text.Json/Converters/DateTimeConverter.cs @@ -1,45 +1,44 @@ -using System; +using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +/// +/// Converts a to or from JSON using ISO8601. +/// +public class DateTimeConverter : JsonConverter { /// - /// Converts a to or from JSON using ISO8601. + /// Determines whether the specified type can be converted. /// - public class DateTimeConverter : JsonConverter + /// The type to compare against. + /// if the type can be converted; otherwise, . + public override bool CanConvert(Type typeToConvert) { - /// - /// Determines whether the specified type can be converted. - /// - /// The type to compare against. - /// if the type can be converted; otherwise, . - public override bool CanConvert(Type typeToConvert) - { - return typeToConvert == typeof(DateTime); - } + return typeToConvert == typeof(DateTime); + } - /// - /// Reads and converts the JSON to . - /// - /// The reader. - /// The type to convert. - /// An object that specifies serialization options to use. - /// The converted value. - public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return reader.GetDateTime(); - } + /// + /// Reads and converts the JSON to . + /// + /// The reader. + /// The type to convert. + /// An object that specifies serialization options to use. + /// The converted value. + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.GetDateTime(); + } - /// - /// Writes a specified as JSON. - /// - /// The writer to write to. - /// The value to convert to JSON. - /// An object that specifies serialization options to use. - public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString("O")); - } + /// + /// Writes a specified as JSON. + /// + /// The writer to write to. + /// The value to convert to JSON. + /// An object that specifies serialization options to use. + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString("O")); } } diff --git a/src/Savvyio.Extensions.Text.Json/Converters/DateTimeOffsetConverter.cs b/src/Savvyio.Extensions.Text.Json/Converters/DateTimeOffsetConverter.cs index 0cdebda0..edabc1b7 100644 --- a/src/Savvyio.Extensions.Text.Json/Converters/DateTimeOffsetConverter.cs +++ b/src/Savvyio.Extensions.Text.Json/Converters/DateTimeOffsetConverter.cs @@ -1,48 +1,47 @@ -using System; +using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +/// +/// Converts a to or from JSON using ISO8601. +/// +public class DateTimeOffsetConverter : JsonConverter { + /// - /// Converts a to or from JSON using ISO8601. + /// Determines whether the specified type can be converted. /// - public class DateTimeOffsetConverter : JsonConverter + /// The type to compare against. + /// if the type can be converted; otherwise, . + public override bool CanConvert(Type typeToConvert) { - - /// - /// Determines whether the specified type can be converted. - /// - /// The type to compare against. - /// if the type can be converted; otherwise, . - public override bool CanConvert(Type typeToConvert) - { - return typeToConvert == typeof(DateTime); - } + return typeToConvert == typeof(DateTime); + } - /// - /// Reads and converts the JSON to . - /// - /// The reader. - /// The type to convert. - /// An object that specifies serialization options to use. - /// The converted value. - public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return reader.GetDateTimeOffset(); - } + /// + /// Reads and converts the JSON to . + /// + /// The reader. + /// The type to convert. + /// An object that specifies serialization options to use. + /// The converted value. + public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.GetDateTimeOffset(); + } - /// - /// Writes a specified as JSON. - /// - /// The writer to write to. - /// The value to convert to JSON. - /// An object that specifies serialization options to use. - public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString("O")); - } + /// + /// Writes a specified as JSON. + /// + /// The writer to write to. + /// The value to convert to JSON. + /// An object that specifies serialization options to use. + public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString("O")); } } diff --git a/src/Savvyio.Extensions.Text.Json/Converters/MessageConverter.cs b/src/Savvyio.Extensions.Text.Json/Converters/MessageConverter.cs index 06dbfae5..0b2a1afb 100644 --- a/src/Savvyio.Extensions.Text.Json/Converters/MessageConverter.cs +++ b/src/Savvyio.Extensions.Text.Json/Converters/MessageConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -7,186 +7,185 @@ using Cuemon.Extensions; using Cuemon.Extensions.Reflection; using Cuemon.Extensions.Text.Json; +using Cuemon.Reflection; using Savvyio.EventDriven; using Savvyio.EventDriven.Messaging.CloudEvents; using Savvyio.EventDriven.Messaging.CloudEvents.Cryptography; using Savvyio.Messaging; using Savvyio.Messaging.Cryptography; -using Savvyio.Reflection; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +/// +/// Converts an (or derived) to or from JSON. +/// +/// +public class MessageConverter : JsonConverterFactory { + internal static readonly Lazy> CloudEventTypes = new(() => AssemblyContext.GetCurrentDomainAssemblies(o => o.ExcludedAssemblies.Add(typeof(IMessage<>).Assembly)).SelectMany(a => a.DefinedTypes.Where(ti => ti.HasInterfaces(typeof(ICloudEvent<>)) && + ti is { IsAbstract: false, IsInterface: false })).ToList()); + /// - /// Converts an (or derived) to or from JSON. + /// Initializes a new instance of the class. /// - /// - public class MessageConverter : JsonConverterFactory + public MessageConverter() { - internal static readonly Lazy> CloudEventTypes = new(() => AssemblyContext.CurrentDomainAssemblies.SelectMany(a => a.DefinedTypes.Where(ti => ti.HasInterfaces(typeof(ICloudEvent<>)) && - ti is { IsAbstract: false, IsInterface: false })).ToList()); - - /// - /// Initializes a new instance of the class. - /// - public MessageConverter() - { - } + } - /// - /// When overridden in a derived class, determines whether the converter instance can convert the specified object type. - /// - /// The type of the object to check whether it can be converted by this converter instance. - /// if the instance can convert the specified object type; otherwise, . + /// + /// When overridden in a derived class, determines whether the converter instance can convert the specified object type. + /// + /// The type of the object to check whether it can be converted by this converter instance. + /// if the instance can convert the specified object type; otherwise, . public override bool CanConvert(Type typeToConvert) - { - return (typeToConvert.IsGenericType && - typeToConvert.GetGenericTypeDefinition().HasInterfaces(typeof(IMessage<>))); - } + { + return (typeToConvert.IsGenericType && + typeToConvert.GetGenericTypeDefinition().HasInterfaces(typeof(IMessage<>))); + } - /// - /// Creates a converter for a specified type. - /// - /// The type handled by the converter. - /// The serialization options to use. - /// A converter that is compatible with . - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) - { - var requestType = typeToConvert.GetGenericArguments()[0]; - return (JsonConverter)Activator.CreateInstance( - typeof(MessageConverter<>).MakeGenericType(requestType), - BindingFlags.Instance | BindingFlags.Public, - binder: null, - args: null, - culture: null)!; - } + /// + /// Creates a converter for a specified type. + /// + /// The type handled by the converter. + /// The serialization options to use. + /// A converter that is compatible with . + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var requestType = typeToConvert.GetGenericArguments()[0]; + return (JsonConverter)Activator.CreateInstance( + typeof(MessageConverter<>).MakeGenericType(requestType), + BindingFlags.Instance | BindingFlags.Public, + binder: null, + args: null, + culture: null)!; } +} - internal sealed class MessageConverter : JsonConverter> where T : IRequest +internal sealed class MessageConverter : JsonConverter> where T : IRequest +{ + public MessageConverter() { - public MessageConverter() - { - } + } - public override IMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override IMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using (var document = JsonDocument.ParseValue(ref reader)) { - using (var document = JsonDocument.ParseValue(ref reader)) + var idKey = options.PropertyNamingPolicy!.ConvertName(nameof(IMessage.Id)); + var sourceKey = options.PropertyNamingPolicy.ConvertName(nameof(IMessage.Source)); + var timeKey = options.PropertyNamingPolicy.ConvertName(nameof(IMessage.Time)); + var typeKey = options.PropertyNamingPolicy.ConvertName(nameof(IMessage.Type)); + var dataKey = options.PropertyNamingPolicy.ConvertName(nameof(IMessage.Data)); + var metadataKey = options.PropertyNamingPolicy.ConvertName(nameof(IMetadata.Metadata)); + var signatureKey = options.PropertyNamingPolicy.ConvertName(nameof(ISignedMessage.Signature)); + + var id = document.RootElement.GetProperty(idKey).GetString(); + var source = document.RootElement.GetProperty(sourceKey).GetString().ToUri(); + var type = document.RootElement.GetProperty(typeKey).GetString(); + var memberType = typeToConvert.GenericTypeArguments[0]; + var time = document.RootElement.GetProperty(timeKey).GetDateTimeOffset().UtcDateTime; + var data = (T)document.RootElement.GetProperty(dataKey).Deserialize(memberType!, options); + + ApplyMetadata(data, memberType, document.RootElement.GetProperty(dataKey), metadataKey, options); + + var message = new Message(id, source, type, data, time); + + if (typeToConvert.HasInterfaces(typeof(ICloudEvent<>))) { - var idKey = options.PropertyNamingPolicy!.ConvertName(nameof(IMessage.Id)); - var sourceKey = options.PropertyNamingPolicy.ConvertName(nameof(IMessage.Source)); - var timeKey = options.PropertyNamingPolicy.ConvertName(nameof(IMessage.Time)); - var typeKey = options.PropertyNamingPolicy.ConvertName(nameof(IMessage.Type)); - var dataKey = options.PropertyNamingPolicy.ConvertName(nameof(IMessage.Data)); - var metadataKey = options.PropertyNamingPolicy.ConvertName(nameof(IMetadata.Metadata)); - var signatureKey = options.PropertyNamingPolicy.ConvertName(nameof(ISignedMessage.Signature)); - - var id = document.RootElement.GetProperty(idKey).GetString(); - var source = document.RootElement.GetProperty(sourceKey).GetString().ToUri(); - var type = document.RootElement.GetProperty(typeKey).GetString(); - var memberType = typeToConvert.GenericTypeArguments[0]; - var time = document.RootElement.GetProperty(timeKey).GetDateTimeOffset().UtcDateTime; - var data = (T)document.RootElement.GetProperty(dataKey).Deserialize(memberType!, options); - - ApplyMetadata(data, memberType, document.RootElement.GetProperty(dataKey), metadataKey, options); - - var message = new Message(id, source, type, data, time); - - if (typeToConvert.HasInterfaces(typeof(ICloudEvent<>))) - { - var reservedKeys = new[] { idKey, sourceKey, timeKey, typeKey, dataKey, metadataKey, signatureKey }; - return CreateCloudEvent(typeToConvert, message, document.RootElement, signatureKey, reservedKeys, options); - } - - if (typeToConvert.HasInterfaces(typeof(ISignedMessage<>))) - { - var signature = document.RootElement.GetProperty(signatureKey).GetString(); - return new SignedMessage(message, signature); - } - - return message; + var reservedKeys = new[] { idKey, sourceKey, timeKey, typeKey, dataKey, metadataKey, signatureKey }; + return CreateCloudEvent(typeToConvert, message, document.RootElement, signatureKey, reservedKeys, options); } - } - // for unknown reasons, Microsoft does not use the custom converter for IMetadataDictionary here; have to fiddle extra around as seen below .. for the record; this just works with Newtonsoft! - private static void ApplyMetadata(T data, Type memberType, JsonElement dataElement, string metadataKey, JsonSerializerOptions options) - { - if (data is not IMetadata) { return; } - - var md = dataElement.GetProperty(metadataKey).Deserialize(options); - var property = memberType.GetAllProperties().SingleOrDefault(pi => pi.Name == nameof(IMetadata.Metadata)); - if (property == null) { return; } - - if (property.CanWrite) + if (typeToConvert.HasInterfaces(typeof(ISignedMessage<>))) { - property.SetValue(data, md); - } - else - { - memberType.GetAllFields().SingleOrDefault(fi => fi.Name.Contains(nameof(IMetadata.Metadata)))?.SetValue(data, md); + var signature = document.RootElement.GetProperty(signatureKey).GetString(); + return new SignedMessage(message, signature); } + + return message; } + } - private static IMessage CreateCloudEvent(Type typeToConvert, Message message, JsonElement root, string signatureKey, string[] reservedKeys, JsonSerializerOptions options) + // for unknown reasons, Microsoft does not use the custom converter for IMetadataDictionary here; have to fiddle extra around as seen below .. for the record; this just works with Newtonsoft! + private static void ApplyMetadata(T data, Type memberType, JsonElement dataElement, string metadataKey, JsonSerializerOptions options) + { + if (data is not IMetadata) { return; } + + var md = dataElement.GetProperty(metadataKey).Deserialize(options); + var property = memberType.GetAllProperties().SingleOrDefault(pi => pi.Name == nameof(IMetadata.Metadata)); + if (property == null) { return; } + + if (property.CanWrite) + { + property.SetValue(data, md); + } + else { - var specVersionKey = options.PropertyNamingPolicy!.ConvertName(nameof(ICloudEvent.Specversion)); - var requestType = typeToConvert.GetGenericArguments()[0]; - var cloudEventType = MessageConverter.CloudEventTypes.Value.Single(ti => ti.FullName!.StartsWith("Savvyio.EventDriven.Messaging.CloudEvents.CloudEvent", StringComparison.Ordinal)); - var specVersion = root.GetProperty(specVersionKey).GetString(); - var cloudEvent = Activator.CreateInstance(cloudEventType.MakeGenericType(requestType), [message, specVersion]) as IMessage; + memberType.GetAllFields().SingleOrDefault(fi => fi.Name.Contains(nameof(IMetadata.Metadata)))?.SetValue(data, md); + } + } - if (typeToConvert.HasInterfaces(typeof(ISignedCloudEvent<>))) - { - var signedCloudEventType = MessageConverter.CloudEventTypes.Value.Single(ti => ti.FullName!.StartsWith("Savvyio.EventDriven.Messaging.CloudEvents.Cryptography.SignedCloudEvent", StringComparison.Ordinal)); - var signature = root.GetProperty(signatureKey).GetString(); + private static IMessage CreateCloudEvent(Type typeToConvert, Message message, JsonElement root, string signatureKey, string[] reservedKeys, JsonSerializerOptions options) + { + var specVersionKey = options.PropertyNamingPolicy!.ConvertName(nameof(ICloudEvent.Specversion)); + var requestType = typeToConvert.GetGenericArguments()[0]; + var cloudEventType = MessageConverter.CloudEventTypes.Value.Single(ti => ti.FullName!.StartsWith("Savvyio.EventDriven.Messaging.CloudEvents.CloudEvent", StringComparison.Ordinal)); + var specVersion = root.GetProperty(specVersionKey).GetString(); + var cloudEvent = Activator.CreateInstance(cloudEventType.MakeGenericType(requestType), [message, specVersion]) as IMessage; - return Activator.CreateInstance(signedCloudEventType.MakeGenericType(requestType), [cloudEvent, signature]) as IMessage; - } + if (typeToConvert.HasInterfaces(typeof(ISignedCloudEvent<>))) + { + var signedCloudEventType = MessageConverter.CloudEventTypes.Value.Single(ti => ti.FullName!.StartsWith("Savvyio.EventDriven.Messaging.CloudEvents.Cryptography.SignedCloudEvent", StringComparison.Ordinal)); + var signature = root.GetProperty(signatureKey).GetString(); - AddExtensionAttributes(cloudEvent, root, [.. reservedKeys, specVersionKey], options); - return cloudEvent; + return Activator.CreateInstance(signedCloudEventType.MakeGenericType(requestType), [cloudEvent, signature]) as IMessage; } - private static void AddExtensionAttributes(IMessage cloudEvent, JsonElement root, string[] reservedKeys, JsonSerializerOptions options) - { - if (cloudEvent is not IDictionary dictionary) { return; } + AddExtensionAttributes(cloudEvent, root, [.. reservedKeys, specVersionKey], options); + return cloudEvent; + } - foreach (var property in root.EnumerateObject().Where(jp => !reservedKeys.Contains(jp.Name))) - { - dictionary.Add(property.Name, property.Value.Deserialize(property.Value.GetType(), options)); - } + private static void AddExtensionAttributes(IMessage cloudEvent, JsonElement root, string[] reservedKeys, JsonSerializerOptions options) + { + if (cloudEvent is not IDictionary dictionary) { return; } + + foreach (var property in root.EnumerateObject().Where(jp => !reservedKeys.Contains(jp.Name))) + { + dictionary.Add(property.Name, property.Value.Deserialize(property.Value.GetType(), options)); } + } - public override void Write(Utf8JsonWriter writer, IMessage value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, IMessage value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Id)), value.Id); + writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Source)), value.Source); + writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Type)), value.Type); + writer.WritePropertyName(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Time))); + writer.WriteObject(value.Time, options); + writer.WritePropertyName(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Data))); + writer.WriteObject(value.Data, options); + + if (value.GetType().HasInterfaces(typeof(ICloudEvent<>))) { - writer.WriteStartObject(); - writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Id)), value.Id); - writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Source)), value.Source); - writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Type)), value.Type); - writer.WritePropertyName(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Time))); - writer.WriteObject(value.Time, options); - writer.WritePropertyName(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(value.Data))); - writer.WriteObject(value.Data, options); - - if (value.GetType().HasInterfaces(typeof(ICloudEvent<>))) - { - dynamic ce = value; - writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(ICloudEvent.Specversion)), ce.Specversion); - } + dynamic ce = value; + writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(ICloudEvent.Specversion)), ce.Specversion); + } - if (value is ISignedMessage sm) - { - writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(sm.Signature)), sm.Signature); - } + if (value is ISignedMessage sm) + { + writer.WriteString(options.PropertyNamingPolicy.DefaultOrConvertName(nameof(sm.Signature)), sm.Signature); + } - if (value is IDictionary dictionary) + if (value is IDictionary dictionary) + { + foreach (var kvp in dictionary) { - foreach (var kvp in dictionary) - { - writer.WritePropertyName(options.PropertyNamingPolicy.DefaultOrConvertName(kvp.Key)); - writer.WriteObject(kvp.Value, options); - } + writer.WritePropertyName(options.PropertyNamingPolicy.DefaultOrConvertName(kvp.Key)); + writer.WriteObject(kvp.Value, options); } - - writer.WriteEndObject(); } + + writer.WriteEndObject(); } } diff --git a/src/Savvyio.Extensions.Text.Json/Converters/MetadataDictionaryConverter.cs b/src/Savvyio.Extensions.Text.Json/Converters/MetadataDictionaryConverter.cs index 3f3bfa4e..8ee551c1 100644 --- a/src/Savvyio.Extensions.Text.Json/Converters/MetadataDictionaryConverter.cs +++ b/src/Savvyio.Extensions.Text.Json/Converters/MetadataDictionaryConverter.cs @@ -1,92 +1,91 @@ -using System; +using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +/// +/// Converts an to or from JSON. +/// +/// +/// Inspiration taken from this article: https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ +public class MetadataDictionaryConverter : JsonConverter { /// - /// Converts an to or from JSON. + /// Determines whether the specified type can be converted. /// - /// - /// Inspiration taken from this article: https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - public class MetadataDictionaryConverter : JsonConverter + /// The type to compare against. + /// if the type can be converted; otherwise, . + public override bool CanConvert(Type typeToConvert) { - /// - /// Determines whether the specified type can be converted. - /// - /// The type to compare against. - /// if the type can be converted; otherwise, . - public override bool CanConvert(Type typeToConvert) - { - return typeof(IMetadataDictionary).IsAssignableFrom(typeToConvert); - } + return typeof(IMetadataDictionary).IsAssignableFrom(typeToConvert); + } - /// - /// Reads and converts the JSON to . - /// - /// The reader. - /// The type to convert. - /// An object that specifies serialization options to use. - /// The converted value. - public override IMetadataDictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + /// + /// Reads and converts the JSON to . + /// + /// The reader. + /// The type to convert. + /// An object that specifies serialization options to use. + /// The converted value. + public override IMetadataDictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var md = new MetadataDictionary(); + while (reader.Read()) { - var md = new MetadataDictionary(); - while (reader.Read()) - { - if (reader.TokenType == JsonTokenType.EndObject) { return md; } - var propertyName = reader.GetString(); - reader.Read(); - md.Add(propertyName!, ExtractValue(ref reader, options)); - } - return md; + if (reader.TokenType == JsonTokenType.EndObject) { return md; } + var propertyName = reader.GetString(); + reader.Read(); + md.Add(propertyName!, ExtractValue(ref reader, options)); } + return md; + } - /// - /// Writes a specified as JSON. - /// - /// The writer to write to. - /// The value to convert to JSON. - /// An object that specifies serialization options to use. - public override void Write(Utf8JsonWriter writer, IMetadataDictionary value, JsonSerializerOptions options) - { - JsonSerializer.Serialize(writer, value, options.Clone(jso => jso.Converters.RemoveAllOf())); // prevent stackoverflow in case this method gets called - } + /// + /// Writes a specified as JSON. + /// + /// The writer to write to. + /// The value to convert to JSON. + /// An object that specifies serialization options to use. + public override void Write(Utf8JsonWriter writer, IMetadataDictionary value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, options.Clone(jso => jso.Converters.RemoveAllOf())); // prevent stackoverflow in case this method gets called + } - private object ExtractValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + private object ExtractValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + switch (reader.TokenType) { - switch (reader.TokenType) - { - case JsonTokenType.String: - if (reader.TryGetDateTime(out var date)) - { - return date; - } - return reader.GetString(); - case JsonTokenType.False: - return false; - case JsonTokenType.True: - return true; - case JsonTokenType.Null: - return null; - case JsonTokenType.Number: - if (reader.TryGetInt64(out var result)) - { - return result; - } - return reader.GetDecimal(); - case JsonTokenType.StartObject: - return Read(ref reader, null!, options); - case JsonTokenType.StartArray: - var list = new List(); - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - list.Add(ExtractValue(ref reader, options)); - } - return list; - default: - throw new JsonException($"'{reader.TokenType}' is not supported."); - } + case JsonTokenType.String: + if (reader.TryGetDateTime(out var date)) + { + return date; + } + return reader.GetString(); + case JsonTokenType.False: + return false; + case JsonTokenType.True: + return true; + case JsonTokenType.Null: + return null; + case JsonTokenType.Number: + if (reader.TryGetInt64(out var result)) + { + return result; + } + return reader.GetDecimal(); + case JsonTokenType.StartObject: + return Read(ref reader, null!, options); + case JsonTokenType.StartArray: + var list = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + list.Add(ExtractValue(ref reader, options)); + } + return list; + default: + throw new JsonException($"'{reader.TokenType}' is not supported."); } } } diff --git a/src/Savvyio.Extensions.Text.Json/Converters/RequestConverter.cs b/src/Savvyio.Extensions.Text.Json/Converters/RequestConverter.cs index 0b5f67bb..87cd5fa0 100644 --- a/src/Savvyio.Extensions.Text.Json/Converters/RequestConverter.cs +++ b/src/Savvyio.Extensions.Text.Json/Converters/RequestConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -6,82 +6,81 @@ using System.Text.Json.Serialization; using Cuemon.Extensions.Reflection; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +/// +/// Converts an to or from JSON. +/// +/// +public class RequestConverter : JsonConverter { /// - /// Converts an to or from JSON. + /// Determines whether the specified type can be converted. /// - /// - public class RequestConverter : JsonConverter + /// The type to compare against. + /// if the type can be converted; otherwise, . + public override bool CanConvert(Type typeToConvert) { - /// - /// Determines whether the specified type can be converted. - /// - /// The type to compare against. - /// if the type can be converted; otherwise, . - public override bool CanConvert(Type typeToConvert) - { - return typeof(IRequest).IsAssignableFrom(typeToConvert); - } + return typeof(IRequest).IsAssignableFrom(typeToConvert); + } - /// - /// Reads and converts the JSON to . - /// - /// The reader. - /// The type to convert. - /// An object that specifies serialization options to use. - /// The converted value. - /// - /// This deserializer only supports rehydration of implementations that either use auto-properties or have a naming convention that makes it possible to tie non-writable properties with the backing field equivalent. - /// - public override IRequest Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + /// + /// Reads and converts the JSON to . + /// + /// The reader. + /// The type to convert. + /// An object that specifies serialization options to use. + /// The converted value. + /// + /// This deserializer only supports rehydration of implementations that either use auto-properties or have a naming convention that makes it possible to tie non-writable properties with the backing field equivalent. + /// + public override IRequest Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using (var document = JsonDocument.ParseValue(ref reader)) { - using (var document = JsonDocument.ParseValue(ref reader)) + var instance = RuntimeHelpers.GetUninitializedObject(typeToConvert); + var properties = typeToConvert.GetAllProperties(); + var jProperties = document.RootElement.EnumerateObject(); + foreach (var property in properties.Where(pi => jProperties.Any(jp => jp.Name.Equals(options.PropertyNamingPolicy!.ConvertName(pi.Name), StringComparison.OrdinalIgnoreCase)))) { - var instance = RuntimeHelpers.GetUninitializedObject(typeToConvert); - var properties = typeToConvert.GetAllProperties(); - var jProperties = document.RootElement.EnumerateObject(); - foreach (var property in properties.Where(pi => jProperties.Any(jp => jp.Name.Equals(options.PropertyNamingPolicy!.ConvertName(pi.Name), StringComparison.OrdinalIgnoreCase)))) + var jProperty = jProperties.Single(jp => jp.Name.Equals(options.PropertyNamingPolicy!.ConvertName(property.Name), StringComparison.OrdinalIgnoreCase)); + if (document.RootElement.TryGetProperty(jProperty.Name, out var element)) { - var jProperty = jProperties.Single(jp => jp.Name.Equals(options.PropertyNamingPolicy!.ConvertName(property.Name), StringComparison.OrdinalIgnoreCase)); - if (document.RootElement.TryGetProperty(jProperty.Name, out var element)) - { - var value = element.Deserialize(property.PropertyType, options); - SetPropertyOrField(instance, typeToConvert, property, value); - } + var value = element.Deserialize(property.PropertyType, options); + SetPropertyOrField(instance, typeToConvert, property, value); } - return instance as IRequest; } + return instance as IRequest; } + } - private static void SetPropertyOrField(object instance, Type typeToConvert, PropertyInfo property, object value) + private static void SetPropertyOrField(object instance, Type typeToConvert, PropertyInfo property, object value) + { + if (property.CanWrite) { - if (property.CanWrite) - { - property.SetValue(instance, value); - return; - } - - var field = property.IsAutoProperty() - ? typeToConvert.GetAllFields().SingleOrDefault(fi => fi.Name.StartsWith($"<{property.Name}>", StringComparison.Ordinal)) - : typeToConvert.GetAllFields().SingleOrDefault(fi => fi.Name.Equals($"_{property.Name}>", StringComparison.OrdinalIgnoreCase)); - if (field == null) - { - throw new NotSupportedException($"This deserializer only supports rehydration of {nameof(IRequest)} implementations that either use auto-properties or have a naming convention that makes it possible to tie non-writable properties with the backing field equivalent."); - } - - field.SetValue(instance, value); + property.SetValue(instance, value); + return; } - /// - /// Writes a specified as JSON. - /// - /// The writer to write to. - /// The value to convert to JSON. - /// An object that specifies serialization options to use. - public override void Write(Utf8JsonWriter writer, IRequest value, JsonSerializerOptions options) + var field = property.IsAutoProperty() + ? typeToConvert.GetAllFields().SingleOrDefault(fi => fi.Name.StartsWith($"<{property.Name}>", StringComparison.Ordinal)) + : typeToConvert.GetAllFields().SingleOrDefault(fi => fi.Name.Equals($"_{property.Name}>", StringComparison.OrdinalIgnoreCase)); + if (field == null) { - JsonSerializer.Serialize(writer, (object)value, options.Clone(jso => jso.Converters.RemoveAllOf())); // prevent stackoverflow in case this method gets called + throw new NotSupportedException($"This deserializer only supports rehydration of {nameof(IRequest)} implementations that either use auto-properties or have a naming convention that makes it possible to tie non-writable properties with the backing field equivalent."); } + + field.SetValue(instance, value); + } + + /// + /// Writes a specified as JSON. + /// + /// The writer to write to. + /// The value to convert to JSON. + /// An object that specifies serialization options to use. + public override void Write(Utf8JsonWriter writer, IRequest value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, (object)value, options.Clone(jso => jso.Converters.RemoveAllOf())); // prevent stackoverflow in case this method gets called } } diff --git a/src/Savvyio.Extensions.Text.Json/Converters/SingleValueObjectConverter.cs b/src/Savvyio.Extensions.Text.Json/Converters/SingleValueObjectConverter.cs index b75e20ff..e44a2660 100644 --- a/src/Savvyio.Extensions.Text.Json/Converters/SingleValueObjectConverter.cs +++ b/src/Savvyio.Extensions.Text.Json/Converters/SingleValueObjectConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using System.Text.Json; @@ -8,69 +8,68 @@ using Cuemon.Extensions.Text.Json; using Savvyio.Domain; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +/// +/// Converts a to or from JSON. +/// +/// +public class SingleValueObjectConverter : JsonConverterFactory { /// - /// Converts a to or from JSON. + /// Initializes a new instance of the class. /// - /// - public class SingleValueObjectConverter : JsonConverterFactory + public SingleValueObjectConverter() { - /// - /// Initializes a new instance of the class. - /// - public SingleValueObjectConverter() - { - } + } - /// - /// When overridden in a derived class, determines whether the converter instance can convert the specified object type. - /// - /// The type of the object to check whether it can be converted by this converter instance. - /// if the instance can convert the specified object type; otherwise, . - public override bool CanConvert(Type typeToConvert) - { - return typeToConvert.HasTypes(typeof(SingleValueObject<>)); - } + /// + /// When overridden in a derived class, determines whether the converter instance can convert the specified object type. + /// + /// The type of the object to check whether it can be converted by this converter instance. + /// if the instance can convert the specified object type; otherwise, . + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert.HasTypes(typeof(SingleValueObject<>)); + } - /// - /// Creates a converter for a specified type. - /// - /// The type handled by the converter. - /// The serialization options to use. - /// A converter that is compatible with . - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) - { - var valueType = typeToConvert.GetInheritedTypes().Single(type => type.IsGenericType - && type.GetGenericTypeDefinition() == typeof(SingleValueObject<>)).GetGenericArguments()[0]; - return (JsonConverter)Activator.CreateInstance( - typeof(SingleValueObjectConverter<>).MakeGenericType(valueType), - BindingFlags.Instance | BindingFlags.Public, - binder: null, - args: null, - culture: null)!; - } + /// + /// Creates a converter for a specified type. + /// + /// The type handled by the converter. + /// The serialization options to use. + /// A converter that is compatible with . + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var valueType = typeToConvert.GetInheritedTypes().Single(type => type.IsGenericType + && type.GetGenericTypeDefinition() == typeof(SingleValueObject<>)).GetGenericArguments()[0]; + return (JsonConverter)Activator.CreateInstance( + typeof(SingleValueObjectConverter<>).MakeGenericType(valueType), + BindingFlags.Instance | BindingFlags.Public, + binder: null, + args: null, + culture: null)!; } +} - internal sealed class SingleValueObjectConverter : JsonConverter> +internal sealed class SingleValueObjectConverter : JsonConverter> +{ + public SingleValueObjectConverter() { - public SingleValueObjectConverter() - { - } + } - public override SingleValueObject Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SingleValueObject Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using (var document = JsonDocument.ParseValue(ref reader)) { - using (var document = JsonDocument.ParseValue(ref reader)) - { - var valueType = typeof(T); - return Activator.CreateInstance(typeToConvert, document.RootElement.Deserialize(valueType)) as SingleValueObject; - } + var valueType = typeof(T); + return Activator.CreateInstance(typeToConvert, document.RootElement.Deserialize(valueType)) as SingleValueObject; } + } - public override void Write(Utf8JsonWriter writer, SingleValueObject value, JsonSerializerOptions options) - { - writer.WriteObject(value.Value, options); - } + public override void Write(Utf8JsonWriter writer, SingleValueObject value, JsonSerializerOptions options) + { + writer.WriteObject(value.Value, options); } } diff --git a/src/Savvyio.Extensions.Text.Json/JsonConverterExtensions.cs b/src/Savvyio.Extensions.Text.Json/JsonConverterExtensions.cs index 2f4d9857..ed0aaa97 100644 --- a/src/Savvyio.Extensions.Text.Json/JsonConverterExtensions.cs +++ b/src/Savvyio.Extensions.Text.Json/JsonConverterExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; @@ -7,136 +7,135 @@ using Savvyio.Extensions.Text.Json.Converters; using Savvyio.Messaging; -namespace Savvyio.Extensions.Text.Json +namespace Savvyio.Extensions.Text.Json; + +/// +/// Extension methods for the class. +/// +public static class JsonConverterExtensions { /// - /// Extension methods for the class. + /// Removes one or more implementations where evaluates true in the collection of . /// - public static class JsonConverterExtensions + /// The type of object or value handled by the . + /// The collection of to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection RemoveAllOf(this ICollection converters) { - /// - /// Removes one or more implementations where evaluates true in the collection of . - /// - /// The type of object or value handled by the . - /// The collection of to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection RemoveAllOf(this ICollection converters) - { - return RemoveAllOf(converters, typeof(T)); - } + return RemoveAllOf(converters, typeof(T)); + } - /// - /// Removes one or more implementations where evaluates true in the collection of . - /// - /// The collection of to extend. - /// The type of objects or values handled by a sequence of . - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection RemoveAllOf(this ICollection converters, params Type[] types) + /// + /// Removes one or more implementations where evaluates true in the collection of . + /// + /// The collection of to extend. + /// The type of objects or values handled by a sequence of . + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection RemoveAllOf(this ICollection converters, params Type[] types) + { + Validator.ThrowIfNull(converters); + Validator.ThrowIfNull(types); + var rejects = types.SelectMany(type => converters.Where(jc => jc.CanConvert(type))).ToList(); + foreach (var reject in rejects) { - Validator.ThrowIfNull(converters); - Validator.ThrowIfNull(types); - var rejects = types.SelectMany(type => converters.Where(jc => jc.CanConvert(type))).ToList(); - foreach (var reject in rejects) - { - converters.Remove(reject); - } - return converters; + converters.Remove(reject); } + return converters; + } - /// - /// Adds a converter to the collection. - /// - /// The collection of to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddMetadataDictionaryConverter(this ICollection converters) - { - Validator.ThrowIfNull(converters); - converters.Add(new MetadataDictionaryConverter()); - return converters; - } + /// + /// Adds a converter to the collection. + /// + /// The collection of to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddMetadataDictionaryConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(new MetadataDictionaryConverter()); + return converters; + } - /// - /// Adds an converter (or derived) to the collection. - /// - /// The collection of to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddMessageConverter(this ICollection converters) - { - Validator.ThrowIfNull(converters); - converters.Add(new MessageConverter()); - return converters; - } + /// + /// Adds an converter (or derived) to the collection. + /// + /// The collection of to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddMessageConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(new MessageConverter()); + return converters; + } - /// - /// Adds a converter to the collection. - /// - /// The collection of to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddRequestConverter(this ICollection converters) - { - Validator.ThrowIfNull(converters); - converters.Add(new RequestConverter()); - return converters; - } + /// + /// Adds a converter to the collection. + /// + /// The collection of to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddRequestConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(new RequestConverter()); + return converters; + } - /// - /// Adds a converter to the collection. - /// - /// The collection of to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddDateTimeConverter(this ICollection converters) - { - Validator.ThrowIfNull(converters); - converters.Add(new DateTimeConverter()); - return converters; - } + /// + /// Adds a converter to the collection. + /// + /// The collection of to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddDateTimeConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(new DateTimeConverter()); + return converters; + } - /// - /// Adds a converter to the collection. - /// - /// The collection of to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddDateTimeOffsetConverter(this ICollection converters) - { - Validator.ThrowIfNull(converters); - converters.Add(new DateTimeOffsetConverter()); - return converters; - } + /// + /// Adds a converter to the collection. + /// + /// The collection of to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddDateTimeOffsetConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(new DateTimeOffsetConverter()); + return converters; + } - /// - /// Adds a converter to the collection. - /// - /// The collection to extend. - /// A reference to so that additional calls can be chained. - /// - /// cannot be null. - /// - public static ICollection AddSingleValueObjectConverter(this ICollection converters) - { - Validator.ThrowIfNull(converters); - converters.Add(new SingleValueObjectConverter()); - return converters; - } + /// + /// Adds a converter to the collection. + /// + /// The collection to extend. + /// A reference to so that additional calls can be chained. + /// + /// cannot be null. + /// + public static ICollection AddSingleValueObjectConverter(this ICollection converters) + { + Validator.ThrowIfNull(converters); + converters.Add(new SingleValueObjectConverter()); + return converters; } } diff --git a/src/Savvyio.Extensions.Text.Json/JsonMarshaller.cs b/src/Savvyio.Extensions.Text.Json/JsonMarshaller.cs index 0629a627..ab5a5d67 100644 --- a/src/Savvyio.Extensions.Text.Json/JsonMarshaller.cs +++ b/src/Savvyio.Extensions.Text.Json/JsonMarshaller.cs @@ -1,66 +1,65 @@ -using System; +using System; using System.IO; using Cuemon.Extensions.Text.Json.Formatters; -namespace Savvyio.Extensions.Text.Json +namespace Savvyio.Extensions.Text.Json; + +/// +/// Provides a class for marshalling data using native JSON support in .NET. +/// +/// +public class JsonMarshaller : IMarshaller { + private readonly Action _setup; + /// - /// Provides a class for marshalling data using native JSON support in .NET. + /// Provides a default instance of the class optimized for messaging. /// - /// - public class JsonMarshaller : IMarshaller - { - private readonly Action _setup; + /// The default instance of the class optimized for messaging. + public static JsonMarshaller Default { get; } = new(o => o.Settings.WriteIndented = false); - /// - /// Provides a default instance of the class optimized for messaging. - /// - /// The default instance of the class optimized for messaging. - public static JsonMarshaller Default { get; } = new(o => o.Settings.WriteIndented = false); - - /// - /// Creates a new instance of the class. - /// - /// The which may be configured. - /// JsonMarshaller. - public static JsonMarshaller Create(Action setup = null) => new(setup); + /// + /// Creates a new instance of the class. + /// + /// The which may be configured. + /// JsonMarshaller. + public static JsonMarshaller Create(Action setup = null) => new(setup); - static JsonMarshaller() - { - Bootstrapper.Initialize(); - } + static JsonMarshaller() + { + Bootstrapper.Initialize(); + } - /// - /// Initializes a new instance of the class. - /// - /// The which may be configured. - public JsonMarshaller(Action setup = null) - { - _setup = setup; - } + /// + /// Initializes a new instance of the class. + /// + /// The which may be configured. + public JsonMarshaller(Action setup = null) + { + _setup = setup; + } - /// - public Stream Serialize(TValue value) - { - return JsonFormatter.SerializeObject(value, _setup); - } + /// + public Stream Serialize(TValue value) + { + return JsonFormatter.SerializeObject(value, _setup); + } - /// - public Stream Serialize(object value, Type inputType) - { - return JsonFormatter.SerializeObject(value, inputType, _setup); - } + /// + public Stream Serialize(object value, Type inputType) + { + return JsonFormatter.SerializeObject(value, inputType, _setup); + } - /// - public TValue Deserialize(Stream data) - { - return JsonFormatter.DeserializeObject(data, _setup); - } + /// + public TValue Deserialize(Stream data) + { + return JsonFormatter.DeserializeObject(data, _setup); + } - /// - public object Deserialize(Stream data, Type returnType) - { - return JsonFormatter.DeserializeObject(data, returnType, _setup); - } + /// + public object Deserialize(Stream data, Type returnType) + { + return JsonFormatter.DeserializeObject(data, returnType, _setup); } } diff --git a/src/Savvyio.Extensions.Text.Json/JsonSerializerOptionsExtensions.cs b/src/Savvyio.Extensions.Text.Json/JsonSerializerOptionsExtensions.cs index abd39b27..c6b268c5 100644 --- a/src/Savvyio.Extensions.Text.Json/JsonSerializerOptionsExtensions.cs +++ b/src/Savvyio.Extensions.Text.Json/JsonSerializerOptionsExtensions.cs @@ -1,29 +1,28 @@ -using System; +using System; using System.Text.Json; using Cuemon; -namespace Savvyio.Extensions.Text.Json +namespace Savvyio.Extensions.Text.Json; + +/// +/// Extension methods for the class. +/// +public static class JsonSerializerOptionsExtensions { /// - /// Extension methods for the class. + /// Copies the options from a instance to a new instance. /// - public static class JsonSerializerOptionsExtensions + /// The to extend. + /// The which may be configured. + /// A new cloned instance of with optional altering as specified by the delegate. + /// + /// cannot be null. + /// + public static JsonSerializerOptions Clone(this JsonSerializerOptions options, Action setup = null) { - /// - /// Copies the options from a instance to a new instance. - /// - /// The to extend. - /// The which may be configured. - /// A new cloned instance of with optional altering as specified by the delegate. - /// - /// cannot be null. - /// - public static JsonSerializerOptions Clone(this JsonSerializerOptions options, Action setup = null) - { - Validator.ThrowIfNull(options); - options = new JsonSerializerOptions(options); - setup?.Invoke(options); - return options; - } + Validator.ThrowIfNull(options); + options = new JsonSerializerOptions(options); + setup?.Invoke(options); + return options; } } diff --git a/src/Savvyio.Extensions.Text.Json/Savvyio.Extensions.Text.Json.csproj b/src/Savvyio.Extensions.Text.Json/Savvyio.Extensions.Text.Json.csproj index d553bcac..a1cff0c3 100644 --- a/src/Savvyio.Extensions.Text.Json/Savvyio.Extensions.Text.Json.csproj +++ b/src/Savvyio.Extensions.Text.Json/Savvyio.Extensions.Text.Json.csproj @@ -1,4 +1,4 @@ - + Extend the Savvy I/O core assemblies with support for official built-in JSON. diff --git a/src/Savvyio.Messaging/Acknowledgeable.cs b/src/Savvyio.Messaging/Acknowledgeable.cs index 44fed49c..b4739ee3 100644 --- a/src/Savvyio.Messaging/Acknowledgeable.cs +++ b/src/Savvyio.Messaging/Acknowledgeable.cs @@ -1,41 +1,40 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Provides a default implementation of the interface. +/// +public abstract record Acknowledgeable : IAcknowledgeable { /// - /// Provides a default implementation of the interface. + /// Initializes a new instance of the class. /// - public abstract record Acknowledgeable : IAcknowledgeable + protected Acknowledgeable() { - /// - /// Initializes a new instance of the class. - /// - protected Acknowledgeable() - { - Properties = new Dictionary(); - } + Properties = new Dictionary(); + } - /// - public event AsyncEventHandler Acknowledged; + /// + public event AsyncEventHandler Acknowledged; - /// - public IDictionary Properties { get; } + /// + public IDictionary Properties { get; } - /// - public async Task AcknowledgeAsync() - { - await OnAcknowledgedAsync(new AcknowledgedEventArgs(Properties)).ConfigureAwait(false); - } + /// + public async Task AcknowledgeAsync() + { + await OnAcknowledgedAsync(new AcknowledgedEventArgs(Properties)).ConfigureAwait(false); + } - /// - /// Raises the event. - /// - /// The instance containing the event data. - protected virtual async Task OnAcknowledgedAsync(AcknowledgedEventArgs e) - { - var handler = Acknowledged; - if (handler != null) { await handler(this, e).ConfigureAwait(false); } - } + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual async Task OnAcknowledgedAsync(AcknowledgedEventArgs e) + { + var handler = Acknowledged; + if (handler != null) { await handler(this, e).ConfigureAwait(false); } } } diff --git a/src/Savvyio.Messaging/Cryptography/MessageExtensions.cs b/src/Savvyio.Messaging/Cryptography/MessageExtensions.cs index 4bdb84dd..3ebc9370 100644 --- a/src/Savvyio.Messaging/Cryptography/MessageExtensions.cs +++ b/src/Savvyio.Messaging/Cryptography/MessageExtensions.cs @@ -1,37 +1,36 @@ -using System; +using System; using Cuemon; using Cuemon.Security.Cryptography; -namespace Savvyio.Messaging.Cryptography +namespace Savvyio.Messaging.Cryptography; + +/// +/// Extension methods for the interface. +/// +public static class MessageExtensions { /// - /// Extension methods for the interface. + /// Converts the specified to an equivalent using the provided configurator. /// - public static class MessageExtensions + /// The type of the payload constraint to the interface. + /// The message to cryptographically sign. + /// The that is used when converting the into an . + /// The that must be configured. + /// An implementation of the interface having a constraint to the interface. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// failed to configure an instance of in a valid state. + /// + public static ISignedMessage Sign(this IMessage message, IMarshaller marshaller, Action setup) where T : IRequest { - /// - /// Converts the specified to an equivalent using the provided configurator. - /// - /// The type of the payload constraint to the interface. - /// The message to cryptographically sign. - /// The that is used when converting the into an . - /// The that must be configured. - /// An implementation of the interface having a constraint to the interface. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// failed to configure an instance of in a valid state. - /// - public static ISignedMessage Sign(this IMessage message, IMarshaller marshaller, Action setup) where T : IRequest - { - Validator.ThrowIfNull(message); - Validator.ThrowIfNull(marshaller); - Validator.ThrowIfInvalidConfigurator(setup, out var options); - var data = marshaller.Serialize(message); - var signature = KeyedHashFactory.CreateHmacCrypto(options.SignatureSecret, options.SignatureAlgorithm).ComputeHash(data).ToHexadecimalString(); - return new SignedMessage(message, signature); - } + Validator.ThrowIfNull(message); + Validator.ThrowIfNull(marshaller); + Validator.ThrowIfInvalidConfigurator(setup, out var options); + var data = marshaller.Serialize(message); + var signature = KeyedHashFactory.CreateHmacCrypto(options.SignatureSecret, options.SignatureAlgorithm).ComputeHash(data).ToHexadecimalString(); + return new SignedMessage(message, signature); } } diff --git a/src/Savvyio.Messaging/Cryptography/SignedMessage.cs b/src/Savvyio.Messaging/Cryptography/SignedMessage.cs index fd084361..2a5e8627 100644 --- a/src/Savvyio.Messaging/Cryptography/SignedMessage.cs +++ b/src/Savvyio.Messaging/Cryptography/SignedMessage.cs @@ -1,58 +1,57 @@ -using System; +using System; using Cuemon; -namespace Savvyio.Messaging.Cryptography +namespace Savvyio.Messaging.Cryptography; + +/// +/// Provides a default implementation of the interface. +/// +/// The type of the payload constraint to the interface. +/// +public record SignedMessage : Acknowledgeable, ISignedMessage where T : IRequest { /// - /// Provides a default implementation of the interface. + /// Initializes a new instance of the class. /// - /// The type of the payload constraint to the interface. - /// - public record SignedMessage : Acknowledgeable, ISignedMessage where T : IRequest + /// The message to cryptographically sign. + /// The cryptographic signature of the . + /// + /// cannot be null - or - + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters. + /// + public SignedMessage(IMessage message, string signature) { - /// - /// Initializes a new instance of the class. - /// - /// The message to cryptographically sign. - /// The cryptographic signature of the . - /// - /// cannot be null - or - - /// cannot be null. - /// - /// - /// cannot be empty or consist only of white-space characters. - /// - public SignedMessage(IMessage message, string signature) - { - Validator.ThrowIfNull(message); - Validator.ThrowIfNullOrWhitespace(signature); - Id = message.Id; - Source = message.Source; - Type = message.Type; - Time = message.Time; - Data = message.Data; - Signature = signature; - } - - /// - public string Id { get; } - - /// - public string Source { get; } - - /// - public string Type { get; } - - /// - public DateTime? Time { get; } - - /// - public T Data { get; } - - /// - /// Gets the cryptographic signature of the message. - /// - /// The cryptographic signature of the message. - public string Signature { get; } + Validator.ThrowIfNull(message); + Validator.ThrowIfNullOrWhitespace(signature); + Id = message.Id; + Source = message.Source; + Type = message.Type; + Time = message.Time; + Data = message.Data; + Signature = signature; } + + /// + public string Id { get; } + + /// + public string Source { get; } + + /// + public string Type { get; } + + /// + public DateTime? Time { get; } + + /// + public T Data { get; } + + /// + /// Gets the cryptographic signature of the message. + /// + /// The cryptographic signature of the message. + public string Signature { get; } } diff --git a/src/Savvyio.Messaging/Cryptography/SignedMessageExtensions.cs b/src/Savvyio.Messaging/Cryptography/SignedMessageExtensions.cs index 6d9768c4..dc421776 100644 --- a/src/Savvyio.Messaging/Cryptography/SignedMessageExtensions.cs +++ b/src/Savvyio.Messaging/Cryptography/SignedMessageExtensions.cs @@ -1,41 +1,40 @@ using System; using Cuemon; -namespace Savvyio.Messaging.Cryptography +namespace Savvyio.Messaging.Cryptography; + +/// +/// Extension methods for the interface. +/// +public static class SignedMessageExtensions { /// - /// Extension methods for the interface. + /// Verifies the digital signature of the message. /// - public static class SignedMessageExtensions + /// The type of the payload constraint to the interface. + /// The message to cryptographically verify. + /// The that is used when converting the into an . + /// The that must be configured. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// failed to configure an instance of in a valid state. + /// + /// + /// signature did not match the cryptographically calculated value. + /// + /// This method throws an if the verification of the digital signature fails. + public static void CheckSignature(this ISignedMessage message, IMarshaller marshaller, Action setup) where T : IRequest { - /// - /// Verifies the digital signature of the message. - /// - /// The type of the payload constraint to the interface. - /// The message to cryptographically verify. - /// The that is used when converting the into an . - /// The that must be configured. - /// - /// cannot be null -or- - /// cannot be null. - /// - /// - /// failed to configure an instance of in a valid state. - /// - /// - /// signature did not match the cryptographically calculated value. - /// - /// This method throws an if the verification of the digital signature fails. - public static void CheckSignature(this ISignedMessage message, IMarshaller marshaller, Action setup) where T : IRequest + Validator.ThrowIfNull(message); + Validator.ThrowIfNull(marshaller); + Validator.ThrowIfInvalidConfigurator(setup, out _); + var baseMessage = message.Clone().Sign(marshaller, setup); + if (!message.Signature.Equals(baseMessage.Signature, StringComparison.Ordinal)) { - Validator.ThrowIfNull(message); - Validator.ThrowIfNull(marshaller); - Validator.ThrowIfInvalidConfigurator(setup, out _); - var baseMessage = message.Clone().Sign(marshaller, setup); - if (!message.Signature.Equals(baseMessage.Signature, StringComparison.Ordinal)) - { - throw new ArgumentOutOfRangeException(nameof(message), message.Signature, "The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with."); - } + throw new ArgumentOutOfRangeException(nameof(message), message.Signature, "The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with."); } } } diff --git a/src/Savvyio.Messaging/Cryptography/SignedMessageOptions.cs b/src/Savvyio.Messaging/Cryptography/SignedMessageOptions.cs index be327b73..43fcacac 100644 --- a/src/Savvyio.Messaging/Cryptography/SignedMessageOptions.cs +++ b/src/Savvyio.Messaging/Cryptography/SignedMessageOptions.cs @@ -1,61 +1,60 @@ -using System; +using System; using Cuemon; using Cuemon.Configuration; using Cuemon.Security.Cryptography; -namespace Savvyio.Messaging.Cryptography +namespace Savvyio.Messaging.Cryptography; + +/// +/// Configuration options that is related to wrapping an implementation inside a cryptographically signed message. +/// +/// +public class SignedMessageOptions : IValidatableParameterObject { /// - /// Configuration options that is related to wrapping an implementation inside a cryptographically signed message. + /// Initializes a new instance of the class. /// - /// - public class SignedMessageOptions : IValidatableParameterObject + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// + /// + /// + /// + public SignedMessageOptions() { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// - /// - /// - /// - public SignedMessageOptions() - { - SignatureAlgorithm = KeyedCryptoAlgorithm.HmacSha256; - SignatureSecret = null; - } + SignatureAlgorithm = KeyedCryptoAlgorithm.HmacSha256; + SignatureSecret = null; + } - /// - /// Gets or sets the cryptographical algorithm used when signing the serialized . - /// - /// The cryptographical algorithm used when signing the serialized . - public KeyedCryptoAlgorithm SignatureAlgorithm { get; set; } + /// + /// Gets or sets the cryptographical algorithm used when signing the serialized . + /// + /// The cryptographical algorithm used when signing the serialized . + public KeyedCryptoAlgorithm SignatureAlgorithm { get; set; } - /// - /// Gets or sets the secret key used when signing the serialized . - /// - /// The secret key used when signing the serialized . - public byte[] SignatureSecret { get; set; } + /// + /// Gets or sets the secret key used when signing the serialized . + /// + /// The secret key used when signing the serialized . + public byte[] SignatureSecret { get; set; } - /// - /// Determines whether the public read-write properties of this instance are in a valid state. - /// - /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. - /// - /// cannot be null - or - - /// cannot have a length of zero. - /// + /// + /// Determines whether the public read-write properties of this instance are in a valid state. + /// + /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. + /// + /// cannot be null - or - + /// cannot have a length of zero. + /// public void ValidateOptions() - { - Validator.ThrowIfInvalidState(SignatureSecret == null || SignatureSecret.Length == 0); - } + { + Validator.ThrowIfInvalidState(SignatureSecret == null || SignatureSecret.Length == 0); } } diff --git a/src/Savvyio.Messaging/Message.cs b/src/Savvyio.Messaging/Message.cs index e2654fa1..b931744f 100644 --- a/src/Savvyio.Messaging/Message.cs +++ b/src/Savvyio.Messaging/Message.cs @@ -1,65 +1,64 @@ -using System; +using System; using Cuemon; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Provides a default implementation of the interface. +/// +/// The type of the payload constraint to the interface. +/// +public record Message : Acknowledgeable, IMessage where T : IRequest { /// - /// Provides a default implementation of the interface. + /// Initializes a new instance of the class. /// - /// The type of the payload constraint to the interface. - /// - public record Message : Acknowledgeable, IMessage where T : IRequest + /// The identifier of the message. + /// The context that describes the origin of the message. + /// The payload of the message. + /// The type that describes the type of event related to the originating occurrence. + /// The time at which this message was generated. + /// + /// cannot be null - or - + /// cannot be null - or - + /// cannot be null - or - + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters - or - + /// cannot be empty or consist only of white-space characters - or - + /// was not expressed as the Coordinated Universal Time (UTC). + /// + public Message(string id, Uri source, string type, T data, DateTime? time = null) { - /// - /// Initializes a new instance of the class. - /// - /// The identifier of the message. - /// The context that describes the origin of the message. - /// The payload of the message. - /// The type that describes the type of event related to the originating occurrence. - /// The time at which this message was generated. - /// - /// cannot be null - or - - /// cannot be null - or - - /// cannot be null - or - - /// cannot be null. - /// - /// - /// cannot be empty or consist only of white-space characters - or - - /// cannot be empty or consist only of white-space characters - or - - /// was not expressed as the Coordinated Universal Time (UTC). - /// - public Message(string id, Uri source, string type, T data, DateTime? time = null) - { - Validator.ThrowIfNullOrWhitespace(id); - Validator.ThrowIfNull(source); - Validator.ThrowIfNullOrWhitespace(type); - Validator.ThrowIfNull(data); - Validator.ThrowIfTrue(() => time.HasValue && time.Value.Kind != DateTimeKind.Utc, nameof(time), "Time must be expressed as the Coordinated Universal Time (UTC)."); - Id = id; - Source = source.OriginalString; - Type = type; - Data = data; - Time = (time ?? DateTime.UtcNow); - } + Validator.ThrowIfNullOrWhitespace(id); + Validator.ThrowIfNull(source); + Validator.ThrowIfNullOrWhitespace(type); + Validator.ThrowIfNull(data); + Validator.ThrowIfTrue(() => time.HasValue && time.Value.Kind != DateTimeKind.Utc, nameof(time), "Time must be expressed as the Coordinated Universal Time (UTC)."); + Id = id; + Source = source.OriginalString; + Type = type; + Data = data; + Time = (time ?? DateTime.UtcNow); + } - internal Message() - { - } + internal Message() + { + } - /// - public string Id { get; internal set; } + /// + public string Id { get; internal set; } - /// - public string Source { get; internal set; } + /// + public string Source { get; internal set; } - /// - public string Type { get; internal set; } + /// + public string Type { get; internal set; } - /// - public DateTime? Time { get; internal set; } + /// + public DateTime? Time { get; internal set; } - /// - public T Data { get; internal set; } - } + /// + public T Data { get; internal set; } } diff --git a/src/Savvyio.Messaging/MessageAsyncEnumerable.cs b/src/Savvyio.Messaging/MessageAsyncEnumerable.cs index d40c7d1a..07a14719 100644 --- a/src/Savvyio.Messaging/MessageAsyncEnumerable.cs +++ b/src/Savvyio.Messaging/MessageAsyncEnumerable.cs @@ -1,58 +1,57 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Cuemon; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Exposes an enumerator that provides asynchronous iteration over values of a specified type. +/// +/// The type of the elements in the collection. +/// +public class MessageAsyncEnumerable : IAsyncEnumerable> where T : IRequest { + private readonly IAsyncEnumerable> _source; + private readonly MessageAsyncEnumerableOptions _options; + + /// - /// Exposes an enumerator that provides asynchronous iteration over values of a specified type. + /// Initializes a new instance of the class. /// - /// The type of the elements in the collection. - /// - public class MessageAsyncEnumerable : IAsyncEnumerable> where T : IRequest + /// The sequence to iterate. + /// The which may be configured. + public MessageAsyncEnumerable(IEnumerable> source, Action> setup = null) : this(source?.ToAsyncEnumerable(), setup) { - private readonly IAsyncEnumerable> _source; - private readonly MessageAsyncEnumerableOptions _options; - - - /// - /// Initializes a new instance of the class. - /// - /// The sequence to iterate. - /// The which may be configured. - public MessageAsyncEnumerable(IEnumerable> source, Action> setup = null) : this(source?.ToAsyncEnumerable(), setup) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The sequence to iterate. - /// The which may be configured. - /// - /// cannot be null. - /// - /// - /// failed to configure an instance of in a valid state. - /// - public MessageAsyncEnumerable(IAsyncEnumerable> source, Action> setup = null) - { - Validator.ThrowIfNull(source); - Validator.ThrowIfInvalidConfigurator(setup, out var options); - _source = source; - _options = options; - } + /// + /// Initializes a new instance of the class. + /// + /// The sequence to iterate. + /// The which may be configured. + /// + /// cannot be null. + /// + /// + /// failed to configure an instance of in a valid state. + /// + public MessageAsyncEnumerable(IAsyncEnumerable> source, Action> setup = null) + { + Validator.ThrowIfNull(source); + Validator.ThrowIfInvalidConfigurator(setup, out var options); + _source = source; + _options = options; + } - /// - /// Returns an enumerator that iterates asynchronously through the collection. - /// - /// A that may be used to cancel the asynchronous iteration. - /// An enumerator that can be used to iterate asynchronously through the collection. - public virtual IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) - { - return new MessageAsyncEnumerator(_source.GetAsyncEnumerator(cancellationToken), _options); - } + /// + /// Returns an enumerator that iterates asynchronously through the collection. + /// + /// A that may be used to cancel the asynchronous iteration. + /// An enumerator that can be used to iterate asynchronously through the collection. + public virtual IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new MessageAsyncEnumerator(_source.GetAsyncEnumerator(cancellationToken), _options); } } diff --git a/src/Savvyio.Messaging/MessageAsyncEnumerableOptions.cs b/src/Savvyio.Messaging/MessageAsyncEnumerableOptions.cs index 156f9366..4183a2d4 100644 --- a/src/Savvyio.Messaging/MessageAsyncEnumerableOptions.cs +++ b/src/Savvyio.Messaging/MessageAsyncEnumerableOptions.cs @@ -1,70 +1,69 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon; using Cuemon.Configuration; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Configuration options that is related to messages. +/// +/// The type of the payload constraint to the interface. +public class MessageAsyncEnumerableOptions : IValidatableParameterObject where T : IRequest { /// - /// Configuration options that is related to messages. + /// Initializes a new instance of the class. /// - /// The type of the payload constraint to the interface. - public class MessageAsyncEnumerableOptions : IValidatableParameterObject where T : IRequest + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// new ConcurrentBag<IDictionary<string, object>>() + /// + /// + /// + /// null + /// + /// + /// + /// null + /// + /// + /// + public MessageAsyncEnumerableOptions() { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// new ConcurrentBag<IDictionary<string, object>>() - /// - /// - /// - /// null - /// - /// - /// - /// null - /// - /// - /// - public MessageAsyncEnumerableOptions() - { - AcknowledgedProperties = new ConcurrentBag>(); - } + AcknowledgedProperties = new ConcurrentBag>(); + } - /// - /// Gets or sets the delegate that is invoked once for each message fetched from a source. - /// - /// The delegate that is invoked once for each message fetched from a source. - public Func, Task> MessageCallback { get; set; } + /// + /// Gets or sets the delegate that is invoked once for each message fetched from a source. + /// + /// The delegate that is invoked once for each message fetched from a source. + public Func, Task> MessageCallback { get; set; } - /// - /// Gets or sets the implementation of an that is used to store all acknowledged properties. Default is a new instance of . - /// - /// The implementation of an that is used to store all acknowledged properties. - public IProducerConsumerCollection> AcknowledgedProperties { get; set; } + /// + /// Gets or sets the implementation of an that is used to store all acknowledged properties. Default is a new instance of . + /// + /// The implementation of an that is used to store all acknowledged properties. + public IProducerConsumerCollection> AcknowledgedProperties { get; set; } - /// - /// Gets or sets the delegate that is invoked at the end of a sequence with all acknowledged properties. - /// - /// The delegate that is invoked at the end of a sequence with all acknowledged properties. - public Func>, Task> AcknowledgedPropertiesCallback { get; set; } + /// + /// Gets or sets the delegate that is invoked at the end of a sequence with all acknowledged properties. + /// + /// The delegate that is invoked at the end of a sequence with all acknowledged properties. + public Func>, Task> AcknowledgedPropertiesCallback { get; set; } - /// - public void ValidateOptions() - { - Validator.ThrowIfInvalidState(AcknowledgedProperties == null, $"{nameof(AcknowledgedProperties)} cannot be null."); - Validator.ThrowIfInvalidState(MessageCallback == null, $"{nameof(MessageCallback)} cannot be null."); - } + /// + public void ValidateOptions() + { + Validator.ThrowIfInvalidState(AcknowledgedProperties == null, $"{nameof(AcknowledgedProperties)} cannot be null."); + Validator.ThrowIfInvalidState(MessageCallback == null, $"{nameof(MessageCallback)} cannot be null."); } } diff --git a/src/Savvyio.Messaging/MessageAsyncEnumerator.cs b/src/Savvyio.Messaging/MessageAsyncEnumerator.cs index 8f7be43a..7ffc8f32 100644 --- a/src/Savvyio.Messaging/MessageAsyncEnumerator.cs +++ b/src/Savvyio.Messaging/MessageAsyncEnumerator.cs @@ -1,51 +1,50 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Cuemon; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +internal sealed class MessageAsyncEnumerator : IAsyncEnumerator> where T : IRequest { - internal sealed class MessageAsyncEnumerator : IAsyncEnumerator> where T : IRequest + private readonly IAsyncEnumerator> _source; + private readonly MessageAsyncEnumerableOptions _options; + + internal MessageAsyncEnumerator(IAsyncEnumerator> source, MessageAsyncEnumerableOptions options) { - private readonly IAsyncEnumerator> _source; - private readonly MessageAsyncEnumerableOptions _options; + Validator.ThrowIfNull(source); + _source = source; + _options = options; + } - internal MessageAsyncEnumerator(IAsyncEnumerator> source, MessageAsyncEnumerableOptions options) - { - Validator.ThrowIfNull(source); - _source = source; - _options = options; - } + public IMessage Current => _source.Current; - public IMessage Current => _source.Current; + public ValueTask DisposeAsync() + { + return _source.DisposeAsync(); + } - public ValueTask DisposeAsync() + public async ValueTask MoveNextAsync() + { + var mn = await _source.MoveNextAsync().ConfigureAwait(false); + if (mn && _options.MessageCallback != null) { - return _source.DisposeAsync(); + _source.Current.Acknowledged += OnAcknowledgedAsync; + await _options.MessageCallback(_source.Current).ConfigureAwait(false); } - - public async ValueTask MoveNextAsync() + else { - var mn = await _source.MoveNextAsync().ConfigureAwait(false); - if (mn && _options.MessageCallback != null) - { - _source.Current.Acknowledged += OnAcknowledgedAsync; - await _options.MessageCallback(_source.Current).ConfigureAwait(false); - } - else + if (_options.AcknowledgedProperties.Count > 0 && _options.AcknowledgedPropertiesCallback != null) { - if (_options.AcknowledgedProperties.Count > 0 && _options.AcknowledgedPropertiesCallback != null) - { - await _options.AcknowledgedPropertiesCallback(_options.AcknowledgedProperties).ConfigureAwait(false); - } + await _options.AcknowledgedPropertiesCallback(_options.AcknowledgedProperties).ConfigureAwait(false); } - return mn; } + return mn; + } - private Task OnAcknowledgedAsync(object sender, AcknowledgedEventArgs e) - { - _options.AcknowledgedProperties.TryAdd(e.Properties); - if (sender is IAcknowledgeable message) { message.Acknowledged -= OnAcknowledgedAsync; } - return Task.CompletedTask; - } + private Task OnAcknowledgedAsync(object sender, AcknowledgedEventArgs e) + { + _options.AcknowledgedProperties.TryAdd(e.Properties); + if (sender is IAcknowledgeable message) { message.Acknowledged -= OnAcknowledgedAsync; } + return Task.CompletedTask; } } diff --git a/src/Savvyio.Messaging/MessageExtensions.cs b/src/Savvyio.Messaging/MessageExtensions.cs index 708e0322..1fbcece4 100644 --- a/src/Savvyio.Messaging/MessageExtensions.cs +++ b/src/Savvyio.Messaging/MessageExtensions.cs @@ -1,34 +1,33 @@ -using System; +using System; using Cuemon; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Extension methods for the interface. +/// +public static class MessageExtensions { /// - /// Extension methods for the interface. + /// Clones the specified using either the specified or the default implementation that return a new instance of . /// - public static class MessageExtensions + /// The type of the payload constraint to the interface. + /// The to clone. + /// The function delegate that is used to clone the specified . Default is null. + /// An implementation of that is an otherwise clone of the specified . + /// + /// cannot be null. + /// + public static IMessage Clone(this IMessage message, Func> copier = null) where T : IRequest { - /// - /// Clones the specified using either the specified or the default implementation that return a new instance of . - /// - /// The type of the payload constraint to the interface. - /// The to clone. - /// The function delegate that is used to clone the specified . Default is null. - /// An implementation of that is an otherwise clone of the specified . - /// - /// cannot be null. - /// - public static IMessage Clone(this IMessage message, Func> copier = null) where T : IRequest + Validator.ThrowIfNull(message); + return copier?.Invoke() ?? new Message() { - Validator.ThrowIfNull(message); - return copier?.Invoke() ?? new Message() - { - Data = message.Data, - Id = message.Id, - Source = message.Source, - Time = message.Time, - Type = message.Type - }; - } + Data = message.Data, + Id = message.Id, + Source = message.Source, + Time = message.Time, + Type = message.Type + }; } } diff --git a/src/Savvyio.Messaging/MessageOptions.cs b/src/Savvyio.Messaging/MessageOptions.cs index 7c77058c..24821544 100644 --- a/src/Savvyio.Messaging/MessageOptions.cs +++ b/src/Savvyio.Messaging/MessageOptions.cs @@ -1,62 +1,61 @@ -using System; +using System; using Cuemon; using Cuemon.Configuration; using Cuemon.Extensions; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +/// +/// Configuration options that is related to wrapping an implementation inside a message. +/// +/// +public class MessageOptions : IValidatableParameterObject { /// - /// Configuration options that is related to wrapping an implementation inside a message. + /// Initializes a new instance of the class. /// - /// - public class MessageOptions : IValidatableParameterObject + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// Guid.NewGuid().ToString("N") + /// + /// + /// + public MessageOptions() { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// Guid.NewGuid().ToString("N") - /// - /// - /// - public MessageOptions() - { - MessageId = Guid.NewGuid().ToString("N"); - Time = null; - } + MessageId = Guid.NewGuid().ToString("N"); + Time = null; + } - /// - /// Gets or sets the identifier of the message. - /// - /// The identifier of the message. - public string MessageId { get; set; } + /// + /// Gets or sets the identifier of the message. + /// + /// The identifier of the message. + public string MessageId { get; set; } - /// - /// Gets or sets the time, expressed as the Coordinated Universal Time (UTC), of the message. - /// - /// The point in time the message was generated. - public DateTime? Time { get; set; } + /// + /// Gets or sets the time, expressed as the Coordinated Universal Time (UTC), of the message. + /// + /// The point in time the message was generated. + public DateTime? Time { get; set; } - /// - /// Determines whether the public read-write properties of this instance are in a valid state. - /// - /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. - /// - /// cannot be null, empty or consist only of white-space characters - or - - /// (when set) was not expressed as the Coordinated Universal Time (UTC). - /// - public virtual void ValidateOptions() - { - Validator.ThrowIfInvalidState(MessageId.IsNullOrWhiteSpace()); - Validator.ThrowIfInvalidState(Time.HasValue && Time.Value.Kind != DateTimeKind.Utc); - } + /// + /// Determines whether the public read-write properties of this instance are in a valid state. + /// + /// This method is expected to throw exceptions when one or more conditions fails to be in a valid state. + /// + /// cannot be null, empty or consist only of white-space characters - or - + /// (when set) was not expressed as the Coordinated Universal Time (UTC). + /// + public virtual void ValidateOptions() + { + Validator.ThrowIfInvalidState(MessageId.IsNullOrWhiteSpace()); + Validator.ThrowIfInvalidState(Time.HasValue && Time.Value.Kind != DateTimeKind.Utc); } } diff --git a/src/Savvyio.Messaging/Savvyio.Messaging.csproj b/src/Savvyio.Messaging/Savvyio.Messaging.csproj index 949f1787..239a7760 100644 --- a/src/Savvyio.Messaging/Savvyio.Messaging.csproj +++ b/src/Savvyio.Messaging/Savvyio.Messaging.csproj @@ -1,14 +1,10 @@ - + Provides the fundamental classes to work with Messaging in a distributed architecture. messaging signed acknowledgeable asyncenumerable asyncenumerator - - - - diff --git a/src/Savvyio.Queries/Query.cs b/src/Savvyio.Queries/Query.cs index d5d4af05..6d605068 100644 --- a/src/Savvyio.Queries/Query.cs +++ b/src/Savvyio.Queries/Query.cs @@ -1,20 +1,19 @@ -namespace Savvyio.Queries +namespace Savvyio.Queries; + +/// +/// Provides a default implementation of of the interface. +/// +/// The type of the result to return. +/// +/// +public abstract record Query : Request, IQuery { /// - /// Provides a default implementation of of the interface. + /// Initializes a new instance of the class. /// - /// The type of the result to return. - /// - /// - public abstract record Query : Request, IQuery + /// The optional metadata to merge with this instance. + protected Query(IMetadata metadata = null) { - /// - /// Initializes a new instance of the class. - /// - /// The optional metadata to merge with this instance. - protected Query(IMetadata metadata = null) - { - this.MergeMetadata(metadata); - } + this.MergeMetadata(metadata); } } diff --git a/src/Savvyio.Queries/QueryDispatcher.cs b/src/Savvyio.Queries/QueryDispatcher.cs index ee9e1054..5acd1052 100644 --- a/src/Savvyio.Queries/QueryDispatcher.cs +++ b/src/Savvyio.Queries/QueryDispatcher.cs @@ -1,49 +1,48 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon; using Cuemon.Threading; using Savvyio.Dispatchers; -namespace Savvyio.Queries +namespace Savvyio.Queries; + +/// +/// Provides a default implementation of of the interface. +/// +/// +/// +public class QueryDispatcher : RequestReplyDispatcher, IQueryDispatcher { /// - /// Provides a default implementation of of the interface. + /// Initializes a new instance of the class. /// - /// - /// - public class QueryDispatcher : RequestReplyDispatcher, IQueryDispatcher + /// The provider of service implementations. + public QueryDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) { - /// - /// Initializes a new instance of the class. - /// - /// The provider of service implementations. - public QueryDispatcher(IServiceLocator serviceLocator) : base(serviceLocator) - { - } + } - /// - /// Queries the specified using Request-Reply/In-Out MEP. - /// - /// The type of the result to return. - /// The to request. - /// TResult. - public TResult Query(IQuery request) - { - Validator.ThrowIfNull(request); - return Dispatch(request, handler => handler.Delegates); - } + /// + /// Queries the specified using Request-Reply/In-Out MEP. + /// + /// The type of the result to return. + /// The to request. + /// TResult. + public TResult Query(IQuery request) + { + Validator.ThrowIfNull(request); + return Dispatch(request, handler => handler.Delegates); + } - /// - /// Queries the specified asynchronous using Request-Reply/In-Out MEP. - /// - /// The type of the result to return. - /// The to request. - /// The which may be configured. - /// A that represents the asynchronous operation. The task result contains the outcome of the query operation. - public Task QueryAsync(IQuery request, Action setup = null) - { - Validator.ThrowIfNull(request); - return DispatchAsync(request, handler => handler.Delegates, setup); - } + /// + /// Queries the specified asynchronous using Request-Reply/In-Out MEP. + /// + /// The type of the result to return. + /// The to request. + /// The which may be configured. + /// A that represents the asynchronous operation. The task result contains the outcome of the query operation. + public Task QueryAsync(IQuery request, Action setup = null) + { + Validator.ThrowIfNull(request); + return DispatchAsync(request, handler => handler.Delegates, setup); } } diff --git a/src/Savvyio.Queries/QueryHandler.cs b/src/Savvyio.Queries/QueryHandler.cs index 36addfdc..17beb652 100644 --- a/src/Savvyio.Queries/QueryHandler.cs +++ b/src/Savvyio.Queries/QueryHandler.cs @@ -1,31 +1,30 @@ -using Savvyio.Handlers; +using Savvyio.Handlers; -namespace Savvyio.Queries +namespace Savvyio.Queries; + +/// +/// Provides a generic and consistent way of handling Query objects that implements the interface. This is an abstract class. +/// +/// +public abstract class QueryHandler : IQueryHandler { /// - /// Provides a generic and consistent way of handling Query objects that implements the interface. This is an abstract class. + /// Initializes a new instance of the class. /// - /// - public abstract class QueryHandler : IQueryHandler + protected QueryHandler() { - /// - /// Initializes a new instance of the class. - /// - protected QueryHandler() - { - Delegates = HandlerFactory.CreateRequestReply(RegisterDelegates); - } + Delegates = HandlerFactory.CreateRequestReply(RegisterDelegates); + } - /// - /// Registers the delegates responsible of handling types that implements the interface. - /// - /// The registry that store the delegates of type . - protected abstract void RegisterDelegates(IRequestReplyRegistry handlers); + /// + /// Registers the delegates responsible of handling types that implements the interface. + /// + /// The registry that store the delegates of type . + protected abstract void RegisterDelegates(IRequestReplyRegistry handlers); - /// - /// Gets the activator responsible of invoking delegates that handles . - /// - /// The activator responsible of invoking delegates that handles . - public IRequestReplyActivator Delegates { get; } - } + /// + /// Gets the activator responsible of invoking delegates that handles . + /// + /// The activator responsible of invoking delegates that handles . + public IRequestReplyActivator Delegates { get; } } diff --git a/src/Savvyio.Queries/SavvyioOptionsExtensions.cs b/src/Savvyio.Queries/SavvyioOptionsExtensions.cs index 3a2625aa..d7a2665a 100644 --- a/src/Savvyio.Queries/SavvyioOptionsExtensions.cs +++ b/src/Savvyio.Queries/SavvyioOptionsExtensions.cs @@ -1,30 +1,29 @@ -namespace Savvyio.Queries +namespace Savvyio.Queries; + +/// +/// Extension methods for the class. +/// +public static class SavvyioOptionsExtensions { /// - /// Extension methods for the class. + /// Adds an implementation of the interface to (if not already registered). /// - public static class SavvyioOptionsExtensions + /// The type that implements the interface. + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + public static SavvyioOptions AddQueryHandler(this SavvyioOptions options) where TImplementation : class, IQueryHandler { - /// - /// Adds an implementation of the interface to (if not already registered). - /// - /// The type that implements the interface. - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - public static SavvyioOptions AddQueryHandler(this SavvyioOptions options) where TImplementation : class, IQueryHandler - { - options.AddHandler(); - return options; - } + options.AddHandler(); + return options; + } - /// - /// Adds a default implementation of the interface. - /// - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - public static SavvyioOptions AddQueryDispatcher(this SavvyioOptions options) - { - return options.AddDispatcher(); - } + /// + /// Adds a default implementation of the interface. + /// + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + public static SavvyioOptions AddQueryDispatcher(this SavvyioOptions options) + { + return options.AddDispatcher(); } } diff --git a/test/Savvyio.Assets.Dapper.Tests/DbConnectionExtensions.cs b/test/Savvyio.Assets.Dapper.Tests/DbConnectionExtensions.cs index d95004d6..891f80b1 100644 --- a/test/Savvyio.Assets.Dapper.Tests/DbConnectionExtensions.cs +++ b/test/Savvyio.Assets.Dapper.Tests/DbConnectionExtensions.cs @@ -1,35 +1,34 @@ -using System.Data; +using System.Data; using Dapper; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public static class DbConnectionExtensions { - public static class DbConnectionExtensions + public static IDbConnection SetDefaults(this IDbConnection cnn) { - public static IDbConnection SetDefaults(this IDbConnection cnn) - { - SqlMapper.AddTypeHandler(new GuidHandler()); - cnn.Open(); - return cnn; - } + SqlMapper.AddTypeHandler(new GuidHandler()); + cnn.Open(); + return cnn; + } - public static IDbConnection AddAccountTable(this IDbConnection cnn) - { - cnn.ExecuteAsync("CREATE TABLE AccountProjection (Id INTEGER PRIMARY KEY, FullName VARCHAR(255), EmailAddress VARCHAR(512))"); - return cnn; - } + public static IDbConnection AddAccountTable(this IDbConnection cnn) + { + cnn.ExecuteAsync("CREATE TABLE AccountProjection (Id INTEGER PRIMARY KEY, FullName VARCHAR(255), EmailAddress VARCHAR(512))"); + return cnn; + } - public static IDbConnection AddPlatformProviderTable(this IDbConnection cnn) - { - cnn.ExecuteAsync("CREATE TABLE PlatformProviderProjection (Id CHAR(36) PRIMARY KEY, Name VARCHAR(255), ThirdLevelDomainName VARCHAR(128), Description VARCHAR(512), " + - "MinimumPasswordLength INTEGER, " + - "EnforcePasswordHistory INTEGER, " + - "MinimumPasswordAge INTEGER, " + - "MaximumPasswordAge INTEGER, " + - "AccountLockoutThreshold INTEGER, " + - "AccountLockoutDuration INTEGER, " + - "AccountLockoutCounterReset INTEGER, " + - "PasswordComplexityRequirement INTEGER"); - return cnn; - } + public static IDbConnection AddPlatformProviderTable(this IDbConnection cnn) + { + cnn.ExecuteAsync("CREATE TABLE PlatformProviderProjection (Id CHAR(36) PRIMARY KEY, Name VARCHAR(255), ThirdLevelDomainName VARCHAR(128), Description VARCHAR(512), " + + "MinimumPasswordLength INTEGER, " + + "EnforcePasswordHistory INTEGER, " + + "MinimumPasswordAge INTEGER, " + + "MaximumPasswordAge INTEGER, " + + "AccountLockoutThreshold INTEGER, " + + "AccountLockoutDuration INTEGER, " + + "AccountLockoutCounterReset INTEGER, " + + "PasswordComplexityRequirement INTEGER"); + return cnn; } } diff --git a/test/Savvyio.Assets.Dapper.Tests/GuidHandler.cs b/test/Savvyio.Assets.Dapper.Tests/GuidHandler.cs index 2fcb243e..f0924832 100644 --- a/test/Savvyio.Assets.Dapper.Tests/GuidHandler.cs +++ b/test/Savvyio.Assets.Dapper.Tests/GuidHandler.cs @@ -1,13 +1,12 @@ -using System; +using System; using System.Data; using Dapper; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public class GuidHandler : SqlMapper.TypeHandler { - public class GuidHandler : SqlMapper.TypeHandler - { - public override void SetValue(IDbDataParameter parameter, Guid value) => parameter.Value = value; + public override void SetValue(IDbDataParameter parameter, Guid value) => parameter.Value = value; - public override Guid Parse(object value) => Guid.Parse((string)value); - } + public override Guid Parse(object value) => Guid.Parse((string)value); } diff --git a/test/Savvyio.Assets.EfCore.Tests/CustomEfCoreDataSource.cs b/test/Savvyio.Assets.EfCore.Tests/CustomEfCoreDataSource.cs index 7dc8116b..8234905f 100644 --- a/test/Savvyio.Assets.EfCore.Tests/CustomEfCoreDataSource.cs +++ b/test/Savvyio.Assets.EfCore.Tests/CustomEfCoreDataSource.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore; using Savvyio.Assets.Domain; using Savvyio.Domain; @@ -6,21 +6,20 @@ using Savvyio.Extensions.DependencyInjection.EFCore; using Savvyio.Extensions.DependencyInjection.EFCore.Domain; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public class CustomEfCoreDataSource : EfCoreAggregateDataSource { - public class CustomEfCoreDataSource : EfCoreAggregateDataSource + public CustomEfCoreDataSource(IDomainEventDispatcher dispatcher) : base(dispatcher, new EfCoreDataSourceOptions() { - public CustomEfCoreDataSource(IDomainEventDispatcher dispatcher) : base(dispatcher, new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase(nameof(Account)).EnableDetailedErrors().LogTo(Console.WriteLine), - ModelConstructor = mb => - { - mb.AddAccount(); - } - }) + ContextConfigurator = b => b.UseInMemoryDatabase(nameof(Account)).EnableDetailedErrors().LogTo(Console.WriteLine), + ModelConstructor = mb => { + mb.AddAccount(); } - - public IPersistentRepository Account => new EfCoreAggregateRepository(this); + }) + { } + + public IPersistentRepository Account => new EfCoreAggregateRepository(this); } diff --git a/test/Savvyio.Assets.EfCore.Tests/LongValueGenerator.cs b/test/Savvyio.Assets.EfCore.Tests/LongValueGenerator.cs index 6369cb64..5480229e 100644 --- a/test/Savvyio.Assets.EfCore.Tests/LongValueGenerator.cs +++ b/test/Savvyio.Assets.EfCore.Tests/LongValueGenerator.cs @@ -1,16 +1,15 @@ -using Cuemon; +using Cuemon; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ValueGeneration; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public class LongValueGenerator : ValueGenerator { - public class LongValueGenerator : ValueGenerator + public override long Next(EntityEntry entry) { - public override long Next(EntityEntry entry) - { - return Generate.RandomNumber(1, 100000); - } - - public override bool GeneratesTemporaryValues { get; } + return Generate.RandomNumber(1, 100000); } + + public override bool GeneratesTemporaryValues { get; } } diff --git a/test/Savvyio.Assets.EfCore.Tests/ModelBuilderExtensions.cs b/test/Savvyio.Assets.EfCore.Tests/ModelBuilderExtensions.cs index 732bc890..dfa23eea 100644 --- a/test/Savvyio.Assets.EfCore.Tests/ModelBuilderExtensions.cs +++ b/test/Savvyio.Assets.EfCore.Tests/ModelBuilderExtensions.cs @@ -1,57 +1,56 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using Savvyio.Assets.Domain; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public static class ModelBuilderExtensions { - public static class ModelBuilderExtensions + public static ModelBuilder AddAccount(this ModelBuilder mb) { - public static ModelBuilder AddAccount(this ModelBuilder mb) + return mb.Entity(entity => { - return mb.Entity(entity => - { - entity.ToTable(nameof(Account)); - entity.HasKey(x => new { x.Id }); - entity.Property(x => x.Id) - .HasColumnName("id") - .HasValueGenerator() - .ValueGeneratedOnAdd(); - entity.Property(x => x.PlatformProviderId) - .HasColumnName("platformProviderId") - .HasColumnType("uniqueidentifier"); - entity.Property(x => x.EmailAddress) - .HasColumnName("emailAddress") - .HasColumnType("varchar(256)"); - entity.Property(x => x.FullName) - .HasColumnName("fullName") - .HasColumnType("varchar(256)"); - entity.Property(x => x.Metadata) - .HasConversion(ap => JsonConvert.SerializeObject(ap), ap => JsonConvert.DeserializeObject(ap)); - }); - } + entity.ToTable(nameof(Account)); + entity.HasKey(x => new { x.Id }); + entity.Property(x => x.Id) + .HasColumnName("id") + .HasValueGenerator() + .ValueGeneratedOnAdd(); + entity.Property(x => x.PlatformProviderId) + .HasColumnName("platformProviderId") + .HasColumnType("uniqueidentifier"); + entity.Property(x => x.EmailAddress) + .HasColumnName("emailAddress") + .HasColumnType("varchar(256)"); + entity.Property(x => x.FullName) + .HasColumnName("fullName") + .HasColumnType("varchar(256)"); + entity.Property(x => x.Metadata) + .HasConversion(ap => JsonConvert.SerializeObject(ap), ap => JsonConvert.DeserializeObject(ap)); + }); + } - public static ModelBuilder AddPlatformProvider(this ModelBuilder mb) + public static ModelBuilder AddPlatformProvider(this ModelBuilder mb) + { + return mb.Entity(entity => { - return mb.Entity(entity => - { - entity.ToTable(nameof(PlatformProvider)); - entity.HasKey(x => new { x.Id }); - entity.Property(x => x.Id) - .HasColumnName("id"); - entity.Property(x => x.Policy) - .HasConversion(ap => JsonConvert.SerializeObject(ap), ap => JsonConvert.DeserializeObject(ap)); - entity.Property(x => x.Name) - .HasColumnName("name") - .HasColumnType("varchar(256)"); - entity.Property(x => x.ThirdLevelDomainName) - .HasColumnName("thirdLevelDomainName") - .HasColumnType("varchar(256)"); - entity.Property(x => x.Description) - .HasColumnName("description") - .HasColumnType("varchar(256)"); - entity.Property(x => x.Metadata) - .HasConversion(ap => JsonConvert.SerializeObject(ap), ap => JsonConvert.DeserializeObject(ap)); - }); - } + entity.ToTable(nameof(PlatformProvider)); + entity.HasKey(x => new { x.Id }); + entity.Property(x => x.Id) + .HasColumnName("id"); + entity.Property(x => x.Policy) + .HasConversion(ap => JsonConvert.SerializeObject(ap), ap => JsonConvert.DeserializeObject(ap)); + entity.Property(x => x.Name) + .HasColumnName("name") + .HasColumnType("varchar(256)"); + entity.Property(x => x.ThirdLevelDomainName) + .HasColumnName("thirdLevelDomainName") + .HasColumnType("varchar(256)"); + entity.Property(x => x.Description) + .HasColumnName("description") + .HasColumnType("varchar(256)"); + entity.Property(x => x.Metadata) + .HasConversion(ap => JsonConvert.SerializeObject(ap), ap => JsonConvert.DeserializeObject(ap)); + }); } } diff --git a/test/Savvyio.Assets.EfCore.Tests/Savvyio.Assets.EfCore.Tests.csproj b/test/Savvyio.Assets.EfCore.Tests/Savvyio.Assets.EfCore.Tests.csproj index f693c479..bd0fd02c 100644 --- a/test/Savvyio.Assets.EfCore.Tests/Savvyio.Assets.EfCore.Tests.csproj +++ b/test/Savvyio.Assets.EfCore.Tests/Savvyio.Assets.EfCore.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Assets diff --git a/test/Savvyio.Assets.Tests/AccountCommandHandler.cs b/test/Savvyio.Assets.Tests/AccountCommandHandler.cs index bad4819b..359bd4bd 100644 --- a/test/Savvyio.Assets.Tests/AccountCommandHandler.cs +++ b/test/Savvyio.Assets.Tests/AccountCommandHandler.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Savvyio.Assets.Commands; using Savvyio.Assets.Domain; @@ -8,53 +8,52 @@ using Savvyio.Extensions.DependencyInjection.Domain; using Savvyio.Handlers; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public class AccountCommandHandler : CommandHandler { - public class AccountCommandHandler : CommandHandler + private readonly IMediator _mediator; + private readonly IUnitOfWork _uow; + private readonly IPersistentRepository _accountRepository; + + public AccountCommandHandler(IMediator mediator = null, IUnitOfWork uow = null, IPersistentRepository accountRepository = null) { - private readonly IMediator _mediator; - private readonly IUnitOfWork _uow; - private readonly IPersistentRepository _accountRepository; + _mediator = mediator; + _uow = uow; + _accountRepository = accountRepository; + } - public AccountCommandHandler(IMediator mediator = null, IUnitOfWork uow = null, IPersistentRepository accountRepository = null) + protected override void RegisterDelegates(IFireForgetRegistry handlers) + { + handlers.RegisterAsync(CreateAccountAsync); + handlers.RegisterAsync(async c => { - _mediator = mediator; - _uow = uow; - _accountRepository = accountRepository; - } + var account = new Account(c.Id); + account.ChangeFullName(c.FullName); + account.ChangeEmailAddress(c.EmailAddress); + _accountRepository?.Add(account); // store in db + await _uow.SaveChangesAsync(); + await _mediator.PublishAsync(new AccountUpdated(account.Id, account.FullName, account.EmailAddress)); // raise integration event + }); + } - protected override void RegisterDelegates(IFireForgetRegistry handlers) + public async Task CreateAccountAsync(CreateAccount c) + { + var account = new Account(c.PlatformProviderId, c.FullName, c.EmailAddress).MergeMetadata(c); + + // check unique email + // through domain event - look at OnInProcAccountInitiated in Savvyio.Assets.Domain.Handlers.AccountDomainEventHandler + try { - handlers.RegisterAsync(CreateAccountAsync); - handlers.RegisterAsync(async c => - { - var account = new Account(c.Id); - account.ChangeFullName(c.FullName); - account.ChangeEmailAddress(c.EmailAddress); - _accountRepository?.Add(account); // store in db - await _uow.SaveChangesAsync(); - await _mediator.PublishAsync(new AccountUpdated(account.Id, account.FullName, account.EmailAddress)); // raise integration event - }); + _accountRepository.Add(account); + await _uow.SaveChangesAsync(); // store in db } - - public async Task CreateAccountAsync(CreateAccount c) + catch (Exception) { - var account = new Account(c.PlatformProviderId, c.FullName, c.EmailAddress).MergeMetadata(c); - - // check unique email - // through domain event - look at OnInProcAccountInitiated in Savvyio.Assets.Domain.Handlers.AccountDomainEventHandler - try - { - _accountRepository.Add(account); - await _uow.SaveChangesAsync(); // store in db - } - catch (Exception) - { - _accountRepository.Remove(account); // undo add - throw; - } - - await _mediator.PublishAsync(new AccountCreated(account.Id, account.FullName, account.EmailAddress).MergeMetadata(account)); // raise integration event + _accountRepository.Remove(account); // undo add + throw; } + + await _mediator.PublishAsync(new AccountCreated(account.Id, account.FullName, account.EmailAddress).MergeMetadata(account)); // raise integration event } } diff --git a/test/Savvyio.Assets.Tests/AccountEventHandler.cs b/test/Savvyio.Assets.Tests/AccountEventHandler.cs index c0e0dd45..061163c5 100644 --- a/test/Savvyio.Assets.Tests/AccountEventHandler.cs +++ b/test/Savvyio.Assets.Tests/AccountEventHandler.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Savvyio.Assets.EventDriven; @@ -12,51 +12,50 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public class AccountEventHandler : IntegrationEventHandler { - public class AccountEventHandler : IntegrationEventHandler + private readonly ITestOutputHelper _output; + private readonly ITestStore _testStore; + private readonly IPersistentDataStore _platformProviderDao; + private readonly IPersistentDataStore _accountDao; + private readonly IPublisher _eventBus; + + public AccountEventHandler(ITestOutputHelper output = null, ITestStore testStore = null, IPersistentDataStore accountDao = null, IPersistentDataStore platformProviderDao = null, IPublisher eventBus = null) { - private readonly ITestOutputHelper _output; - private readonly ITestStore _testStore; - private readonly IPersistentDataStore _platformProviderDao; - private readonly IPersistentDataStore _accountDao; - private readonly IPublisher _eventBus; + _output = output; + _testStore = testStore; + _accountDao = accountDao; + _platformProviderDao = platformProviderDao; + _eventBus = eventBus; + } - public AccountEventHandler(ITestOutputHelper output = null, ITestStore testStore = null, IPersistentDataStore accountDao = null, IPersistentDataStore platformProviderDao = null, IPublisher eventBus = null) - { - _output = output; - _testStore = testStore; - _accountDao = accountDao; - _platformProviderDao = platformProviderDao; - _eventBus = eventBus; - } + protected override void RegisterDelegates(IFireForgetRegistry handlers) + { + handlers.RegisterAsync(OnOutProcAccountCreated); + handlers.RegisterAsync(OnOutProcAccountUpdated); + } - protected override void RegisterDelegates(IFireForgetRegistry handlers) - { - handlers.RegisterAsync(OnOutProcAccountCreated); - handlers.RegisterAsync(OnOutProcAccountUpdated); - } + private Task OnOutProcAccountUpdated(AccountUpdated e) + { + _testStore?.Add(e); + _eventBus?.PublishAsync(e.ToMessage("urn:event:account-updated".ToSnsUri(), nameof(AccountUpdated))); + _output?.WriteLines($"IE {nameof(OnOutProcAccountUpdated)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + } - private Task OnOutProcAccountUpdated(AccountUpdated e) + public async Task OnOutProcAccountCreated(AccountCreated e) + { + _testStore?.Add(e); + if (_eventBus != null) { - _testStore?.Add(e); - _eventBus?.PublishAsync(e.ToMessage("urn:event:account-updated".ToSnsUri(), nameof(AccountUpdated))); - _output?.WriteLines($"IE {nameof(OnOutProcAccountUpdated)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; + await _eventBus.PublishAsync(e.ToMessage("account-created-flow-test.fifo".ToSnsUri(), nameof(AccountCreated))).ConfigureAwait(false); } - - public async Task OnOutProcAccountCreated(AccountCreated e) + _output?.WriteLines($"IE {nameof(OnOutProcAccountCreated)}", JsonSerializer.Serialize(e)); + if (_accountDao != null) { - _testStore?.Add(e); - if (_eventBus != null) - { - await _eventBus.PublishAsync(e.ToMessage("account-created-flow-test.fifo".ToSnsUri(), nameof(AccountCreated))).ConfigureAwait(false); - } - _output?.WriteLines($"IE {nameof(OnOutProcAccountCreated)}", JsonSerializer.Serialize(e)); - if (_accountDao != null) - { - await _accountDao.CreateAsync(new AccountProjection(e.Id, e.FullName, e.EmailAddress)).ConfigureAwait(false); - } + await _accountDao.CreateAsync(new AccountProjection(e.Id, e.FullName, e.EmailAddress)).ConfigureAwait(false); } } } diff --git a/test/Savvyio.Assets.Tests/AccountQueryHandler.cs b/test/Savvyio.Assets.Tests/AccountQueryHandler.cs index 653dc12e..34ab1365 100644 --- a/test/Savvyio.Assets.Tests/AccountQueryHandler.cs +++ b/test/Savvyio.Assets.Tests/AccountQueryHandler.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Cuemon; using Savvyio.Assets.EventDriven; using Savvyio.Assets.Queries; @@ -6,35 +6,34 @@ using Savvyio.Handlers; using Savvyio.Queries; -namespace Savvyio.Assets -{ - public class AccountQueryHandler : QueryHandler - { - private readonly IReadableDataStore _accountDao; +namespace Savvyio.Assets; - public AccountQueryHandler(IReadableDataStore accountDao = null) - { - _accountDao = accountDao; - } +public class AccountQueryHandler : QueryHandler +{ + private readonly IReadableDataStore _accountDao; - protected override void RegisterDelegates(IRequestReplyRegistry handlers) - { - handlers.RegisterAsync(GetAccountAsync); - handlers.RegisterAsync(s => Task.FromResult(new AccountProjection(222, "A", "B"))); - handlers.RegisterAsync(GetFakeAccountAsync); - handlers.RegisterAsync(_ => Task.FromResult(new AccountCreated(222, "A", "B"))); - } + public AccountQueryHandler(IReadableDataStore accountDao = null) + { + _accountDao = accountDao; + } - private Task GetFakeAccountAsync(GetFakeAccount a) - { - return Task.FromResult(new AccountCreated(a.Id, $"{a.Id}___{Generate.RandomString(16)}", $"{a.Id}@no.where")); - } + protected override void RegisterDelegates(IRequestReplyRegistry handlers) + { + handlers.RegisterAsync(GetAccountAsync); + handlers.RegisterAsync(s => Task.FromResult(new AccountProjection(222, "A", "B"))); + handlers.RegisterAsync(GetFakeAccountAsync); + handlers.RegisterAsync(_ => Task.FromResult(new AccountCreated(222, "A", "B"))); + } - private async Task GetAccountAsync(GetAccount arg1) - { - var dao = await _accountDao.GetByIdAsync(arg1.Id); - return dao; - } + private Task GetFakeAccountAsync(GetFakeAccount a) + { + return Task.FromResult(new AccountCreated(a.Id, $"{a.Id}___{Generate.RandomString(16)}", $"{a.Id}@no.where")); + } + private async Task GetAccountAsync(GetAccount arg1) + { + var dao = await _accountDao.GetByIdAsync(arg1.Id); + return dao; } + } diff --git a/test/Savvyio.Assets.Tests/Commands/CreateAccount.cs b/test/Savvyio.Assets.Tests/Commands/CreateAccount.cs index ce3fbf5a..6a116122 100644 --- a/test/Savvyio.Assets.Tests/Commands/CreateAccount.cs +++ b/test/Savvyio.Assets.Tests/Commands/CreateAccount.cs @@ -1,22 +1,21 @@ -using System; +using System; using Savvyio.Assets.Domain; using Savvyio.Commands; -namespace Savvyio.Assets.Commands +namespace Savvyio.Assets.Commands; + +public record CreateAccount : Command { - public record CreateAccount : Command + public CreateAccount(PlatformProviderId platformProviderId, FullName fullName, EmailAddress emailAddress) { - public CreateAccount(PlatformProviderId platformProviderId, FullName fullName, EmailAddress emailAddress) - { - PlatformProviderId = platformProviderId; - FullName = fullName; - EmailAddress = emailAddress; - } + PlatformProviderId = platformProviderId; + FullName = fullName; + EmailAddress = emailAddress; + } - public Guid PlatformProviderId { get; } + public Guid PlatformProviderId { get; } - public string FullName { get; } + public string FullName { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Assets.Tests/Commands/CreateAccountWithValueObjects.cs b/test/Savvyio.Assets.Tests/Commands/CreateAccountWithValueObjects.cs index b2ac8907..e94c3c00 100644 --- a/test/Savvyio.Assets.Tests/Commands/CreateAccountWithValueObjects.cs +++ b/test/Savvyio.Assets.Tests/Commands/CreateAccountWithValueObjects.cs @@ -1,21 +1,20 @@ -using Savvyio.Assets.Domain; +using Savvyio.Assets.Domain; using Savvyio.Commands; -namespace Savvyio.Assets.Commands +namespace Savvyio.Assets.Commands; + +public record CreateAccountWithValueObjects : Command { - public record CreateAccountWithValueObjects : Command + public CreateAccountWithValueObjects(PlatformProviderId platformProviderId, FullName fullName, EmailAddress emailAddress) { - public CreateAccountWithValueObjects(PlatformProviderId platformProviderId, FullName fullName, EmailAddress emailAddress) - { - PlatformProviderId = platformProviderId; - FullName = fullName; - EmailAddress = emailAddress; - } + PlatformProviderId = platformProviderId; + FullName = fullName; + EmailAddress = emailAddress; + } - public PlatformProviderId PlatformProviderId { get; } + public PlatformProviderId PlatformProviderId { get; } - public FullName FullName { get; } + public FullName FullName { get; } - public EmailAddress EmailAddress { get; } - } + public EmailAddress EmailAddress { get; } } diff --git a/test/Savvyio.Assets.Tests/Commands/CreateMemberCommand.cs b/test/Savvyio.Assets.Tests/Commands/CreateMemberCommand.cs index f7fd1dcc..67d01baf 100644 --- a/test/Savvyio.Assets.Tests/Commands/CreateMemberCommand.cs +++ b/test/Savvyio.Assets.Tests/Commands/CreateMemberCommand.cs @@ -1,20 +1,19 @@ -using Savvyio.Commands; +using Savvyio.Commands; -namespace Savvyio.Assets.Commands +namespace Savvyio.Assets.Commands; + +public record CreateMemberCommand : Command { - public record CreateMemberCommand : Command + public CreateMemberCommand(string name, byte age, string emailAddress) { - public CreateMemberCommand(string name, byte age, string emailAddress) - { - Name = name; - Age = age; - EmailAddress = emailAddress; - } + Name = name; + Age = age; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public byte Age { get; } + public byte Age { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Assets.Tests/Commands/CreatePlatformProvider.cs b/test/Savvyio.Assets.Tests/Commands/CreatePlatformProvider.cs index a4a6b922..0d073d95 100644 --- a/test/Savvyio.Assets.Tests/Commands/CreatePlatformProvider.cs +++ b/test/Savvyio.Assets.Tests/Commands/CreatePlatformProvider.cs @@ -1,21 +1,20 @@ -using Savvyio.Assets.Domain; +using Savvyio.Assets.Domain; using Savvyio.Commands; -namespace Savvyio.Assets.Commands +namespace Savvyio.Assets.Commands; + +public record CreatePlatformProvider : Command { - public record CreatePlatformProvider : Command + public CreatePlatformProvider(Name name, ThirdLevelDomainName thirdLevelDomainName, Description description = null) { - public CreatePlatformProvider(Name name, ThirdLevelDomainName thirdLevelDomainName, Description description = null) - { - Name = name; - ThirdLevelDomainName = thirdLevelDomainName; - Description = description; - } + Name = name; + ThirdLevelDomainName = thirdLevelDomainName; + Description = description; + } - public string Name { get; } + public string Name { get; } - public string ThirdLevelDomainName { get; } + public string ThirdLevelDomainName { get; } - public string Description { get; } - } + public string Description { get; } } diff --git a/test/Savvyio.Assets.Tests/Commands/UpdateAccount.cs b/test/Savvyio.Assets.Tests/Commands/UpdateAccount.cs index 413b2afc..e343ea58 100644 --- a/test/Savvyio.Assets.Tests/Commands/UpdateAccount.cs +++ b/test/Savvyio.Assets.Tests/Commands/UpdateAccount.cs @@ -1,21 +1,20 @@ -using Savvyio.Assets.Domain; +using Savvyio.Assets.Domain; using Savvyio.Commands; -namespace Savvyio.Assets.Commands +namespace Savvyio.Assets.Commands; + +public record UpdateAccount : Command { - public record UpdateAccount : Command + public UpdateAccount(AccountId id, FullName fullName, EmailAddress emailAddress) { - public UpdateAccount(AccountId id, FullName fullName, EmailAddress emailAddress) - { - Id = id; - FullName = fullName; - EmailAddress = emailAddress; - } + Id = id; + FullName = fullName; + EmailAddress = emailAddress; + } - public long Id { get; } + public long Id { get; } - public string FullName { get; } + public string FullName { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Assets.Tests/Commands/UpdatePlatformProvider.cs b/test/Savvyio.Assets.Tests/Commands/UpdatePlatformProvider.cs index 01fdb000..d5d4c7f5 100644 --- a/test/Savvyio.Assets.Tests/Commands/UpdatePlatformProvider.cs +++ b/test/Savvyio.Assets.Tests/Commands/UpdatePlatformProvider.cs @@ -1,25 +1,24 @@ -using System; +using System; using Savvyio.Assets.Domain; using Savvyio.Commands; -namespace Savvyio.Assets.Commands +namespace Savvyio.Assets.Commands; + +public record UpdatePlatformProvider : Command { - public record UpdatePlatformProvider : Command + public UpdatePlatformProvider(PlatformProviderId id, Name name, ThirdLevelDomainName thirdLevelDomainName, Description description = null) { - public UpdatePlatformProvider(PlatformProviderId id, Name name, ThirdLevelDomainName thirdLevelDomainName, Description description = null) - { - Id = id; - Name = name; - ThirdLevelDomainName = thirdLevelDomainName; - Description = description; - } + Id = id; + Name = name; + ThirdLevelDomainName = thirdLevelDomainName; + Description = description; + } - public Guid Id { get; } + public Guid Id { get; } - public string Name { get; } + public string Name { get; } - public string ThirdLevelDomainName { get; } + public string ThirdLevelDomainName { get; } - public string Description { get; } - } + public string Description { get; } } diff --git a/test/Savvyio.Assets.Tests/Commands/UpdatePlatformProviderAccountPolicy.cs b/test/Savvyio.Assets.Tests/Commands/UpdatePlatformProviderAccountPolicy.cs index d541f04f..978db668 100644 --- a/test/Savvyio.Assets.Tests/Commands/UpdatePlatformProviderAccountPolicy.cs +++ b/test/Savvyio.Assets.Tests/Commands/UpdatePlatformProviderAccountPolicy.cs @@ -1,39 +1,38 @@ -using System; +using System; using Savvyio.Commands; -namespace Savvyio.Assets.Commands +namespace Savvyio.Assets.Commands; + +public record UpdatePlatformProviderAccountPolicy : Command { - public record UpdatePlatformProviderAccountPolicy : Command + public UpdatePlatformProviderAccountPolicy(Guid id, int minimumPasswordLength, int enforcePasswordHistory, TimeSpan minimumPasswordAge, TimeSpan maximumPasswordAge, int accountLockoutThreshold, TimeSpan accountLockoutDuration, TimeSpan accountLockoutCounterReset, bool passwordComplexityRequirement) { - public UpdatePlatformProviderAccountPolicy(Guid id, int minimumPasswordLength, int enforcePasswordHistory, TimeSpan minimumPasswordAge, TimeSpan maximumPasswordAge, int accountLockoutThreshold, TimeSpan accountLockoutDuration, TimeSpan accountLockoutCounterReset, bool passwordComplexityRequirement) - { - Id = id; - MinimumPasswordLength = minimumPasswordLength; - EnforcePasswordHistory = enforcePasswordHistory; - MinimumPasswordAge = minimumPasswordAge; - MaximumPasswordAge = maximumPasswordAge; - AccountLockoutThreshold = accountLockoutThreshold; - AccountLockoutDuration = accountLockoutDuration; - AccountLockoutCounterReset = accountLockoutCounterReset; - PasswordComplexityRequirement = passwordComplexityRequirement; - } + Id = id; + MinimumPasswordLength = minimumPasswordLength; + EnforcePasswordHistory = enforcePasswordHistory; + MinimumPasswordAge = minimumPasswordAge; + MaximumPasswordAge = maximumPasswordAge; + AccountLockoutThreshold = accountLockoutThreshold; + AccountLockoutDuration = accountLockoutDuration; + AccountLockoutCounterReset = accountLockoutCounterReset; + PasswordComplexityRequirement = passwordComplexityRequirement; + } - public Guid Id { get; } + public Guid Id { get; } - public int MinimumPasswordLength { get; } + public int MinimumPasswordLength { get; } - public int EnforcePasswordHistory { get; } + public int EnforcePasswordHistory { get; } - public TimeSpan MinimumPasswordAge { get; } + public TimeSpan MinimumPasswordAge { get; } - public TimeSpan MaximumPasswordAge { get; } + public TimeSpan MaximumPasswordAge { get; } - public int AccountLockoutThreshold { get; } + public int AccountLockoutThreshold { get; } - public TimeSpan AccountLockoutDuration { get; } + public TimeSpan AccountLockoutDuration { get; } - public TimeSpan AccountLockoutCounterReset { get; } + public TimeSpan AccountLockoutCounterReset { get; } - public bool PasswordComplexityRequirement { get; } - } + public bool PasswordComplexityRequirement { get; } } diff --git a/test/Savvyio.Assets.Tests/DbMarker.cs b/test/Savvyio.Assets.Tests/DbMarker.cs index ef6a69c7..0377e408 100644 --- a/test/Savvyio.Assets.Tests/DbMarker.cs +++ b/test/Savvyio.Assets.Tests/DbMarker.cs @@ -1,10 +1,9 @@ -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public struct DbMarker { - public struct DbMarker - { - } +} - public struct AnotherDbMarker - { - } +public struct AnotherDbMarker +{ } diff --git a/test/Savvyio.Assets.Tests/Domain/Account.cs b/test/Savvyio.Assets.Tests/Domain/Account.cs index e40817fe..1ec63697 100644 --- a/test/Savvyio.Assets.Tests/Domain/Account.cs +++ b/test/Savvyio.Assets.Tests/Domain/Account.cs @@ -1,78 +1,77 @@ -using System; +using System; using Cuemon; using Savvyio.Assets.Domain.Events; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public class Account : AggregateRoot { - public class Account : AggregateRoot + Account() // efcore { - Account() // efcore - { - } + } - public Account(PlatformProviderId platformProviderId, FullName fullName, EmailAddress emailAddress) - { - Validator.ThrowIfNull(platformProviderId, nameof(platformProviderId)); - Validator.ThrowIfNull(fullName, nameof(fullName)); - Validator.ThrowIfNull(emailAddress, nameof(emailAddress)); - PlatformProviderId = platformProviderId; - FullName = fullName; - EmailAddress = emailAddress; - AddEvent(new AccountInitiated(this)); - } + public Account(PlatformProviderId platformProviderId, FullName fullName, EmailAddress emailAddress) + { + Validator.ThrowIfNull(platformProviderId, nameof(platformProviderId)); + Validator.ThrowIfNull(fullName, nameof(fullName)); + Validator.ThrowIfNull(emailAddress, nameof(emailAddress)); + PlatformProviderId = platformProviderId; + FullName = fullName; + EmailAddress = emailAddress; + AddEvent(new AccountInitiated(this)); + } - public Account(AccountId id) : base(id) - { - } + public Account(AccountId id) : base(id) + { + } - public Guid PlatformProviderId { get; private set; } + public Guid PlatformProviderId { get; private set; } - public string FullName { get; private set; } + public string FullName { get; private set; } - public string EmailAddress { get; private set; } + public string EmailAddress { get; private set; } - public void ChangeFullName(FullName fullName) - { - Validator.ThrowIfNull(fullName, nameof(fullName)); - if (fullName.Equals((FullName)FullName)) { return; } - FullName = fullName; - AddEvent(new AccountFullNameChanged(this)); - } + public void ChangeFullName(FullName fullName) + { + Validator.ThrowIfNull(fullName, nameof(fullName)); + if (fullName.Equals((FullName)FullName)) { return; } + FullName = fullName; + AddEvent(new AccountFullNameChanged(this)); + } - public void ChangeEmailAddress(EmailAddress emailAddress) - { - Validator.ThrowIfNull(emailAddress, nameof(emailAddress)); - if (emailAddress.Equals((EmailAddress)EmailAddress)) { return; } - EmailAddress = emailAddress; - AddEvent(new AccountEmailAddressChanged(this)); - } + public void ChangeEmailAddress(EmailAddress emailAddress) + { + Validator.ThrowIfNull(emailAddress, nameof(emailAddress)); + if (emailAddress.Equals((EmailAddress)EmailAddress)) { return; } + EmailAddress = emailAddress; + AddEvent(new AccountEmailAddressChanged(this)); + } - public void Promote(Credentials credentials) - { - Validator.ThrowIfNull(credentials, nameof(credentials)); - var identity = new UserAccount(Id, credentials.UserName); - AddEvent(new UserAccountInitiated(identity)); - ChangePassword(credentials); - } + public void Promote(Credentials credentials) + { + Validator.ThrowIfNull(credentials, nameof(credentials)); + var identity = new UserAccount(Id, credentials.UserName); + AddEvent(new UserAccountInitiated(identity)); + ChangePassword(credentials); + } - public void ChangePassword(Credentials credentials) - { - Validator.ThrowIfNull(credentials, nameof(credentials)); - var password = new UserAccountPassword(Id, credentials); - AddEvent(new UserAccountPasswordInitiated(password)); - } + public void ChangePassword(Credentials credentials) + { + Validator.ThrowIfNull(credentials, nameof(credentials)); + var password = new UserAccountPassword(Id, credentials); + AddEvent(new UserAccountPasswordInitiated(password)); + } - public void RegisterFailedLogonAttempt(string userHostAddress) - { - Validator.ThrowIfNullOrWhitespace(userHostAddress); - var failedLogonAttempt = new UserAccountFailedLogonAttempt(Id, userHostAddress); - AddEvent(new UserAccountFailedLogonAttemptCreated(failedLogonAttempt)); - } + public void RegisterFailedLogonAttempt(string userHostAddress) + { + Validator.ThrowIfNullOrWhitespace(userHostAddress); + var failedLogonAttempt = new UserAccountFailedLogonAttempt(Id, userHostAddress); + AddEvent(new UserAccountFailedLogonAttemptCreated(failedLogonAttempt)); + } - public void Demote() - { - AddEvent(new UserAccountRemoved(new UserAccount(Id))); - } + public void Demote() + { + AddEvent(new UserAccountRemoved(new UserAccount(Id))); } } diff --git a/test/Savvyio.Assets.Tests/Domain/AccountId.cs b/test/Savvyio.Assets.Tests/Domain/AccountId.cs index 2b6d34e9..2e4bb6cd 100644 --- a/test/Savvyio.Assets.Tests/Domain/AccountId.cs +++ b/test/Savvyio.Assets.Tests/Domain/AccountId.cs @@ -1,18 +1,17 @@ -using Cuemon; +using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public sealed record AccountId : SingleValueObject { - public sealed record AccountId : SingleValueObject + public static implicit operator AccountId(long value) { - public static implicit operator AccountId(long value) - { - return new AccountId(value); - } + return new AccountId(value); + } - public AccountId(long id) : base(id) - { - Validator.ThrowIfLowerThanOrEqual(id, 0, nameof(id)); - } + public AccountId(long id) : base(id) + { + Validator.ThrowIfLowerThanOrEqual(id, 0, nameof(id)); } } diff --git a/test/Savvyio.Assets.Tests/Domain/Coordinates.cs b/test/Savvyio.Assets.Tests/Domain/Coordinates.cs index 28e4a4d9..31134ec1 100644 --- a/test/Savvyio.Assets.Tests/Domain/Coordinates.cs +++ b/test/Savvyio.Assets.Tests/Domain/Coordinates.cs @@ -1,22 +1,21 @@ -using Cuemon; +using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record Coordinates : ValueObject { - public record Coordinates : ValueObject + public Coordinates(double latitude, double longitude) { - public Coordinates(double latitude, double longitude) - { - Validator.ThrowIfLowerThan(latitude, -90, nameof(latitude)); - Validator.ThrowIfGreaterThan(latitude, 90, nameof(latitude)); - Validator.ThrowIfLowerThan(longitude, -180, nameof(longitude)); - Validator.ThrowIfGreaterThan(longitude, 180, nameof(longitude)); - Latitude = latitude; - Longitude = longitude; - } + Validator.ThrowIfLowerThan(latitude, -90, nameof(latitude)); + Validator.ThrowIfGreaterThan(latitude, 90, nameof(latitude)); + Validator.ThrowIfLowerThan(longitude, -180, nameof(longitude)); + Validator.ThrowIfGreaterThan(longitude, 180, nameof(longitude)); + Latitude = latitude; + Longitude = longitude; + } - public double Latitude { get; } + public double Latitude { get; } - public double Longitude { get; } - } + public double Longitude { get; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Credentials.cs b/test/Savvyio.Assets.Tests/Domain/Credentials.cs index 58a6d48c..340b3665 100644 --- a/test/Savvyio.Assets.Tests/Domain/Credentials.cs +++ b/test/Savvyio.Assets.Tests/Domain/Credentials.cs @@ -1,32 +1,31 @@ -using Cuemon; +using Cuemon; using Cuemon.Collections.Generic; using Cuemon.Extensions; using Cuemon.Security.Cryptography; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record Credentials : ValueObject { - public record Credentials : ValueObject + public Credentials(string username, string password, string salt) { - public Credentials(string username, string password, string salt) - { - Validator.ThrowIfNullOrWhitespace(username); - Validator.ThrowIfNullOrWhitespace(password); - Validator.ThrowIfNullOrWhitespace(salt); - UserName = username; - Hash = KeyedHashFactory.CreateHmacCryptoSha256(salt.ToByteArray()).ComputeHash(password).ToHexadecimalString(); - Salt = salt; - } + Validator.ThrowIfNullOrWhitespace(username); + Validator.ThrowIfNullOrWhitespace(password); + Validator.ThrowIfNullOrWhitespace(salt); + UserName = username; + Hash = KeyedHashFactory.CreateHmacCryptoSha256(salt.ToByteArray()).ComputeHash(password).ToHexadecimalString(); + Salt = salt; + } - public string UserName { get; private set; } + public string UserName { get; private set; } - public string Hash { get; private set; } + public string Hash { get; private set; } - public string Salt { get; private set; } + public string Salt { get; private set; } - public override string ToString() - { - return DelimitedString.Create(Arguments.ToEnumerableOf(UserName, Hash), o => o.Delimiter = ":"); - } + public override string ToString() + { + return DelimitedString.Create(Arguments.ToEnumerableOf(UserName, Hash), o => o.Delimiter = ":"); } } diff --git a/test/Savvyio.Assets.Tests/Domain/Description.cs b/test/Savvyio.Assets.Tests/Domain/Description.cs index 7cc5ca54..36cdd128 100644 --- a/test/Savvyio.Assets.Tests/Domain/Description.cs +++ b/test/Savvyio.Assets.Tests/Domain/Description.cs @@ -1,21 +1,20 @@ -using Cuemon; +using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record Description : SingleValueObject { - public record Description : SingleValueObject + public static implicit operator Description(string value) { - public static implicit operator Description(string value) - { - return new Description(value); - } + return new Description(value); + } - public Description(string value = null) : base(value) + public Description(string value = null) : base(value) + { + Validator.CheckParameter(value, () => { - Validator.CheckParameter(value, () => - { - if (value != null) { Validator.ThrowIfGreaterThan(value.Length, 1024, nameof(value)); } - }); - } + if (value != null) { Validator.ThrowIfGreaterThan(value.Length, 1024, nameof(value)); } + }); } } diff --git a/test/Savvyio.Assets.Tests/Domain/EmailAddress.cs b/test/Savvyio.Assets.Tests/Domain/EmailAddress.cs index 8dade1c0..ec8e924a 100644 --- a/test/Savvyio.Assets.Tests/Domain/EmailAddress.cs +++ b/test/Savvyio.Assets.Tests/Domain/EmailAddress.cs @@ -1,19 +1,18 @@ -using Cuemon; +using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record EmailAddress : SingleValueObject { - public record EmailAddress : SingleValueObject + public static implicit operator EmailAddress(string value) { - public static implicit operator EmailAddress(string value) - { - return new EmailAddress(value); - } + return new EmailAddress(value); + } - public EmailAddress(string value) : base(value) - { - Validator.ThrowIfNullOrWhitespace(value); - Validator.ThrowIfNotEmailAddress(value); - } + public EmailAddress(string value) : base(value) + { + Validator.ThrowIfNullOrWhitespace(value); + Validator.ThrowIfNotEmailAddress(value); } } diff --git a/test/Savvyio.Assets.Tests/Domain/EventSourcing/TracedAccount.cs b/test/Savvyio.Assets.Tests/Domain/EventSourcing/TracedAccount.cs index 4c1727f0..9a5644a1 100644 --- a/test/Savvyio.Assets.Tests/Domain/EventSourcing/TracedAccount.cs +++ b/test/Savvyio.Assets.Tests/Domain/EventSourcing/TracedAccount.cs @@ -1,73 +1,72 @@ -using System; +using System; using System.Collections.Generic; using Cuemon; using Savvyio.Assets.Domain.Events; using Savvyio.Domain.EventSourcing; using Savvyio.Handlers; -namespace Savvyio.Assets.Domain.EventSourcing +namespace Savvyio.Assets.Domain.EventSourcing; + +public class TracedAccount : TracedAggregateRoot { - public class TracedAccount : TracedAggregateRoot + TracedAccount(Guid id, IEnumerable events) : this(new TracedAccountId(id), events) // EfCoreTracedAggregateRepository { - TracedAccount(Guid id, IEnumerable events) : this(new TracedAccountId(id), events) // EfCoreTracedAggregateRepository - { - } + } - public TracedAccount(TracedAccountId id, IEnumerable events) : base(id, events) - { - } + public TracedAccount(TracedAccountId id, IEnumerable events) : base(id, events) + { + } - public TracedAccount(TracedAccountId id, PlatformProviderId platformProviderId, FullName fullName, EmailAddress emailAddress) - { - Validator.ThrowIfNull(platformProviderId); - Validator.ThrowIfNull(fullName); - Validator.ThrowIfNull(emailAddress); - AddEvent(new TracedAccountInitiated(id, platformProviderId, fullName, emailAddress)); - } + public TracedAccount(TracedAccountId id, PlatformProviderId platformProviderId, FullName fullName, EmailAddress emailAddress) + { + Validator.ThrowIfNull(platformProviderId); + Validator.ThrowIfNull(fullName); + Validator.ThrowIfNull(emailAddress); + AddEvent(new TracedAccountInitiated(id, platformProviderId, fullName, emailAddress)); + } - protected override void RegisterDelegates(IFireForgetRegistry handler) - { - handler.Register(OnInitiated); - handler.Register(OnChangeFullName); - handler.Register(OnEmailChanged); - } + protected override void RegisterDelegates(IFireForgetRegistry handler) + { + handler.Register(OnInitiated); + handler.Register(OnChangeFullName); + handler.Register(OnEmailChanged); + } - private void OnInitiated(TracedAccountInitiated e) - { - Id = e.Id; - PlatformProviderId = e.PlatformProviderId; - FullName = e.FullName; - EmailAddress = e.EmailAddress; - } + private void OnInitiated(TracedAccountInitiated e) + { + Id = e.Id; + PlatformProviderId = e.PlatformProviderId; + FullName = e.FullName; + EmailAddress = e.EmailAddress; + } - private void OnChangeFullName(TracedAccountFullNameChanged e) - { - FullName = e.FullName; - } + private void OnChangeFullName(TracedAccountFullNameChanged e) + { + FullName = e.FullName; + } - private void OnEmailChanged(TracedAccountEmailAddressChanged e) - { - EmailAddress = e.EmailAddress; - } + private void OnEmailChanged(TracedAccountEmailAddressChanged e) + { + EmailAddress = e.EmailAddress; + } - public Guid PlatformProviderId { get; private set; } + public Guid PlatformProviderId { get; private set; } - public string FullName { get; private set; } + public string FullName { get; private set; } - public string EmailAddress { get; private set; } + public string EmailAddress { get; private set; } - public void ChangeFullName(FullName fullName) - { - Validator.ThrowIfNull(fullName); - if (fullName.Equals((FullName)FullName)) { return; } - AddEvent(new TracedAccountFullNameChanged(fullName)); - } + public void ChangeFullName(FullName fullName) + { + Validator.ThrowIfNull(fullName); + if (fullName.Equals((FullName)FullName)) { return; } + AddEvent(new TracedAccountFullNameChanged(fullName)); + } - public void ChangeEmailAddress(EmailAddress emailAddress) - { - Validator.ThrowIfNull(emailAddress); - if (emailAddress.Equals((EmailAddress)EmailAddress)) { return; } - AddEvent(new TracedAccountEmailAddressChanged(emailAddress)); - } + public void ChangeEmailAddress(EmailAddress emailAddress) + { + Validator.ThrowIfNull(emailAddress); + if (emailAddress.Equals((EmailAddress)EmailAddress)) { return; } + AddEvent(new TracedAccountEmailAddressChanged(emailAddress)); } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/AccountEmailAddressChanged.cs b/test/Savvyio.Assets.Tests/Domain/Events/AccountEmailAddressChanged.cs index 755b0534..0d8b9063 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/AccountEmailAddressChanged.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/AccountEmailAddressChanged.cs @@ -1,14 +1,13 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record AccountEmailAddressChanged : DomainEvent { - public record AccountEmailAddressChanged : DomainEvent + public AccountEmailAddressChanged(Account account) { - public AccountEmailAddressChanged(Account account) - { - EmailAddress = account.EmailAddress; - } - - public string EmailAddress { get; private set; } + EmailAddress = account.EmailAddress; } + + public string EmailAddress { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/AccountFullNameChanged.cs b/test/Savvyio.Assets.Tests/Domain/Events/AccountFullNameChanged.cs index b77c7b67..dca5435a 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/AccountFullNameChanged.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/AccountFullNameChanged.cs @@ -1,14 +1,13 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record AccountFullNameChanged : DomainEvent { - public record AccountFullNameChanged : DomainEvent + public AccountFullNameChanged(Account account) { - public AccountFullNameChanged(Account account) - { - FullName = account.FullName; - } - - public string FullName { get; private set; } + FullName = account.FullName; } + + public string FullName { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/AccountInitiated.cs b/test/Savvyio.Assets.Tests/Domain/Events/AccountInitiated.cs index 654194f8..39fdd263 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/AccountInitiated.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/AccountInitiated.cs @@ -1,25 +1,24 @@ -using System; +using System; using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record AccountInitiated : DomainEvent { - public record AccountInitiated : DomainEvent + public AccountInitiated() { - public AccountInitiated() - { - } + } - public AccountInitiated(Account account) - { - PlatformProviderId = account.PlatformProviderId; - FullName = account.FullName; - EmailAddress = account.EmailAddress; - } + public AccountInitiated(Account account) + { + PlatformProviderId = account.PlatformProviderId; + FullName = account.FullName; + EmailAddress = account.EmailAddress; + } - public Guid PlatformProviderId { get; private set; } + public Guid PlatformProviderId { get; private set; } - public string FullName { get; private set; } + public string FullName { get; private set; } - public string EmailAddress { get; private set; } - } + public string EmailAddress { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/AccountInitiatedChained.cs b/test/Savvyio.Assets.Tests/Domain/Events/AccountInitiatedChained.cs index f2ff7ac9..e8775653 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/AccountInitiatedChained.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/AccountInitiatedChained.cs @@ -1,11 +1,10 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record AccountInitiatedChained : DomainEvent { - public record AccountInitiatedChained : DomainEvent + public AccountInitiatedChained() { - public AccountInitiatedChained() - { - } } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderAccountPolicyChanged.cs b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderAccountPolicyChanged.cs index 8ddd84f9..80ba6ca9 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderAccountPolicyChanged.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderAccountPolicyChanged.cs @@ -1,24 +1,23 @@ -using System; +using System; using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record PlatformProviderAccountPolicyChanged : DomainEvent { - public record PlatformProviderAccountPolicyChanged : DomainEvent + PlatformProviderAccountPolicyChanged() { - PlatformProviderAccountPolicyChanged() - { - } + } - public PlatformProviderAccountPolicyChanged(PlatformProvider provider) - { - Id = provider.Id; - Policy = provider.Policy; - } + public PlatformProviderAccountPolicyChanged(PlatformProvider provider) + { + Id = provider.Id; + Policy = provider.Policy; + } - public Guid Id { get; private set; } + public Guid Id { get; private set; } - public PlatformProviderAccountPolicy Policy { get; private set; } - } + public PlatformProviderAccountPolicy Policy { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderDescriptionChanged.cs b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderDescriptionChanged.cs index 71d96d03..a4d99390 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderDescriptionChanged.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderDescriptionChanged.cs @@ -1,22 +1,21 @@ -using System; +using System; using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record PlatformProviderDescriptionChanged : DomainEvent { - public record PlatformProviderDescriptionChanged : DomainEvent + PlatformProviderDescriptionChanged() { - PlatformProviderDescriptionChanged() - { - } + } - public PlatformProviderDescriptionChanged(PlatformProvider provider) - { - Id = provider.Id; - Description = provider.Description; - } + public PlatformProviderDescriptionChanged(PlatformProvider provider) + { + Id = provider.Id; + Description = provider.Description; + } - public Guid Id { get; private set; } + public Guid Id { get; private set; } - public string Description { get; private set; } - } + public string Description { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderInitiated.cs b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderInitiated.cs index f03dce9f..578f6b52 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderInitiated.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderInitiated.cs @@ -1,31 +1,30 @@ -using System; +using System; using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record PlatformProviderInitiated : DomainEvent { - public record PlatformProviderInitiated : DomainEvent + PlatformProviderInitiated() { - PlatformProviderInitiated() - { - } + } - public PlatformProviderInitiated(PlatformProvider provider) - { - Id = provider.Id; - Name = provider.Name; - ThirdLevelDomainName = provider.ThirdLevelDomainName; - Description = provider.Description; - Policy = provider.Policy; - } + public PlatformProviderInitiated(PlatformProvider provider) + { + Id = provider.Id; + Name = provider.Name; + ThirdLevelDomainName = provider.ThirdLevelDomainName; + Description = provider.Description; + Policy = provider.Policy; + } - public Guid Id { get; private set; } + public Guid Id { get; private set; } - public string Name { get; private set; } + public string Name { get; private set; } - public string ThirdLevelDomainName { get; private set; } + public string ThirdLevelDomainName { get; private set; } - public string Description { get; private set; } + public string Description { get; private set; } - public PlatformProviderAccountPolicy Policy { get; private set; } - } + public PlatformProviderAccountPolicy Policy { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderNameChanged.cs b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderNameChanged.cs index 9898db89..cb4879a7 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderNameChanged.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderNameChanged.cs @@ -1,23 +1,22 @@ -using System; +using System; using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record PlatformProviderNameChanged : DomainEvent { - public record PlatformProviderNameChanged : DomainEvent + PlatformProviderNameChanged() { - PlatformProviderNameChanged() - { - } + } - public PlatformProviderNameChanged(PlatformProvider provider) - { - Id = provider.Id; - Name = provider.Name; - } + public PlatformProviderNameChanged(PlatformProvider provider) + { + Id = provider.Id; + Name = provider.Name; + } - public Guid Id { get; private set; } + public Guid Id { get; private set; } - public string Name { get; private set; } - } + public string Name { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderThirdLevelDomainNameChanged.cs b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderThirdLevelDomainNameChanged.cs index d03c47d8..4407b531 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderThirdLevelDomainNameChanged.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/PlatformProviderThirdLevelDomainNameChanged.cs @@ -1,22 +1,21 @@ -using System; +using System; using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record PlatformProviderThirdLevelDomainNameChanged : DomainEvent { - public record PlatformProviderThirdLevelDomainNameChanged : DomainEvent + PlatformProviderThirdLevelDomainNameChanged() { - PlatformProviderThirdLevelDomainNameChanged() - { - } + } - public PlatformProviderThirdLevelDomainNameChanged(PlatformProvider provider) - { - Id = provider.Id; - ThirdLevelDomainName = provider.ThirdLevelDomainName; - } + public PlatformProviderThirdLevelDomainNameChanged(PlatformProvider provider) + { + Id = provider.Id; + ThirdLevelDomainName = provider.ThirdLevelDomainName; + } - public Guid Id { get; private set; } + public Guid Id { get; private set; } - public string ThirdLevelDomainName { get; private set; } - } + public string ThirdLevelDomainName { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountEmailAddressChanged.cs b/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountEmailAddressChanged.cs index 54ce64da..9d8bf7be 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountEmailAddressChanged.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountEmailAddressChanged.cs @@ -1,14 +1,13 @@ -using Savvyio.Domain.EventSourcing; +using Savvyio.Domain.EventSourcing; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record TracedAccountEmailAddressChanged : TracedDomainEvent { - public record TracedAccountEmailAddressChanged : TracedDomainEvent + public TracedAccountEmailAddressChanged(string emailAddress) { - public TracedAccountEmailAddressChanged(string emailAddress) - { - EmailAddress = emailAddress; - } - - public string EmailAddress { get; private set; } + EmailAddress = emailAddress; } + + public string EmailAddress { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountFullNameChanged.cs b/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountFullNameChanged.cs index cff09762..793bbfc3 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountFullNameChanged.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountFullNameChanged.cs @@ -1,14 +1,13 @@ -using Savvyio.Domain.EventSourcing; +using Savvyio.Domain.EventSourcing; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record TracedAccountFullNameChanged : TracedDomainEvent { - public record TracedAccountFullNameChanged : TracedDomainEvent + public TracedAccountFullNameChanged(string fullName) { - public TracedAccountFullNameChanged(string fullName) - { - FullName = fullName; - } - - public string FullName { get; private set; } + FullName = fullName; } + + public string FullName { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountInitiated.cs b/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountInitiated.cs index 641957fb..f0dcf29f 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountInitiated.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/TracedAccountInitiated.cs @@ -1,24 +1,23 @@ -using System; +using System; using Savvyio.Domain.EventSourcing; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record TracedAccountInitiated : TracedDomainEvent { - public record TracedAccountInitiated : TracedDomainEvent + public TracedAccountInitiated(Guid id, Guid platformProviderId, string fullName, string emailAddress) { - public TracedAccountInitiated(Guid id, Guid platformProviderId, string fullName, string emailAddress) - { - Id = id; - PlatformProviderId = platformProviderId; - FullName = fullName; - EmailAddress = emailAddress; - } + Id = id; + PlatformProviderId = platformProviderId; + FullName = fullName; + EmailAddress = emailAddress; + } - public Guid Id { get; } + public Guid Id { get; } - public Guid PlatformProviderId { get; } + public Guid PlatformProviderId { get; } - public string FullName { get; } + public string FullName { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/UserAccountFailedLogonAttemptCreated.cs b/test/Savvyio.Assets.Tests/Domain/Events/UserAccountFailedLogonAttemptCreated.cs index 1660c196..9cd98135 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/UserAccountFailedLogonAttemptCreated.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/UserAccountFailedLogonAttemptCreated.cs @@ -1,21 +1,20 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record UserAccountFailedLogonAttemptCreated : DomainEvent { - public record UserAccountFailedLogonAttemptCreated : DomainEvent + public UserAccountFailedLogonAttemptCreated() { - public UserAccountFailedLogonAttemptCreated() - { - } + } - public UserAccountFailedLogonAttemptCreated(UserAccountFailedLogonAttempt failedLogonAttempt) - { - AccountId = failedLogonAttempt.AccountId; - UserHostAddress = failedLogonAttempt.UserHostAddress; - } + public UserAccountFailedLogonAttemptCreated(UserAccountFailedLogonAttempt failedLogonAttempt) + { + AccountId = failedLogonAttempt.AccountId; + UserHostAddress = failedLogonAttempt.UserHostAddress; + } - public long AccountId { get; private set; } + public long AccountId { get; private set; } - public string UserHostAddress { get; private set; } - } + public string UserHostAddress { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/UserAccountInitiated.cs b/test/Savvyio.Assets.Tests/Domain/Events/UserAccountInitiated.cs index 4f459dab..4ec914ea 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/UserAccountInitiated.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/UserAccountInitiated.cs @@ -1,18 +1,17 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record UserAccountInitiated : DomainEvent { - public record UserAccountInitiated : DomainEvent + public UserAccountInitiated() { - public UserAccountInitiated() - { - } - - public UserAccountInitiated(UserAccount account) - { - UserName = account.UserName; - } + } - public string UserName { get; private set; } + public UserAccountInitiated(UserAccount account) + { + UserName = account.UserName; } + + public string UserName { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/UserAccountPasswordInitiated.cs b/test/Savvyio.Assets.Tests/Domain/Events/UserAccountPasswordInitiated.cs index 7648034d..936df299 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/UserAccountPasswordInitiated.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/UserAccountPasswordInitiated.cs @@ -1,24 +1,23 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record UserAccountPasswordInitiated : DomainEvent { - public record UserAccountPasswordInitiated : DomainEvent + public UserAccountPasswordInitiated() { - public UserAccountPasswordInitiated() - { - } + } - public UserAccountPasswordInitiated(UserAccountPassword password) - { - AccountId = password.AccountId; - Hash = password.Hash; - Salt = password.Salt; - } + public UserAccountPasswordInitiated(UserAccountPassword password) + { + AccountId = password.AccountId; + Hash = password.Hash; + Salt = password.Salt; + } - public long AccountId { get; private set; } + public long AccountId { get; private set; } - public string Hash { get; private set; } + public string Hash { get; private set; } - public string Salt { get; private set; } - } + public string Salt { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/Events/UserAccountRemoved.cs b/test/Savvyio.Assets.Tests/Domain/Events/UserAccountRemoved.cs index 59c4a308..29842bb5 100644 --- a/test/Savvyio.Assets.Tests/Domain/Events/UserAccountRemoved.cs +++ b/test/Savvyio.Assets.Tests/Domain/Events/UserAccountRemoved.cs @@ -1,18 +1,17 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Assets.Domain.Events +namespace Savvyio.Assets.Domain.Events; + +public record UserAccountRemoved : DomainEvent { - public record UserAccountRemoved : DomainEvent + public UserAccountRemoved() { - public UserAccountRemoved() - { - } - - public UserAccountRemoved(UserAccount account) - { - Id = account.Id; - } + } - public long Id { get; private set; } + public UserAccountRemoved(UserAccount account) + { + Id = account.Id; } + + public long Id { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/FullName.cs b/test/Savvyio.Assets.Tests/Domain/FullName.cs index a439f4dc..733c0b64 100644 --- a/test/Savvyio.Assets.Tests/Domain/FullName.cs +++ b/test/Savvyio.Assets.Tests/Domain/FullName.cs @@ -1,22 +1,21 @@ -using Cuemon; +using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record FullName : SingleValueObject { - public record FullName : SingleValueObject + public static implicit operator FullName(string value) { - public static implicit operator FullName(string value) - { - return new FullName(value); - } + return new FullName(value); + } - public FullName(string firstName, string lastName) : this($"{firstName?.Trim()} {lastName?.Trim()}") - { - } + public FullName(string firstName, string lastName) : this($"{firstName?.Trim()} {lastName?.Trim()}") + { + } - public FullName(string value) : base(value) - { - Validator.ThrowIfNullOrWhitespace(value); - } + public FullName(string value) : base(value) + { + Validator.ThrowIfNullOrWhitespace(value); } } diff --git a/test/Savvyio.Assets.Tests/Domain/Handlers/AccountDomainEventHandler.cs b/test/Savvyio.Assets.Tests/Domain/Handlers/AccountDomainEventHandler.cs index 5eb3cf14..99b0b5a8 100644 --- a/test/Savvyio.Assets.Tests/Domain/Handlers/AccountDomainEventHandler.cs +++ b/test/Savvyio.Assets.Tests/Domain/Handlers/AccountDomainEventHandler.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Text.Json; using System.Threading.Tasks; using Codebelt.Extensions.Xunit; @@ -10,84 +10,83 @@ using Savvyio.Handlers; using Xunit; -namespace Savvyio.Assets.Domain.Handlers +namespace Savvyio.Assets.Domain.Handlers; + +public class AccountDomainEventHandler : DomainEventHandler { - public class AccountDomainEventHandler : DomainEventHandler - { - private readonly ITestOutputHelper _output; - private readonly ITestStore _testStore; - private readonly IDomainEventDispatcher _dispatcher; - private readonly ISearchableDataStore _accountDao; + private readonly ITestOutputHelper _output; + private readonly ITestStore _testStore; + private readonly IDomainEventDispatcher _dispatcher; + private readonly ISearchableDataStore _accountDao; - public AccountDomainEventHandler(ITestOutputHelper output = null, ITestStore testStore = null, IDomainEventDispatcher dispatcher = null, ISearchableDataStore accountDao = null) - { - _output = output; - _testStore = testStore; - _dispatcher = dispatcher; - _accountDao = accountDao; - } + public AccountDomainEventHandler(ITestOutputHelper output = null, ITestStore testStore = null, IDomainEventDispatcher dispatcher = null, ISearchableDataStore accountDao = null) + { + _output = output; + _testStore = testStore; + _dispatcher = dispatcher; + _accountDao = accountDao; + } - protected override void RegisterDelegates(IFireForgetRegistry handlers) - { - handlers.RegisterAsync(OnInProcAccountInitiated); - handlers.RegisterAsync(OnInProcAccountEmailAddressChanged); - handlers.RegisterAsync(OnInProcAccountFullNameChanged); - handlers.RegisterAsync(OnInProcAccountInitiatedChained); - handlers.RegisterAsync(OnInProcTracedAccountInitiated); - handlers.RegisterAsync(OnInProcTracedAccountEmailAddressChanged); - } + protected override void RegisterDelegates(IFireForgetRegistry handlers) + { + handlers.RegisterAsync(OnInProcAccountInitiated); + handlers.RegisterAsync(OnInProcAccountEmailAddressChanged); + handlers.RegisterAsync(OnInProcAccountFullNameChanged); + handlers.RegisterAsync(OnInProcAccountInitiatedChained); + handlers.RegisterAsync(OnInProcTracedAccountInitiated); + handlers.RegisterAsync(OnInProcTracedAccountEmailAddressChanged); + } - private Task OnInProcTracedAccountInitiated(TracedAccountInitiated e) - { - _testStore?.Add(e); - _output?.WriteLines($"DE {nameof(OnInProcTracedAccountInitiated)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - } + private Task OnInProcTracedAccountInitiated(TracedAccountInitiated e) + { + _testStore?.Add(e); + _output?.WriteLines($"DE {nameof(OnInProcTracedAccountInitiated)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + } - private Task OnInProcTracedAccountEmailAddressChanged(TracedAccountEmailAddressChanged e) - { - _testStore?.Add(e); - _output?.WriteLines($"DE {nameof(OnInProcTracedAccountEmailAddressChanged)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - } + private Task OnInProcTracedAccountEmailAddressChanged(TracedAccountEmailAddressChanged e) + { + _testStore?.Add(e); + _output?.WriteLines($"DE {nameof(OnInProcTracedAccountEmailAddressChanged)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + } - private Task OnInProcAccountInitiatedChained(AccountInitiatedChained e) - { - _testStore?.Add(e); - _output?.WriteLines($"DE {nameof(OnInProcAccountInitiatedChained)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - } + private Task OnInProcAccountInitiatedChained(AccountInitiatedChained e) + { + _testStore?.Add(e); + _output?.WriteLines($"DE {nameof(OnInProcAccountInitiatedChained)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + } - private Task OnInProcAccountFullNameChanged(AccountFullNameChanged e) - { - _testStore?.Add(e); - _output?.WriteLines($"DE {nameof(OnInProcAccountFullNameChanged)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - } + private Task OnInProcAccountFullNameChanged(AccountFullNameChanged e) + { + _testStore?.Add(e); + _output?.WriteLines($"DE {nameof(OnInProcAccountFullNameChanged)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + } - private Task OnInProcAccountEmailAddressChanged(AccountEmailAddressChanged e) - { - _testStore?.Add(e); - _output?.WriteLines($"DE {nameof(OnInProcAccountEmailAddressChanged)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - } + private Task OnInProcAccountEmailAddressChanged(AccountEmailAddressChanged e) + { + _testStore?.Add(e); + _output?.WriteLines($"DE {nameof(OnInProcAccountEmailAddressChanged)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + } - private async Task OnInProcAccountInitiated(AccountInitiated e) + private async Task OnInProcAccountInitiated(AccountInitiated e) + { + if (_accountDao != null) { - if (_accountDao != null) + var dao = await _accountDao.FindAllAsync(o => { - var dao = await _accountDao.FindAllAsync(o => - { - o.CommandText = "SELECT * FROM AccountProjection WHERE EmailAddress = @EmailAddress"; - o.Parameters = new { e.EmailAddress }; - }).SingleOrDefaultAsync().ConfigureAwait(false); + o.CommandText = "SELECT * FROM AccountProjection WHERE EmailAddress = @EmailAddress"; + o.Parameters = new { e.EmailAddress }; + }).SingleOrDefaultAsync().ConfigureAwait(false); - if (dao != null) { throw new ValidationException("Email address has already been registered."); } - } - - _testStore?.Add(e); - _output?.WriteLines($"DE {nameof(OnInProcAccountInitiated)}", JsonSerializer.Serialize(e)); - await _dispatcher.RaiseAsync(new AccountInitiatedChained().MergeMetadata(e).SetCausationId(e.GetEventId())).ConfigureAwait(false); + if (dao != null) { throw new ValidationException("Email address has already been registered."); } } + + _testStore?.Add(e); + _output?.WriteLines($"DE {nameof(OnInProcAccountInitiated)}", JsonSerializer.Serialize(e)); + await _dispatcher.RaiseAsync(new AccountInitiatedChained().MergeMetadata(e).SetCausationId(e.GetEventId())).ConfigureAwait(false); } } diff --git a/test/Savvyio.Assets.Tests/Domain/Handlers/PlatformProviderDomainEventHandler.cs b/test/Savvyio.Assets.Tests/Domain/Handlers/PlatformProviderDomainEventHandler.cs index 96b567aa..a45fa68b 100644 --- a/test/Savvyio.Assets.Tests/Domain/Handlers/PlatformProviderDomainEventHandler.cs +++ b/test/Savvyio.Assets.Tests/Domain/Handlers/PlatformProviderDomainEventHandler.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Domain.Events; @@ -6,51 +6,50 @@ using Savvyio.Handlers; using Xunit; -namespace Savvyio.Assets.Domain.Handlers +namespace Savvyio.Assets.Domain.Handlers; + +public class PlatformProviderDomainEventHandler : DomainEventHandler { - public class PlatformProviderDomainEventHandler : DomainEventHandler + private readonly ITestOutputHelper _output; + private readonly ITestStore _testStore; + + public PlatformProviderDomainEventHandler(ITestOutputHelper output = null, ITestStore testStore = null) { - private readonly ITestOutputHelper _output; - private readonly ITestStore _testStore; + _output = output; + _testStore = testStore; + } - public PlatformProviderDomainEventHandler(ITestOutputHelper output = null, ITestStore testStore = null) + protected override void RegisterDelegates(IFireForgetRegistry handlers) + { + handlers.RegisterAsync(e => { - _output = output; - _testStore = testStore; - } - - protected override void RegisterDelegates(IFireForgetRegistry handlers) + _testStore.Add(e); + _output.WriteLines($"DE {nameof(PlatformProviderAccountPolicyChanged)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + }); + handlers.RegisterAsync(e => + { + _testStore.Add(e); + _output.WriteLines($"DE {nameof(PlatformProviderInitiated)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + }); + handlers.RegisterAsync(e => + { + _testStore.Add(e); + _output.WriteLines($"DE {nameof(PlatformProviderDescriptionChanged)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + }); + handlers.RegisterAsync(e => + { + _testStore.Add(e); + _output.WriteLines($"DE {nameof(PlatformProviderNameChanged)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + }); + handlers.RegisterAsync(e => { - handlers.RegisterAsync(e => - { - _testStore.Add(e); - _output.WriteLines($"DE {nameof(PlatformProviderAccountPolicyChanged)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - }); - handlers.RegisterAsync(e => - { - _testStore.Add(e); - _output.WriteLines($"DE {nameof(PlatformProviderInitiated)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - }); - handlers.RegisterAsync(e => - { - _testStore.Add(e); - _output.WriteLines($"DE {nameof(PlatformProviderDescriptionChanged)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - }); - handlers.RegisterAsync(e => - { - _testStore.Add(e); - _output.WriteLines($"DE {nameof(PlatformProviderNameChanged)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - }); - handlers.RegisterAsync(e => - { - _testStore.Add(e); - _output.WriteLines($"DE {nameof(PlatformProviderThirdLevelDomainNameChanged)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - }); - } + _testStore.Add(e); + _output.WriteLines($"DE {nameof(PlatformProviderThirdLevelDomainNameChanged)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + }); } } diff --git a/test/Savvyio.Assets.Tests/Domain/Name.cs b/test/Savvyio.Assets.Tests/Domain/Name.cs index 1f31f033..86a115eb 100644 --- a/test/Savvyio.Assets.Tests/Domain/Name.cs +++ b/test/Savvyio.Assets.Tests/Domain/Name.cs @@ -1,18 +1,17 @@ -using Cuemon; +using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record Name : SingleValueObject { - public record Name : SingleValueObject + public static implicit operator Name(string value) { - public static implicit operator Name(string value) - { - return new Name(value); - } + return new Name(value); + } - public Name(string value) : base(value) - { - Validator.ThrowIfNullOrWhitespace(value); - } + public Name(string value) : base(value) + { + Validator.ThrowIfNullOrWhitespace(value); } } diff --git a/test/Savvyio.Assets.Tests/Domain/PlatformProvider.cs b/test/Savvyio.Assets.Tests/Domain/PlatformProvider.cs index 73b71e62..b1d0ac2a 100644 --- a/test/Savvyio.Assets.Tests/Domain/PlatformProvider.cs +++ b/test/Savvyio.Assets.Tests/Domain/PlatformProvider.cs @@ -1,70 +1,69 @@ -using System; +using System; using Cuemon; using Savvyio.Assets.Domain.Events; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public class PlatformProvider : AggregateRoot { - public class PlatformProvider : AggregateRoot + PlatformProvider() // efcore { - PlatformProvider() // efcore - { - } + } - public PlatformProvider(Name name, ThirdLevelDomainName thirdLevelDomainName, Description description = null) - { - Validator.ThrowIfNull(name); - Validator.ThrowIfNull(thirdLevelDomainName); - Id = Guid.NewGuid(); - Name = name; - ThirdLevelDomainName = thirdLevelDomainName; - Description = description ?? new Description(); - Policy = new PlatformProviderAccountPolicy(); - AddEvent(new PlatformProviderInitiated(this)); - } + public PlatformProvider(Name name, ThirdLevelDomainName thirdLevelDomainName, Description description = null) + { + Validator.ThrowIfNull(name); + Validator.ThrowIfNull(thirdLevelDomainName); + Id = Guid.NewGuid(); + Name = name; + ThirdLevelDomainName = thirdLevelDomainName; + Description = description ?? new Description(); + Policy = new PlatformProviderAccountPolicy(); + AddEvent(new PlatformProviderInitiated(this)); + } - public PlatformProvider(PlatformProviderId id) : base(id) - { - } + public PlatformProvider(PlatformProviderId id) : base(id) + { + } - public string Name { get; private set; } + public string Name { get; private set; } - public string ThirdLevelDomainName { get; private set; } + public string ThirdLevelDomainName { get; private set; } - public string Description { get; private set; } + public string Description { get; private set; } - public PlatformProviderAccountPolicy Policy { get; private set; } + public PlatformProviderAccountPolicy Policy { get; private set; } - public void ChangeName(Name name) - { - Validator.ThrowIfNullOrWhitespace(name); - if (name.Equals((Name)Name)) { return; } - Name = name; - AddEvent(new PlatformProviderNameChanged(this)); - } + public void ChangeName(Name name) + { + Validator.ThrowIfNullOrWhitespace(name); + if (name.Equals((Name)Name)) { return; } + Name = name; + AddEvent(new PlatformProviderNameChanged(this)); + } - public void ChangeThirdLevelDomainName(ThirdLevelDomainName thirdLevelDomainName) - { - Validator.ThrowIfNull(thirdLevelDomainName); - if (thirdLevelDomainName.Equals((ThirdLevelDomainName)ThirdLevelDomainName)) { return; } - ThirdLevelDomainName = thirdLevelDomainName; - AddEvent(new PlatformProviderThirdLevelDomainNameChanged(this)); - } + public void ChangeThirdLevelDomainName(ThirdLevelDomainName thirdLevelDomainName) + { + Validator.ThrowIfNull(thirdLevelDomainName); + if (thirdLevelDomainName.Equals((ThirdLevelDomainName)ThirdLevelDomainName)) { return; } + ThirdLevelDomainName = thirdLevelDomainName; + AddEvent(new PlatformProviderThirdLevelDomainNameChanged(this)); + } - public void ChangeDescription(Description description) - { - Validator.ThrowIfNull(description); - if (Description != null && description.Equals((Description)Description)) { return; } - Description = description; - AddEvent(new PlatformProviderDescriptionChanged(this)); - } + public void ChangeDescription(Description description) + { + Validator.ThrowIfNull(description); + if (Description != null && description.Equals((Description)Description)) { return; } + Description = description; + AddEvent(new PlatformProviderDescriptionChanged(this)); + } - public void ChangeAccountPolicy(PlatformProviderAccountPolicy policy) - { - Validator.ThrowIfNull(policy); - if (policy.Equals(Policy)) { return; } - Policy = policy; - AddEvent(new PlatformProviderAccountPolicyChanged(this)); - } + public void ChangeAccountPolicy(PlatformProviderAccountPolicy policy) + { + Validator.ThrowIfNull(policy); + if (policy.Equals(Policy)) { return; } + Policy = policy; + AddEvent(new PlatformProviderAccountPolicyChanged(this)); } } diff --git a/test/Savvyio.Assets.Tests/Domain/PlatformProviderAccountPolicy.cs b/test/Savvyio.Assets.Tests/Domain/PlatformProviderAccountPolicy.cs index 32c7b852..a6701a28 100644 --- a/test/Savvyio.Assets.Tests/Domain/PlatformProviderAccountPolicy.cs +++ b/test/Savvyio.Assets.Tests/Domain/PlatformProviderAccountPolicy.cs @@ -1,40 +1,39 @@ -using System; +using System; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record PlatformProviderAccountPolicy : ValueObject { - public record PlatformProviderAccountPolicy : ValueObject + public PlatformProviderAccountPolicy() : this(8, 0, TimeSpan.Zero, TimeSpan.Zero, 10, TimeSpan.Zero, TimeSpan.Zero, true) { - public PlatformProviderAccountPolicy() : this(8, 0, TimeSpan.Zero, TimeSpan.Zero, 10, TimeSpan.Zero, TimeSpan.Zero, true) - { - } + } - public PlatformProviderAccountPolicy(int minimumPasswordLength, int enforcePasswordHistory, TimeSpan minimumPasswordAge, TimeSpan maximumPasswordAge, int accountLockoutThreshold, TimeSpan accountLockoutDuration, TimeSpan accountLockoutCounterReset, bool passwordComplexityRequirement) - { - MinimumPasswordLength = minimumPasswordLength; - EnforcePasswordHistory = enforcePasswordHistory; - MinimumPasswordAge = minimumPasswordAge.Ticks; - MaximumPasswordAge = maximumPasswordAge.Ticks; - AccountLockoutThreshold = accountLockoutThreshold; - AccountLockoutDuration = accountLockoutDuration.Ticks; - AccountLockoutCounterReset = accountLockoutCounterReset.Ticks; - PasswordComplexityRequirement = passwordComplexityRequirement; - } + public PlatformProviderAccountPolicy(int minimumPasswordLength, int enforcePasswordHistory, TimeSpan minimumPasswordAge, TimeSpan maximumPasswordAge, int accountLockoutThreshold, TimeSpan accountLockoutDuration, TimeSpan accountLockoutCounterReset, bool passwordComplexityRequirement) + { + MinimumPasswordLength = minimumPasswordLength; + EnforcePasswordHistory = enforcePasswordHistory; + MinimumPasswordAge = minimumPasswordAge.Ticks; + MaximumPasswordAge = maximumPasswordAge.Ticks; + AccountLockoutThreshold = accountLockoutThreshold; + AccountLockoutDuration = accountLockoutDuration.Ticks; + AccountLockoutCounterReset = accountLockoutCounterReset.Ticks; + PasswordComplexityRequirement = passwordComplexityRequirement; + } - public int MinimumPasswordLength { get; private set; } + public int MinimumPasswordLength { get; private set; } - public int EnforcePasswordHistory { get; private set; } + public int EnforcePasswordHistory { get; private set; } - public long MinimumPasswordAge { get; private set; } + public long MinimumPasswordAge { get; private set; } - public long MaximumPasswordAge { get; private set; } + public long MaximumPasswordAge { get; private set; } - public int AccountLockoutThreshold { get; private set; } + public int AccountLockoutThreshold { get; private set; } - public long AccountLockoutDuration { get; private set; } + public long AccountLockoutDuration { get; private set; } - public long AccountLockoutCounterReset { get; private set; } + public long AccountLockoutCounterReset { get; private set; } - public bool PasswordComplexityRequirement { get; private set; } - } + public bool PasswordComplexityRequirement { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/PlatformProviderId.cs b/test/Savvyio.Assets.Tests/Domain/PlatformProviderId.cs index 3fb3287d..96c88d1d 100644 --- a/test/Savvyio.Assets.Tests/Domain/PlatformProviderId.cs +++ b/test/Savvyio.Assets.Tests/Domain/PlatformProviderId.cs @@ -1,19 +1,18 @@ -using System; +using System; using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public sealed record PlatformProviderId : SingleValueObject { - public sealed record PlatformProviderId : SingleValueObject + public static implicit operator PlatformProviderId(Guid value) { - public static implicit operator PlatformProviderId(Guid value) - { - return new PlatformProviderId(value); - } + return new PlatformProviderId(value); + } - public PlatformProviderId(Guid id) : base(id) - { - Validator.ThrowIfEqual(id, Guid.Empty, nameof(id)); - } + public PlatformProviderId(Guid id) : base(id) + { + Validator.ThrowIfEqual(id, Guid.Empty, nameof(id)); } } diff --git a/test/Savvyio.Assets.Tests/Domain/ThirdLevelDomainName.cs b/test/Savvyio.Assets.Tests/Domain/ThirdLevelDomainName.cs index a2ee9e93..0517bfa7 100644 --- a/test/Savvyio.Assets.Tests/Domain/ThirdLevelDomainName.cs +++ b/test/Savvyio.Assets.Tests/Domain/ThirdLevelDomainName.cs @@ -1,19 +1,18 @@ -using Cuemon; +using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record ThirdLevelDomainName : SingleValueObject { - public record ThirdLevelDomainName : SingleValueObject + public static implicit operator ThirdLevelDomainName(string value) { - public static implicit operator ThirdLevelDomainName(string value) - { - return new ThirdLevelDomainName(value); - } + return new ThirdLevelDomainName(value); + } - public ThirdLevelDomainName(string value) : base(value) - { - Validator.ThrowIfNullOrWhitespace(value); - Validator.ThrowIfDifferent(Alphanumeric.Letters, value, nameof(value), $"One or more invalid character(s) specified. Allowed characters are: {Alphanumeric.Letters}"); - } + public ThirdLevelDomainName(string value) : base(value) + { + Validator.ThrowIfNullOrWhitespace(value); + Validator.ThrowIfDifferent(Alphanumeric.Letters, value, nameof(value), $"One or more invalid character(s) specified. Allowed characters are: {Alphanumeric.Letters}"); } } diff --git a/test/Savvyio.Assets.Tests/Domain/Timestamp.cs b/test/Savvyio.Assets.Tests/Domain/Timestamp.cs index a1d5389a..524dd439 100644 --- a/test/Savvyio.Assets.Tests/Domain/Timestamp.cs +++ b/test/Savvyio.Assets.Tests/Domain/Timestamp.cs @@ -1,19 +1,18 @@ -using System; +using System; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record Timestamp : ValueObject { - public record Timestamp : ValueObject + public Timestamp(DateTimeOffset value) { - public Timestamp(DateTimeOffset value) - { - Value = value; - } + Value = value; + } - public DateTimeOffset Value { get; } + public DateTimeOffset Value { get; } - public string TimeZone { get; set; } + public string TimeZone { get; set; } - public bool IsDaylightSavingTime { get; set; } - } + public bool IsDaylightSavingTime { get; set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/TracedAccountId.cs b/test/Savvyio.Assets.Tests/Domain/TracedAccountId.cs index 7fef2967..4214207a 100644 --- a/test/Savvyio.Assets.Tests/Domain/TracedAccountId.cs +++ b/test/Savvyio.Assets.Tests/Domain/TracedAccountId.cs @@ -1,19 +1,18 @@ -using System; +using System; using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public record TracedAccountId : SingleValueObject { - public record TracedAccountId : SingleValueObject + public static implicit operator TracedAccountId(Guid value) { - public static implicit operator TracedAccountId(Guid value) - { - return new TracedAccountId(value); - } + return new TracedAccountId(value); + } - public TracedAccountId(Guid id) : base(id) - { - Validator.ThrowIfTrue(() => id == Guid.Empty, nameof(id), "Value cannot be an empty UUID."); - } + public TracedAccountId(Guid id) : base(id) + { + Validator.ThrowIfTrue(() => id == Guid.Empty, nameof(id), "Value cannot be an empty UUID."); } } diff --git a/test/Savvyio.Assets.Tests/Domain/UserAccount.cs b/test/Savvyio.Assets.Tests/Domain/UserAccount.cs index a653dd48..1af37b0c 100644 --- a/test/Savvyio.Assets.Tests/Domain/UserAccount.cs +++ b/test/Savvyio.Assets.Tests/Domain/UserAccount.cs @@ -1,32 +1,31 @@ -using System; +using System; using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public class UserAccount : Entity { - public class UserAccount : Entity + public UserAccount(AccountId accountId, string userName) : base(accountId) { - public UserAccount(AccountId accountId, string userName) : base(accountId) - { - Validator.ThrowIfNullOrWhitespace(userName); - UserName = userName; - } + Validator.ThrowIfNullOrWhitespace(userName); + UserName = userName; + } - public UserAccount(AccountId id) : base(id) - { + public UserAccount(AccountId id) : base(id) + { - } + } - public string UserName { get; private set; } + public string UserName { get; private set; } - public int FailedLogonAttemptCount { get; private set; } + public int FailedLogonAttemptCount { get; private set; } - public int SuccessfulLogonCount { get; private set; } + public int SuccessfulLogonCount { get; private set; } - public DateTimeOffset LastLogon { get; private set; } + public DateTimeOffset LastLogon { get; private set; } - public DateTimeOffset AccountLockout { get; private set; } + public DateTimeOffset AccountLockout { get; private set; } - public DateTimeOffset AccountLockoutReset { get; private set; } - } + public DateTimeOffset AccountLockoutReset { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/UserAccountFailedLogonAttempt.cs b/test/Savvyio.Assets.Tests/Domain/UserAccountFailedLogonAttempt.cs index be6cc7a3..7656739b 100644 --- a/test/Savvyio.Assets.Tests/Domain/UserAccountFailedLogonAttempt.cs +++ b/test/Savvyio.Assets.Tests/Domain/UserAccountFailedLogonAttempt.cs @@ -1,21 +1,20 @@ -using Cuemon; +using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public class UserAccountFailedLogonAttempt : Entity { - public class UserAccountFailedLogonAttempt : Entity + public UserAccountFailedLogonAttempt(AccountId accountId, string userHostAddress) { - public UserAccountFailedLogonAttempt(AccountId accountId, string userHostAddress) - { - Validator.ThrowIfNull(accountId); - Validator.ThrowIfNullOrWhitespace(userHostAddress); + Validator.ThrowIfNull(accountId); + Validator.ThrowIfNullOrWhitespace(userHostAddress); - AccountId = accountId; - UserHostAddress = userHostAddress; - } + AccountId = accountId; + UserHostAddress = userHostAddress; + } - public long AccountId { get; private set; } + public long AccountId { get; private set; } - public string UserHostAddress { get; private set; } - } + public string UserHostAddress { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/Domain/UserAccountPassword.cs b/test/Savvyio.Assets.Tests/Domain/UserAccountPassword.cs index c193845a..00c7268a 100644 --- a/test/Savvyio.Assets.Tests/Domain/UserAccountPassword.cs +++ b/test/Savvyio.Assets.Tests/Domain/UserAccountPassword.cs @@ -1,24 +1,23 @@ -using Cuemon; +using Cuemon; using Savvyio.Domain; -namespace Savvyio.Assets.Domain +namespace Savvyio.Assets.Domain; + +public class UserAccountPassword : Entity { - public class UserAccountPassword : Entity + public UserAccountPassword(AccountId accountId, Credentials password) { - public UserAccountPassword(AccountId accountId, Credentials password) - { - Validator.ThrowIfNull(accountId); - Validator.ThrowIfNull(password); + Validator.ThrowIfNull(accountId); + Validator.ThrowIfNull(password); - AccountId = accountId; - Hash = password.Hash; - Salt = password.Salt; - } + AccountId = accountId; + Hash = password.Hash; + Salt = password.Salt; + } - public long AccountId { get; private set; } + public long AccountId { get; private set; } - public string Hash { get; private set; } + public string Hash { get; private set; } - public string Salt { get; private set; } - } + public string Salt { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/EventDriven/AccountCreated.cs b/test/Savvyio.Assets.Tests/EventDriven/AccountCreated.cs index 6622a123..ccaf9d75 100644 --- a/test/Savvyio.Assets.Tests/EventDriven/AccountCreated.cs +++ b/test/Savvyio.Assets.Tests/EventDriven/AccountCreated.cs @@ -1,24 +1,23 @@ -using Savvyio.EventDriven; +using Savvyio.EventDriven; -namespace Savvyio.Assets.EventDriven +namespace Savvyio.Assets.EventDriven; + +public record AccountCreated : IntegrationEvent { - public record AccountCreated : IntegrationEvent + AccountCreated() { - AccountCreated() - { - } + } - public AccountCreated(long id, string fullName, string emailAddress) - { - Id = id; - FullName = fullName; - EmailAddress = emailAddress; - } + public AccountCreated(long id, string fullName, string emailAddress) + { + Id = id; + FullName = fullName; + EmailAddress = emailAddress; + } - public long Id { get; private set; } + public long Id { get; private set; } - public string FullName { get; private set; } + public string FullName { get; private set; } - public string EmailAddress { get; private set; } - } + public string EmailAddress { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/EventDriven/AccountUpdated.cs b/test/Savvyio.Assets.Tests/EventDriven/AccountUpdated.cs index 48eb5ee4..f2467e66 100644 --- a/test/Savvyio.Assets.Tests/EventDriven/AccountUpdated.cs +++ b/test/Savvyio.Assets.Tests/EventDriven/AccountUpdated.cs @@ -1,24 +1,23 @@ -using Savvyio.EventDriven; +using Savvyio.EventDriven; -namespace Savvyio.Assets.EventDriven +namespace Savvyio.Assets.EventDriven; + +public record AccountUpdated : IntegrationEvent { - public record AccountUpdated : IntegrationEvent + AccountUpdated() { - AccountUpdated() - { - } + } - public AccountUpdated(long id, string fullName, string emailAddress) - { - Id = id; - FullName = fullName; - EmailAddress = emailAddress; - } + public AccountUpdated(long id, string fullName, string emailAddress) + { + Id = id; + FullName = fullName; + EmailAddress = emailAddress; + } - public long Id { get; private set; } + public long Id { get; private set; } - public string FullName { get; private set; } + public string FullName { get; private set; } - public string EmailAddress { get; private set; } - } + public string EmailAddress { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/EventDriven/MemberCreated.cs b/test/Savvyio.Assets.Tests/EventDriven/MemberCreated.cs index 093e5d6c..58d0c631 100644 --- a/test/Savvyio.Assets.Tests/EventDriven/MemberCreated.cs +++ b/test/Savvyio.Assets.Tests/EventDriven/MemberCreated.cs @@ -1,17 +1,16 @@ -using Savvyio.EventDriven; +using Savvyio.EventDriven; -namespace Savvyio.Assets.EventDriven +namespace Savvyio.Assets.EventDriven; + +public record MemberCreated : IntegrationEvent { - public record MemberCreated : IntegrationEvent + public MemberCreated(string name, string emailAddress) { - public MemberCreated(string name, string emailAddress) - { - Name = name; - EmailAddress = emailAddress; - } + Name = name; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Assets.Tests/EventDriven/PlatformProviderCreated.cs b/test/Savvyio.Assets.Tests/EventDriven/PlatformProviderCreated.cs index 2129bb09..8fb1fb98 100644 --- a/test/Savvyio.Assets.Tests/EventDriven/PlatformProviderCreated.cs +++ b/test/Savvyio.Assets.Tests/EventDriven/PlatformProviderCreated.cs @@ -1,32 +1,31 @@ -using System; +using System; using Savvyio.Assets.Domain; using Savvyio.EventDriven; -namespace Savvyio.Assets.EventDriven +namespace Savvyio.Assets.EventDriven; + +public record PlatformProviderCreated : IntegrationEvent { - public record PlatformProviderCreated : IntegrationEvent + PlatformProviderCreated() { - PlatformProviderCreated() - { - } + } - public PlatformProviderCreated(PlatformProvider provider) - { - Id = provider.Id; - Name = provider.Name; - ThirdLevelDomainName = provider.ThirdLevelDomainName; - Description = provider.Description; - Policy = provider.Policy; - } + public PlatformProviderCreated(PlatformProvider provider) + { + Id = provider.Id; + Name = provider.Name; + ThirdLevelDomainName = provider.ThirdLevelDomainName; + Description = provider.Description; + Policy = provider.Policy; + } - public Guid Id { get; private set; } + public Guid Id { get; private set; } - public string Name { get; private set; } + public string Name { get; private set; } - public string ThirdLevelDomainName { get; private set; } + public string ThirdLevelDomainName { get; private set; } - public string Description { get; private set; } + public string Description { get; private set; } - public PlatformProviderAccountPolicy Policy { get; private set; } - } + public PlatformProviderAccountPolicy Policy { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/EventDriven/PlatformProviderUpdated.cs b/test/Savvyio.Assets.Tests/EventDriven/PlatformProviderUpdated.cs index 7815bda9..986141e6 100644 --- a/test/Savvyio.Assets.Tests/EventDriven/PlatformProviderUpdated.cs +++ b/test/Savvyio.Assets.Tests/EventDriven/PlatformProviderUpdated.cs @@ -1,32 +1,31 @@ -using System; +using System; using Savvyio.Assets.Domain; using Savvyio.EventDriven; -namespace Savvyio.Assets.EventDriven +namespace Savvyio.Assets.EventDriven; + +public record PlatformProviderUpdated : IntegrationEvent { - public record PlatformProviderUpdated : IntegrationEvent + PlatformProviderUpdated() { - PlatformProviderUpdated() - { - } + } - public PlatformProviderUpdated(PlatformProvider provider) - { - Id = provider.Id; - Name = provider.Name; - ThirdLevelDomainName = provider.ThirdLevelDomainName; - Description = provider.Description; - Policy = provider.Policy; - } + public PlatformProviderUpdated(PlatformProvider provider) + { + Id = provider.Id; + Name = provider.Name; + ThirdLevelDomainName = provider.ThirdLevelDomainName; + Description = provider.Description; + Policy = provider.Policy; + } - public Guid Id { get; private set; } + public Guid Id { get; private set; } - public string Name { get; private set; } + public string Name { get; private set; } - public string ThirdLevelDomainName { get; private set; } + public string ThirdLevelDomainName { get; private set; } - public string Description { get; private set; } + public string Description { get; private set; } - public PlatformProviderAccountPolicy Policy { get; private set; } - } + public PlatformProviderAccountPolicy Policy { get; private set; } } diff --git a/test/Savvyio.Assets.Tests/PlatformProviderHandler.cs b/test/Savvyio.Assets.Tests/PlatformProviderHandler.cs index 34d39154..591a95e9 100644 --- a/test/Savvyio.Assets.Tests/PlatformProviderHandler.cs +++ b/test/Savvyio.Assets.Tests/PlatformProviderHandler.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Commands; @@ -12,67 +12,66 @@ using Savvyio.Handlers; using Xunit; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public class PlatformProviderHandler : ICommandHandler, IIntegrationEventHandler { - public class PlatformProviderHandler : ICommandHandler, IIntegrationEventHandler + private readonly IMediator _mediator; + private readonly ITestOutputHelper _output; + private readonly IPersistentDataStore, PlatformProvider> _activeRecordRepository; + private readonly ITestStore _testStore; + + public PlatformProviderHandler(IMediator mediator = null, ITestOutputHelper output = null, ITestStore testStore = null, IPersistentDataStore, PlatformProvider> activeRecordRepository = null) { - private readonly IMediator _mediator; - private readonly ITestOutputHelper _output; - private readonly IPersistentDataStore, PlatformProvider> _activeRecordRepository; - private readonly ITestStore _testStore; + _mediator = mediator; + _output = output; + _testStore = testStore; + _activeRecordRepository = activeRecordRepository; + } - public PlatformProviderHandler(IMediator mediator = null, ITestOutputHelper output = null, ITestStore testStore = null, IPersistentDataStore, PlatformProvider> activeRecordRepository = null) + IFireForgetActivator IFireForgetHandler.Delegates => HandlerFactory.CreateFireForget(handler => + { + handler.RegisterAsync(e => { - _mediator = mediator; - _output = output; - _testStore = testStore; - _activeRecordRepository = activeRecordRepository; - } + _testStore.Add(e); + _output.WriteLines($"IE {nameof(PlatformProviderCreated)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; + }); - IFireForgetActivator IFireForgetHandler.Delegates => HandlerFactory.CreateFireForget(handler => + handler.RegisterAsync(e => { - handler.RegisterAsync(e => - { - _testStore.Add(e); - _output.WriteLines($"IE {nameof(PlatformProviderCreated)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - }); - - handler.RegisterAsync(e => - { - _testStore.Add(e); - _output.WriteLines($"IE {nameof(PlatformProviderUpdated)}", JsonSerializer.Serialize(e)); - return Task.CompletedTask; - }); + _testStore.Add(e); + _output.WriteLines($"IE {nameof(PlatformProviderUpdated)}", JsonSerializer.Serialize(e)); + return Task.CompletedTask; }); + }); - IFireForgetActivator IFireForgetHandler.Delegates => HandlerFactory.CreateFireForget(handler => + IFireForgetActivator IFireForgetHandler.Delegates => HandlerFactory.CreateFireForget(handler => + { + handler.RegisterAsync(async c => { - handler.RegisterAsync(async c => - { - _output.WriteLines($"C {nameof(CreatePlatformProvider)}", JsonSerializer.Serialize(c)); - var provider = new PlatformProvider(c.Name, c.ThirdLevelDomainName, c.Description).MergeMetadata(c); - await _activeRecordRepository.CreateAsync(provider); // store in db - await _mediator.PublishAsync(new PlatformProviderCreated(provider).MergeMetadata(c)); - }); + _output.WriteLines($"C {nameof(CreatePlatformProvider)}", JsonSerializer.Serialize(c)); + var provider = new PlatformProvider(c.Name, c.ThirdLevelDomainName, c.Description).MergeMetadata(c); + await _activeRecordRepository.CreateAsync(provider); // store in db + await _mediator.PublishAsync(new PlatformProviderCreated(provider).MergeMetadata(c)); + }); - handler.RegisterAsync(async c => - { - var provider = new PlatformProvider(c.Id); - provider.ChangeDescription(c.Description); - provider.ChangeName(c.Name); - provider.ChangeThirdLevelDomainName(c.ThirdLevelDomainName); - await _activeRecordRepository.UpdateAsync(provider); // store in db - await _mediator.PublishAsync(new PlatformProviderUpdated(provider)); - }); + handler.RegisterAsync(async c => + { + var provider = new PlatformProvider(c.Id); + provider.ChangeDescription(c.Description); + provider.ChangeName(c.Name); + provider.ChangeThirdLevelDomainName(c.ThirdLevelDomainName); + await _activeRecordRepository.UpdateAsync(provider); // store in db + await _mediator.PublishAsync(new PlatformProviderUpdated(provider)); + }); - handler.RegisterAsync(async c => - { - var provider = new PlatformProvider(c.Id); - provider.ChangeAccountPolicy(new PlatformProviderAccountPolicy(c.MinimumPasswordLength, c.EnforcePasswordHistory, c.MinimumPasswordAge, c.MaximumPasswordAge, c.AccountLockoutThreshold, c.AccountLockoutDuration, c.AccountLockoutCounterReset, c.PasswordComplexityRequirement)); - await _activeRecordRepository.UpdateAsync(provider); - await _mediator.PublishAsync(new PlatformProviderUpdated(provider)); - }); + handler.RegisterAsync(async c => + { + var provider = new PlatformProvider(c.Id); + provider.ChangeAccountPolicy(new PlatformProviderAccountPolicy(c.MinimumPasswordLength, c.EnforcePasswordHistory, c.MinimumPasswordAge, c.MaximumPasswordAge, c.AccountLockoutThreshold, c.AccountLockoutDuration, c.AccountLockoutCounterReset, c.PasswordComplexityRequirement)); + await _activeRecordRepository.UpdateAsync(provider); + await _mediator.PublishAsync(new PlatformProviderUpdated(provider)); }); - } + }); } diff --git a/test/Savvyio.Assets.Tests/Queries/AccountProjection.cs b/test/Savvyio.Assets.Tests/Queries/AccountProjection.cs index 1acb9c15..57535429 100644 --- a/test/Savvyio.Assets.Tests/Queries/AccountProjection.cs +++ b/test/Savvyio.Assets.Tests/Queries/AccountProjection.cs @@ -1,28 +1,27 @@ -namespace Savvyio.Assets.Queries +namespace Savvyio.Assets.Queries; + +public class AccountProjection { - public class AccountProjection + public AccountProjection() { - public AccountProjection() - { - } + } - public AccountProjection(long id, string fullName, string emailAddress) - { - Id = id; - FullName = fullName; - EmailAddress = emailAddress; - } + public AccountProjection(long id, string fullName, string emailAddress) + { + Id = id; + FullName = fullName; + EmailAddress = emailAddress; + } - public AccountProjection(string fullName, string emailAddress) - { - FullName = fullName; - EmailAddress = emailAddress; - } + public AccountProjection(string fullName, string emailAddress) + { + FullName = fullName; + EmailAddress = emailAddress; + } - public long Id { get; set; } + public long Id { get; set; } - public string FullName { get; set; } + public string FullName { get; set; } - public string EmailAddress { get; set; } - } + public string EmailAddress { get; set; } } diff --git a/test/Savvyio.Assets.Tests/Queries/GetAccount.cs b/test/Savvyio.Assets.Tests/Queries/GetAccount.cs index 2d0eb459..fddcffca 100644 --- a/test/Savvyio.Assets.Tests/Queries/GetAccount.cs +++ b/test/Savvyio.Assets.Tests/Queries/GetAccount.cs @@ -1,14 +1,13 @@ -using Savvyio.Queries; +using Savvyio.Queries; -namespace Savvyio.Assets.Queries +namespace Savvyio.Assets.Queries; + +public record GetAccount : Query { - public record GetAccount : Query + public GetAccount(long id) { - public GetAccount(long id) - { - Id = id; - } - - public long Id { get; } + Id = id; } + + public long Id { get; } } diff --git a/test/Savvyio.Assets.Tests/Queries/GetFakeAccount.cs b/test/Savvyio.Assets.Tests/Queries/GetFakeAccount.cs index e96a1938..22f4da88 100644 --- a/test/Savvyio.Assets.Tests/Queries/GetFakeAccount.cs +++ b/test/Savvyio.Assets.Tests/Queries/GetFakeAccount.cs @@ -1,15 +1,14 @@ -using Savvyio.Assets.EventDriven; +using Savvyio.Assets.EventDriven; using Savvyio.Queries; -namespace Savvyio.Assets.Queries +namespace Savvyio.Assets.Queries; + +public record GetFakeAccount : Query { - public record GetFakeAccount : Query + public GetFakeAccount(long id) { - public GetFakeAccount(long id) - { - Id = id; - } - - public long Id { get; } + Id = id; } + + public long Id { get; } } diff --git a/test/Savvyio.Assets.Tests/Queries/PlatformProviderProjection.cs b/test/Savvyio.Assets.Tests/Queries/PlatformProviderProjection.cs index 9c6a697a..bee19ed4 100644 --- a/test/Savvyio.Assets.Tests/Queries/PlatformProviderProjection.cs +++ b/test/Savvyio.Assets.Tests/Queries/PlatformProviderProjection.cs @@ -1,35 +1,34 @@ -using System; +using System; -namespace Savvyio.Assets.Queries +namespace Savvyio.Assets.Queries; + +public class PlatformProviderProjection { - public class PlatformProviderProjection + public PlatformProviderProjection() { - public PlatformProviderProjection() - { - } + } - public Guid Id { get; set; } + public Guid Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public string ThirdLevelDomainName { get; set; } + public string ThirdLevelDomainName { get; set; } - public string Description { get; set; } + public string Description { get; set; } - public int MinimumPasswordLength { get; set; } + public int MinimumPasswordLength { get; set; } - public int EnforcePasswordHistory { get; set; } + public int EnforcePasswordHistory { get; set; } - public long MinimumPasswordAge { get; set; } + public long MinimumPasswordAge { get; set; } - public long MaximumPasswordAge { get; set; } + public long MaximumPasswordAge { get; set; } - public int AccountLockoutThreshold { get; set; } + public int AccountLockoutThreshold { get; set; } - public long AccountLockoutDuration { get; set; } + public long AccountLockoutDuration { get; set; } - public long AccountLockoutCounterReset { get; set; } + public long AccountLockoutCounterReset { get; set; } - public bool PasswordComplexityRequirement { get; set; } - } + public bool PasswordComplexityRequirement { get; set; } } diff --git a/test/Savvyio.Assets.Tests/Savvyio.Assets.Tests.csproj b/test/Savvyio.Assets.Tests/Savvyio.Assets.Tests.csproj index a0116fe6..0b71b9ce 100644 --- a/test/Savvyio.Assets.Tests/Savvyio.Assets.Tests.csproj +++ b/test/Savvyio.Assets.Tests/Savvyio.Assets.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Assets diff --git a/test/Savvyio.Commands.Tests/Assets/DefaultCommand.cs b/test/Savvyio.Commands.Tests/Assets/DefaultCommand.cs index 1c7ef5d7..08c9f7ce 100644 --- a/test/Savvyio.Commands.Tests/Assets/DefaultCommand.cs +++ b/test/Savvyio.Commands.Tests/Assets/DefaultCommand.cs @@ -1,6 +1,5 @@ -namespace Savvyio.Commands.Assets +namespace Savvyio.Commands.Assets; + +internal record DefaultCommand : Command { - internal record DefaultCommand : Command - { - } } diff --git a/test/Savvyio.Commands.Tests/Assets/DefaultCommandHandler.cs b/test/Savvyio.Commands.Tests/Assets/DefaultCommandHandler.cs index fe23e4c5..edc201db 100644 --- a/test/Savvyio.Commands.Tests/Assets/DefaultCommandHandler.cs +++ b/test/Savvyio.Commands.Tests/Assets/DefaultCommandHandler.cs @@ -1,26 +1,25 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Savvyio.Handlers; -namespace Savvyio.Commands.Assets +namespace Savvyio.Commands.Assets; + +internal class DefaultCommandHandler : CommandHandler { - internal class DefaultCommandHandler : CommandHandler - { - private readonly ITestStore _ts; + private readonly ITestStore _ts; - public DefaultCommandHandler(ITestStore ts) - { - _ts = ts; - } + public DefaultCommandHandler(ITestStore ts) + { + _ts = ts; + } - protected override void RegisterDelegates(IFireForgetRegistry handlers) + protected override void RegisterDelegates(IFireForgetRegistry handlers) + { + handlers.Register(_ => _ts.Add(nameof(DefaultCommand))); + handlers.RegisterAsync(_ => { - handlers.Register(_ => _ts.Add(nameof(DefaultCommand))); - handlers.RegisterAsync(_ => - { - _ts.Add(nameof(DefaultCommand) + "Async"); - return Task.CompletedTask; - }); - } + _ts.Add(nameof(DefaultCommand) + "Async"); + return Task.CompletedTask; + }); } } diff --git a/test/Savvyio.Commands.Tests/CommandDispatcherTest.cs b/test/Savvyio.Commands.Tests/CommandDispatcherTest.cs index 9ea9cd2a..5528b2c2 100644 --- a/test/Savvyio.Commands.Tests/CommandDispatcherTest.cs +++ b/test/Savvyio.Commands.Tests/CommandDispatcherTest.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Microsoft.Extensions.DependencyInjection; using Savvyio.Commands.Assets; @@ -7,41 +7,40 @@ using Savvyio.Handlers; using Xunit; -namespace Savvyio.Commands +namespace Savvyio.Commands; + +public class CommandDispatcherTest : Test { - public class CommandDispatcherTest : Test + public CommandDispatcherTest(ITestOutputHelper output) : base(output) { - public CommandDispatcherTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void CommandDispatcher_Ensure_Initialization_Defaults() - { - var sp = new ServiceCollection().AddServiceLocator().BuildServiceProvider(); - var sut = new CommandDispatcher(sp.GetRequiredService()); + [Fact] + public void CommandDispatcher_Ensure_Initialization_Defaults() + { + var sp = new ServiceCollection().AddServiceLocator().BuildServiceProvider(); + var sut = new CommandDispatcher(sp.GetRequiredService()); - Assert.Throws(() => sut.Commit(new DefaultCommand())); - Assert.ThrowsAsync(() => sut.CommitAsync(new DefaultCommand())); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - } + Assert.Throws(() => sut.Commit(new DefaultCommand())); + Assert.ThrowsAsync(() => sut.CommitAsync(new DefaultCommand())); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + } - [Fact] - public async Task CommandDispatcher_ShouldCommit_DefaultCommand() - { - var sp = new ServiceCollection().AddServiceLocator().AddTransient().AddSingleton, InMemoryTestStore>().BuildServiceProvider(); - var sut = new CommandDispatcher(sp.GetRequiredService()); - var ts = sp.GetRequiredService>(); + [Fact] + public async Task CommandDispatcher_ShouldCommit_DefaultCommand() + { + var sp = new ServiceCollection().AddServiceLocator().AddTransient().AddSingleton, InMemoryTestStore>().BuildServiceProvider(); + var sut = new CommandDispatcher(sp.GetRequiredService()); + var ts = sp.GetRequiredService>(); - sut.Commit(new DefaultCommand()); - await sut.CommitAsync(new DefaultCommand()); + sut.Commit(new DefaultCommand()); + await sut.CommitAsync(new DefaultCommand()); - Assert.Collection(ts.Query(), - s => Assert.Equal(s, nameof(DefaultCommand)), - s => Assert.Equal(s, nameof(DefaultCommand) + "Async")); - } + Assert.Collection(ts.Query(), + s => Assert.Equal(s, nameof(DefaultCommand)), + s => Assert.Equal(s, nameof(DefaultCommand) + "Async")); } } diff --git a/test/Savvyio.Commands.Tests/CommandHandlerTest.cs b/test/Savvyio.Commands.Tests/CommandHandlerTest.cs index 9e8c3ec5..92d39331 100644 --- a/test/Savvyio.Commands.Tests/CommandHandlerTest.cs +++ b/test/Savvyio.Commands.Tests/CommandHandlerTest.cs @@ -1,28 +1,27 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Commands; using Savvyio.Commands.Assets; using Xunit; -namespace Savvyio.Commands +namespace Savvyio.Commands; + +public class CommandHandlerTest : Test { - public class CommandHandlerTest : Test + public CommandHandlerTest(ITestOutputHelper output) : base(output) { - public CommandHandlerTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task CommandHandler_Ensure_Initialization_Defaults() - { - var sut = new DefaultCommandHandler(new InMemoryTestStore()); + [Fact] + public async Task CommandHandler_Ensure_Initialization_Defaults() + { + var sut = new DefaultCommandHandler(new InMemoryTestStore()); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.True(sut.Delegates.TryInvoke(new DefaultCommand())); - Assert.True((await sut.Delegates.TryInvokeAsync(new DefaultCommand())).Succeeded); - Assert.False(sut.Delegates.TryInvoke(new CreatePlatformProvider("Name", "Tld", "Description"))); - Assert.False((await sut.Delegates.TryInvokeAsync(new CreatePlatformProvider("Name", "Tld", "Description"))).Succeeded); - } + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.True(sut.Delegates.TryInvoke(new DefaultCommand())); + Assert.True((await sut.Delegates.TryInvokeAsync(new DefaultCommand())).Succeeded); + Assert.False(sut.Delegates.TryInvoke(new CreatePlatformProvider("Name", "Tld", "Description"))); + Assert.False((await sut.Delegates.TryInvokeAsync(new CreatePlatformProvider("Name", "Tld", "Description"))).Succeeded); } } diff --git a/test/Savvyio.Commands.Tests/CommandTest.cs b/test/Savvyio.Commands.Tests/CommandTest.cs index 5ddddbf4..b64e5d15 100644 --- a/test/Savvyio.Commands.Tests/CommandTest.cs +++ b/test/Savvyio.Commands.Tests/CommandTest.cs @@ -1,26 +1,25 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Savvyio.Commands.Assets; using Xunit; -namespace Savvyio.Commands +namespace Savvyio.Commands; + +public class CommandTest : Test { - public class CommandTest : Test + public CommandTest(ITestOutputHelper output) : base(output) { - public CommandTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void DefaultCommand_Ensure_Initialization_Defaults() - { - var sut = new DefaultCommand(); + [Fact] + public void DefaultCommand_Ensure_Initialization_Defaults() + { + var sut = new DefaultCommand(); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.Contains(sut.Metadata, pair => pair.Key == MetadataDictionary.CorrelationId); - } + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.Contains(sut.Metadata, pair => pair.Key == MetadataDictionary.CorrelationId); } } diff --git a/test/Savvyio.Commands.Tests/Messaging/CommandExtensionsTest.cs b/test/Savvyio.Commands.Tests/Messaging/CommandExtensionsTest.cs index 51ece9cb..410d144b 100644 --- a/test/Savvyio.Commands.Tests/Messaging/CommandExtensionsTest.cs +++ b/test/Savvyio.Commands.Tests/Messaging/CommandExtensionsTest.cs @@ -1,33 +1,32 @@ -using Cuemon.Extensions; +using Cuemon.Extensions; using Cuemon.Extensions.Reflection; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Commands.Messaging +namespace Savvyio.Commands.Messaging; + +public class CommandExtensionsTest : Test { - public class CommandExtensionsTest : Test + public CommandExtensionsTest(ITestOutputHelper output) : base(output) { - public CommandExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void EncloseToMessage_CreateMemberCommand_ShouldBeWrappedInMessage() - { - var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com"); - var sut2 = "https://fancy.api/members".ToUri(); - var sut3 = "com.example.someevent"; - var sut4 = sut1.ToMessage(sut2, sut3); + [Fact] + public void EncloseToMessage_CreateMemberCommand_ShouldBeWrappedInMessage() + { + var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com"); + var sut2 = "https://fancy.api/members".ToUri(); + var sut3 = "com.example.someevent"; + var sut4 = sut1.ToMessage(sut2, sut3); - Assert.Equal(sut1.EmailAddress, sut4.Data.EmailAddress); - Assert.Equal(sut1.Age, sut4.Data.Age); - Assert.Equal(sut1.Name, sut4.Data.Name); - Assert.NotNull(sut4.Id); - Assert.NotNull(sut4.Time); - Assert.Equal(sut1.GetType().ToFullNameIncludingAssemblyName(), sut4.Data.GetMemberType()); - Assert.Equal(sut2.OriginalString, sut4.Source); - Assert.Equal(sut3, sut4.Type); - } + Assert.Equal(sut1.EmailAddress, sut4.Data.EmailAddress); + Assert.Equal(sut1.Age, sut4.Data.Age); + Assert.Equal(sut1.Name, sut4.Data.Name); + Assert.NotNull(sut4.Id); + Assert.NotNull(sut4.Time); + Assert.Equal(sut1.GetType().ToFullNameIncludingAssemblyName(), sut4.Data.GetMemberType()); + Assert.Equal(sut2.OriginalString, sut4.Source); + Assert.Equal(sut3, sut4.Type); } } diff --git a/test/Savvyio.Commands.Tests/SavvyioOptionsExtensionsTest.cs b/test/Savvyio.Commands.Tests/SavvyioOptionsExtensionsTest.cs index 83fb3d3c..c27eea6e 100644 --- a/test/Savvyio.Commands.Tests/SavvyioOptionsExtensionsTest.cs +++ b/test/Savvyio.Commands.Tests/SavvyioOptionsExtensionsTest.cs @@ -1,43 +1,42 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Savvyio.Assets; using Xunit; -namespace Savvyio.Commands +namespace Savvyio.Commands; + +public class SavvyioOptionsExtensionsTest : Test { - public class SavvyioOptionsExtensionsTest : Test + public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) { - public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() - { - var sut = new SavvyioOptions().AddHandlers(); - Assert.Empty(sut.HandlerServiceTypes); - Assert.Empty(sut.HandlerImplementationTypes); - } + [Fact] + public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() + { + var sut = new SavvyioOptions().AddHandlers(); + Assert.Empty(sut.HandlerServiceTypes); + Assert.Empty(sut.HandlerImplementationTypes); + } - [Fact] - public void AddCommandHandler_ShouldAddCommandHandler() - { - var sut = new SavvyioOptions().AddCommandHandler(); + [Fact] + public void AddCommandHandler_ShouldAddCommandHandler() + { + var sut = new SavvyioOptions().AddCommandHandler(); - Assert.NotEmpty(sut.HandlerServiceTypes); - Assert.NotEmpty(sut.HandlerImplementationTypes); - Assert.Collection(sut.HandlerServiceTypes, type => Assert.Equal(typeof(ICommandHandler), type)); - Assert.Collection(sut.HandlerImplementationTypes, type => Assert.Equal(typeof(AccountCommandHandler), type)); - } + Assert.NotEmpty(sut.HandlerServiceTypes); + Assert.NotEmpty(sut.HandlerImplementationTypes); + Assert.Collection(sut.HandlerServiceTypes, type => Assert.Equal(typeof(ICommandHandler), type)); + Assert.Collection(sut.HandlerImplementationTypes, type => Assert.Equal(typeof(AccountCommandHandler), type)); + } - [Fact] - public void AddCommandDispatcher_ShouldAddCommandDispatcher() - { - var sut = new SavvyioOptions().AddCommandDispatcher(); + [Fact] + public void AddCommandDispatcher_ShouldAddCommandDispatcher() + { + var sut = new SavvyioOptions().AddCommandDispatcher(); - Assert.NotEmpty(sut.DispatcherServiceTypes); - Assert.NotEmpty(sut.DispatcherImplementationTypes); - Assert.Collection(sut.DispatcherServiceTypes, type => Assert.Equal(typeof(ICommandDispatcher), type)); - Assert.Collection(sut.DispatcherImplementationTypes, type => Assert.Equal(typeof(CommandDispatcher), type)); - } + Assert.NotEmpty(sut.DispatcherServiceTypes); + Assert.NotEmpty(sut.DispatcherImplementationTypes); + Assert.Collection(sut.DispatcherServiceTypes, type => Assert.Equal(typeof(ICommandDispatcher), type)); + Assert.Collection(sut.DispatcherImplementationTypes, type => Assert.Equal(typeof(CommandDispatcher), type)); } } diff --git a/test/Savvyio.Core.Tests/Assets/AccountHandlers.cs b/test/Savvyio.Core.Tests/Assets/AccountHandlers.cs index 3aa7b1f4..aa82f74e 100644 --- a/test/Savvyio.Core.Tests/Assets/AccountHandlers.cs +++ b/test/Savvyio.Core.Tests/Assets/AccountHandlers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Cuemon; using Codebelt.Extensions.Xunit; @@ -9,46 +9,45 @@ using Savvyio.Handlers; using Savvyio.Queries; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public class AccountHandlers : ICommandHandler, IQueryHandler { - public class AccountHandlers : ICommandHandler, IQueryHandler + private readonly ICommandDispatcher _commandDispatcher; + private readonly ITestStore _commandTestStore; + private readonly ITestStore _queryTestStore; + private readonly IQueryDispatcher _queryDispatcher; + + public AccountHandlers(ICommandDispatcher commandDispatcher = null, ITestStore commandTestStore = null, ITestStore queryTestStore = null, IQueryDispatcher queryDispatcher = null) { - private readonly ICommandDispatcher _commandDispatcher; - private readonly ITestStore _commandTestStore; - private readonly ITestStore _queryTestStore; - private readonly IQueryDispatcher _queryDispatcher; + _commandDispatcher = commandDispatcher; + _commandTestStore = commandTestStore; + _queryTestStore = queryTestStore; + _queryDispatcher = queryDispatcher; + } - public AccountHandlers(ICommandDispatcher commandDispatcher = null, ITestStore commandTestStore = null, ITestStore queryTestStore = null, IQueryDispatcher queryDispatcher = null) + IFireForgetActivator IFireForgetHandler.Delegates => HandlerFactory.CreateFireForget(registry => + { + registry.Register(ca => _commandTestStore.Add(ca)); + registry.RegisterAsync(async ca => { - _commandDispatcher = commandDispatcher; - _commandTestStore = commandTestStore; - _queryTestStore = queryTestStore; - _queryDispatcher = queryDispatcher; - } + await Task.Delay(TimeSpan.FromSeconds(0.5)); + _commandTestStore.Add(ca); + }); + }); - IFireForgetActivator IFireForgetHandler.Delegates => HandlerFactory.CreateFireForget(registry => + IRequestReplyActivator IRequestReplyHandler.Delegates => HandlerFactory.CreateRequestReply(registry => + { + registry.Register(a => { - registry.Register(ca => _commandTestStore.Add(ca)); - registry.RegisterAsync(async ca => - { - await Task.Delay(TimeSpan.FromSeconds(0.5)); - _commandTestStore.Add(ca); - }); + _queryTestStore.Add(a); + return new AccountCreated(a.Id, $"{a.Id}___{Generate.RandomString(16)}", $"{a.Id}@no.where"); }); - - IRequestReplyActivator IRequestReplyHandler.Delegates => HandlerFactory.CreateRequestReply(registry => + registry.RegisterAsync(async a => { - registry.Register(a => - { - _queryTestStore.Add(a); - return new AccountCreated(a.Id, $"{a.Id}___{Generate.RandomString(16)}", $"{a.Id}@no.where"); - }); - registry.RegisterAsync(async a => - { - await Task.Delay(TimeSpan.FromSeconds(0.5)); - _queryTestStore.Add(a); - return new AccountCreated(a.Id, $"{a.Id}___{Generate.RandomString(16)}", $"{a.Id}@no.where"); - }); + await Task.Delay(TimeSpan.FromSeconds(0.5)); + _queryTestStore.Add(a); + return new AccountCreated(a.Id, $"{a.Id}___{Generate.RandomString(16)}", $"{a.Id}@no.where"); }); - } + }); } diff --git a/test/Savvyio.Core.Tests/Assets/DummyRequest.cs b/test/Savvyio.Core.Tests/Assets/DummyRequest.cs index 87536316..428b97be 100644 --- a/test/Savvyio.Core.Tests/Assets/DummyRequest.cs +++ b/test/Savvyio.Core.Tests/Assets/DummyRequest.cs @@ -1,8 +1,7 @@ -using System; +using System; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public record DummyRequest(Guid Uuid, int Number) : Request { - public record DummyRequest(Guid Uuid, int Number) : Request - { - } } diff --git a/test/Savvyio.Core.Tests/Assets/FakeCommand.cs b/test/Savvyio.Core.Tests/Assets/FakeCommand.cs index e2ef1780..ce5c3ffe 100644 --- a/test/Savvyio.Core.Tests/Assets/FakeCommand.cs +++ b/test/Savvyio.Core.Tests/Assets/FakeCommand.cs @@ -1,25 +1,24 @@ -using System; +using System; using Savvyio.Commands; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +internal record FakeCommand : Command { - internal record FakeCommand : Command + public FakeCommand() { - public FakeCommand() - { - Type = null; - } + Type = null; + } - public object Value { get; set; } + public object Value { get; set; } - public Type Type { get; protected set; } - } + public Type Type { get; protected set; } +} - internal record FakeCommand : FakeCommand +internal record FakeCommand : FakeCommand +{ + public FakeCommand() { - public FakeCommand() - { - Type = typeof(T); - } + Type = typeof(T); } } diff --git a/test/Savvyio.Core.Tests/Assets/FakeQuery.cs b/test/Savvyio.Core.Tests/Assets/FakeQuery.cs index efcbbcdf..306c25b0 100644 --- a/test/Savvyio.Core.Tests/Assets/FakeQuery.cs +++ b/test/Savvyio.Core.Tests/Assets/FakeQuery.cs @@ -1,8 +1,7 @@ -using Savvyio.Queries; +using Savvyio.Queries; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +internal record FakeQuery : Query { - internal record FakeQuery : Query - { - } } diff --git a/test/Savvyio.Core.Tests/Assets/RootDummyRequest.cs b/test/Savvyio.Core.Tests/Assets/RootDummyRequest.cs index 68d19811..de502fe6 100644 --- a/test/Savvyio.Core.Tests/Assets/RootDummyRequest.cs +++ b/test/Savvyio.Core.Tests/Assets/RootDummyRequest.cs @@ -1,8 +1,7 @@ -using System; +using System; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public record RootDummyRequest(Guid Uuid, int Number, bool Hidden = false) : DummyRequest(Uuid, Number) { - public record RootDummyRequest(Guid Uuid, int Number, bool Hidden = false) : DummyRequest(Uuid, Number) - { - } } diff --git a/test/Savvyio.Core.Tests/Dispatchers/FireForgetDispatcherTest.cs b/test/Savvyio.Core.Tests/Dispatchers/FireForgetDispatcherTest.cs index 1817a1e0..6b970c67 100644 --- a/test/Savvyio.Core.Tests/Dispatchers/FireForgetDispatcherTest.cs +++ b/test/Savvyio.Core.Tests/Dispatchers/FireForgetDispatcherTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using Codebelt.Extensions.Xunit; @@ -10,70 +10,69 @@ using Savvyio.Handlers; using Xunit; -namespace Savvyio.Dispatchers +namespace Savvyio.Dispatchers; + +public class FireForgetDispatcherTest : Test { - public class FireForgetDispatcherTest : Test + public FireForgetDispatcherTest(ITestOutputHelper output) : base(output) { - public FireForgetDispatcherTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Dispatch_ShouldFailWithOrphanedHandlerException() - { - var sc = new ServiceCollection().AddSavvyIO(o => o.AddAssemblyToScan(typeof(FireForgetDispatcherTest).Assembly)); - var sp = sc.BuildServiceProvider(); - var sut = new CommandDispatcher(sp.GetRequiredService()); + [Fact] + public void Dispatch_ShouldFailWithOrphanedHandlerException() + { + var sc = new ServiceCollection().AddSavvyIO(o => o.AddAssemblyToScan(typeof(FireForgetDispatcherTest).Assembly)); + var sp = sc.BuildServiceProvider(); + var sut = new CommandDispatcher(sp.GetRequiredService()); - Assert.Throws(() => sut.Commit(new FakeCommand())); - } + Assert.Throws(() => sut.Commit(new FakeCommand())); + } - [Fact] - public void Dispatch_ShouldDispatchCommandToDesignatedHandler() - { - var sc = new ServiceCollection() - .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(FireForgetDispatcherTest).Assembly, typeof(ICommand).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) - .AddScoped, InMemoryTestStore>(); + [Fact] + public void Dispatch_ShouldDispatchCommandToDesignatedHandler() + { + var sc = new ServiceCollection() + .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(FireForgetDispatcherTest).Assembly, typeof(ICommand).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) + .AddScoped, InMemoryTestStore>(); - var sp = sc.BuildServiceProvider(); - var sut = new CommandDispatcher(sp.GetRequiredService()); - var ca = new CreateAccount(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); - var cs = sp.GetRequiredService>(); + var sp = sc.BuildServiceProvider(); + var sut = new CommandDispatcher(sp.GetRequiredService()); + var ca = new CreateAccount(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); + var cs = sp.GetRequiredService>(); - sut.Commit(ca); + sut.Commit(ca); - Assert.Equal(ca.FullName, cs.QueryFor().Single().FullName); - Assert.Equal(ca.EmailAddress, cs.QueryFor().Single().EmailAddress); - Assert.Equal(ca.PlatformProviderId, cs.QueryFor().Single().PlatformProviderId); - } + Assert.Equal(ca.FullName, cs.QueryFor().Single().FullName); + Assert.Equal(ca.EmailAddress, cs.QueryFor().Single().EmailAddress); + Assert.Equal(ca.PlatformProviderId, cs.QueryFor().Single().PlatformProviderId); + } - [Fact] - public async Task DispatchAsync_ShouldFailWithOrphanedHandlerExceptionAsync() - { - var sc = new ServiceCollection().AddSavvyIO(o => o.AddAssemblyToScan(typeof(FireForgetDispatcherTest).Assembly)); - var sp = sc.BuildServiceProvider(); - var sut = new CommandDispatcher(sp.GetRequiredService()); + [Fact] + public async Task DispatchAsync_ShouldFailWithOrphanedHandlerExceptionAsync() + { + var sc = new ServiceCollection().AddSavvyIO(o => o.AddAssemblyToScan(typeof(FireForgetDispatcherTest).Assembly)); + var sp = sc.BuildServiceProvider(); + var sut = new CommandDispatcher(sp.GetRequiredService()); - await Assert.ThrowsAsync(async () => await sut.CommitAsync(new FakeCommand())); - } + await Assert.ThrowsAsync(async () => await sut.CommitAsync(new FakeCommand())); + } - [Fact] - public async Task DispatchAsync_ShouldDispatchCommandToDesignatedHandlerAsync() - { - var sc = new ServiceCollection() - .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(FireForgetDispatcherTest).Assembly, typeof(ICommand).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) - .AddScoped, InMemoryTestStore>(); + [Fact] + public async Task DispatchAsync_ShouldDispatchCommandToDesignatedHandlerAsync() + { + var sc = new ServiceCollection() + .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(FireForgetDispatcherTest).Assembly, typeof(ICommand).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) + .AddScoped, InMemoryTestStore>(); - var sp = sc.BuildServiceProvider(); - var sut = new CommandDispatcher(sp.GetRequiredService()); - var ca = new CreateAccount(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); - var cs = sp.GetRequiredService>(); + var sp = sc.BuildServiceProvider(); + var sut = new CommandDispatcher(sp.GetRequiredService()); + var ca = new CreateAccount(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); + var cs = sp.GetRequiredService>(); - await sut.CommitAsync(ca); + await sut.CommitAsync(ca); - Assert.Equal(ca.FullName, cs.QueryFor().Single().FullName); - Assert.Equal(ca.EmailAddress, cs.QueryFor().Single().EmailAddress); - Assert.Equal(ca.PlatformProviderId, cs.QueryFor().Single().PlatformProviderId); - } + Assert.Equal(ca.FullName, cs.QueryFor().Single().FullName); + Assert.Equal(ca.EmailAddress, cs.QueryFor().Single().EmailAddress); + Assert.Equal(ca.PlatformProviderId, cs.QueryFor().Single().PlatformProviderId); } } diff --git a/test/Savvyio.Core.Tests/Dispatchers/RequestReplyDispatcherTest.cs b/test/Savvyio.Core.Tests/Dispatchers/RequestReplyDispatcherTest.cs index bbe517de..dabca2ff 100644 --- a/test/Savvyio.Core.Tests/Dispatchers/RequestReplyDispatcherTest.cs +++ b/test/Savvyio.Core.Tests/Dispatchers/RequestReplyDispatcherTest.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Microsoft.Extensions.DependencyInjection; @@ -9,70 +9,69 @@ using Savvyio.Queries; using Xunit; -namespace Savvyio.Dispatchers +namespace Savvyio.Dispatchers; + +public class RequestReplyDispatcherTest : Test { - public class RequestReplyDispatcherTest : Test + public RequestReplyDispatcherTest(ITestOutputHelper output) : base(output) { - public RequestReplyDispatcherTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Dispatch_ShouldFailWithOrphanedHandlerException() - { - var sc = new ServiceCollection().AddSavvyIO(o => o.AddAssemblyToScan(typeof(RequestReplyDispatcherTest).Assembly)); - var sp = sc.BuildServiceProvider(); - var sut = new QueryDispatcher(sp.GetRequiredService()); + [Fact] + public void Dispatch_ShouldFailWithOrphanedHandlerException() + { + var sc = new ServiceCollection().AddSavvyIO(o => o.AddAssemblyToScan(typeof(RequestReplyDispatcherTest).Assembly)); + var sp = sc.BuildServiceProvider(); + var sut = new QueryDispatcher(sp.GetRequiredService()); - Assert.Throws(() => sut.Query(new FakeQuery())); - } + Assert.Throws(() => sut.Query(new FakeQuery())); + } - [Fact] - public void Dispatch_ShouldDispatchCommandToDesignatedHandler() - { - var sc = new ServiceCollection() - .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(RequestReplyDispatcherTest).Assembly, typeof(IQuery).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) - .AddScoped, InMemoryTestStore>(); + [Fact] + public void Dispatch_ShouldDispatchCommandToDesignatedHandler() + { + var sc = new ServiceCollection() + .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(RequestReplyDispatcherTest).Assembly, typeof(IQuery).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) + .AddScoped, InMemoryTestStore>(); - var sp = sc.BuildServiceProvider(); - var sut = new QueryDispatcher(sp.GetRequiredService()); - var ga = new GetFakeAccount(2313); - var cs = sp.GetRequiredService>(); + var sp = sc.BuildServiceProvider(); + var sut = new QueryDispatcher(sp.GetRequiredService()); + var ga = new GetFakeAccount(2313); + var cs = sp.GetRequiredService>(); - var result = sut.Query(ga); + var result = sut.Query(ga); - Assert.NotNull(result); - Assert.Equal(ga.Id, result.Id); - Assert.Equal(ga.Id, cs.QueryFor().Single().Id); - } + Assert.NotNull(result); + Assert.Equal(ga.Id, result.Id); + Assert.Equal(ga.Id, cs.QueryFor().Single().Id); + } - [Fact] - public async Task DispatchAsync_ShouldFailWithOrphanedHandlerExceptionAsync() - { - var sc = new ServiceCollection().AddSavvyIO(o => o.AddAssemblyToScan(typeof(RequestReplyDispatcherTest).Assembly)); - var sp = sc.BuildServiceProvider(); - var sut = new QueryDispatcher(sp.GetRequiredService()); + [Fact] + public async Task DispatchAsync_ShouldFailWithOrphanedHandlerExceptionAsync() + { + var sc = new ServiceCollection().AddSavvyIO(o => o.AddAssemblyToScan(typeof(RequestReplyDispatcherTest).Assembly)); + var sp = sc.BuildServiceProvider(); + var sut = new QueryDispatcher(sp.GetRequiredService()); - await Assert.ThrowsAsync(async () => await sut.QueryAsync(new FakeQuery())); - } + await Assert.ThrowsAsync(async () => await sut.QueryAsync(new FakeQuery())); + } - [Fact] - public async Task DispatchAsync_ShouldDispatchCommandToDesignatedHandlerAsync() - { - var sc = new ServiceCollection() - .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(RequestReplyDispatcherTest).Assembly, typeof(IQuery).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) - .AddScoped, InMemoryTestStore>(); + [Fact] + public async Task DispatchAsync_ShouldDispatchCommandToDesignatedHandlerAsync() + { + var sc = new ServiceCollection() + .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(RequestReplyDispatcherTest).Assembly, typeof(IQuery).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) + .AddScoped, InMemoryTestStore>(); - var sp = sc.BuildServiceProvider(); - var sut = new QueryDispatcher(sp.GetRequiredService()); - var ga = new GetFakeAccount(74893297432); - var cs = sp.GetRequiredService>(); + var sp = sc.BuildServiceProvider(); + var sut = new QueryDispatcher(sp.GetRequiredService()); + var ga = new GetFakeAccount(74893297432); + var cs = sp.GetRequiredService>(); - var result = await sut.QueryAsync(ga); + var result = await sut.QueryAsync(ga); - Assert.NotNull(result); - Assert.StartsWith(ga.Id.ToString(), result.EmailAddress); - Assert.Equal(ga.Id, cs.QueryFor().Single().Id); - } + Assert.NotNull(result); + Assert.StartsWith(ga.Id.ToString(), result.EmailAddress); + Assert.Equal(ga.Id, cs.QueryFor().Single().Id); } } diff --git a/test/Savvyio.Core.Tests/HandlerFactoryTest.cs b/test/Savvyio.Core.Tests/HandlerFactoryTest.cs index 9c6d4da2..97259eb7 100644 --- a/test/Savvyio.Core.Tests/HandlerFactoryTest.cs +++ b/test/Savvyio.Core.Tests/HandlerFactoryTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -9,156 +9,155 @@ using Savvyio.Handlers; using Xunit; -namespace Savvyio +namespace Savvyio; + +public class HandlerFactoryTest : Test { - public class HandlerFactoryTest : Test + public HandlerFactoryTest(ITestOutputHelper output) : base(output) { - public HandlerFactoryTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void CreateFireForget_ShouldInvokeRegisteredHandlers() + [Fact] + public void CreateFireForget_ShouldInvokeRegisteredHandlers() + { + var store = new List(); + var sut1 = HandlerFactory.CreateFireForget(registry => { - var store = new List(); - var sut1 = HandlerFactory.CreateFireForget(registry => - { - registry.Register(c => store.Add(c)); - registry.Register>(c => store.Add(c)); - registry.Register>(c => store.Add(c)); - registry.Register>(c => store.Add(c)); - registry.Register>(c => store.Add(c)); - }); + registry.Register(c => store.Add(c)); + registry.Register>(c => store.Add(c)); + registry.Register>(c => store.Add(c)); + registry.Register>(c => store.Add(c)); + registry.Register>(c => store.Add(c)); + }); - Assert.NotNull(sut1); - Assert.True(sut1.TryInvoke(new FakeCommand())); - Assert.True(sut1.TryInvoke(new FakeCommand())); - Assert.True(sut1.TryInvoke(new FakeCommand())); - Assert.True(sut1.TryInvoke(new FakeCommand())); - Assert.True(sut1.TryInvoke(new FakeCommand())); - Assert.False(sut1.TryInvoke(new FakeCommand())); - Assert.False(sut1.TryInvoke(new FakeCommand())); - Assert.IsType(store.Single(c => c.Type == null)); - Assert.IsType>(store.Single(c => c.Type == typeof(int))); - Assert.IsType>(store.Single(c => c.Type == typeof(Guid))); - Assert.IsType>(store.Single(c => c.Type == typeof(DateTime))); - Assert.IsType>(store.Single(c => c.Type == typeof(Stream))); - } + Assert.NotNull(sut1); + Assert.True(sut1.TryInvoke(new FakeCommand())); + Assert.True(sut1.TryInvoke(new FakeCommand())); + Assert.True(sut1.TryInvoke(new FakeCommand())); + Assert.True(sut1.TryInvoke(new FakeCommand())); + Assert.True(sut1.TryInvoke(new FakeCommand())); + Assert.False(sut1.TryInvoke(new FakeCommand())); + Assert.False(sut1.TryInvoke(new FakeCommand())); + Assert.IsType(store.Single(c => c.Type == null)); + Assert.IsType>(store.Single(c => c.Type == typeof(int))); + Assert.IsType>(store.Single(c => c.Type == typeof(Guid))); + Assert.IsType>(store.Single(c => c.Type == typeof(DateTime))); + Assert.IsType>(store.Single(c => c.Type == typeof(Stream))); + } - [Fact] - public Task CreateFireForget_ShouldInvokeRegisteredHandlersAsync() + [Fact] + public Task CreateFireForget_ShouldInvokeRegisteredHandlersAsync() + { + var store = new List(); + var sut1 = HandlerFactory.CreateFireForget(registry => { - var store = new List(); - var sut1 = HandlerFactory.CreateFireForget(registry => + registry.RegisterAsync((c, _) => + { + store.Add(c); + return Task.CompletedTask; + }); + registry.RegisterAsync>(c => + { + store.Add(c); + return Task.CompletedTask; + }); + registry.RegisterAsync>(c => { - registry.RegisterAsync((c, _) => - { - store.Add(c); - return Task.CompletedTask; - }); - registry.RegisterAsync>(c => - { - store.Add(c); - return Task.CompletedTask; - }); - registry.RegisterAsync>(c => - { - store.Add(c); - return Task.CompletedTask; - }); - registry.RegisterAsync>(c => - { - store.Add(c); - return Task.CompletedTask; - }); - registry.RegisterAsync>(c => - { - store.Add(c); - return Task.CompletedTask; - }); + store.Add(c); + return Task.CompletedTask; }); + registry.RegisterAsync>(c => + { + store.Add(c); + return Task.CompletedTask; + }); + registry.RegisterAsync>(c => + { + store.Add(c); + return Task.CompletedTask; + }); + }); - Assert.NotNull(sut1); - Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); - Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); - Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); - Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); - Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); - Assert.False(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); - Assert.False(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); - Assert.IsType(store.Single(c => c.Type == null)); - Assert.IsType>(store.Single(c => c.Type == typeof(int))); - Assert.IsType>(store.Single(c => c.Type == typeof(Guid))); - Assert.IsType>(store.Single(c => c.Type == typeof(DateTime))); - Assert.IsType>(store.Single(c => c.Type == typeof(Stream))); + Assert.NotNull(sut1); + Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); + Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); + Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); + Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); + Assert.True(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); + Assert.False(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); + Assert.False(sut1.TryInvokeAsync(new FakeCommand()).GetAwaiter().GetResult().Succeeded); + Assert.IsType(store.Single(c => c.Type == null)); + Assert.IsType>(store.Single(c => c.Type == typeof(int))); + Assert.IsType>(store.Single(c => c.Type == typeof(Guid))); + Assert.IsType>(store.Single(c => c.Type == typeof(DateTime))); + Assert.IsType>(store.Single(c => c.Type == typeof(Stream))); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - [Fact] - public void CreateRequestReply_ShouldInvokeRegisteredHandlers() + [Fact] + public void CreateRequestReply_ShouldInvokeRegisteredHandlers() + { + var sut1 = HandlerFactory.CreateRequestReply(registry => { - var sut1 = HandlerFactory.CreateRequestReply(registry => - { - registry.Register(c => c.Type); - registry.Register, Type>(c => c.Type); - registry.Register, Type>(c => c.Type); - registry.Register, Type>(c => c.Type); - registry.Register, Type>(c => c.Type); - }); + registry.Register(c => c.Type); + registry.Register, Type>(c => c.Type); + registry.Register, Type>(c => c.Type); + registry.Register, Type>(c => c.Type); + registry.Register, Type>(c => c.Type); + }); - Assert.NotNull(sut1); - Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc1)); - Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc2)); - Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc3)); - Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc4)); - Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc5)); - Assert.False(sut1.TryInvoke(new FakeCommand(), out var fc6)); - Assert.False(sut1.TryInvoke(new FakeCommand(), out var fc7)); - Assert.Null(fc1); - Assert.Equal(typeof(int), fc2); - Assert.Equal(typeof(Guid), fc3); - Assert.Equal(typeof(DateTime), fc4); - Assert.Equal(typeof(Stream), fc5); - Assert.Null(fc6); - Assert.Null(fc7); - } + Assert.NotNull(sut1); + Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc1)); + Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc2)); + Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc3)); + Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc4)); + Assert.True(sut1.TryInvoke(new FakeCommand(), out var fc5)); + Assert.False(sut1.TryInvoke(new FakeCommand(), out var fc6)); + Assert.False(sut1.TryInvoke(new FakeCommand(), out var fc7)); + Assert.Null(fc1); + Assert.Equal(typeof(int), fc2); + Assert.Equal(typeof(Guid), fc3); + Assert.Equal(typeof(DateTime), fc4); + Assert.Equal(typeof(Stream), fc5); + Assert.Null(fc6); + Assert.Null(fc7); + } - [Fact] - public async Task CreateRequestReply_ShouldInvokeRegisteredHandlersAsync() + [Fact] + public async Task CreateRequestReply_ShouldInvokeRegisteredHandlersAsync() + { + var sut1 = HandlerFactory.CreateRequestReply(registry => { - var sut1 = HandlerFactory.CreateRequestReply(registry => - { - registry.RegisterAsync((c, _) => Task.FromResult(c.Type)); - registry.RegisterAsync, Type>(c => Task.FromResult(c.Type)); - registry.RegisterAsync, Type>(c => Task.FromResult(c.Type)); - registry.RegisterAsync, Type>(c => Task.FromResult(c.Type)); - registry.RegisterAsync, Type>(c => Task.FromResult(c.Type)); - }); + registry.RegisterAsync((c, _) => Task.FromResult(c.Type)); + registry.RegisterAsync, Type>(c => Task.FromResult(c.Type)); + registry.RegisterAsync, Type>(c => Task.FromResult(c.Type)); + registry.RegisterAsync, Type>(c => Task.FromResult(c.Type)); + registry.RegisterAsync, Type>(c => Task.FromResult(c.Type)); + }); - var sut2 = await sut1.TryInvokeAsync(new FakeCommand()); - var sut3 = await sut1.TryInvokeAsync(new FakeCommand()); - var sut4 = await sut1.TryInvokeAsync(new FakeCommand()); - var sut5 = await sut1.TryInvokeAsync(new FakeCommand()); - var sut6 = await sut1.TryInvokeAsync(new FakeCommand()); - var sut7 = await sut1.TryInvokeAsync(new FakeCommand()); - var sut8 = await sut1.TryInvokeAsync(new FakeCommand()); + var sut2 = await sut1.TryInvokeAsync(new FakeCommand()); + var sut3 = await sut1.TryInvokeAsync(new FakeCommand()); + var sut4 = await sut1.TryInvokeAsync(new FakeCommand()); + var sut5 = await sut1.TryInvokeAsync(new FakeCommand()); + var sut6 = await sut1.TryInvokeAsync(new FakeCommand()); + var sut7 = await sut1.TryInvokeAsync(new FakeCommand()); + var sut8 = await sut1.TryInvokeAsync(new FakeCommand()); - Assert.NotNull(sut1); - Assert.True(sut2.Succeeded); - Assert.True(sut3.Succeeded); - Assert.True(sut4.Succeeded); - Assert.True(sut5.Succeeded); - Assert.True(sut6.Succeeded); - Assert.False(sut7.Succeeded); - Assert.False(sut8.Succeeded); - Assert.Null(sut2.Result); - Assert.Equal(typeof(int), sut3.Result); - Assert.Equal(typeof(Guid), sut4.Result); - Assert.Equal(typeof(DateTime), sut5.Result); - Assert.Equal(typeof(Stream), sut6.Result); - Assert.Null(sut7.Result); - Assert.Null(sut8.Result); - } + Assert.NotNull(sut1); + Assert.True(sut2.Succeeded); + Assert.True(sut3.Succeeded); + Assert.True(sut4.Succeeded); + Assert.True(sut5.Succeeded); + Assert.True(sut6.Succeeded); + Assert.False(sut7.Succeeded); + Assert.False(sut8.Succeeded); + Assert.Null(sut2.Result); + Assert.Equal(typeof(int), sut3.Result); + Assert.Equal(typeof(Guid), sut4.Result); + Assert.Equal(typeof(DateTime), sut5.Result); + Assert.Equal(typeof(Stream), sut6.Result); + Assert.Null(sut7.Result); + Assert.Null(sut8.Result); } } diff --git a/test/Savvyio.Core.Tests/Messaging/Cryptography/SignedMessageOptionsTest.cs b/test/Savvyio.Core.Tests/Messaging/Cryptography/SignedMessageOptionsTest.cs index 7b357710..9d1e27d1 100644 --- a/test/Savvyio.Core.Tests/Messaging/Cryptography/SignedMessageOptionsTest.cs +++ b/test/Savvyio.Core.Tests/Messaging/Cryptography/SignedMessageOptionsTest.cs @@ -1,47 +1,46 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Messaging.Cryptography +namespace Savvyio.Messaging.Cryptography; + +public class SignedMessageOptionsTest : Test { - public class SignedMessageOptionsTest : Test + public SignedMessageOptionsTest(ITestOutputHelper helper) : base(helper) { - public SignedMessageOptionsTest(ITestOutputHelper helper) : base(helper) - { - } + } - [Fact] - public void ValidateOptions_WhenSignatureSecretIsNull_ShouldThrowInvalidOperationException() - { - var options = new SignedMessageOptions(); - options.SignatureSecret = null; + [Fact] + public void ValidateOptions_WhenSignatureSecretIsNull_ShouldThrowInvalidOperationException() + { + var options = new SignedMessageOptions(); + options.SignatureSecret = null; - var ex = Assert.Throws(() => options.ValidateOptions()); + var ex = Assert.Throws(() => options.ValidateOptions()); - TestOutput.WriteLine(ex.Message); + TestOutput.WriteLine(ex.Message); - Assert.Equal("Operation is not valid due to the current state of the object. (Expression 'SignatureSecret == null || SignatureSecret.Length == 0')", ex.Message); - } + Assert.Equal("Operation is not valid due to the current state of the object. (Expression 'SignatureSecret == null || SignatureSecret.Length == 0')", ex.Message); + } - [Fact] - public void ValidateOptions_WhenSignatureSecretIsEmpty_ShouldThrowInvalidOperationException() - { - var options = new SignedMessageOptions(); - options.SignatureSecret = new byte[0]; + [Fact] + public void ValidateOptions_WhenSignatureSecretIsEmpty_ShouldThrowInvalidOperationException() + { + var options = new SignedMessageOptions(); + options.SignatureSecret = new byte[0]; - var ex = Assert.Throws(() => options.ValidateOptions()); + var ex = Assert.Throws(() => options.ValidateOptions()); - TestOutput.WriteLine(ex.Message); + TestOutput.WriteLine(ex.Message); - Assert.Equal("Operation is not valid due to the current state of the object. (Expression 'SignatureSecret == null || SignatureSecret.Length == 0')", ex.Message); - } + Assert.Equal("Operation is not valid due to the current state of the object. (Expression 'SignatureSecret == null || SignatureSecret.Length == 0')", ex.Message); + } - [Fact] - public void ValidateOptions_WhenAllPropertiesAreValid_ShouldNotThrowException() - { - var options = new SignedMessageOptions(); - options.SignatureSecret = new byte[] { 1, 2, 3 }; - options.ValidateOptions(); - } + [Fact] + public void ValidateOptions_WhenAllPropertiesAreValid_ShouldNotThrowException() + { + var options = new SignedMessageOptions(); + options.SignatureSecret = new byte[] { 1, 2, 3 }; + options.ValidateOptions(); } } diff --git a/test/Savvyio.Core.Tests/Messaging/Cryptography/SignedMessageTest.cs b/test/Savvyio.Core.Tests/Messaging/Cryptography/SignedMessageTest.cs index ea6992c6..195599c0 100644 --- a/test/Savvyio.Core.Tests/Messaging/Cryptography/SignedMessageTest.cs +++ b/test/Savvyio.Core.Tests/Messaging/Cryptography/SignedMessageTest.cs @@ -1,86 +1,85 @@ -using System; +using System; using Cuemon.Extensions; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Messaging.Cryptography +namespace Savvyio.Messaging.Cryptography; + +public class SignedMessageTest : Test { - public class SignedMessageTest : Test + public SignedMessageTest(ITestOutputHelper helper) : base(helper) + { + } + + [Fact] + public void Constructor_WhenMessageIsNull_ShouldThrowArgumentNullException() { - public SignedMessageTest(ITestOutputHelper helper) : base(helper) - { - } - - [Fact] - public void Constructor_WhenMessageIsNull_ShouldThrowArgumentNullException() - { - var ex = Assert.Throws(() => new SignedMessage(null, "signature")); - - TestOutput.WriteLine(ex.Message); - - Assert.Equal("Value cannot be null. (Parameter 'message')", ex.Message); - } - - [Fact] - public void Constructor_WhenSignatureIsNull_ShouldThrowArgumentNullException() - { - var uuid = Guid.NewGuid(); - var fullName = "John Doe"; - var email = "jdoe@me.com"; - var message = new Message("MyId", "uri:source".ToUri(), nameof(CreateAccount), new CreateAccount(uuid, fullName, email)); - var ex = Assert.Throws(() => new SignedMessage(message, null)); - - TestOutput.WriteLine(ex.Message); - - Assert.Equal("Value cannot be null. (Parameter 'signature')", ex.Message); - } - - [Fact] - public void Constructor_WhenSignatureIsEmpty_ShouldThrowArgumentException() - { - var uuid = Guid.NewGuid(); - var fullName = "John Doe"; - var email = "jdoe@me.com"; - var message = new Message("MyId", "uri:source".ToUri(), nameof(CreateAccount), new CreateAccount(uuid, fullName, email)); - var ex = Assert.Throws(() => new SignedMessage(message, string.Empty)); - - TestOutput.WriteLine(ex.Message); - - Assert.Equal("Value cannot be empty. (Parameter 'signature')", ex.Message); - } - - [Fact] - public void Constructor_WhenSignatureIsWhitespace_ShouldThrowArgumentException() - { - var uuid = Guid.NewGuid(); - var fullName = "John Doe"; - var email = "jdoe@me.com"; - var message = new Message("MyId", "uri:source".ToUri(), nameof(CreateAccount), new CreateAccount(uuid, fullName, email)); - var ex = Assert.Throws(() => new SignedMessage(message, " ")); - - TestOutput.WriteLine(ex.Message); - - Assert.Equal("Value cannot consist only of white-space characters. (Parameter 'signature')", ex.Message); - } - - [Fact] - public void Constructor_WhenAllPropertiesAreValid_ShouldCreateInstance() - { - var uuid = Guid.NewGuid(); - var fullName = "John Doe"; - var email = "jdoe@me.com"; - var message = new Message("MyId", "uri:source".ToUri(), nameof(CreateAccount), new CreateAccount(uuid, fullName, email)); - var signature = "signature"; - var signedMessage = new SignedMessage(message, signature); - - Assert.NotNull(signedMessage); - Assert.Equal(message.Id, signedMessage.Id); - Assert.Equal(message.Source, signedMessage.Source); - Assert.Equal(message.Type, signedMessage.Type); - Assert.Equal(message.Time, signedMessage.Time); - Assert.Equal(message.Data, signedMessage.Data); - Assert.Equal(signature, signedMessage.Signature); - } + var ex = Assert.Throws(() => new SignedMessage(null, "signature")); + + TestOutput.WriteLine(ex.Message); + + Assert.Equal("Value cannot be null. (Parameter 'message')", ex.Message); + } + + [Fact] + public void Constructor_WhenSignatureIsNull_ShouldThrowArgumentNullException() + { + var uuid = Guid.NewGuid(); + var fullName = "John Doe"; + var email = "jdoe@me.com"; + var message = new Message("MyId", "uri:source".ToUri(), nameof(CreateAccount), new CreateAccount(uuid, fullName, email)); + var ex = Assert.Throws(() => new SignedMessage(message, null)); + + TestOutput.WriteLine(ex.Message); + + Assert.Equal("Value cannot be null. (Parameter 'signature')", ex.Message); + } + + [Fact] + public void Constructor_WhenSignatureIsEmpty_ShouldThrowArgumentException() + { + var uuid = Guid.NewGuid(); + var fullName = "John Doe"; + var email = "jdoe@me.com"; + var message = new Message("MyId", "uri:source".ToUri(), nameof(CreateAccount), new CreateAccount(uuid, fullName, email)); + var ex = Assert.Throws(() => new SignedMessage(message, string.Empty)); + + TestOutput.WriteLine(ex.Message); + + Assert.Equal("Value cannot be empty. (Parameter 'signature')", ex.Message); + } + + [Fact] + public void Constructor_WhenSignatureIsWhitespace_ShouldThrowArgumentException() + { + var uuid = Guid.NewGuid(); + var fullName = "John Doe"; + var email = "jdoe@me.com"; + var message = new Message("MyId", "uri:source".ToUri(), nameof(CreateAccount), new CreateAccount(uuid, fullName, email)); + var ex = Assert.Throws(() => new SignedMessage(message, " ")); + + TestOutput.WriteLine(ex.Message); + + Assert.Equal("Value cannot consist only of white-space characters. (Parameter 'signature')", ex.Message); + } + + [Fact] + public void Constructor_WhenAllPropertiesAreValid_ShouldCreateInstance() + { + var uuid = Guid.NewGuid(); + var fullName = "John Doe"; + var email = "jdoe@me.com"; + var message = new Message("MyId", "uri:source".ToUri(), nameof(CreateAccount), new CreateAccount(uuid, fullName, email)); + var signature = "signature"; + var signedMessage = new SignedMessage(message, signature); + + Assert.NotNull(signedMessage); + Assert.Equal(message.Id, signedMessage.Id); + Assert.Equal(message.Source, signedMessage.Source); + Assert.Equal(message.Type, signedMessage.Type); + Assert.Equal(message.Time, signedMessage.Time); + Assert.Equal(message.Data, signedMessage.Data); + Assert.Equal(signature, signedMessage.Signature); } } diff --git a/test/Savvyio.Core.Tests/Messaging/MessageOptionsTest.cs b/test/Savvyio.Core.Tests/Messaging/MessageOptionsTest.cs index 177a076d..a0036752 100644 --- a/test/Savvyio.Core.Tests/Messaging/MessageOptionsTest.cs +++ b/test/Savvyio.Core.Tests/Messaging/MessageOptionsTest.cs @@ -1,56 +1,55 @@ -using System; +using System; using Cuemon; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +public class MessageOptionsTest : Test { - public class MessageOptionsTest : Test + public MessageOptionsTest(ITestOutputHelper output) : base(output) { - public MessageOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void MessageOptions_ShouldHaveDefaultValues() - { - var sut = new MessageOptions(); + [Fact] + public void MessageOptions_ShouldHaveDefaultValues() + { + var sut = new MessageOptions(); - Assert.NotNull(sut.MessageId); - Assert.Null(sut.Time); - } + Assert.NotNull(sut.MessageId); + Assert.Null(sut.Time); + } - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - public void MessageOptions_ThrowsInvalidOperationException_WhenMessageIdIsNullOrEmptyOrConsistOfWhiteSpaceOnly(string messageId) + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void MessageOptions_ThrowsInvalidOperationException_WhenMessageIdIsNullOrEmptyOrConsistOfWhiteSpaceOnly(string messageId) + { + var sut1 = new MessageOptions { - var sut1 = new MessageOptions - { - MessageId = messageId - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - - Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(MessageOptions.MessageId)}.IsNullOrWhiteSpace()')", sut2.Message); - Assert.Equal($"{nameof(MessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } - - [Fact] - public void MessageOptions_ThrowsInvalidOperationException_WhenTimeIsLocal() + MessageId = messageId + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + + Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(MessageOptions.MessageId)}.IsNullOrWhiteSpace()')", sut2.Message); + Assert.Equal($"{nameof(MessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } + + [Fact] + public void MessageOptions_ThrowsInvalidOperationException_WhenTimeIsLocal() + { + var sut1 = new MessageOptions { - var sut1 = new MessageOptions - { - Time = DateTime.Now - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - - Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(MessageOptions.Time)}.HasValue && {nameof(MessageOptions.Time)}.Value.Kind != DateTimeKind.Utc')", sut2.Message); - Assert.Equal($"{nameof(MessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } + Time = DateTime.Now + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + + Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(MessageOptions.Time)}.HasValue && {nameof(MessageOptions.Time)}.Value.Kind != DateTimeKind.Utc')", sut2.Message); + Assert.Equal($"{nameof(MessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); } } diff --git a/test/Savvyio.Core.Tests/Messaging/MessageTest.cs b/test/Savvyio.Core.Tests/Messaging/MessageTest.cs index c46f2886..9e657c6f 100644 --- a/test/Savvyio.Core.Tests/Messaging/MessageTest.cs +++ b/test/Savvyio.Core.Tests/Messaging/MessageTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.Reflection; @@ -6,60 +6,59 @@ using Savvyio.Assets; using Xunit; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +public class MessageTest : Test { - public class MessageTest : Test + public MessageTest(ITestOutputHelper output) : base(output) { - public MessageTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Ctor_WithRequiredArguments_RemainingArgumentsShouldHaveDefaultValues() - { - var id = Generate.RandomString(16); - var source = "urn:source".ToUri(); - var dataUuid = Guid.NewGuid(); - var dataNumber = Generate.RandomNumber(); - var data = new RootDummyRequest(dataUuid, dataNumber); - var utcNow = DateTime.UtcNow; - var sut = new Message(id, source, nameof(RootDummyRequest), data); + [Fact] + public void Ctor_WithRequiredArguments_RemainingArgumentsShouldHaveDefaultValues() + { + var id = Generate.RandomString(16); + var source = "urn:source".ToUri(); + var dataUuid = Guid.NewGuid(); + var dataNumber = Generate.RandomNumber(); + var data = new RootDummyRequest(dataUuid, dataNumber); + var utcNow = DateTime.UtcNow; + var sut = new Message(id, source, nameof(RootDummyRequest), data); - TestOutput.WriteLine(sut.Data.ToString()); + TestOutput.WriteLine(sut.Data.ToString()); - var convertedTime = sut.Time!; - Assert.Equal(id, sut.Id); - Assert.Equal(source.OriginalString, sut.Source); - Assert.Equal(data, sut.Data); - Assert.Equal(data.GetType().ToFullNameIncludingAssemblyName(), sut.Data.GetMemberType()); - Assert.Equal(nameof(RootDummyRequest), sut.Type); - Assert.Equal(DateTimeKind.Utc, convertedTime.Value.Kind); - Assert.InRange(convertedTime.Value, utcNow.Subtract(TimeSpan.FromSeconds(2)), utcNow.Add(TimeSpan.FromSeconds(2))); - } + var convertedTime = sut.Time!; + Assert.Equal(id, sut.Id); + Assert.Equal(source.OriginalString, sut.Source); + Assert.Equal(data, sut.Data); + Assert.Equal(data.GetType().ToFullNameIncludingAssemblyName(), sut.Data.GetMemberType()); + Assert.Equal(nameof(RootDummyRequest), sut.Type); + Assert.Equal(DateTimeKind.Utc, convertedTime.Value.Kind); + Assert.InRange(convertedTime.Value, utcNow.Subtract(TimeSpan.FromSeconds(2)), utcNow.Add(TimeSpan.FromSeconds(2))); + } - [Fact] - public void Ctor_WithBothRequiredAndOptionalArguments() - { - var id = Generate.RandomString(16); - var source = "urn:source".ToUri(); - var dataUuid = Guid.NewGuid(); - var dataNumber = Generate.RandomNumber(); - var dataType = typeof(DummyRequest); - var type = "com.example.someevent"; - var data = new RootDummyRequest(dataUuid, dataNumber).SetMemberType(dataType); - var utcNow = DateTime.UtcNow; - var sut1 = new Message(id, source, type, data, utcNow); + [Fact] + public void Ctor_WithBothRequiredAndOptionalArguments() + { + var id = Generate.RandomString(16); + var source = "urn:source".ToUri(); + var dataUuid = Guid.NewGuid(); + var dataNumber = Generate.RandomNumber(); + var dataType = typeof(DummyRequest); + var type = "com.example.someevent"; + var data = new RootDummyRequest(dataUuid, dataNumber).SetMemberType(dataType); + var utcNow = DateTime.UtcNow; + var sut1 = new Message(id, source, type, data, utcNow); - TestOutput.WriteLine(sut1.Data.ToString()); + TestOutput.WriteLine(sut1.Data.ToString()); - var convertedTime = sut1.Time!; - Assert.Equal(id, sut1.Id); - Assert.Equal(source.OriginalString, sut1.Source); - Assert.Equal(data, sut1.Data); - Assert.Equal(type, sut1.Type); - Assert.Equal(dataType.ToFullNameIncludingAssemblyName(), sut1.Data.GetMemberType()); - Assert.Equal(DateTimeKind.Utc, convertedTime.Value.Kind); - Assert.Equal(utcNow, convertedTime); - } + var convertedTime = sut1.Time!; + Assert.Equal(id, sut1.Id); + Assert.Equal(source.OriginalString, sut1.Source); + Assert.Equal(data, sut1.Data); + Assert.Equal(type, sut1.Type); + Assert.Equal(dataType.ToFullNameIncludingAssemblyName(), sut1.Data.GetMemberType()); + Assert.Equal(DateTimeKind.Utc, convertedTime.Value.Kind); + Assert.Equal(utcNow, convertedTime); } } diff --git a/test/Savvyio.Core.Tests/MetadataDictionaryTest.cs b/test/Savvyio.Core.Tests/MetadataDictionaryTest.cs index 14416b9b..4fe3ba5e 100644 --- a/test/Savvyio.Core.Tests/MetadataDictionaryTest.cs +++ b/test/Savvyio.Core.Tests/MetadataDictionaryTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Cuemon; @@ -6,193 +6,192 @@ using Cuemon.Reflection; using Xunit; -namespace Savvyio +namespace Savvyio; + +public class MetadataDictionaryTest : Test { - public class MetadataDictionaryTest : Test + public MetadataDictionaryTest(ITestOutputHelper output) : base(output) { - public MetadataDictionaryTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Indexer_ShouldGetAndSetElementWithSpecifiedKey() - { - var sut = new MetadataDictionary(); - sut["key1"] = "value"; - sut["key2"] = Guid.Empty; - sut["key3"] = int.MaxValue; - - Assert.Equal(sut["key1"], "value"); - Assert.Equal(sut["key2"], Guid.Empty); - Assert.Equal(sut["key3"], int.MaxValue); - } + [Fact] + public void Indexer_ShouldGetAndSetElementWithSpecifiedKey() + { + var sut = new MetadataDictionary(); + sut["key1"] = "value"; + sut["key2"] = Guid.Empty; + sut["key3"] = int.MaxValue; + + Assert.Equal(sut["key1"], "value"); + Assert.Equal(sut["key2"], Guid.Empty); + Assert.Equal(sut["key3"], int.MaxValue); + } - [Fact] - public void ReservedKeywords_ShouldMatchNumberOfPublicConst() + [Fact] + public void ReservedKeywords_ShouldMatchNumberOfPublicConst() + { + var sut = typeof(MetadataDictionary); + var mr = new MemberReflection(o => { - var sut = typeof(MetadataDictionary); - var mr = new MemberReflection(o => - { - o.ExcludeInheritancePath = true; - }); + o.ExcludeInheritancePath = true; + }); - var reservedKeywords = MetadataDictionary.ReservedKeywords; - var keywordConstants = sut.GetFields(mr.Flags).Where(fi => fi.IsPublic).Select(fi => fi.Name); + var reservedKeywords = MetadataDictionary.ReservedKeywords; + var keywordConstants = sut.GetFields(mr.Flags).Where(fi => fi.IsPublic).Select(fi => fi.Name); - Assert.Equal(reservedKeywords, keywordConstants); - } + Assert.Equal(reservedKeywords, keywordConstants); + } - [Fact] - public void Count_ShouldMatchNumberOfElements() + [Fact] + public void Count_ShouldMatchNumberOfElements() + { + var sut = new MetadataDictionary(); + for (var i = 0; i < 10; i++) { - var sut = new MetadataDictionary(); - for (var i = 0; i < 10; i++) - { - sut.Add(i.ToString(), i); - } - - Assert.Equal(sut.Count, 10); + sut.Add(i.ToString(), i); } - [Fact] - public void IsReadOnly_ShouldBeFalse() - { - var sut = new MetadataDictionary(); - - Assert.False(sut.IsReadOnly); - } + Assert.Equal(sut.Count, 10); + } - [Fact] - public void Keys_ShouldAllBeNumbers() - { - var sut = new MetadataDictionary(); - for (var i = 0; i < 10; i++) - { - sut.Add(i.ToString(), i); - } + [Fact] + public void IsReadOnly_ShouldBeFalse() + { + var sut = new MetadataDictionary(); - Assert.Equal(Generate.RangeOf(10, i => i.ToString()), sut.Keys); - } + Assert.False(sut.IsReadOnly); + } - [Fact] - public void Values_ShouldAllBeNumbers() + [Fact] + public void Keys_ShouldAllBeNumbers() + { + var sut = new MetadataDictionary(); + for (var i = 0; i < 10; i++) { - var sut = new MetadataDictionary(); - for (var i = 0; i < 10; i++) - { - sut.Add(i.ToString(), i); - } - - Assert.Equal(Generate.RangeOf(10, i => i), sut.Values.Cast()); + sut.Add(i.ToString(), i); } - [Fact] - public void Add_ShouldAddElementWithSpecifiedKey() + Assert.Equal(Generate.RangeOf(10, i => i.ToString()), sut.Keys); + } + + [Fact] + public void Values_ShouldAllBeNumbers() + { + var sut = new MetadataDictionary(); + for (var i = 0; i < 10; i++) { - var sut = new MetadataDictionary(); - sut.Add("key1", "value"); - sut.Add("key2", Guid.Empty); - sut.Add(new KeyValuePair("key3", int.MaxValue)); - - Assert.Equal(sut["key1"], "value"); - Assert.Equal(sut["key2"], Guid.Empty); - Assert.Equal(sut["key3"], int.MaxValue); + sut.Add(i.ToString(), i); } - [Fact] - public void Clear_ShouldRemoveAllElements() - { - var sut = new MetadataDictionary(); - sut.Add("key1", "value"); - sut.Add("key2", Guid.Empty); - sut.Add(new KeyValuePair("key3", int.MaxValue)); - sut.Clear(); + Assert.Equal(Generate.RangeOf(10, i => i), sut.Values.Cast()); + } - Assert.Empty(sut); - } + [Fact] + public void Add_ShouldAddElementWithSpecifiedKey() + { + var sut = new MetadataDictionary(); + sut.Add("key1", "value"); + sut.Add("key2", Guid.Empty); + sut.Add(new KeyValuePair("key3", int.MaxValue)); + + Assert.Equal(sut["key1"], "value"); + Assert.Equal(sut["key2"], Guid.Empty); + Assert.Equal(sut["key3"], int.MaxValue); + } - [Fact] - public void CopyTo_ShouldCopyElementsToArray() - { - var sut = new MetadataDictionary(); - sut.Add("key1", "value"); - sut.Add("key2", Guid.Empty); - sut.Add(new KeyValuePair("key3", int.MaxValue)); + [Fact] + public void Clear_ShouldRemoveAllElements() + { + var sut = new MetadataDictionary(); + sut.Add("key1", "value"); + sut.Add("key2", Guid.Empty); + sut.Add(new KeyValuePair("key3", int.MaxValue)); + sut.Clear(); - var col = new KeyValuePair[3]; - sut.CopyTo(col, 0); + Assert.Empty(sut); + } - Assert.Equal(sut, col); - } + [Fact] + public void CopyTo_ShouldCopyElementsToArray() + { + var sut = new MetadataDictionary(); + sut.Add("key1", "value"); + sut.Add("key2", Guid.Empty); + sut.Add(new KeyValuePair("key3", int.MaxValue)); - [Fact] - public void Contains_ShouldFindMatchingItems() - { - var sut = new MetadataDictionary(); - sut.Add("key1", "value"); - sut.Add("key2", Guid.Empty); - sut.Add(new KeyValuePair("key3", int.MaxValue)); - - Assert.True(sut.Contains(new KeyValuePair("key1", "value"))); - Assert.True(sut.Contains(new KeyValuePair("key2", Guid.Empty))); - Assert.True(sut.Contains(new KeyValuePair("key3", int.MaxValue))); - Assert.False(sut.Contains(new KeyValuePair("key4", DateTime.MaxValue))); - } + var col = new KeyValuePair[3]; + sut.CopyTo(col, 0); - [Fact] - public void ContainsKey_ShouldFindMatchingItems() - { - var sut = new MetadataDictionary(); - sut.Add("key1", "value"); - sut.Add("key2", Guid.Empty); - sut.Add(new KeyValuePair("key3", int.MaxValue)); - - Assert.True(sut.ContainsKey("key1")); - Assert.True(sut.ContainsKey("key2")); - Assert.True(sut.ContainsKey("key3")); - Assert.False(sut.ContainsKey("key4")); - } + Assert.Equal(sut, col); + } - [Fact] - public void Remove_ShouldRemoveItemByKey() - { - var sut = new MetadataDictionary(); - sut.Add("key1", "value"); - sut.Add("key2", Guid.Empty); - sut.Add(new KeyValuePair("key3", int.MaxValue)); + [Fact] + public void Contains_ShouldFindMatchingItems() + { + var sut = new MetadataDictionary(); + sut.Add("key1", "value"); + sut.Add("key2", Guid.Empty); + sut.Add(new KeyValuePair("key3", int.MaxValue)); + + Assert.True(sut.Contains(new KeyValuePair("key1", "value"))); + Assert.True(sut.Contains(new KeyValuePair("key2", Guid.Empty))); + Assert.True(sut.Contains(new KeyValuePair("key3", int.MaxValue))); + Assert.False(sut.Contains(new KeyValuePair("key4", DateTime.MaxValue))); + } - sut.Remove("key1"); + [Fact] + public void ContainsKey_ShouldFindMatchingItems() + { + var sut = new MetadataDictionary(); + sut.Add("key1", "value"); + sut.Add("key2", Guid.Empty); + sut.Add(new KeyValuePair("key3", int.MaxValue)); + + Assert.True(sut.ContainsKey("key1")); + Assert.True(sut.ContainsKey("key2")); + Assert.True(sut.ContainsKey("key3")); + Assert.False(sut.ContainsKey("key4")); + } - Assert.False(sut.ContainsKey("key1")); - Assert.True(sut.Count == 2, "sut.Count == 2"); - } + [Fact] + public void Remove_ShouldRemoveItemByKey() + { + var sut = new MetadataDictionary(); + sut.Add("key1", "value"); + sut.Add("key2", Guid.Empty); + sut.Add(new KeyValuePair("key3", int.MaxValue)); - [Fact] - public void Remove_ShouldRemoveItemByKeyValuePair() - { - var kvp = new KeyValuePair("key1", "value"); - var sut = new MetadataDictionary(); - sut.Add("key1", "value"); - sut.Add("key2", Guid.Empty); - sut.Add(new KeyValuePair("key3", int.MaxValue)); + sut.Remove("key1"); - sut.Remove(kvp); + Assert.False(sut.ContainsKey("key1")); + Assert.True(sut.Count == 2, "sut.Count == 2"); + } - Assert.False(sut.Contains(kvp)); - Assert.True(sut.Count == 2, "sut.Count == 2"); - } + [Fact] + public void Remove_ShouldRemoveItemByKeyValuePair() + { + var kvp = new KeyValuePair("key1", "value"); + var sut = new MetadataDictionary(); + sut.Add("key1", "value"); + sut.Add("key2", Guid.Empty); + sut.Add(new KeyValuePair("key3", int.MaxValue)); - [Fact] - public void TryGetValue_ShouldRetrieveValue() - { - var sut = new MetadataDictionary(); - sut.Add("key1", "value"); - sut.Add("key2", Guid.Empty); - sut.Add(new KeyValuePair("key3", int.MaxValue)); + sut.Remove(kvp); - sut.TryGetValue("key2", out var key2Value); + Assert.False(sut.Contains(kvp)); + Assert.True(sut.Count == 2, "sut.Count == 2"); + } - Assert.Equal(Guid.Empty, key2Value); - } + [Fact] + public void TryGetValue_ShouldRetrieveValue() + { + var sut = new MetadataDictionary(); + sut.Add("key1", "value"); + sut.Add("key2", Guid.Empty); + sut.Add(new KeyValuePair("key3", int.MaxValue)); + + sut.TryGetValue("key2", out var key2Value); + + Assert.Equal(Guid.Empty, key2Value); } } diff --git a/test/Savvyio.Core.Tests/MetadataExtensionsTest.cs b/test/Savvyio.Core.Tests/MetadataExtensionsTest.cs index 28da0db3..da6ec00a 100644 --- a/test/Savvyio.Core.Tests/MetadataExtensionsTest.cs +++ b/test/Savvyio.Core.Tests/MetadataExtensionsTest.cs @@ -3,170 +3,169 @@ using Cuemon.Extensions.Reflection; using Xunit; -namespace Savvyio +namespace Savvyio; + +public class MetadataExtensionsTest : Test { - public class MetadataExtensionsTest : Test + public MetadataExtensionsTest(ITestOutputHelper output) : base(output) { - public MetadataExtensionsTest(ITestOutputHelper output) : base(output) - { - - } - private class TestMetadata : IMetadata - { - public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); - } + } + + private class TestMetadata : IMetadata + { + public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); + } - [Fact] - public void GetCausationId_ShouldReturnCausationId() - { - var request = new TestMetadata(); - request.Metadata[MetadataDictionary.CausationId] = "causation-id"; + [Fact] + public void GetCausationId_ShouldReturnCausationId() + { + var request = new TestMetadata(); + request.Metadata[MetadataDictionary.CausationId] = "causation-id"; - var result = request.GetCausationId(); + var result = request.GetCausationId(); - Assert.Equal("causation-id", result); - } + Assert.Equal("causation-id", result); + } - [Fact] - public void GetCorrelationId_ShouldReturnCorrelationId() - { - var request = new TestMetadata(); - request.Metadata[MetadataDictionary.CorrelationId] = "correlation-id"; + [Fact] + public void GetCorrelationId_ShouldReturnCorrelationId() + { + var request = new TestMetadata(); + request.Metadata[MetadataDictionary.CorrelationId] = "correlation-id"; - var result = request.GetCorrelationId(); + var result = request.GetCorrelationId(); - Assert.Equal("correlation-id", result); - } + Assert.Equal("correlation-id", result); + } - [Fact] - public void GetRequestId_ShouldReturnRequestId() - { - var request = new TestMetadata(); - request.SetRequestId("request-id"); + [Fact] + public void GetRequestId_ShouldReturnRequestId() + { + var request = new TestMetadata(); + request.SetRequestId("request-id"); - var result = request.GetRequestId(); + var result = request.GetRequestId(); - Assert.Equal("request-id", result); - } + Assert.Equal("request-id", result); + } - [Fact] - public void GetMemberType_ShouldReturnMemberType() - { - var request = new TestMetadata(); - request.Metadata[MetadataDictionary.MemberType] = "member-type"; + [Fact] + public void GetMemberType_ShouldReturnMemberType() + { + var request = new TestMetadata(); + request.Metadata[MetadataDictionary.MemberType] = "member-type"; - var result = request.GetMemberType(); + var result = request.GetMemberType(); - Assert.Equal("member-type", result); - } + Assert.Equal("member-type", result); + } - [Fact] - public void SetCausationId_ShouldSetCausationId() - { - var request = new TestMetadata(); + [Fact] + public void SetCausationId_ShouldSetCausationId() + { + var request = new TestMetadata(); - request.SetCausationId("new-causation-id"); + request.SetCausationId("new-causation-id"); - Assert.Equal("new-causation-id", request.Metadata[MetadataDictionary.CausationId]); - } + Assert.Equal("new-causation-id", request.Metadata[MetadataDictionary.CausationId]); + } - [Fact] - public void SetCorrelationId_ShouldSetCorrelationId() - { - var request = new TestMetadata(); + [Fact] + public void SetCorrelationId_ShouldSetCorrelationId() + { + var request = new TestMetadata(); - request.SetCorrelationId("new-correlation-id"); + request.SetCorrelationId("new-correlation-id"); - Assert.Equal("new-correlation-id", request.Metadata[MetadataDictionary.CorrelationId]); - } + Assert.Equal("new-correlation-id", request.Metadata[MetadataDictionary.CorrelationId]); + } - [Fact] - public void SetRequestId_ShouldSetRequestId() - { - var request = new TestMetadata(); + [Fact] + public void SetRequestId_ShouldSetRequestId() + { + var request = new TestMetadata(); - request.SetRequestId("new-request-id"); + request.SetRequestId("new-request-id"); - Assert.Equal("new-request-id", request.Metadata[MetadataDictionary.RequestId]); - } + Assert.Equal("new-request-id", request.Metadata[MetadataDictionary.RequestId]); + } - [Fact] - public void SetEventId_ShouldSetEventId() - { - var request = new TestMetadata(); + [Fact] + public void SetEventId_ShouldSetEventId() + { + var request = new TestMetadata(); - request.SetEventId("new-event-id"); + request.SetEventId("new-event-id"); - Assert.Equal("new-event-id", request.Metadata[MetadataDictionary.EventId]); - } + Assert.Equal("new-event-id", request.Metadata[MetadataDictionary.EventId]); + } - [Fact] - public void SetTimestamp_ShouldSetTimestamp() - { - var request = new TestMetadata(); - var timestamp = DateTime.UtcNow; + [Fact] + public void SetTimestamp_ShouldSetTimestamp() + { + var request = new TestMetadata(); + var timestamp = DateTime.UtcNow; - request.SetTimestamp(timestamp); + request.SetTimestamp(timestamp); - Assert.Equal(timestamp, request.Metadata[MetadataDictionary.Timestamp]); - } + Assert.Equal(timestamp, request.Metadata[MetadataDictionary.Timestamp]); + } - [Fact] - public void SetTimestamp_ShouldSetCurrentUtcTimestamp_WhenNoTimestampProvided() - { - var request = new TestMetadata(); + [Fact] + public void SetTimestamp_ShouldSetCurrentUtcTimestamp_WhenNoTimestampProvided() + { + var request = new TestMetadata(); - request.SetTimestamp(); + request.SetTimestamp(); - Assert.True(request.Metadata.ContainsKey(MetadataDictionary.Timestamp)); - Assert.IsType(request.Metadata[MetadataDictionary.Timestamp]); - } + Assert.True(request.Metadata.ContainsKey(MetadataDictionary.Timestamp)); + Assert.IsType(request.Metadata[MetadataDictionary.Timestamp]); + } - [Fact] - public void SetMemberType_ShouldSetMemberType() - { - var request = new TestMetadata(); - var type = typeof(string); + [Fact] + public void SetMemberType_ShouldSetMemberType() + { + var request = new TestMetadata(); + var type = typeof(string); - request.SetMemberType(type); + request.SetMemberType(type); - Assert.Equal(type.ToFullNameIncludingAssemblyName(), request.Metadata[MetadataDictionary.MemberType]); - } + Assert.Equal(type.ToFullNameIncludingAssemblyName(), request.Metadata[MetadataDictionary.MemberType]); + } - [Fact] - public void SaveMetadata_ShouldAddOrUpdateMetadata() - { - var request = new TestMetadata(); + [Fact] + public void SaveMetadata_ShouldAddOrUpdateMetadata() + { + var request = new TestMetadata(); - request.SaveMetadata("key", "value"); + request.SaveMetadata("key", "value"); - Assert.Equal("value", request.Metadata["key"]); - } + Assert.Equal("value", request.Metadata["key"]); + } - [Fact] - public void MergeMetadata_ShouldCopyMetadataFromSourceToDestination() - { - var source = new TestMetadata(); - source.Metadata["key1"] = "value1"; - var destination = new TestMetadata(); + [Fact] + public void MergeMetadata_ShouldCopyMetadataFromSourceToDestination() + { + var source = new TestMetadata(); + source.Metadata["key1"] = "value1"; + var destination = new TestMetadata(); - destination.MergeMetadata(source); + destination.MergeMetadata(source); - Assert.Equal("value1", destination.Metadata["key1"]); - } + Assert.Equal("value1", destination.Metadata["key1"]); + } - [Fact] - public void MergeMetadata_ShouldNotOverwriteExistingMetadata() - { - var source = new TestMetadata(); - source.Metadata["key1"] = "value1"; - var destination = new TestMetadata(); - destination.Metadata["key1"] = "existing-value"; + [Fact] + public void MergeMetadata_ShouldNotOverwriteExistingMetadata() + { + var source = new TestMetadata(); + source.Metadata["key1"] = "value1"; + var destination = new TestMetadata(); + destination.Metadata["key1"] = "existing-value"; - destination.MergeMetadata(source); + destination.MergeMetadata(source); - Assert.Equal("existing-value", destination.Metadata["key1"]); - } + Assert.Equal("existing-value", destination.Metadata["key1"]); } } diff --git a/test/Savvyio.Core.Tests/OrphanedHandlerExceptionTest.cs b/test/Savvyio.Core.Tests/OrphanedHandlerExceptionTest.cs index d99b7c50..10ac80ef 100644 --- a/test/Savvyio.Core.Tests/OrphanedHandlerExceptionTest.cs +++ b/test/Savvyio.Core.Tests/OrphanedHandlerExceptionTest.cs @@ -1,4 +1,4 @@ -using Cuemon.Extensions.IO; +using Cuemon.Extensions.IO; using Cuemon.Extensions.Text.Json.Formatters; using Codebelt.Extensions.Xunit; using Savvyio.Assets; @@ -6,29 +6,28 @@ using Savvyio.Handlers; using Xunit; -namespace Savvyio +namespace Savvyio; + +public class OrphanedHandlerExceptionTest : Test { - public class OrphanedHandlerExceptionTest : Test + public OrphanedHandlerExceptionTest(ITestOutputHelper output) : base(output) { - public OrphanedHandlerExceptionTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Ctor_ShouldBeSerializableAndHaveCorrectDefaultMessage() - { - var sut = OrphanedHandlerException.Create(new FakeCommand(), "fake"); + } - using (var stream = JsonFormatter.SerializeObject(sut)) - { - TestOutput.WriteLine(stream.ToEncodedString(o => o.LeaveOpen = true)); + [Fact] + public void Ctor_ShouldBeSerializableAndHaveCorrectDefaultMessage() + { + var sut = OrphanedHandlerException.Create(new FakeCommand(), "fake"); - var desEx = JsonFormatter.DeserializeObject(stream); + using (var stream = JsonFormatter.SerializeObject(sut)) + { + TestOutput.WriteLine(stream.ToEncodedString(o => o.LeaveOpen = true)); - Assert.Equal(sut.Message, desEx.Message); - Assert.Equal(sut.ToString(), desEx.ToString()); - } + var desEx = JsonFormatter.DeserializeObject(stream); + Assert.Equal(sut.Message, desEx.Message); + Assert.Equal(sut.ToString(), desEx.ToString()); } + } } diff --git a/test/Savvyio.Core.Tests/Reflection/AssemblyContextBehaviorTest.cs b/test/Savvyio.Core.Tests/Reflection/AssemblyContextBehaviorTest.cs index a586f8e1..8926efe5 100644 --- a/test/Savvyio.Core.Tests/Reflection/AssemblyContextBehaviorTest.cs +++ b/test/Savvyio.Core.Tests/Reflection/AssemblyContextBehaviorTest.cs @@ -1,15 +1,20 @@ #nullable enable using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; using System.Runtime.Loader; using Codebelt.Extensions.Xunit; using Cuemon; +using Savvyio; using Savvyio.Commands; using Xunit; -namespace Savvyio.Reflection; +namespace Cuemon.Reflection; public class AssemblyContextBehaviorTest : Test { @@ -19,212 +24,304 @@ public AssemblyContextBehaviorTest(ITestOutputHelper output) : base(output) [Theory] [InlineData(null, false)] + [InlineData("", false)] [InlineData("System.Application", false)] [InlineData("Microsoft.Application", false)] [InlineData("Application.System", true)] [InlineData("system.Application", true)] [InlineData("microsoft.Application", true)] - public void AssemblyFilterCallback_ShouldUseOrdinalPrefixFiltering(string? fullName, bool expected) + public void AssemblyFilter_ShouldUseOrdinalPrefixFiltering(string? fullName, bool expected) { - using var context = new IsolatedAssemblyContext(); + Assert.Equal(expected, new AssemblyContextOptions().AssemblyFilter(new ExportedTypesAssembly(fullName))); + } - Assert.Equal(expected, context.AssemblyFilter(new ReferencedAssembly(fullName))); + [Fact] + public void AssemblyFilter_ShouldRejectNullAssembly() + { + Assert.False(new AssemblyContextOptions().AssemblyFilter(null!)); } [Theory] + [InlineData(null, false)] [InlineData("System.Application", false)] [InlineData("Microsoft.Application", false)] [InlineData("Application.System", true)] [InlineData("system.Application", true)] [InlineData("microsoft.Application", true)] - public void AssemblyDependenciesFilterCallback_ShouldUseOrdinalPrefixFiltering(string name, bool expected) + public void ReferencedAssemblyFilter_ShouldUseOrdinalPrefixFiltering(string? name, bool expected) { - using var context = new IsolatedAssemblyContext(); + var assemblyName = name is null ? null : new AssemblyName(name); - Assert.Equal(expected, context.ReferenceFilter(new AssemblyName(name))); + Assert.Equal(expected, new AssemblyContextOptions().ReferencedAssemblyFilter(assemblyName!)); } [Theory] - [InlineData(nameof(AssemblyContext.AssemblyFilterCallback))] - [InlineData(nameof(AssemblyContext.AssemblyDependenciesCallback))] - [InlineData(nameof(AssemblyContext.AssemblyDependenciesFilterCallback))] - public void Callback_ShouldRejectNullAndPreservePreviousDelegate(string propertyName) + [InlineData(typeof(string), false)] + [InlineData(typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException), false)] + [InlineData(typeof(SavvyioOptions), true)] + public void AssemblyFilter_ShouldInspectExportedRootNamespaces(Type exportedType, bool expected) { - using var context = new IsolatedAssemblyContext(); - var property = context.Type.GetProperty(propertyName)!; - var original = property.GetValue(null); + var assembly = new ExportedTypesAssembly($"Application.NamespaceFilter.{exportedType.FullName}", () => [exportedType]); - var error = Assert.Throws(() => property.SetValue(null, null)); + Assert.Equal(expected, new AssemblyContextOptions().AssemblyFilter(assembly)); + } + + [Theory] + [InlineData(typeof(string), false)] + [InlineData(typeof(SavvyioOptions), true)] + public void AssemblyFilter_ShouldInspectLoadableTypes_WhenSomeExportedTypesCannotLoad(Type exportedType, bool expected) + { + var assembly = new ExportedTypesAssembly($"Application.PartialLoad.{exportedType.FullName}", () => + throw new ReflectionTypeLoadException([null, exportedType], [new TypeLoadException()])); - Assert.Equal("value", Assert.IsType(error.InnerException).ParamName); - Assert.Same(original, property.GetValue(null)); + Assert.Equal(expected, new AssemblyContextOptions().AssemblyFilter(assembly)); } [Fact] - public void AssemblyDependenciesCallback_ShouldTraverseTransitiveReferences() + public void AssemblyFilter_ShouldAllowAssembly_WhenExportedTypeScanHasRecoverableFailure() + { + var assembly = new ExportedTypesAssembly("Application.RecoverableTypeScanFailure", () => throw new NotSupportedException()); + + Assert.True(new AssemblyContextOptions().AssemblyFilter(assembly)); + } + + [Fact] + public void AssemblyFilter_ShouldAllowAssembly_WhenExportedTypesAreUnavailable() + { + var assembly = new ExportedTypesAssembly("Application.UnavailableExportedTypes", () => null!); + + Assert.True(new AssemblyContextOptions().AssemblyFilter(assembly)); + } + + [Fact] + public void GetCurrentDomainAssemblies_ShouldTraverseTransitiveReferences() { - using var context = new IsolatedAssemblyContext(); var root = typeof(Command).Assembly; - var core = typeof(AssemblyContext).Assembly; - var transitive = typeof(Cuemon.Reflection.AssemblyContext).Assembly; + var core = typeof(SavvyioOptions).Assembly; + var transitive = typeof(AssemblyContext).Assembly; // Commands references Savvyio.Core, which in turn references Cuemon.Core. Assert.DoesNotContain(root.GetReferencedAssemblies(), name => name.FullName == transitive.FullName); - var assemblies = context.Dependencies(root).ToList(); + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => assembly == root; + options.ExcludedAssemblies.Clear(); + }); - Assert.Equal(root, assemblies[0]); - Assert.Contains(assemblies, assembly => assembly.FullName == core.FullName); - Assert.Contains(assemblies, assembly => assembly.FullName == transitive.FullName); + Assert.Same(root, assemblies[0]); + Assert.Contains(core, assemblies); + Assert.Contains(transitive, assemblies); Assert.Equal(assemblies.Count, assemblies.Distinct().Count()); Assert.DoesNotContain(assemblies, assembly => assembly.FullName!.StartsWith("System", StringComparison.Ordinal)); Assert.DoesNotContain(assemblies, assembly => assembly.FullName!.StartsWith("Microsoft", StringComparison.Ordinal)); } [Fact] - public void AssemblyDependenciesCallback_ShouldSkipSelfReferencesAndDuplicates() + public void GetCurrentDomainAssemblies_ShouldSkipSelfReferencesAndDuplicates() { - using var context = new IsolatedAssemblyContext(); var dependency = typeof(Validator).Assembly; - var root = new ReferencedAssembly("Application.Root", new AssemblyName("Application.Root"), dependency.GetName(), dependency.GetName()); - context.ReferenceFilter = name => name.Name is "Application.Root" or "Cuemon.Kernel"; + var rootName = new AssemblyName("Savvyio.Tests.AssemblyContext.SelfReferences") { Version = new Version(1, 0, 0, 0) }; + using var fixture = new ReferencedAssemblyFixture(rootName, rootName, dependency.GetName(), dependency.GetName()); + var root = fixture.Assembly; + var inspected = new List(); + Assert.Equal(3, root.GetReferencedAssemblies().Length); + Assert.Equal(root.FullName, root.GetReferencedAssemblies()[0].FullName); + + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => assembly == root; + options.ReferencedAssemblyFilter = name => + { + inspected.Add(name); + return name.FullName == root.FullName || name.FullName == dependency.FullName; + }; + }); - Assert.Equal(new Assembly[] { root, dependency }, context.Dependencies(root)); + Assert.Equal(new[] { root, dependency }, assemblies); + Assert.Equal(root.GetReferencedAssemblies().Concat(dependency.GetReferencedAssemblies()).Select(name => name.FullName), + inspected.Select(name => name.FullName)); } [Fact] - public void AssemblyDependenciesCallback_ShouldContinueAfterUnresolvableReference() + public void GetCurrentDomainAssemblies_ShouldContinueAfterUnresolvableReference() { - using var context = new IsolatedAssemblyContext(); var dependency = typeof(Validator).Assembly; - var root = new ReferencedAssembly("Application.Root", new AssemblyName("Savvyio.Tests.NonexistentDependency.8d9b24a2"), dependency.GetName()); - context.ReferenceFilter = name => name.Name == "Savvyio.Tests.NonexistentDependency.8d9b24a2" || name.FullName == dependency.FullName; + var missing = new AssemblyName("Savvyio.Tests.NonexistentDependency.8d9b24a2"); + using var fixture = new ReferencedAssemblyFixture(new AssemblyName("Savvyio.Tests.AssemblyContext.MissingReference"), missing, dependency.GetName()); + var root = fixture.Assembly; + + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => assembly == root; + options.ReferencedAssemblyFilter = name => name.Name == missing.Name || name.FullName == dependency.FullName; + }); - Assert.Equal(new Assembly[] { root, dependency }, context.Dependencies(root)); + Assert.Equal(new[] { root, dependency }, assemblies); } [Fact] - public void AssemblyDependenciesCallback_ShouldApplyCustomFilterWithoutFilteringRoot() + public void GetCurrentDomainAssemblies_ShouldApplyReferenceFilterWithoutFilteringRoot() { - using var context = new IsolatedAssemblyContext(); var root = typeof(Command).Assembly; var inspected = new List(); - context.ReferenceFilter = name => - { - inspected.Add(name); - return false; - }; - Assert.Same(root, Assert.Single(context.Dependencies(root))); + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => assembly == root; + options.ReferencedAssemblyFilter = name => + { + inspected.Add(name); + return false; + }; + }); + + Assert.Same(root, Assert.Single(assemblies)); Assert.Equal(root.GetReferencedAssemblies().Select(name => name.FullName), inspected.Select(name => name.FullName)); } [Fact] - public void CurrentDomainAssemblies_ShouldFilterExpandDeduplicateAndExcludeOwnAssembly() + public void GetCurrentDomainAssemblies_ShouldFilterExpandDeduplicateAndExcludeInfrastructureAssemblies() { - using var context = new IsolatedAssemblyContext(); - var root = typeof(AssemblyContextBehaviorTest).Assembly; var dependency = typeof(Command).Assembly; + var savvyioCore = typeof(SavvyioOptions).Assembly; + var cuemonCore = typeof(AssemblyContext).Assembly; + using var fixture = new ReferencedAssemblyFixture(new AssemblyName("Savvyio.Tests.AssemblyContext.SharedReferences"), + dependency.GetName(), dependency.GetName(), savvyioCore.GetName(), cuemonCore.GetName()); + var root = fixture.Assembly; var expanded = new List(); - context.AssemblyFilter = assembly => assembly == root; - context.Dependencies = assembly => - { - expanded.Add(assembly); - return [assembly, dependency, dependency, context.Type.Assembly]; - }; - var assemblies = context.CurrentDomainAssemblies; - - Assert.Same(root, Assert.Single(expanded)); - Assert.Equal(new[] { root, dependency }, assemblies); + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => + { + if (assembly != root && assembly != dependency) { return false; } + expanded.Add(assembly); + return true; + }; + options.ReferencedAssemblyFilter = name => name.FullName == dependency.FullName || + name.FullName == savvyioCore.FullName || name.FullName == cuemonCore.FullName; + options.ExcludedAssemblies.Add(savvyioCore); + }); + + Assert.Equal(2, expanded.Count); + Assert.Contains(root, expanded); + Assert.Contains(dependency, expanded); + Assert.Equal(2, assemblies.Count); + Assert.Contains(root, assemblies); + Assert.Contains(dependency, assemblies); + Assert.DoesNotContain(savvyioCore, assemblies); + Assert.DoesNotContain(cuemonCore, assemblies); Assert.Throws(() => ((IList)assemblies).Add(root)); } [Fact] - public void CurrentDomainAssemblies_ShouldCacheFirstResultDespiteCallbackChanges() + public void GetCurrentDomainAssemblies_ShouldSkipTraversal_WhenReferencesAreDisabled() { - using var context = new IsolatedAssemblyContext(); - var root = typeof(AssemblyContextBehaviorTest).Assembly; - context.AssemblyFilter = assembly => assembly == root; - context.Dependencies = assembly => [assembly]; - var first = context.CurrentDomainAssemblies; + var root = typeof(Command).Assembly; - context.AssemblyFilter = _ => throw new InvalidOperationException("Must not rescan."); - context.Dependencies = _ => throw new InvalidOperationException("Must not traverse again."); - context.ReferenceFilter = _ => throw new InvalidOperationException("Must not refilter."); + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => assembly == root; + options.IncludeReferencedAssemblies = false; + options.ReferencedAssemblyFilter = _ => throw new InvalidOperationException("Must not traverse references."); + }); - Assert.Same(first, context.CurrentDomainAssemblies); - Assert.Same(root, Assert.Single(first)); + Assert.Same(root, Assert.Single(assemblies)); } [Fact] - public void CurrentDomainAssemblies_ShouldAllowEmptyResult() + public void GetCurrentDomainAssemblies_ShouldUseFreshOptionsAndResultsForEachCall() { - using var context = new IsolatedAssemblyContext(); - context.AssemblyFilter = _ => false; - context.Dependencies = _ => throw new InvalidOperationException("No assembly passed the filter."); + var firstRoot = typeof(Command).Assembly; + var secondRoot = typeof(SavvyioOptions).Assembly; + var first = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => assembly == firstRoot; + options.IncludeReferencedAssemblies = false; + options.ExcludedAssemblies.Add(secondRoot); + }); + + var second = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => assembly == secondRoot; + options.ReferencedAssemblyFilter = _ => false; + }); - Assert.Empty(context.CurrentDomainAssemblies); + Assert.NotSame(first, second); + Assert.Same(firstRoot, Assert.Single(first)); + Assert.Same(secondRoot, Assert.Single(second)); } [Fact] - public void CurrentDomainAssemblies_ShouldCacheCallbackFailure() + public void GetCurrentDomainAssemblies_ShouldAllowEmptyResult() { - using var context = new IsolatedAssemblyContext(); + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = _ => false; + options.ReferencedAssemblyFilter = _ => throw new InvalidOperationException("No assembly passed the filter."); + }); + + Assert.Empty(assemblies); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void GetCurrentDomainAssemblies_ShouldPropagateFilterFailureWithoutCachingIt(bool failReferenceFilter) + { + var root = typeof(Command).Assembly; var expected = new InvalidOperationException("Assembly filter failed."); - context.AssemblyFilter = _ => throw expected; - var first = Assert.Throws(() => context.CurrentDomainAssemblies); - context.AssemblyFilter = _ => false; - var second = Assert.Throws(() => context.CurrentDomainAssemblies); + var error = Assert.Throws(() => AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => failReferenceFilter ? assembly == root : throw expected; + options.ReferencedAssemblyFilter = _ => throw expected; + })); + var next = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => assembly == root; + options.IncludeReferencedAssemblies = false; + }); - Assert.Same(expected, first.InnerException); - Assert.Same(expected, second.InnerException); + Assert.Same(expected, error); + Assert.Same(root, Assert.Single(next)); } - private sealed class ReferencedAssembly(string? fullName, params AssemblyName[] references) : Assembly + private sealed class ExportedTypesAssembly(string? fullName, Func? exportedTypes = null) : Assembly { public override string? FullName => fullName; - public override AssemblyName[] GetReferencedAssemblies() => references; + public override Type[] GetExportedTypes() => exportedTypes is null ? [typeof(SavvyioOptions)] : exportedTypes(); } - // A separate load context gives each test fresh static callbacks and Lazy state. - // This avoids resetting private fields or changing the application's global scan. - private sealed class IsolatedAssemblyContext : IDisposable + // Real in-memory metadata lets the public scanner traverse self, duplicate and missing + // references without invoking private helpers or altering global configuration. + private sealed class ReferencedAssemblyFixture : IDisposable { private readonly AssemblyLoadContext _loadContext = new(null, isCollectible: true); - public IsolatedAssemblyContext() - { - Type = _loadContext.LoadFromAssemblyPath(typeof(AssemblyContext).Assembly.Location) - .GetType(typeof(AssemblyContext).FullName!, throwOnError: true)!; - } - - public Type Type { get; } - - public Func AssemblyFilter - { - get => Get>(nameof(AssemblyContext.AssemblyFilterCallback)); - set => Set(nameof(AssemblyContext.AssemblyFilterCallback), value); - } - - public Func> Dependencies + public ReferencedAssemblyFixture(AssemblyName name, params AssemblyName[] references) { - get => Get>>(nameof(AssemblyContext.AssemblyDependenciesCallback)); - set => Set(nameof(AssemblyContext.AssemblyDependenciesCallback), value); + var metadata = new MetadataBuilder(); + metadata.AddModule(0, metadata.GetOrAddString(name.Name!), default, default, default); + metadata.AddAssembly(metadata.GetOrAddString(name.Name!), name.Version ?? new Version(1, 0, 0, 0), + default, default, default, default); + metadata.AddTypeDefinition(TypeAttributes.NotPublic, default, metadata.GetOrAddString(""), + default, MetadataTokens.FieldDefinitionHandle(1), MetadataTokens.MethodDefinitionHandle(1)); + foreach (var reference in references) + { + metadata.AddAssemblyReference(metadata.GetOrAddString(reference.Name!), reference.Version ?? new Version(1, 0, 0, 0), + metadata.GetOrAddString(reference.CultureName ?? ""), metadata.GetOrAddBlob(reference.GetPublicKeyToken() ?? []), + default, default); + } + var image = new BlobBuilder(); + new ManagedPEBuilder(new PEHeaderBuilder(imageCharacteristics: Characteristics.Dll), + new MetadataRootBuilder(metadata), new BlobBuilder()).Serialize(image); + using var stream = new MemoryStream(image.ToArray()); + Assembly = _loadContext.LoadFromStream(stream); } - public Func ReferenceFilter - { - get => Get>(nameof(AssemblyContext.AssemblyDependenciesFilterCallback)); - set => Set(nameof(AssemblyContext.AssemblyDependenciesFilterCallback), value); - } - - public IReadOnlyList CurrentDomainAssemblies => Get>(nameof(AssemblyContext.CurrentDomainAssemblies)); - - private T Get(string name) => (T)Type.GetProperty(name)!.GetValue(null)!; - - private void Set(string name, object value) => Type.GetProperty(name)!.SetValue(null, value); + public Assembly Assembly { get; } public void Dispose() => _loadContext.Unload(); } diff --git a/test/Savvyio.Core.Tests/Reflection/AssemblyContextTest.cs b/test/Savvyio.Core.Tests/Reflection/AssemblyContextTest.cs index 48f23786..2a5e09db 100644 --- a/test/Savvyio.Core.Tests/Reflection/AssemblyContextTest.cs +++ b/test/Savvyio.Core.Tests/Reflection/AssemblyContextTest.cs @@ -1,185 +1,177 @@ +#nullable enable using System; -using System.Linq; using System.Reflection; using Codebelt.Extensions.Xunit; -using Savvyio.Reflection; +using Savvyio; using Xunit; -namespace Savvyio.Reflection +namespace Cuemon.Reflection; + +public class AssemblyContextTest : Test { - public class AssemblyContextTest : Test + public AssemblyContextTest(ITestOutputHelper output) : base(output) { - public AssemblyContextTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void CurrentDomainAssemblies_ShouldReturnNonEmptyList() - { - var assemblies = AssemblyContext.CurrentDomainAssemblies; + [Fact] + public void GetCurrentDomainAssemblies_ShouldReturnNonEmptyList() + { + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(); - Assert.NotNull(assemblies); - Assert.NotEmpty(assemblies); - } + Assert.NotNull(assemblies); + Assert.NotEmpty(assemblies); + Assert.Contains(typeof(AssemblyContextTest).Assembly, assemblies); + } - [Fact] - public void CurrentDomainAssemblies_ShouldNotContainSavvyioCoreAssembly() - { - var savvyioCoreAssembly = typeof(AssemblyContext).Assembly; + [Fact] + public void GetCurrentDomainAssemblies_ShouldExcludeCuemonCoreByDefault() + { + Assert.DoesNotContain(typeof(AssemblyContext).Assembly, AssemblyContext.GetCurrentDomainAssemblies()); + } - Assert.DoesNotContain(savvyioCoreAssembly, AssemblyContext.CurrentDomainAssemblies); - } + [Fact] + public void GetCurrentDomainAssemblies_ShouldExcludeSavvyioCore_WhenConfigured() + { + var savvyioCore = typeof(SavvyioOptions).Assembly; - [Fact] - public void CurrentDomainAssemblies_ShouldNotContainSystemOrMicrosoftAssemblies() + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(options => { - foreach (var assembly in AssemblyContext.CurrentDomainAssemblies) - { - Assert.False(assembly.FullName?.StartsWith(nameof(System), StringComparison.Ordinal), - $"Assembly '{assembly.FullName}' should have been filtered out."); - Assert.False(assembly.FullName?.StartsWith(nameof(Microsoft), StringComparison.Ordinal), - $"Assembly '{assembly.FullName}' should have been filtered out."); - } - } - - [Fact] - public void AssemblyFilterCallback_ShouldReturnDefaultNonNullDelegate() - { - var callback = AssemblyContext.AssemblyFilterCallback; + options.AssemblyFilter = assembly => assembly == savvyioCore; + options.ExcludedAssemblies.Add(savvyioCore); + }); - Assert.NotNull(callback); - } + Assert.DoesNotContain(savvyioCore, assemblies); + Assert.DoesNotContain(typeof(AssemblyContext).Assembly, assemblies); + } - [Fact] - public void AssemblyFilterCallback_ShouldAcceptCustomDelegate() - { - var original = AssemblyContext.AssemblyFilterCallback; - try - { - Func custom = _ => true; - AssemblyContext.AssemblyFilterCallback = custom; - - Assert.Same(custom, AssemblyContext.AssemblyFilterCallback); - } - finally - { - AssemblyContext.AssemblyFilterCallback = original; - } - } - - [Fact] - public void AssemblyFilterCallback_ShouldThrowArgumentNullException_WhenAssignedNull() + [Fact] + public void GetCurrentDomainAssemblies_ShouldNotContainSystemOrMicrosoftAssemblies() + { + Assert.All(AssemblyContext.GetCurrentDomainAssemblies(), assembly => { - Assert.Throws(() => AssemblyContext.AssemblyFilterCallback = null); - } + Assert.False(assembly.FullName?.StartsWith(nameof(System), StringComparison.Ordinal), + $"Assembly '{assembly.FullName}' should have been filtered out."); + Assert.False(assembly.FullName?.StartsWith(nameof(Microsoft), StringComparison.Ordinal), + $"Assembly '{assembly.FullName}' should have been filtered out."); + }); + } - [Fact] - public void AssemblyDependenciesCallback_ShouldReturnDefaultNonNullDelegate() - { - var callback = AssemblyContext.AssemblyDependenciesCallback; + [Fact] + public void AssemblyContextOptions_ShouldHaveValidDefaults() + { + var options = new AssemblyContextOptions(); - Assert.NotNull(callback); - } + Assert.True(options.IncludeReferencedAssemblies); + Assert.NotNull(options.AssemblyFilter); + Assert.NotNull(options.ReferencedAssemblyFilter); + Assert.Equal(new[] { typeof(AssemblyContext).Assembly }, options.ExcludedAssemblies); + options.ValidateOptions(); + } - [Fact] - public void AssemblyDependenciesCallback_ShouldAcceptCustomDelegate() - { - var original = AssemblyContext.AssemblyDependenciesCallback; - try - { - Func> custom = a => Enumerable.Repeat(a, 1); - AssemblyContext.AssemblyDependenciesCallback = custom; - - Assert.Same(custom, AssemblyContext.AssemblyDependenciesCallback); - } - finally - { - AssemblyContext.AssemblyDependenciesCallback = original; - } - } - - [Fact] - public void AssemblyDependenciesCallback_ShouldThrowArgumentNullException_WhenAssignedNull() - { - Assert.Throws(() => AssemblyContext.AssemblyDependenciesCallback = null); - } + [Fact] + public void AssemblyFilter_ShouldAcceptCustomDelegate() + { + var options = new AssemblyContextOptions(); + Func custom = _ => true; - [Fact] - public void AssemblyDependenciesFilterCallback_ShouldReturnDefaultNonNullDelegate() - { - var callback = AssemblyContext.AssemblyDependenciesFilterCallback; + options.AssemblyFilter = custom; - Assert.NotNull(callback); - } + Assert.Same(custom, options.AssemblyFilter); + options.ValidateOptions(); + } - [Fact] - public void AssemblyDependenciesFilterCallback_ShouldAcceptCustomDelegate() - { - var original = AssemblyContext.AssemblyDependenciesFilterCallback; - try - { - Func custom = _ => true; - AssemblyContext.AssemblyDependenciesFilterCallback = custom; - - Assert.Same(custom, AssemblyContext.AssemblyDependenciesFilterCallback); - } - finally - { - AssemblyContext.AssemblyDependenciesFilterCallback = original; - } - } - - [Fact] - public void AssemblyDependenciesFilterCallback_ShouldThrowArgumentNullException_WhenAssignedNull() - { - Assert.Throws(() => AssemblyContext.AssemblyDependenciesFilterCallback = null); - } + [Fact] + public void ReferencedAssemblyFilter_ShouldAcceptCustomDelegate() + { + var options = new AssemblyContextOptions(); + Func custom = _ => true; - [Fact] - public void AssemblyFilterCallback_DefaultFilter_ShouldIncludeNonSystemAssembly() - { - var callback = AssemblyContext.AssemblyFilterCallback; - var savvyioAssembly = typeof(AssemblyContext).Assembly; + options.ReferencedAssemblyFilter = custom; - Assert.True(callback(savvyioAssembly)); - } + Assert.Same(custom, options.ReferencedAssemblyFilter); + options.ValidateOptions(); + } - [Fact] - public void AssemblyFilterCallback_DefaultFilter_ShouldExcludeSystemAssembly() - { - var callback = AssemblyContext.AssemblyFilterCallback; - var systemAssembly = typeof(string).Assembly; + [Fact] + public void AssemblyContextOptions_ShouldKeepConfigurationLocalToEachInstance() + { + var first = new AssemblyContextOptions + { + AssemblyFilter = _ => false, + ReferencedAssemblyFilter = _ => false, + IncludeReferencedAssemblies = false + }; + first.ExcludedAssemblies.Clear(); + var second = new AssemblyContextOptions(); + + Assert.NotSame(first.AssemblyFilter, second.AssemblyFilter); + Assert.NotSame(first.ReferencedAssemblyFilter, second.ReferencedAssemblyFilter); + Assert.True(second.IncludeReferencedAssemblies); + Assert.Equal(new[] { typeof(AssemblyContext).Assembly }, second.ExcludedAssemblies); + } - Assert.False(callback(systemAssembly)); - } + [Theory] + [InlineData(nameof(AssemblyContextOptions.AssemblyFilter))] + [InlineData(nameof(AssemblyContextOptions.ReferencedAssemblyFilter))] + [InlineData(nameof(AssemblyContextOptions.ExcludedAssemblies))] + public void ValidateOptions_ShouldRejectNullConfiguration(string propertyName) + { + var options = new AssemblyContextOptions(); + typeof(AssemblyContextOptions).GetProperty(propertyName)!.SetValue(options, null); - [Fact] - public void AssemblyDependenciesFilterCallback_DefaultFilter_ShouldIncludeNonSystemAssemblyName() - { - var callback = AssemblyContext.AssemblyDependenciesFilterCallback; - var assemblyName = typeof(AssemblyContext).Assembly.GetName(); + var error = Assert.Throws(options.ValidateOptions); - Assert.True(callback(assemblyName)); - } + Assert.Contains(propertyName, error.Message); + } - [Fact] - public void AssemblyDependenciesFilterCallback_DefaultFilter_ShouldExcludeSystemAssemblyName() - { - var callback = AssemblyContext.AssemblyDependenciesFilterCallback; - var assemblyName = typeof(string).Assembly.GetName(); + [Theory] + [InlineData(nameof(AssemblyContextOptions.AssemblyFilter))] + [InlineData(nameof(AssemblyContextOptions.ReferencedAssemblyFilter))] + [InlineData(nameof(AssemblyContextOptions.ExcludedAssemblies))] + public void GetCurrentDomainAssemblies_ShouldRejectInvalidConfiguration(string propertyName) + { + var error = Assert.Throws(() => AssemblyContext.GetCurrentDomainAssemblies(options => + typeof(AssemblyContextOptions).GetProperty(propertyName)!.SetValue(options, null))); - Assert.False(callback(assemblyName)); - } + Assert.Equal("setup", error.ParamName); + Assert.Contains(propertyName, Assert.IsType(error.InnerException).Message); + } - [Fact] - public void AssemblyDependenciesCallback_DefaultCallback_ShouldYieldAtLeastTheInputAssembly() - { - var callback = AssemblyContext.AssemblyDependenciesCallback; - var assembly = typeof(AssemblyContext).Assembly; + [Fact] + public void AssemblyFilter_ShouldIncludeSavvyioAssembly() + { + Assert.True(new AssemblyContextOptions().AssemblyFilter(typeof(SavvyioOptions).Assembly)); + } + + [Fact] + public void AssemblyFilter_ShouldExcludeSystemAssembly() + { + Assert.False(new AssemblyContextOptions().AssemblyFilter(typeof(string).Assembly)); + } + + [Fact] + public void ReferencedAssemblyFilter_ShouldIncludeSavvyioAssemblyName() + { + Assert.True(new AssemblyContextOptions().ReferencedAssemblyFilter(typeof(SavvyioOptions).Assembly.GetName())); + } - var result = callback(assembly).ToList(); + [Fact] + public void ReferencedAssemblyFilter_ShouldExcludeSystemAssemblyName() + { + Assert.False(new AssemblyContextOptions().ReferencedAssemblyFilter(typeof(string).Assembly.GetName())); + } + + [Fact] + public void GetCurrentDomainAssemblies_ShouldIncludeRoot_WhenAllReferencesAreFilteredOut() + { + var root = typeof(SavvyioOptions).Assembly; + + var assemblies = AssemblyContext.GetCurrentDomainAssemblies(options => + { + options.AssemblyFilter = assembly => assembly == root; + options.ReferencedAssemblyFilter = _ => false; + }); - Assert.Contains(assembly, result); - } + Assert.Same(root, Assert.Single(assemblies)); } } diff --git a/test/Savvyio.Core.Tests/Savvyio.Core.Tests.csproj b/test/Savvyio.Core.Tests/Savvyio.Core.Tests.csproj index c12e8521..80b0275a 100644 --- a/test/Savvyio.Core.Tests/Savvyio.Core.Tests.csproj +++ b/test/Savvyio.Core.Tests/Savvyio.Core.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio diff --git a/test/Savvyio.Core.Tests/SavvyioOptionsExtensionsTest.cs b/test/Savvyio.Core.Tests/SavvyioOptionsExtensionsTest.cs index 4a361b9d..7b50e220 100644 --- a/test/Savvyio.Core.Tests/SavvyioOptionsExtensionsTest.cs +++ b/test/Savvyio.Core.Tests/SavvyioOptionsExtensionsTest.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Codebelt.Extensions.Xunit; using Savvyio.Assets; using Savvyio.Assets.Domain; @@ -10,112 +10,111 @@ using Savvyio.Queries; using Xunit; -namespace Savvyio +namespace Savvyio; + +public class SavvyioOptionsExtensionsTest : Test { - public class SavvyioOptionsExtensionsTest : Test + public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) { - public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddDispatchers_ShouldNotAddAnyDispatchersFromCurrentDomain() - { - var sut = new SavvyioOptions().AddDispatchers(); - Assert.Empty(sut.DispatcherServiceTypes); - Assert.Empty(sut.DispatcherImplementationTypes); - } + [Fact] + public void AddDispatchers_ShouldNotAddAnyDispatchersFromCurrentDomain() + { + var sut = new SavvyioOptions().AddDispatchers(); + Assert.Empty(sut.DispatcherServiceTypes); + Assert.Empty(sut.DispatcherImplementationTypes); + } - [Fact] - public void AddDispatchers_ShouldAddDispatchersFromSpecifiedAssemblies() - { - var sut = new SavvyioOptions().AddDispatchers(typeof(Command).Assembly, typeof(DomainEvent).Assembly, typeof(IntegrationEvent).Assembly, typeof(Query<>).Assembly); + [Fact] + public void AddDispatchers_ShouldAddDispatchersFromSpecifiedAssemblies() + { + var sut = new SavvyioOptions().AddDispatchers(typeof(Command).Assembly, typeof(DomainEvent).Assembly, typeof(IntegrationEvent).Assembly, typeof(Query<>).Assembly); - Assert.NotEmpty(sut.DispatcherServiceTypes); - Assert.NotEmpty(sut.DispatcherImplementationTypes); - Assert.Collection(sut.DispatcherServiceTypes, - type => Assert.Equal(typeof(ICommandDispatcher), type), - type => Assert.Equal(typeof(IDomainEventDispatcher), type), - type => Assert.Equal(typeof(IIntegrationEventDispatcher), type), - type => Assert.Equal(typeof(IQueryDispatcher), type)); - Assert.Collection(sut.DispatcherImplementationTypes, - type => Assert.Equal(typeof(CommandDispatcher), type), - type => Assert.Equal(typeof(DomainEventDispatcher), type), - type => Assert.Equal(typeof(IntegrationEventDispatcher), type), - type => Assert.Equal(typeof(QueryDispatcher), type)); - } + Assert.NotEmpty(sut.DispatcherServiceTypes); + Assert.NotEmpty(sut.DispatcherImplementationTypes); + Assert.Collection(sut.DispatcherServiceTypes, + type => Assert.Equal(typeof(ICommandDispatcher), type), + type => Assert.Equal(typeof(IDomainEventDispatcher), type), + type => Assert.Equal(typeof(IIntegrationEventDispatcher), type), + type => Assert.Equal(typeof(IQueryDispatcher), type)); + Assert.Collection(sut.DispatcherImplementationTypes, + type => Assert.Equal(typeof(CommandDispatcher), type), + type => Assert.Equal(typeof(DomainEventDispatcher), type), + type => Assert.Equal(typeof(IntegrationEventDispatcher), type), + type => Assert.Equal(typeof(QueryDispatcher), type)); + } - [Fact] - public void AddDispatchers_ShouldAutomaticallyAddDispatchersFromReferencedAssemblies() - { - var sut = new SavvyioOptions().UseAutomaticDispatcherDiscovery(true); + [Fact] + public void AddDispatchers_ShouldAutomaticallyAddDispatchersFromReferencedAssemblies() + { + var sut = new SavvyioOptions().UseAutomaticDispatcherDiscovery(true); - Assert.NotEmpty(sut.DispatcherServiceTypes); - Assert.NotEmpty(sut.DispatcherImplementationTypes); - Assert.Collection(sut.DispatcherServiceTypes.OrderBy(type => type.Name), - type => Assert.Equal(typeof(ICommandDispatcher), type), - type => Assert.Equal(typeof(IDomainEventDispatcher), type), - type => Assert.Equal(typeof(IIntegrationEventDispatcher), type), - type => Assert.Equal(typeof(IMediator), type), - type => Assert.Equal(typeof(IQueryDispatcher), type)); - Assert.Collection(sut.DispatcherImplementationTypes.OrderBy(type => type.Name), - type => Assert.Equal(typeof(CommandDispatcher), type), - type => Assert.Equal(typeof(DomainEventDispatcher), type), - type => Assert.Equal(typeof(IntegrationEventDispatcher), type), - type => Assert.Equal(typeof(Mediator), type), - type => Assert.Equal(typeof(QueryDispatcher), type)); - } + Assert.NotEmpty(sut.DispatcherServiceTypes); + Assert.NotEmpty(sut.DispatcherImplementationTypes); + Assert.Collection(sut.DispatcherServiceTypes.OrderBy(type => type.Name), + type => Assert.Equal(typeof(ICommandDispatcher), type), + type => Assert.Equal(typeof(IDomainEventDispatcher), type), + type => Assert.Equal(typeof(IIntegrationEventDispatcher), type), + type => Assert.Equal(typeof(IMediator), type), + type => Assert.Equal(typeof(IQueryDispatcher), type)); + Assert.Collection(sut.DispatcherImplementationTypes.OrderBy(type => type.Name), + type => Assert.Equal(typeof(CommandDispatcher), type), + type => Assert.Equal(typeof(DomainEventDispatcher), type), + type => Assert.Equal(typeof(IntegrationEventDispatcher), type), + type => Assert.Equal(typeof(Mediator), type), + type => Assert.Equal(typeof(QueryDispatcher), type)); + } - [Fact] - public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() - { - var sut = new SavvyioOptions().AddHandlers(); - Assert.Empty(sut.HandlerServiceTypes); - Assert.Empty(sut.HandlerImplementationTypes); - } + [Fact] + public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() + { + var sut = new SavvyioOptions().AddHandlers(); + Assert.Empty(sut.HandlerServiceTypes); + Assert.Empty(sut.HandlerImplementationTypes); + } - [Fact] - public void AddHandlers_ShouldAddHandlersFromSpecifiedAssemblies() - { - var sut = new SavvyioOptions().AddHandlers(typeof(ICommand).Assembly, typeof(IDomainEvent).Assembly, typeof(IIntegrationEvent).Assembly, typeof(IQuery).Assembly, typeof(Account).Assembly); + [Fact] + public void AddHandlers_ShouldAddHandlersFromSpecifiedAssemblies() + { + var sut = new SavvyioOptions().AddHandlers(typeof(ICommand).Assembly, typeof(IDomainEvent).Assembly, typeof(IIntegrationEvent).Assembly, typeof(IQuery).Assembly, typeof(Account).Assembly); - Assert.NotEmpty(sut.HandlerServiceTypes); - Assert.NotEmpty(sut.HandlerImplementationTypes); - Assert.Collection(sut.HandlerServiceTypes, - type => Assert.Equal(typeof(ICommandHandler), type), - type => Assert.Equal(typeof(IDomainEventHandler), type), - type => Assert.Equal(typeof(IIntegrationEventHandler), type), - type => Assert.Equal(typeof(IQueryHandler), type)); - Assert.Collection(sut.HandlerImplementationTypes, - type => Assert.Equal(typeof(AccountCommandHandler), type), - type => Assert.Equal(typeof(PlatformProviderHandler), type), - type => Assert.Equal(typeof(AccountDomainEventHandler), type), - type => Assert.Equal(typeof(PlatformProviderDomainEventHandler), type), - type => Assert.Equal(typeof(AccountEventHandler), type), - type => Assert.Equal(typeof(AccountQueryHandler), type)); - } + Assert.NotEmpty(sut.HandlerServiceTypes); + Assert.NotEmpty(sut.HandlerImplementationTypes); + Assert.Collection(sut.HandlerServiceTypes, + type => Assert.Equal(typeof(ICommandHandler), type), + type => Assert.Equal(typeof(IDomainEventHandler), type), + type => Assert.Equal(typeof(IIntegrationEventHandler), type), + type => Assert.Equal(typeof(IQueryHandler), type)); + Assert.Collection(sut.HandlerImplementationTypes, + type => Assert.Equal(typeof(AccountCommandHandler), type), + type => Assert.Equal(typeof(PlatformProviderHandler), type), + type => Assert.Equal(typeof(AccountDomainEventHandler), type), + type => Assert.Equal(typeof(PlatformProviderDomainEventHandler), type), + type => Assert.Equal(typeof(AccountEventHandler), type), + type => Assert.Equal(typeof(AccountQueryHandler), type)); + } - [Fact] - public void AddHandlers_ShouldAddHandlersAutomaticallyFromReferencedAssemblies() - { - var sut = new SavvyioOptions().UseAutomaticHandlerDiscovery(true); + [Fact] + public void AddHandlers_ShouldAddHandlersAutomaticallyFromReferencedAssemblies() + { + var sut = new SavvyioOptions().UseAutomaticHandlerDiscovery(true); - Assert.NotEmpty(sut.HandlerServiceTypes); - Assert.NotEmpty(sut.HandlerImplementationTypes); - Assert.Collection(sut.HandlerServiceTypes.OrderBy(type => type.Name), - type => Assert.Equal(typeof(ICommandHandler), type), - type => Assert.Equal(typeof(IDomainEventHandler), type), - type => Assert.Equal(typeof(IIntegrationEventHandler), type), - type => Assert.Equal(typeof(IQueryHandler), type)); - Assert.Collection(sut.HandlerImplementationTypes.OrderBy(type => type.Name), - type => Assert.Equal(typeof(AccountCommandHandler), type), - type => Assert.Equal(typeof(AccountDomainEventHandler), type), - type => Assert.Equal(typeof(AccountEventHandler), type), - type => Assert.Equal(typeof(AccountHandlers), type), - type => Assert.Equal(typeof(AccountQueryHandler), type), - type => Assert.Equal(typeof(PlatformProviderDomainEventHandler), type), - type => Assert.Equal(typeof(PlatformProviderHandler), type) - ); - } + Assert.NotEmpty(sut.HandlerServiceTypes); + Assert.NotEmpty(sut.HandlerImplementationTypes); + Assert.Collection(sut.HandlerServiceTypes.OrderBy(type => type.Name), + type => Assert.Equal(typeof(ICommandHandler), type), + type => Assert.Equal(typeof(IDomainEventHandler), type), + type => Assert.Equal(typeof(IIntegrationEventHandler), type), + type => Assert.Equal(typeof(IQueryHandler), type)); + Assert.Collection(sut.HandlerImplementationTypes.OrderBy(type => type.Name), + type => Assert.Equal(typeof(AccountCommandHandler), type), + type => Assert.Equal(typeof(AccountDomainEventHandler), type), + type => Assert.Equal(typeof(AccountEventHandler), type), + type => Assert.Equal(typeof(AccountHandlers), type), + type => Assert.Equal(typeof(AccountQueryHandler), type), + type => Assert.Equal(typeof(PlatformProviderDomainEventHandler), type), + type => Assert.Equal(typeof(PlatformProviderHandler), type) + ); } } diff --git a/test/Savvyio.Core.Tests/SavvyioOptionsTest.cs b/test/Savvyio.Core.Tests/SavvyioOptionsTest.cs index 1d46421b..9bfa4cad 100644 --- a/test/Savvyio.Core.Tests/SavvyioOptionsTest.cs +++ b/test/Savvyio.Core.Tests/SavvyioOptionsTest.cs @@ -1,26 +1,25 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio +namespace Savvyio; + +public class SavvyioOptionsTest : Test { - public class SavvyioOptionsTest : Test + public SavvyioOptionsTest(ITestOutputHelper output) : base(output) { - public SavvyioOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void SavvyioOptions_Ensure_Initialization_Defaults() - { - var sut = new SavvyioOptions(); + [Fact] + public void SavvyioOptions_Ensure_Initialization_Defaults() + { + var sut = new SavvyioOptions(); - Assert.False(sut.AllowDispatcherDiscovery); - Assert.False(sut.AllowHandlerDiscovery); - Assert.False(sut.IncludeHandlerServicesDescriptor); - Assert.Empty(sut.DispatcherImplementationTypes); - Assert.Empty(sut.DispatcherServiceTypes); - Assert.Empty(sut.HandlerImplementationTypes); - Assert.Empty(sut.HandlerServiceTypes); - } + Assert.False(sut.AllowDispatcherDiscovery); + Assert.False(sut.AllowHandlerDiscovery); + Assert.False(sut.IncludeHandlerServicesDescriptor); + Assert.Empty(sut.DispatcherImplementationTypes); + Assert.Empty(sut.DispatcherServiceTypes); + Assert.Empty(sut.HandlerImplementationTypes); + Assert.Empty(sut.HandlerServiceTypes); } } diff --git a/test/Savvyio.Domain.EventSourcing.Tests/EfCoreModelBuilderExtensionsTest.cs b/test/Savvyio.Domain.EventSourcing.Tests/EfCoreModelBuilderExtensionsTest.cs index 4b517ff7..b169bae6 100644 --- a/test/Savvyio.Domain.EventSourcing.Tests/EfCoreModelBuilderExtensionsTest.cs +++ b/test/Savvyio.Domain.EventSourcing.Tests/EfCoreModelBuilderExtensionsTest.cs @@ -6,39 +6,38 @@ using Savvyio.Extensions.EFCore; using Xunit; -namespace Savvyio.Extensions.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.EFCore.Domain.EventSourcing; + +public class EfCoreModelBuilderExtensionsTest : Test { - public class EfCoreModelBuilderExtensionsTest : Test + public EfCoreModelBuilderExtensionsTest(ITestOutputHelper output) : base(output) { - public EfCoreModelBuilderExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ModelBuilderExtensions_ShouldApplyConfiguredEventSourcingSchema() + [Fact] + public void ModelBuilderExtensions_ShouldApplyConfiguredEventSourcingSchema() + { + var source = new EfCoreDataSource(new EfCoreDataSourceOptions { - var source = new EfCoreDataSource(new EfCoreDataSourceOptions + ContextConfigurator = b => b.UseInMemoryDatabase("event-sourcing-" + Guid.NewGuid()), + ModelConstructor = mb => mb.AddEventSourcing(o => { - ContextConfigurator = b => b.UseInMemoryDatabase("event-sourcing-" + Guid.NewGuid()), - ModelConstructor = mb => mb.AddEventSourcing(o => - { - o.TableName = "TraceEvents"; - o.CompositePrimaryKeyIdColumnName = "aggregate_id"; - o.CompositePrimaryKeyVersionColumnName = "aggregate_version"; - o.TimestampColumnName = "recorded_at"; - o.TypeColumnName = "aggregate_type"; - o.PayloadColumnName = "body"; - }) - }); + o.TableName = "TraceEvents"; + o.CompositePrimaryKeyIdColumnName = "aggregate_id"; + o.CompositePrimaryKeyVersionColumnName = "aggregate_version"; + o.TimestampColumnName = "recorded_at"; + o.TypeColumnName = "aggregate_type"; + o.PayloadColumnName = "body"; + }) + }); - var schema = source.DbContext.Model.ToDebugString(MetadataDebugStringOptions.LongDefault); + var schema = source.DbContext.Model.ToDebugString(MetadataDebugStringOptions.LongDefault); - Assert.Contains("Relational:TableName: TraceEvents", schema); - Assert.Contains("Relational:ColumnName: aggregate_id", schema); - Assert.Contains("Relational:ColumnName: aggregate_version", schema); - Assert.Contains("Relational:ColumnName: recorded_at", schema); - Assert.Contains("Relational:ColumnName: aggregate_type", schema); - Assert.Contains("Relational:ColumnName: body", schema); - } + Assert.Contains("Relational:TableName: TraceEvents", schema); + Assert.Contains("Relational:ColumnName: aggregate_id", schema); + Assert.Contains("Relational:ColumnName: aggregate_version", schema); + Assert.Contains("Relational:ColumnName: recorded_at", schema); + Assert.Contains("Relational:ColumnName: aggregate_type", schema); + Assert.Contains("Relational:ColumnName: body", schema); } } diff --git a/test/Savvyio.Domain.EventSourcing.Tests/EfCoreTracedAggregateEntityOptionsTest.cs b/test/Savvyio.Domain.EventSourcing.Tests/EfCoreTracedAggregateEntityOptionsTest.cs index 891a165f..7db7bb12 100644 --- a/test/Savvyio.Domain.EventSourcing.Tests/EfCoreTracedAggregateEntityOptionsTest.cs +++ b/test/Savvyio.Domain.EventSourcing.Tests/EfCoreTracedAggregateEntityOptionsTest.cs @@ -1,61 +1,60 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.EFCore.Domain.EventSourcing; + +public class EfCoreTracedAggregateEntityOptionsTest : Test { - public class EfCoreTracedAggregateEntityOptionsTest : Test + public EfCoreTracedAggregateEntityOptionsTest(ITestOutputHelper output) : base(output) { - public EfCoreTracedAggregateEntityOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void EfCoreTracedAggregateEntityOptions_Ensure_Initialization_Defaults() - { - var sut = new EfCoreTracedAggregateEntityOptions(); + [Fact] + public void EfCoreTracedAggregateEntityOptions_Ensure_Initialization_Defaults() + { + var sut = new EfCoreTracedAggregateEntityOptions(); - Assert.Equal("DomainEvents", sut.TableName); - Assert.Equal("id", sut.CompositePrimaryKeyIdColumnName); - Assert.Equal("uniqueidentifier", sut.CompositePrimaryKeyIdColumnType); - Assert.Equal("version", sut.CompositePrimaryKeyVersionColumnName); - Assert.Equal("int", sut.CompositePrimaryKeyVersionColumnType); - Assert.Equal("timestamp", sut.TimestampColumnName); - Assert.Equal("datetime", sut.TimestampColumnType); - Assert.Equal("clrtype", sut.TypeColumnName); - Assert.Equal("varchar(1024)", sut.TypeColumnType); - Assert.Equal("payload", sut.PayloadColumnName); - Assert.Equal("varchar(max)", sut.PayloadColumnType); - } + Assert.Equal("DomainEvents", sut.TableName); + Assert.Equal("id", sut.CompositePrimaryKeyIdColumnName); + Assert.Equal("uniqueidentifier", sut.CompositePrimaryKeyIdColumnType); + Assert.Equal("version", sut.CompositePrimaryKeyVersionColumnName); + Assert.Equal("int", sut.CompositePrimaryKeyVersionColumnType); + Assert.Equal("timestamp", sut.TimestampColumnName); + Assert.Equal("datetime", sut.TimestampColumnType); + Assert.Equal("clrtype", sut.TypeColumnName); + Assert.Equal("varchar(1024)", sut.TypeColumnType); + Assert.Equal("payload", sut.PayloadColumnName); + Assert.Equal("varchar(max)", sut.PayloadColumnType); + } - [Fact] - public void EfCoreTracedAggregateEntityOptions_ShouldAcceptCustomValues() + [Fact] + public void EfCoreTracedAggregateEntityOptions_ShouldAcceptCustomValues() + { + var sut = new EfCoreTracedAggregateEntityOptions { - var sut = new EfCoreTracedAggregateEntityOptions - { - TableName = "TraceEvents", - CompositePrimaryKeyIdColumnName = "aggregate_id", - CompositePrimaryKeyIdColumnType = "uuid", - CompositePrimaryKeyVersionColumnName = "aggregate_version", - CompositePrimaryKeyVersionColumnType = "bigint", - TimestampColumnName = "recorded_at", - TimestampColumnType = "datetime2", - TypeColumnName = "aggregate_type", - TypeColumnType = "nvarchar(512)", - PayloadColumnName = "body", - PayloadColumnType = "varbinary(max)" - }; + TableName = "TraceEvents", + CompositePrimaryKeyIdColumnName = "aggregate_id", + CompositePrimaryKeyIdColumnType = "uuid", + CompositePrimaryKeyVersionColumnName = "aggregate_version", + CompositePrimaryKeyVersionColumnType = "bigint", + TimestampColumnName = "recorded_at", + TimestampColumnType = "datetime2", + TypeColumnName = "aggregate_type", + TypeColumnType = "nvarchar(512)", + PayloadColumnName = "body", + PayloadColumnType = "varbinary(max)" + }; - Assert.Equal("TraceEvents", sut.TableName); - Assert.Equal("aggregate_id", sut.CompositePrimaryKeyIdColumnName); - Assert.Equal("uuid", sut.CompositePrimaryKeyIdColumnType); - Assert.Equal("aggregate_version", sut.CompositePrimaryKeyVersionColumnName); - Assert.Equal("bigint", sut.CompositePrimaryKeyVersionColumnType); - Assert.Equal("recorded_at", sut.TimestampColumnName); - Assert.Equal("datetime2", sut.TimestampColumnType); - Assert.Equal("aggregate_type", sut.TypeColumnName); - Assert.Equal("nvarchar(512)", sut.TypeColumnType); - Assert.Equal("body", sut.PayloadColumnName); - Assert.Equal("varbinary(max)", sut.PayloadColumnType); - } + Assert.Equal("TraceEvents", sut.TableName); + Assert.Equal("aggregate_id", sut.CompositePrimaryKeyIdColumnName); + Assert.Equal("uuid", sut.CompositePrimaryKeyIdColumnType); + Assert.Equal("aggregate_version", sut.CompositePrimaryKeyVersionColumnName); + Assert.Equal("bigint", sut.CompositePrimaryKeyVersionColumnType); + Assert.Equal("recorded_at", sut.TimestampColumnName); + Assert.Equal("datetime2", sut.TimestampColumnType); + Assert.Equal("aggregate_type", sut.TypeColumnName); + Assert.Equal("nvarchar(512)", sut.TypeColumnType); + Assert.Equal("body", sut.PayloadColumnName); + Assert.Equal("varbinary(max)", sut.PayloadColumnType); } } diff --git a/test/Savvyio.Domain.EventSourcing.Tests/EfCoreTracedDomainEventExtensionsTest.cs b/test/Savvyio.Domain.EventSourcing.Tests/EfCoreTracedDomainEventExtensionsTest.cs index e063d317..2137198a 100644 --- a/test/Savvyio.Domain.EventSourcing.Tests/EfCoreTracedDomainEventExtensionsTest.cs +++ b/test/Savvyio.Domain.EventSourcing.Tests/EfCoreTracedDomainEventExtensionsTest.cs @@ -5,24 +5,23 @@ using Savvyio.Extensions.Newtonsoft.Json; using Xunit; -namespace Savvyio.Extensions.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.EFCore.Domain.EventSourcing; + +public class TracedDomainEventExtensionsTest : Test { - public class TracedDomainEventExtensionsTest : Test + public TracedDomainEventExtensionsTest(ITestOutputHelper output) : base(output) { - public TracedDomainEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void TracedDomainEventExtensions_ShouldConvertDomainEventToByteArray() - { - var domainEvent = new TracedAccountEmailAddressChanged("test@unit.test"); - var expected = NewtonsoftJsonMarshaller.Default.Serialize(domainEvent, typeof(ITracedDomainEvent)).ToByteArray(); + [Fact] + public void TracedDomainEventExtensions_ShouldConvertDomainEventToByteArray() + { + var domainEvent = new TracedAccountEmailAddressChanged("test@unit.test"); + var expected = NewtonsoftJsonMarshaller.Default.Serialize(domainEvent, typeof(ITracedDomainEvent)).ToByteArray(); - var sut = domainEvent.ToByteArray(NewtonsoftJsonMarshaller.Default); + var sut = domainEvent.ToByteArray(NewtonsoftJsonMarshaller.Default); - Assert.NotEmpty(sut); - Assert.Equal(expected, sut); - } + Assert.NotEmpty(sut); + Assert.Equal(expected, sut); } } diff --git a/test/Savvyio.Domain.EventSourcing.Tests/TracedAggregateRootTest.cs b/test/Savvyio.Domain.EventSourcing.Tests/TracedAggregateRootTest.cs index 459c9be1..790f7a9e 100644 --- a/test/Savvyio.Domain.EventSourcing.Tests/TracedAggregateRootTest.cs +++ b/test/Savvyio.Domain.EventSourcing.Tests/TracedAggregateRootTest.cs @@ -1,4 +1,4 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Cuemon; using Cuemon.Collections.Generic; using Microsoft.EntityFrameworkCore; @@ -27,280 +27,281 @@ using System.Threading.Tasks; using Xunit; -namespace Savvyio.Domain.EventSourcing +namespace Savvyio.Domain.EventSourcing; + +public class TracedAggregateRootTest : Test { - public class TracedAggregateRootTest : Test + public TracedAggregateRootTest(ITestOutputHelper output) : base(output) { - public TracedAggregateRootTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void TracedAggregateRoot_ShouldApplyAccountInitiatedToAggregate() - { - var id = Guid.NewGuid(); - var providerId = new PlatformProviderId(Guid.NewGuid()); - var fullName = new FullName("Michael", "Mortensen"); - var emailAddress = new EmailAddress("root@gimlichael.dev"); - - var sut = new TracedAccount(id, providerId, fullName, emailAddress); - - Assert.Equal(1, sut.Version); - Assert.Equal(id, sut.Id); - Assert.Equal(fullName, sut.FullName); - Assert.Equal(emailAddress, sut.EmailAddress); - Assert.Equal(providerId, sut.PlatformProviderId); - Assert.Equal(1, sut.Events.Count); - } + [Fact] + public void TracedAggregateRoot_ShouldApplyAccountInitiatedToAggregate() + { + var id = Guid.NewGuid(); + var providerId = new PlatformProviderId(Guid.NewGuid()); + var fullName = new FullName("Michael", "Mortensen"); + var emailAddress = new EmailAddress("root@gimlichael.dev"); + + var sut = new TracedAccount(id, providerId, fullName, emailAddress); + + Assert.Equal(1, sut.Version); + Assert.Equal(id, sut.Id); + Assert.Equal(fullName, sut.FullName); + Assert.Equal(emailAddress, sut.EmailAddress); + Assert.Equal(providerId, sut.PlatformProviderId); + Assert.Equal(1, sut.Events.Count); + } - [Fact] - public void TracedAggregateRoot_ShouldApplyAllEventsToAggregate() - { - var id = Guid.NewGuid(); - var providerId = new PlatformProviderId(Guid.NewGuid()); - var fullName = new FullName("Michael", "Mortennes"); - var emailAddress = new EmailAddress("root@gimlichael.ved"); - - var sut = new TracedAccount(id, providerId, fullName, emailAddress); - sut.ChangeEmailAddress(new EmailAddress("root@gimlichael.dev")); - sut.ChangeFullName(new FullName("Michael Mortensen")); - - Assert.Equal(3, sut.Version); - Assert.Equal(id, sut.Id); - Assert.NotEqual(fullName, sut.FullName); - Assert.NotEqual(emailAddress, sut.EmailAddress); - Assert.Equal(providerId, sut.PlatformProviderId); - Assert.Equal(3, sut.Events.Count); - } + [Fact] + public void TracedAggregateRoot_ShouldApplyAllEventsToAggregate() + { + var id = Guid.NewGuid(); + var providerId = new PlatformProviderId(Guid.NewGuid()); + var fullName = new FullName("Michael", "Mortennes"); + var emailAddress = new EmailAddress("root@gimlichael.ved"); + + var sut = new TracedAccount(id, providerId, fullName, emailAddress); + sut.ChangeEmailAddress(new EmailAddress("root@gimlichael.dev")); + sut.ChangeFullName(new FullName("Michael Mortensen")); + + Assert.Equal(3, sut.Version); + Assert.Equal(id, sut.Id); + Assert.NotEqual(fullName, sut.FullName); + Assert.NotEqual(emailAddress, sut.EmailAddress); + Assert.Equal(providerId, sut.PlatformProviderId); + Assert.Equal(3, sut.Events.Count); + } - [Theory] - [InlineData(typeof(NewtonsoftJsonMarshaller))] - [InlineData(typeof(JsonMarshaller))] - public async Task EfCoreDataStore_ShouldRaiseDomainEventsAndDehydrateEventsToStorage(Type formatterType) + [Theory] + [InlineData(typeof(NewtonsoftJsonMarshaller))] + [InlineData(typeof(JsonMarshaller))] + public async Task EfCoreDataStore_ShouldRaiseDomainEventsAndDehydrateEventsToStorage(Type formatterType) + { + string schema = null; + var sc = new ServiceCollection(); + var dbName = "Dummy_" + Generate.RandomString(10); // ensure unique in-memory database name per test run due to .NET 10 changes from Microsoft + sc.AddSavvyIO(o => o.AddDomainEventDispatcher().AddDomainEventHandler()); + + switch (formatterType) { - string schema = null; - var sc = new ServiceCollection(); - var dbName = "Dummy_" + Generate.RandomString(10); // ensure unique in-memory database name per test run due to .NET 10 changes from Microsoft - sc.AddSavvyIO(o => o.AddDomainEventDispatcher().AddDomainEventHandler()); - - switch (formatterType) - { - case Type newton when newton == typeof(NewtonsoftJsonMarshaller): - sc.AddMarshaller(); - sc.AddEfCoreAggregateDataSource(o => + case Type newton when newton == typeof(NewtonsoftJsonMarshaller): + sc.AddMarshaller(); + sc.AddEfCoreAggregateDataSource(o => + { + o.ContextConfigurator = b => b.UseInMemoryDatabase(dbName).EnableSensitiveDataLogging().EnableDetailedErrors().EnableServiceProviderCaching(false).LogTo(Console.WriteLine, LogLevel.Trace); + o.ModelConstructor = mb => { - o.ContextConfigurator = b => b.UseInMemoryDatabase(dbName).EnableSensitiveDataLogging().EnableDetailedErrors().EnableServiceProviderCaching(false).LogTo(Console.WriteLine, LogLevel.Trace); - o.ModelConstructor = mb => - { - mb.AddEventSourcing(eo => eo.TableName = $"{nameof(TracedAccount)}_DomainEvents"); - schema = mb.Model.ToDebugString(MetadataDebugStringOptions.LongDefault); - TestOutput.WriteLine(schema); - }; - }); - sc.AddEfCoreTracedAggregateRepository(); - break; - case Type builtin when builtin == typeof(JsonMarshaller): - sc.AddMarshaller(); - sc.AddEfCoreAggregateDataSource(o => + mb.AddEventSourcing(eo => eo.TableName = $"{nameof(TracedAccount)}_DomainEvents"); + schema = mb.Model.ToDebugString(MetadataDebugStringOptions.LongDefault); + TestOutput.WriteLine(schema); + }; + }); + sc.AddEfCoreTracedAggregateRepository(); + break; + case Type builtin when builtin == typeof(JsonMarshaller): + sc.AddMarshaller(); + sc.AddEfCoreAggregateDataSource(o => + { + o.ContextConfigurator = b => b.UseInMemoryDatabase(dbName).EnableSensitiveDataLogging().EnableDetailedErrors().EnableServiceProviderCaching(false).LogTo(Console.WriteLine, LogLevel.Trace); + o.ModelConstructor = mb => { - o.ContextConfigurator = b => b.UseInMemoryDatabase(dbName).EnableSensitiveDataLogging().EnableDetailedErrors().EnableServiceProviderCaching(false).LogTo(Console.WriteLine, LogLevel.Trace); - o.ModelConstructor = mb => - { - mb.AddEventSourcing(eo => eo.TableName = $"{nameof(TracedAccount)}_DomainEvents"); - schema = mb.Model.ToDebugString(MetadataDebugStringOptions.LongDefault); - TestOutput.WriteLine(schema); - }; - }); - sc.AddEfCoreTracedAggregateRepository(); - break; - } + mb.AddEventSourcing(eo => eo.TableName = $"{nameof(TracedAccount)}_DomainEvents"); + schema = mb.Model.ToDebugString(MetadataDebugStringOptions.LongDefault); + TestOutput.WriteLine(schema); + }; + }); + sc.AddEfCoreTracedAggregateRepository(); + break; + } - sc.AddScoped, InMemoryTestStore>(); + sc.AddScoped, InMemoryTestStore>(); - var sp = sc.BuildServiceProvider(); + var sp = sc.BuildServiceProvider(); - var ds = formatterType == typeof(NewtonsoftJsonMarshaller) - ? sp.GetRequiredService>() as IEfCoreDataSource - : sp.GetRequiredService>() as IEfCoreDataSource; - var sut4 = formatterType == typeof(NewtonsoftJsonMarshaller) - ? sp.GetRequiredService>() as ITracedAggregateRepository - : sp.GetRequiredService>() as ITracedAggregateRepository; + var ds = formatterType == typeof(NewtonsoftJsonMarshaller) + ? sp.GetRequiredService>() as IEfCoreDataSource + : sp.GetRequiredService>() as IEfCoreDataSource; + var sut4 = formatterType == typeof(NewtonsoftJsonMarshaller) + ? sp.GetRequiredService>() as ITracedAggregateRepository + : sp.GetRequiredService>() as ITracedAggregateRepository; - var id = Guid.NewGuid(); - var providerId = Guid.NewGuid(); - var name = "Test"; - var email = "test@unit.test"; + var id = Guid.NewGuid(); + var providerId = Guid.NewGuid(); + var name = "Test"; + var email = "test@unit.test"; - var ta = new TracedAccount(id, providerId, name, email); + var ta = new TracedAccount(id, providerId, name, email); + + for (var i = 0; i < 1000; i++) + { + ta.ChangeEmailAddress($"{Generate.RandomString(8)}@gimlichael.dev"); + } - for (var i = 0; i < 1000; i++) - { - ta.ChangeEmailAddress($"{Generate.RandomString(8)}@gimlichael.dev"); - } + ta.ChangeEmailAddress("root@gimlichael.dev"); - ta.ChangeEmailAddress("root@gimlichael.dev"); + sut4.Add(ta); - sut4.Add(ta); + await ds.SaveChangesAsync(); // should raise domain events - await ds.SaveChangesAsync(); // should raise domain events + var sut5 = await sut4.GetByIdAsync(id); - var sut5 = await sut4.GetByIdAsync(id); + Assert.Equal(id, sp.GetRequiredService>().QueryFor().Single().Id); + Assert.Equal(providerId, sp.GetRequiredService>().QueryFor().Single().PlatformProviderId); + Assert.Equal(name, sp.GetRequiredService>().QueryFor().Single().FullName); + Assert.Equal(email, sp.GetRequiredService>().QueryFor().Single().EmailAddress); + Assert.Equal("root@gimlichael.dev", sp.GetRequiredService>().QueryFor().Last().EmailAddress); - Assert.Equal(id, sp.GetRequiredService>().QueryFor().Single().Id); - Assert.Equal(providerId, sp.GetRequiredService>().QueryFor().Single().PlatformProviderId); - Assert.Equal(name, sp.GetRequiredService>().QueryFor().Single().FullName); - Assert.Equal(email, sp.GetRequiredService>().QueryFor().Single().EmailAddress); - Assert.Equal("root@gimlichael.dev", sp.GetRequiredService>().QueryFor().Last().EmailAddress); + Assert.Equal(id, sut5.Id); + Assert.Equal(providerId, sut5.PlatformProviderId); + Assert.Equal(name, sut5.FullName); + Assert.Equal("root@gimlichael.dev", sut5.EmailAddress); - Assert.Equal(id, sut5.Id); - Assert.Equal(providerId, sut5.PlatformProviderId); - Assert.Equal(name, sut5.FullName); - Assert.Equal("root@gimlichael.dev", sut5.EmailAddress); + var normalizedSchema = string.Join(Environment.NewLine, schema.ReplaceLineEndings().Split(Environment.NewLine).Select(line => line.TrimEnd())); - Assert.True(Match(@"Model: + Assert.True(Match(@"Model: EntityType: EfCoreTracedAggregateEntity - Properties: + Properties: Id (_id, Guid) Required PK AfterSave:Throw - Annotations: + Annotations: Relational:ColumnName: id Relational:ColumnType: uniqueidentifier Version (_version, long) Required PK AfterSave:Throw - Annotations: + Annotations: Relational:ColumnName: version Relational:ColumnType: int Payload (_payload, byte[]) - Annotations: + Annotations: Relational:ColumnName: payload Relational:ColumnType: varchar(max) Timestamp (_timestamp, DateTime) Required - Annotations: + Annotations: Relational:ColumnName: timestamp Relational:ColumnType: datetime Type (_type, string) - Annotations: + Annotations: Relational:ColumnName: clrtype Relational:ColumnType: varchar(1024) - Keys: + Keys: Id, Version PK - Annotations: - Relational:Schema: + Annotations: + Relational:Schema: Relational:TableName: TracedAccount_DomainEvents RelationshipDiscoveryConvention:NavigationCandidates: Microsoft.EntityFrameworkCore.Utilities.OrderedDictionary`2[System.Reflection.PropertyInfo,System.ValueTuple`2[System.Type,System.Nullable`1[System.Boolean]]] -Annotations: +Annotations: BaseTypeDiscoveryConvention:DerivedTypes: System.Collections.Generic.Dictionary`2[System.Type,System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.IConventionEntityType]] InversePropertyAttributeConvention:InverseNavigations: System.Collections.Generic.Dictionary`2[System.Type,System.Collections.Generic.SortedSet`1[System.Type]] NonNullableConventionState: System.Reflection.NullabilityInfoContext - ProductVersion: *".ReplaceLineEndings(), schema.ReplaceLineEndings(), o => o.ThrowOnNoMatch = true)); - } + ProductVersion: *".ReplaceLineEndings(), normalizedSchema, o => o.ThrowOnNoMatch = true)); + } - [Fact] - public void EfCoreDataStore_ShouldRehydrateFromProvidedEvents() - { - var id = Guid.NewGuid(); - var providerId = Guid.NewGuid(); - var name = "Test"; - var email = "test@unit.test"; - var newName = "Michael Mortensen"; - var newEmail = "root@gimlichael.dev"; - var expectedVersion = 3; - - var sut = new TracedAccount(id, Arguments.ToEnumerableOf( - new TracedAccountInitiated(id, providerId, name, email).SetAggregateVersion(1), - new TracedAccountEmailAddressChanged(newEmail).SetAggregateVersion(2), - new TracedAccountFullNameChanged(newName).SetAggregateVersion(3))); - - - Assert.Empty(sut.Events); - Assert.Equal(id, sut.Id); - Assert.Equal(providerId, sut.PlatformProviderId); - Assert.Equal(expectedVersion, sut.Version); - Assert.Equal(newName, sut.FullName); - Assert.Equal(newEmail, sut.EmailAddress); - } + [Fact] + public void EfCoreDataStore_ShouldRehydrateFromProvidedEvents() + { + var id = Guid.NewGuid(); + var providerId = Guid.NewGuid(); + var name = "Test"; + var email = "test@unit.test"; + var newName = "Michael Mortensen"; + var newEmail = "root@gimlichael.dev"; + var expectedVersion = 3; + + var sut = new TracedAccount(id, Arguments.ToEnumerableOf( + new TracedAccountInitiated(id, providerId, name, email).SetAggregateVersion(1), + new TracedAccountEmailAddressChanged(newEmail).SetAggregateVersion(2), + new TracedAccountFullNameChanged(newName).SetAggregateVersion(3))); + + + Assert.Empty(sut.Events); + Assert.Equal(id, sut.Id); + Assert.Equal(providerId, sut.PlatformProviderId); + Assert.Equal(expectedVersion, sut.Version); + Assert.Equal(newName, sut.FullName); + Assert.Equal(newEmail, sut.EmailAddress); + } - [Fact] - public void EfCoreTracedAggregateEntity_ShouldExposeExplicitInterfaceMembers() - { - var id = Guid.NewGuid(); - var providerId = new PlatformProviderId(Guid.NewGuid()); - var fullName = new FullName("Test", "User"); - var emailAddress = new EmailAddress("test@unit.test"); - var ta = new TracedAccount(id, providerId, fullName, emailAddress); - var domainEvent = ta.Events.First(); - var marshaller = new JsonMarshaller(); - - var entity = new EfCoreTracedAggregateEntity(ta, domainEvent, marshaller); - - var metadata = ((IMetadata)entity).Metadata; - var events = ((IAggregateRoot)entity).Events; - ((IAggregateRoot)entity).RemoveAllEvents(); // no-op - - Assert.NotNull(metadata); - Assert.Single(events); - Assert.Single(((IAggregateRoot)entity).Events); // RemoveAllEvents is no-op - } + [Fact] + public void EfCoreTracedAggregateEntity_ShouldExposeExplicitInterfaceMembers() + { + var id = Guid.NewGuid(); + var providerId = new PlatformProviderId(Guid.NewGuid()); + var fullName = new FullName("Test", "User"); + var emailAddress = new EmailAddress("test@unit.test"); + var ta = new TracedAccount(id, providerId, fullName, emailAddress); + var domainEvent = ta.Events.First(); + var marshaller = new JsonMarshaller(); + + var entity = new EfCoreTracedAggregateEntity(ta, domainEvent, marshaller); + + var metadata = ((IMetadata)entity).Metadata; + var events = ((IAggregateRoot)entity).Events; + ((IAggregateRoot)entity).RemoveAllEvents(); // no-op + + Assert.NotNull(metadata); + Assert.Single(events); + Assert.Single(((IAggregateRoot)entity).Events); // RemoveAllEvents is no-op + } - [Fact] - public async Task EfCoreTracedAggregateRepository_AddRange_ShouldPersistMultipleAggregates() + [Fact] + public async Task EfCoreTracedAggregateRepository_AddRange_ShouldPersistMultipleAggregates() + { + var sc = new ServiceCollection(); + var dbName = "Dummy_AddRange_" + Generate.RandomString(10); + sc.AddSavvyIO(o => o.AddDomainEventDispatcher().AddDomainEventHandler()); + sc.AddMarshaller(); + sc.AddEfCoreAggregateDataSource(o => { - var sc = new ServiceCollection(); - var dbName = "Dummy_AddRange_" + Generate.RandomString(10); - sc.AddSavvyIO(o => o.AddDomainEventDispatcher().AddDomainEventHandler()); - sc.AddMarshaller(); - sc.AddEfCoreAggregateDataSource(o => - { - o.ContextConfigurator = b => b.UseInMemoryDatabase(dbName).EnableSensitiveDataLogging().EnableDetailedErrors(); - o.ModelConstructor = mb => mb.AddEventSourcing(eo => eo.TableName = $"{nameof(TracedAccount)}_DomainEvents"); - }); - sc.AddEfCoreTracedAggregateRepository(); - sc.AddScoped, InMemoryTestStore>(); - - var sp = sc.BuildServiceProvider(); - var ds = sp.GetRequiredService>() as IEfCoreDataSource; - var sut4 = sp.GetRequiredService>() as ITracedAggregateRepository; - - var id1 = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - var providerId = Guid.NewGuid(); - - var ta1 = new TracedAccount(id1, providerId, "Name1", "email1@test.com"); - var ta2 = new TracedAccount(id2, providerId, "Name2", "email2@test.com"); - - sut4.AddRange([ta1, ta2]); - await ds.SaveChangesAsync(); - - var result1 = await sut4.GetByIdAsync(id1); - var result2 = await sut4.GetByIdAsync(id2); - - Assert.Equal(id1, result1.Id); - Assert.Equal("Name1", result1.FullName); - Assert.Equal(id2, result2.Id); - Assert.Equal("Name2", result2.FullName); - } + o.ContextConfigurator = b => b.UseInMemoryDatabase(dbName).EnableSensitiveDataLogging().EnableDetailedErrors(); + o.ModelConstructor = mb => mb.AddEventSourcing(eo => eo.TableName = $"{nameof(TracedAccount)}_DomainEvents"); + }); + sc.AddEfCoreTracedAggregateRepository(); + sc.AddScoped, InMemoryTestStore>(); - [Fact] - public async Task EfCoreTracedAggregateRepository_GetByIdAsync_ShouldThrowMissingMethodException_WhenEntityLacksRehydrationConstructor() - { - var sc = new ServiceCollection(); - var dbName = "Dummy_NoCtor_" + Generate.RandomString(10); - sc.AddSavvyIO(o => o.AddDomainEventDispatcher()); - sc.AddMarshaller(); - sc.AddEfCoreAggregateDataSource(o => - { - o.ContextConfigurator = b => b.UseInMemoryDatabase(dbName); - o.ModelConstructor = mb => mb.AddEventSourcing(eo => eo.TableName = "NoCtorTracedAccount_DomainEvents"); - }); - sc.AddEfCoreTracedAggregateRepository(); - - var sp = sc.BuildServiceProvider(); - var sut = sp.GetRequiredService>() as ITracedAggregateRepository; - - await Assert.ThrowsAsync(() => sut.GetByIdAsync(Guid.NewGuid())); - } + var sp = sc.BuildServiceProvider(); + var ds = sp.GetRequiredService>() as IEfCoreDataSource; + var sut4 = sp.GetRequiredService>() as ITracedAggregateRepository; + + var id1 = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var providerId = Guid.NewGuid(); + + var ta1 = new TracedAccount(id1, providerId, "Name1", "email1@test.com"); + var ta2 = new TracedAccount(id2, providerId, "Name2", "email2@test.com"); - private class NoCtorTracedAccount : TracedAggregateRoot + sut4.AddRange([ta1, ta2]); + await ds.SaveChangesAsync(); + + var result1 = await sut4.GetByIdAsync(id1); + var result2 = await sut4.GetByIdAsync(id2); + + Assert.Equal(id1, result1.Id); + Assert.Equal("Name1", result1.FullName); + Assert.Equal(id2, result2.Id); + Assert.Equal("Name2", result2.FullName); + } + + [Fact] + public async Task EfCoreTracedAggregateRepository_GetByIdAsync_ShouldThrowMissingMethodException_WhenEntityLacksRehydrationConstructor() + { + var sc = new ServiceCollection(); + var dbName = "Dummy_NoCtor_" + Generate.RandomString(10); + sc.AddSavvyIO(o => o.AddDomainEventDispatcher()); + sc.AddMarshaller(); + sc.AddEfCoreAggregateDataSource(o => { - protected override void RegisterDelegates(IFireForgetRegistry handler) { } - } + o.ContextConfigurator = b => b.UseInMemoryDatabase(dbName); + o.ModelConstructor = mb => mb.AddEventSourcing(eo => eo.TableName = "NoCtorTracedAccount_DomainEvents"); + }); + sc.AddEfCoreTracedAggregateRepository(); + + var sp = sc.BuildServiceProvider(); + var sut = sp.GetRequiredService>() as ITracedAggregateRepository; + + await Assert.ThrowsAsync(() => sut.GetByIdAsync(Guid.NewGuid())); + } + + private class NoCtorTracedAccount : TracedAggregateRoot + { + protected override void RegisterDelegates(IFireForgetRegistry handler) { } } } diff --git a/test/Savvyio.Domain.EventSourcing.Tests/TracedDomainEventExtensionsTest.cs b/test/Savvyio.Domain.EventSourcing.Tests/TracedDomainEventExtensionsTest.cs index 5629af2f..f22978ac 100644 --- a/test/Savvyio.Domain.EventSourcing.Tests/TracedDomainEventExtensionsTest.cs +++ b/test/Savvyio.Domain.EventSourcing.Tests/TracedDomainEventExtensionsTest.cs @@ -3,34 +3,33 @@ using Savvyio.Assets.Domain.Events; using Xunit; -namespace Savvyio.Domain.EventSourcing +namespace Savvyio.Domain.EventSourcing; + +public class TracedDomainEventExtensionsTest : Test { - public class TracedDomainEventExtensionsTest : Test + public TracedDomainEventExtensionsTest(ITestOutputHelper output) : base(output) { - public TracedDomainEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void TracedDomainEventExtensions_ShouldSetAndGetAggregateVersion() - { - var domainEvent = new TracedAccountEmailAddressChanged("test@unit.test"); + [Fact] + public void TracedDomainEventExtensions_ShouldSetAndGetAggregateVersion() + { + var domainEvent = new TracedAccountEmailAddressChanged("test@unit.test"); - var sut = domainEvent.SetAggregateVersion(42); + var sut = domainEvent.SetAggregateVersion(42); - Assert.Same(domainEvent, sut); - Assert.Equal(42, sut.GetAggregateVersion()); - } + Assert.Same(domainEvent, sut); + Assert.Equal(42, sut.GetAggregateVersion()); + } - [Fact] - public void TracedDomainEventExtensions_ShouldReturnMemberTypeMetadata() - { - var sut = new TracedAccountInitiated(Guid.NewGuid(), Guid.NewGuid(), "Jane Doe", "jd@office.com"); + [Fact] + public void TracedDomainEventExtensions_ShouldReturnMemberTypeMetadata() + { + var sut = new TracedAccountInitiated(Guid.NewGuid(), Guid.NewGuid(), "Jane Doe", "jd@office.com"); - var memberType = sut.GetMemberType(); + var memberType = sut.GetMemberType(); - Assert.StartsWith(typeof(TracedAccountInitiated).FullName, memberType); - Assert.Contains("Savvyio.Assets.Tests", memberType); - } + Assert.StartsWith(typeof(TracedAccountInitiated).FullName, memberType); + Assert.Contains("Savvyio.Assets.Tests", memberType); } } diff --git a/test/Savvyio.Domain.Tests/AggregateRootTest.cs b/test/Savvyio.Domain.Tests/AggregateRootTest.cs index 1e7e6c69..2992d018 100644 --- a/test/Savvyio.Domain.Tests/AggregateRootTest.cs +++ b/test/Savvyio.Domain.Tests/AggregateRootTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon.Extensions.Reflection; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Commands; @@ -6,114 +6,113 @@ using Savvyio.Assets.Domain.Events; using Xunit; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +public class AggregateRootTest : Test { - public class AggregateRootTest : Test + public AggregateRootTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void AggregateRoot_Account_ShouldHaveMetadata_FromCommand() + { + var id = Guid.NewGuid(); + var idString = id.ToString("N"); + var fullname = "Michael Mortensen"; + var email = "root@gimlichael.dev"; + var fakeMemberType = typeof(UpdateAccount); + + var command = new CreateAccount(id, fullname, email) + .SetCausationId(idString) + .SetCorrelationId(idString) + .SetMemberType(fakeMemberType); + + var sut = new Account(command.PlatformProviderId, command.FullName, command.EmailAddress).MergeMetadata(command); + + Assert.Collection(sut.Metadata, + pair => + { + Assert.Equal(pair.Key, MetadataDictionary.MemberType); + Assert.Equal(pair.Value, fakeMemberType.ToFullNameIncludingAssemblyName()); + }, + pair => + { + Assert.Equal(pair.Key, MetadataDictionary.CorrelationId); + Assert.Equal(pair.Value, idString); + }, + pair => + { + Assert.Equal(pair.Key, MetadataDictionary.CausationId); + Assert.Equal(pair.Value, idString); + }); + + Assert.True(sut.Metadata.Count == 3, "sut.Metadata.Count == 3"); + } + + [Fact] + public void AggregateRoot_Account_ShouldHaveDefaultMetadata() + { + var fullname = "Michael Mortensen"; + var email = "root@gimlichael.dev"; + + var sut = new Account(Guid.NewGuid(), fullname, email); + + Assert.True(sut.Metadata.Count == 0, "sut.Metadata.Count == 0"); + } + + [Fact] + public void AggregateRoot_PlatformProvider_ShouldRaise_PlatformProviderInitiated_Event() + { + var name = "AmaZure"; + var thirdLvlDomainName = "amz"; + var description = "The next big thing in cloud providers!"; + + var sut = new PlatformProvider(name, thirdLvlDomainName, description); + + Assert.Equal(sut.Description, description); + Assert.Equal(sut.Name, name); + Assert.Equal(sut.ThirdLevelDomainName, thirdLvlDomainName); + Assert.Equal(sut.Policy, new PlatformProviderAccountPolicy()); + Assert.True(sut.Events.Count == 1, "sut.Events.Count == 1"); + Assert.Collection(sut.Events, e => Assert.IsType(e)); + } + + [Fact] + public void AggregateRoot_PlatformProvider_ShouldRaise_FourDifferent_Events() { - public AggregateRootTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void AggregateRoot_Account_ShouldHaveMetadata_FromCommand() - { - var id = Guid.NewGuid(); - var idString = id.ToString("N"); - var fullname = "Michael Mortensen"; - var email = "root@gimlichael.dev"; - var fakeMemberType = typeof(UpdateAccount); - - var command = new CreateAccount(id, fullname, email) - .SetCausationId(idString) - .SetCorrelationId(idString) - .SetMemberType(fakeMemberType); - - var sut = new Account(command.PlatformProviderId, command.FullName, command.EmailAddress).MergeMetadata(command); - - Assert.Collection(sut.Metadata, - pair => - { - Assert.Equal(pair.Key, MetadataDictionary.MemberType); - Assert.Equal(pair.Value, fakeMemberType.ToFullNameIncludingAssemblyName()); - }, - pair => - { - Assert.Equal(pair.Key, MetadataDictionary.CorrelationId); - Assert.Equal(pair.Value, idString); - }, - pair => - { - Assert.Equal(pair.Key, MetadataDictionary.CausationId); - Assert.Equal(pair.Value, idString); - }); - - Assert.True(sut.Metadata.Count == 3, "sut.Metadata.Count == 3"); - } - - [Fact] - public void AggregateRoot_Account_ShouldHaveDefaultMetadata() - { - var fullname = "Michael Mortensen"; - var email = "root@gimlichael.dev"; - - var sut = new Account(Guid.NewGuid(), fullname, email); - - Assert.True(sut.Metadata.Count == 0, "sut.Metadata.Count == 0"); - } - - [Fact] - public void AggregateRoot_PlatformProvider_ShouldRaise_PlatformProviderInitiated_Event() - { - var name = "AmaZure"; - var thirdLvlDomainName = "amz"; - var description = "The next big thing in cloud providers!"; - - var sut = new PlatformProvider(name, thirdLvlDomainName, description); - - Assert.Equal(sut.Description, description); - Assert.Equal(sut.Name, name); - Assert.Equal(sut.ThirdLevelDomainName, thirdLvlDomainName); - Assert.Equal(sut.Policy, new PlatformProviderAccountPolicy()); - Assert.True(sut.Events.Count == 1, "sut.Events.Count == 1"); - Assert.Collection(sut.Events, e => Assert.IsType(e)); - } - - [Fact] - public void AggregateRoot_PlatformProvider_ShouldRaise_FourDifferent_Events() - { - var name = "AmaZure"; - var thirdLvlDomainName = "amz"; - var description = "The next big thing in cloud providers!"; - var newAccountPolicy = new PlatformProviderAccountPolicy(5, 0, TimeSpan.Zero, TimeSpan.Zero, 0, TimeSpan.Zero, TimeSpan.Zero, false); - var newDescription = "The next big thing in platform providers in the cloud!"; - var newName = "AwaZure"; - var newThirdLvlDomainName = "awz"; - - var sut = new PlatformProvider(name, thirdLvlDomainName, description); - sut.RemoveAllEvents(); - - Assert.Equal(sut.Description, description); - Assert.Equal(sut.Name, name); - Assert.Equal(sut.ThirdLevelDomainName, thirdLvlDomainName); - Assert.Equal(sut.Policy, new PlatformProviderAccountPolicy()); - Assert.True(sut.Events.Count == 0, "sut.Events.Count == 0"); - - sut.ChangeAccountPolicy(newAccountPolicy); - sut.ChangeDescription(newDescription); - sut.ChangeName(newName); - sut.ChangeThirdLevelDomainName(newThirdLvlDomainName); - - Assert.Equal(sut.Description, newDescription); - Assert.Equal(sut.Name, newName); - Assert.Equal(sut.ThirdLevelDomainName, newThirdLvlDomainName); - Assert.Equal(sut.Policy, newAccountPolicy); - Assert.True(sut.Events.Count == 4, "sut.Events.Count == 4"); - - Assert.Collection(sut.Events, - e => Assert.IsType(e), - e => Assert.IsType(e), - e => Assert.IsType(e), - e => Assert.IsType(e)); - } + var name = "AmaZure"; + var thirdLvlDomainName = "amz"; + var description = "The next big thing in cloud providers!"; + var newAccountPolicy = new PlatformProviderAccountPolicy(5, 0, TimeSpan.Zero, TimeSpan.Zero, 0, TimeSpan.Zero, TimeSpan.Zero, false); + var newDescription = "The next big thing in platform providers in the cloud!"; + var newName = "AwaZure"; + var newThirdLvlDomainName = "awz"; + + var sut = new PlatformProvider(name, thirdLvlDomainName, description); + sut.RemoveAllEvents(); + + Assert.Equal(sut.Description, description); + Assert.Equal(sut.Name, name); + Assert.Equal(sut.ThirdLevelDomainName, thirdLvlDomainName); + Assert.Equal(sut.Policy, new PlatformProviderAccountPolicy()); + Assert.True(sut.Events.Count == 0, "sut.Events.Count == 0"); + + sut.ChangeAccountPolicy(newAccountPolicy); + sut.ChangeDescription(newDescription); + sut.ChangeName(newName); + sut.ChangeThirdLevelDomainName(newThirdLvlDomainName); + + Assert.Equal(sut.Description, newDescription); + Assert.Equal(sut.Name, newName); + Assert.Equal(sut.ThirdLevelDomainName, newThirdLvlDomainName); + Assert.Equal(sut.Policy, newAccountPolicy); + Assert.True(sut.Events.Count == 4, "sut.Events.Count == 4"); + + Assert.Collection(sut.Events, + e => Assert.IsType(e), + e => Assert.IsType(e), + e => Assert.IsType(e), + e => Assert.IsType(e)); } } diff --git a/test/Savvyio.Domain.Tests/Assets/AccountHandlers.cs b/test/Savvyio.Domain.Tests/Assets/AccountHandlers.cs index aff7730b..4afd3852 100644 --- a/test/Savvyio.Domain.Tests/Assets/AccountHandlers.cs +++ b/test/Savvyio.Domain.Tests/Assets/AccountHandlers.cs @@ -1,53 +1,52 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Domain.Events; using Savvyio.Handlers; -namespace Savvyio.Domain.Assets +namespace Savvyio.Domain.Assets; + +public class AccountHandlers : IDomainEventHandler { - public class AccountHandlers : IDomainEventHandler + private readonly ITestStore _domainEventStore; + private readonly IDomainEventDispatcher _domainEventDispatcher; + + public AccountHandlers(ITestStore domainEventStore = null, IDomainEventDispatcher domainEventDispatcher = null) { - private readonly ITestStore _domainEventStore; - private readonly IDomainEventDispatcher _domainEventDispatcher; + _domainEventStore = domainEventStore; + _domainEventDispatcher = domainEventDispatcher; + } - public AccountHandlers(ITestStore domainEventStore = null, IDomainEventDispatcher domainEventDispatcher = null) + IFireForgetActivator IFireForgetHandler.Delegates => HandlerFactory.CreateFireForget(registry => + { + registry.Register(a => _domainEventStore.Add(a)); + registry.Register(a => _domainEventStore.Add(a)); + registry.Register(a => _domainEventStore.Add(a)); + registry.Register(a => _domainEventStore.Add(a)); + registry.Register(a => _domainEventStore.Add(a)); + registry.RegisterAsync(a => { - _domainEventStore = domainEventStore; - _domainEventDispatcher = domainEventDispatcher; - } - - IFireForgetActivator IFireForgetHandler.Delegates => HandlerFactory.CreateFireForget(registry => + _domainEventStore.Add(a); + return Task.CompletedTask; + }); + registry.RegisterAsync(a => { - registry.Register(a => _domainEventStore.Add(a)); - registry.Register(a => _domainEventStore.Add(a)); - registry.Register(a => _domainEventStore.Add(a)); - registry.Register(a => _domainEventStore.Add(a)); - registry.Register(a => _domainEventStore.Add(a)); - registry.RegisterAsync(a => - { - _domainEventStore.Add(a); - return Task.CompletedTask; - }); - registry.RegisterAsync(a => - { - _domainEventStore.Add(a); - return Task.CompletedTask; - }); - registry.RegisterAsync(a => - { - _domainEventStore.Add(a); - return Task.CompletedTask; - }); - registry.RegisterAsync(a => - { - _domainEventStore.Add(a); - return Task.CompletedTask; - }); - registry.RegisterAsync(a => - { - _domainEventStore.Add(a); - return Task.CompletedTask; - }); + _domainEventStore.Add(a); + return Task.CompletedTask; }); - } + registry.RegisterAsync(a => + { + _domainEventStore.Add(a); + return Task.CompletedTask; + }); + registry.RegisterAsync(a => + { + _domainEventStore.Add(a); + return Task.CompletedTask; + }); + registry.RegisterAsync(a => + { + _domainEventStore.Add(a); + return Task.CompletedTask; + }); + }); } diff --git a/test/Savvyio.Domain.Tests/Assets/Mixed.cs b/test/Savvyio.Domain.Tests/Assets/Mixed.cs index c7e5f088..281a665c 100644 --- a/test/Savvyio.Domain.Tests/Assets/Mixed.cs +++ b/test/Savvyio.Domain.Tests/Assets/Mixed.cs @@ -1,19 +1,18 @@ -using System; +using System; using Savvyio.Assets.Domain; -namespace Savvyio.Domain.Assets +namespace Savvyio.Domain.Assets; + +public record Mixed : ValueObject { - public record Mixed : ValueObject + public Mixed() { - public Mixed() - { - } + } - public AccountId AccountId { get; set; } + public AccountId AccountId { get; set; } - public Money Money { get; set; } + public Money Money { get; set; } - public decimal SomeDecimal { get; set; } = Decimal.MaxValue; - } + public decimal SomeDecimal { get; set; } = Decimal.MaxValue; } diff --git a/test/Savvyio.Domain.Tests/Assets/Money.cs b/test/Savvyio.Domain.Tests/Assets/Money.cs index 6b7bfc55..8f6eb602 100644 --- a/test/Savvyio.Domain.Tests/Assets/Money.cs +++ b/test/Savvyio.Domain.Tests/Assets/Money.cs @@ -1,23 +1,22 @@ -using System; +using System; -namespace Savvyio.Domain.Assets +namespace Savvyio.Domain.Assets; + +public enum Currency { - public enum Currency - { - USD, - MYR - } + USD, + MYR +} - public record Money : ValueObject - { - public Currency Currency { get; init; } - public decimal Amount { get; init; } +public record Money : ValueObject +{ + public Currency Currency { get; init; } + public decimal Amount { get; init; } - public Money(Currency currency, decimal amount) - { - if (amount < 0) throw new ArgumentOutOfRangeException(nameof(amount)); - Currency = currency; - Amount = amount; - } + public Money(Currency currency, decimal amount) + { + if (amount < 0) throw new ArgumentOutOfRangeException(nameof(amount)); + Currency = currency; + Amount = amount; } } diff --git a/test/Savvyio.Domain.Tests/DomainEventDispatcherExtensionsTest.cs b/test/Savvyio.Domain.Tests/DomainEventDispatcherExtensionsTest.cs index 647160f1..77eb8da6 100644 --- a/test/Savvyio.Domain.Tests/DomainEventDispatcherExtensionsTest.cs +++ b/test/Savvyio.Domain.Tests/DomainEventDispatcherExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using Cuemon; @@ -13,162 +13,161 @@ using Savvyio.Extensions.DependencyInjection; using Xunit; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +public class DomainEventDispatcherExtensionsTest : Test { - public class DomainEventDispatcherExtensionsTest : Test + public DomainEventDispatcherExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void RaiseMany_ShouldRaiseOneEventFromAggregate() + { + var sc = new ServiceCollection() + .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEventDispatcher).Assembly, typeof(DomainEventDispatcherExtensionsTest).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) + .AddScoped, InMemoryTestStore>(); + + var firstName = " Michael"; + var lastName = "Mortensen "; + var emailAddress = "root@gimlichael.dev"; + var providerId = Guid.NewGuid(); + + var sp = sc.BuildServiceProvider(); + var sut = new DomainEventDispatcher(sp.GetRequiredService()); + var ar = new Account(new PlatformProviderId(providerId), new FullName(firstName, lastName), new EmailAddress(emailAddress)); + + Assert.NotEmpty(ar.Events); + + sut.RaiseMany(ar); + + var des = sp.GetRequiredService>(); + + Assert.Empty(ar.Events); + Assert.Equal("Michael Mortensen", des.QueryFor().Single().FullName); + Assert.Equal(emailAddress, des.QueryFor().Single().EmailAddress); + Assert.Equal(providerId, des.QueryFor().Single().PlatformProviderId); + } + + [Fact] + public void RaiseMany_ShouldRaiseManyEventsFromAggregate() + { + var sc = new ServiceCollection() + .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEventDispatcher).Assembly, typeof(DomainEventDispatcherExtensionsTest).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) + .AddScoped, InMemoryTestStore>(); + + var sp = sc.BuildServiceProvider(); + var sut = new DomainEventDispatcher(sp.GetRequiredService()); + var ar = new Account(new AccountId(100)); + + var fullName = "Michael Mortensen"; + var emailAddress = "root@gimlichael.dev"; + var providerId = Guid.NewGuid(); + var userName = "gimlichael"; + var password = "p@assw0rd"; + var salt = Generate.RandomString(32); + + // emulate orm + var properties = ar.GetType().GetProperties(new MemberReflection()); + properties.Single(pi => pi.Name == "FullName").SetValue(ar, "Michael Motrensen"); + properties.Single(pi => pi.Name == "EmailAddress").SetValue(ar, "root@gimlichael.ved"); + properties.Single(pi => pi.Name == "PlatformProviderId").SetValue(ar, providerId); + // done + + ar.ChangeFullName(new FullName(fullName)); + ar.ChangeEmailAddress(emailAddress); + ar.Promote(new Credentials(userName, password, salt)); + + Assert.NotEmpty(ar.Events); + + sut.RaiseMany(ar); + + var des = sp.GetRequiredService>(); + + Assert.Empty(ar.Events); + Assert.Equal(fullName, ar.FullName); + Assert.Equal(emailAddress, ar.EmailAddress); + Assert.Equal(providerId, ar.PlatformProviderId); + + Assert.Equal(fullName, des.QueryFor().Single().FullName); + Assert.Equal(emailAddress, des.QueryFor().Single().EmailAddress); + Assert.Equal(userName, des.QueryFor().Single().UserName); + Assert.Equal(salt, des.QueryFor().Single().Salt); + Assert.Equal(KeyedHashFactory.CreateHmacCryptoSha256(salt.ToByteArray()).ComputeHash(password).ToHexadecimalString(), des.QueryFor().Single().Hash); + } + + [Fact] + public async Task RaiseManyAsync_ShouldRaiseOneEventFromAggregateAsync() + { + var sc = new ServiceCollection() + .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEventDispatcher).Assembly, typeof(DomainEventDispatcherExtensionsTest).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) + .AddScoped, InMemoryTestStore>(); + + var firstName = " Michael"; + var lastName = "Mortensen "; + var emailAddress = "root@gimlichael.dev"; + var providerId = Guid.NewGuid(); + + var sp = sc.BuildServiceProvider(); + var sut = new DomainEventDispatcher(sp.GetRequiredService()); + var ar = new Account(new PlatformProviderId(providerId), new FullName(firstName, lastName), new EmailAddress(emailAddress)); + + Assert.NotEmpty(ar.Events); + + await sut.RaiseManyAsync(ar); + + var des = sp.GetRequiredService>(); + + Assert.Empty(ar.Events); + Assert.Equal("Michael Mortensen", des.QueryFor().Single().FullName); + Assert.Equal(emailAddress, des.QueryFor().Single().EmailAddress); + Assert.Equal(providerId, des.QueryFor().Single().PlatformProviderId); + } + + [Fact] + public async Task RaiseManyAsync_ShouldRaiseManyEventsFromAggregateAsync() { - public DomainEventDispatcherExtensionsTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void RaiseMany_ShouldRaiseOneEventFromAggregate() - { - var sc = new ServiceCollection() - .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEventDispatcher).Assembly, typeof(DomainEventDispatcherExtensionsTest).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) - .AddScoped, InMemoryTestStore>(); - - var firstName = " Michael"; - var lastName = "Mortensen "; - var emailAddress = "root@gimlichael.dev"; - var providerId = Guid.NewGuid(); - - var sp = sc.BuildServiceProvider(); - var sut = new DomainEventDispatcher(sp.GetRequiredService()); - var ar = new Account(new PlatformProviderId(providerId), new FullName(firstName, lastName), new EmailAddress(emailAddress)); - - Assert.NotEmpty(ar.Events); - - sut.RaiseMany(ar); - - var des = sp.GetRequiredService>(); - - Assert.Empty(ar.Events); - Assert.Equal("Michael Mortensen", des.QueryFor().Single().FullName); - Assert.Equal(emailAddress, des.QueryFor().Single().EmailAddress); - Assert.Equal(providerId, des.QueryFor().Single().PlatformProviderId); - } - - [Fact] - public void RaiseMany_ShouldRaiseManyEventsFromAggregate() - { - var sc = new ServiceCollection() - .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEventDispatcher).Assembly, typeof(DomainEventDispatcherExtensionsTest).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) - .AddScoped, InMemoryTestStore>(); - - var sp = sc.BuildServiceProvider(); - var sut = new DomainEventDispatcher(sp.GetRequiredService()); - var ar = new Account(new AccountId(100)); - - var fullName = "Michael Mortensen"; - var emailAddress = "root@gimlichael.dev"; - var providerId = Guid.NewGuid(); - var userName = "gimlichael"; - var password = "p@assw0rd"; - var salt = Generate.RandomString(32); - - // emulate orm - var properties = ar.GetType().GetProperties(new MemberReflection()); - properties.Single(pi => pi.Name == "FullName").SetValue(ar, "Michael Motrensen"); - properties.Single(pi => pi.Name == "EmailAddress").SetValue(ar, "root@gimlichael.ved"); - properties.Single(pi => pi.Name == "PlatformProviderId").SetValue(ar, providerId); - // done - - ar.ChangeFullName(new FullName(fullName)); - ar.ChangeEmailAddress(emailAddress); - ar.Promote(new Credentials(userName, password, salt)); - - Assert.NotEmpty(ar.Events); - - sut.RaiseMany(ar); - - var des = sp.GetRequiredService>(); - - Assert.Empty(ar.Events); - Assert.Equal(fullName, ar.FullName); - Assert.Equal(emailAddress, ar.EmailAddress); - Assert.Equal(providerId, ar.PlatformProviderId); - - Assert.Equal(fullName, des.QueryFor().Single().FullName); - Assert.Equal(emailAddress, des.QueryFor().Single().EmailAddress); - Assert.Equal(userName, des.QueryFor().Single().UserName); - Assert.Equal(salt, des.QueryFor().Single().Salt); - Assert.Equal(KeyedHashFactory.CreateHmacCryptoSha256(salt.ToByteArray()).ComputeHash(password).ToHexadecimalString(), des.QueryFor().Single().Hash); - } - - [Fact] - public async Task RaiseManyAsync_ShouldRaiseOneEventFromAggregateAsync() - { - var sc = new ServiceCollection() - .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEventDispatcher).Assembly, typeof(DomainEventDispatcherExtensionsTest).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) - .AddScoped, InMemoryTestStore>(); - - var firstName = " Michael"; - var lastName = "Mortensen "; - var emailAddress = "root@gimlichael.dev"; - var providerId = Guid.NewGuid(); - - var sp = sc.BuildServiceProvider(); - var sut = new DomainEventDispatcher(sp.GetRequiredService()); - var ar = new Account(new PlatformProviderId(providerId), new FullName(firstName, lastName), new EmailAddress(emailAddress)); - - Assert.NotEmpty(ar.Events); - - await sut.RaiseManyAsync(ar); - - var des = sp.GetRequiredService>(); - - Assert.Empty(ar.Events); - Assert.Equal("Michael Mortensen", des.QueryFor().Single().FullName); - Assert.Equal(emailAddress, des.QueryFor().Single().EmailAddress); - Assert.Equal(providerId, des.QueryFor().Single().PlatformProviderId); - } - - [Fact] - public async Task RaiseManyAsync_ShouldRaiseManyEventsFromAggregateAsync() - { - var sc = new ServiceCollection() - .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEventDispatcher).Assembly, typeof(DomainEventDispatcherExtensionsTest).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) - .AddScoped, InMemoryTestStore>(); - - var sp = sc.BuildServiceProvider(); - var sut = new DomainEventDispatcher(sp.GetRequiredService()); - var ar = new Account(new AccountId(100)); - - var fullName = "Michael Mortensen"; - var emailAddress = "root@gimlichael.dev"; - var providerId = Guid.NewGuid(); - var userName = "gimlichael"; - var password = "p@assw0rd"; - var salt = Generate.RandomString(32); - - // emulate orm - var properties = ar.GetType().GetProperties(new MemberReflection()); - properties.Single(pi => pi.Name == "FullName").SetValue(ar, "Michael Motrensen"); - properties.Single(pi => pi.Name == "EmailAddress").SetValue(ar, "root@gimlichael.ved"); - properties.Single(pi => pi.Name == "PlatformProviderId").SetValue(ar, providerId); - // done - - ar.ChangeFullName(new FullName(fullName)); - ar.ChangeEmailAddress(emailAddress); - ar.Promote(new Credentials(userName, password, salt)); - - Assert.NotEmpty(ar.Events); - - await sut.RaiseManyAsync(ar); - - var des = sp.GetRequiredService>(); - - Assert.Empty(ar.Events); - Assert.Equal(fullName, ar.FullName); - Assert.Equal(emailAddress, ar.EmailAddress); - Assert.Equal(providerId, ar.PlatformProviderId); - - Assert.Equal(fullName, des.QueryFor().Single().FullName); - Assert.Equal(emailAddress, des.QueryFor().Single().EmailAddress); - Assert.Equal(userName, des.QueryFor().Single().UserName); - Assert.Equal(salt, des.QueryFor().Single().Salt); - Assert.Equal(KeyedHashFactory.CreateHmacCryptoSha256(salt.ToByteArray()).ComputeHash(password).ToHexadecimalString(), des.QueryFor().Single().Hash); - } + var sc = new ServiceCollection() + .AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEventDispatcher).Assembly, typeof(DomainEventDispatcherExtensionsTest).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()) + .AddScoped, InMemoryTestStore>(); + + var sp = sc.BuildServiceProvider(); + var sut = new DomainEventDispatcher(sp.GetRequiredService()); + var ar = new Account(new AccountId(100)); + + var fullName = "Michael Mortensen"; + var emailAddress = "root@gimlichael.dev"; + var providerId = Guid.NewGuid(); + var userName = "gimlichael"; + var password = "p@assw0rd"; + var salt = Generate.RandomString(32); + + // emulate orm + var properties = ar.GetType().GetProperties(new MemberReflection()); + properties.Single(pi => pi.Name == "FullName").SetValue(ar, "Michael Motrensen"); + properties.Single(pi => pi.Name == "EmailAddress").SetValue(ar, "root@gimlichael.ved"); + properties.Single(pi => pi.Name == "PlatformProviderId").SetValue(ar, providerId); + // done + + ar.ChangeFullName(new FullName(fullName)); + ar.ChangeEmailAddress(emailAddress); + ar.Promote(new Credentials(userName, password, salt)); + + Assert.NotEmpty(ar.Events); + + await sut.RaiseManyAsync(ar); + + var des = sp.GetRequiredService>(); + + Assert.Empty(ar.Events); + Assert.Equal(fullName, ar.FullName); + Assert.Equal(emailAddress, ar.EmailAddress); + Assert.Equal(providerId, ar.PlatformProviderId); + + Assert.Equal(fullName, des.QueryFor().Single().FullName); + Assert.Equal(emailAddress, des.QueryFor().Single().EmailAddress); + Assert.Equal(userName, des.QueryFor().Single().UserName); + Assert.Equal(salt, des.QueryFor().Single().Salt); + Assert.Equal(KeyedHashFactory.CreateHmacCryptoSha256(salt.ToByteArray()).ComputeHash(password).ToHexadecimalString(), des.QueryFor().Single().Hash); } } diff --git a/test/Savvyio.Domain.Tests/DomainEventTest.cs b/test/Savvyio.Domain.Tests/DomainEventTest.cs index 11e1d638..6328eba9 100644 --- a/test/Savvyio.Domain.Tests/DomainEventTest.cs +++ b/test/Savvyio.Domain.Tests/DomainEventTest.cs @@ -1,66 +1,65 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Domain; using Savvyio.Assets.Domain.Events; using Xunit; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +public class DomainEventTest : Test { - public class DomainEventTest : Test + public DomainEventTest(ITestOutputHelper output) : base(output) { - public DomainEventTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void DomainEvent_AccountInitiated_ShouldHaveDefaultMetadata() - { - var fullname = "Michael Mortensen"; - var email = "root@gimlichael.dev"; + [Fact] + public void DomainEvent_AccountInitiated_ShouldHaveDefaultMetadata() + { + var fullname = "Michael Mortensen"; + var email = "root@gimlichael.dev"; - var account = new Account(Guid.NewGuid(), fullname, email); - var sut = new AccountInitiated(account); - var ts = DateTime.UtcNow; + var account = new Account(Guid.NewGuid(), fullname, email); + var sut = new AccountInitiated(account); + var ts = DateTime.UtcNow; - Assert.Equal(account.EmailAddress, sut.EmailAddress); - Assert.Equal(account.FullName, sut.FullName); - Assert.Equal(account.PlatformProviderId, sut.PlatformProviderId); - Assert.Empty(account.Metadata); - Assert.Collection(sut.Metadata.Keys, - s => Assert.Equal(s, MetadataDictionary.MemberType), - s => Assert.Equal(s, MetadataDictionary.EventId), - s => Assert.Equal(s, MetadataDictionary.Timestamp)); - Assert.True(sut.Metadata.Count == 3, "sut.Metadata.Count == 3"); - Assert.InRange(DateTime.UtcNow, sut.GetTimestamp(), ts.AddSeconds(1)); - } + Assert.Equal(account.EmailAddress, sut.EmailAddress); + Assert.Equal(account.FullName, sut.FullName); + Assert.Equal(account.PlatformProviderId, sut.PlatformProviderId); + Assert.Empty(account.Metadata); + Assert.Collection(sut.Metadata.Keys, + s => Assert.Equal(s, MetadataDictionary.MemberType), + s => Assert.Equal(s, MetadataDictionary.EventId), + s => Assert.Equal(s, MetadataDictionary.Timestamp)); + Assert.True(sut.Metadata.Count == 3, "sut.Metadata.Count == 3"); + Assert.InRange(DateTime.UtcNow, sut.GetTimestamp(), ts.AddSeconds(1)); + } - [Fact] - public void DomainEvent_AccountInitiated_ShouldHaveAdditionalMetadata_CopiedFromAggregate() - { - var id = Guid.NewGuid(); - var fullname = "Michael Mortensen"; - var email = "root@gimlichael.dev"; + [Fact] + public void DomainEvent_AccountInitiated_ShouldHaveAdditionalMetadata_CopiedFromAggregate() + { + var id = Guid.NewGuid(); + var fullname = "Michael Mortensen"; + var email = "root@gimlichael.dev"; - var account = new Account(id, fullname, email) - .SaveMetadata("Key1", "Key1Value") - .SaveMetadata("Key2", "Key2Value") - .SaveMetadata("correlationid", "makingSureCorrelationIdIsWrittenPascalCase"); + var account = new Account(id, fullname, email) + .SaveMetadata("Key1", "Key1Value") + .SaveMetadata("Key2", "Key2Value") + .SaveMetadata("correlationid", "makingSureCorrelationIdIsWrittenPascalCase"); - var sut = new AccountInitiated(account).MergeMetadata(account); - var ts = DateTime.UtcNow; + var sut = new AccountInitiated(account).MergeMetadata(account); + var ts = DateTime.UtcNow; - Assert.Equal(account.EmailAddress, sut.EmailAddress); - Assert.Equal(account.FullName, sut.FullName); - Assert.Equal(account.PlatformProviderId, sut.PlatformProviderId); - Assert.Collection(sut.Metadata.Keys, - s => Assert.Equal(s, MetadataDictionary.MemberType), - s => Assert.Equal(s, MetadataDictionary.EventId), - s => Assert.Equal(s, MetadataDictionary.Timestamp), - s => Assert.Equal(s, "Key1"), - s => Assert.Equal(s, "Key2"), - s => Assert.Equal(s, MetadataDictionary.CorrelationId)); ; - Assert.True(sut.Metadata.Count == 6, "sut.Metadata.Count == 6"); - Assert.InRange(DateTime.UtcNow, sut.GetTimestamp(), ts.AddSeconds(1)); - } + Assert.Equal(account.EmailAddress, sut.EmailAddress); + Assert.Equal(account.FullName, sut.FullName); + Assert.Equal(account.PlatformProviderId, sut.PlatformProviderId); + Assert.Collection(sut.Metadata.Keys, + s => Assert.Equal(s, MetadataDictionary.MemberType), + s => Assert.Equal(s, MetadataDictionary.EventId), + s => Assert.Equal(s, MetadataDictionary.Timestamp), + s => Assert.Equal(s, "Key1"), + s => Assert.Equal(s, "Key2"), + s => Assert.Equal(s, MetadataDictionary.CorrelationId)); ; + Assert.True(sut.Metadata.Count == 6, "sut.Metadata.Count == 6"); + Assert.InRange(DateTime.UtcNow, sut.GetTimestamp(), ts.AddSeconds(1)); } } diff --git a/test/Savvyio.Domain.Tests/EntityTest.cs b/test/Savvyio.Domain.Tests/EntityTest.cs index 77dd3d7a..258e18dd 100644 --- a/test/Savvyio.Domain.Tests/EntityTest.cs +++ b/test/Savvyio.Domain.Tests/EntityTest.cs @@ -1,34 +1,33 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Domain; using Xunit; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +public class EntityTest : Test { - public class EntityTest : Test + public EntityTest(ITestOutputHelper output) : base(output) { - public EntityTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Entity_Account_ShouldBeTransient() - { - var sut = new Account(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); + [Fact] + public void Entity_Account_ShouldBeTransient() + { + var sut = new Account(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); - Assert.Equal(0, sut.Id); - Assert.True(sut.IsTransient); - Assert.IsType(sut.Id); - } + Assert.Equal(0, sut.Id); + Assert.True(sut.IsTransient); + Assert.IsType(sut.Id); + } - [Fact] - public void Entity_Account_ShouldBeNonTransient() - { - var sut = new Account(100); + [Fact] + public void Entity_Account_ShouldBeNonTransient() + { + var sut = new Account(100); - Assert.Equal(100, sut.Id); - Assert.False(sut.IsTransient); - Assert.IsType(sut.Id); - } + Assert.Equal(100, sut.Id); + Assert.False(sut.IsTransient); + Assert.IsType(sut.Id); } } diff --git a/test/Savvyio.Domain.Tests/SavvyioOptionsExtensionsTest.cs b/test/Savvyio.Domain.Tests/SavvyioOptionsExtensionsTest.cs index 57fa2fbe..9eeb9bed 100644 --- a/test/Savvyio.Domain.Tests/SavvyioOptionsExtensionsTest.cs +++ b/test/Savvyio.Domain.Tests/SavvyioOptionsExtensionsTest.cs @@ -1,43 +1,42 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Savvyio.Assets.Domain.Handlers; using Xunit; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +public class SavvyioOptionsExtensionsTest : Test { - public class SavvyioOptionsExtensionsTest : Test + public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) { - public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() - { - var sut = new SavvyioOptions().AddHandlers(); - Assert.Empty(sut.HandlerServiceTypes); - Assert.Empty(sut.HandlerImplementationTypes); - } + [Fact] + public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() + { + var sut = new SavvyioOptions().AddHandlers(); + Assert.Empty(sut.HandlerServiceTypes); + Assert.Empty(sut.HandlerImplementationTypes); + } - [Fact] - public void AddDomainEventHandler_ShouldAddDomainEventHandler() - { - var sut = new SavvyioOptions().AddDomainEventHandler(); + [Fact] + public void AddDomainEventHandler_ShouldAddDomainEventHandler() + { + var sut = new SavvyioOptions().AddDomainEventHandler(); - Assert.NotEmpty(sut.HandlerServiceTypes); - Assert.NotEmpty(sut.HandlerImplementationTypes); - Assert.Collection(sut.HandlerServiceTypes, type => Assert.Equal(typeof(IDomainEventHandler), type)); - Assert.Collection(sut.HandlerImplementationTypes, type => Assert.Equal(typeof(AccountDomainEventHandler), type)); - } + Assert.NotEmpty(sut.HandlerServiceTypes); + Assert.NotEmpty(sut.HandlerImplementationTypes); + Assert.Collection(sut.HandlerServiceTypes, type => Assert.Equal(typeof(IDomainEventHandler), type)); + Assert.Collection(sut.HandlerImplementationTypes, type => Assert.Equal(typeof(AccountDomainEventHandler), type)); + } - [Fact] - public void AddDomainEventDispatcher_ShouldAddDomainEventDispatcher() - { - var sut = new SavvyioOptions().AddDomainEventDispatcher(); + [Fact] + public void AddDomainEventDispatcher_ShouldAddDomainEventDispatcher() + { + var sut = new SavvyioOptions().AddDomainEventDispatcher(); - Assert.NotEmpty(sut.DispatcherServiceTypes); - Assert.NotEmpty(sut.DispatcherImplementationTypes); - Assert.Collection(sut.DispatcherServiceTypes, type => Assert.Equal(typeof(IDomainEventDispatcher), type)); - Assert.Collection(sut.DispatcherImplementationTypes, type => Assert.Equal(typeof(DomainEventDispatcher), type)); - } + Assert.NotEmpty(sut.DispatcherServiceTypes); + Assert.NotEmpty(sut.DispatcherImplementationTypes); + Assert.Collection(sut.DispatcherServiceTypes, type => Assert.Equal(typeof(IDomainEventDispatcher), type)); + Assert.Collection(sut.DispatcherImplementationTypes, type => Assert.Equal(typeof(DomainEventDispatcher), type)); } } diff --git a/test/Savvyio.Domain.Tests/SingleValueObjectTest.cs b/test/Savvyio.Domain.Tests/SingleValueObjectTest.cs index 4391cec3..4dd0f4d5 100644 --- a/test/Savvyio.Domain.Tests/SingleValueObjectTest.cs +++ b/test/Savvyio.Domain.Tests/SingleValueObjectTest.cs @@ -1,49 +1,48 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Domain; using Xunit; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +public class SingleValueObjectTest : Test { - public class SingleValueObjectTest : Test + public SingleValueObjectTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void SingleValueObject_PlatformProviderId_ShouldBeEqual() { - public SingleValueObjectTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void SingleValueObject_PlatformProviderId_ShouldBeEqual() - { - var id = Guid.NewGuid(); - var sut1 = new PlatformProviderId(id); - var sut2 = new PlatformProviderId(id); - - TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); - - Assert.Equal(sut1, sut2); - } - - [Fact] - public void SingleValueObject_PlatformProviderId_ShouldNotBeEqual() - { - var id1 = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - var sut1 = new PlatformProviderId(id1); - var sut2 = new PlatformProviderId(id2); - - TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); - - Assert.NotEqual(sut1, sut2); - } - - [Fact] - public void SingleValueObject_EmailAddress_ShouldConvertBiderictional() - { - var email = "root@gimlichael.dev"; - var sut = new EmailAddress(email); - - Assert.Equal(email, sut); - Assert.Equal((EmailAddress)email, sut); - } + var id = Guid.NewGuid(); + var sut1 = new PlatformProviderId(id); + var sut2 = new PlatformProviderId(id); + + TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); + + Assert.Equal(sut1, sut2); + } + + [Fact] + public void SingleValueObject_PlatformProviderId_ShouldNotBeEqual() + { + var id1 = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var sut1 = new PlatformProviderId(id1); + var sut2 = new PlatformProviderId(id2); + + TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); + + Assert.NotEqual(sut1, sut2); + } + + [Fact] + public void SingleValueObject_EmailAddress_ShouldConvertBiderictional() + { + var email = "root@gimlichael.dev"; + var sut = new EmailAddress(email); + + Assert.Equal(email, sut); + Assert.Equal((EmailAddress)email, sut); } } diff --git a/test/Savvyio.Domain.Tests/ValueObjectTest.cs b/test/Savvyio.Domain.Tests/ValueObjectTest.cs index 62129d43..91f3bf64 100644 --- a/test/Savvyio.Domain.Tests/ValueObjectTest.cs +++ b/test/Savvyio.Domain.Tests/ValueObjectTest.cs @@ -1,80 +1,79 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Domain; using Savvyio.Domain.Assets; using Xunit; -namespace Savvyio.Domain +namespace Savvyio.Domain; + +public class ValueObjectTest : Test { - public class ValueObjectTest : Test + public ValueObjectTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ValueObject_PlatformProviderAccountPolicy_ShouldBeEqual() + { + var sut1 = new PlatformProviderAccountPolicy(); + var sut2 = new PlatformProviderAccountPolicy(); + + TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); + + Assert.True(sut1 == sut2); + Assert.Equal(sut1, sut2); + Assert.Equal(sut1.GetHashCode(), sut2.GetHashCode()); + } + + [Fact] + public void ValueObject_PlatformProviderAccountPolicy_ShouldNotBeEqual() + { + var sut1 = new PlatformProviderAccountPolicy(); + var sut2 = new PlatformProviderAccountPolicy(1, 24, TimeSpan.Zero, TimeSpan.FromDays(1), 5, TimeSpan.FromHours(1), TimeSpan.FromMinutes(10), false); + + TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); + + Assert.True(sut1 != sut2); + Assert.NotEqual(sut1, sut2); + Assert.NotEqual(sut1.GetHashCode(), sut2.GetHashCode()); + } + + [Fact] + public void ValueObject_Money_ShouldNotBeEqual() { - public ValueObjectTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void ValueObject_PlatformProviderAccountPolicy_ShouldBeEqual() - { - var sut1 = new PlatformProviderAccountPolicy(); - var sut2 = new PlatformProviderAccountPolicy(); - - TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); - - Assert.True(sut1 == sut2); - Assert.Equal(sut1, sut2); - Assert.Equal(sut1.GetHashCode(), sut2.GetHashCode()); - } - - [Fact] - public void ValueObject_PlatformProviderAccountPolicy_ShouldNotBeEqual() - { - var sut1 = new PlatformProviderAccountPolicy(); - var sut2 = new PlatformProviderAccountPolicy(1, 24, TimeSpan.Zero, TimeSpan.FromDays(1), 5, TimeSpan.FromHours(1), TimeSpan.FromMinutes(10), false); - - TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); - - Assert.True(sut1 != sut2); - Assert.NotEqual(sut1, sut2); - Assert.NotEqual(sut1.GetHashCode(), sut2.GetHashCode()); - } - - [Fact] - public void ValueObject_Money_ShouldNotBeEqual() - { - var sut1 = new Money(Currency.MYR, 100); - var sut2 = new Money(Currency.USD, 101); - - TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); - - Assert.True(sut1 != sut2); - Assert.NotEqual(sut1, sut2); - Assert.NotEqual(sut1.GetHashCode(), sut2.GetHashCode()); - } - - [Fact] - public void ValueObject_MixedSimpleAndValueObject_ShouldNotBeEqual() - { - var sut1 = new Mixed() { AccountId = 2414214, Money = new Money(Currency.MYR, 123) }; - var sut2 = new Mixed() { AccountId = 315551312, Money = new Money(Currency.MYR, 321) }; - - TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); - - Assert.True(sut1 != sut2); - Assert.NotEqual(sut1, sut2); - Assert.NotEqual(sut1.GetHashCode(), sut2.GetHashCode()); - } - - [Fact] - public void ValueObject_MixedSimpleAndValueObject_ShouldBeEqual() - { - var sut1 = new Mixed() { AccountId = 2414214, Money = new Money(Currency.MYR, 123) }; - var sut2 = new Mixed() { AccountId = 2414214, Money = new Money(Currency.MYR, 123) }; - - TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); - - Assert.True(sut1 == sut2); - Assert.Equal(sut1, sut2); - Assert.Equal(sut1.GetHashCode(), sut2.GetHashCode()); - } + var sut1 = new Money(Currency.MYR, 100); + var sut2 = new Money(Currency.USD, 101); + + TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); + + Assert.True(sut1 != sut2); + Assert.NotEqual(sut1, sut2); + Assert.NotEqual(sut1.GetHashCode(), sut2.GetHashCode()); + } + + [Fact] + public void ValueObject_MixedSimpleAndValueObject_ShouldNotBeEqual() + { + var sut1 = new Mixed() { AccountId = 2414214, Money = new Money(Currency.MYR, 123) }; + var sut2 = new Mixed() { AccountId = 315551312, Money = new Money(Currency.MYR, 321) }; + + TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); + + Assert.True(sut1 != sut2); + Assert.NotEqual(sut1, sut2); + Assert.NotEqual(sut1.GetHashCode(), sut2.GetHashCode()); + } + + [Fact] + public void ValueObject_MixedSimpleAndValueObject_ShouldBeEqual() + { + var sut1 = new Mixed() { AccountId = 2414214, Money = new Money(Currency.MYR, 123) }; + var sut2 = new Mixed() { AccountId = 2414214, Money = new Money(Currency.MYR, 123) }; + + TestOutput.WriteLines(sut1.ToString(), sut2.ToString()); + + Assert.True(sut1 == sut2); + Assert.Equal(sut1, sut2); + Assert.Equal(sut1.GetHashCode(), sut2.GetHashCode()); } } diff --git a/test/Savvyio.EventDriven.Tests/IntegrationEventDispatcherTest.cs b/test/Savvyio.EventDriven.Tests/IntegrationEventDispatcherTest.cs index af105b00..a42cf5bf 100644 --- a/test/Savvyio.EventDriven.Tests/IntegrationEventDispatcherTest.cs +++ b/test/Savvyio.EventDriven.Tests/IntegrationEventDispatcherTest.cs @@ -5,44 +5,43 @@ using Savvyio.Handlers; using Xunit; -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +public class IntegrationEventDispatcherTest : Test { - public class IntegrationEventDispatcherTest : Test + public IntegrationEventDispatcherTest(ITestOutputHelper output) : base(output) { - public IntegrationEventDispatcherTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Publish_ShouldDispatchIntegrationEvent() - { - var services = new ServiceCollection() - .AddServiceLocator() - .AddSingleton, InMemoryTestStore>() - .AddTransient(); - var provider = services.BuildServiceProvider(); - var sut = new IntegrationEventDispatcher(provider.GetRequiredService()); + [Fact] + public void Publish_ShouldDispatchIntegrationEvent() + { + var services = new ServiceCollection() + .AddServiceLocator() + .AddSingleton, InMemoryTestStore>() + .AddTransient(); + var provider = services.BuildServiceProvider(); + var sut = new IntegrationEventDispatcher(provider.GetRequiredService()); - sut.Publish(new TestIntegrationEvent("published")); + sut.Publish(new TestIntegrationEvent("published")); - Assert.Collection(provider.GetRequiredService>().Query(), item => Assert.Equal("published", item)); - } + Assert.Collection(provider.GetRequiredService>().Query(), item => Assert.Equal("published", item)); + } - private sealed record TestIntegrationEvent(string Value) : IntegrationEvent; + private sealed record TestIntegrationEvent(string Value) : IntegrationEvent; - private sealed class TestIntegrationEventHandler : IntegrationEventHandler - { - private readonly ITestStore _store; + private sealed class TestIntegrationEventHandler : IntegrationEventHandler + { + private readonly ITestStore _store; - public TestIntegrationEventHandler(ITestStore store) - { - _store = store; - } + public TestIntegrationEventHandler(ITestStore store) + { + _store = store; + } - protected override void RegisterDelegates(IFireForgetRegistry handlers) - { - handlers.Register(message => _store.Add(message.Value)); - } + protected override void RegisterDelegates(IFireForgetRegistry handlers) + { + handlers.Register(message => _store.Add(message.Value)); } } } diff --git a/test/Savvyio.EventDriven.Tests/IntegrationEventTest.cs b/test/Savvyio.EventDriven.Tests/IntegrationEventTest.cs index b2e40f8c..91854ecb 100644 --- a/test/Savvyio.EventDriven.Tests/IntegrationEventTest.cs +++ b/test/Savvyio.EventDriven.Tests/IntegrationEventTest.cs @@ -1,62 +1,61 @@ -using System; +using System; using Cuemon.Extensions.Reflection; using Codebelt.Extensions.Xunit; using Savvyio.Assets.EventDriven; using Xunit; -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +public class IntegrationEventTest : Test { - public class IntegrationEventTest : Test + public IntegrationEventTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void IntegrationEvent_AccountCreated_ShouldHaveMetadata_WithSpecifiedEventId() { - public IntegrationEventTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void IntegrationEvent_AccountCreated_ShouldHaveMetadata_WithSpecifiedEventId() - { - var eventId = Guid.NewGuid().ToString("N"); - var id = 100; - var fullname = "Michael Mortensen"; - var email = "root@gimlichael.dev"; - - var sut = new AccountCreated(100, fullname, email).SetEventId(eventId); - - Assert.Equal(email, sut.EmailAddress); - Assert.Equal(fullname, sut.FullName); - Assert.Equal(id, sut.Id); - - Assert.Collection(sut.Metadata, - kvp => - { - Assert.Equal(kvp.Key, MetadataDictionary.MemberType); - Assert.Equal(kvp.Value, sut.GetType().ToFullNameIncludingAssemblyName()); - }, - kvp => - { - Assert.Equal(kvp.Key, MetadataDictionary.EventId); - Assert.Equal(kvp.Value, eventId); - }, - kvp => - { - Assert.Equal(kvp.Key, MetadataDictionary.Timestamp); - Assert.Equal(kvp.Value, sut.GetTimestamp()); - }); - Assert.True(sut.Metadata.Count == 3, "sut.Metadata.Count == 3"); - } - - [Fact] - public void IntegrationEvent_AccountCreated_ShouldExposeMetadataThroughExtensions() - { - var eventId = Guid.NewGuid().ToString("N"); - var timestamp = DateTime.UtcNow; - var sut = new AccountCreated(100, "Michael Mortensen", "root@gimlichael.dev") - .SetEventId(eventId) - .SetTimestamp(timestamp); - - Assert.Equal(eventId, sut.GetEventId()); - Assert.Equal(timestamp, sut.GetTimestamp()); - Assert.Equal(sut.GetType().ToFullNameIncludingAssemblyName(), sut.GetMemberType()); - } + var eventId = Guid.NewGuid().ToString("N"); + var id = 100; + var fullname = "Michael Mortensen"; + var email = "root@gimlichael.dev"; + + var sut = new AccountCreated(100, fullname, email).SetEventId(eventId); + + Assert.Equal(email, sut.EmailAddress); + Assert.Equal(fullname, sut.FullName); + Assert.Equal(id, sut.Id); + + Assert.Collection(sut.Metadata, + kvp => + { + Assert.Equal(kvp.Key, MetadataDictionary.MemberType); + Assert.Equal(kvp.Value, sut.GetType().ToFullNameIncludingAssemblyName()); + }, + kvp => + { + Assert.Equal(kvp.Key, MetadataDictionary.EventId); + Assert.Equal(kvp.Value, eventId); + }, + kvp => + { + Assert.Equal(kvp.Key, MetadataDictionary.Timestamp); + Assert.Equal(kvp.Value, sut.GetTimestamp()); + }); + Assert.True(sut.Metadata.Count == 3, "sut.Metadata.Count == 3"); + } + + [Fact] + public void IntegrationEvent_AccountCreated_ShouldExposeMetadataThroughExtensions() + { + var eventId = Guid.NewGuid().ToString("N"); + var timestamp = DateTime.UtcNow; + var sut = new AccountCreated(100, "Michael Mortensen", "root@gimlichael.dev") + .SetEventId(eventId) + .SetTimestamp(timestamp); + + Assert.Equal(eventId, sut.GetEventId()); + Assert.Equal(timestamp, sut.GetTimestamp()); + Assert.Equal(sut.GetType().ToFullNameIncludingAssemblyName(), sut.GetMemberType()); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/AcknowledgeableTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/AcknowledgeableTest.cs index 10140abc..eb44ffaa 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/AcknowledgeableTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/AcknowledgeableTest.cs @@ -4,31 +4,30 @@ using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +public class AcknowledgeableTest : Test { - public class AcknowledgeableTest : Test + public AcknowledgeableTest(ITestOutputHelper output) : base(output) { - public AcknowledgeableTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task AcknowledgeAsync_ShouldRaiseAcknowledgedEvent_WithMessageProperties() + [Fact] + public async Task AcknowledgeAsync_ShouldRaiseAcknowledgedEvent_WithMessageProperties() + { + var message = new Message("mid1", "urn:members:1".ToUri(), nameof(CreateMemberCommand), new CreateMemberCommand("Jane", 21, "j@j.com")); + AcknowledgedEventArgs acknowledged = null; + message.Properties["messageId"] = message.Id; + message.Acknowledged += (_, e) => { - var message = new Message("mid1", "urn:members:1".ToUri(), nameof(CreateMemberCommand), new CreateMemberCommand("Jane", 21, "j@j.com")); - AcknowledgedEventArgs acknowledged = null; - message.Properties["messageId"] = message.Id; - message.Acknowledged += (_, e) => - { - acknowledged = e; - return Task.CompletedTask; - }; + acknowledged = e; + return Task.CompletedTask; + }; - await message.AcknowledgeAsync(); + await message.AcknowledgeAsync(); - Assert.NotNull(acknowledged); - Assert.Same(message.Properties, acknowledged.Properties); - Assert.Equal(message.Id, acknowledged.Properties["messageId"]); - } + Assert.NotNull(acknowledged); + Assert.Same(message.Properties, acknowledged.Properties); + Assert.Equal(message.Id, acknowledged.Properties["messageId"]); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/CloudEventDictionaryTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/CloudEventDictionaryTest.cs index 799f646a..62a9bfb0 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/CloudEventDictionaryTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/CloudEventDictionaryTest.cs @@ -6,74 +6,73 @@ using Savvyio.Assets.EventDriven; using Xunit; -namespace Savvyio.EventDriven.Messaging.CloudEvents +namespace Savvyio.EventDriven.Messaging.CloudEvents; + +public class CloudEventDictionaryTest : Test { - public class CloudEventDictionaryTest : Test + public CloudEventDictionaryTest(ITestOutputHelper output) : base(output) { - public CloudEventDictionaryTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void IDictionaryMembers_ShouldManageExtensionAttributes() - { - var sut = CreateCloudEvent(); - IDictionary dictionary = sut; - ICollection> collection = dictionary; + [Fact] + public void IDictionaryMembers_ShouldManageExtensionAttributes() + { + var sut = CreateCloudEvent(); + IDictionary dictionary = sut; + ICollection> collection = dictionary; - sut.Add("TenantId", 42); - sut["TraceId"] = "abc123"; + sut.Add("TenantId", 42); + sut["TraceId"] = "abc123"; - Assert.True(sut.ContainsKey("tenantid")); - Assert.True(sut.ContainsKey("traceid")); - Assert.Equal(42, sut["tenantid"]); - Assert.True(sut.TryGetValue("traceid", out var traceId)); - Assert.Equal("abc123", traceId); - Assert.Contains("tenantid", dictionary.Keys); - Assert.Contains("traceid", dictionary.Keys); - Assert.Contains(42, dictionary.Values); - Assert.Contains("abc123", dictionary.Values); - Assert.True(collection.Contains(new KeyValuePair("tenantid", 42))); - var copy = new KeyValuePair[collection.Count]; - collection.CopyTo(copy, 0); - Assert.Contains(copy, kvp => kvp.Key == "tenantid" && Equals(kvp.Value, 42)); - Assert.False(collection.IsReadOnly); - Assert.True(sut.Remove("traceid")); - Assert.False(sut.ContainsKey("traceid")); - } + Assert.True(sut.ContainsKey("tenantid")); + Assert.True(sut.ContainsKey("traceid")); + Assert.Equal(42, sut["tenantid"]); + Assert.True(sut.TryGetValue("traceid", out var traceId)); + Assert.Equal("abc123", traceId); + Assert.Contains("tenantid", dictionary.Keys); + Assert.Contains("traceid", dictionary.Keys); + Assert.Contains(42, dictionary.Values); + Assert.Contains("abc123", dictionary.Values); + Assert.True(collection.Contains(new KeyValuePair("tenantid", 42))); + var copy = new KeyValuePair[collection.Count]; + collection.CopyTo(copy, 0); + Assert.Contains(copy, kvp => kvp.Key == "tenantid" && Equals(kvp.Value, 42)); + Assert.False(collection.IsReadOnly); + Assert.True(sut.Remove("traceid")); + Assert.False(sut.ContainsKey("traceid")); + } - [Fact] - public void EnumeratorsAndClear_ShouldExposeAndResetExtensionAttributes() - { - var sut = CreateCloudEvent(); - IDictionary dictionary = sut; - ICollection> collection = dictionary; + [Fact] + public void EnumeratorsAndClear_ShouldExposeAndResetExtensionAttributes() + { + var sut = CreateCloudEvent(); + IDictionary dictionary = sut; + ICollection> collection = dictionary; - collection.Add(new KeyValuePair("CorrelationId", "corr-1")); - collection.Add(new KeyValuePair("TenantId", 7)); + collection.Add(new KeyValuePair("CorrelationId", "corr-1")); + collection.Add(new KeyValuePair("TenantId", 7)); - var genericItems = dictionary.ToList(); - var nongenericItems = ((IEnumerable)sut).Cast>().ToList(); + var genericItems = dictionary.ToList(); + var nongenericItems = ((IEnumerable)sut).Cast>().ToList(); - Assert.Equal(2, genericItems.Count); - Assert.Equal(genericItems, nongenericItems); - Assert.True(collection.Remove(new KeyValuePair("tenantid", 7))); - Assert.Single(dictionary); + Assert.Equal(2, genericItems.Count); + Assert.Equal(genericItems, nongenericItems); + Assert.True(collection.Remove(new KeyValuePair("tenantid", 7))); + Assert.Single(dictionary); - sut.Clear(); + sut.Clear(); - Assert.Empty(dictionary); - } + Assert.Empty(dictionary); + } - private static CloudEvent CreateCloudEvent() + private static CloudEvent CreateCloudEvent() + { + var message = new MemberCreated("Jane Doe", "jd@office.com").ToMessage("https://savvyio.net/members".ToUri(), nameof(MemberCreated), o => { - var message = new MemberCreated("Jane Doe", "jd@office.com").ToMessage("https://savvyio.net/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "message-id"; - o.Time = new System.DateTime(2024, 1, 1, 0, 0, 0, System.DateTimeKind.Utc); - }); + o.MessageId = "message-id"; + o.Time = new System.DateTime(2024, 1, 1, 0, 0, 0, System.DateTimeKind.Utc); + }); - return (CloudEvent)message.ToCloudEvent(); - } + return (CloudEvent)message.ToCloudEvent(); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/CloudEventTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/CloudEventTest.cs index 67c08451..091a0340 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/CloudEventTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/CloudEventTest.cs @@ -1,37 +1,36 @@ -using System; +using System; using Cuemon.Extensions; using Codebelt.Extensions.Xunit; using Savvyio.Assets.EventDriven; using Xunit; -namespace Savvyio.EventDriven.Messaging.CloudEvents +namespace Savvyio.EventDriven.Messaging.CloudEvents; + +public class CloudEventTest : Test { - public class CloudEventTest : Test + public CloudEventTest(ITestOutputHelper output) : base(output) { - public CloudEventTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void CloudEventsMessage_Initialization_Success() - { - // Arrange - var source = "https://savvyio.net/members".ToUri(); - var data = new AccountCreated(123347285, "John Doe", "jd@savvyio.net"); - var type = "net.savvyio.members.account-created"; - var time = DateTime.UtcNow; - var message = data.ToMessage(source, type); + [Fact] + public void CloudEventsMessage_Initialization_Success() + { + // Arrange + var source = "https://savvyio.net/members".ToUri(); + var data = new AccountCreated(123347285, "John Doe", "jd@savvyio.net"); + var type = "net.savvyio.members.account-created"; + var time = DateTime.UtcNow; + var message = data.ToMessage(source, type); - // Act - var cloudEventsMessage = message.ToCloudEvent(); + // Act + var cloudEventsMessage = message.ToCloudEvent(); - // Assert - Assert.Equal(message.Id, cloudEventsMessage.Id); - Assert.Equal(source.OriginalString, cloudEventsMessage.Source); - Assert.Equal(type, cloudEventsMessage.Type); - Assert.InRange(cloudEventsMessage.Time!.Value, time, time.AddSeconds(1)); - Assert.Equal(data, cloudEventsMessage.Data); - Assert.Equal("1.0", cloudEventsMessage.Specversion); - } + // Assert + Assert.Equal(message.Id, cloudEventsMessage.Id); + Assert.Equal(source.OriginalString, cloudEventsMessage.Source); + Assert.Equal(type, cloudEventsMessage.Type); + Assert.InRange(cloudEventsMessage.Time!.Value, time, time.AddSeconds(1)); + Assert.Equal(data, cloudEventsMessage.Data); + Assert.Equal("1.0", cloudEventsMessage.Specversion); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/Cryptography/CloudEventExtensionsTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/Cryptography/CloudEventExtensionsTest.cs index 9a368080..54e3cb7f 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/Cryptography/CloudEventExtensionsTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/CloudEvents/Cryptography/CloudEventExtensionsTest.cs @@ -5,57 +5,56 @@ using Savvyio.Extensions.Text.Json; using Xunit; -namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography +namespace Savvyio.EventDriven.Messaging.CloudEvents.Cryptography; + +public class CloudEventExtensionsTest : Test { - public class CloudEventExtensionsTest : Test + public CloudEventExtensionsTest(ITestOutputHelper output) : base(output) { - public CloudEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void SignCloudEvent_ShouldCreateSignedCloudEvent_WithVerifiableSignature() - { - var marshaller = new JsonMarshaller(); - var cloudEvent = CreateCloudEvent(); + [Fact] + public void SignCloudEvent_ShouldCreateSignedCloudEvent_WithVerifiableSignature() + { + var marshaller = new JsonMarshaller(); + var cloudEvent = CreateCloudEvent(); - var signed = cloudEvent.SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var signed = cloudEvent.SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Assert.Equal(cloudEvent.Id, signed.Id); - Assert.Equal(cloudEvent.Source, signed.Source); - Assert.Equal(cloudEvent.Type, signed.Type); - Assert.Equal(cloudEvent.Time, signed.Time); - Assert.Equal(cloudEvent.Data, signed.Data); - Assert.Equal(cloudEvent.Specversion, signed.Specversion); - Assert.False(string.IsNullOrWhiteSpace(signed.Signature)); + Assert.Equal(cloudEvent.Id, signed.Id); + Assert.Equal(cloudEvent.Source, signed.Source); + Assert.Equal(cloudEvent.Type, signed.Type); + Assert.Equal(cloudEvent.Time, signed.Time); + Assert.Equal(cloudEvent.Data, signed.Data); + Assert.Equal(cloudEvent.Specversion, signed.Specversion); + Assert.False(string.IsNullOrWhiteSpace(signed.Signature)); - signed.CheckCloudEventSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - } + signed.CheckCloudEventSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + } - [Fact] - public void CheckCloudEventSignature_ShouldThrow_WhenSecretDoesNotMatch() - { - var marshaller = new JsonMarshaller(); - var signed = CreateCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + [Fact] + public void CheckCloudEventSignature_ShouldThrow_WhenSecretDoesNotMatch() + { + var marshaller = new JsonMarshaller(); + var signed = CreateCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var exception = Assert.Throws(() => signed.CheckCloudEventSignature(marshaller, o => o.SignatureSecret = new byte[] { 3, 2, 1 })); + var exception = Assert.Throws(() => signed.CheckCloudEventSignature(marshaller, o => o.SignatureSecret = new byte[] { 3, 2, 1 })); - Assert.Equal(signed.Signature, exception.ActualValue); - } + Assert.Equal(signed.Signature, exception.ActualValue); + } - private static CloudEvent CreateCloudEvent() + private static CloudEvent CreateCloudEvent() + { + var utc = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc); + var @event = new MemberCreated("Jane Doe", "jd@office.com") + .SetEventId("event-123") + .SetTimestamp(utc); + var message = @event.ToMessage("https://api.example.com/members".ToUri(), nameof(MemberCreated), o => { - var utc = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc); - var @event = new MemberCreated("Jane Doe", "jd@office.com") - .SetEventId("event-123") - .SetTimestamp(utc); - var message = @event.ToMessage("https://api.example.com/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "message-123"; - o.Time = utc; - }); - - return (CloudEvent)message.ToCloudEvent(); - } + o.MessageId = "message-123"; + o.Time = utc; + }); + + return (CloudEvent)message.ToCloudEvent(); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/Cryptography/SignedMessageOptionsTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/Cryptography/SignedMessageOptionsTest.cs index bd0db700..1f5f1eaf 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/Cryptography/SignedMessageOptionsTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/Cryptography/SignedMessageOptionsTest.cs @@ -2,36 +2,35 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Messaging.Cryptography +namespace Savvyio.Messaging.Cryptography; + +public class SignedMessageOptionsTest : Test { - public class SignedMessageOptionsTest : Test + public SignedMessageOptionsTest(ITestOutputHelper output) : base(output) { - public SignedMessageOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ValidateOptions_ShouldThrow_WhenSignatureSecretIsNull() - { - var options = new SignedMessageOptions(); + [Fact] + public void ValidateOptions_ShouldThrow_WhenSignatureSecretIsNull() + { + var options = new SignedMessageOptions(); - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_ShouldThrow_WhenSignatureSecretIsEmpty() - { - var options = new SignedMessageOptions { SignatureSecret = Array.Empty() }; + [Fact] + public void ValidateOptions_ShouldThrow_WhenSignatureSecretIsEmpty() + { + var options = new SignedMessageOptions { SignatureSecret = Array.Empty() }; - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_ShouldSucceed_WhenSignatureSecretIsSet() - { - var options = new SignedMessageOptions { SignatureSecret = new byte[] { 1, 2, 3 } }; + [Fact] + public void ValidateOptions_ShouldSucceed_WhenSignatureSecretIsSet() + { + var options = new SignedMessageOptions { SignatureSecret = new byte[] { 1, 2, 3 } }; - options.ValidateOptions(); - } + options.ValidateOptions(); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/Cryptography/SignedMessageTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/Cryptography/SignedMessageTest.cs index 5741b74b..25cd4cd9 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/Cryptography/SignedMessageTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/Cryptography/SignedMessageTest.cs @@ -5,54 +5,53 @@ using Savvyio.Commands.Messaging; using Xunit; -namespace Savvyio.Messaging.Cryptography +namespace Savvyio.Messaging.Cryptography; + +public class SignedMessageTest : Test { - public class SignedMessageTest : Test + public SignedMessageTest(ITestOutputHelper output) : base(output) { - public SignedMessageTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Ctor_ShouldThrowArgumentNullException_WhenMessageIsNull() - { - Assert.Throws(() => new SignedMessage(null, "sig")); - } + [Fact] + public void Ctor_ShouldThrowArgumentNullException_WhenMessageIsNull() + { + Assert.Throws(() => new SignedMessage(null, "sig")); + } - [Fact] - public void Ctor_ShouldThrowArgumentNullException_WhenSignatureIsNull() - { - var message = new CreateMemberCommand("Jane", 21, "j@j.com").ToMessage("https://api.example.com".ToUri(), "test"); + [Fact] + public void Ctor_ShouldThrowArgumentNullException_WhenSignatureIsNull() + { + var message = new CreateMemberCommand("Jane", 21, "j@j.com").ToMessage("https://api.example.com".ToUri(), "test"); - Assert.Throws(() => new SignedMessage(message, null)); - } + Assert.Throws(() => new SignedMessage(message, null)); + } - [Fact] - public void Ctor_ShouldThrowArgumentException_WhenSignatureIsEmpty() - { - var message = new CreateMemberCommand("Jane", 21, "j@j.com").ToMessage("https://api.example.com".ToUri(), "test"); + [Fact] + public void Ctor_ShouldThrowArgumentException_WhenSignatureIsEmpty() + { + var message = new CreateMemberCommand("Jane", 21, "j@j.com").ToMessage("https://api.example.com".ToUri(), "test"); - Assert.Throws(() => new SignedMessage(message, string.Empty)); - } + Assert.Throws(() => new SignedMessage(message, string.Empty)); + } - [Fact] - public void Ctor_ShouldCopyMessageProperties() + [Fact] + public void Ctor_ShouldCopyMessageProperties() + { + var utc = DateTime.UtcNow; + var message = new CreateMemberCommand("Jane", 21, "j@j.com").ToMessage("https://api.example.com".ToUri(), "test", o => { - var utc = DateTime.UtcNow; - var message = new CreateMemberCommand("Jane", 21, "j@j.com").ToMessage("https://api.example.com".ToUri(), "test", o => - { - o.MessageId = "abc123"; - o.Time = utc; - }); - - var signed = new SignedMessage(message, "mysig"); - - Assert.Equal(message.Id, signed.Id); - Assert.Equal(message.Source, signed.Source); - Assert.Equal(message.Type, signed.Type); - Assert.Equal(message.Time, signed.Time); - Assert.Equal(message.Data, signed.Data); - Assert.Equal("mysig", signed.Signature); - } + o.MessageId = "abc123"; + o.Time = utc; + }); + + var signed = new SignedMessage(message, "mysig"); + + Assert.Equal(message.Id, signed.Id); + Assert.Equal(message.Source, signed.Source); + Assert.Equal(message.Type, signed.Type); + Assert.Equal(message.Time, signed.Time); + Assert.Equal(message.Data, signed.Data); + Assert.Equal("mysig", signed.Signature); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/InMemoryEventBusTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/InMemoryEventBusTest.cs index fea300be..0fa0a281 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/InMemoryEventBusTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/InMemoryEventBusTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -12,83 +12,82 @@ using Xunit; using Xunit.v3.Priority; -namespace Savvyio.EventDriven.Messaging +namespace Savvyio.EventDriven.Messaging; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class InMemoryEventBusTest : Test { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class InMemoryEventBusTest : Test + private static readonly InMemoryEventBus Bus = new(); + private static readonly InMemoryTestStore> Comparer = new(); + + public InMemoryEventBusTest(ITestOutputHelper output) : base(output) { - private static readonly InMemoryEventBus Bus = new(); - private static readonly InMemoryTestStore> Comparer = new(); + } - public InMemoryEventBusTest(ITestOutputHelper output) : base(output) - { - } + [Fact, Priority(0)] + public async Task PublishAsync_MemberCreated_OneTime() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = "urn:member-events:1".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(MemberCreated)); - [Fact, Priority(0)] - public async Task PublishAsync_MemberCreated_OneTime() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = "urn:member-events:1".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(MemberCreated)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = Environment.NewLine)); + Comparer.Add(sut3); - Comparer.Add(sut3); + await Bus.PublishAsync(sut3); + } - await Bus.PublishAsync(sut3); - } + [Fact, Priority(1)] + public async Task SubscribeAsync_MemberCreated_OneTime() + { + var sut1 = Comparer.Query(message => message.Source == "urn:member-events:1").Single(); + Bus.SubscribeAsync((sut2, _) => + { + Assert.Equal(sut1.Data, sut2.Data); + Assert.Equal(sut1.Time, sut2.Time); + Assert.Equal(sut1.Source, sut2.Source); + Assert.Equal(sut1.Id, sut2.Id); + Assert.Equal(sut1.Type, sut2.Type); + return Task.CompletedTask; + }); + } - [Fact, Priority(1)] - public async Task SubscribeAsync_MemberCreated_OneTime() + [Fact, Priority(2)] + public async Task PublishAsync_MemberCreated_ThousandTimes() + { + var messages = Generate.RangeOf(1000, i => { - var sut1 = Comparer.Query(message => message.Source == "urn:member-events:1").Single(); - Bus.SubscribeAsync((sut2, _) => - { - Assert.Equal(sut1.Data, sut2.Data); - Assert.Equal(sut1.Time, sut2.Time); - Assert.Equal(sut1.Source, sut2.Source); - Assert.Equal(sut1.Id, sut2.Id); - Assert.Equal(sut1.Type, sut2.Type); - return Task.CompletedTask; - }); - } + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new MemberCreated(Generate.RandomString(10), email).ToMessage($"urn:member-events-many:{i}".ToUri(), nameof(MemberCreated)); + }); - [Fact, Priority(2)] - public async Task PublishAsync_MemberCreated_ThousandTimes() + await ParallelFactory.ForEachAsync(messages, (message, token) => { - var messages = Generate.RangeOf(1000, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new MemberCreated(Generate.RandomString(10), email).ToMessage($"urn:member-events-many:{i}".ToUri(), nameof(MemberCreated)); - }); + Comparer.Add(message); + return Bus.PublishAsync(message, o => o.CancellationToken = token); + }).ConfigureAwait(false); + } - await ParallelFactory.ForEachAsync(messages, (message, token) => - { - Comparer.Add(message); - return Bus.PublishAsync(message, o => o.CancellationToken = token); - }).ConfigureAwait(false); - } + [Fact, Priority(3)] + public async Task SubscribeAsync_MemberCreated_All() + { + var sut1 = Comparer.Query(message => message.Source.StartsWith("urn:member-events-many")).ToList(); + var sut2 = new List>(); + var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - [Fact, Priority(3)] - public async Task SubscribeAsync_MemberCreated_All() + await Bus.SubscribeAsync((message, _) => { - var sut1 = Comparer.Query(message => message.Source.StartsWith("urn:member-events-many")).ToList(); - var sut2 = new List>(); - var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - - await Bus.SubscribeAsync((message, _) => - { - sut2.Add(message); - return Task.CompletedTask; - }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); + sut2.Add(message); + return Task.CompletedTask; + }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); - TestOutput.WriteLine(sut2.Count.ToString()); - TestOutput.WriteLines(sut2.Take(10)); + TestOutput.WriteLine(sut2.Count.ToString()); + TestOutput.WriteLines(sut2.Take(10)); - Assert.Equal(sut1.Count, sut2.Count); - Assert.Equal(sut1, sut2); - Assert.Equal(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); - Assert.Equal(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); - } + Assert.Equal(sut1.Count, sut2.Count); + Assert.Equal(sut1, sut2); + Assert.Equal(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); + Assert.Equal(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/IntegrationEventExtensionsTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/IntegrationEventExtensionsTest.cs index 9eec3339..80f04ae5 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/IntegrationEventExtensionsTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/IntegrationEventExtensionsTest.cs @@ -1,31 +1,30 @@ -using Cuemon.Extensions; +using Cuemon.Extensions; using Cuemon.Extensions.Reflection; using Codebelt.Extensions.Xunit; using Savvyio.Assets.EventDriven; using Xunit; -namespace Savvyio.EventDriven.Messaging +namespace Savvyio.EventDriven.Messaging; + +public class IntegrationEventExtensionsTest : Test { - public class IntegrationEventExtensionsTest : Test + public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) { - public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void EncloseToMessage_CreateMemberCommand_ShouldBeWrappedInMessage() - { - var sut1 = new MemberCreated("Jane Doe", "jd@office.com"); - var sut2 = "urn:member-events:1".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(MemberCreated)); + [Fact] + public void EncloseToMessage_CreateMemberCommand_ShouldBeWrappedInMessage() + { + var sut1 = new MemberCreated("Jane Doe", "jd@office.com"); + var sut2 = "urn:member-events:1".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(MemberCreated)); - Assert.Equal(sut1.EmailAddress, sut3.Data.EmailAddress); - Assert.Equal(sut1.Name, sut3.Data.Name); - Assert.NotNull(sut3.Id); - Assert.NotNull(sut3.Time); - Assert.Equal(sut1.GetType().ToFullNameIncludingAssemblyName(), sut3.Data.GetMemberType()); - Assert.Equal(nameof(MemberCreated), sut3.Type); - Assert.Equal(sut2.OriginalString, sut3.Source); - } + Assert.Equal(sut1.EmailAddress, sut3.Data.EmailAddress); + Assert.Equal(sut1.Name, sut3.Data.Name); + Assert.NotNull(sut3.Id); + Assert.NotNull(sut3.Time); + Assert.Equal(sut1.GetType().ToFullNameIncludingAssemblyName(), sut3.Data.GetMemberType()); + Assert.Equal(nameof(MemberCreated), sut3.Type); + Assert.Equal(sut2.OriginalString, sut3.Source); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/MessageAsyncEnumerableOptionsTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/MessageAsyncEnumerableOptionsTest.cs index fc7fb3a0..7362893c 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/MessageAsyncEnumerableOptionsTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/MessageAsyncEnumerableOptionsTest.cs @@ -6,55 +6,54 @@ using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +public class MessageAsyncEnumerableOptionsTest : Test { - public class MessageAsyncEnumerableOptionsTest : Test + public MessageAsyncEnumerableOptionsTest(ITestOutputHelper output) : base(output) { - public MessageAsyncEnumerableOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Ctor_ShouldInitializeAcknowledgedProperties() - { - var options = new MessageAsyncEnumerableOptions(); + [Fact] + public void Ctor_ShouldInitializeAcknowledgedProperties() + { + var options = new MessageAsyncEnumerableOptions(); - Assert.IsType>>(options.AcknowledgedProperties); - } + Assert.IsType>>(options.AcknowledgedProperties); + } - [Fact] - public void ValidateOptions_ShouldThrow_WhenAcknowledgedPropertiesIsNull() + [Fact] + public void ValidateOptions_ShouldThrow_WhenAcknowledgedPropertiesIsNull() + { + var options = new MessageAsyncEnumerableOptions { - var options = new MessageAsyncEnumerableOptions - { - AcknowledgedProperties = null, - MessageCallback = _ => Task.CompletedTask - }; + AcknowledgedProperties = null, + MessageCallback = _ => Task.CompletedTask + }; - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_ShouldThrow_WhenMessageCallbackIsNull() + [Fact] + public void ValidateOptions_ShouldThrow_WhenMessageCallbackIsNull() + { + var options = new MessageAsyncEnumerableOptions { - var options = new MessageAsyncEnumerableOptions - { - AcknowledgedProperties = new ConcurrentBag>() - }; + AcknowledgedProperties = new ConcurrentBag>() + }; - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_ShouldSucceed_WhenRequiredPropertiesAreSet() + [Fact] + public void ValidateOptions_ShouldSucceed_WhenRequiredPropertiesAreSet() + { + var options = new MessageAsyncEnumerableOptions { - var options = new MessageAsyncEnumerableOptions - { - AcknowledgedProperties = new ConcurrentBag>(), - MessageCallback = _ => Task.CompletedTask - }; - - options.ValidateOptions(); - } + AcknowledgedProperties = new ConcurrentBag>(), + MessageCallback = _ => Task.CompletedTask + }; + + options.ValidateOptions(); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/MessageAsyncEnumerableTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/MessageAsyncEnumerableTest.cs index 4ef826e1..3c398137 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/MessageAsyncEnumerableTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/MessageAsyncEnumerableTest.cs @@ -7,55 +7,54 @@ using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +public class MessageAsyncEnumerableTest : Test { - public class MessageAsyncEnumerableTest : Test + public MessageAsyncEnumerableTest(ITestOutputHelper output) : base(output) { - public MessageAsyncEnumerableTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Ctor_ShouldThrowArgumentNullException_WhenSourceIsNull() - { - Assert.Throws(() => new MessageAsyncEnumerable((IAsyncEnumerable>)null)); - } + [Fact] + public void Ctor_ShouldThrowArgumentNullException_WhenSourceIsNull() + { + Assert.Throws(() => new MessageAsyncEnumerable((IAsyncEnumerable>)null)); + } - [Fact] - public async Task GetAsyncEnumerator_ShouldInvokeCallbacks_AndCollectAcknowledgedProperties() + [Fact] + public async Task GetAsyncEnumerator_ShouldInvokeCallbacks_AndCollectAcknowledgedProperties() + { + var callbacks = new List(); + List> acknowledged = null; + var utc = DateTime.UtcNow; + var source = new IMessage[] + { + new Message("mid1", "urn:members:1".ToUri(), nameof(CreateMemberCommand), new CreateMemberCommand("Jane", 21, "jane@example.com"), utc), + new Message("mid2", "urn:members:2".ToUri(), nameof(CreateMemberCommand), new CreateMemberCommand("John", 22, "john@example.com"), utc) + }; + var sut = new MessageAsyncEnumerable(source, o => { - var callbacks = new List(); - List> acknowledged = null; - var utc = DateTime.UtcNow; - var source = new IMessage[] + o.MessageCallback = async message => { - new Message("mid1", "urn:members:1".ToUri(), nameof(CreateMemberCommand), new CreateMemberCommand("Jane", 21, "jane@example.com"), utc), - new Message("mid2", "urn:members:2".ToUri(), nameof(CreateMemberCommand), new CreateMemberCommand("John", 22, "john@example.com"), utc) + callbacks.Add(message.Id); + message.Properties["messageId"] = message.Id; + await message.AcknowledgeAsync(); }; - var sut = new MessageAsyncEnumerable(source, o => - { - o.MessageCallback = async message => - { - callbacks.Add(message.Id); - message.Properties["messageId"] = message.Id; - await message.AcknowledgeAsync(); - }; - o.AcknowledgedPropertiesCallback = properties => - { - acknowledged = properties.ToList(); - return Task.CompletedTask; - }; - }); - - await foreach (var message in sut) + o.AcknowledgedPropertiesCallback = properties => { - Assert.NotNull(message); - } + acknowledged = properties.ToList(); + return Task.CompletedTask; + }; + }); - Assert.Equal(new[] { "mid1", "mid2" }, callbacks); - Assert.NotNull(acknowledged); - Assert.Equal(2, acknowledged.Count); - Assert.Equal(new[] { "mid1", "mid2" }, acknowledged.Select(p => p["messageId"]).Cast().OrderBy(id => id).ToArray()); + await foreach (var message in sut) + { + Assert.NotNull(message); } + + Assert.Equal(new[] { "mid1", "mid2" }, callbacks); + Assert.NotNull(acknowledged); + Assert.Equal(2, acknowledged.Count); + Assert.Equal(new[] { "mid1", "mid2" }, acknowledged.Select(p => p["messageId"]).Cast().OrderBy(id => id).ToArray()); } } diff --git a/test/Savvyio.EventDriven.Tests/Messaging/MessageOptionsTest.cs b/test/Savvyio.EventDriven.Tests/Messaging/MessageOptionsTest.cs index 6f5c04e4..301b4cf3 100644 --- a/test/Savvyio.EventDriven.Tests/Messaging/MessageOptionsTest.cs +++ b/test/Savvyio.EventDriven.Tests/Messaging/MessageOptionsTest.cs @@ -2,36 +2,35 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Messaging +namespace Savvyio.Messaging; + +public class MessageOptionsTest : Test { - public class MessageOptionsTest : Test + public MessageOptionsTest(ITestOutputHelper output) : base(output) { - public MessageOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ValidateOptions_ShouldThrow_WhenMessageIdIsNull() - { - var options = new MessageOptions { MessageId = null }; + [Fact] + public void ValidateOptions_ShouldThrow_WhenMessageIdIsNull() + { + var options = new MessageOptions { MessageId = null }; - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_ShouldThrow_WhenTimeIsNotUtc() - { - var options = new MessageOptions { Time = DateTime.Now }; + [Fact] + public void ValidateOptions_ShouldThrow_WhenTimeIsNotUtc() + { + var options = new MessageOptions { Time = DateTime.Now }; - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_ShouldSucceed_WhenValid() - { - var options = new MessageOptions { MessageId = "abc", Time = DateTime.UtcNow }; + [Fact] + public void ValidateOptions_ShouldSucceed_WhenValid() + { + var options = new MessageOptions { MessageId = "abc", Time = DateTime.UtcNow }; - options.ValidateOptions(); - } + options.ValidateOptions(); } } diff --git a/test/Savvyio.EventDriven.Tests/SavvyioOptionsExtensionsTest.cs b/test/Savvyio.EventDriven.Tests/SavvyioOptionsExtensionsTest.cs index b080cc66..6aa2266b 100644 --- a/test/Savvyio.EventDriven.Tests/SavvyioOptionsExtensionsTest.cs +++ b/test/Savvyio.EventDriven.Tests/SavvyioOptionsExtensionsTest.cs @@ -1,43 +1,42 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Savvyio.Assets; using Xunit; -namespace Savvyio.EventDriven +namespace Savvyio.EventDriven; + +public class SavvyioOptionsExtensionsTest : Test { - public class SavvyioOptionsExtensionsTest : Test + public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) { - public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() - { - var sut = new SavvyioOptions().AddHandlers(); - Assert.Empty(sut.HandlerServiceTypes); - Assert.Empty(sut.HandlerImplementationTypes); - } + [Fact] + public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() + { + var sut = new SavvyioOptions().AddHandlers(); + Assert.Empty(sut.HandlerServiceTypes); + Assert.Empty(sut.HandlerImplementationTypes); + } - [Fact] - public void AddIntegrationEventHandler_ShouldAddDomainEventHandler() - { - var sut = new SavvyioOptions().AddIntegrationEventHandler(); + [Fact] + public void AddIntegrationEventHandler_ShouldAddDomainEventHandler() + { + var sut = new SavvyioOptions().AddIntegrationEventHandler(); - Assert.NotEmpty(sut.HandlerServiceTypes); - Assert.NotEmpty(sut.HandlerImplementationTypes); - Assert.Collection(sut.HandlerServiceTypes, type => Assert.Equal(typeof(IIntegrationEventHandler), type)); - Assert.Collection(sut.HandlerImplementationTypes, type => Assert.Equal(typeof(AccountEventHandler), type)); - } + Assert.NotEmpty(sut.HandlerServiceTypes); + Assert.NotEmpty(sut.HandlerImplementationTypes); + Assert.Collection(sut.HandlerServiceTypes, type => Assert.Equal(typeof(IIntegrationEventHandler), type)); + Assert.Collection(sut.HandlerImplementationTypes, type => Assert.Equal(typeof(AccountEventHandler), type)); + } - [Fact] - public void AddIntegrationEventDispatcher_ShouldAddDomainEventDispatcher() - { - var sut = new SavvyioOptions().AddIntegrationEventDispatcher(); + [Fact] + public void AddIntegrationEventDispatcher_ShouldAddDomainEventDispatcher() + { + var sut = new SavvyioOptions().AddIntegrationEventDispatcher(); - Assert.NotEmpty(sut.DispatcherServiceTypes); - Assert.NotEmpty(sut.DispatcherImplementationTypes); - Assert.Collection(sut.DispatcherServiceTypes, type => Assert.Equal(typeof(IIntegrationEventDispatcher), type)); - Assert.Collection(sut.DispatcherImplementationTypes, type => Assert.Equal(typeof(IntegrationEventDispatcher), type)); - } + Assert.NotEmpty(sut.DispatcherServiceTypes); + Assert.NotEmpty(sut.DispatcherImplementationTypes); + Assert.Collection(sut.DispatcherServiceTypes, type => Assert.Equal(typeof(IIntegrationEventDispatcher), type)); + Assert.Collection(sut.DispatcherImplementationTypes, type => Assert.Equal(typeof(IntegrationEventDispatcher), type)); } } diff --git a/test/Savvyio.Extensions.Dapper.Tests/Assets/AccountData.cs b/test/Savvyio.Extensions.Dapper.Tests/Assets/AccountData.cs index 5e971740..303f607d 100644 --- a/test/Savvyio.Extensions.Dapper.Tests/Assets/AccountData.cs +++ b/test/Savvyio.Extensions.Dapper.Tests/Assets/AccountData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon.Extensions; @@ -6,38 +6,37 @@ using Dapper; using Savvyio.Assets.Domain; -namespace Savvyio.Extensions.Dapper.Assets +namespace Savvyio.Extensions.Dapper.Assets; + +public class AccountData : DapperDataStore { - public class AccountData : DapperDataStore + public AccountData(IDapperDataSource source) : base(source) { - public AccountData(IDapperDataSource source) : base(source) - { - } + } - public override Task CreateAsync(Account dto, Action setup = null) - { - return Source.ExecuteAsync("INSERT INTO AccountProjection (FullName, EmailAddress) VALUES (@FullName, @EmailAddress)", dto); - } + public override Task CreateAsync(Account dto, Action setup = null) + { + return Source.ExecuteAsync("INSERT INTO AccountProjection (FullName, EmailAddress) VALUES (@FullName, @EmailAddress)", dto); + } - public override Task UpdateAsync(Account dto, Action setup = null) - { - return Source.ExecuteAsync("UPDATE AccountProjection SET FullName = @FullName WHERE Id = @Id", dto); - } + public override Task UpdateAsync(Account dto, Action setup = null) + { + return Source.ExecuteAsync("UPDATE AccountProjection SET FullName = @FullName WHERE Id = @Id", dto); + } - public override Task GetByIdAsync(object id, Action setup) - { - return Source.QuerySingleOrDefaultAsync("SELECT * FROM AccountProjection WHERE Id = @Id", id); - } + public override Task GetByIdAsync(object id, Action setup) + { + return Source.QuerySingleOrDefaultAsync("SELECT * FROM AccountProjection WHERE Id = @Id", id); + } - public override async Task> FindAllAsync(Action setup = null) - { - var cd = setup.Configure(); - return setup == null ? await Source.QueryAsync("SELECT * FROM AccountProjection").ConfigureAwait(false) : await Source.QueryAsync(cd).ConfigureAwait(false); - } + public override async Task> FindAllAsync(Action setup = null) + { + var cd = setup.Configure(); + return setup == null ? await Source.QueryAsync("SELECT * FROM AccountProjection").ConfigureAwait(false) : await Source.QueryAsync(cd).ConfigureAwait(false); + } - public override Task DeleteAsync(Account dto, Action setup = null) - { - return Source.ExecuteAsync("DELETE FROM AccountProjection WHERE Id = @Id", dto); - } + public override Task DeleteAsync(Account dto, Action setup = null) + { + return Source.ExecuteAsync("DELETE FROM AccountProjection WHERE Id = @Id", dto); } } diff --git a/test/Savvyio.Extensions.Dapper.Tests/DapperDataSourceOptionsTest.cs b/test/Savvyio.Extensions.Dapper.Tests/DapperDataSourceOptionsTest.cs index c105b4bd..6d14a65b 100644 --- a/test/Savvyio.Extensions.Dapper.Tests/DapperDataSourceOptionsTest.cs +++ b/test/Savvyio.Extensions.Dapper.Tests/DapperDataSourceOptionsTest.cs @@ -1,34 +1,33 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Microsoft.Data.Sqlite; using Xunit; -namespace Savvyio.Extensions.Dapper +namespace Savvyio.Extensions.Dapper; + +public class DapperDataSourceOptionsTest : Test { - public class DapperDataSourceOptionsTest : Test + public DapperDataSourceOptionsTest(ITestOutputHelper output) : base(output) { - public DapperDataSourceOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void DapperDataSourceOptions_Ensure_Initialization_Defaults() - { - var sut = new DapperDataSourceOptions(); + [Fact] + public void DapperDataSourceOptions_Ensure_Initialization_Defaults() + { + var sut = new DapperDataSourceOptions(); - Assert.Null(sut.ConnectionFactory); - } + Assert.Null(sut.ConnectionFactory); + } - [Fact] - public void DapperDataSourceOptions_ValidateOptions_ShouldPassWhenConnectionFactoryIsSet() + [Fact] + public void DapperDataSourceOptions_ValidateOptions_ShouldPassWhenConnectionFactoryIsSet() + { + var sut = new DapperDataSourceOptions { - var sut = new DapperDataSourceOptions - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") - }; + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") + }; - var exception = Record.Exception(sut.ValidateOptions); + var exception = Record.Exception(sut.ValidateOptions); - Assert.Null(exception); - } + Assert.Null(exception); } } diff --git a/test/Savvyio.Extensions.Dapper.Tests/DapperDataSourceTest.cs b/test/Savvyio.Extensions.Dapper.Tests/DapperDataSourceTest.cs index 2dc4c21a..e0dcbbf0 100644 --- a/test/Savvyio.Extensions.Dapper.Tests/DapperDataSourceTest.cs +++ b/test/Savvyio.Extensions.Dapper.Tests/DapperDataSourceTest.cs @@ -1,123 +1,122 @@ -using System; +using System; using System.Collections.Generic; using System.Data; using Codebelt.Extensions.Xunit; using Microsoft.Data.Sqlite; using Xunit; -namespace Savvyio.Extensions.Dapper +namespace Savvyio.Extensions.Dapper; + +public class DapperDataSourceTest : Test { - public class DapperDataSourceTest : Test + public DapperDataSourceTest(ITestOutputHelper output) : base(output) { - public DapperDataSourceTest(ITestOutputHelper output) : base(output) - { - } + } - [Theory] - [MemberData(nameof(GetDapperDataSourceOptions))] - public void DapperDataSource_ShouldFailWithArgumentNullException(DapperDataSourceOptions options) - { - var sut = options == null - ? Assert.Throws(() => new DapperDataSource(options)) - : Assert.Throws(() => new DapperDataSource(options)); - TestOutput.WriteLine(sut.ToString()); - } + [Theory] + [MemberData(nameof(GetDapperDataSourceOptions))] + public void DapperDataSource_ShouldFailWithArgumentNullException(DapperDataSourceOptions options) + { + var sut = options == null + ? Assert.Throws(() => new DapperDataSource(options)) + : Assert.Throws(() => new DapperDataSource(options)); + TestOutput.WriteLine(sut.ToString()); + } - private static IEnumerable GetDapperDataSourceOptions() + private static IEnumerable GetDapperDataSourceOptions() + { + return new List { - return new List - { - new object[] { null }, - new object[] { new DapperDataSourceOptions() } - }; - } + new object[] { null }, + new object[] { new DapperDataSourceOptions() } + }; + } - [Fact] - public void DapperDataSource_ShouldFailWithInvalidOperationException() + [Fact] + public void DapperDataSource_ShouldFailWithInvalidOperationException() + { + var options = new DapperDataSourceOptions() { - var options = new DapperDataSourceOptions() + ConnectionFactory = () => { - ConnectionFactory = () => - { - var cnn = new SqliteConnection("Data Source=:memory:"); - cnn.Open(); - return cnn; - } - }; - var sut = new DapperDataSource(options); - sut.Dispose(); - - Assert.Throws(() => sut.BeginTransaction()); - Assert.True(sut.Disposed); - } + var cnn = new SqliteConnection("Data Source=:memory:"); + cnn.Open(); + return cnn; + } + }; + var sut = new DapperDataSource(options); + sut.Dispose(); - [Fact] - public void DapperDataSource_ShouldExposeConnectionMembers() + Assert.Throws(() => sut.BeginTransaction()); + Assert.True(sut.Disposed); + } + + [Fact] + public void DapperDataSource_ShouldExposeConnectionMembers() + { + var sut = new DapperDataSource(new DapperDataSourceOptions { - var sut = new DapperDataSource(new DapperDataSourceOptions - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") - }); + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") + }); - Assert.Equal(ConnectionState.Open, sut.State); - Assert.NotNull(sut.CreateCommand()); - Assert.Equal("Data Source=:memory:", sut.ConnectionString); + Assert.Equal(ConnectionState.Open, sut.State); + Assert.NotNull(sut.CreateCommand()); + Assert.Equal("Data Source=:memory:", sut.ConnectionString); - using (var transaction = sut.BeginTransaction()) - { - Assert.NotNull(transaction); - transaction.Rollback(); - } + using (var transaction = sut.BeginTransaction()) + { + Assert.NotNull(transaction); + transaction.Rollback(); + } - Assert.Throws(() => sut.ChangeDatabase("main")); - Assert.Equal("main", sut.Database); + Assert.Throws(() => sut.ChangeDatabase("main")); + Assert.Equal("main", sut.Database); - sut.Close(); - Assert.Equal(ConnectionState.Closed, sut.State); + sut.Close(); + Assert.Equal(ConnectionState.Closed, sut.State); - sut.Open(); - Assert.Equal(ConnectionState.Open, sut.State); - } + sut.Open(); + Assert.Equal(ConnectionState.Open, sut.State); + } - [Fact] - public void DapperDataSource_ShouldSupportBeginTransactionWithIsolationLevel() + [Fact] + public void DapperDataSource_ShouldSupportBeginTransactionWithIsolationLevel() + { + var sut = new DapperDataSource(new DapperDataSourceOptions { - var sut = new DapperDataSource(new DapperDataSourceOptions - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") - }); + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") + }); - using var transaction = sut.BeginTransaction(IsolationLevel.ReadCommitted); + using var transaction = sut.BeginTransaction(IsolationLevel.ReadCommitted); - Assert.NotNull(transaction); - } + Assert.NotNull(transaction); + } - [Fact] - public void DapperDataSource_ShouldExposeConnectionTimeout() + [Fact] + public void DapperDataSource_ShouldExposeConnectionTimeout() + { + var sut = new DapperDataSource(new DapperDataSourceOptions { - var sut = new DapperDataSource(new DapperDataSourceOptions - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") - }); + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") + }); - var timeout = sut.ConnectionTimeout; + var timeout = sut.ConnectionTimeout; - Assert.True(timeout >= 0); - TestOutput.WriteLine($"ConnectionTimeout: {timeout}"); - } + Assert.True(timeout >= 0); + TestOutput.WriteLine($"ConnectionTimeout: {timeout}"); + } - [Fact] - public void DapperDataSource_ShouldSupportConnectionStringSetter() + [Fact] + public void DapperDataSource_ShouldSupportConnectionStringSetter() + { + var sut = new DapperDataSource(new DapperDataSourceOptions { - var sut = new DapperDataSource(new DapperDataSourceOptions - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") - }); + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:") + }); - sut.Close(); - sut.ConnectionString = "Data Source=:memory:"; + sut.Close(); + sut.ConnectionString = "Data Source=:memory:"; - Assert.Equal("Data Source=:memory:", sut.ConnectionString); - } + Assert.Equal("Data Source=:memory:", sut.ConnectionString); } } diff --git a/test/Savvyio.Extensions.Dapper.Tests/DapperDataStoreTest.cs b/test/Savvyio.Extensions.Dapper.Tests/DapperDataStoreTest.cs index a3ddc3cc..672c7a46 100644 --- a/test/Savvyio.Extensions.Dapper.Tests/DapperDataStoreTest.cs +++ b/test/Savvyio.Extensions.Dapper.Tests/DapperDataStoreTest.cs @@ -7,109 +7,108 @@ using Savvyio.Extensions.Dapper.Assets; using Xunit; -namespace Savvyio.Extensions.Dapper +namespace Savvyio.Extensions.Dapper; + +public class DapperDataStoreTest : Test { - public class DapperDataStoreTest : Test + public DapperDataStoreTest(ITestOutputHelper output) : base(output) { - public DapperDataStoreTest(ITestOutputHelper output) : base(output) - { - } + } + + [Fact] + public async Task DapperDataAccessObject_ShouldCreateObject() + { + var id = Guid.NewGuid(); + var name = "Test"; + var email = "test@unit.test"; - [Fact] - public async Task DapperDataAccessObject_ShouldCreateObject() + var options = new DapperDataSourceOptions() { - var id = Guid.NewGuid(); - var name = "Test"; - var email = "test@unit.test"; + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() + }; - var options = new DapperDataSourceOptions() - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() - }; + var sut1 = new DapperDataSource(options); - var sut1 = new DapperDataSource(options); + var sut2 = new AccountData(sut1); + await sut2.CreateAsync(new Account(id, name, email)); - var sut2 = new AccountData(sut1); - await sut2.CreateAsync(new Account(id, name, email)); + var sut3 = await sut2.FindAllAsync(o => o.CommandText = "SELECT * FROM AccountProjection WHERE Id = 1").SingleOrDefaultAsync(); - var sut3 = await sut2.FindAllAsync(o => o.CommandText = "SELECT * FROM AccountProjection WHERE Id = 1").SingleOrDefaultAsync(); + sut2.Dispose(); - sut2.Dispose(); + Assert.True(sut1.Disposed, "sut1.Disposed"); + Assert.True(sut2.Disposed, "sut2.Disposed"); + Assert.Equal(name, sut3.FullName); + Assert.Equal(email, sut3.EmailAddress); + } - Assert.True(sut1.Disposed, "sut1.Disposed"); - Assert.True(sut2.Disposed, "sut2.Disposed"); - Assert.Equal(name, sut3.FullName); - Assert.Equal(email, sut3.EmailAddress); - } + [Fact] + public async Task DapperDataAccessObject_ShouldRemoveObject() + { + var id = Guid.NewGuid(); + var name = "Test"; + var email = "test@unit.test"; + var dto = new Account(id, name, email); - [Fact] - public async Task DapperDataAccessObject_ShouldRemoveObject() + var options = new DapperDataSourceOptions() { - var id = Guid.NewGuid(); - var name = "Test"; - var email = "test@unit.test"; - var dto = new Account(id, name, email); + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() + }; - var options = new DapperDataSourceOptions() - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() - }; + var sut1 = new DapperDataSource(options); - var sut1 = new DapperDataSource(options); + var sut2 = new AccountData(sut1); + await sut2.CreateAsync(dto); - var sut2 = new AccountData(sut1); - await sut2.CreateAsync(dto); + var sut3 = await sut2.FindAllAsync(o => o.CommandText = "SELECT * FROM AccountProjection WHERE Id = 1").SingleOrDefaultAsync(); - var sut3 = await sut2.FindAllAsync(o => o.CommandText = "SELECT * FROM AccountProjection WHERE Id = 1").SingleOrDefaultAsync(); + await sut2.DeleteAsync(sut3); - await sut2.DeleteAsync(sut3); + var sut4 = await sut2.FindAllAsync(o => o.CommandText = "SELECT * FROM AccountProjection"); - var sut4 = await sut2.FindAllAsync(o => o.CommandText = "SELECT * FROM AccountProjection"); + sut2.Dispose(); - sut2.Dispose(); + Assert.True(sut1.Disposed, "sut1.Disposed"); + Assert.True(sut2.Disposed, "sut2.Disposed"); + Assert.NotNull(sut3); + Assert.Empty(sut4); + } - Assert.True(sut1.Disposed, "sut1.Disposed"); - Assert.True(sut2.Disposed, "sut2.Disposed"); - Assert.NotNull(sut3); - Assert.Empty(sut4); - } + [Fact] + public async Task DapperDataAccessObject_ShouldUpdateObject() + { + var id = Guid.NewGuid(); + var name = "Test"; + var newName = "Unit Test"; + var email = "test@unit.test"; + var dto = new Account(id, name, email); - [Fact] - public async Task DapperDataAccessObject_ShouldUpdateObject() + var sut1 = new DapperDataSource(new DapperDataSourceOptions() { - var id = Guid.NewGuid(); - var name = "Test"; - var newName = "Unit Test"; - var email = "test@unit.test"; - var dto = new Account(id, name, email); + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() + }); - var sut1 = new DapperDataSource(new DapperDataSourceOptions() - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() - }); + var sut2 = new AccountData(sut1); + await sut2.CreateAsync(dto); - var sut2 = new AccountData(sut1); - await sut2.CreateAsync(dto); + var sut3 = await sut2.FindAllAsync(o => o.CommandText = "SELECT * FROM AccountProjection WHERE Id = 1").SingleOrDefaultAsync(); - var sut3 = await sut2.FindAllAsync(o => o.CommandText = "SELECT * FROM AccountProjection WHERE Id = 1").SingleOrDefaultAsync(); + sut3.ChangeFullName(newName); - sut3.ChangeFullName(newName); + await sut2.UpdateAsync(sut3); - await sut2.UpdateAsync(sut3); - - var sut4 = await sut2.FindAllAsync(o => - { - o.Parameters = new { sut3.Id }; - o.CommandText = "SELECT * FROM AccountProjection WHERE Id = @Id"; - }).SingleOrDefaultAsync(); + var sut4 = await sut2.FindAllAsync(o => + { + o.Parameters = new { sut3.Id }; + o.CommandText = "SELECT * FROM AccountProjection WHERE Id = @Id"; + }).SingleOrDefaultAsync(); - sut2.Dispose(); + sut2.Dispose(); - Assert.True(sut1.Disposed, "sut1.Disposed"); - Assert.True(sut2.Disposed, "sut2.Disposed"); - Assert.NotEqual(name, sut3.FullName); - Assert.Equal(newName, sut4.FullName); - } + Assert.True(sut1.Disposed, "sut1.Disposed"); + Assert.True(sut2.Disposed, "sut2.Disposed"); + Assert.NotEqual(name, sut3.FullName); + Assert.Equal(newName, sut4.FullName); } } diff --git a/test/Savvyio.Extensions.Dapper.Tests/DapperQueryOptionsTest.cs b/test/Savvyio.Extensions.Dapper.Tests/DapperQueryOptionsTest.cs index 73cb373f..39a232c2 100644 --- a/test/Savvyio.Extensions.Dapper.Tests/DapperQueryOptionsTest.cs +++ b/test/Savvyio.Extensions.Dapper.Tests/DapperQueryOptionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Data; using System.Threading; using Cuemon.Extensions; @@ -7,65 +7,64 @@ using Microsoft.Data.Sqlite; using Xunit; -namespace Savvyio.Extensions.Dapper +namespace Savvyio.Extensions.Dapper; + +public class DapperQueryOptionsTest : Test { - public class DapperQueryOptionsTest : Test + public DapperQueryOptionsTest(ITestOutputHelper output) : base(output) { - public DapperQueryOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void DapperOptions_Ensure_Initialization_Defaults() - { - var sut = new DapperQueryOptions(); + [Fact] + public void DapperOptions_Ensure_Initialization_Defaults() + { + var sut = new DapperQueryOptions(); - var cd = (CommandDefinition)sut; + var cd = (CommandDefinition)sut; - Assert.Null(sut.CommandText); - Assert.Equal(CommandFlags.Buffered, sut.CommandFlags); - Assert.Equal(TimeSpan.FromSeconds(30), sut.CommandTimeout); - Assert.Equal(CommandType.Text, sut.CommandType); - Assert.Null(sut.Parameters); - Assert.Null(sut.Transaction); - Assert.Equal(default(CancellationToken), sut.CancellationToken); - Assert.Equal(sut.CommandText, cd.CommandText); - Assert.Equal(sut.CommandFlags, cd.Flags); - Assert.Equal(sut.CommandTimeout.TotalSeconds.As(), cd.CommandTimeout); - Assert.Equal(sut.CommandType, cd.CommandType); - Assert.Equal(sut.Parameters, cd.Parameters); - Assert.Equal(sut.Transaction, cd.Transaction); + Assert.Null(sut.CommandText); + Assert.Equal(CommandFlags.Buffered, sut.CommandFlags); + Assert.Equal(TimeSpan.FromSeconds(30), sut.CommandTimeout); + Assert.Equal(CommandType.Text, sut.CommandType); + Assert.Null(sut.Parameters); + Assert.Null(sut.Transaction); + Assert.Equal(default(CancellationToken), sut.CancellationToken); + Assert.Equal(sut.CommandText, cd.CommandText); + Assert.Equal(sut.CommandFlags, cd.Flags); + Assert.Equal(sut.CommandTimeout.TotalSeconds.As(), cd.CommandTimeout); + Assert.Equal(sut.CommandType, cd.CommandType); + Assert.Equal(sut.Parameters, cd.Parameters); + Assert.Equal(sut.Transaction, cd.Transaction); - } + } - [Fact] - public void DapperOptions_ShouldPreserveCustomValuesInImplicitConversion() + [Fact] + public void DapperOptions_ShouldPreserveCustomValuesInImplicitConversion() + { + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + using var transaction = connection.BeginTransaction(); + using var cts = new CancellationTokenSource(); + var parameters = new { Id = 42 }; + var sut = new DapperQueryOptions { - using var connection = new SqliteConnection("Data Source=:memory:"); - connection.Open(); - using var transaction = connection.BeginTransaction(); - using var cts = new CancellationTokenSource(); - var parameters = new { Id = 42 }; - var sut = new DapperQueryOptions - { - CommandText = "SELECT 1", - CommandFlags = CommandFlags.NoCache, - CommandTimeout = TimeSpan.FromSeconds(12), - CommandType = CommandType.StoredProcedure, - Parameters = parameters, - Transaction = transaction, - CancellationToken = cts.Token - }; + CommandText = "SELECT 1", + CommandFlags = CommandFlags.NoCache, + CommandTimeout = TimeSpan.FromSeconds(12), + CommandType = CommandType.StoredProcedure, + Parameters = parameters, + Transaction = transaction, + CancellationToken = cts.Token + }; - var cd = (CommandDefinition)sut; + var cd = (CommandDefinition)sut; - Assert.Equal("SELECT 1", cd.CommandText); - Assert.Equal(CommandFlags.NoCache, cd.Flags); - Assert.Equal(12, cd.CommandTimeout); - Assert.Equal(CommandType.StoredProcedure, cd.CommandType); - Assert.Equal(parameters, cd.Parameters); - Assert.Equal(transaction, cd.Transaction); - Assert.Equal(cts.Token, cd.CancellationToken); - } + Assert.Equal("SELECT 1", cd.CommandText); + Assert.Equal(CommandFlags.NoCache, cd.Flags); + Assert.Equal(12, cd.CommandTimeout); + Assert.Equal(CommandType.StoredProcedure, cd.CommandType); + Assert.Equal(parameters, cd.Parameters); + Assert.Equal(transaction, cd.Transaction); + Assert.Equal(cts.Token, cd.CancellationToken); } } diff --git a/test/Savvyio.Extensions.Dapper.Tests/Savvyio.Extensions.Dapper.Tests.csproj b/test/Savvyio.Extensions.Dapper.Tests/Savvyio.Extensions.Dapper.Tests.csproj index cc8078d6..dfa56e3a 100644 --- a/test/Savvyio.Extensions.Dapper.Tests/Savvyio.Extensions.Dapper.Tests.csproj +++ b/test/Savvyio.Extensions.Dapper.Tests/Savvyio.Extensions.Dapper.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/test/Savvyio.Extensions.DapperExtensions.Tests/DapperExtensionsDataStoreTest.cs b/test/Savvyio.Extensions.DapperExtensions.Tests/DapperExtensionsDataStoreTest.cs index 54693b71..4da1769d 100644 --- a/test/Savvyio.Extensions.DapperExtensions.Tests/DapperExtensionsDataStoreTest.cs +++ b/test/Savvyio.Extensions.DapperExtensions.Tests/DapperExtensionsDataStoreTest.cs @@ -8,107 +8,106 @@ using Savvyio.Extensions.Dapper; using Xunit; -namespace Savvyio.Extensions.DapperExtensions +namespace Savvyio.Extensions.DapperExtensions; + +public class DapperExtensionsDataStoreTest : Test { - public class DapperExtensionsDataStoreTest : Test + public DapperExtensionsDataStoreTest(ITestOutputHelper output) : base(output) { - public DapperExtensionsDataStoreTest(ITestOutputHelper output) : base(output) - { - DapperAsyncExtensions.SqlDialect = new SqliteDialect(); - } + DapperAsyncExtensions.SqlDialect = new SqliteDialect(); + } - [Fact] - public async Task DataStore_ShouldCreateObject() + [Fact] + public async Task DataStore_ShouldCreateObject() + { + var name = "Test"; + var email = "test@unit.test"; + + var options = new DapperDataSourceOptions() { - var name = "Test"; - var email = "test@unit.test"; + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() + }; - var options = new DapperDataSourceOptions() - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() - }; + var sut1 = new DapperDataSource(options); - var sut1 = new DapperDataSource(options); + var sut2 = new DapperExtensionsDataStore(sut1); + await sut2.CreateAsync(new AccountProjection(name, email)); - var sut2 = new DapperExtensionsDataStore(sut1); - await sut2.CreateAsync(new AccountProjection(name, email)); + var sut3 = await sut2.GetByIdAsync(1); - var sut3 = await sut2.GetByIdAsync(1); + sut2.Dispose(); - sut2.Dispose(); + Assert.True(sut1.Disposed, "sut1.Disposed"); + Assert.True(sut2.Disposed, "sut2.Disposed"); + Assert.Equal(name, sut3.FullName); + Assert.Equal(email, sut3.EmailAddress); + } - Assert.True(sut1.Disposed, "sut1.Disposed"); - Assert.True(sut2.Disposed, "sut2.Disposed"); - Assert.Equal(name, sut3.FullName); - Assert.Equal(email, sut3.EmailAddress); - } + [Fact] + public async Task DataStore_ShouldRemoveObject() + { + var name = "Test"; + var email = "test@unit.test"; + var dto = new AccountProjection(name, email); - [Fact] - public async Task DataStore_ShouldRemoveObject() + var sut1 = new DapperDataSource(new DapperDataSourceOptions() { - var name = "Test"; - var email = "test@unit.test"; - var dto = new AccountProjection(name, email); + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() + }); - var sut1 = new DapperDataSource(new DapperDataSourceOptions() - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() - }); + var sut2 = new DapperExtensionsDataStore(sut1); + await sut2.CreateAsync(dto); - var sut2 = new DapperExtensionsDataStore(sut1); - await sut2.CreateAsync(dto); + var sut3 = await sut2.GetByIdAsync(1); - var sut3 = await sut2.GetByIdAsync(1); + await sut2.DeleteAsync(sut3); - await sut2.DeleteAsync(sut3); + var sut4 = await sut2.FindAllAsync(); - var sut4 = await sut2.FindAllAsync(); + var sut5 = await sut2.GetByIdAsync(1); - var sut5 = await sut2.GetByIdAsync(1); + sut2.Dispose(); - sut2.Dispose(); + Assert.True(sut1.Disposed, "sut1.Disposed"); + Assert.True(sut2.Disposed, "sut2.Disposed"); + Assert.NotNull(sut3); + Assert.Empty(sut4); + Assert.Null(sut5); + } - Assert.True(sut1.Disposed, "sut1.Disposed"); - Assert.True(sut2.Disposed, "sut2.Disposed"); - Assert.NotNull(sut3); - Assert.Empty(sut4); - Assert.Null(sut5); - } + [Fact] + public async Task DataStore_ShouldUpdateObject() + { + var name = "Test"; + var newName = "Unit Test"; + var email = "test@unit.test"; + var dto = new AccountProjection(name, email); - [Fact] - public async Task DataStore_ShouldUpdateObject() + var sut1 = new DapperDataSource(new DapperDataSourceOptions() { - var name = "Test"; - var newName = "Unit Test"; - var email = "test@unit.test"; - var dto = new AccountProjection(name, email); + ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() + }); - var sut1 = new DapperDataSource(new DapperDataSourceOptions() - { - ConnectionFactory = () => new SqliteConnection("Data Source=:memory:").SetDefaults().AddAccountTable() - }); + var sut2 = new DapperExtensionsDataStore(sut1); + await sut2.CreateAsync(dto); - var sut2 = new DapperExtensionsDataStore(sut1); - await sut2.CreateAsync(dto); + var sut3 = await sut2.GetByIdAsync(1); - var sut3 = await sut2.GetByIdAsync(1); + sut3.FullName = newName; - sut3.FullName = newName; + await sut2.UpdateAsync(sut3); - await sut2.UpdateAsync(sut3); - - var sut4 = await sut2.FindAllAsync(o => - { - o.Predicate = a => a.Id; - o.Value = sut3.Id; - }).SingleOrDefaultAsync(); + var sut4 = await sut2.FindAllAsync(o => + { + o.Predicate = a => a.Id; + o.Value = sut3.Id; + }).SingleOrDefaultAsync(); - sut2.Dispose(); + sut2.Dispose(); - Assert.True(sut1.Disposed, "sut1.Disposed"); - Assert.True(sut2.Disposed, "sut2.Disposed"); - Assert.NotEqual(name, sut3.FullName); - Assert.Equal(newName, sut4.FullName); - } + Assert.True(sut1.Disposed, "sut1.Disposed"); + Assert.True(sut2.Disposed, "sut2.Disposed"); + Assert.NotEqual(name, sut3.FullName); + Assert.Equal(newName, sut4.FullName); } } diff --git a/test/Savvyio.Extensions.DapperExtensions.Tests/DapperExtensionsQueryOptionsTest.cs b/test/Savvyio.Extensions.DapperExtensions.Tests/DapperExtensionsQueryOptionsTest.cs index 5aeaf0b8..2bb7df0d 100644 --- a/test/Savvyio.Extensions.DapperExtensions.Tests/DapperExtensionsQueryOptionsTest.cs +++ b/test/Savvyio.Extensions.DapperExtensions.Tests/DapperExtensionsQueryOptionsTest.cs @@ -1,30 +1,29 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using DapperExtensions.Predicate; using Savvyio.Assets.Queries; using Xunit; -namespace Savvyio.Extensions.DapperExtensions +namespace Savvyio.Extensions.DapperExtensions; + +public class DapperExtensionsQueryOptionsTest : Test { - public class DapperExtensionsQueryOptionsTest : Test + public DapperExtensionsQueryOptionsTest(ITestOutputHelper output) : base(output) { - public DapperExtensionsQueryOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void DapperOptions_Ensure_Initialization_Defaults() - { - var sut = new DapperExtensionsQueryOptions(); + [Fact] + public void DapperOptions_Ensure_Initialization_Defaults() + { + var sut = new DapperExtensionsQueryOptions(); - Assert.Null(sut.Value); - Assert.Equal(Operator.Eq, sut.Op); - Assert.Equal(DatabaseFunction.None, sut.Function); - Assert.Null(sut.FunctionParameters); - Assert.Null(sut.Predicate); - Assert.Equal(default, sut.CancellationToken); - Assert.False(sut.Not); - Assert.True(sut.UseColumnPrefix); + Assert.Null(sut.Value); + Assert.Equal(Operator.Eq, sut.Op); + Assert.Equal(DatabaseFunction.None, sut.Function); + Assert.Null(sut.FunctionParameters); + Assert.Null(sut.Predicate); + Assert.Equal(default, sut.CancellationToken); + Assert.False(sut.Not); + Assert.True(sut.UseColumnPrefix); - } } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/Savvyio.Extensions.DependencyInjection.Dapper.Tests.csproj b/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/Savvyio.Extensions.DependencyInjection.Dapper.Tests.csproj index 9fcb7449..be0dd610 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/Savvyio.Extensions.DependencyInjection.Dapper.Tests.csproj +++ b/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/Savvyio.Extensions.DependencyInjection.Dapper.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Extensions.DependencyInjection.Dapper diff --git a/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/ServiceCollectionExtensionsTest.cs index e7647157..7734330d 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/ServiceCollectionExtensionsTest.cs @@ -1,42 +1,41 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; using Savvyio.Assets; using Savvyio.Extensions.Dapper; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.Dapper +namespace Savvyio.Extensions.DependencyInjection.Dapper; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddDapperDataSource_ShouldAddDefaultImplementation() - { - var sut1 = new ServiceCollection(); - sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(sut2); + [Fact] + public void AddDapperDataSource_ShouldAddDefaultImplementation() + { + var sut1 = new ServiceCollection(); + sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(sut2); - Assert.IsType(sut2.GetRequiredService()); - Assert.IsType(sut2.GetRequiredService()); - Assert.Same(sut2.GetRequiredService(), sut2.GetRequiredService()); - } + Assert.IsType(sut2.GetRequiredService()); + Assert.IsType(sut2.GetRequiredService()); + Assert.Same(sut2.GetRequiredService(), sut2.GetRequiredService()); + } - [Fact] - public void AddDapperDataSource_ShouldAddDefaultImplementationWithMarker() - { - var sut1 = new ServiceCollection(); - sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(sut2); + [Fact] + public void AddDapperDataSource_ShouldAddDefaultImplementationWithMarker() + { + var sut1 = new ServiceCollection(); + sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(sut2); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.Same(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - } + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.Same(sut2.GetRequiredService>(), sut2.GetRequiredService>()); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/ServiceCollectionRegistrationTest.cs b/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/ServiceCollectionRegistrationTest.cs index 06c22814..32b92fc4 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/ServiceCollectionRegistrationTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Dapper.Tests/ServiceCollectionRegistrationTest.cs @@ -8,59 +8,58 @@ using Savvyio.Extensions.Dapper; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.Dapper +namespace Savvyio.Extensions.DependencyInjection.Dapper; + +public class ServiceCollectionRegistrationTest : Test { - public class ServiceCollectionRegistrationTest : Test + public ServiceCollectionRegistrationTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void AddDapperDataStore_ShouldTypeForwardPersistentDataStore() + { + var services = new ServiceCollection(); + services.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); + services.AddDapperDataStore(); + var provider = services.BuildServiceProvider(); + + Assert.IsType(provider.GetRequiredService>()); + } + + private sealed class FakeRecord { - public ServiceCollectionRegistrationTest(ITestOutputHelper output) : base(output) + } + + private sealed class FakeDapperDataStore : DapperDataStore + { + public FakeDapperDataStore(Savvyio.Extensions.Dapper.IDapperDataSource source) : base(source) { } - [Fact] - public void AddDapperDataStore_ShouldTypeForwardPersistentDataStore() + public override Task CreateAsync(FakeRecord dto, Action setup = null) { - var services = new ServiceCollection(); - services.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); - services.AddDapperDataStore(); - var provider = services.BuildServiceProvider(); - - Assert.IsType(provider.GetRequiredService>()); + return Task.CompletedTask; } - private sealed class FakeRecord + public override Task UpdateAsync(FakeRecord dto, Action setup = null) { + return Task.CompletedTask; } - private sealed class FakeDapperDataStore : DapperDataStore + public override Task GetByIdAsync(object id, Action setup = null) { - public FakeDapperDataStore(Savvyio.Extensions.Dapper.IDapperDataSource source) : base(source) - { - } - - public override Task CreateAsync(FakeRecord dto, Action setup = null) - { - return Task.CompletedTask; - } - - public override Task UpdateAsync(FakeRecord dto, Action setup = null) - { - return Task.CompletedTask; - } - - public override Task GetByIdAsync(object id, Action setup = null) - { - return Task.FromResult(null); - } + return Task.FromResult(null); + } - public override Task> FindAllAsync(Action setup = null) - { - return Task.FromResult>(Array.Empty()); - } + public override Task> FindAllAsync(Action setup = null) + { + return Task.FromResult>(Array.Empty()); + } - public override Task DeleteAsync(FakeRecord dto, Action setup = null) - { - return Task.CompletedTask; - } + public override Task DeleteAsync(FakeRecord dto, Action setup = null) + { + return Task.CompletedTask; } } } diff --git a/test/Savvyio.Extensions.DependencyInjection.DapperExtensions.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.DapperExtensions.Tests/ServiceCollectionExtensionsTest.cs index e900ec99..09ad2c98 100644 --- a/test/Savvyio.Extensions.DependencyInjection.DapperExtensions.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.DapperExtensions.Tests/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; using Savvyio.Assets; @@ -9,45 +9,44 @@ using Savvyio.Extensions.DependencyInjection.Data; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.DapperExtensions +namespace Savvyio.Extensions.DependencyInjection.DapperExtensions; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddDapperExtensionsDataStore_ShouldAddManyImplementations() - { - var sut1 = new ServiceCollection(); - sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); - sut1.AddDapperExtensionsDataStore(); - sut1.AddDapperExtensionsDataStore(); - var sut2 = sut1.BuildServiceProvider(); + [Fact] + public void AddDapperExtensionsDataStore_ShouldAddManyImplementations() + { + var sut1 = new ServiceCollection(); + sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); + sut1.AddDapperExtensionsDataStore(); + sut1.AddDapperExtensionsDataStore(); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType>(sut2.GetRequiredService>>()); - Assert.IsType>(sut2.GetRequiredService>>()); - } + Assert.IsType>(sut2.GetRequiredService>>()); + Assert.IsType>(sut2.GetRequiredService>>()); + } - [Fact] - public void AddDapperExtensionsDataStore_ShouldAddManyImplementationsWithMarkers() - { - var sut1 = new ServiceCollection(); - sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); - sut1.AddDapperExtensionsDataStore(); - sut1.AddDapperExtensionsDataStore(); - sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); - sut1.AddDapperExtensionsDataStore(); - sut1.AddDapperExtensionsDataStore(); - var sut2 = sut1.BuildServiceProvider(); + [Fact] + public void AddDapperExtensionsDataStore_ShouldAddManyImplementationsWithMarkers() + { + var sut1 = new ServiceCollection(); + sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); + sut1.AddDapperExtensionsDataStore(); + sut1.AddDapperExtensionsDataStore(); + sut1.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection("Data Source=:memory:")); + sut1.AddDapperExtensionsDataStore(); + sut1.AddDapperExtensionsDataStore(); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); - Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); - Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); - Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); - Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); - Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); - } + Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); + Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); + Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); + Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); + Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); + Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.Tests/ServiceCollectionExtensionsTest.cs index c8d3a775..99f1b62a 100644 --- a/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing.Tests/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; @@ -13,40 +13,39 @@ using Savvyio.Extensions.Text.Json; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing +namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddEfCoreTracedAggregateRepository_ShouldHaveTypeForwardedImplementations() - { - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); - sut1.AddEfCoreTracedAggregateRepository(); - var sut2 = sut1.BuildServiceProvider(); + [Fact] + public void AddEfCoreTracedAggregateRepository_ShouldHaveTypeForwardedImplementations() + { + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); + sut1.AddEfCoreTracedAggregateRepository(); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - } + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + } - [Fact] - public void AddEfCoreTracedAggregateRepository_ShouldHaveTypeForwardedImplementationsWithMarker() - { - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); - sut1.AddEfCoreTracedAggregateRepository(); - var sut2 = sut1.BuildServiceProvider(); + [Fact] + public void AddEfCoreTracedAggregateRepository_ShouldHaveTypeForwardedImplementationsWithMarker() + { + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); + sut1.AddEfCoreTracedAggregateRepository(); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - } + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.Tests/AggregateDataSourceRegistrationTest.cs b/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.Tests/AggregateDataSourceRegistrationTest.cs index 0c7c451b..fb83e85a 100644 --- a/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.Tests/AggregateDataSourceRegistrationTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.Tests/AggregateDataSourceRegistrationTest.cs @@ -11,50 +11,49 @@ using Savvyio.Extensions.EFCore.Domain; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain +namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain; + +public class AggregateDataSourceRegistrationTest : Test { - public class AggregateDataSourceRegistrationTest : Test + public AggregateDataSourceRegistrationTest(ITestOutputHelper output) : base(output) { - public AggregateDataSourceRegistrationTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddEfCoreAggregateDataSource_ShouldTypeForwardDefaultImplementation() - { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddEfCoreAggregateDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("aggregate-" + Guid.NewGuid())); - var provider = services.BuildServiceProvider(); - - Assert.IsType(provider.GetRequiredService()); - Assert.IsType(provider.GetRequiredService()); - Assert.IsType(provider.GetRequiredService()); - } + [Fact] + public void AddEfCoreAggregateDataSource_ShouldTypeForwardDefaultImplementation() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddEfCoreAggregateDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("aggregate-" + Guid.NewGuid())); + var provider = services.BuildServiceProvider(); - [Fact] - public void AddEfCoreAggregateDataSource_ShouldTypeForwardMarkedImplementation() + Assert.IsType(provider.GetRequiredService()); + Assert.IsType(provider.GetRequiredService()); + Assert.IsType(provider.GetRequiredService()); + } + + [Fact] + public void AddEfCoreAggregateDataSource_ShouldTypeForwardMarkedImplementation() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddEfCoreAggregateDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("aggregate-marker-" + Guid.NewGuid())); + var provider = services.BuildServiceProvider(); + + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + } + + private sealed class SilentDomainEventDispatcher : IDomainEventDispatcher + { + public void Raise(IDomainEvent request) { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddEfCoreAggregateDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("aggregate-marker-" + Guid.NewGuid())); - var provider = services.BuildServiceProvider(); - - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); } - private sealed class SilentDomainEventDispatcher : IDomainEventDispatcher + public Task RaiseAsync(IDomainEvent request, Action setup = null) { - public void Raise(IDomainEvent request) - { - } - - public Task RaiseAsync(IDomainEvent request, Action setup = null) - { - return Task.CompletedTask; - } + return Task.CompletedTask; } } } diff --git a/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.Tests/ServiceCollectionExtensionsTest.cs index 71e237b2..2c25abff 100644 --- a/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.EFCore.Domain.Tests/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Savvyio.Assets; @@ -8,44 +8,43 @@ using Savvyio.Extensions.EFCore.Domain; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain +namespace Savvyio.Extensions.DependencyInjection.EFCore.Domain; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddEfCoreAggregateRepository_ShouldHaveTypeForwardedImplementations() - { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); - sut1.AddEfCoreAggregateRepository(); - var sut2 = sut1.BuildServiceProvider(); + [Fact] + public void AddEfCoreAggregateRepository_ShouldHaveTypeForwardedImplementations() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); + sut1.AddEfCoreAggregateRepository(); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - } + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + } - [Fact] - public void AddEfCoreAggregateRepository_ShouldHaveTypeForwardedImplementationsWithMarker() - { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); - sut1.AddEfCoreAggregateRepository(); - var sut2 = sut1.BuildServiceProvider(); + [Fact] + public void AddEfCoreAggregateRepository_ShouldHaveTypeForwardedImplementationsWithMarker() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); + sut1.AddEfCoreAggregateRepository(); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - } + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.EFCore.Tests/Savvyio.Extensions.DependencyInjection.EFCore.Tests.csproj b/test/Savvyio.Extensions.DependencyInjection.EFCore.Tests/Savvyio.Extensions.DependencyInjection.EFCore.Tests.csproj index ee485ec4..9aca8821 100644 --- a/test/Savvyio.Extensions.DependencyInjection.EFCore.Tests/Savvyio.Extensions.DependencyInjection.EFCore.Tests.csproj +++ b/test/Savvyio.Extensions.DependencyInjection.EFCore.Tests/Savvyio.Extensions.DependencyInjection.EFCore.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Extensions.DependencyInjection.EFCore diff --git a/test/Savvyio.Extensions.DependencyInjection.EFCore.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.EFCore.Tests/ServiceCollectionExtensionsTest.cs index 5013f750..fee8882e 100644 --- a/test/Savvyio.Extensions.DependencyInjection.EFCore.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.EFCore.Tests/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; @@ -11,112 +11,111 @@ using Savvyio.Extensions.EFCore; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.EFCore +namespace Savvyio.Extensions.DependencyInjection.EFCore; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddEfCoreDataSource_ShouldAddDefaultImplementation() - { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(AnotherDbMarker))); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(sut2); + [Fact] + public void AddEfCoreDataSource_ShouldAddDefaultImplementation() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(AnotherDbMarker))); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(sut2); - Assert.IsType(sut2.GetRequiredService()); - Assert.IsType(sut2.GetRequiredService()); - Assert.IsType(sut2.GetRequiredService()); - Assert.Same(sut2.GetRequiredService(), sut2.GetRequiredService()); - } + Assert.IsType(sut2.GetRequiredService()); + Assert.IsType(sut2.GetRequiredService()); + Assert.IsType(sut2.GetRequiredService()); + Assert.Same(sut2.GetRequiredService(), sut2.GetRequiredService()); + } - [Fact] - public void AddEfCoreDataSource_ShouldAddDefaultImplementationWithMarker() - { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(sut2); + [Fact] + public void AddEfCoreDataSource_ShouldAddDefaultImplementationWithMarker() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(sut2); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.Same(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - } + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.Same(sut2.GetRequiredService>(), sut2.GetRequiredService>()); + } - [Fact] - public void AddEfCoreRepository_ShouldAddManyImplementations() - { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); - sut1.AddEfCoreRepository(); - sut1.AddEfCoreRepository(); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(sut2); + [Fact] + public void AddEfCoreRepository_ShouldAddManyImplementations() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); + sut1.AddEfCoreRepository(); + sut1.AddEfCoreRepository(); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(sut2); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - } + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + } - [Fact] - public void AddEfCoreRepository_ShouldAddManyImplementationsWithMarkers() - { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(DbMarker))); - sut1.AddEfCoreRepository(); - sut1.AddEfCoreRepository(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(AnotherDbMarker))); - sut1.AddEfCoreRepository(); - sut1.AddEfCoreRepository(); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(sut2); + [Fact] + public void AddEfCoreRepository_ShouldAddManyImplementationsWithMarkers() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(DbMarker))); + sut1.AddEfCoreRepository(); + sut1.AddEfCoreRepository(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(AnotherDbMarker))); + sut1.AddEfCoreRepository(); + sut1.AddEfCoreRepository(); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(sut2); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - } + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); + Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); + } - [Fact] - public void AddEfCoreDataAccessObject_ShouldAddManyImplementations() - { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); - sut1.AddEfCoreDataStore(); - sut1.AddEfCoreDataStore(); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(sut2); + [Fact] + public void AddEfCoreDataAccessObject_ShouldAddManyImplementations() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase("fake")); + sut1.AddEfCoreDataStore(); + sut1.AddEfCoreDataStore(); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(sut2); - TestOutput.WriteLine(sut2.GetRequiredService>>().GetType().FullName); + TestOutput.WriteLine(sut2.GetRequiredService>>().GetType().FullName); - Assert.IsType>(sut2.GetRequiredService>>()); - Assert.IsType>(sut2.GetRequiredService>>()); - } + Assert.IsType>(sut2.GetRequiredService>>()); + Assert.IsType>(sut2.GetRequiredService>>()); + } - [Fact] - public void AddEfCoreDataAccessObject_ShouldAddManyImplementationsWithMarkers() - { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(DbMarker))); - sut1.AddEfCoreDataStore(); - sut1.AddEfCoreDataStore(); - sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(AnotherDbMarker))); - sut1.AddEfCoreDataStore(); - sut1.AddEfCoreDataStore(); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(sut2); + [Fact] + public void AddEfCoreDataAccessObject_ShouldAddManyImplementationsWithMarkers() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(DbMarker))); + sut1.AddEfCoreDataStore(); + sut1.AddEfCoreDataStore(); + sut1.AddEfCoreDataSource(o => o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(AnotherDbMarker))); + sut1.AddEfCoreDataStore(); + sut1.AddEfCoreDataStore(); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(sut2); - Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); - Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); - Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); - Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); - Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); - Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); - } + Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); + Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); + Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); + Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); + Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); + Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/Assets/BusMarker.cs b/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/Assets/BusMarker.cs index 2ce1a04d..44a228c6 100644 --- a/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/Assets/BusMarker.cs +++ b/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/Assets/BusMarker.cs @@ -1,7 +1,6 @@ -namespace Savvyio.Extensions.DependencyInjection.NATS.Assets +namespace Savvyio.Extensions.DependencyInjection.NATS.Assets; + +public struct BusMarker { - public struct BusMarker - { - } -} \ No newline at end of file +} diff --git a/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/Assets/QueueMarker.cs b/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/Assets/QueueMarker.cs index 0c0bfe7e..a33312ed 100644 --- a/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/Assets/QueueMarker.cs +++ b/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/Assets/QueueMarker.cs @@ -1,7 +1,6 @@ -namespace Savvyio.Extensions.DependencyInjection.NATS.Assets +namespace Savvyio.Extensions.DependencyInjection.NATS.Assets; + +public struct QueueMarker { - public struct QueueMarker - { - } -} \ No newline at end of file +} diff --git a/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/ServiceCollectionExtensionsTest.cs index 5884406f..36555891 100644 --- a/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.NATS.Tests/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Microsoft.Extensions.DependencyInjection; using Savvyio.Commands; using Savvyio.EventDriven; @@ -12,129 +12,136 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.NATS +namespace Savvyio.Extensions.DependencyInjection.NATS; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } + + [Fact] + public void AddNatsCommandQueue_ShouldRegisterNatsCommandQueueAndConfigureOptions() + { + // Arrange + var services = new ServiceCollection(); + bool natsSetupCalled = false; - [Fact] - public void AddNatsCommandQueue_ShouldRegisterNatsCommandQueueAndConfigureOptions() + // Act + services.AddMarshaller(); + services.AddNatsCommandQueue(options => { - // Arrange - var services = new ServiceCollection(); - bool natsSetupCalled = false; - - // Act - services.AddMarshaller(); - services.AddNatsCommandQueue(options => - { - natsSetupCalled = true; - options.StreamName = "stream"; - options.ConsumerName = "consumer"; - options.Subject = "subject"; - }); - - // Assert - Assert.True(natsSetupCalled); - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(provider); - var queue = provider.GetService(); - - - Assert.IsType(provider.GetRequiredService>()); - Assert.IsType(provider.GetRequiredService>()); - Assert.IsType(provider.GetRequiredService>()); - - Assert.NotNull(queue); - } - - [Fact] - public void AddNatsCommandQueue_Generic_ShouldRegisterNatsCommandQueueAndConfigureOptions() + natsSetupCalled = true; + options.StreamName = "stream"; + options.ConsumerName = "consumer"; + options.Subject = "subject"; + }); + + // Assert + Assert.False(natsSetupCalled); + var provider = services.BuildServiceProvider(); + Assert.False(natsSetupCalled); + ConfiguredOptionsAssertions.Verify(provider); + Assert.True(natsSetupCalled); + var queue = provider.GetService(); + + + Assert.IsType(provider.GetRequiredService>()); + Assert.IsType(provider.GetRequiredService>()); + Assert.IsType(provider.GetRequiredService>()); + + Assert.NotNull(queue); + } + + [Fact] + public void AddNatsCommandQueue_Generic_ShouldRegisterNatsCommandQueueAndConfigureOptions() + { + // Arrange + var services = new ServiceCollection(); + bool natsSetupCalled = false; + + // Act + services.AddMarshaller(); + services.AddNatsCommandQueue(options => { - // Arrange - var services = new ServiceCollection(); - bool natsSetupCalled = false; - - // Act - services.AddMarshaller(); - services.AddNatsCommandQueue(options => - { - natsSetupCalled = true; - options.StreamName = "stream"; - options.ConsumerName = "consumer"; - options.Subject = "subject"; - }); - - // Assert - Assert.True(natsSetupCalled); - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(provider); - var queue = provider.GetService>(); - - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); - - Assert.NotNull(queue); - } - - [Fact] - public void AddNatsEventBus_ShouldRegisterNatsEventBusAndConfigureOptions() + natsSetupCalled = true; + options.StreamName = "stream"; + options.ConsumerName = "consumer"; + options.Subject = "subject"; + }); + + // Assert + Assert.False(natsSetupCalled); + var provider = services.BuildServiceProvider(); + Assert.False(natsSetupCalled); + ConfiguredOptionsAssertions.Verify>(provider); + Assert.True(natsSetupCalled); + var queue = provider.GetService>(); + + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + + Assert.NotNull(queue); + } + + [Fact] + public void AddNatsEventBus_ShouldRegisterNatsEventBusAndConfigureOptions() + { + // Arrange + var services = new ServiceCollection(); + bool natsSetupCalled = false; + + // Act + services.AddMarshaller(); + services.AddNatsEventBus(options => { - // Arrange - var services = new ServiceCollection(); - bool natsSetupCalled = false; - - // Act - services.AddMarshaller(); - services.AddNatsEventBus(options => - { - natsSetupCalled = true; - options.Subject = "subject"; - }); - - // Assert - Assert.True(natsSetupCalled); - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(provider); - var bus = provider.GetService(); - - Assert.IsType(provider.GetRequiredService>()); - Assert.IsType(provider.GetRequiredService>()); - Assert.IsType(provider.GetRequiredService>()); - - Assert.NotNull(bus); - } - - [Fact] - public void AddNatsEventBus_Generic_ShouldRegisterNatsEventBusAndConfigureOptions() + natsSetupCalled = true; + options.Subject = "subject"; + }); + + // Assert + Assert.False(natsSetupCalled); + var provider = services.BuildServiceProvider(); + Assert.False(natsSetupCalled); + ConfiguredOptionsAssertions.Verify(provider); + Assert.True(natsSetupCalled); + var bus = provider.GetService(); + + Assert.IsType(provider.GetRequiredService>()); + Assert.IsType(provider.GetRequiredService>()); + Assert.IsType(provider.GetRequiredService>()); + + Assert.NotNull(bus); + } + + [Fact] + public void AddNatsEventBus_Generic_ShouldRegisterNatsEventBusAndConfigureOptions() + { + // Arrange + var services = new ServiceCollection(); + bool natsSetupCalled = false; + + // Act + services.AddMarshaller(); + services.AddNatsEventBus(options => { - // Arrange - var services = new ServiceCollection(); - bool natsSetupCalled = false; - - // Act - services.AddMarshaller(); - services.AddNatsEventBus(options => - { - natsSetupCalled = true; - options.Subject = "subject"; - }); - - // Assert - Assert.True(natsSetupCalled); - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(provider); - var bus = provider.GetService>(); - - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); - - Assert.NotNull(bus); - } + natsSetupCalled = true; + options.Subject = "subject"; + }); + + // Assert + Assert.False(natsSetupCalled); + var provider = services.BuildServiceProvider(); + Assert.False(natsSetupCalled); + ConfiguredOptionsAssertions.Verify>(provider); + Assert.True(natsSetupCalled); + var bus = provider.GetService>(); + + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + + Assert.NotNull(bus); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.Tests/ServiceCollectionExtensionsTest.cs index a21c5a10..550c25ba 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.Tests/ServiceCollectionExtensionsTest.cs @@ -8,83 +8,82 @@ using Savvyio.Extensions.Newtonsoft.Json; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.Tests +namespace Savvyio.Extensions.DependencyInjection.Newtonsoft.Json.Tests; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + [Fact] + public void AddNewtonsoftJsonMarshaller_ShouldThrowOnNullServices() { - [Fact] - public void AddNewtonsoftJsonMarshaller_ShouldThrowOnNullServices() - { - // Arrange - IServiceCollection services = null; + // Arrange + IServiceCollection services = null; - // Act & Assert - Assert.Throws(() => - ServiceCollectionExtensions.AddNewtonsoftJsonMarshaller(services)); - } + // Act & Assert + Assert.Throws(() => + ServiceCollectionExtensions.AddNewtonsoftJsonMarshaller(services)); + } - [Fact] - public void AddNewtonsoftJsonMarshaller_ShouldRegisterNewtonsoftJsonMarshallerWithDefaultOptions() - { - // Arrange - var services = new ServiceCollection(); + [Fact] + public void AddNewtonsoftJsonMarshaller_ShouldRegisterNewtonsoftJsonMarshallerWithDefaultOptions() + { + // Arrange + var services = new ServiceCollection(); - // Act - services.AddNewtonsoftJsonMarshaller(); + // Act + services.AddNewtonsoftJsonMarshaller(); - // Assert - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(provider); - var marshaller = provider.GetService(); - Assert.NotNull(marshaller); + // Assert + var provider = services.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(provider); + var marshaller = provider.GetService(); + Assert.NotNull(marshaller); - // Check that options are registered and default formatting is None - var options = provider.GetService(); - Assert.NotNull(options); - Assert.Equal(Formatting.None, options.Settings.Formatting); - } + // Check that options are registered and default formatting is None + var options = provider.GetService(); + Assert.NotNull(options); + Assert.Equal(Formatting.None, options.Settings.Formatting); + } - [Fact] - public void AddNewtonsoftJsonMarshaller_ShouldApplyCustomJsonSetup() + [Fact] + public void AddNewtonsoftJsonMarshaller_ShouldApplyCustomJsonSetup() + { + // Arrange + var services = new ServiceCollection(); + var called = false; + + // Act + services.AddNewtonsoftJsonMarshaller(o => { - // Arrange - var services = new ServiceCollection(); - var called = false; + o.Settings.Formatting = Formatting.Indented; + called = true; + }); - // Act - services.AddNewtonsoftJsonMarshaller(o => - { - o.Settings.Formatting = Formatting.Indented; - called = true; - }); + // Assert + var provider = services.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(provider); + var options = provider.GetService(); + Assert.NotNull(options); + Assert.True(called); + Assert.Equal(Formatting.Indented, options.Settings.Formatting); + } - // Assert - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(provider); - var options = provider.GetService(); - Assert.NotNull(options); - Assert.True(called); - Assert.Equal(Formatting.Indented, options.Settings.Formatting); - } + [Fact] + public void AddNewtonsoftJsonMarshaller_ShouldApplyCustomServiceSetup() + { + // Arrange + var services = new ServiceCollection(); + ServiceLifetime? lifetime = null; - [Fact] - public void AddNewtonsoftJsonMarshaller_ShouldApplyCustomServiceSetup() + // Act + services.AddNewtonsoftJsonMarshaller(null, o => { - // Arrange - var services = new ServiceCollection(); - ServiceLifetime? lifetime = null; - - // Act - services.AddNewtonsoftJsonMarshaller(null, o => - { - o.Lifetime = ServiceLifetime.Scoped; - lifetime = o.Lifetime; - }); + o.Lifetime = ServiceLifetime.Scoped; + lifetime = o.Lifetime; + }); - // Assert - var descriptor = Assert.Single(services, d => d.ServiceType == typeof(NewtonsoftJsonMarshaller)); - Assert.Equal(ServiceLifetime.Scoped, descriptor.Lifetime); - Assert.Equal(ServiceLifetime.Scoped, lifetime); - } + // Assert + var descriptor = Assert.Single(services, d => d.ServiceType == typeof(NewtonsoftJsonMarshaller)); + Assert.Equal(ServiceLifetime.Scoped, descriptor.Lifetime); + Assert.Equal(ServiceLifetime.Scoped, lifetime); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/Assets/BusMarker.cs b/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/Assets/BusMarker.cs index 150b9c6c..d4212758 100644 --- a/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/Assets/BusMarker.cs +++ b/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/Assets/BusMarker.cs @@ -1,6 +1,5 @@ -namespace Savvyio.Extensions.DependencyInjection.QueueStorage.Assets +namespace Savvyio.Extensions.DependencyInjection.QueueStorage.Assets; + +public struct BusMarker { - public struct BusMarker - { - } } diff --git a/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/Assets/QueueMarker.cs b/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/Assets/QueueMarker.cs index afb2514e..e2520bce 100644 --- a/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/Assets/QueueMarker.cs +++ b/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/Assets/QueueMarker.cs @@ -1,6 +1,5 @@ -namespace Savvyio.Extensions.DependencyInjection.QueueStorage.Assets +namespace Savvyio.Extensions.DependencyInjection.QueueStorage.Assets; + +public struct QueueMarker { - public struct QueueMarker - { - } } diff --git a/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/ServiceCollectionExtensionsTest.cs index cc3fea59..c0d42751 100644 --- a/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.QueueStorage.Tests/ServiceCollectionExtensionsTest.cs @@ -18,178 +18,177 @@ using Xunit; using Xunit.Sdk; -namespace Savvyio.Extensions.DependencyInjection.QueueStorage +namespace Savvyio.Extensions.DependencyInjection.QueueStorage; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void AddAzureCommandQueue_ShouldAddDefaultImplementation() { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) + Action azureSetup = o => { - } + o.StorageAccountName = "account"; + o.QueueName = "queue"; + o.SasCredential = new AzureSasCredential("sas"); + }; + + var services = new ServiceCollection(); + services.AddMarshaller(); + services.AddAzureCommandQueue(azureSetup); + var provider = services.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(provider); + + Assert.IsType(provider.GetRequiredService>()); + Assert.IsType(provider.GetRequiredService>()); + Assert.IsType(provider.GetRequiredService>()); + + var opt1 = new AzureQueueOptions(); + var opt2 = new AzureQueueOptions(); + var opt3 = new AzureQueueOptions(); - [Fact] - public void AddAzureCommandQueue_ShouldAddDefaultImplementation() + azureSetup(opt1); + provider.GetRequiredService>()(opt2); + + Assert.Equivalent(azureSetup, provider.GetRequiredService>()); + Assert.Equivalent(opt1, opt2); + + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); + } + + [Fact] + public void AddAzureCommandQueue_ShouldAddDefaultImplementationWithMarker() + { + Action> azureSetup = o => { - Action azureSetup = o => - { - o.StorageAccountName = "account"; - o.QueueName = "queue"; - o.SasCredential = new AzureSasCredential("sas"); - }; - - var services = new ServiceCollection(); - services.AddMarshaller(); - services.AddAzureCommandQueue(azureSetup); - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(provider); - - Assert.IsType(provider.GetRequiredService>()); - Assert.IsType(provider.GetRequiredService>()); - Assert.IsType(provider.GetRequiredService>()); - - var opt1 = new AzureQueueOptions(); - var opt2 = new AzureQueueOptions(); - var opt3 = new AzureQueueOptions(); - - azureSetup(opt1); - provider.GetRequiredService>()(opt2); - - Assert.Equivalent(azureSetup, provider.GetRequiredService>()); - Assert.Equivalent(opt1, opt2); - - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } - - [Fact] - public void AddAzureCommandQueue_ShouldAddDefaultImplementationWithMarker() + o.StorageAccountName = "account"; + o.QueueName = "queue"; + o.SasCredential = new AzureSasCredential("sas"); + }; + + var services = new ServiceCollection(); + services.AddMarshaller(); + services.AddAzureCommandQueue(azureSetup); + var provider = services.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(provider); + + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + + var opt1 = new AzureQueueOptions(); + var opt2 = new AzureQueueOptions(); + var opt3 = new AzureQueueOptions(); + + azureSetup(opt1); + provider.GetRequiredService>>()(opt2); + + Assert.Equivalent(azureSetup, provider.GetRequiredService>>()); + Assert.Equivalent(opt1, opt2); + + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); + } + + [Fact] + public void AddAzureEventBus_ShouldAddDefaultImplementation() + { + Action azureQueueSetup = o => + { + o.StorageAccountName = "account"; + o.QueueName = "queue"; + o.SasCredential = new AzureSasCredential("sas"); + }; + Action azureEventBusSetup = o => { - Action> azureSetup = o => - { - o.StorageAccountName = "account"; - o.QueueName = "queue"; - o.SasCredential = new AzureSasCredential("sas"); - }; - - var services = new ServiceCollection(); - services.AddMarshaller(); - services.AddAzureCommandQueue(azureSetup); - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(provider); - - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); - - var opt1 = new AzureQueueOptions(); - var opt2 = new AzureQueueOptions(); - var opt3 = new AzureQueueOptions(); - - azureSetup(opt1); - provider.GetRequiredService>>()(opt2); - - Assert.Equivalent(azureSetup, provider.GetRequiredService>>()); - Assert.Equivalent(opt1, opt2); - - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } - - [Fact] - public void AddAzureEventBus_ShouldAddDefaultImplementation() + o.TopicEndpoint = new Uri("https://example.com"); + o.SasCredential = new AzureSasCredential("sas"); + }; + + var services = new ServiceCollection(); + services.AddMarshaller(); + services.AddAzureEventBus(azureQueueSetup, azureEventBusSetup); + var provider = services.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(provider); + ConfiguredOptionsAssertions.Verify(provider); + + Assert.IsType(provider.GetRequiredService>()); + Assert.IsType(provider.GetRequiredService>()); + Assert.IsType(provider.GetRequiredService>()); + + var q1 = new AzureQueueOptions(); + var q2 = new AzureQueueOptions(); + var q3 = new AzureQueueOptions(); + var b1 = new CoreEvent.AzureEventBusOptions(); + var b2 = new CoreEvent.AzureEventBusOptions(); + var b3 = new CoreEvent.AzureEventBusOptions(); + + azureQueueSetup(q1); + provider.GetRequiredService>()(q2); + azureEventBusSetup(b1); + provider.GetRequiredService>()(b2); + + Assert.Equivalent(azureQueueSetup, provider.GetRequiredService>()); + Assert.Equivalent(azureEventBusSetup, provider.GetRequiredService>()); + Assert.Equivalent(q1, q2); + Assert.Equivalent(b1, b2); + + Assert.Throws(() => Assert.Equivalent(q1, q3)); + Assert.Throws(() => Assert.Equivalent(q2, q3)); + Assert.Throws(() => Assert.Equivalent(b1, b3)); + Assert.Throws(() => Assert.Equivalent(b2, b3)); + } + + [Fact] + public void AddAzureEventBus_ShouldAddDefaultImplementationWithMarker() + { + Action> azureQueueSetup = o => { - Action azureQueueSetup = o => - { - o.StorageAccountName = "account"; - o.QueueName = "queue"; - o.SasCredential = new AzureSasCredential("sas"); - }; - Action azureEventBusSetup = o => - { - o.TopicEndpoint = new Uri("https://example.com"); - o.SasCredential = new AzureSasCredential("sas"); - }; - - var services = new ServiceCollection(); - services.AddMarshaller(); - services.AddAzureEventBus(azureQueueSetup, azureEventBusSetup); - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(provider); - ConfiguredOptionsAssertions.Verify(provider); - - Assert.IsType(provider.GetRequiredService>()); - Assert.IsType(provider.GetRequiredService>()); - Assert.IsType(provider.GetRequiredService>()); - - var q1 = new AzureQueueOptions(); - var q2 = new AzureQueueOptions(); - var q3 = new AzureQueueOptions(); - var b1 = new CoreEvent.AzureEventBusOptions(); - var b2 = new CoreEvent.AzureEventBusOptions(); - var b3 = new CoreEvent.AzureEventBusOptions(); - - azureQueueSetup(q1); - provider.GetRequiredService>()(q2); - azureEventBusSetup(b1); - provider.GetRequiredService>()(b2); - - Assert.Equivalent(azureQueueSetup, provider.GetRequiredService>()); - Assert.Equivalent(azureEventBusSetup, provider.GetRequiredService>()); - Assert.Equivalent(q1, q2); - Assert.Equivalent(b1, b2); - - Assert.Throws(() => Assert.Equivalent(q1, q3)); - Assert.Throws(() => Assert.Equivalent(q2, q3)); - Assert.Throws(() => Assert.Equivalent(b1, b3)); - Assert.Throws(() => Assert.Equivalent(b2, b3)); - } - - [Fact] - public void AddAzureEventBus_ShouldAddDefaultImplementationWithMarker() + o.StorageAccountName = "account"; + o.QueueName = "queue"; + o.SasCredential = new AzureSasCredential("sas"); + }; + Action> azureEventBusSetup = o => { - Action> azureQueueSetup = o => - { - o.StorageAccountName = "account"; - o.QueueName = "queue"; - o.SasCredential = new AzureSasCredential("sas"); - }; - Action> azureEventBusSetup = o => - { - o.TopicEndpoint = new Uri("https://example.com"); - o.SasCredential = new AzureSasCredential("sas"); - }; - - var services = new ServiceCollection(); - services.AddMarshaller(); - services.AddAzureEventBus(azureQueueSetup, azureEventBusSetup); - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(provider); - ConfiguredOptionsAssertions.Verify>(provider); - - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); - Assert.IsType>(provider.GetRequiredService>()); - - var q1 = new AzureQueueOptions(); - var q2 = new AzureQueueOptions(); - var q3 = new AzureQueueOptions(); - var b1 = new DIEvent.AzureEventBusOptions(); - var b2 = new DIEvent.AzureEventBusOptions(); - var b3 = new DIEvent.AzureEventBusOptions(); - - azureQueueSetup(q1); - provider.GetRequiredService>>()(q2); - azureEventBusSetup(b1); - provider.GetRequiredService>>()(b2); - - Assert.Equivalent(azureQueueSetup, provider.GetRequiredService>>()); - Assert.Equivalent(azureEventBusSetup, provider.GetRequiredService>>()); - Assert.Equivalent(q1, q2); - Assert.Equivalent(b1, b2); - - Assert.Throws(() => Assert.Equivalent(q1, q3)); - Assert.Throws(() => Assert.Equivalent(q2, q3)); - Assert.Throws(() => Assert.Equivalent(b1, b3)); - Assert.Throws(() => Assert.Equivalent(b2, b3)); - } + o.TopicEndpoint = new Uri("https://example.com"); + o.SasCredential = new AzureSasCredential("sas"); + }; + + var services = new ServiceCollection(); + services.AddMarshaller(); + services.AddAzureEventBus(azureQueueSetup, azureEventBusSetup); + var provider = services.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(provider); + ConfiguredOptionsAssertions.Verify>(provider); + + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + Assert.IsType>(provider.GetRequiredService>()); + + var q1 = new AzureQueueOptions(); + var q2 = new AzureQueueOptions(); + var q3 = new AzureQueueOptions(); + var b1 = new DIEvent.AzureEventBusOptions(); + var b2 = new DIEvent.AzureEventBusOptions(); + var b3 = new DIEvent.AzureEventBusOptions(); + + azureQueueSetup(q1); + provider.GetRequiredService>>()(q2); + azureEventBusSetup(b1); + provider.GetRequiredService>>()(b2); + + Assert.Equivalent(azureQueueSetup, provider.GetRequiredService>>()); + Assert.Equivalent(azureEventBusSetup, provider.GetRequiredService>>()); + Assert.Equivalent(q1, q2); + Assert.Equivalent(b1, b2); + + Assert.Throws(() => Assert.Equivalent(q1, q3)); + Assert.Throws(() => Assert.Equivalent(q2, q3)); + Assert.Throws(() => Assert.Equivalent(b1, b3)); + Assert.Throws(() => Assert.Equivalent(b2, b3)); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Assets/BusMarker.cs b/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Assets/BusMarker.cs index dbbc5afd..e69d2945 100644 --- a/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Assets/BusMarker.cs +++ b/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Assets/BusMarker.cs @@ -1,7 +1,6 @@ -namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.Assets +namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.Assets; + +public struct BusMarker { - public struct BusMarker - { - } -} \ No newline at end of file +} diff --git a/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Assets/QueueMarker.cs b/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Assets/QueueMarker.cs index 06d36897..654c1008 100644 --- a/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Assets/QueueMarker.cs +++ b/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Assets/QueueMarker.cs @@ -1,7 +1,6 @@ -namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.Assets +namespace Savvyio.Extensions.DependencyInjection.RabbitMQ.Assets; + +public struct QueueMarker { - public struct QueueMarker - { - } -} \ No newline at end of file +} diff --git a/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests.csproj b/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests.csproj index f286ad51..5b6e1170 100644 --- a/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests.csproj +++ b/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/ServiceCollectionExtensionsTest.cs index 586fd5fd..d218828a 100644 --- a/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.RabbitMQ.Tests/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Microsoft.Extensions.DependencyInjection; using Savvyio.Commands; @@ -15,140 +15,139 @@ using Xunit; using Xunit.Sdk; -namespace Savvyio.Extensions.DependencyInjection.RabbitMQ +namespace Savvyio.Extensions.DependencyInjection.RabbitMQ; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddRabbitMqCommandQueue_ShouldAddDefaultImplementation() + [Fact] + public void AddRabbitMqCommandQueue_ShouldAddDefaultImplementation() + { + Action rabbitMqSetup = o => { - Action rabbitMqSetup = o => - { - o.QueueName = "urn:queue"; - }; + o.QueueName = "urn:queue"; + }; - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddRabbitMqCommandQueue(rabbitMqSetup); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(sut2); + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddRabbitMqCommandQueue(rabbitMqSetup); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(sut2); - Assert.IsType(sut2.GetRequiredService>()); - Assert.IsType(sut2.GetRequiredService>()); - Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); - var opt1 = new RabbitMqCommandQueueOptions(); - var opt2 = new RabbitMqCommandQueueOptions(); - var opt3 = new RabbitMqCommandQueueOptions(); + var opt1 = new RabbitMqCommandQueueOptions(); + var opt2 = new RabbitMqCommandQueueOptions(); + var opt3 = new RabbitMqCommandQueueOptions(); - rabbitMqSetup.Invoke(opt1); - sut2.GetRequiredService>().Invoke(opt2); + rabbitMqSetup.Invoke(opt1); + sut2.GetRequiredService>().Invoke(opt2); - Assert.Equivalent(rabbitMqSetup, sut2.GetRequiredService>()); - Assert.Equivalent(opt1, opt2); + Assert.Equivalent(rabbitMqSetup, sut2.GetRequiredService>()); + Assert.Equivalent(opt1, opt2); - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); + } - [Fact] - public void AddRabbitMqCommandQueue_ShouldAddDefaultImplementationWithMarker() + [Fact] + public void AddRabbitMqCommandQueue_ShouldAddDefaultImplementationWithMarker() + { + Action> rabbitMqSetup = o => { - Action> rabbitMqSetup = o => - { - o.QueueName = "urn:queue"; - }; + o.QueueName = "urn:queue"; + }; - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddRabbitMqCommandQueue(rabbitMqSetup); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(sut2); + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddRabbitMqCommandQueue(rabbitMqSetup); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(sut2); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); - var opt1 = new RabbitMqCommandQueueOptions(); - var opt2 = new RabbitMqCommandQueueOptions(); - var opt3 = new RabbitMqCommandQueueOptions(); + var opt1 = new RabbitMqCommandQueueOptions(); + var opt2 = new RabbitMqCommandQueueOptions(); + var opt3 = new RabbitMqCommandQueueOptions(); - rabbitMqSetup.Invoke(opt1); - sut2.GetRequiredService>>().Invoke(opt2); + rabbitMqSetup.Invoke(opt1); + sut2.GetRequiredService>>().Invoke(opt2); - Assert.Equivalent(rabbitMqSetup, sut2.GetRequiredService>>()); - Assert.Equivalent(opt1, opt2); + Assert.Equivalent(rabbitMqSetup, sut2.GetRequiredService>>()); + Assert.Equivalent(opt1, opt2); - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); + } - [Fact] - public void AddRabbitMqEventBus_ShouldAddDefaultImplementation() + [Fact] + public void AddRabbitMqEventBus_ShouldAddDefaultImplementation() + { + Action rabbitMqSetup = o => { - Action rabbitMqSetup = o => - { - o.ExchangeName = "urn:bus"; - }; + o.ExchangeName = "urn:bus"; + }; - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddRabbitMqEventBus(rabbitMqSetup); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(sut2); + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddRabbitMqEventBus(rabbitMqSetup); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(sut2); - Assert.IsType(sut2.GetRequiredService>()); - Assert.IsType(sut2.GetRequiredService>()); - Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); - var opt1 = new RabbitMqEventBusOptions(); - var opt2 = new RabbitMqEventBusOptions(); - var opt3 = new RabbitMqEventBusOptions(); + var opt1 = new RabbitMqEventBusOptions(); + var opt2 = new RabbitMqEventBusOptions(); + var opt3 = new RabbitMqEventBusOptions(); - rabbitMqSetup.Invoke(opt1); - sut2.GetRequiredService>().Invoke(opt2); + rabbitMqSetup.Invoke(opt1); + sut2.GetRequiredService>().Invoke(opt2); - Assert.Equivalent(rabbitMqSetup, sut2.GetRequiredService>()); - Assert.Equivalent(opt1, opt2); + Assert.Equivalent(rabbitMqSetup, sut2.GetRequiredService>()); + Assert.Equivalent(opt1, opt2); - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); + } - [Fact] - public void AddRabbitMqEventBus_ShouldAddDefaultImplementationWithMarker() + [Fact] + public void AddRabbitMqEventBus_ShouldAddDefaultImplementationWithMarker() + { + Action> rabbitMqSetup = o => { - Action> rabbitMqSetup = o => - { - o.ExchangeName = "urn:bus"; - }; - - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddRabbitMqEventBus(rabbitMqSetup); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(sut2); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - - var opt1 = new RabbitMqEventBusOptions(); - var opt2 = new RabbitMqEventBusOptions(); - var opt3 = new RabbitMqEventBusOptions(); - - rabbitMqSetup.Invoke(opt1); - sut2.GetRequiredService>>().Invoke(opt2); - - Assert.Equivalent(rabbitMqSetup, sut2.GetRequiredService>>()); - Assert.Equivalent(opt1, opt2); - - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } + o.ExchangeName = "urn:bus"; + }; + + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddRabbitMqEventBus(rabbitMqSetup); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(sut2); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + + var opt1 = new RabbitMqEventBusOptions(); + var opt2 = new RabbitMqEventBusOptions(); + var opt3 = new RabbitMqEventBusOptions(); + + rabbitMqSetup.Invoke(opt1); + sut2.GetRequiredService>>().Invoke(opt2); + + Assert.Equivalent(rabbitMqSetup, sut2.GetRequiredService>>()); + Assert.Equivalent(opt1, opt2); + + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/Assets/BusMarker.cs b/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/Assets/BusMarker.cs index b1aa3deb..0e292566 100644 --- a/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/Assets/BusMarker.cs +++ b/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/Assets/BusMarker.cs @@ -1,7 +1,6 @@ -namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.Assets +namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.Assets; + +public struct BusMarker { - public struct BusMarker - { - } -} \ No newline at end of file +} diff --git a/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/Assets/QueueMarker.cs b/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/Assets/QueueMarker.cs index 1cae328b..9625915f 100644 --- a/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/Assets/QueueMarker.cs +++ b/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/Assets/QueueMarker.cs @@ -1,7 +1,6 @@ -namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.Assets +namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService.Assets; + +public struct QueueMarker { - public struct QueueMarker - { - } -} \ No newline at end of file +} diff --git a/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/ServiceCollectionExtensionsTest.cs index 9c0ce9fd..428dafbc 100644 --- a/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.SimpleQueueService.Tests/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Amazon; using Amazon.Runtime; using Codebelt.Extensions.Xunit; @@ -17,148 +17,147 @@ using Xunit; using Xunit.Sdk; -namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService +namespace Savvyio.Extensions.DependencyInjection.SimpleQueueService; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddAmazonCommandQueue_ShouldAddDefaultImplementation() + [Fact] + public void AddAmazonCommandQueue_ShouldAddDefaultImplementation() + { + Action awsSetup = o => { - Action awsSetup = o => - { - o.Credentials = new AnonymousAWSCredentials(); - o.Endpoint = RegionEndpoint.EUWest1; - o.SourceQueue = new Uri("urn:queue"); - }; - - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddAmazonCommandQueue(awsSetup); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(sut2); - - Assert.IsType(sut2.GetRequiredService>()); - Assert.IsType(sut2.GetRequiredService>()); - Assert.IsType(sut2.GetRequiredService>()); - - var opt1 = new AmazonCommandQueueOptions(); - var opt2 = new AmazonCommandQueueOptions(); - var opt3 = new AmazonCommandQueueOptions(); - - awsSetup.Invoke(opt1); - sut2.GetRequiredService>().Invoke(opt2); - - Assert.Equivalent(awsSetup, sut2.GetRequiredService>()); - Assert.Equivalent(opt1, opt2); - - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } - - [Fact] - public void AddAmazonCommandQueue_ShouldAddDefaultImplementationWithMarker() + o.Credentials = new AnonymousAWSCredentials(); + o.Endpoint = RegionEndpoint.EUWest1; + o.SourceQueue = new Uri("urn:queue"); + }; + + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddAmazonCommandQueue(awsSetup); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(sut2); + + Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); + + var opt1 = new AmazonCommandQueueOptions(); + var opt2 = new AmazonCommandQueueOptions(); + var opt3 = new AmazonCommandQueueOptions(); + + awsSetup.Invoke(opt1); + sut2.GetRequiredService>().Invoke(opt2); + + Assert.Equivalent(awsSetup, sut2.GetRequiredService>()); + Assert.Equivalent(opt1, opt2); + + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); + } + + [Fact] + public void AddAmazonCommandQueue_ShouldAddDefaultImplementationWithMarker() + { + Action> awsSetup = o => { - Action> awsSetup = o => - { - o.Credentials = new AnonymousAWSCredentials(); - o.Endpoint = RegionEndpoint.EUWest1; - o.SourceQueue = new Uri("urn:queue"); - }; - - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddAmazonCommandQueue(awsSetup); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(sut2); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - - var opt1 = new AmazonCommandQueueOptions(); - var opt2 = new AmazonCommandQueueOptions(); - var opt3 = new AmazonCommandQueueOptions(); - - awsSetup.Invoke(opt1); - sut2.GetRequiredService>>().Invoke(opt2); - - Assert.Equivalent(awsSetup, sut2.GetRequiredService>>()); - Assert.Equivalent(opt1, opt2); - - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } - - [Fact] - public void AddAmazonEventBus_ShouldAddDefaultImplementation() + o.Credentials = new AnonymousAWSCredentials(); + o.Endpoint = RegionEndpoint.EUWest1; + o.SourceQueue = new Uri("urn:queue"); + }; + + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddAmazonCommandQueue(awsSetup); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(sut2); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + + var opt1 = new AmazonCommandQueueOptions(); + var opt2 = new AmazonCommandQueueOptions(); + var opt3 = new AmazonCommandQueueOptions(); + + awsSetup.Invoke(opt1); + sut2.GetRequiredService>>().Invoke(opt2); + + Assert.Equivalent(awsSetup, sut2.GetRequiredService>>()); + Assert.Equivalent(opt1, opt2); + + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); + } + + [Fact] + public void AddAmazonEventBus_ShouldAddDefaultImplementation() + { + Action awsSetup = o => { - Action awsSetup = o => - { - o.Credentials = new AnonymousAWSCredentials(); - o.Endpoint = RegionEndpoint.EUWest1; - o.SourceQueue = new Uri("urn:bus"); - }; - - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddAmazonEventBus(awsSetup); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(sut2); - - Assert.IsType(sut2.GetRequiredService>()); - Assert.IsType(sut2.GetRequiredService>()); - Assert.IsType(sut2.GetRequiredService>()); - - var opt1 = new AmazonEventBusOptions(); - var opt2 = new AmazonEventBusOptions(); - var opt3 = new AmazonEventBusOptions(); - - awsSetup.Invoke(opt1); - sut2.GetRequiredService>().Invoke(opt2); - - Assert.Equivalent(awsSetup, sut2.GetRequiredService>()); - Assert.Equivalent(opt1, opt2); - - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } - - [Fact] - public void AddAmazonEventBus_ShouldAddDefaultImplementationWithMarker() + o.Credentials = new AnonymousAWSCredentials(); + o.Endpoint = RegionEndpoint.EUWest1; + o.SourceQueue = new Uri("urn:bus"); + }; + + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddAmazonEventBus(awsSetup); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(sut2); + + Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); + Assert.IsType(sut2.GetRequiredService>()); + + var opt1 = new AmazonEventBusOptions(); + var opt2 = new AmazonEventBusOptions(); + var opt3 = new AmazonEventBusOptions(); + + awsSetup.Invoke(opt1); + sut2.GetRequiredService>().Invoke(opt2); + + Assert.Equivalent(awsSetup, sut2.GetRequiredService>()); + Assert.Equivalent(opt1, opt2); + + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); + } + + [Fact] + public void AddAmazonEventBus_ShouldAddDefaultImplementationWithMarker() + { + Action> awsSetup = o => { - Action> awsSetup = o => - { - o.Credentials = new AnonymousAWSCredentials(); - o.Endpoint = RegionEndpoint.EUWest1; - o.SourceQueue = new Uri("urn:bus"); - }; - - var sut1 = new ServiceCollection(); - sut1.AddMarshaller(); - sut1.AddAmazonEventBus(awsSetup); - var sut2 = sut1.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify>(sut2); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - - var opt1 = new AmazonEventBusOptions(); - var opt2 = new AmazonEventBusOptions(); - var opt3 = new AmazonEventBusOptions(); - - awsSetup.Invoke(opt1); - sut2.GetRequiredService>>().Invoke(opt2); - - Assert.Equivalent(awsSetup, sut2.GetRequiredService>>()); - Assert.Equivalent(opt1, opt2); - - Assert.Throws(() => Assert.Equivalent(opt1, opt3)); - Assert.Throws(() => Assert.Equivalent(opt2, opt3)); - } + o.Credentials = new AnonymousAWSCredentials(); + o.Endpoint = RegionEndpoint.EUWest1; + o.SourceQueue = new Uri("urn:bus"); + }; + + var sut1 = new ServiceCollection(); + sut1.AddMarshaller(); + sut1.AddAmazonEventBus(awsSetup); + var sut2 = sut1.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify>(sut2); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + + var opt1 = new AmazonEventBusOptions(); + var opt2 = new AmazonEventBusOptions(); + var opt3 = new AmazonEventBusOptions(); + + awsSetup.Invoke(opt1); + sut2.GetRequiredService>>().Invoke(opt2); + + Assert.Equivalent(awsSetup, sut2.GetRequiredService>>()); + Assert.Equivalent(opt1, opt2); + + Assert.Throws(() => Assert.Equivalent(opt1, opt3)); + Assert.Throws(() => Assert.Equivalent(opt2, opt3)); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeDataSource.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeDataSource.cs index 6e5749fe..55a8ca49 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeDataSource.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeDataSource.cs @@ -1,10 +1,9 @@ -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +public class FakeDataSource : IDataSource { - public class FakeDataSource : IDataSource - { - } +} - public class FakeDataSource : FakeDataSource, IDataSource - { - } +public class FakeDataSource : FakeDataSource, IDataSource +{ } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeDataStore.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeDataStore.cs index 3ec4e1db..1ba67056 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeDataStore.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeDataStore.cs @@ -1,54 +1,53 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon.Threading; using Savvyio.Data; using Savvyio.Extensions.DependencyInjection.Data; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +public class FakeDataStore : IPersistentDataStore where T : class { - public class FakeDataStore : IPersistentDataStore where T : class + public FakeDataStore(IDataSource ds) { - public FakeDataStore(IDataSource ds) - { - } + } - public Task CreateAsync(T dto, Action setup = null) - { - throw new NotImplementedException(); - } + public Task CreateAsync(T dto, Action setup = null) + { + throw new NotImplementedException(); + } - public Task DeleteAsync(T dto, Action setup = null) - { - throw new NotImplementedException(); - } + public Task DeleteAsync(T dto, Action setup = null) + { + throw new NotImplementedException(); + } - public Task> FindAllAsync(Action setup = null) - { - throw new NotImplementedException(); - } + public Task> FindAllAsync(Action setup = null) + { + throw new NotImplementedException(); + } - public Task FindAsync(Action setup = null) - { - throw new NotImplementedException(); - } + public Task FindAsync(Action setup = null) + { + throw new NotImplementedException(); + } - public Task UpdateAsync(T dto, Action setup = null) - { - throw new NotImplementedException(); - } + public Task UpdateAsync(T dto, Action setup = null) + { + throw new NotImplementedException(); + } - public Task GetByIdAsync(object id, Action setup) - { - throw new NotImplementedException(); - } + public Task GetByIdAsync(object id, Action setup) + { + throw new NotImplementedException(); } +} - public class FakeDataStore : FakeDataStore, IPersistentDataStore where T : class +public class FakeDataStore : FakeDataStore, IPersistentDataStore where T : class +{ + public FakeDataStore(IDataSource ds) : base(ds) { - public FakeDataStore(IDataSource ds) : base(ds) - { - } } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeRepository.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeRepository.cs index 66d7e4bb..abbbe7da 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeRepository.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/FakeRepository.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Threading.Tasks; @@ -6,56 +6,55 @@ using Savvyio.Domain; using Savvyio.Extensions.DependencyInjection.Domain; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +public class FakeRepository : IPersistentRepository where TEntity : class, IIdentity { - public class FakeRepository : IPersistentRepository where TEntity : class, IIdentity + public FakeRepository(IDataSource ds) { - public FakeRepository(IDataSource ds) - { - } + } - public void Add(TEntity entity) - { - throw new NotImplementedException(); - } + public void Add(TEntity entity) + { + throw new NotImplementedException(); + } - public void AddRange(IEnumerable entities) - { - throw new NotImplementedException(); - } + public void AddRange(IEnumerable entities) + { + throw new NotImplementedException(); + } - public Task> FindAllAsync(Expression> predicate = null, Action setup = null) - { - throw new NotImplementedException(); - } + public Task> FindAllAsync(Expression> predicate = null, Action setup = null) + { + throw new NotImplementedException(); + } - public Task FindAsync(Expression> predicate, Action setup = null) - { - throw new NotImplementedException(); - } + public Task FindAsync(Expression> predicate, Action setup = null) + { + throw new NotImplementedException(); + } - public Task GetByIdAsync(TKey id, Action setup = null) - { - throw new NotImplementedException(); - } + public Task GetByIdAsync(TKey id, Action setup = null) + { + throw new NotImplementedException(); + } - public void Remove(TEntity entity) - { - throw new NotImplementedException(); - } + public void Remove(TEntity entity) + { + throw new NotImplementedException(); + } - public void RemoveRange(IEnumerable entities) - { - throw new NotImplementedException(); - } + public void RemoveRange(IEnumerable entities) + { + throw new NotImplementedException(); } +} - public class FakeRepository : FakeRepository, IPersistentRepository where TEntity : class, IIdentity +public class FakeRepository : FakeRepository, IPersistentRepository where TEntity : class, IIdentity +{ + public FakeRepository(IDataSource ds) : base(ds) { - public FakeRepository(IDataSource ds) : base(ds) - { - } } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestCommand.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestCommand.cs index 3f7a7182..d5ea2785 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestCommand.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestCommand.cs @@ -1,8 +1,7 @@ -using Savvyio.Commands; +using Savvyio.Commands; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +public record TestCommand : Command { - public record TestCommand : Command - { - } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestCommandHandler.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestCommandHandler.cs index 5cde07c4..d9cc471d 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestCommandHandler.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestCommandHandler.cs @@ -1,18 +1,17 @@ -using System; +using System; using Savvyio.Commands; using Savvyio.Handlers; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +public class TestCommandHandler : CommandHandler { - public class TestCommandHandler : CommandHandler + public TestCommandHandler() { - public TestCommandHandler() - { - } + } - protected override void RegisterDelegates(IFireForgetRegistry handlers) - { - handlers.Register(_ => throw new NotImplementedException()); - } + protected override void RegisterDelegates(IFireForgetRegistry handlers) + { + handlers.Register(_ => throw new NotImplementedException()); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestDomainEvent.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestDomainEvent.cs index 87ca338c..0afc8200 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestDomainEvent.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestDomainEvent.cs @@ -1,8 +1,7 @@ -using Savvyio.Domain; +using Savvyio.Domain; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +public record TestDomainEvent : DomainEvent { - public record TestDomainEvent : DomainEvent - { - } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestDomainEventHandler.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestDomainEventHandler.cs index 4fc8e546..905a3c1d 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestDomainEventHandler.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestDomainEventHandler.cs @@ -1,13 +1,12 @@ -using Savvyio.Domain; +using Savvyio.Domain; using Savvyio.Handlers; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +public class TestDomainEventHandler : DomainEventHandler { - public class TestDomainEventHandler : DomainEventHandler + protected override void RegisterDelegates(IFireForgetRegistry handlers) { - protected override void RegisterDelegates(IFireForgetRegistry handlers) - { - handlers.Register(delegate (TestDomainEvent _) { }); - } + handlers.Register(delegate (TestDomainEvent _) { }); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestIntegrationEvent.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestIntegrationEvent.cs index a847ef77..aa06aca5 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestIntegrationEvent.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestIntegrationEvent.cs @@ -1,8 +1,7 @@ -using Savvyio.EventDriven; +using Savvyio.EventDriven; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +internal record TestIntegrationEvent : IntegrationEvent { - internal record TestIntegrationEvent : IntegrationEvent - { - } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestIntegrationEventHandler.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestIntegrationEventHandler.cs index 9f851196..54c6a871 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestIntegrationEventHandler.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestIntegrationEventHandler.cs @@ -1,19 +1,18 @@ -using System; +using System; using Savvyio.EventDriven; using Savvyio.Handlers; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +internal class TestIntegrationEventHandler : IntegrationEventHandler { - internal class TestIntegrationEventHandler : IntegrationEventHandler + protected override void RegisterDelegates(IFireForgetRegistry handlers) { - protected override void RegisterDelegates(IFireForgetRegistry handlers) - { - handlers.Register(Handler); - } + handlers.Register(Handler); + } - private void Handler(TestIntegrationEvent obj) - { - throw new NotImplementedException(); - } + private void Handler(TestIntegrationEvent obj) + { + throw new NotImplementedException(); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestQuery.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestQuery.cs index 5abb1fcd..598d4dac 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestQuery.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestQuery.cs @@ -1,9 +1,8 @@ -using System; +using System; using Savvyio.Queries; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +internal record TestQuery : Query { - internal record TestQuery : Query - { - } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestQueryHandler.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestQueryHandler.cs index 79d75907..a51abe98 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestQueryHandler.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Assets/TestQueryHandler.cs @@ -1,19 +1,18 @@ -using System; +using System; using Savvyio.Handlers; using Savvyio.Queries; -namespace Savvyio.Extensions.DependencyInjection.Assets +namespace Savvyio.Extensions.DependencyInjection.Assets; + +internal class TestQueryHandler : QueryHandler { - internal class TestQueryHandler : QueryHandler + protected override void RegisterDelegates(IRequestReplyRegistry handlers) { - protected override void RegisterDelegates(IRequestReplyRegistry handlers) - { - handlers.Register(Handler); - } + handlers.Register(Handler); + } - private Guid Handler(TestQuery arg) - { - throw new NotImplementedException(); - } + private Guid Handler(TestQuery arg) + { + throw new NotImplementedException(); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/ConfiguredOptionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/ConfiguredOptionsTest.cs index 7c39d445..fa058b1c 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/ConfiguredOptionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/ConfiguredOptionsTest.cs @@ -1,9 +1,9 @@ #nullable enable using System; using System.Collections.Generic; -using System.Linq; using Codebelt.Extensions.Xunit; using Cuemon.Configuration; +using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Xunit; @@ -21,7 +21,7 @@ public void AddConfiguredOptions_ShouldRejectNullServicesBeforeInvokingSetup() { var called = false; - var error = Assert.Throws(() => ServiceCollectionExtensions.AddConfiguredOptions(null!, _ => called = true)); + var error = Assert.Throws(() => Cuemon.Extensions.DependencyInjection.ServiceCollectionExtensions.AddConfiguredOptions(null!, _ => called = true)); Assert.Equal("services", error.ParamName); Assert.False(called); @@ -59,55 +59,77 @@ public void AddConfiguredOptions_ShouldReturnSameCollectionAndRegisterThreeConsu Assert.Same(direct, scope.ServiceProvider.GetRequiredService()); Assert.Same(callback, scope.ServiceProvider.GetRequiredService>()); Assert.Same(microsoft, scope.ServiceProvider.GetRequiredService>()); - Assert.NotSame(direct, microsoft.Value); + Assert.Same(direct, microsoft.Value); Assert.Equal(ServiceLifetime.Singleton, Assert.Single(services, descriptor => descriptor.ServiceType == typeof(Options)).Lifetime); Assert.Equal(ServiceLifetime.Singleton, Assert.Single(services, descriptor => descriptor.ServiceType == typeof(Action)).Lifetime); } - [Fact] - public void AddConfiguredOptions_ShouldConfigureEagerlyAndAgainWhenMicrosoftOptionsAreMaterialized() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void AddConfiguredOptions_ShouldConfigureOnceWhenDefaultOptionsAreMaterialized(bool resolveDirectFirst) { var services = new ServiceCollection(); var invocations = 0; services.AddConfiguredOptions(options => options.Value = (++invocations).ToString()); - Assert.Equal(1, invocations); - using var provider = services.BuildServiceProvider(); - Assert.Equal("1", provider.GetRequiredService().Value); - Assert.Equal(1, invocations); + Assert.Equal(0, invocations); + using var provider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true, ValidateOnBuild = true }); var options = provider.GetRequiredService>(); + Assert.Equal(0, invocations); + var first = resolveDirectFirst ? provider.GetRequiredService() : options.Value; + var second = resolveDirectFirst ? options.Value : provider.GetRequiredService(); + + Assert.Equal("1", first.Value); + Assert.Same(first, second); + Assert.Same(first, provider.GetRequiredService()); + Assert.Same(first, options.Value); Assert.Equal(1, invocations); - Assert.Equal("2", options.Value.Value); - Assert.Same(options.Value, options.Value); - Assert.Equal(2, invocations); } - [Fact] - public void AddConfiguredOptions_ShouldValidateEagerlyAndWrapInvalidStateWithoutRegisteringServices() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void AddConfiguredOptions_ShouldReportInvalidStateWhenOptionsAreMaterialized(bool resolveDirect) { var services = new ServiceCollection(); - var error = Assert.Throws(() => services.AddConfiguredOptions(options => options.Value = "invalid")); + services.AddConfiguredOptions(options => options.Value = "invalid"); + using var provider = services.BuildServiceProvider(); - Assert.Equal("setup", error.ParamName); - Assert.IsType(error.InnerException); - Assert.Empty(services); + var error = Assert.Throws(() => + { + if (resolveDirect) { provider.GetRequiredService(); } + else { _ = provider.GetRequiredService>().Value; } + }); + + Assert.Equal(Microsoft.Extensions.Options.Options.DefaultName, error.OptionsName); + Assert.Equal(typeof(ValidatedOptions), error.OptionsType); + Assert.Equal("Value must be valid.", Assert.Single(error.Failures)); } - [Fact] - public void AddConfiguredOptions_ShouldPropagateSetupFailureWithoutRegisteringServices() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void AddConfiguredOptions_ShouldPropagateSetupFailureWhenOptionsAreMaterialized(bool resolveDirect) { var services = new ServiceCollection(); var expected = new InvalidOperationException("Configuration failed."); - var actual = Record.Exception(() => services.AddConfiguredOptions(_ => throw expected)); + services.AddConfiguredOptions(_ => throw expected); + using var provider = services.BuildServiceProvider(); + + var actual = Record.Exception(() => + { + if (resolveDirect) { provider.GetRequiredService(); } + else { _ = provider.GetRequiredService>().Value; } + }); Assert.Same(expected, actual); - Assert.Empty(services); } [Fact] - public void AddConfiguredOptions_ShouldPostConfigureThenValidateOnlyTheEagerInstance() + public void AddConfiguredOptions_ShouldPostConfigureThenValidateSharedOptionsAndExposeRawSetup() { var services = new ServiceCollection(); services.AddConfiguredOptions(options => @@ -123,29 +145,32 @@ public void AddConfiguredOptions_ShouldPostConfigureThenValidateOnlyTheEagerInst provider.GetRequiredService>()(manual); Assert.Equal(new[] { "configure", "postconfigure", "validate" }, direct.Stages); - Assert.Equal(new[] { "configure" }, microsoft.Stages); + Assert.Same(direct, microsoft); Assert.Equal(new[] { "configure" }, manual.Stages); } [Fact] - public void AddConfiguredOptions_ShouldAppendRepeatedRegistrationsAndComposeMicrosoftConfigurations() + public void AddConfiguredOptions_ShouldKeepFirstSetupAndRegisterEachConsumptionFormOnce() { var services = new ServiceCollection(); Action first = options => options.Value += "first"; Action second = options => options.Value += "second"; services.AddConfiguredOptions(first); services.AddConfiguredOptions(second); + services.AddConfiguredOptions(first); using var provider = services.BuildServiceProvider(); - Assert.Equal(new[] { "first", "second" }, provider.GetServices().Select(options => options.Value)); - Assert.Equal("second", provider.GetRequiredService().Value); - Assert.Same(second, provider.GetRequiredService>()); - Assert.Equal(new[] { first, second }, provider.GetServices>()); - Assert.Equal("firstsecond", provider.GetRequiredService>().Value.Value); + var direct = Assert.Single(provider.GetServices()); + Assert.Equal("first", direct.Value); + Assert.Same(first, Assert.Single(provider.GetServices>())); + Assert.Same(direct, provider.GetRequiredService>().Value); + var manual = new Options(); + provider.GetRequiredService>()(manual); + Assert.Equal("first", manual.Value); } [Fact] - public void AddConfiguredOptions_ShouldComposeMicrosoftConfigureAndPostConfigureWithoutChangingDirectOptions() + public void AddConfiguredOptions_ShouldComposeMicrosoftConfigureAndPostConfigureForSharedOptions() { var services = new ServiceCollection(); services.Configure(options => options.Value += "before;"); @@ -154,8 +179,9 @@ public void AddConfiguredOptions_ShouldComposeMicrosoftConfigureAndPostConfigure services.PostConfigure(options => options.Value += "post;"); using var provider = services.BuildServiceProvider(); - Assert.Equal("primary;", provider.GetRequiredService().Value); - Assert.Equal("before;primary;after;post;", provider.GetRequiredService>().Value.Value); + var direct = provider.GetRequiredService(); + Assert.Equal("before;primary;after;post;", direct.Value); + Assert.Same(direct, provider.GetRequiredService>().Value); var manual = new Options(); provider.GetRequiredService>()(manual); Assert.Equal("primary;", manual.Value); diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/Data/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/Data/ServiceCollectionExtensionsTest.cs index 969d9e92..184f9b87 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/Data/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/Data/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Cuemon.Threading; using Microsoft.Extensions.DependencyInjection; @@ -10,118 +10,117 @@ using Savvyio.Extensions.DependencyInjection.Domain; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.Data +namespace Savvyio.Extensions.DependencyInjection.Data; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void AddDataStore_ShouldAddDefaultImplementation() + { + var sut1 = new ServiceCollection(); + sut1.AddDataSource(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType(sut2.GetRequiredService()); + } + + [Fact] + public void AddDataStore_ShouldAddDefaultImplementation_Implicit() + { + var sut1 = new ServiceCollection(); + sut1.AddDataSource(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType(sut2.GetRequiredService()); + } + + [Fact] + public void AddDataStore_ShouldAddDefaultImplementationWithMarker() + { + var sut1 = new ServiceCollection(); + sut1.AddDataSource>(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>()); + } + + [Fact] + public void AddDataStore_ShouldAddDefaultImplementationWithMarker_Implicit() + { + var sut1 = new ServiceCollection(); + sut1.AddDataSource>(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>()); + } + + [Fact] + public void AddRepository_ShouldAddManyImplementations() + { + var sut1 = new ServiceCollection(); + sut1.AddDataSource(); + sut1.AddRepository, Account, long>(); + sut1.AddRepository, PlatformProvider, Guid>(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + } + + [Fact] + public void AddRepository_ShouldAddManyImplementationsWithMarkers() + { + var sut1 = new ServiceCollection(); + sut1.AddDataSource>(); + sut1.AddRepository, Account, long>(); + sut1.AddRepository, PlatformProvider, Guid>(); + sut1.AddDataSource>(); + sut1.AddRepository, Account, long>(); + sut1.AddRepository, PlatformProvider, Guid>(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); + Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); + } + + [Fact] + public void AddDataAccessObject_ShouldAddManyImplementations() + { + var sut1 = new ServiceCollection(); + sut1.AddDataSource(); + sut1.AddDataStore, Account>(); + sut1.AddDataStore, PlatformProvider>(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + } + + [Fact] + public void AddDataAccessObject_ShouldAddManyImplementationsWithMarkers() { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void AddDataStore_ShouldAddDefaultImplementation() - { - var sut1 = new ServiceCollection(); - sut1.AddDataSource(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType(sut2.GetRequiredService()); - } - - [Fact] - public void AddDataStore_ShouldAddDefaultImplementation_Implicit() - { - var sut1 = new ServiceCollection(); - sut1.AddDataSource(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType(sut2.GetRequiredService()); - } - - [Fact] - public void AddDataStore_ShouldAddDefaultImplementationWithMarker() - { - var sut1 = new ServiceCollection(); - sut1.AddDataSource>(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>()); - } - - [Fact] - public void AddDataStore_ShouldAddDefaultImplementationWithMarker_Implicit() - { - var sut1 = new ServiceCollection(); - sut1.AddDataSource>(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>()); - } - - [Fact] - public void AddRepository_ShouldAddManyImplementations() - { - var sut1 = new ServiceCollection(); - sut1.AddDataSource(); - sut1.AddRepository, Account, long>(); - sut1.AddRepository, PlatformProvider, Guid>(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - } - - [Fact] - public void AddRepository_ShouldAddManyImplementationsWithMarkers() - { - var sut1 = new ServiceCollection(); - sut1.AddDataSource>(); - sut1.AddRepository, Account, long>(); - sut1.AddRepository, PlatformProvider, Guid>(); - sut1.AddDataSource>(); - sut1.AddRepository, Account, long>(); - sut1.AddRepository, PlatformProvider, Guid>(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - } - - [Fact] - public void AddDataAccessObject_ShouldAddManyImplementations() - { - var sut1 = new ServiceCollection(); - sut1.AddDataSource(); - sut1.AddDataStore, Account>(); - sut1.AddDataStore, PlatformProvider>(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - } - - [Fact] - public void AddDataAccessObject_ShouldAddManyImplementationsWithMarkers() - { - var sut1 = new ServiceCollection(); - sut1.AddDataSource>(); - sut1.AddDataStore, Account>(); - sut1.AddDataStore, PlatformProvider>(); - sut1.AddDataSource>(); - sut1.AddDataStore, Account>(); - sut1.AddDataStore, PlatformProvider>(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - } + var sut1 = new ServiceCollection(); + sut1.AddDataSource>(); + sut1.AddDataStore, Account>(); + sut1.AddDataStore, PlatformProvider>(); + sut1.AddDataSource>(); + sut1.AddDataStore, Account>(); + sut1.AddDataStore, PlatformProvider>(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); + Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/SavvyioDependencyInjectionOptionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/SavvyioDependencyInjectionOptionsTest.cs index 9554aae9..ad790c0c 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/SavvyioDependencyInjectionOptionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/SavvyioDependencyInjectionOptionsTest.cs @@ -1,31 +1,30 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace Savvyio.Extensions.DependencyInjection +namespace Savvyio.Extensions.DependencyInjection; + +public class SavvyioDependencyInjectionOptionsTest : Test { - public class SavvyioDependencyInjectionOptionsTest : Test + public SavvyioDependencyInjectionOptionsTest(ITestOutputHelper output) : base(output) { - public SavvyioDependencyInjectionOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void SavvyioDependencyInjectionOptions_Ensure_Initialization_Defaults() - { - var sut = new SavvyioDependencyInjectionOptions(); + [Fact] + public void SavvyioDependencyInjectionOptions_Ensure_Initialization_Defaults() + { + var sut = new SavvyioDependencyInjectionOptions(); - Assert.False(sut.AllowDispatcherDiscovery); - Assert.False(sut.AllowHandlerDiscovery); - Assert.False(sut.IncludeHandlerServicesDescriptor); - Assert.Equal(ServiceLifetime.Transient, sut.ServiceLocatorLifetime); - Assert.Equal(ServiceLifetime.Transient, sut.HandlerServicesLifetime); - Assert.Equal(ServiceLifetime.Transient, sut.DispatcherServicesLifetime); - Assert.Empty(sut.DispatcherImplementationTypes); - Assert.Empty(sut.DispatcherServiceTypes); - Assert.Empty(sut.HandlerImplementationTypes); - Assert.Empty(sut.HandlerServiceTypes); - Assert.Empty(sut.AssembliesToScan); - } + Assert.False(sut.AllowDispatcherDiscovery); + Assert.False(sut.AllowHandlerDiscovery); + Assert.False(sut.IncludeHandlerServicesDescriptor); + Assert.Equal(ServiceLifetime.Transient, sut.ServiceLocatorLifetime); + Assert.Equal(ServiceLifetime.Transient, sut.HandlerServicesLifetime); + Assert.Equal(ServiceLifetime.Transient, sut.DispatcherServicesLifetime); + Assert.Empty(sut.DispatcherImplementationTypes); + Assert.Empty(sut.DispatcherServiceTypes); + Assert.Empty(sut.HandlerImplementationTypes); + Assert.Empty(sut.HandlerServiceTypes); + Assert.Empty(sut.AssembliesToScan); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/ServiceCollectionExtensionsTest.cs index d5d0311b..8004ee00 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Microsoft.Extensions.DependencyInjection; using Savvyio.Commands; using Savvyio.Dispatchers; @@ -8,48 +8,48 @@ using Savvyio.Queries; using Xunit; -namespace Savvyio.Extensions.DependencyInjection +namespace Savvyio.Extensions.DependencyInjection; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddSavvyIO_ShouldIncludeHandlerServicesDescriptor() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => o.EnableHandlerServicesDescriptor()); - sut1.AddHandlerServicesDescriptor(); - var sut2 = sut1.BuildServiceProvider(); - var sut3 = sut2.GetRequiredService(); - Assert.NotNull(sut3); - } - - [Fact] - public void AddSavvyIO_ShouldIncludeServiceFactory() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(); - var sut2 = sut1.BuildServiceProvider(); - var sut3 = sut2.GetRequiredService(); - Assert.NotNull(sut3); - } - - [Fact] - public void AddSavvyIO_ShouldIncludeCommandHandlerFromLambda() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(Command).Assembly, typeof(TestCommand).Assembly).EnableHandlerServicesDescriptor().EnableDispatcherDiscovery().EnableHandlerDiscovery()); - sut1.AddHandlerServicesDescriptor(); - var sut2 = sut1.BuildServiceProvider(); + [Fact] + public void AddSavvyIO_ShouldIncludeHandlerServicesDescriptor() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => o.EnableHandlerServicesDescriptor()); + sut1.AddHandlerServicesDescriptor(); + var sut2 = sut1.BuildServiceProvider(); + var sut3 = sut2.GetRequiredService(); + Assert.NotNull(sut3); + } - var plainText = sut2.GetRequiredService().ToString(); + [Fact] + public void AddSavvyIO_ShouldIncludeServiceFactory() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(); + var sut2 = sut1.BuildServiceProvider(); + var sut3 = sut2.GetRequiredService(); + Assert.NotNull(sut3); + } + + [Fact] + public void AddSavvyIO_ShouldIncludeCommandHandlerFromLambda() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(Command).Assembly, typeof(TestCommand).Assembly).EnableHandlerServicesDescriptor().EnableDispatcherDiscovery().EnableHandlerDiscovery()); + sut1.AddHandlerServicesDescriptor(); + var sut2 = sut1.BuildServiceProvider(); + + var plainText = sut2.GetRequiredService().ToString(); - TestOutput.WriteLine(plainText); + TestOutput.WriteLine(plainText); - Assert.True(Match(""" + Assert.True(Match(""" Discovered 1 ICommandHandler implementation covering a total of 1 ICommand method Assembly: Savvyio.Extensions.DependencyInjection.Tests @@ -91,58 +91,58 @@ Discovered 1 IQueryHandler implementation covering a total of 1 IQuery method ----------------------------------------------------------------------------- """.ReplaceLineEndings(), plainText, o => o.ThrowOnNoMatch = true)); - Assert.IsType(sut2.GetRequiredService()); - } + Assert.IsType(sut2.GetRequiredService()); + } - [Fact] - public void AddSavvyIO_ShouldIncludeCommandDispatchers() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => o.AddAssemblyToScan(typeof(Command).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); - var sut2 = sut1.BuildServiceProvider(); + [Fact] + public void AddSavvyIO_ShouldIncludeCommandDispatchers() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => o.AddAssemblyToScan(typeof(Command).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType(sut2.GetRequiredService()); - } + Assert.IsType(sut2.GetRequiredService()); + } - [Fact] - public void AddSavvyIO_ShouldNotIncludeCommandDispatchers() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => - { - o.AddAssemblyToScan(typeof(Command).Assembly); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.Null(sut2.GetService()); - } - - [Fact] - public void AddSavvyIO_ShouldNotIncludeCommandHandlerFromLamda() + [Fact] + public void AddSavvyIO_ShouldNotIncludeCommandDispatchers() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => - { - o.AddAssemblyRangeToScan(typeof(Command).Assembly, typeof(TestCommand).Assembly); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.Null(sut2.GetService()); - } - - [Fact] - public void AddSavvyIO_ShouldIncludeDomainEventHandlerFromDelegate() + o.AddAssemblyToScan(typeof(Command).Assembly); + }); + var sut2 = sut1.BuildServiceProvider(); + + Assert.Null(sut2.GetService()); + } + + [Fact] + public void AddSavvyIO_ShouldNotIncludeCommandHandlerFromLamda() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEvent).Assembly, typeof(TestDomainEvent).Assembly).EnableHandlerServicesDescriptor().EnableDispatcherDiscovery().EnableHandlerDiscovery()); - sut1.AddHandlerServicesDescriptor(); - var sut2 = sut1.BuildServiceProvider(); + o.AddAssemblyRangeToScan(typeof(Command).Assembly, typeof(TestCommand).Assembly); + }); + var sut2 = sut1.BuildServiceProvider(); + + Assert.Null(sut2.GetService()); + } - var plainText = sut2.GetRequiredService().ToString(); + [Fact] + public void AddSavvyIO_ShouldIncludeDomainEventHandlerFromDelegate() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(DomainEvent).Assembly, typeof(TestDomainEvent).Assembly).EnableHandlerServicesDescriptor().EnableDispatcherDiscovery().EnableHandlerDiscovery()); + sut1.AddHandlerServicesDescriptor(); + var sut2 = sut1.BuildServiceProvider(); - TestOutput.WriteLine(plainText); + var plainText = sut2.GetRequiredService().ToString(); - Assert.True(Match(""" + TestOutput.WriteLine(plainText); + + Assert.True(Match(""" Discovered 1 ICommandHandler implementation covering a total of 1 ICommand method Assembly: Savvyio.Extensions.DependencyInjection.Tests @@ -184,163 +184,162 @@ Discovered 1 IQueryHandler implementation covering a total of 1 IQuery method ----------------------------------------------------------------------------- """.ReplaceLineEndings(), plainText, o => o.ThrowOnNoMatch = true)); - Assert.IsType(sut2.GetRequiredService()); - } + Assert.IsType(sut2.GetRequiredService()); + } - [Fact] - public void AddSavvyIO_ShouldIncludeDomainEventDispatchers() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => o.AddAssemblyToScan(typeof(DomainEvent).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); - var sut2 = sut1.BuildServiceProvider(); + [Fact] + public void AddSavvyIO_ShouldIncludeDomainEventDispatchers() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => o.AddAssemblyToScan(typeof(DomainEvent).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType(sut2.GetRequiredService()); - } + Assert.IsType(sut2.GetRequiredService()); + } - [Fact] - public void AddSavvyIO_ShouldNotIncludeDomainEventDispatchers() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => - { - o.AddAssemblyToScan(typeof(DomainEvent).Assembly); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.Null(sut2.GetService()); - } - - [Fact] - public void AddSavvyIO_ShouldNotIncludeDomainEventHandlerFromDelegate() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => - { - o.AddAssemblyRangeToScan(typeof(DomainEvent).Assembly, typeof(TestDomainEvent).Assembly); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.Null(sut2.GetService()); - } - - [Fact] - public void AddSavvyIO_ShouldIncludeIntegrationEventHandlerFromDeclaredMethod() + [Fact] + public void AddSavvyIO_ShouldNotIncludeDomainEventDispatchers() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(IntegrationEvent).Assembly, typeof(TestIntegrationEvent).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); - var sut2 = sut1.BuildServiceProvider(); + o.AddAssemblyToScan(typeof(DomainEvent).Assembly); + }); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType(sut2.GetRequiredService()); - } + Assert.Null(sut2.GetService()); + } - [Fact] - public void AddSavvyIO_ShouldIncludeIntegrationEventDispatchers() + [Fact] + public void AddSavvyIO_ShouldNotIncludeDomainEventHandlerFromDelegate() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => o.AddAssemblyToScan(typeof(IntegrationEvent).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); - var sut2 = sut1.BuildServiceProvider(); + o.AddAssemblyRangeToScan(typeof(DomainEvent).Assembly, typeof(TestDomainEvent).Assembly); + }); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType(sut2.GetRequiredService()); - } + Assert.Null(sut2.GetService()); + } - [Fact] - public void AddSavvyIO_ShouldNotIncludeIntegrationEventDispatchers() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => - { - o.AddAssemblyToScan(typeof(IntegrationEvent).Assembly); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.Null(sut2.GetService()); - } - - [Fact] - public void AddSavvyIO_ShouldNotIncludeIntegrationEventHandlerFromDeclaredMethod() - { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => - { - o.AddAssemblyRangeToScan(typeof(IntegrationEvent).Assembly, typeof(TestIntegrationEvent).Assembly); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.Null(sut2.GetService()); - } - - [Fact] - public void AddSavvyIO_ShouldIncludeQueryHandlerFromDeclaredMethod() + [Fact] + public void AddSavvyIO_ShouldIncludeIntegrationEventHandlerFromDeclaredMethod() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(IntegrationEvent).Assembly, typeof(TestIntegrationEvent).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType(sut2.GetRequiredService()); + } + + [Fact] + public void AddSavvyIO_ShouldIncludeIntegrationEventDispatchers() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => o.AddAssemblyToScan(typeof(IntegrationEvent).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType(sut2.GetRequiredService()); + } + + [Fact] + public void AddSavvyIO_ShouldNotIncludeIntegrationEventDispatchers() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(Query<>).Assembly, typeof(TestQuery).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); - var sut2 = sut1.BuildServiceProvider(); + o.AddAssemblyToScan(typeof(IntegrationEvent).Assembly); + }); + var sut2 = sut1.BuildServiceProvider(); - Assert.IsType(sut2.GetRequiredService()); - } + Assert.Null(sut2.GetService()); + } - [Fact] - public void AddSavvyIO_ShouldIncludeQueryDispatchers() + [Fact] + public void AddSavvyIO_ShouldNotIncludeIntegrationEventHandlerFromDeclaredMethod() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => o.AddAssemblyToScan(typeof(Query<>).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); - var sut2 = sut1.BuildServiceProvider(); + o.AddAssemblyRangeToScan(typeof(IntegrationEvent).Assembly, typeof(TestIntegrationEvent).Assembly); + }); + var sut2 = sut1.BuildServiceProvider(); + + Assert.Null(sut2.GetService()); + } - Assert.IsType(sut2.GetRequiredService()); - } + [Fact] + public void AddSavvyIO_ShouldIncludeQueryHandlerFromDeclaredMethod() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => o.AddAssemblyRangeToScan(typeof(Query<>).Assembly, typeof(TestQuery).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); + var sut2 = sut1.BuildServiceProvider(); - [Fact] - public void AddSavvyIO_ShouldNotIncludeQueryDispatchers() + Assert.IsType(sut2.GetRequiredService()); + } + + [Fact] + public void AddSavvyIO_ShouldIncludeQueryDispatchers() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => o.AddAssemblyToScan(typeof(Query<>).Assembly).EnableDispatcherDiscovery().EnableHandlerDiscovery()); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType(sut2.GetRequiredService()); + } + + [Fact] + public void AddSavvyIO_ShouldNotIncludeQueryDispatchers() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => - { - o.AddAssemblyToScan(typeof(Query<>).Assembly); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.Null(sut2.GetService()); - } - - [Fact] - public void AddSavvyIO_ShouldNotIncludeQueryHandlerFromDeclaredMethod() + o.AddAssemblyToScan(typeof(Query<>).Assembly); + }); + var sut2 = sut1.BuildServiceProvider(); + + Assert.Null(sut2.GetService()); + } + + [Fact] + public void AddSavvyIO_ShouldNotIncludeQueryHandlerFromDeclaredMethod() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => - { - o.AddAssemblyRangeToScan(typeof(Query<>).Assembly, typeof(TestQuery).Assembly); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.Null(sut2.GetService()); - } - - [Fact] - public void AddSavvyIO_ShouldHaveAllManuallyAddedHandlersAndDispatchers() + o.AddAssemblyRangeToScan(typeof(Query<>).Assembly, typeof(TestQuery).Assembly); + }); + var sut2 = sut1.BuildServiceProvider(); + + Assert.Null(sut2.GetService()); + } + + [Fact] + public void AddSavvyIO_ShouldHaveAllManuallyAddedHandlersAndDispatchers() + { + var sut1 = new ServiceCollection(); + sut1.AddSavvyIO(o => { - var sut1 = new ServiceCollection(); - sut1.AddSavvyIO(o => - { - o.AddCommandDispatcher() - .AddDomainEventDispatcher() - .AddIntegrationEventDispatcher() - .AddQueryDispatcher(); - o.AddCommandHandler() - .AddDomainEventHandler() - .AddIntegrationEventHandler() - .AddQueryHandler(); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.NotNull(sut2.GetService()); - Assert.NotNull(sut2.GetService()); - Assert.NotNull(sut2.GetService()); - Assert.NotNull(sut2.GetService()); - - Assert.NotNull(sut2.GetService()); - Assert.NotNull(sut2.GetService()); - Assert.NotNull(sut2.GetService()); - Assert.NotNull(sut2.GetService()); - } + o.AddCommandDispatcher() + .AddDomainEventDispatcher() + .AddIntegrationEventDispatcher() + .AddQueryDispatcher(); + o.AddCommandHandler() + .AddDomainEventHandler() + .AddIntegrationEventHandler() + .AddQueryHandler(); + }); + var sut2 = sut1.BuildServiceProvider(); + + Assert.NotNull(sut2.GetService()); + Assert.NotNull(sut2.GetService()); + Assert.NotNull(sut2.GetService()); + Assert.NotNull(sut2.GetService()); + + Assert.NotNull(sut2.GetService()); + Assert.NotNull(sut2.GetService()); + Assert.NotNull(sut2.GetService()); + Assert.NotNull(sut2.GetService()); } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Tests/ServiceCollectionRegistrationTest.cs b/test/Savvyio.Extensions.DependencyInjection.Tests/ServiceCollectionRegistrationTest.cs index 57a980e8..2b93da6f 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Tests/ServiceCollectionRegistrationTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Tests/ServiceCollectionRegistrationTest.cs @@ -3,103 +3,103 @@ using System.IO; using Codebelt.Extensions.Xunit; using Cuemon.Configuration; +using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Savvyio.Dispatchers; using Xunit; -namespace Savvyio.Extensions.DependencyInjection +namespace Savvyio.Extensions.DependencyInjection; + +public class ServiceCollectionRegistrationTest : Test { - public class ServiceCollectionRegistrationTest : Test + public ServiceCollectionRegistrationTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionRegistrationTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddConfiguredOptions_ShouldRegisterAllSupportedRepresentations() - { - var services = new ServiceCollection(); - services.AddConfiguredOptions(o => o.Name = "Configured"); - var provider = services.BuildServiceProvider(); - var action = provider.GetRequiredService>(); - var target = new TestOptions(); + [Fact] + public void AddConfiguredOptions_ShouldRegisterAllSupportedRepresentations() + { + var services = new ServiceCollection(); + services.AddConfiguredOptions(o => o.Name = "Configured"); + var provider = services.BuildServiceProvider(); + var action = provider.GetRequiredService>(); + var target = new TestOptions(); - action(target); + action(target); - Assert.Equal("Configured", provider.GetRequiredService>().Value.Name); - Assert.Equal("Configured", provider.GetRequiredService().Name); - Assert.Equal("Configured", target.Name); - } + Assert.Equal("Configured", provider.GetRequiredService>().Value.Name); + Assert.Equal("Configured", provider.GetRequiredService().Name); + Assert.Equal("Configured", target.Name); + } + + [Fact] + public void AddMarshaller_ShouldRegisterFactoryImplementation() + { + var services = new ServiceCollection(); + services.AddMarshaller(_ => new FakeMarshaller(), o => o.Lifetime = ServiceLifetime.Singleton); + var provider = services.BuildServiceProvider(); + + var sut1 = provider.GetRequiredService(); + var sut2 = provider.GetRequiredService(); + + Assert.IsType(sut1); + Assert.Same(sut1, sut2); + } - [Fact] - public void AddMarshaller_ShouldRegisterFactoryImplementation() + [Fact] + public void AddServiceLocator_ShouldRespectCustomFactoryAndLifetime() + { + var services = new ServiceCollection(); + services.AddServiceLocator(o => { - var services = new ServiceCollection(); - services.AddMarshaller(_ => new FakeMarshaller(), o => o.Lifetime = ServiceLifetime.Singleton); - var provider = services.BuildServiceProvider(); + o.Lifetime = ServiceLifetime.Singleton; + o.ImplementationFactory = _ => new ServiceLocator(_ => Array.Empty()); + }); + var provider = services.BuildServiceProvider(); - var sut1 = provider.GetRequiredService(); - var sut2 = provider.GetRequiredService(); + var sut1 = provider.GetRequiredService(); + var sut2 = provider.GetRequiredService(); - Assert.IsType(sut1); - Assert.Same(sut1, sut2); - } + Assert.Same(sut1, sut2); + Assert.Empty(sut1.GetServices(typeof(string))); + } + + [Fact] + public void AddHandlerServicesDescriptor_ShouldDoNothingWhenDescriptorIsMissing() + { + var services = new ServiceCollection(); + services.AddHandlerServicesDescriptor(); + var provider = services.BuildServiceProvider(); + + Assert.Null(provider.GetService()); + } + + private sealed class TestOptions : IParameterObject + { + public string Name { get; set; } + } - [Fact] - public void AddServiceLocator_ShouldRespectCustomFactoryAndLifetime() + private sealed class FakeMarshaller : IMarshaller + { + public Stream Serialize(TValue value) { - var services = new ServiceCollection(); - services.AddServiceLocator(o => - { - o.Lifetime = ServiceLifetime.Singleton; - o.ImplementationFactory = _ => new ServiceLocator(_ => Array.Empty()); - }); - var provider = services.BuildServiceProvider(); - - var sut1 = provider.GetRequiredService(); - var sut2 = provider.GetRequiredService(); - - Assert.Same(sut1, sut2); - Assert.Empty(sut1.GetServices(typeof(string))); + return new MemoryStream(new byte[] { 1, 2, 3 }); } - [Fact] - public void AddHandlerServicesDescriptor_ShouldDoNothingWhenDescriptorIsMissing() + public Stream Serialize(object value, Type inputType) { - var services = new ServiceCollection(); - services.AddHandlerServicesDescriptor(); - var provider = services.BuildServiceProvider(); - - Assert.Null(provider.GetService()); + return new MemoryStream(new byte[] { 1, 2, 3 }); } - private sealed class TestOptions : IParameterObject + public TValue Deserialize(Stream data) { - public string Name { get; set; } + return default; } - private sealed class FakeMarshaller : IMarshaller + public object Deserialize(Stream data, Type returnType) { - public Stream Serialize(TValue value) - { - return new MemoryStream(new byte[] { 1, 2, 3 }); - } - - public Stream Serialize(object value, Type inputType) - { - return new MemoryStream(new byte[] { 1, 2, 3 }); - } - - public TValue Deserialize(Stream data) - { - return default; - } - - public object Deserialize(Stream data, Type returnType) - { - return null; - } + return null; } } } diff --git a/test/Savvyio.Extensions.DependencyInjection.Text.Json.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.DependencyInjection.Text.Json.Tests/ServiceCollectionExtensionsTest.cs index 549e9a5a..05bb2cce 100644 --- a/test/Savvyio.Extensions.DependencyInjection.Text.Json.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.DependencyInjection.Text.Json.Tests/ServiceCollectionExtensionsTest.cs @@ -6,83 +6,82 @@ using Savvyio.Extensions.Text.Json; using Xunit; -namespace Savvyio.Extensions.DependencyInjection.Text.Json.Tests +namespace Savvyio.Extensions.DependencyInjection.Text.Json.Tests; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + [Fact] + public void AddJsonMarshaller_ShouldThrowOnNullServices() { - [Fact] - public void AddJsonMarshaller_ShouldThrowOnNullServices() - { - // Arrange - IServiceCollection services = null; + // Arrange + IServiceCollection services = null; - // Act & Assert - Assert.Throws(() => - ServiceCollectionExtensions.AddJsonMarshaller(services)); - } + // Act & Assert + Assert.Throws(() => + ServiceCollectionExtensions.AddJsonMarshaller(services)); + } - [Fact] - public void AddJsonMarshaller_ShouldRegisterJsonMarshallerWithDefaultOptions() - { - // Arrange - var services = new ServiceCollection(); + [Fact] + public void AddJsonMarshaller_ShouldRegisterJsonMarshallerWithDefaultOptions() + { + // Arrange + var services = new ServiceCollection(); - // Act - services.AddJsonMarshaller(); + // Act + services.AddJsonMarshaller(); - // Assert - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(provider); - var marshaller = provider.GetService(); - Assert.NotNull(marshaller); + // Assert + var provider = services.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(provider); + var marshaller = provider.GetService(); + Assert.NotNull(marshaller); - // Check that options are registered and default WriteIndented is false - var options = provider.GetService(); - Assert.NotNull(options); - Assert.False(options.Settings.WriteIndented); - } + // Check that options are registered and default WriteIndented is false + var options = provider.GetService(); + Assert.NotNull(options); + Assert.False(options.Settings.WriteIndented); + } - [Fact] - public void AddJsonMarshaller_ShouldApplyCustomJsonSetup() + [Fact] + public void AddJsonMarshaller_ShouldApplyCustomJsonSetup() + { + // Arrange + var services = new ServiceCollection(); + var called = false; + + // Act + services.AddJsonMarshaller(o => { - // Arrange - var services = new ServiceCollection(); - var called = false; + o.Settings.WriteIndented = true; + called = true; + }); - // Act - services.AddJsonMarshaller(o => - { - o.Settings.WriteIndented = true; - called = true; - }); + // Assert + var provider = services.BuildServiceProvider(); + ConfiguredOptionsAssertions.Verify(provider); + var options = provider.GetService(); + Assert.NotNull(options); + Assert.True(called); + Assert.True(options.Settings.WriteIndented); + } - // Assert - var provider = services.BuildServiceProvider(); - ConfiguredOptionsAssertions.Verify(provider); - var options = provider.GetService(); - Assert.NotNull(options); - Assert.True(called); - Assert.True(options.Settings.WriteIndented); - } + [Fact] + public void AddJsonMarshaller_ShouldApplyCustomServiceSetup() + { + // Arrange + var services = new ServiceCollection(); + ServiceLifetime? lifetime = null; - [Fact] - public void AddJsonMarshaller_ShouldApplyCustomServiceSetup() + // Act + services.AddJsonMarshaller(null, o => { - // Arrange - var services = new ServiceCollection(); - ServiceLifetime? lifetime = null; - - // Act - services.AddJsonMarshaller(null, o => - { - o.Lifetime = ServiceLifetime.Scoped; - lifetime = o.Lifetime; - }); + o.Lifetime = ServiceLifetime.Scoped; + lifetime = o.Lifetime; + }); - // Assert - var descriptor = Assert.Single(services, d => d.ServiceType == typeof(JsonMarshaller)); - Assert.Equal(ServiceLifetime.Scoped, descriptor.Lifetime); - Assert.Equal(ServiceLifetime.Scoped, lifetime); - } + // Assert + var descriptor = Assert.Single(services, d => d.ServiceType == typeof(JsonMarshaller)); + Assert.Equal(ServiceLifetime.Scoped, descriptor.Lifetime); + Assert.Equal(ServiceLifetime.Scoped, lifetime); } } diff --git a/test/Savvyio.Extensions.Dispatchers.Tests/MediatorSyncTest.cs b/test/Savvyio.Extensions.Dispatchers.Tests/MediatorSyncTest.cs index a9634f1b..647a856e 100644 --- a/test/Savvyio.Extensions.Dispatchers.Tests/MediatorSyncTest.cs +++ b/test/Savvyio.Extensions.Dispatchers.Tests/MediatorSyncTest.cs @@ -9,127 +9,126 @@ using Savvyio.Queries; using Xunit; -namespace Savvyio.Extensions +namespace Savvyio.Extensions; + +public class MediatorSyncTest : Test { - public class MediatorSyncTest : Test + public MediatorSyncTest(ITestOutputHelper output) : base(output) { - public MediatorSyncTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Commit_ShouldDispatchCommandSynchronously() - { - var provider = CreateServiceProvider(services => services.AddTransient()); - var sut = new Mediator(provider.GetRequiredService()); + [Fact] + public void Commit_ShouldDispatchCommandSynchronously() + { + var provider = CreateServiceProvider(services => services.AddTransient()); + var sut = new Mediator(provider.GetRequiredService()); - sut.Commit(new TestMediatorCommand()); + sut.Commit(new TestMediatorCommand()); - Assert.Collection(provider.GetRequiredService>().Query(), item => Assert.Equal("command", item)); - } + Assert.Collection(provider.GetRequiredService>().Query(), item => Assert.Equal("command", item)); + } - [Fact] - public void Raise_ShouldDispatchDomainEventSynchronously() - { - var provider = CreateServiceProvider(services => services.AddTransient()); - var sut = new Mediator(provider.GetRequiredService()); + [Fact] + public void Raise_ShouldDispatchDomainEventSynchronously() + { + var provider = CreateServiceProvider(services => services.AddTransient()); + var sut = new Mediator(provider.GetRequiredService()); - sut.Raise(new TestMediatorDomainEvent()); + sut.Raise(new TestMediatorDomainEvent()); - Assert.Collection(provider.GetRequiredService>().Query(), item => Assert.Equal("domain-event", item)); - } - - [Fact] - public void Publish_ShouldDispatchIntegrationEventSynchronously() - { - var provider = CreateServiceProvider(services => services.AddTransient()); - var sut = new Mediator(provider.GetRequiredService()); + Assert.Collection(provider.GetRequiredService>().Query(), item => Assert.Equal("domain-event", item)); + } - sut.Publish(new TestMediatorIntegrationEvent()); + [Fact] + public void Publish_ShouldDispatchIntegrationEventSynchronously() + { + var provider = CreateServiceProvider(services => services.AddTransient()); + var sut = new Mediator(provider.GetRequiredService()); - Assert.Collection(provider.GetRequiredService>().Query(), item => Assert.Equal("integration-event", item)); - } + sut.Publish(new TestMediatorIntegrationEvent()); - [Fact] - public void Query_ShouldDispatchQuerySynchronously() - { - var provider = CreateServiceProvider(services => services.AddTransient()); - var sut = new Mediator(provider.GetRequiredService()); + Assert.Collection(provider.GetRequiredService>().Query(), item => Assert.Equal("integration-event", item)); + } - var result = sut.Query(new TestMediatorQuery()); + [Fact] + public void Query_ShouldDispatchQuerySynchronously() + { + var provider = CreateServiceProvider(services => services.AddTransient()); + var sut = new Mediator(provider.GetRequiredService()); - Assert.Equal("query", result); - } + var result = sut.Query(new TestMediatorQuery()); - private static ServiceProvider CreateServiceProvider(System.Action setup) - { - var services = new ServiceCollection() - .AddServiceLocator() - .AddSingleton, InMemoryTestStore>(); + Assert.Equal("query", result); + } - setup(services); - return services.BuildServiceProvider(); - } + private static ServiceProvider CreateServiceProvider(System.Action setup) + { + var services = new ServiceCollection() + .AddServiceLocator() + .AddSingleton, InMemoryTestStore>(); - private sealed record TestMediatorCommand : Command; + setup(services); + return services.BuildServiceProvider(); + } - private sealed record TestMediatorDomainEvent : DomainEvent; + private sealed record TestMediatorCommand : Command; - private sealed record TestMediatorIntegrationEvent : IntegrationEvent; + private sealed record TestMediatorDomainEvent : DomainEvent; - private sealed record TestMediatorQuery : Query; + private sealed record TestMediatorIntegrationEvent : IntegrationEvent; - private sealed class TestMediatorCommandHandler : CommandHandler - { - private readonly ITestStore _store; + private sealed record TestMediatorQuery : Query; - public TestMediatorCommandHandler(ITestStore store) - { - _store = store; - } + private sealed class TestMediatorCommandHandler : CommandHandler + { + private readonly ITestStore _store; - protected override void RegisterDelegates(IFireForgetRegistry handlers) - { - handlers.Register(_ => _store.Add("command")); - } + public TestMediatorCommandHandler(ITestStore store) + { + _store = store; } - private sealed class TestMediatorDomainEventHandler : DomainEventHandler + protected override void RegisterDelegates(IFireForgetRegistry handlers) { - private readonly ITestStore _store; + handlers.Register(_ => _store.Add("command")); + } + } - public TestMediatorDomainEventHandler(ITestStore store) - { - _store = store; - } + private sealed class TestMediatorDomainEventHandler : DomainEventHandler + { + private readonly ITestStore _store; - protected override void RegisterDelegates(IFireForgetRegistry handlers) - { - handlers.Register(_ => _store.Add("domain-event")); - } + public TestMediatorDomainEventHandler(ITestStore store) + { + _store = store; } - private sealed class TestMediatorIntegrationEventHandler : IntegrationEventHandler + protected override void RegisterDelegates(IFireForgetRegistry handlers) { - private readonly ITestStore _store; + handlers.Register(_ => _store.Add("domain-event")); + } + } + + private sealed class TestMediatorIntegrationEventHandler : IntegrationEventHandler + { + private readonly ITestStore _store; - public TestMediatorIntegrationEventHandler(ITestStore store) - { - _store = store; - } + public TestMediatorIntegrationEventHandler(ITestStore store) + { + _store = store; + } - protected override void RegisterDelegates(IFireForgetRegistry handlers) - { - handlers.Register(_ => _store.Add("integration-event")); - } + protected override void RegisterDelegates(IFireForgetRegistry handlers) + { + handlers.Register(_ => _store.Add("integration-event")); } + } - private sealed class TestMediatorQueryHandler : QueryHandler + private sealed class TestMediatorQueryHandler : QueryHandler + { + protected override void RegisterDelegates(IRequestReplyRegistry handlers) { - protected override void RegisterDelegates(IRequestReplyRegistry handlers) - { - handlers.Register(_ => "query"); - } + handlers.Register(_ => "query"); } } } diff --git a/test/Savvyio.Extensions.Dispatchers.Tests/MediatorTest.cs b/test/Savvyio.Extensions.Dispatchers.Tests/MediatorTest.cs index 8d0fdd76..779495e6 100644 --- a/test/Savvyio.Extensions.Dispatchers.Tests/MediatorTest.cs +++ b/test/Savvyio.Extensions.Dispatchers.Tests/MediatorTest.cs @@ -21,155 +21,154 @@ using Savvyio.Queries; using Xunit; -namespace Savvyio.Extensions.Dispatchers +namespace Savvyio.Extensions.Dispatchers; + +public class MediatorTest : Test { - public class MediatorTest : Test + public MediatorTest(ITestOutputHelper output) : base(output) { - public MediatorTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Host_MediatorShouldBeRegistered_UsingDefaultOptions() + [Fact] + public void Host_MediatorShouldBeRegistered_UsingDefaultOptions() + { + using (var test = HostTestFactory.Create(services => services.AddSavvyIO(registry => registry.AddMediator()))) { - using (var test = HostTestFactory.Create(services => services.AddSavvyIO(registry => registry.AddMediator()))) - { - var mediator = test.Host.Services.GetRequiredService(); + var mediator = test.Host.Services.GetRequiredService(); - Assert.IsType(mediator); - } + Assert.IsType(mediator); } + } - [Fact] - public void Host_MediatorDescriptorShouldNotBeRegistered_UsingDefaultOptions() - { - using var test = HostTestFactory.Create(services => services.AddSavvyIO(registry => registry.AddMediator())); - Assert.Throws(() => test.Host.Services.GetRequiredService()); - } + [Fact] + public void Host_MediatorDescriptorShouldNotBeRegistered_UsingDefaultOptions() + { + using var test = HostTestFactory.Create(services => services.AddSavvyIO(registry => registry.AddMediator())); + Assert.Throws(() => test.Host.Services.GetRequiredService()); + } - [Fact] - public void Host_MediatorDescriptorShouldBeRegistered() - { - using var test = HostTestFactory.Create(services => services.AddSavvyIO(registry => registry.AddMediator().EnableHandlerServicesDescriptor()).AddHandlerServicesDescriptor()); - var descriptor = test.Host.Services.GetRequiredService(); + [Fact] + public void Host_MediatorDescriptorShouldBeRegistered() + { + using var test = HostTestFactory.Create(services => services.AddSavvyIO(registry => registry.AddMediator().EnableHandlerServicesDescriptor()).AddHandlerServicesDescriptor()); + var descriptor = test.Host.Services.GetRequiredService(); - Assert.IsType(descriptor); + Assert.IsType(descriptor); - TestOutput.WriteLine(descriptor.ToString()); - } + TestOutput.WriteLine(descriptor.ToString()); + } - [Fact] - public async Task Mediator_ShouldInvoke_CreateAccountAsync_OnInProcAccountCreated_OnOutProcAccountCreated() + [Fact] + public async Task Mediator_ShouldInvoke_CreateAccountAsync_OnInProcAccountCreated_OnOutProcAccountCreated() + { + await using var test = HostTestFactory.Create(services => { - await using var test = HostTestFactory.Create(services => + services.AddSingleton(TestOutput); + services.AddEfCoreRepository(); + services.AddEfCoreDataStore(); + services.AddDataSource() + .AddUnitOfWork(); + services.AddEfCoreDataSource(o => { - services.AddSingleton(TestOutput); - services.AddEfCoreRepository(); - services.AddEfCoreDataStore(); - services.AddDataSource() - .AddUnitOfWork(); - services.AddEfCoreDataSource(o => - { - o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(PlatformProvider)).EnableDetailedErrors().LogTo(Console.WriteLine); - o.ModelConstructor = mb => mb.AddPlatformProvider(); - }); - services.AddSavvyIO(o => o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true).AddMediator()); - services.AddHandlerServicesDescriptor(); - services.AddSingleton, InMemoryTestStore>(); - services.AddSingleton, InMemoryTestStore>(); - services.AddSingleton, InMemoryTestStore>(); + o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(PlatformProvider)).EnableDetailedErrors().LogTo(Console.WriteLine); + o.ModelConstructor = mb => mb.AddPlatformProvider(); }); - using var scope = test.Host.Services.CreateScope(); - var mediator = scope.ServiceProvider.GetRequiredService(); - var descriptor = scope.ServiceProvider.GetRequiredService(); - var deStore = scope.ServiceProvider.GetRequiredService>(); - var ieStore = scope.ServiceProvider.GetRequiredService>(); - - TestOutput.WriteLine(descriptor.ToString()); - - var id = Guid.NewGuid(); - var clientProvidedCorrelationId = Guid.NewGuid().ToString("N"); - - await mediator.CommitAsync(new CreateAccount(id, "Michael Mortensen", "root@gimlichael.dev") - .SetCorrelationId(clientProvidedCorrelationId) - .SetCausationId(clientProvidedCorrelationId)); - - Assert.Equal(id, deStore.QueryFor().Single().PlatformProviderId); - Assert.InRange(ieStore.QueryFor().Single().Id, 1, 100000); - } + services.AddSavvyIO(o => o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true).AddMediator()); + services.AddHandlerServicesDescriptor(); + services.AddSingleton, InMemoryTestStore>(); + services.AddSingleton, InMemoryTestStore>(); + services.AddSingleton, InMemoryTestStore>(); + }); + using var scope = test.Host.Services.CreateScope(); + var mediator = scope.ServiceProvider.GetRequiredService(); + var descriptor = scope.ServiceProvider.GetRequiredService(); + var deStore = scope.ServiceProvider.GetRequiredService>(); + var ieStore = scope.ServiceProvider.GetRequiredService>(); + + TestOutput.WriteLine(descriptor.ToString()); + + var id = Guid.NewGuid(); + var clientProvidedCorrelationId = Guid.NewGuid().ToString("N"); + + await mediator.CommitAsync(new CreateAccount(id, "Michael Mortensen", "root@gimlichael.dev") + .SetCorrelationId(clientProvidedCorrelationId) + .SetCausationId(clientProvidedCorrelationId)); + + Assert.Equal(id, deStore.QueryFor().Single().PlatformProviderId); + Assert.InRange(ieStore.QueryFor().Single().Id, 1, 100000); + } - [Fact] - public async Task Mediator_ShouldInvoke_CreatePlatformProviderAsyncLambda_OnInProcPlatformProviderInitiated_OnOutProcPlatformProviderCreated() + [Fact] + public async Task Mediator_ShouldInvoke_CreatePlatformProviderAsyncLambda_OnInProcPlatformProviderInitiated_OnOutProcPlatformProviderCreated() + { + await using var test = HostTestFactory.Create(services => { - await using var test = HostTestFactory.Create(services => + services.AddSingleton(TestOutput); + services.AddEfCoreRepository(); + services.AddEfCoreDataStore(); + services.AddDataSource() + .AddUnitOfWork(); + services.AddEfCoreAggregateDataSource(o => { - services.AddSingleton(TestOutput); - services.AddEfCoreRepository(); - services.AddEfCoreDataStore(); - services.AddDataSource() - .AddUnitOfWork(); - services.AddEfCoreAggregateDataSource(o => - { - o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(PlatformProvider)).EnableDetailedErrors().LogTo(Console.WriteLine); - o.ModelConstructor = mb => mb.AddPlatformProvider(); - }); - services.AddSavvyIO(o => - { - o.AddMediator().EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true); - }); - services.AddHandlerServicesDescriptor(); - services.AddSingleton, InMemoryTestStore>(); - services.AddSingleton, InMemoryTestStore>(); - services.AddSingleton, InMemoryTestStore>(); + o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(PlatformProvider)).EnableDetailedErrors().LogTo(Console.WriteLine); + o.ModelConstructor = mb => mb.AddPlatformProvider(); }); - var scope = test.Host.Services.CreateScope(); - var mediator = scope.ServiceProvider.GetRequiredService(); - var descriptor = scope.ServiceProvider.GetRequiredService(); - var deStore = scope.ServiceProvider.GetRequiredService>(); - var ieStore = scope.ServiceProvider.GetRequiredService>(); - - TestOutput.WriteLine(descriptor.ToString()); - - var createPlatformProvider = new CreatePlatformProvider("Whitelabel Inc.", "wl", "An example of a whitelabel platform provider."); - var clientProvidedCorrelationId = Guid.NewGuid().ToString("N"); - - await mediator.CommitAsync(createPlatformProvider - .SetCorrelationId(clientProvidedCorrelationId) - .SetCausationId(clientProvidedCorrelationId)); - - Assert.NotEqual(Guid.Empty, deStore.QueryFor().Single().Id); - Assert.Equal(deStore.QueryFor().Single().Id, ieStore.QueryFor().Single().Id); - } - - [Fact] - public async Task QueryTest() - { - await using var test = HostTestFactory.Create(services => + services.AddSavvyIO(o => { - services.AddSingleton(TestOutput); - services.AddSingleton, EfCoreRepository>(); - services.AddSingleton, EfCoreRepository>(); - services.AddEfCoreDataSource(o => o.ModelConstructor = builder => builder.AddAccount()); - services.AddEfCoreDataSource(o => o.ModelConstructor = builder => builder.AddPlatformProvider()); - services.AddSavvyIO(o => o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true)); - services.AddHandlerServicesDescriptor(); - services.AddSingleton, InMemoryTestStore>(); - services.AddSingleton, InMemoryTestStore>(); - services.AddSingleton, InMemoryTestStore>(); + o.AddMediator().EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true); }); - using var scope = test.Host.Services.CreateScope(); - var mediator = scope.ServiceProvider.GetRequiredService(); - var descriptor = scope.ServiceProvider.GetRequiredService(); - var deStore = scope.ServiceProvider.GetRequiredService>(); - var ieStore = scope.ServiceProvider.GetRequiredService>(); - - TestOutput.WriteLine(descriptor.ToString()); - - var result = await mediator.QueryAsync(new GetFakeAccount(10)); - - TestOutput.WriteLine(JsonConvert.SerializeObject(result)); + services.AddHandlerServicesDescriptor(); + services.AddSingleton, InMemoryTestStore>(); + services.AddSingleton, InMemoryTestStore>(); + services.AddSingleton, InMemoryTestStore>(); + }); + var scope = test.Host.Services.CreateScope(); + var mediator = scope.ServiceProvider.GetRequiredService(); + var descriptor = scope.ServiceProvider.GetRequiredService(); + var deStore = scope.ServiceProvider.GetRequiredService>(); + var ieStore = scope.ServiceProvider.GetRequiredService>(); + + TestOutput.WriteLine(descriptor.ToString()); + + var createPlatformProvider = new CreatePlatformProvider("Whitelabel Inc.", "wl", "An example of a whitelabel platform provider."); + var clientProvidedCorrelationId = Guid.NewGuid().ToString("N"); + + await mediator.CommitAsync(createPlatformProvider + .SetCorrelationId(clientProvidedCorrelationId) + .SetCausationId(clientProvidedCorrelationId)); + + Assert.NotEqual(Guid.Empty, deStore.QueryFor().Single().Id); + Assert.Equal(deStore.QueryFor().Single().Id, ieStore.QueryFor().Single().Id); + } - Assert.Equal(10, result.Id); - } + [Fact] + public async Task QueryTest() + { + await using var test = HostTestFactory.Create(services => + { + services.AddSingleton(TestOutput); + services.AddSingleton, EfCoreRepository>(); + services.AddSingleton, EfCoreRepository>(); + services.AddEfCoreDataSource(o => o.ModelConstructor = builder => builder.AddAccount()); + services.AddEfCoreDataSource(o => o.ModelConstructor = builder => builder.AddPlatformProvider()); + services.AddSavvyIO(o => o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true)); + services.AddHandlerServicesDescriptor(); + services.AddSingleton, InMemoryTestStore>(); + services.AddSingleton, InMemoryTestStore>(); + services.AddSingleton, InMemoryTestStore>(); + }); + using var scope = test.Host.Services.CreateScope(); + var mediator = scope.ServiceProvider.GetRequiredService(); + var descriptor = scope.ServiceProvider.GetRequiredService(); + var deStore = scope.ServiceProvider.GetRequiredService>(); + var ieStore = scope.ServiceProvider.GetRequiredService>(); + + TestOutput.WriteLine(descriptor.ToString()); + + var result = await mediator.QueryAsync(new GetFakeAccount(10)); + + TestOutput.WriteLine(JsonConvert.SerializeObject(result)); + + Assert.Equal(10, result.Id); } } diff --git a/test/Savvyio.Extensions.Dispatchers.Tests/Savvyio.Extensions.Dispatchers.Tests.csproj b/test/Savvyio.Extensions.Dispatchers.Tests/Savvyio.Extensions.Dispatchers.Tests.csproj index e6f65113..86ad7ef1 100644 --- a/test/Savvyio.Extensions.Dispatchers.Tests/Savvyio.Extensions.Dispatchers.Tests.csproj +++ b/test/Savvyio.Extensions.Dispatchers.Tests/Savvyio.Extensions.Dispatchers.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Extensions.Dispatchers diff --git a/test/Savvyio.Extensions.Dispatchers.Tests/SavvyioOptionsExtensionsTest.cs b/test/Savvyio.Extensions.Dispatchers.Tests/SavvyioOptionsExtensionsTest.cs index b8e9c899..237d78bf 100644 --- a/test/Savvyio.Extensions.Dispatchers.Tests/SavvyioOptionsExtensionsTest.cs +++ b/test/Savvyio.Extensions.Dispatchers.Tests/SavvyioOptionsExtensionsTest.cs @@ -6,73 +6,72 @@ using Savvyio.Queries; using Xunit; -namespace Savvyio.Extensions +namespace Savvyio.Extensions; + +public class SavvyioOptionsExtensionsTest : Test { - public class SavvyioOptionsExtensionsTest : Test + public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) { - public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void UseAutomaticDispatcherDiscovery_ShouldDiscoverAcrossCurrentDomain() - { - var options = new SavvyioOptions(); + [Fact] + public void UseAutomaticDispatcherDiscovery_ShouldDiscoverAcrossCurrentDomain() + { + var options = new SavvyioOptions(); - Assert.Same(options, options.UseAutomaticDispatcherDiscovery(true)); + Assert.Same(options, options.UseAutomaticDispatcherDiscovery(true)); - Assert.Contains(typeof(ICommandDispatcher), options.DispatcherServiceTypes); - Assert.Contains(typeof(IDomainEventDispatcher), options.DispatcherServiceTypes); - Assert.Contains(typeof(IIntegrationEventDispatcher), options.DispatcherServiceTypes); - Assert.Contains(typeof(IQueryDispatcher), options.DispatcherServiceTypes); - Assert.Contains(typeof(CommandDispatcher), options.DispatcherImplementationTypes); - Assert.Contains(typeof(DomainEventDispatcher), options.DispatcherImplementationTypes); - Assert.Contains(typeof(IntegrationEventDispatcher), options.DispatcherImplementationTypes); - Assert.Contains(typeof(QueryDispatcher), options.DispatcherImplementationTypes); - } + Assert.Contains(typeof(ICommandDispatcher), options.DispatcherServiceTypes); + Assert.Contains(typeof(IDomainEventDispatcher), options.DispatcherServiceTypes); + Assert.Contains(typeof(IIntegrationEventDispatcher), options.DispatcherServiceTypes); + Assert.Contains(typeof(IQueryDispatcher), options.DispatcherServiceTypes); + Assert.Contains(typeof(CommandDispatcher), options.DispatcherImplementationTypes); + Assert.Contains(typeof(DomainEventDispatcher), options.DispatcherImplementationTypes); + Assert.Contains(typeof(IntegrationEventDispatcher), options.DispatcherImplementationTypes); + Assert.Contains(typeof(QueryDispatcher), options.DispatcherImplementationTypes); + } - [Fact] - public void UseAutomaticHandlerDiscovery_ShouldDiscoverAcrossCurrentDomainWithoutDuplicates() - { - var options = new SavvyioOptions(); + [Fact] + public void UseAutomaticHandlerDiscovery_ShouldDiscoverAcrossCurrentDomainWithoutDuplicates() + { + var options = new SavvyioOptions(); - Assert.Same(options, options.UseAutomaticHandlerDiscovery(true)); - options.UseAutomaticHandlerDiscovery(true); + Assert.Same(options, options.UseAutomaticHandlerDiscovery(true)); + options.UseAutomaticHandlerDiscovery(true); - Assert.Contains(typeof(ICommandHandler), options.HandlerServiceTypes); - Assert.Contains(typeof(IDomainEventHandler), options.HandlerServiceTypes); - Assert.Contains(typeof(IIntegrationEventHandler), options.HandlerServiceTypes); - Assert.Contains(typeof(IQueryHandler), options.HandlerServiceTypes); - Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorCommandHandler"); - Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorDomainEventHandler"); - Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorIntegrationEventHandler"); - Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorQueryHandler"); - Assert.Equal(options.HandlerImplementationTypes.Count, options.HandlerImplementationTypes.Distinct().Count()); - } + Assert.Contains(typeof(ICommandHandler), options.HandlerServiceTypes); + Assert.Contains(typeof(IDomainEventHandler), options.HandlerServiceTypes); + Assert.Contains(typeof(IIntegrationEventHandler), options.HandlerServiceTypes); + Assert.Contains(typeof(IQueryHandler), options.HandlerServiceTypes); + Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorCommandHandler"); + Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorDomainEventHandler"); + Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorIntegrationEventHandler"); + Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorQueryHandler"); + Assert.Equal(options.HandlerImplementationTypes.Count, options.HandlerImplementationTypes.Distinct().Count()); + } - [Fact] - public void UseAutomaticDispatcherDiscovery_ShouldUseCallingAssembly_WhenBruteAssemblyScanningIsFalse() - { - var options = new SavvyioOptions().UseAutomaticDispatcherDiscovery(); + [Fact] + public void UseAutomaticDispatcherDiscovery_ShouldUseCallingAssembly_WhenBruteAssemblyScanningIsFalse() + { + var options = new SavvyioOptions().UseAutomaticDispatcherDiscovery(); - Assert.Empty(options.DispatcherServiceTypes); - Assert.Empty(options.DispatcherImplementationTypes); - } + Assert.Empty(options.DispatcherServiceTypes); + Assert.Empty(options.DispatcherImplementationTypes); + } - [Fact] - public void UseAutomaticHandlerDiscovery_ShouldUseCallingAssembly_WhenBruteAssemblyScanningIsFalse() - { - var options = new SavvyioOptions().UseAutomaticHandlerDiscovery(); + [Fact] + public void UseAutomaticHandlerDiscovery_ShouldUseCallingAssembly_WhenBruteAssemblyScanningIsFalse() + { + var options = new SavvyioOptions().UseAutomaticHandlerDiscovery(); - Assert.Collection(options.HandlerServiceTypes.OrderBy(type => type.Name), - type => Assert.Equal(typeof(ICommandHandler), type), - type => Assert.Equal(typeof(IDomainEventHandler), type), - type => Assert.Equal(typeof(IIntegrationEventHandler), type), - type => Assert.Equal(typeof(IQueryHandler), type)); - Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorCommandHandler"); - Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorDomainEventHandler"); - Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorIntegrationEventHandler"); - Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorQueryHandler"); - } + Assert.Collection(options.HandlerServiceTypes.OrderBy(type => type.Name), + type => Assert.Equal(typeof(ICommandHandler), type), + type => Assert.Equal(typeof(IDomainEventHandler), type), + type => Assert.Equal(typeof(IIntegrationEventHandler), type), + type => Assert.Equal(typeof(IQueryHandler), type)); + Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorCommandHandler"); + Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorDomainEventHandler"); + Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorIntegrationEventHandler"); + Assert.Contains(options.HandlerImplementationTypes, type => type.Name == "TestMediatorQueryHandler"); } } diff --git a/test/Savvyio.Extensions.EFCore.Tests/DomainEventDispatcherExtensionsTest.cs b/test/Savvyio.Extensions.EFCore.Tests/DomainEventDispatcherExtensionsTest.cs index 332105b7..e041af09 100644 --- a/test/Savvyio.Extensions.EFCore.Tests/DomainEventDispatcherExtensionsTest.cs +++ b/test/Savvyio.Extensions.EFCore.Tests/DomainEventDispatcherExtensionsTest.cs @@ -10,71 +10,70 @@ using Savvyio.Domain; using Xunit; -namespace Savvyio.Extensions.EFCore.Domain +namespace Savvyio.Extensions.EFCore.Domain; + +public class DomainEventDispatcherExtensionsTest : Test { - public class DomainEventDispatcherExtensionsTest : Test + public DomainEventDispatcherExtensionsTest(ITestOutputHelper output) : base(output) { - public DomainEventDispatcherExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void DomainEventDispatcherExtensions_ShouldRaiseManySynchronously() + [Fact] + public void DomainEventDispatcherExtensions_ShouldRaiseManySynchronously() + { + var dispatcher = new TrackingDomainEventDispatcher(); + var source = new EfCoreAggregateDataSource(null, new EfCoreDataSourceOptions { - var dispatcher = new TrackingDomainEventDispatcher(); - var source = new EfCoreAggregateDataSource(null, new EfCoreDataSourceOptions - { - ContextConfigurator = b => b.UseInMemoryDatabase("dispatcher-" + Guid.NewGuid()), - ModelConstructor = mb => mb.AddAccount() - }); - var repository = new EfCoreRepository(source); - var id = Guid.NewGuid(); + ContextConfigurator = b => b.UseInMemoryDatabase("dispatcher-" + Guid.NewGuid()), + ModelConstructor = mb => mb.AddAccount() + }); + var repository = new EfCoreRepository(source); + var id = Guid.NewGuid(); - repository.Add(new Account(id, "Test", "test@unit.test")); - dispatcher.RaiseMany(source.DbContext); + repository.Add(new Account(id, "Test", "test@unit.test")); + dispatcher.RaiseMany(source.DbContext); - var tracked = source.DbContext.ChangeTracker.Entries>().Single().Entity; + var tracked = source.DbContext.ChangeTracker.Entries>().Single().Entity; - Assert.Single(dispatcher.Events); - Assert.IsType(dispatcher.Events.Single()); - Assert.Equal(id, ((AccountInitiated)dispatcher.Events.Single()).PlatformProviderId); - Assert.Empty(tracked.Events); - } + Assert.Single(dispatcher.Events); + Assert.IsType(dispatcher.Events.Single()); + Assert.Equal(id, ((AccountInitiated)dispatcher.Events.Single()).PlatformProviderId); + Assert.Empty(tracked.Events); + } - [Fact] - public async Task EfCoreAggregateDataSource_ShouldSaveChangesWithoutDispatcher() + [Fact] + public async Task EfCoreAggregateDataSource_ShouldSaveChangesWithoutDispatcher() + { + var id = Guid.NewGuid(); + var source = new EfCoreAggregateDataSource(null, new EfCoreDataSourceOptions { - var id = Guid.NewGuid(); - var source = new EfCoreAggregateDataSource(null, new EfCoreDataSourceOptions - { - ContextConfigurator = b => b.UseInMemoryDatabase("aggregate-null-dispatcher-" + Guid.NewGuid()), - ModelConstructor = mb => mb.AddAccount() - }); - var repository = new EfCoreRepository(source); + ContextConfigurator = b => b.UseInMemoryDatabase("aggregate-null-dispatcher-" + Guid.NewGuid()), + ModelConstructor = mb => mb.AddAccount() + }); + var repository = new EfCoreRepository(source); - repository.Add(new Account(id, "Test", "test@unit.test")); - await source.SaveChangesAsync(); + repository.Add(new Account(id, "Test", "test@unit.test")); + await source.SaveChangesAsync(); - var sut = await repository.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + var sut = await repository.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - Assert.NotNull(sut); - Assert.Equal(id, sut.PlatformProviderId); - } + Assert.NotNull(sut); + Assert.Equal(id, sut.PlatformProviderId); + } - private sealed class TrackingDomainEventDispatcher : IDomainEventDispatcher - { - public List Events { get; } = new(); + private sealed class TrackingDomainEventDispatcher : IDomainEventDispatcher + { + public List Events { get; } = new(); - public void Raise(IDomainEvent request) - { - Events.Add(request); - } + public void Raise(IDomainEvent request) + { + Events.Add(request); + } - public Task RaiseAsync(IDomainEvent request, Action setup = null) - { - Events.Add(request); - return Task.CompletedTask; - } + public Task RaiseAsync(IDomainEvent request, Action setup = null) + { + Events.Add(request); + return Task.CompletedTask; } } } diff --git a/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataSourceOptionsTest.cs b/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataSourceOptionsTest.cs index a0ff4754..48809562 100644 --- a/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataSourceOptionsTest.cs +++ b/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataSourceOptionsTest.cs @@ -1,22 +1,21 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +public class EfCoreDataSourceOptionsTest : Test { - public class EfCoreDataSourceOptionsTest : Test + public EfCoreDataSourceOptionsTest(ITestOutputHelper output) : base(output) { - public EfCoreDataSourceOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void EfCoreDataSourceOptions_Ensure_Initialization_Defaults() - { - var sut = new EfCoreDataSourceOptions(); + [Fact] + public void EfCoreDataSourceOptions_Ensure_Initialization_Defaults() + { + var sut = new EfCoreDataSourceOptions(); - Assert.Null(sut.ContextConfigurator); - Assert.Null(sut.ConventionsConfigurator); - Assert.Null(sut.ModelConstructor); - } + Assert.Null(sut.ContextConfigurator); + Assert.Null(sut.ConventionsConfigurator); + Assert.Null(sut.ModelConstructor); } } diff --git a/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataSourceTest.cs b/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataSourceTest.cs index b38f33f7..9e9450cb 100644 --- a/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataSourceTest.cs +++ b/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataSourceTest.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using Codebelt.Extensions.Xunit; +using Cuemon.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Savvyio.Assets; @@ -13,62 +14,61 @@ using Savvyio.Extensions.EFCore.Domain; using Xunit; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +public class EfCoreDataSourceTest : Test { - public class EfCoreDataSourceTest : Test + public EfCoreDataSourceTest(ITestOutputHelper output) : base(output) { - public EfCoreDataSourceTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task EfCoreDataSource_ShouldFailWithInvalidOperationException_NoDatabaseProvider() - { - var sut = new EfCoreDataSource(new EfCoreDataSourceOptions()); - var ex = await Assert.ThrowsAsync(() => sut.SaveChangesAsync()); - Assert.StartsWith("No database provider has been configured for this DbContext.", ex.Message); - } + [Fact] + public async Task EfCoreDataSource_ShouldFailWithInvalidOperationException_NoDatabaseProvider() + { + var sut = new EfCoreDataSource(new EfCoreDataSourceOptions()); + var ex = await Assert.ThrowsAsync(() => sut.SaveChangesAsync()); + Assert.StartsWith("No database provider has been configured for this DbContext.", ex.Message); + } - [Fact] - public async Task EfCoreDataSource_ShouldFailWithObjectDisposedException() + [Fact] + public async Task EfCoreDataSource_ShouldFailWithObjectDisposedException() + { + var sut = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var sut = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy") - }); - sut.Dispose(); + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy") + }); + sut.Dispose(); - await Assert.ThrowsAsync(() => sut.SaveChangesAsync()); - Assert.True(sut.Disposed); - } + await Assert.ThrowsAsync(() => sut.SaveChangesAsync()); + Assert.True(sut.Disposed); + } - [Fact] - public async Task EfCoreDataSource_ShouldRaiseDomainEvents() + [Fact] + public async Task EfCoreDataSource_ShouldRaiseDomainEvents() + { + var sut1 = new ServiceCollection(); + sut1.AddServiceLocator(); + sut1.AddScoped(); + sut1.AddScoped(); + sut1.AddScoped, InMemoryTestStore>(); + sut1.AddConfiguredOptions(o => { - var sut1 = new ServiceCollection(); - sut1.AddServiceLocator(); - sut1.AddScoped(); - sut1.AddScoped(); - sut1.AddScoped, InMemoryTestStore>(); - sut1.AddConfiguredOptions(o => - { - o.ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"); - o.ModelConstructor = mb => mb.AddAccount(); - }); - var sut2 = sut1.BuildServiceProvider(); - var sut3 = new EfCoreAggregateDataSource(sut2.GetRequiredService(), sut2.GetRequiredService()); - var sut4 = new EfCoreRepository(sut3); + o.ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"); + o.ModelConstructor = mb => mb.AddAccount(); + }); + var sut2 = sut1.BuildServiceProvider(); + var sut3 = new EfCoreAggregateDataSource(sut2.GetRequiredService(), sut2.GetRequiredService()); + var sut4 = new EfCoreRepository(sut3); - var id = Guid.NewGuid(); - var name = "Test"; - var email = "test@unit.test"; + var id = Guid.NewGuid(); + var name = "Test"; + var email = "test@unit.test"; - sut4.Add(new Account(id, name, email)); - await sut3.SaveChangesAsync(); // should raise domain events + sut4.Add(new Account(id, name, email)); + await sut3.SaveChangesAsync(); // should raise domain events - Assert.Equal(id, sut2.GetRequiredService>().QueryFor().Single().PlatformProviderId); - Assert.Equal(name, sut2.GetRequiredService>().QueryFor().Single().FullName); - Assert.Equal(email, sut2.GetRequiredService>().QueryFor().Single().EmailAddress); - } + Assert.Equal(id, sut2.GetRequiredService>().QueryFor().Single().PlatformProviderId); + Assert.Equal(name, sut2.GetRequiredService>().QueryFor().Single().FullName); + Assert.Equal(email, sut2.GetRequiredService>().QueryFor().Single().EmailAddress); } } diff --git a/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataStoreTest.cs b/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataStoreTest.cs index 12e10486..b7143b57 100644 --- a/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataStoreTest.cs +++ b/test/Savvyio.Extensions.EFCore.Tests/EfCoreDataStoreTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using Codebelt.Extensions.Xunit; @@ -7,131 +7,130 @@ using Savvyio.Assets.Domain; using Xunit; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +public class EfCoreDataStoreTest : Test { - public class EfCoreDataStoreTest : Test + public EfCoreDataStoreTest(ITestOutputHelper output) : base(output) { - public EfCoreDataStoreTest(ITestOutputHelper output) : base(output) - { - } + } + + [Fact] + public async Task EfCoreDataStore_ShouldCreateObject() + { + var id = Guid.NewGuid(); + var name = "Test"; + var email = "test@unit.test"; - [Fact] - public async Task EfCoreDataStore_ShouldCreateObject() + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var id = Guid.NewGuid(); - var name = "Test"; - var email = "test@unit.test"; + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), + ModelConstructor = mb => mb.AddAccount() + }); - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), - ModelConstructor = mb => mb.AddAccount() - }); + var sut2 = new EfCoreDataStore(sut1); + await sut2.CreateAsync(new Account(id, name, email)); - var sut2 = new EfCoreDataStore(sut1); - await sut2.CreateAsync(new Account(id, name, email)); + var sut3 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - var sut3 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + Assert.Equal(id, sut3.PlatformProviderId); + Assert.Equal(name, sut3.FullName); + Assert.Equal(email, sut3.EmailAddress); + } - Assert.Equal(id, sut3.PlatformProviderId); - Assert.Equal(name, sut3.FullName); - Assert.Equal(email, sut3.EmailAddress); - } + [Fact] + public async Task EfCoreDataStore_ShouldRemoveObject() + { + var id = Guid.NewGuid(); + var name = "Test"; + var email = "test@unit.test"; + var dto = new Account(id, name, email); - [Fact] - public async Task EfCoreDataStore_ShouldRemoveObject() + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var id = Guid.NewGuid(); - var name = "Test"; - var email = "test@unit.test"; - var dto = new Account(id, name, email); + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), + ModelConstructor = mb => mb.AddAccount() + }); - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), - ModelConstructor = mb => mb.AddAccount() - }); + var sut2 = new EfCoreDataStore(sut1); + await sut2.CreateAsync(dto); - var sut2 = new EfCoreDataStore(sut1); - await sut2.CreateAsync(dto); + var sut3 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - var sut3 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + await sut2.DeleteAsync(dto); - await sut2.DeleteAsync(dto); + var sut4 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - var sut4 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + Assert.NotNull(sut3); + Assert.Null(sut4); + } - Assert.NotNull(sut3); - Assert.Null(sut4); - } + [Fact] + public async Task EfCoreDataStore_ShouldUpdateObject() + { + var id = Guid.NewGuid(); + var name = "Test"; + var newName = "Unit Test"; + var email = "test@unit.test"; + var dto = new Account(id, name, email); - [Fact] - public async Task EfCoreDataStore_ShouldUpdateObject() + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var id = Guid.NewGuid(); - var name = "Test"; - var newName = "Unit Test"; - var email = "test@unit.test"; - var dto = new Account(id, name, email); + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), + ModelConstructor = mb => mb.AddAccount() + }); - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), - ModelConstructor = mb => mb.AddAccount() - }); + var sut2 = new EfCoreDataStore(sut1); + await sut2.CreateAsync(dto); - var sut2 = new EfCoreDataStore(sut1); - await sut2.CreateAsync(dto); + var sut3 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - var sut3 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + sut3.ChangeFullName(newName); - sut3.ChangeFullName(newName); + await sut2.UpdateAsync(sut3); - await sut2.UpdateAsync(sut3); + var sut4 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - var sut4 = await sut2.FindAllAsync(o => o.Predicate = a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - - Assert.NotEqual(name, sut3.FullName); - Assert.Equal(newName, sut4.FullName); - } + Assert.NotEqual(name, sut3.FullName); + Assert.Equal(newName, sut4.FullName); + } - [Fact] - public async Task EfCoreDataStore_ShouldGetObjectById() + [Fact] + public async Task EfCoreDataStore_ShouldGetObjectById() + { + var id = Guid.NewGuid(); + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var id = Guid.NewGuid(); - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy_" + Guid.NewGuid()), - ModelConstructor = mb => mb.AddAccount() - }); - var sut2 = new EfCoreDataStore(sut1); - var dto = new Account(id, "Test", "test@unit.test"); - - await sut2.CreateAsync(dto); - - var sut3 = await sut2.GetByIdAsync(dto.Id); - var sut4 = await sut2.GetByIdAsync(long.MaxValue); - - Assert.Equal(dto.Id, sut3.Id); - Assert.Null(sut4); - } - - [Fact] - public async Task EfCoreDataStore_ShouldReturnAllObjectsWhenPredicateIsMissing() + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy_" + Guid.NewGuid()), + ModelConstructor = mb => mb.AddAccount() + }); + var sut2 = new EfCoreDataStore(sut1); + var dto = new Account(id, "Test", "test@unit.test"); + + await sut2.CreateAsync(dto); + + var sut3 = await sut2.GetByIdAsync(dto.Id); + var sut4 = await sut2.GetByIdAsync(long.MaxValue); + + Assert.Equal(dto.Id, sut3.Id); + Assert.Null(sut4); + } + + [Fact] + public async Task EfCoreDataStore_ShouldReturnAllObjectsWhenPredicateIsMissing() + { + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy_" + Guid.NewGuid()), - ModelConstructor = mb => mb.AddAccount() - }); - var sut2 = new EfCoreDataStore(sut1); + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy_" + Guid.NewGuid()), + ModelConstructor = mb => mb.AddAccount() + }); + var sut2 = new EfCoreDataStore(sut1); - await sut2.CreateAsync(new Account(Guid.NewGuid(), "Test1", "test1@unit.test")); - await sut2.CreateAsync(new Account(Guid.NewGuid(), "Test2", "test2@unit.test")); + await sut2.CreateAsync(new Account(Guid.NewGuid(), "Test1", "test1@unit.test")); + await sut2.CreateAsync(new Account(Guid.NewGuid(), "Test2", "test2@unit.test")); - var sut3 = await sut2.FindAllAsync(); + var sut3 = await sut2.FindAllAsync(); - Assert.Equal(2, sut3.Count()); - } + Assert.Equal(2, sut3.Count()); } } diff --git a/test/Savvyio.Extensions.EFCore.Tests/EfCoreRepositoryTest.cs b/test/Savvyio.Extensions.EFCore.Tests/EfCoreRepositoryTest.cs index 902a595e..cf972a0b 100644 --- a/test/Savvyio.Extensions.EFCore.Tests/EfCoreRepositoryTest.cs +++ b/test/Savvyio.Extensions.EFCore.Tests/EfCoreRepositoryTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -9,171 +9,170 @@ using Savvyio.Extensions.DependencyInjection.EFCore; using Xunit; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +public class EfCoreRepositoryTest : Test { - public class EfCoreRepositoryTest : Test + public EfCoreRepositoryTest(ITestOutputHelper output) : base(output) { - public EfCoreRepositoryTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void EfCoreRepository_ShouldFailWithInvalidModel() + [Fact] + public void EfCoreRepository_ShouldFailWithInvalidModel() + { + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy") - }); - var sut2 = new EfCoreRepository(sut1); - var ex = Assert.Throws(() => sut2.Add(new Account(1))); - Assert.StartsWith("Cannot create a DbSet for 'Account' because this type is not included in the model for the context.", ex.Message); - } - - [Fact] - public async Task EfCoreRepository_ShouldAddEntity() + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy") + }); + var sut2 = new EfCoreRepository(sut1); + var ex = Assert.Throws(() => sut2.Add(new Account(1))); + Assert.StartsWith("Cannot create a DbSet for 'Account' because this type is not included in the model for the context.", ex.Message); + } + + [Fact] + public async Task EfCoreRepository_ShouldAddEntity() + { + var id = Guid.NewGuid(); + var name = "Test"; + var email = "test@unit.test"; + + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var id = Guid.NewGuid(); - var name = "Test"; - var email = "test@unit.test"; + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), + ModelConstructor = mb => mb.AddAccount() + }); - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), - ModelConstructor = mb => mb.AddAccount() - }); + var sut2 = new EfCoreRepository(sut1); + sut2.Add(new Account(id, name, email)); + await sut1.SaveChangesAsync(); - var sut2 = new EfCoreRepository(sut1); - sut2.Add(new Account(id, name, email)); - await sut1.SaveChangesAsync(); + var sut3 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - var sut3 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + Assert.Equal(id, sut3.PlatformProviderId); + Assert.Equal(name, sut3.FullName); + Assert.Equal(email, sut3.EmailAddress); + } - Assert.Equal(id, sut3.PlatformProviderId); - Assert.Equal(name, sut3.FullName); - Assert.Equal(email, sut3.EmailAddress); - } + [Fact] + public async Task EfCoreRepository_ShouldRemoveEntity() + { + var id = Guid.NewGuid(); + var name = "Test"; + var email = "test@unit.test"; + var entity = new Account(id, name, email); - [Fact] - public async Task EfCoreRepository_ShouldRemoveEntity() + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var id = Guid.NewGuid(); - var name = "Test"; - var email = "test@unit.test"; - var entity = new Account(id, name, email); + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), + ModelConstructor = mb => mb.AddAccount() + }); + + var sut2 = new EfCoreRepository(sut1); + sut2.Add(entity); + await sut1.SaveChangesAsync(); + + var sut3 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), - ModelConstructor = mb => mb.AddAccount() - }); + sut2.Remove(entity); + await sut1.SaveChangesAsync(); - var sut2 = new EfCoreRepository(sut1); - sut2.Add(entity); - await sut1.SaveChangesAsync(); + var sut4 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - var sut3 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + Assert.NotNull(sut3); + Assert.Null(sut4); + } - sut2.Remove(entity); - await sut1.SaveChangesAsync(); + [Fact] + public async Task EfCoreRepository_ShouldUpdateEntity() + { + var id = Guid.NewGuid(); + var name = "Test"; + var newName = "Unit Test"; + var email = "test@unit.test"; + var entity = new Account(id, name, email); - var sut4 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() + { + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), + ModelConstructor = mb => mb.AddAccount() + }); - Assert.NotNull(sut3); - Assert.Null(sut4); - } + var events = new List(); - [Fact] - public async Task EfCoreRepository_ShouldUpdateEntity() + sut1.DbContext.ChangeTracker.StateChanged += (sender, args) => { - var id = Guid.NewGuid(); - var name = "Test"; - var newName = "Unit Test"; - var email = "test@unit.test"; - var entity = new Account(id, name, email); + var trace = $"{sender}: {args.OldState} --> {args.NewState}"; + events.Add(trace); + TestOutput.WriteLine(trace); + }; - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy"), - ModelConstructor = mb => mb.AddAccount() - }); + var sut2 = new EfCoreRepository(sut1); - var events = new List(); + sut2.Add(entity); - sut1.DbContext.ChangeTracker.StateChanged += (sender, args) => - { - var trace = $"{sender}: {args.OldState} --> {args.NewState}"; - events.Add(trace); - TestOutput.WriteLine(trace); - }; + await sut1.SaveChangesAsync(); - var sut2 = new EfCoreRepository(sut1); + var sut3 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - sut2.Add(entity); + Assert.Equal(entity, sut3); - await sut1.SaveChangesAsync(); + sut3.ChangeFullName(newName); - var sut3 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + await sut1.SaveChangesAsync(); // modified - Assert.Equal(entity, sut3); + var sut4 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); - sut3.ChangeFullName(newName); + Assert.NotEqual(name, sut3.FullName); + Assert.Equal(newName, sut4.FullName); + Assert.Collection(events, + s => Assert.Equal("Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker: Added --> Unchanged", s), + s => Assert.Equal("Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker: Unchanged --> Modified", s), + s => Assert.Equal("Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker: Modified --> Unchanged", s)); + } + + [Fact] + public async Task EfCoreRepository_ShouldGetEntityById() + { + var entity = new Account(Guid.NewGuid(), "Test", "test@unit.test"); + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() + { + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy_" + Guid.NewGuid()), + ModelConstructor = mb => mb.AddAccount() + }); + var sut2 = new EfCoreRepository(sut1); - await sut1.SaveChangesAsync(); // modified + sut2.Add(entity); + await sut1.SaveChangesAsync(); - var sut4 = await sut2.FindAllAsync(a => a.PlatformProviderId == id).SingleOrDefaultAsync(); + var sut3 = await sut2.GetByIdAsync(entity.Id); + var sut4 = await sut2.GetByIdAsync(long.MaxValue); - Assert.NotEqual(name, sut3.FullName); - Assert.Equal(newName, sut4.FullName); - Assert.Collection(events, - s => Assert.Equal("Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker: Added --> Unchanged", s), - s => Assert.Equal("Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker: Unchanged --> Modified", s), - s => Assert.Equal("Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker: Modified --> Unchanged", s)); - } + Assert.Equal(entity.Id, sut3.Id); + Assert.Null(sut4); + } - [Fact] - public async Task EfCoreRepository_ShouldGetEntityById() + [Fact] + public async Task EfCoreRepository_ShouldAddAndRemoveRangeOfEntities() + { + var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() { - var entity = new Account(Guid.NewGuid(), "Test", "test@unit.test"); - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy_" + Guid.NewGuid()), - ModelConstructor = mb => mb.AddAccount() - }); - var sut2 = new EfCoreRepository(sut1); - - sut2.Add(entity); - await sut1.SaveChangesAsync(); - - var sut3 = await sut2.GetByIdAsync(entity.Id); - var sut4 = await sut2.GetByIdAsync(long.MaxValue); - - Assert.Equal(entity.Id, sut3.Id); - Assert.Null(sut4); - } - - [Fact] - public async Task EfCoreRepository_ShouldAddAndRemoveRangeOfEntities() + ContextConfigurator = b => b.UseInMemoryDatabase("Dummy_" + Guid.NewGuid()), + ModelConstructor = mb => mb.AddAccount() + }); + var sut2 = new EfCoreRepository(sut1); + var entities = new[] { - var sut1 = new EfCoreDataSource(new EfCoreDataSourceOptions() - { - ContextConfigurator = b => b.UseInMemoryDatabase("Dummy_" + Guid.NewGuid()), - ModelConstructor = mb => mb.AddAccount() - }); - var sut2 = new EfCoreRepository(sut1); - var entities = new[] - { - new Account(Guid.NewGuid(), "Test1", "test1@unit.test"), - new Account(Guid.NewGuid(), "Test2", "test2@unit.test") - }; - - sut2.AddRange(entities); - await sut1.SaveChangesAsync(); - Assert.Equal(2, (await sut2.FindAllAsync()).Count()); - - sut2.RemoveRange(entities); - await sut1.SaveChangesAsync(); - - Assert.Empty(await sut2.FindAllAsync()); - } + new Account(Guid.NewGuid(), "Test1", "test1@unit.test"), + new Account(Guid.NewGuid(), "Test2", "test2@unit.test") + }; + + sut2.AddRange(entities); + await sut1.SaveChangesAsync(); + Assert.Equal(2, (await sut2.FindAllAsync()).Count()); + + sut2.RemoveRange(entities); + await sut1.SaveChangesAsync(); + + Assert.Empty(await sut2.FindAllAsync()); } } diff --git a/test/Savvyio.Extensions.EFCore.Tests/Savvyio.Extensions.EFCore.Tests.csproj b/test/Savvyio.Extensions.EFCore.Tests/Savvyio.Extensions.EFCore.Tests.csproj index 5630ca53..1aa5063b 100644 --- a/test/Savvyio.Extensions.EFCore.Tests/Savvyio.Extensions.EFCore.Tests.csproj +++ b/test/Savvyio.Extensions.EFCore.Tests/Savvyio.Extensions.EFCore.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Extensions.EFCore diff --git a/test/Savvyio.Extensions.EFCore.Tests/ServiceCollectionExtensionsTest.cs b/test/Savvyio.Extensions.EFCore.Tests/ServiceCollectionExtensionsTest.cs index 20b3eb08..dbba757d 100644 --- a/test/Savvyio.Extensions.EFCore.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.Extensions.EFCore.Tests/ServiceCollectionExtensionsTest.cs @@ -1,5 +1,6 @@ -using System; +using System; using Codebelt.Extensions.Xunit; +using Cuemon.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -13,154 +14,153 @@ using Savvyio.Extensions.DependencyInjection.EFCore; using Xunit; -namespace Savvyio.Extensions.EFCore +namespace Savvyio.Extensions.EFCore; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void AddEfCoreDataStore_ShouldAddDefaultImplementationWithDefaultOptions() + { + var sut1 = new ServiceCollection(); + sut1.AddDataSource() + .AddUnitOfWork(); + sut1.AddConfiguredOptions(_ => { }); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType(sut2.GetRequiredService()); + Assert.IsType(sut2.GetRequiredService()); + Assert.Same(sut2.GetRequiredService(), sut2.GetRequiredService()); + Assert.Null(sut2.GetRequiredService>().Value.ContextConfigurator); + Assert.Null(sut2.GetRequiredService>().Value.ModelConstructor); + Assert.Null(sut2.GetRequiredService>().Value.ConventionsConfigurator); + } + + [Fact] + public void AddEfCoreDataStore_ShouldAddDefaultImplementation() { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => { - } + o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); + o.ModelConstructor = builder => builder.AddAccount(); + o.ConventionsConfigurator = _ => { }; + }); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType(sut2.GetRequiredService()); + Assert.NotNull(sut2.GetRequiredService>().Value.ContextConfigurator); + Assert.NotNull(sut2.GetRequiredService>().Value.ModelConstructor); + Assert.NotNull(sut2.GetRequiredService>().Value.ConventionsConfigurator); + } - [Fact] - public void AddEfCoreDataStore_ShouldAddDefaultImplementationWithDefaultOptions() + [Fact] + public void AddEfCoreDataStore_ShouldAddDefaultImplementationWithMarker() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => { - var sut1 = new ServiceCollection(); - sut1.AddDataSource() - .AddUnitOfWork(); - sut1.AddConfiguredOptions(_ => { }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType(sut2.GetRequiredService()); - Assert.IsType(sut2.GetRequiredService()); - Assert.Same(sut2.GetRequiredService(), sut2.GetRequiredService()); - Assert.Null(sut2.GetRequiredService>().Value.ContextConfigurator); - Assert.Null(sut2.GetRequiredService>().Value.ModelConstructor); - Assert.Null(sut2.GetRequiredService>().Value.ConventionsConfigurator); - } - - [Fact] - public void AddEfCoreDataStore_ShouldAddDefaultImplementation() + o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); + o.ModelConstructor = builder => builder.AddAccount(); + }); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.NotNull(sut2.GetRequiredService>>().Value.ContextConfigurator); + Assert.NotNull(sut2.GetRequiredService>>().Value.ModelConstructor); + Assert.Null(sut2.GetRequiredService>>().Value.ConventionsConfigurator); + } + + [Fact] + public void AddEfCoreRepository_ShouldAddManyImplementations() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => - { - o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); - o.ModelConstructor = builder => builder.AddAccount(); - o.ConventionsConfigurator = _ => { }; - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType(sut2.GetRequiredService()); - Assert.NotNull(sut2.GetRequiredService>().Value.ContextConfigurator); - Assert.NotNull(sut2.GetRequiredService>().Value.ModelConstructor); - Assert.NotNull(sut2.GetRequiredService>().Value.ConventionsConfigurator); - } - - [Fact] - public void AddEfCoreDataStore_ShouldAddDefaultImplementationWithMarker() + o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); + o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); + }); + sut1.AddEfCoreRepository(); + sut1.AddEfCoreRepository(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + } + + [Fact] + public void AddEfCoreRepository_ShouldAddManyImplementationsWithMarkers() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => - { - o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); - o.ModelConstructor = builder => builder.AddAccount(); - }); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.NotNull(sut2.GetRequiredService>>().Value.ContextConfigurator); - Assert.NotNull(sut2.GetRequiredService>>().Value.ModelConstructor); - Assert.Null(sut2.GetRequiredService>>().Value.ConventionsConfigurator); - } - - [Fact] - public void AddEfCoreRepository_ShouldAddManyImplementations() + o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); + o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); + }); + sut1.AddEfCoreRepository(); + sut1.AddEfCoreRepository(); + sut1.AddEfCoreDataSource(o => { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => - { - o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); - o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); - }); - sut1.AddEfCoreRepository(); - sut1.AddEfCoreRepository(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - } - - [Fact] - public void AddEfCoreRepository_ShouldAddManyImplementationsWithMarkers() + o.ContextConfigurator = builder => builder.UseInMemoryDatabase("AnotherDummy"); + o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); + }); + sut1.AddEfCoreRepository(); + sut1.AddEfCoreRepository(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.IsType>(sut2.GetRequiredService>()); + Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); + Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); + } + + [Fact] + public void AddEfCoreDataStore_ShouldAddManyImplementations() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => - { - o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); - o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); - }); - sut1.AddEfCoreRepository(); - sut1.AddEfCoreRepository(); - sut1.AddEfCoreDataSource(o => - { - o.ContextConfigurator = builder => builder.UseInMemoryDatabase("AnotherDummy"); - o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); - }); - sut1.AddEfCoreRepository(); - sut1.AddEfCoreRepository(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.IsType>(sut2.GetRequiredService>()); - Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - Assert.NotSame(sut2.GetRequiredService>(), sut2.GetRequiredService>()); - } - - [Fact] - public void AddEfCoreDataStore_ShouldAddManyImplementations() + o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); + o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); + }); + sut1.AddEfCoreDataStore(); + sut1.AddEfCoreDataStore(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService>>()); + Assert.IsType>(sut2.GetRequiredService>>()); + } + + [Fact] + public void AddEfCoreDataStore_ShouldAddManyImplementationsWithMarkers() + { + var sut1 = new ServiceCollection(); + sut1.AddEfCoreDataSource(o => { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => - { - o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); - o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); - }); - sut1.AddEfCoreDataStore(); - sut1.AddEfCoreDataStore(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService>>()); - Assert.IsType>(sut2.GetRequiredService>>()); - } - - [Fact] - public void AddEfCoreDataStore_ShouldAddManyImplementationsWithMarkers() + o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); + o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); + }); + sut1.AddEfCoreDataStore(); + sut1.AddEfCoreDataStore(); + sut1.AddEfCoreDataSource(o => { - var sut1 = new ServiceCollection(); - sut1.AddEfCoreDataSource(o => - { - o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); - o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); - }); - sut1.AddEfCoreDataStore(); - sut1.AddEfCoreDataStore(); - sut1.AddEfCoreDataSource(o => - { - o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); - o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); - }); - sut1.AddEfCoreDataStore(); - sut1.AddEfCoreDataStore(); - var sut2 = sut1.BuildServiceProvider(); - - Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); - Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); - Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); - Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); - Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); - Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); - } + o.ContextConfigurator = builder => builder.UseInMemoryDatabase("Dummy"); + o.ModelConstructor = builder => builder.AddAccount().AddPlatformProvider(); + }); + sut1.AddEfCoreDataStore(); + sut1.AddEfCoreDataStore(); + var sut2 = sut1.BuildServiceProvider(); + + Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); + Assert.IsType>(sut2.GetRequiredService, DbMarker>>()); + Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); + Assert.IsType>(sut2.GetRequiredService, AnotherDbMarker>>()); + Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); + Assert.NotSame(sut2.GetRequiredService, DbMarker>>(), sut2.GetRequiredService, AnotherDbMarker>>()); } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/Assets/CreateMemberCommand.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/Assets/CreateMemberCommand.cs index 48481a77..f90e7ab4 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/Assets/CreateMemberCommand.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/Assets/CreateMemberCommand.cs @@ -1,20 +1,19 @@ -using Savvyio.Commands; +using Savvyio.Commands; -namespace Savvyio.Extensions.NATS.Assets +namespace Savvyio.Extensions.NATS.Assets; + +internal record CreateMemberCommand : Command { - internal record CreateMemberCommand : Command + public CreateMemberCommand(string name, byte age, string emailAddress) { - public CreateMemberCommand(string name, byte age, string emailAddress) - { - Name = name; - Age = age; - EmailAddress = emailAddress; - } + Name = name; + Age = age; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public byte Age { get; } + public byte Age { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/Assets/MemberCreated.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/Assets/MemberCreated.cs index a8bc248f..810fe39f 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/Assets/MemberCreated.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/Assets/MemberCreated.cs @@ -1,17 +1,16 @@ -using Savvyio.EventDriven; +using Savvyio.EventDriven; -namespace Savvyio.Extensions.NATS.Assets +namespace Savvyio.Extensions.NATS.Assets; + +public record MemberCreated : IntegrationEvent { - public record MemberCreated : IntegrationEvent + public MemberCreated(string name, string emailAddress) { - public MemberCreated(string name, string emailAddress) - { - Name = name; - EmailAddress = emailAddress; - } + Name = name; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/NatsCommandQueueJsonSerializerContextTest.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/NatsCommandQueueJsonSerializerContextTest.cs index f8bd02e0..682969ad 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/NatsCommandQueueJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/NatsCommandQueueJsonSerializerContextTest.cs @@ -18,184 +18,184 @@ using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; +using Cuemon.Extensions.DependencyInjection; using Xunit; -namespace Savvyio.Extensions.NATS.Commands +namespace Savvyio.Extensions.NATS.Commands; + +public class NatsCommandQueueJsonSerializerContextTest : Test { - public class NatsCommandQueueJsonSerializerContextTest : Test + public NatsCommandQueueJsonSerializerContextTest(ITestOutputHelper output) : base(output) { - public NatsCommandQueueJsonSerializerContextTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.AutoAcknowledge = true; - o.Subject = Guid.NewGuid().ToString(); - o.StreamName = Guid.NewGuid().ToString(); - o.ConsumerName = Guid.NewGuid().ToString(); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.AutoAcknowledge = true; + o.Subject = Guid.NewGuid().ToString(); + o.StreamName = Guid.NewGuid().ToString(); + o.ConsumerName = Guid.NewGuid().ToString(); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var urn = "https://fancy.io/members".ToUri(); - var message = member.ToMessage(urn, nameof(CreateMemberCommand)); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var urn = "https://fancy.io/members".ToUri(); + var message = member.ToMessage(urn, nameof(CreateMemberCommand)); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - Task.Run(async () => + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await queue.WaitUntilConsumerReadyAsync(); + await queue.WaitUntilConsumerReadyAsync(); - await queue.SendAsync(message.Yield()).ConfigureAwait(false); + await queue.SendAsync(message.Yield()).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime_Signed() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.AutoAcknowledge = true; - o.Subject = Guid.NewGuid().ToString(); - o.StreamName = Guid.NewGuid().ToString(); - o.ConsumerName = Guid.NewGuid().ToString(); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.AutoAcknowledge = true; + o.Subject = Guid.NewGuid().ToString(); + o.StreamName = Guid.NewGuid().ToString(); + o.ConsumerName = Guid.NewGuid().ToString(); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var urn = "https://fancy.io/members/signed".ToUri(); - var message = member.ToMessage(urn, nameof(CreateMemberCommand)).Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var urn = "https://fancy.io/members/signed".ToUri(); + var message = member.ToMessage(urn, nameof(CreateMemberCommand)).Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - Task.Run(async () => + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await queue.WaitUntilConsumerReadyAsync(); + await queue.WaitUntilConsumerReadyAsync(); - await queue.SendAsync(message.Yield()).ConfigureAwait(false); + await queue.SendAsync(message.Yield()).ConfigureAwait(false); - var received = (await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10))) as ISignedMessage; - received.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var received = (await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10))) as ISignedMessage; + received.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_HundredTimes() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_HundredTimes() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.StreamName = Guid.NewGuid().ToString(); - o.ConsumerName = Guid.NewGuid().ToString(); - o.Subject = Guid.NewGuid().ToString(); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.StreamName = Guid.NewGuid().ToString(); + o.ConsumerName = Guid.NewGuid().ToString(); + o.Subject = Guid.NewGuid().ToString(); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var messages = Generate.RangeOf(100, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - return message; - }).ToList(); + var messages = Generate.RangeOf(100, i => + { + var email = $"{Generate.RandomString(5)}@outlook.com"; + var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + return message; + }).ToList(); - var receivedMessages = Channel.CreateUnbounded>(); + var receivedMessages = Channel.CreateUnbounded>(); - var count1 = 0; - Task.Run(async () => + var count1 = 0; + Task.Run(async () => + { + while (count1 < messages.Count) { - while (count1 < messages.Count) + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - Interlocked.Increment(ref count1); - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - await msg.AcknowledgeAsync().ConfigureAwait(false); - } + Interlocked.Increment(ref count1); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + await msg.AcknowledgeAsync().ConfigureAwait(false); } - }); + } + }); - var count2 = 0; - Task.Run(async () => + var count2 = 0; + Task.Run(async () => + { + while (count2 < messages.Count) { - while (count2 < messages.Count) + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - Interlocked.Increment(ref count2); - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - await msg.AcknowledgeAsync().ConfigureAwait(false); - } + Interlocked.Increment(ref count2); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + await msg.AcknowledgeAsync().ConfigureAwait(false); } - }); + } + }); - await queue.WaitUntilConsumerReadyAsync(); + await queue.WaitUntilConsumerReadyAsync(); - await queue.SendAsync(messages).ConfigureAwait(false); + await queue.SendAsync(messages).ConfigureAwait(false); - var received = await MessageTestHelper.ReadAsync(receivedMessages.Reader, messages.Count); + var received = await MessageTestHelper.ReadAsync(receivedMessages.Reader, messages.Count); - Assert.Equal(messages.Count, count1 + count2); + Assert.Equal(messages.Count, count1 + count2); - TestOutput.WriteLine(count1.ToString()); - TestOutput.WriteLine(count2.ToString()); + TestOutput.WriteLine(count1.ToString()); + TestOutput.WriteLine(count2.ToString()); - TestOutput.WriteLine(received.Count.ToString()); - TestOutput.WriteLines(received.Take(10)); + TestOutput.WriteLine(received.Count.ToString()); + TestOutput.WriteLines(received.Take(10)); - Assert.Equivalent(messages.Count, received.Count); - Assert.Equivalent(messages, received); - Assert.Equivalent(messages.Select(message => message.Data), received.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received.Select(message => message.Data.Metadata)); - } + Assert.Equivalent(messages.Count, received.Count); + Assert.Equivalent(messages, received); + Assert.Equivalent(messages.Select(message => message.Data), received.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/NatsCommandQueueNewtonsoftJsonSerializerContextTest.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/NatsCommandQueueNewtonsoftJsonSerializerContextTest.cs index ac591682..0ddb3c23 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/NatsCommandQueueNewtonsoftJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/NatsCommandQueueNewtonsoftJsonSerializerContextTest.cs @@ -18,184 +18,184 @@ using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; +using Cuemon.Extensions.DependencyInjection; using Xunit; -namespace Savvyio.Extensions.NATS.Commands +namespace Savvyio.Extensions.NATS.Commands; + +public class NatsCommandQueueNewtonsoftJsonSerializerContextTest : Test { - public class NatsCommandQueueNewtonsoftJsonSerializerContextTest : Test + public NatsCommandQueueNewtonsoftJsonSerializerContextTest(ITestOutputHelper output) : base(output) { - public NatsCommandQueueNewtonsoftJsonSerializerContextTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.AutoAcknowledge = true; - o.Subject = Guid.NewGuid().ToString(); - o.StreamName = Guid.NewGuid().ToString(); - o.ConsumerName = Guid.NewGuid().ToString(); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.AutoAcknowledge = true; + o.Subject = Guid.NewGuid().ToString(); + o.StreamName = Guid.NewGuid().ToString(); + o.ConsumerName = Guid.NewGuid().ToString(); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var urn = "https://fancy.io/members".ToUri(); - var message = member.ToMessage(urn, nameof(CreateMemberCommand)); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var urn = "https://fancy.io/members".ToUri(); + var message = member.ToMessage(urn, nameof(CreateMemberCommand)); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - Task.Run(async () => + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await queue.WaitUntilConsumerReadyAsync(); + await queue.WaitUntilConsumerReadyAsync(); - await queue.SendAsync(message.Yield()).ConfigureAwait(false); + await queue.SendAsync(message.Yield()).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime_Signed() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.AutoAcknowledge = true; - o.Subject = Guid.NewGuid().ToString(); - o.StreamName = Guid.NewGuid().ToString(); - o.ConsumerName = Guid.NewGuid().ToString(); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.AutoAcknowledge = true; + o.Subject = Guid.NewGuid().ToString(); + o.StreamName = Guid.NewGuid().ToString(); + o.ConsumerName = Guid.NewGuid().ToString(); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var urn = "https://fancy.io/members/signed".ToUri(); - var message = member.ToMessage(urn, nameof(CreateMemberCommand)).Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var urn = "https://fancy.io/members/signed".ToUri(); + var message = member.ToMessage(urn, nameof(CreateMemberCommand)).Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - Task.Run(async () => + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await queue.WaitUntilConsumerReadyAsync(); + await queue.WaitUntilConsumerReadyAsync(); - await queue.SendAsync(message.Yield()).ConfigureAwait(false); + await queue.SendAsync(message.Yield()).ConfigureAwait(false); - var received = (await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10))) as ISignedMessage; - received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var received = (await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10))) as ISignedMessage; + received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_HundredTimes() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_HundredTimes() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.StreamName = Guid.NewGuid().ToString(); - o.ConsumerName = Guid.NewGuid().ToString(); - o.Subject = Guid.NewGuid().ToString(); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.StreamName = Guid.NewGuid().ToString(); + o.ConsumerName = Guid.NewGuid().ToString(); + o.Subject = Guid.NewGuid().ToString(); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var messages = Generate.RangeOf(100, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - return message; - }).ToList(); + var messages = Generate.RangeOf(100, i => + { + var email = $"{Generate.RandomString(5)}@outlook.com"; + var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + return message; + }).ToList(); - var receivedMessages = Channel.CreateUnbounded>(); + var receivedMessages = Channel.CreateUnbounded>(); - var count1 = 0; - Task.Run(async () => + var count1 = 0; + Task.Run(async () => + { + while (count1 < messages.Count) { - while (count1 < messages.Count) + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - Interlocked.Increment(ref count1); - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - await msg.AcknowledgeAsync().ConfigureAwait(false); - } + Interlocked.Increment(ref count1); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + await msg.AcknowledgeAsync().ConfigureAwait(false); } - }); + } + }); - var count2 = 0; - Task.Run(async () => + var count2 = 0; + Task.Run(async () => + { + while (count2 < messages.Count) { - while (count2 < messages.Count) + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - Interlocked.Increment(ref count2); - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - await msg.AcknowledgeAsync().ConfigureAwait(false); - } + Interlocked.Increment(ref count2); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + await msg.AcknowledgeAsync().ConfigureAwait(false); } - }); + } + }); - await queue.WaitUntilConsumerReadyAsync(); + await queue.WaitUntilConsumerReadyAsync(); - await queue.SendAsync(messages).ConfigureAwait(false); + await queue.SendAsync(messages).ConfigureAwait(false); - var received = await MessageTestHelper.ReadAsync(receivedMessages.Reader, messages.Count); + var received = await MessageTestHelper.ReadAsync(receivedMessages.Reader, messages.Count); - Assert.Equal(messages.Count, count1 + count2); + Assert.Equal(messages.Count, count1 + count2); - TestOutput.WriteLine(count1.ToString()); - TestOutput.WriteLine(count2.ToString()); + TestOutput.WriteLine(count1.ToString()); + TestOutput.WriteLine(count2.ToString()); - TestOutput.WriteLine(received.Count.ToString()); - TestOutput.WriteLines(received.Take(10)); + TestOutput.WriteLine(received.Count.ToString()); + TestOutput.WriteLines(received.Take(10)); - Assert.Equivalent(messages.Count, received.Count); - Assert.Equivalent(messages, received); - Assert.Equivalent(messages.Select(message => message.Data), received.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received.Select(message => message.Data.Metadata)); - } + Assert.Equivalent(messages.Count, received.Count); + Assert.Equivalent(messages, received); + Assert.Equivalent(messages.Select(message => message.Data), received.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/ObservableNatsCommandQueue.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/ObservableNatsCommandQueue.cs index 7170af63..67a4c2ce 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/ObservableNatsCommandQueue.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/Commands/ObservableNatsCommandQueue.cs @@ -5,26 +5,25 @@ using NATS.Client.JetStream.Models; using Savvyio.Messaging; -namespace Savvyio.Extensions.NATS.Commands +namespace Savvyio.Extensions.NATS.Commands; + +internal sealed class ObservableNatsCommandQueue : NatsCommandQueue { - internal sealed class ObservableNatsCommandQueue : NatsCommandQueue - { - private readonly TaskCompletionSource _consumerReady = new(TaskCreationOptions.RunContinuationsAsynchronously); + private readonly TaskCompletionSource _consumerReady = new(TaskCreationOptions.RunContinuationsAsynchronously); - public ObservableNatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options) : base(marshaller, options) - { - } + public ObservableNatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options) : base(marshaller, options) + { + } - internal Task WaitUntilConsumerReadyAsync() - { - return _consumerReady.Task.WaitAsync(TimeSpan.FromSeconds(10)); - } + internal Task WaitUntilConsumerReadyAsync() + { + return _consumerReady.Task.WaitAsync(TimeSpan.FromSeconds(10)); + } - protected override async Task CreateConsumerAsync(StreamConfig streamConfig, ConsumerConfig consumerConfig, CancellationToken cancellationToken) - { - var consumer = await base.CreateConsumerAsync(streamConfig, consumerConfig, cancellationToken).ConfigureAwait(false); - _consumerReady.TrySetResult(); - return consumer; - } + protected override async Task CreateConsumerAsync(StreamConfig streamConfig, ConsumerConfig consumerConfig, CancellationToken cancellationToken) + { + var consumer = await base.CreateConsumerAsync(streamConfig, consumerConfig, cancellationToken).ConfigureAwait(false); + _consumerReady.TrySetResult(); + return consumer; } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/NatsEventBusJsonSerializerContextTest.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/NatsEventBusJsonSerializerContextTest.cs index 6db420a2..dbe6746f 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/NatsEventBusJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/NatsEventBusJsonSerializerContextTest.cs @@ -6,6 +6,7 @@ using Codebelt.Extensions.Xunit.Hosting; using Cuemon; using Cuemon.Extensions; +using Cuemon.Extensions.DependencyInjection; using Cuemon.Extensions.IO; using Cuemon.Threading; using Microsoft.Extensions.DependencyInjection; @@ -21,280 +22,279 @@ using Savvyio.Messaging.Cryptography; using Xunit; -namespace Savvyio.Extensions.NATS.EventDriven +namespace Savvyio.Extensions.NATS.EventDriven; + +public class NatsEventBusJsonSerializerContextTest : Test { - public class NatsEventBusJsonSerializerContextTest : Test + public NatsEventBusJsonSerializerContextTest(ITestOutputHelper output) : base(output) { - public NatsEventBusJsonSerializerContextTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event"); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event"); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await bus.WaitUntilSubscribedAsync(); + await bus.WaitUntilSubscribedAsync(); - await bus.PublishAsync(message).ConfigureAwait(false); + await bus.PublishAsync(message).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_Signed() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed").Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed").Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await bus.WaitUntilSubscribedAsync(); + await bus.WaitUntilSubscribedAsync(); - await bus.PublishAsync(message).ConfigureAwait(false); + await bus.PublishAsync(message).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedMessage; - received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedMessage; + received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Signature, received.Signature); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Signature, received.Signature); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-cloud-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.cloud-event").ToCloudEvent(); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-cloud-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.cloud-event").ToCloudEvent(); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await bus.WaitUntilSubscribedAsync(); + await bus.WaitUntilSubscribedAsync(); - await bus.PublishAsync(message); + await bus.PublishAsync(message); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ICloudEvent; + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ICloudEvent; - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Specversion, received.Specversion); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Specversion, received.Specversion); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await bus.WaitUntilSubscribedAsync(); + await bus.WaitUntilSubscribedAsync(); - await bus.PublishAsync(message); + await bus.PublishAsync(message); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedCloudEvent; + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedCloudEvent; - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Specversion, received.Specversion); - Assert.Equivalent(message.Signature, received.Signature); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Specversion, received.Specversion); + Assert.Equivalent(message.Signature, received.Signature); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_HundredTimes() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_HundredTimes() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); - - var messages = Generate.RangeOf(100, _ => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}"); - }).ToList(); - var receivedMessages1 = Channel.CreateUnbounded>(); - var receivedMessages2 = Channel.CreateUnbounded>(); - - var handlerInvocations1 = 0; - Task.Run(async () => - { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations1++; - await receivedMessages1.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var handlerInvocations2 = 0; - Task.Run(async () => + var messages = Generate.RangeOf(100, _ => + { + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}"); + }).ToList(); + var receivedMessages1 = Channel.CreateUnbounded>(); + var receivedMessages2 = Channel.CreateUnbounded>(); + + var handlerInvocations1 = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations2++; - await receivedMessages2.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); - - await bus.WaitUntilSubscribedAsync(2); + handlerInvocations1++; + await receivedMessages1.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await ParallelFactory.ForEachAsync(messages, (message, token) => + var handlerInvocations2 = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - return bus.PublishAsync(message, o => o.CancellationToken = token); + handlerInvocations2++; + await receivedMessages2.Writer.WriteAsync(msg, token).ConfigureAwait(false); }).ConfigureAwait(false); + }); - var received1Task = MessageTestHelper.ReadAsync(receivedMessages1.Reader, messages.Count); - var received2Task = MessageTestHelper.ReadAsync(receivedMessages2.Reader, messages.Count); - await Task.WhenAll(received1Task, received2Task); - var received1 = await received1Task; - var received2 = await received2Task; - - TestOutput.WriteLine(received1.Count.ToString()); - TestOutput.WriteLines(received1.Take(10).Select(m => marshaller.Serialize(m).ToEncodedString())); - - Assert.Equal(100, handlerInvocations1); - Assert.Equal(100, handlerInvocations2); - Assert.Equivalent(messages.Count, received1.Count); - Assert.Equivalent(messages, received1); - Assert.Equivalent(messages, received2); - Assert.Equivalent(messages.Select(message => message.Data), received1.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received1.Select(message => message.Data.Metadata)); - Assert.Equivalent(messages.Select(message => message.Data), received2.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received2.Select(message => message.Data.Metadata)); - } + await bus.WaitUntilSubscribedAsync(2); + + await ParallelFactory.ForEachAsync(messages, (message, token) => + { + return bus.PublishAsync(message, o => o.CancellationToken = token); + }).ConfigureAwait(false); + + var received1Task = MessageTestHelper.ReadAsync(receivedMessages1.Reader, messages.Count); + var received2Task = MessageTestHelper.ReadAsync(receivedMessages2.Reader, messages.Count); + await Task.WhenAll(received1Task, received2Task); + var received1 = await received1Task; + var received2 = await received2Task; + + TestOutput.WriteLine(received1.Count.ToString()); + TestOutput.WriteLines(received1.Take(10).Select(m => marshaller.Serialize(m).ToEncodedString())); + + Assert.Equal(100, handlerInvocations1); + Assert.Equal(100, handlerInvocations2); + Assert.Equivalent(messages.Count, received1.Count); + Assert.Equivalent(messages, received1); + Assert.Equivalent(messages, received2); + Assert.Equivalent(messages.Select(message => message.Data), received1.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received1.Select(message => message.Data.Metadata)); + Assert.Equivalent(messages.Select(message => message.Data), received2.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received2.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/NatsEventBusNewtonsoftJsonSerializerContextTest.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/NatsEventBusNewtonsoftJsonSerializerContextTest.cs index 84a41c65..14040013 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/NatsEventBusNewtonsoftJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/NatsEventBusNewtonsoftJsonSerializerContextTest.cs @@ -6,6 +6,7 @@ using Codebelt.Extensions.Xunit.Hosting; using Cuemon; using Cuemon.Extensions; +using Cuemon.Extensions.DependencyInjection; using Cuemon.Extensions.IO; using Cuemon.Threading; using Microsoft.Extensions.DependencyInjection; @@ -21,280 +22,279 @@ using Savvyio.Messaging.Cryptography; using Xunit; -namespace Savvyio.Extensions.NATS.EventDriven +namespace Savvyio.Extensions.NATS.EventDriven; + +public class NatsEventBusNewtonsoftJsonSerializerContextTest : Test { - public class NatsEventBusNewtonsoftJsonSerializerContextTest : Test + public NatsEventBusNewtonsoftJsonSerializerContextTest(ITestOutputHelper output) : base(output) { - public NatsEventBusNewtonsoftJsonSerializerContextTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event"); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event"); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await bus.WaitUntilSubscribedAsync(); + await bus.WaitUntilSubscribedAsync(); - await bus.PublishAsync(message).ConfigureAwait(false); + await bus.PublishAsync(message).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_Signed() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed").Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed").Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await bus.WaitUntilSubscribedAsync(); + await bus.WaitUntilSubscribedAsync(); - await bus.PublishAsync(message).ConfigureAwait(false); + await bus.PublishAsync(message).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedMessage; - received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedMessage; + received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Signature, received.Signature); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Signature, received.Signature); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.cloud-event").ToCloudEvent(); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.cloud-event").ToCloudEvent(); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await bus.WaitUntilSubscribedAsync(); + await bus.WaitUntilSubscribedAsync(); - await bus.PublishAsync(message); + await bus.PublishAsync(message); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ICloudEvent; + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ICloudEvent; - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Specversion, received.Specversion); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Specversion, received.Specversion); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await bus.WaitUntilSubscribedAsync(); + await bus.WaitUntilSubscribedAsync(); - await bus.PublishAsync(message); + await bus.PublishAsync(message); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedCloudEvent; + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedCloudEvent; - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Specversion, received.Specversion); - Assert.Equivalent(message.Signature, received.Signature); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Specversion, received.Specversion); + Assert.Equivalent(message.Signature, received.Signature); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_HundredTimes() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_HundredTimes() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.NatsUrl = NatsTestEnvironment.Url; - o.Subject = Generate.RandomString(10); - }); + o.NatsUrl = NatsTestEnvironment.Url; + o.Subject = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); - - var messages = Generate.RangeOf(100, _ => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}"); - }).ToList(); - var receivedMessages1 = Channel.CreateUnbounded>(); - var receivedMessages2 = Channel.CreateUnbounded>(); - - var handlerInvocations1 = 0; - Task.Run(async () => - { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations1++; - await receivedMessages1.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var handlerInvocations2 = 0; - Task.Run(async () => + var messages = Generate.RangeOf(100, _ => + { + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}"); + }).ToList(); + var receivedMessages1 = Channel.CreateUnbounded>(); + var receivedMessages2 = Channel.CreateUnbounded>(); + + var handlerInvocations1 = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations2++; - await receivedMessages2.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); - - await bus.WaitUntilSubscribedAsync(2); + handlerInvocations1++; + await receivedMessages1.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await ParallelFactory.ForEachAsync(messages, (message, token) => + var handlerInvocations2 = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - return bus.PublishAsync(message, o => o.CancellationToken = token); + handlerInvocations2++; + await receivedMessages2.Writer.WriteAsync(msg, token).ConfigureAwait(false); }).ConfigureAwait(false); + }); - var received1Task = MessageTestHelper.ReadAsync(receivedMessages1.Reader, messages.Count); - var received2Task = MessageTestHelper.ReadAsync(receivedMessages2.Reader, messages.Count); - await Task.WhenAll(received1Task, received2Task); - var received1 = await received1Task; - var received2 = await received2Task; - - TestOutput.WriteLine(received1.Count.ToString()); - TestOutput.WriteLines(received1.Take(10)); - - Assert.Equal(100, handlerInvocations1); - Assert.Equal(100, handlerInvocations2); - Assert.Equivalent(messages.Count, received1.Count); - Assert.Equivalent(messages, received1); - Assert.Equivalent(messages, received2); - Assert.Equivalent(messages.Select(message => message.Data), received1.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received1.Select(message => message.Data.Metadata)); - Assert.Equivalent(messages.Select(message => message.Data), received2.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received2.Select(message => message.Data.Metadata)); - } + await bus.WaitUntilSubscribedAsync(2); + + await ParallelFactory.ForEachAsync(messages, (message, token) => + { + return bus.PublishAsync(message, o => o.CancellationToken = token); + }).ConfigureAwait(false); + + var received1Task = MessageTestHelper.ReadAsync(receivedMessages1.Reader, messages.Count); + var received2Task = MessageTestHelper.ReadAsync(receivedMessages2.Reader, messages.Count); + await Task.WhenAll(received1Task, received2Task); + var received1 = await received1Task; + var received2 = await received2Task; + + TestOutput.WriteLine(received1.Count.ToString()); + TestOutput.WriteLines(received1.Take(10)); + + Assert.Equal(100, handlerInvocations1); + Assert.Equal(100, handlerInvocations2); + Assert.Equivalent(messages.Count, received1.Count); + Assert.Equivalent(messages, received1); + Assert.Equivalent(messages, received2); + Assert.Equivalent(messages.Select(message => message.Data), received1.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received1.Select(message => message.Data.Metadata)); + Assert.Equivalent(messages.Select(message => message.Data), received2.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received2.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/ObservableNatsEventBus.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/ObservableNatsEventBus.cs index 2c63894f..dfb29ae0 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/ObservableNatsEventBus.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/EventDriven/ObservableNatsEventBus.cs @@ -6,79 +6,78 @@ using NATS.Client.Core; using Savvyio.Messaging; -namespace Savvyio.Extensions.NATS.EventDriven +namespace Savvyio.Extensions.NATS.EventDriven; + +internal sealed class ObservableNatsEventBus : NatsEventBus { - internal sealed class ObservableNatsEventBus : NatsEventBus - { - private readonly object _subscribedSyncRoot = new(); - private TaskCompletionSource _subscribed = new(TaskCreationOptions.RunContinuationsAsynchronously); - private int _subscriptionCount; + private readonly object _subscribedSyncRoot = new(); + private TaskCompletionSource _subscribed = new(TaskCreationOptions.RunContinuationsAsynchronously); + private int _subscriptionCount; - public ObservableNatsEventBus(IMarshaller marshaller, NatsEventBusOptions options) : base(marshaller, options) - { - } + public ObservableNatsEventBus(IMarshaller marshaller, NatsEventBusOptions options) : base(marshaller, options) + { + } - internal Task WaitUntilSubscribedAsync(int expectedSubscriptionCount = 1) - { - if (expectedSubscriptionCount < 1) { throw new ArgumentOutOfRangeException(nameof(expectedSubscriptionCount)); } - return WaitUntilSubscribedCoreAsync(expectedSubscriptionCount).WaitAsync(TimeSpan.FromSeconds(10)); - } + internal Task WaitUntilSubscribedAsync(int expectedSubscriptionCount = 1) + { + if (expectedSubscriptionCount < 1) { throw new ArgumentOutOfRangeException(nameof(expectedSubscriptionCount)); } + return WaitUntilSubscribedCoreAsync(expectedSubscriptionCount).WaitAsync(TimeSpan.FromSeconds(10)); + } - private async Task WaitUntilSubscribedCoreAsync(int expectedSubscriptionCount) + private async Task WaitUntilSubscribedCoreAsync(int expectedSubscriptionCount) + { + while (Volatile.Read(ref _subscriptionCount) < expectedSubscriptionCount) { - while (Volatile.Read(ref _subscriptionCount) < expectedSubscriptionCount) + Task subscribedTask; + lock (_subscribedSyncRoot) { - Task subscribedTask; - lock (_subscribedSyncRoot) + if (_subscriptionCount >= expectedSubscriptionCount) { - if (_subscriptionCount >= expectedSubscriptionCount) - { - return; - } - - subscribedTask = _subscribed.Task; + return; } - await subscribedTask.ConfigureAwait(false); + subscribedTask = _subscribed.Task; } + + await subscribedTask.ConfigureAwait(false); } + } - private void SignalSubscribed() + private void SignalSubscribed() + { + TaskCompletionSource subscribed; + lock (_subscribedSyncRoot) { - TaskCompletionSource subscribed; - lock (_subscribedSyncRoot) - { - _subscriptionCount++; - subscribed = _subscribed; - _subscribed = new(TaskCreationOptions.RunContinuationsAsynchronously); - } - - subscribed.TrySetResult(); + _subscriptionCount++; + subscribed = _subscribed; + _subscribed = new(TaskCreationOptions.RunContinuationsAsynchronously); } - protected override async IAsyncEnumerable SubscribeMessagesAsync(string subject, NatsSubOpts options, [EnumeratorCancellation] CancellationToken cancellationToken) + subscribed.TrySetResult(); + } + + protected override async IAsyncEnumerable SubscribeMessagesAsync(string subject, NatsSubOpts options, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var subscriptionRegistered = 0; + var observedOptions = new NatsSubOpts { - var subscriptionRegistered = 0; - var observedOptions = new NatsSubOpts + Events = new NatsSubEvents { - Events = new NatsSubEvents + OnSubscribed = _ => { - OnSubscribed = _ => + if (Interlocked.Exchange(ref subscriptionRegistered, 1) == 0) { - if (Interlocked.Exchange(ref subscriptionRegistered, 1) == 0) - { - SignalSubscribed(); - } - - return default; + SignalSubscribed(); } - } - }; - await foreach (var message in base.SubscribeMessagesAsync(subject, observedOptions, cancellationToken)) - { - yield return message; + return default; + } } + }; + + await foreach (var message in base.SubscribeMessagesAsync(subject, observedOptions, cancellationToken)) + { + yield return message; } } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/MessageTestHelper.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/MessageTestHelper.cs index 4c32efeb..82994ff2 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/MessageTestHelper.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/MessageTestHelper.cs @@ -4,19 +4,18 @@ using System.Threading.Channels; using System.Threading.Tasks; -namespace Savvyio.Extensions.NATS +namespace Savvyio.Extensions.NATS; + +internal static class MessageTestHelper { - internal static class MessageTestHelper + internal static async Task> ReadAsync(ChannelReader reader, int count) { - internal static async Task> ReadAsync(ChannelReader reader, int count) + using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var messages = new List(count); + while (messages.Count < count) { - using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - var messages = new List(count); - while (messages.Count < count) - { - messages.Add(await reader.ReadAsync(timeout.Token)); - } - return messages; + messages.Add(await reader.ReadAsync(timeout.Token)); } + return messages; } } diff --git a/test/Savvyio.Extensions.NATS.FunctionalTests/NatsTestEnvironment.cs b/test/Savvyio.Extensions.NATS.FunctionalTests/NatsTestEnvironment.cs index 21a49848..45f6a7da 100644 --- a/test/Savvyio.Extensions.NATS.FunctionalTests/NatsTestEnvironment.cs +++ b/test/Savvyio.Extensions.NATS.FunctionalTests/NatsTestEnvironment.cs @@ -1,9 +1,8 @@ using System; -namespace Savvyio.Extensions.NATS +namespace Savvyio.Extensions.NATS; + +internal static class NatsTestEnvironment { - internal static class NatsTestEnvironment - { - internal static Uri Url { get; } = new(Environment.GetEnvironmentVariable("SAVVYIO_NATS_URL") ?? "nats://127.0.0.1:4222"); - } + internal static Uri Url { get; } = new(Environment.GetEnvironmentVariable("SAVVYIO_NATS_URL") ?? "nats://127.0.0.1:4222"); } diff --git a/test/Savvyio.Extensions.NATS.Tests/Commands/NatsCommandQueueOptionsTest.cs b/test/Savvyio.Extensions.NATS.Tests/Commands/NatsCommandQueueOptionsTest.cs index 9546eb88..1f741f73 100644 --- a/test/Savvyio.Extensions.NATS.Tests/Commands/NatsCommandQueueOptionsTest.cs +++ b/test/Savvyio.Extensions.NATS.Tests/Commands/NatsCommandQueueOptionsTest.cs @@ -1,176 +1,175 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using System; using Xunit; -namespace Savvyio.Extensions.NATS.Commands +namespace Savvyio.Extensions.NATS.Commands; + +public class NatsCommandQueueOptionsTest : Test { - public class NatsCommandQueueOptionsTest : Test + public NatsCommandQueueOptionsTest(ITestOutputHelper output) : base(output) { - public NatsCommandQueueOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_Sets_Default_Values() - { - var options = new NatsCommandQueueOptions(); - - Assert.Equal(25, options.MaxMessages); - Assert.Equal(TimeSpan.Zero, options.Heartbeat); - Assert.Equal(TimeSpan.Zero, options.Expires); - Assert.Null(options.StreamName); - Assert.Null(options.ConsumerName); - Assert.False(options.AutoAcknowledge); - } - - [Theory] - [InlineData(0, 1)] - [InlineData(1, 1)] - [InlineData(25, 25)] - [InlineData(32767, 32767)] - [InlineData(40000, 32767)] - public void MaxMessages_Clamps_Value(int input, int expected) + [Fact] + public void Constructor_Sets_Default_Values() + { + var options = new NatsCommandQueueOptions(); + + Assert.Equal(25, options.MaxMessages); + Assert.Equal(TimeSpan.Zero, options.Heartbeat); + Assert.Equal(TimeSpan.Zero, options.Expires); + Assert.Null(options.StreamName); + Assert.Null(options.ConsumerName); + Assert.False(options.AutoAcknowledge); + } + + [Theory] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(25, 25)] + [InlineData(32767, 32767)] + [InlineData(40000, 32767)] + public void MaxMessages_Clamps_Value(int input, int expected) + { + var options = new NatsCommandQueueOptions { - var options = new NatsCommandQueueOptions - { - MaxMessages = input - }; + MaxMessages = input + }; - Assert.Equal(expected, options.MaxMessages); - } + Assert.Equal(expected, options.MaxMessages); + } - [Fact] - public void Expires_Sets_Heartbeat_When_Expires_AtLeast_30Seconds() - { - var options = new NatsCommandQueueOptions(); + [Fact] + public void Expires_Sets_Heartbeat_When_Expires_AtLeast_30Seconds() + { + var options = new NatsCommandQueueOptions(); - options.Expires = TimeSpan.FromSeconds(29); - Assert.Equal(TimeSpan.FromSeconds(29), options.Expires); - Assert.Equal(TimeSpan.Zero, options.Heartbeat); + options.Expires = TimeSpan.FromSeconds(29); + Assert.Equal(TimeSpan.FromSeconds(29), options.Expires); + Assert.Equal(TimeSpan.Zero, options.Heartbeat); - options.Expires = TimeSpan.FromSeconds(30); - Assert.Equal(TimeSpan.FromSeconds(30), options.Expires); - Assert.Equal(TimeSpan.FromSeconds(5), options.Heartbeat); + options.Expires = TimeSpan.FromSeconds(30); + Assert.Equal(TimeSpan.FromSeconds(30), options.Expires); + Assert.Equal(TimeSpan.FromSeconds(5), options.Heartbeat); - options.Expires = TimeSpan.FromSeconds(60); - Assert.Equal(TimeSpan.FromSeconds(60), options.Expires); - Assert.Equal(TimeSpan.FromSeconds(5), options.Heartbeat); - } + options.Expires = TimeSpan.FromSeconds(60); + Assert.Equal(TimeSpan.FromSeconds(60), options.Expires); + Assert.Equal(TimeSpan.FromSeconds(5), options.Heartbeat); + } - [Fact] - public void Heartbeat_Can_Be_Set_Manually() + [Fact] + public void Heartbeat_Can_Be_Set_Manually() + { + var options = new NatsCommandQueueOptions { - var options = new NatsCommandQueueOptions - { - Heartbeat = TimeSpan.FromSeconds(10) - }; + Heartbeat = TimeSpan.FromSeconds(10) + }; - Assert.Equal(TimeSpan.FromSeconds(10), options.Heartbeat); - } + Assert.Equal(TimeSpan.FromSeconds(10), options.Heartbeat); + } - [Fact] - public void Heartbeat_Can_Be_Overridden_After_Expires() - { - var options = new NatsCommandQueueOptions(); + [Fact] + public void Heartbeat_Can_Be_Overridden_After_Expires() + { + var options = new NatsCommandQueueOptions(); - options.Expires = TimeSpan.FromSeconds(60); - Assert.Equal(TimeSpan.FromSeconds(5), options.Heartbeat); + options.Expires = TimeSpan.FromSeconds(60); + Assert.Equal(TimeSpan.FromSeconds(5), options.Heartbeat); - options.Heartbeat = TimeSpan.FromSeconds(15); - Assert.Equal(TimeSpan.FromSeconds(15), options.Heartbeat); - } + options.Heartbeat = TimeSpan.FromSeconds(15); + Assert.Equal(TimeSpan.FromSeconds(15), options.Heartbeat); + } - [Fact] - public void StreamName_And_ConsumerName_Can_Be_Set_And_Gotten() + [Fact] + public void StreamName_And_ConsumerName_Can_Be_Set_And_Gotten() + { + var options = new NatsCommandQueueOptions { - var options = new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = "consumer" - }; - - Assert.Equal("stream", options.StreamName); - Assert.Equal("consumer", options.ConsumerName); - } - - [Fact] - public void AutoAcknowledge_Can_Be_Set_And_Gotten() + StreamName = "stream", + ConsumerName = "consumer" + }; + + Assert.Equal("stream", options.StreamName); + Assert.Equal("consumer", options.ConsumerName); + } + + [Fact] + public void AutoAcknowledge_Can_Be_Set_And_Gotten() + { + var options = new NatsCommandQueueOptions { - var options = new NatsCommandQueueOptions - { - AutoAcknowledge = true - }; + AutoAcknowledge = true + }; - Assert.True(options.AutoAcknowledge); - } + Assert.True(options.AutoAcknowledge); + } - [Fact] - public void ValidateOptions_Throws_If_StreamName_Is_Null_Or_Whitespace() + [Fact] + public void ValidateOptions_Throws_If_StreamName_Is_Null_Or_Whitespace() + { + var options = new NatsCommandQueueOptions { - var options = new NatsCommandQueueOptions - { - StreamName = null, - ConsumerName = "consumer" - }; + StreamName = null, + ConsumerName = "consumer" + }; - Assert.Throws(() => options.ValidateOptions()); + Assert.Throws(() => options.ValidateOptions()); - options.StreamName = ""; - Assert.Throws(() => options.ValidateOptions()); + options.StreamName = ""; + Assert.Throws(() => options.ValidateOptions()); - options.StreamName = " "; - Assert.Throws(() => options.ValidateOptions()); - } + options.StreamName = " "; + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_Throws_If_ConsumerName_Is_Null_Or_Whitespace() + [Fact] + public void ValidateOptions_Throws_If_ConsumerName_Is_Null_Or_Whitespace() + { + var options = new NatsCommandQueueOptions { - var options = new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = null - }; + StreamName = "stream", + ConsumerName = null + }; - Assert.Throws(() => options.ValidateOptions()); + Assert.Throws(() => options.ValidateOptions()); - options.ConsumerName = ""; - Assert.Throws(() => options.ValidateOptions()); + options.ConsumerName = ""; + Assert.Throws(() => options.ValidateOptions()); - options.ConsumerName = " "; - Assert.Throws(() => options.ValidateOptions()); - } + options.ConsumerName = " "; + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_Throws_If_Base_Options_Invalid() + [Fact] + public void ValidateOptions_Throws_If_Base_Options_Invalid() + { + var options = new NatsCommandQueueOptions { - var options = new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = "consumer", - Subject = null // base class property - }; - - // NatsMessageOptions.ValidateOptions throws if Subject is null or whitespace - Assert.Throws(() => options.ValidateOptions()); - - options.Subject = "subject"; - options.NatsUrl = null; // base class property - Assert.Throws(() => options.ValidateOptions()); - } - - [Fact] - public void ValidateOptions_Does_Not_Throw_If_Valid() + StreamName = "stream", + ConsumerName = "consumer", + Subject = null // base class property + }; + + // NatsMessageOptions.ValidateOptions throws if Subject is null or whitespace + Assert.Throws(() => options.ValidateOptions()); + + options.Subject = "subject"; + options.NatsUrl = null; // base class property + Assert.Throws(() => options.ValidateOptions()); + } + + [Fact] + public void ValidateOptions_Does_Not_Throw_If_Valid() + { + var options = new NatsCommandQueueOptions { - var options = new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = "consumer", - Subject = "subject", - NatsUrl = new Uri("nats://localhost:4222") - }; - - var ex = Record.Exception(() => options.ValidateOptions()); - Assert.Null(ex); - } + StreamName = "stream", + ConsumerName = "consumer", + Subject = "subject", + NatsUrl = new Uri("nats://localhost:4222") + }; + + var ex = Record.Exception(() => options.ValidateOptions()); + Assert.Null(ex); } } diff --git a/test/Savvyio.Extensions.NATS.Tests/Commands/NatsCommandQueueTest.cs b/test/Savvyio.Extensions.NATS.Tests/Commands/NatsCommandQueueTest.cs index 2e78021b..9f627f00 100644 --- a/test/Savvyio.Extensions.NATS.Tests/Commands/NatsCommandQueueTest.cs +++ b/test/Savvyio.Extensions.NATS.Tests/Commands/NatsCommandQueueTest.cs @@ -15,334 +15,333 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.NATS.Commands +namespace Savvyio.Extensions.NATS.Commands; + +public class NatsCommandQueueTest : Test { - public class NatsCommandQueueTest : Test - { - private IMarshaller _marshaller = JsonMarshaller.Default; - private NatsCommandQueueOptions _options = new (); + private IMarshaller _marshaller = JsonMarshaller.Default; + private NatsCommandQueueOptions _options = new(); - public NatsCommandQueueTest(ITestOutputHelper output) : base(output) { } + public NatsCommandQueueTest(ITestOutputHelper output) : base(output) { } - [Fact] - public void Constructor_ShouldInitialize_WithValidOptions() + [Fact] + public void Constructor_ShouldInitialize_WithValidOptions() + { + var options = new NatsCommandQueueOptions { - var options = new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = "consumer", - Subject = "subject" - }; + StreamName = "stream", + ConsumerName = "consumer", + Subject = "subject" + }; - var queue = new NatsCommandQueue(_marshaller, options); + var queue = new NatsCommandQueue(_marshaller, options); - Assert.NotNull(queue); - Assert.IsAssignableFrom>(queue); - Assert.IsAssignableFrom>(queue); - Assert.IsAssignableFrom>(queue); - } + Assert.NotNull(queue); + Assert.IsAssignableFrom>(queue); + Assert.IsAssignableFrom>(queue); + Assert.IsAssignableFrom>(queue); + } - [Fact] - public void Constructor_ShouldThrow_WhenMarshallerIsNull() - { - Assert.Throws(() => new NatsCommandQueue(null, _options)); - } + [Fact] + public void Constructor_ShouldThrow_WhenMarshallerIsNull() + { + Assert.Throws(() => new NatsCommandQueue(null, _options)); + } - [Fact] - public void Constructor_ShouldThrow_WhenOptionsIsNull() - { - Assert.Throws(() => new NatsCommandQueue(_marshaller, null)); - } + [Fact] + public void Constructor_ShouldThrow_WhenOptionsIsNull() + { + Assert.Throws(() => new NatsCommandQueue(_marshaller, null)); + } - - [Fact] - public void Constructor_ShouldThrow_WhenOptionsAreInvalid() - { - Assert.Throws(() => new NatsCommandQueue(_marshaller, _options)); - } - [Fact] - public void GetHealthCheckTarget_Should_Return_Connection() + [Fact] + public void Constructor_ShouldThrow_WhenOptionsAreInvalid() + { + Assert.Throws(() => new NatsCommandQueue(_marshaller, _options)); + } + + [Fact] + public void GetHealthCheckTarget_Should_Return_Connection() + { + var queue = new NatsCommandQueue(_marshaller, new NatsCommandQueueOptions { - var queue = new NatsCommandQueue(_marshaller, new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = "consumer", - Subject = "subject" - }); + StreamName = "stream", + ConsumerName = "consumer", + Subject = "subject" + }); - Assert.Same(queue.GetHealthCheckTarget(), queue.GetHealthCheckTarget()); - Assert.NotNull(queue.GetHealthCheckTarget()); - } + Assert.Same(queue.GetHealthCheckTarget(), queue.GetHealthCheckTarget()); + Assert.NotNull(queue.GetHealthCheckTarget()); + } - [Fact] - public async Task SendAsync_WithEmptyMessages_ShouldCoverJetStreamContextCreation() + [Fact] + public async Task SendAsync_WithEmptyMessages_ShouldCoverJetStreamContextCreation() + { + var queue = new NatsCommandQueue(_marshaller, new NatsCommandQueueOptions { - var queue = new NatsCommandQueue(_marshaller, new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = "consumer", - Subject = "subject" - }); + StreamName = "stream", + ConsumerName = "consumer", + Subject = "subject" + }); - await queue.SendAsync(Array.Empty>()); + await queue.SendAsync(Array.Empty>()); - Assert.True(true); - } + Assert.True(true); + } - [Fact] - public async Task SendAsync_WithOneMessage_ShouldCoverLoopBodyBeforeConnectionFailure() + [Fact] + public async Task SendAsync_WithOneMessage_ShouldCoverLoopBodyBeforeConnectionFailure() + { + var queue = new NatsCommandQueue(_marshaller, new NatsCommandQueueOptions { - var queue = new NatsCommandQueue(_marshaller, new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = "consumer", - Subject = "subject", - NatsUrl = new Uri("nats://localhost:59999") - }); + StreamName = "stream", + ConsumerName = "consumer", + Subject = "subject", + NatsUrl = new Uri("nats://localhost:59999") + }); - var message = new Message( - Guid.NewGuid().ToString("N"), - new Uri("urn:test"), - "test", - new TestCommand()); + var message = new Message( + Guid.NewGuid().ToString("N"), + new Uri("urn:test"), + "test", + new TestCommand()); - var ex = await Record.ExceptionAsync(() => queue.SendAsync(new IMessage[] { message })); + var ex = await Record.ExceptionAsync(() => queue.SendAsync(new IMessage[] { message })); - Assert.NotNull(ex); - } + Assert.NotNull(ex); + } - [Fact] - public async Task SendAsync_ShouldPublishSerializedMessages() - { - var queue = new TestableNatsCommandQueue(_marshaller, CreateOptions()); - var message = CreateMessage(); + [Fact] + public async Task SendAsync_ShouldPublishSerializedMessages() + { + var queue = new TestableNatsCommandQueue(_marshaller, CreateOptions()); + var message = CreateMessage(); - await queue.SendAsync([message]); + await queue.SendAsync([message]); - Assert.Equal("subject", queue.PublishedSubject); - Assert.Equal(message.GetType().ToFullNameIncludingAssemblyName(), queue.PublishedHeaders["type"]); - Assert.NotNull(queue.PublishedMessage); - } + Assert.Equal("subject", queue.PublishedSubject); + Assert.Equal(message.GetType().ToFullNameIncludingAssemblyName(), queue.PublishedHeaders["type"]); + Assert.NotNull(queue.PublishedMessage); + } - [Fact] - public async Task SendAsync_ShouldUseJetStreamContext_WhenPublishingMessage() - { - var context = new Mock(); - context.Setup(c => c.PublishAsync(It.IsAny(), It.IsAny(), null, null, It.IsAny(), It.IsAny())) - .Returns(new ValueTask(default(PubAckResponse))); - var queue = new ContextNatsCommandQueue(_marshaller, CreateOptions(), context.Object); - var message = CreateMessage(); + [Fact] + public async Task SendAsync_ShouldUseJetStreamContext_WhenPublishingMessage() + { + var context = new Mock(); + context.Setup(c => c.PublishAsync(It.IsAny(), It.IsAny(), null, null, It.IsAny(), It.IsAny())) + .Returns(new ValueTask(default(PubAckResponse))); + var queue = new ContextNatsCommandQueue(_marshaller, CreateOptions(), context.Object); + var message = CreateMessage(); - await queue.SendAsync([message]); + await queue.SendAsync([message]); - context.Verify(c => c.PublishAsync("subject", It.IsAny(), null, null, It.Is(headers => headers["type"] == message.GetType().ToFullNameIncludingAssemblyName()), It.IsAny()), Times.Once); - } + context.Verify(c => c.PublishAsync("subject", It.IsAny(), null, null, It.Is(headers => headers["type"] == message.GetType().ToFullNameIncludingAssemblyName()), It.IsAny()), Times.Once); + } - [Fact] - public async Task ReceiveAsync_ShouldDeserializeFetchedMessagesAndAcknowledge() + [Fact] + public async Task ReceiveAsync_ShouldDeserializeFetchedMessagesAndAcknowledge() + { + var options = CreateOptions(); + options.AutoAcknowledge = true; + options.Expires = TimeSpan.FromSeconds(30); + var message = CreateMessage(); + var queue = new TestableNatsCommandQueue(_marshaller, options, message); + + var received = new List>(); + await foreach (var item in queue.ReceiveAsync()) { - var options = CreateOptions(); - options.AutoAcknowledge = true; - options.Expires = TimeSpan.FromSeconds(30); - var message = CreateMessage(); - var queue = new TestableNatsCommandQueue(_marshaller, options, message); - - var received = new List>(); - await foreach (var item in queue.ReceiveAsync()) - { - received.Add(item); - } - - Assert.Single(received); - Assert.Equal(message.Id, received[0].Id); - Assert.Equal("stream", queue.StreamConfig.Name); - Assert.Equal("consumer", queue.ConsumerConfig.Name); - Assert.Equal(TimeSpan.FromSeconds(30), queue.FetchOptions.Expires); - Assert.Equal(TimeSpan.FromSeconds(5), queue.FetchOptions.IdleHeartbeat); - Assert.Equal(1, queue.AcknowledgedCount); + received.Add(item); } - [Fact] - public async Task ReceiveAsync_ShouldUseJetStreamContext_WhenCreatingConsumer() - { - var context = new Mock(); - context.Setup(c => c.CreateOrUpdateStreamAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(Mock.Of()); - context.Setup(c => c.CreateOrUpdateConsumerAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(Mock.Of()); - var queue = new ContextNatsCommandQueue(_marshaller, CreateOptions(), context.Object); - - var consumer = await queue.CallCreateConsumerAsync( - new StreamConfig("stream", ["subject"]), - new ConsumerConfig("consumer"), - CancellationToken.None); - - Assert.NotNull(consumer); - context.Verify(c => c.CreateOrUpdateStreamAsync(It.Is(config => config.Name == "stream"), It.IsAny()), Times.Once); - context.Verify(c => c.CreateOrUpdateConsumerAsync("stream", It.Is(config => config.Name == "consumer"), It.IsAny()), Times.Once); - } + Assert.Single(received); + Assert.Equal(message.Id, received[0].Id); + Assert.Equal("stream", queue.StreamConfig.Name); + Assert.Equal("consumer", queue.ConsumerConfig.Name); + Assert.Equal(TimeSpan.FromSeconds(30), queue.FetchOptions.Expires); + Assert.Equal(TimeSpan.FromSeconds(5), queue.FetchOptions.IdleHeartbeat); + Assert.Equal(1, queue.AcknowledgedCount); + } - [Fact] - public async Task ReceiveAsync_ShouldFetchMessagesFromConsumer() - { - var message = CreateMessage(); - var payload = JsonMarshaller.Default.Serialize(message).ToByteArray().ToBase64String(); - var headers = new NatsHeaders - { - { "type", message.GetType().ToFullNameIncludingAssemblyName() } - }; - var natsMessage = new NatsMsg("subject", "reply", payload.Length, headers, payload, Mock.Of(), default); - var jetStreamMessage = new NatsJSMsg(natsMessage, Mock.Of()); - var consumer = new Mock(); - consumer.Setup(c => c.FetchAsync(It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(Yield>(jetStreamMessage)); - var queue = new ContextNatsCommandQueue(_marshaller, CreateOptions(), Mock.Of()); - - var fetched = await queue.CallFetchMessagesAsync(consumer.Object, new NatsJSFetchOpts(), CancellationToken.None); - - Assert.Equal(1, fetched.Count); - Assert.Equal(payload, fetched.Data); - } + [Fact] + public async Task ReceiveAsync_ShouldUseJetStreamContext_WhenCreatingConsumer() + { + var context = new Mock(); + context.Setup(c => c.CreateOrUpdateStreamAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Mock.Of()); + context.Setup(c => c.CreateOrUpdateConsumerAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(Mock.Of()); + var queue = new ContextNatsCommandQueue(_marshaller, CreateOptions(), context.Object); + + var consumer = await queue.CallCreateConsumerAsync( + new StreamConfig("stream", ["subject"]), + new ConsumerConfig("consumer"), + CancellationToken.None); + + Assert.NotNull(consumer); + context.Verify(c => c.CreateOrUpdateStreamAsync(It.Is(config => config.Name == "stream"), It.IsAny()), Times.Once); + context.Verify(c => c.CreateOrUpdateConsumerAsync("stream", It.Is(config => config.Name == "consumer"), It.IsAny()), Times.Once); + } - [Fact] - public async Task ReceiveAsync_WithPreCancelledToken_ShouldThrowException() + [Fact] + public async Task ReceiveAsync_ShouldFetchMessagesFromConsumer() + { + var message = CreateMessage(); + var payload = JsonMarshaller.Default.Serialize(message).ToByteArray().ToBase64String(); + var headers = new NatsHeaders { - using var cts = new CancellationTokenSource(); - cts.Cancel(); - - var queue = new NatsCommandQueue(_marshaller, new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = "consumer", - Subject = "subject" - }); - - var exception = await Record.ExceptionAsync(async () => - { - await foreach (var _ in queue.ReceiveAsync(o => o.CancellationToken = cts.Token)) { } - }); + { "type", message.GetType().ToFullNameIncludingAssemblyName() } + }; + var natsMessage = new NatsMsg("subject", "reply", payload.Length, headers, payload, Mock.Of(), default); + var jetStreamMessage = new NatsJSMsg(natsMessage, Mock.Of()); + var consumer = new Mock(); + consumer.Setup(c => c.FetchAsync(It.IsAny(), It.IsAny>(), It.IsAny())) + .Returns(Yield>(jetStreamMessage)); + var queue = new ContextNatsCommandQueue(_marshaller, CreateOptions(), Mock.Of()); + + var fetched = await queue.CallFetchMessagesAsync(consumer.Object, new NatsJSFetchOpts(), CancellationToken.None); + + Assert.Equal(1, fetched.Count); + Assert.Equal(payload, fetched.Data); + } - Assert.NotNull(exception); - } + [Fact] + public async Task ReceiveAsync_WithPreCancelledToken_ShouldThrowException() + { + using var cts = new CancellationTokenSource(); + cts.Cancel(); - private static NatsCommandQueueOptions CreateOptions() + var queue = new NatsCommandQueue(_marshaller, new NatsCommandQueueOptions { - return new NatsCommandQueueOptions - { - StreamName = "stream", - ConsumerName = "consumer", - Subject = "subject" - }; - } + StreamName = "stream", + ConsumerName = "consumer", + Subject = "subject" + }); - private static IMessage CreateMessage() + var exception = await Record.ExceptionAsync(async () => { - return new Message( - Guid.NewGuid().ToString("N"), - new Uri("urn:test"), - "test", - new TestCommand()); - } + await foreach (var _ in queue.ReceiveAsync(o => o.CancellationToken = cts.Token)) { } + }); + + Assert.NotNull(exception); + } - private sealed class TestableNatsCommandQueue : NatsCommandQueue + private static NatsCommandQueueOptions CreateOptions() + { + return new NatsCommandQueueOptions { - private readonly IMessage _message; + StreamName = "stream", + ConsumerName = "consumer", + Subject = "subject" + }; + } - public TestableNatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options, IMessage message = null) : base(marshaller, options) - { - _message = message; - } + private static IMessage CreateMessage() + { + return new Message( + Guid.NewGuid().ToString("N"), + new Uri("urn:test"), + "test", + new TestCommand()); + } - public string PublishedSubject { get; private set; } + private sealed class TestableNatsCommandQueue : NatsCommandQueue + { + private readonly IMessage _message; - public string PublishedMessage { get; private set; } + public TestableNatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options, IMessage message = null) : base(marshaller, options) + { + _message = message; + } - public NatsHeaders PublishedHeaders { get; private set; } + public string PublishedSubject { get; private set; } - public StreamConfig StreamConfig { get; private set; } + public string PublishedMessage { get; private set; } - public ConsumerConfig ConsumerConfig { get; private set; } + public NatsHeaders PublishedHeaders { get; private set; } - public NatsJSFetchOpts FetchOptions { get; private set; } + public StreamConfig StreamConfig { get; private set; } - public int AcknowledgedCount { get; private set; } + public ConsumerConfig ConsumerConfig { get; private set; } - protected override Task PublishMessageAsync(INatsJSContext context, string subject, string message, NatsHeaders headers) - { - PublishedSubject = subject; - PublishedMessage = message; - PublishedHeaders = headers; - return Task.CompletedTask; - } + public NatsJSFetchOpts FetchOptions { get; private set; } - protected override Task CreateConsumerAsync(StreamConfig streamConfig, ConsumerConfig consumerConfig, CancellationToken cancellationToken) - { - StreamConfig = streamConfig; - ConsumerConfig = consumerConfig; - return Task.FromResult(null); - } + public int AcknowledgedCount { get; private set; } - protected override async IAsyncEnumerable FetchMessagesAsync(INatsJSConsumer consumer, NatsJSFetchOpts options, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) - { - FetchOptions = options; - if (_message != null) - { - var payload = JsonMarshaller.Default.Serialize(_message).ToByteArray().ToBase64String(); - yield return new ReceivedNatsMessage(new NatsHeaders - { - { "type", _message.GetType().ToFullNameIncludingAssemblyName() } - }, payload, _ => - { - AcknowledgedCount++; - return Task.CompletedTask; - }); - } - - await Task.CompletedTask.ConfigureAwait(false); - } + protected override Task PublishMessageAsync(INatsJSContext context, string subject, string message, NatsHeaders headers) + { + PublishedSubject = subject; + PublishedMessage = message; + PublishedHeaders = headers; + return Task.CompletedTask; } - private sealed class ContextNatsCommandQueue : NatsCommandQueue + protected override Task CreateConsumerAsync(StreamConfig streamConfig, ConsumerConfig consumerConfig, CancellationToken cancellationToken) { - private readonly INatsJSContext _context; + StreamConfig = streamConfig; + ConsumerConfig = consumerConfig; + return Task.FromResult(null); + } - public ContextNatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options, INatsJSContext context) : base(marshaller, options) + protected override async IAsyncEnumerable FetchMessagesAsync(INatsJSConsumer consumer, NatsJSFetchOpts options, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) + { + FetchOptions = options; + if (_message != null) { - _context = context; + var payload = JsonMarshaller.Default.Serialize(_message).ToByteArray().ToBase64String(); + yield return new ReceivedNatsMessage(new NatsHeaders + { + { "type", _message.GetType().ToFullNameIncludingAssemblyName() } + }, payload, _ => + { + AcknowledgedCount++; + return Task.CompletedTask; + }); } - public Task CallCreateConsumerAsync(StreamConfig streamConfig, ConsumerConfig consumerConfig, CancellationToken cancellationToken) - { - return base.CreateConsumerAsync(streamConfig, consumerConfig, cancellationToken); - } + await Task.CompletedTask.ConfigureAwait(false); + } + } - public async Task<(int Count, string Data)> CallFetchMessagesAsync(INatsJSConsumer consumer, NatsJSFetchOpts options, CancellationToken cancellationToken) - { - var count = 0; - string data = null; - await foreach (var message in base.FetchMessagesAsync(consumer, options, cancellationToken)) - { - count++; - data = message.Data; - await Record.ExceptionAsync(() => message.AcknowledgeAsync(cancellationToken)).ConfigureAwait(false); - } + private sealed class ContextNatsCommandQueue : NatsCommandQueue + { + private readonly INatsJSContext _context; - return (count, data); - } + public ContextNatsCommandQueue(IMarshaller marshaller, NatsCommandQueueOptions options, INatsJSContext context) : base(marshaller, options) + { + _context = context; + } + + public Task CallCreateConsumerAsync(StreamConfig streamConfig, ConsumerConfig consumerConfig, CancellationToken cancellationToken) + { + return base.CreateConsumerAsync(streamConfig, consumerConfig, cancellationToken); + } - protected override INatsJSContext CreateJetStreamContext() + public async Task<(int Count, string Data)> CallFetchMessagesAsync(INatsJSConsumer consumer, NatsJSFetchOpts options, CancellationToken cancellationToken) + { + var count = 0; + string data = null; + await foreach (var message in base.FetchMessagesAsync(consumer, options, cancellationToken)) { - return _context; + count++; + data = message.Data; + await Record.ExceptionAsync(() => message.AcknowledgeAsync(cancellationToken)).ConfigureAwait(false); } + + return (count, data); } - private static async IAsyncEnumerable Yield(T item) + protected override INatsJSContext CreateJetStreamContext() { - yield return item; - await Task.CompletedTask.ConfigureAwait(false); + return _context; } + } - private record TestCommand : Command { } + private static async IAsyncEnumerable Yield(T item) + { + yield return item; + await Task.CompletedTask.ConfigureAwait(false); } + + private record TestCommand : Command { } } diff --git a/test/Savvyio.Extensions.NATS.Tests/EventDriven/NatsEventBusOptionsTest.cs b/test/Savvyio.Extensions.NATS.Tests/EventDriven/NatsEventBusOptionsTest.cs index ad01e1db..88fa0131 100644 --- a/test/Savvyio.Extensions.NATS.Tests/EventDriven/NatsEventBusOptionsTest.cs +++ b/test/Savvyio.Extensions.NATS.Tests/EventDriven/NatsEventBusOptionsTest.cs @@ -1,30 +1,29 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.NATS.EventDriven +namespace Savvyio.Extensions.NATS.EventDriven; + +public class NatsEventBusOptionsTest : Test { - public class NatsEventBusOptionsTest : Test + public NatsEventBusOptionsTest(ITestOutputHelper output) : base(output) { - public NatsEventBusOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_Should_Inherit_Defaults_From_Base_Type() - { - var options = new NatsEventBusOptions(); + [Fact] + public void Constructor_Should_Inherit_Defaults_From_Base_Type() + { + var options = new NatsEventBusOptions(); - Assert.IsAssignableFrom(options); - Assert.Equal(new System.Uri("nats://127.0.0.1:4222"), options.NatsUrl); - Assert.Null(options.Subject); - } + Assert.IsAssignableFrom(options); + Assert.Equal(new System.Uri("nats://127.0.0.1:4222"), options.NatsUrl); + Assert.Null(options.Subject); + } - [Fact] - public void ValidateOptions_Should_Require_Subject() - { - var options = new NatsEventBusOptions(); + [Fact] + public void ValidateOptions_Should_Require_Subject() + { + var options = new NatsEventBusOptions(); - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); } } diff --git a/test/Savvyio.Extensions.NATS.Tests/EventDriven/NatsEventBusTest.cs b/test/Savvyio.Extensions.NATS.Tests/EventDriven/NatsEventBusTest.cs index b78e0823..d4270756 100644 --- a/test/Savvyio.Extensions.NATS.Tests/EventDriven/NatsEventBusTest.cs +++ b/test/Savvyio.Extensions.NATS.Tests/EventDriven/NatsEventBusTest.cs @@ -13,180 +13,182 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.NATS.EventDriven +namespace Savvyio.Extensions.NATS.EventDriven; + +public class NatsEventBusTest : Test { - public class NatsEventBusTest : Test - { - private IMarshaller _marshaller = JsonMarshaller.Default; - private NatsEventBusOptions _options = new (); + private IMarshaller _marshaller = JsonMarshaller.Default; + private NatsEventBusOptions _options = new(); - public NatsEventBusTest(ITestOutputHelper output) : base(output) { } + public NatsEventBusTest(ITestOutputHelper output) : base(output) { } - [Fact] - public void Constructor_ShouldInitialize_WithValidOptions() + [Fact] + public void Constructor_ShouldInitialize_WithValidOptions() + { + var options = new NatsEventBusOptions { - var options = new NatsEventBusOptions - { - Subject = "subject" - }; + Subject = "subject" + }; - var bus = new NatsEventBus(_marshaller, options); + var bus = new NatsEventBus(_marshaller, options); - Assert.NotNull(bus); - Assert.IsAssignableFrom>(bus); - Assert.IsAssignableFrom>(bus); - Assert.IsAssignableFrom>(bus); - } + Assert.NotNull(bus); + Assert.IsAssignableFrom>(bus); + Assert.IsAssignableFrom>(bus); + Assert.IsAssignableFrom>(bus); + } - [Fact] - public void Constructor_ShouldThrow_WhenMarshallerIsNull() - { - Assert.Throws(() => new NatsEventBus(null, _options)); - } - - [Fact] - public void Constructor_ShouldThrow_WhenOptionsIsNull() - { - Assert.Throws(() => new NatsEventBus(_marshaller, null)); - } + [Fact] + public void Constructor_ShouldThrow_WhenMarshallerIsNull() + { + Assert.Throws(() => new NatsEventBus(null, _options)); + } - - [Fact] - public void Constructor_ShouldThrow_WhenOptionsAreInvalid() - { - Assert.Throws(() => new NatsEventBus(_marshaller, _options)); - } + [Fact] + public void Constructor_ShouldThrow_WhenOptionsIsNull() + { + Assert.Throws(() => new NatsEventBus(_marshaller, null)); + } - [Fact] - public void GetHealthCheckTarget_Should_Return_Connection() - { - var bus = new NatsEventBus(_marshaller, new NatsEventBusOptions - { - Subject = "subject" - }); - Assert.Same(bus.GetHealthCheckTarget(), bus.GetHealthCheckTarget()); - Assert.NotNull(bus.GetHealthCheckTarget()); - } + [Fact] + public void Constructor_ShouldThrow_WhenOptionsAreInvalid() + { + Assert.Throws(() => new NatsEventBus(_marshaller, _options)); + } - [Fact] - public async Task PublishAsync_WithPreCancelledToken_ShouldThrowOperationCanceledException() + [Fact] + public void GetHealthCheckTarget_Should_Return_Connection() + { + var bus = new NatsEventBus(_marshaller, new NatsEventBusOptions { - using var cts = new CancellationTokenSource(); - cts.Cancel(); + Subject = "subject" + }); - var bus = new NatsEventBus(_marshaller, new NatsEventBusOptions { Subject = "subject" }); - var message = new Message( - Guid.NewGuid().ToString("N"), - new Uri("urn:test"), - "test.event", - new TestIntegrationEvent()); + Assert.Same(bus.GetHealthCheckTarget(), bus.GetHealthCheckTarget()); + Assert.NotNull(bus.GetHealthCheckTarget()); + } - var ex = await Record.ExceptionAsync( - () => bus.PublishAsync(message, o => o.CancellationToken = cts.Token)); + [Fact] + public async Task PublishAsync_WithPreCancelledToken_ShouldThrowOperationCanceledException() + { + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await using var bus = new TestableNatsEventBus(_marshaller, new NatsEventBusOptions { Subject = "subject" }); + var message = new Message( + Guid.NewGuid().ToString("N"), + new Uri("urn:test"), + "test.event", + new TestIntegrationEvent()); + + var ex = await Record.ExceptionAsync( + () => bus.PublishAsync(message, o => o.CancellationToken = cts.Token)); + + Assert.NotNull(ex); + Assert.Equal(cts.Token, Assert.IsAssignableFrom(ex).CancellationToken); + Assert.Null(bus.PublishedSubject); + Assert.Null(bus.PublishedMessage); + Assert.Null(bus.PublishedHeaders); + } - Assert.NotNull(ex); - Assert.IsAssignableFrom(ex); - } + [Fact] + public async Task PublishAsync_ShouldPublishSerializedMessage() + { + var bus = new TestableNatsEventBus(_marshaller, new NatsEventBusOptions { Subject = "subject" }); + var message = CreateMessage(); - [Fact] - public async Task PublishAsync_ShouldPublishSerializedMessage() - { - var bus = new TestableNatsEventBus(_marshaller, new NatsEventBusOptions { Subject = "subject" }); - var message = CreateMessage(); + await bus.PublishAsync(message); - await bus.PublishAsync(message); + Assert.Equal("subject", bus.PublishedSubject); + Assert.Equal(message.GetType().ToFullNameIncludingAssemblyName(), bus.PublishedHeaders["type"]); + Assert.NotNull(bus.PublishedMessage); + } - Assert.Equal("subject", bus.PublishedSubject); - Assert.Equal(message.GetType().ToFullNameIncludingAssemblyName(), bus.PublishedHeaders["type"]); - Assert.NotNull(bus.PublishedMessage); - } + [Fact] + public async Task SubscribeAsync_WithPreCancelledToken_ShouldThrowOperationCanceledException() + { + using var cts = new CancellationTokenSource(); + cts.Cancel(); - [Fact] - public async Task SubscribeAsync_WithPreCancelledToken_ShouldThrowOperationCanceledException() - { - using var cts = new CancellationTokenSource(); - cts.Cancel(); + var bus = new NatsEventBus(_marshaller, new NatsEventBusOptions { Subject = "subject" }); - var bus = new NatsEventBus(_marshaller, new NatsEventBusOptions { Subject = "subject" }); + var ex = await Record.ExceptionAsync( + () => bus.SubscribeAsync( + (msg, ct) => Task.CompletedTask, + o => o.CancellationToken = cts.Token)); - var ex = await Record.ExceptionAsync( - () => bus.SubscribeAsync( - (msg, ct) => Task.CompletedTask, - o => o.CancellationToken = cts.Token)); + Assert.NotNull(ex); + Assert.IsAssignableFrom(ex); + } - Assert.NotNull(ex); - Assert.IsAssignableFrom(ex); - } + [Fact] + public async Task SubscribeAsync_ShouldDeserializeMessagesAndInvokeHandler() + { + var message = CreateMessage(); + var bus = new TestableNatsEventBus(_marshaller, new NatsEventBusOptions { Subject = "subject" }, message); + IMessage received = null; - [Fact] - public async Task SubscribeAsync_ShouldDeserializeMessagesAndInvokeHandler() + await bus.SubscribeAsync((msg, _) => { - var message = CreateMessage(); - var bus = new TestableNatsEventBus(_marshaller, new NatsEventBusOptions { Subject = "subject" }, message); - IMessage received = null; + received = msg; + return Task.CompletedTask; + }); - await bus.SubscribeAsync((msg, _) => - { - received = msg; - return Task.CompletedTask; - }); + Assert.NotNull(received); + Assert.Equal(message.Id, received.Id); + Assert.Equal("subject", bus.SubscribedSubject); + } - Assert.NotNull(received); - Assert.Equal(message.Id, received.Id); - Assert.Equal("subject", bus.SubscribedSubject); - } + private static IMessage CreateMessage() + { + return new Message( + Guid.NewGuid().ToString("N"), + new Uri("urn:test"), + "test.event", + new TestIntegrationEvent()); + } - private static IMessage CreateMessage() - { - return new Message( - Guid.NewGuid().ToString("N"), - new Uri("urn:test"), - "test.event", - new TestIntegrationEvent()); - } + private sealed class TestableNatsEventBus : NatsEventBus + { + private readonly IMessage _message; - private sealed class TestableNatsEventBus : NatsEventBus + public TestableNatsEventBus(IMarshaller marshaller, NatsEventBusOptions options, IMessage message = null) : base(marshaller, options) { - private readonly IMessage _message; - - public TestableNatsEventBus(IMarshaller marshaller, NatsEventBusOptions options, IMessage message = null) : base(marshaller, options) - { - _message = message; - } + _message = message; + } - public string PublishedSubject { get; private set; } + public string PublishedSubject { get; private set; } - public string PublishedMessage { get; private set; } + public string PublishedMessage { get; private set; } - public NatsHeaders PublishedHeaders { get; private set; } + public NatsHeaders PublishedHeaders { get; private set; } - public string SubscribedSubject { get; private set; } + public string SubscribedSubject { get; private set; } - protected override Task PublishMessageAsync(string subject, string message, NatsHeaders headers, CancellationToken cancellationToken) - { - PublishedSubject = subject; - PublishedMessage = message; - PublishedHeaders = headers; - return Task.CompletedTask; - } + protected override Task PublishMessageAsync(string subject, string message, NatsHeaders headers, CancellationToken cancellationToken) + { + PublishedSubject = subject; + PublishedMessage = message; + PublishedHeaders = headers; + return Task.CompletedTask; + } - protected override async IAsyncEnumerable SubscribeMessagesAsync(string subject, NatsSubOpts options, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) + protected override async IAsyncEnumerable SubscribeMessagesAsync(string subject, NatsSubOpts options, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) + { + SubscribedSubject = subject; + if (_message != null) { - SubscribedSubject = subject; - if (_message != null) + yield return new ReceivedNatsMessage(new NatsHeaders { - yield return new ReceivedNatsMessage(new NatsHeaders - { - { "type", _message.GetType().ToFullNameIncludingAssemblyName() } - }, JsonMarshaller.Default.Serialize(_message).ToByteArray().ToBase64String()); - } - - await Task.CompletedTask.ConfigureAwait(false); + { "type", _message.GetType().ToFullNameIncludingAssemblyName() } + }, JsonMarshaller.Default.Serialize(_message).ToByteArray().ToBase64String()); } - } - private record TestIntegrationEvent : IntegrationEvent { } + await Task.CompletedTask.ConfigureAwait(false); + } } + + private record TestIntegrationEvent : IntegrationEvent { } } diff --git a/test/Savvyio.Extensions.NATS.Tests/NatsMessageOptionsTest.cs b/test/Savvyio.Extensions.NATS.Tests/NatsMessageOptionsTest.cs index ce9d699b..57d581f7 100644 --- a/test/Savvyio.Extensions.NATS.Tests/NatsMessageOptionsTest.cs +++ b/test/Savvyio.Extensions.NATS.Tests/NatsMessageOptionsTest.cs @@ -1,60 +1,59 @@ - using System; +using System; using Codebelt.Extensions.Xunit; using Cuemon; using Xunit; -namespace Savvyio.Extensions.NATS +namespace Savvyio.Extensions.NATS; + +public class NatsMessageOptionsTest : Test { - public class NatsMessageOptionsTest : Test + public NatsMessageOptionsTest(ITestOutputHelper output) : base(output) { } + + [Fact] + public void Constructor_ShouldSetDefaults() + { + var options = new NatsMessageOptions(); + Assert.NotNull(options.NatsUrl); + Assert.Equal("nats://127.0.0.1:4222", options.NatsUrl.OriginalString); + Assert.Null(options.Subject); + } + + [Fact] + public void ValidateOptions_ShouldThrow_WhenNatsUrlIsNull() + { + var options = new NatsMessageOptions { NatsUrl = null, Subject = "foo" }; + Assert.Throws(() => options.ValidateOptions()); + } + + [Fact] + public void ValidateOptions_ShouldThrow_WhenSubjectIsNullOrWhitespace() + { + var options = new NatsMessageOptions { Subject = null }; + Assert.Throws(() => options.ValidateOptions()); + + options.Subject = ""; + Assert.Throws(() => options.ValidateOptions()); + + options.Subject = " "; + Assert.Throws(() => options.ValidateOptions()); + } + + [Fact] + public void ValidateOptions_ShouldNotThrow_WhenValid() { - public NatsMessageOptionsTest(ITestOutputHelper output) : base(output) { } - - [Fact] - public void Constructor_ShouldSetDefaults() - { - var options = new NatsMessageOptions(); - Assert.NotNull(options.NatsUrl); - Assert.Equal("nats://127.0.0.1:4222", options.NatsUrl.OriginalString); - Assert.Null(options.Subject); - } - - [Fact] - public void ValidateOptions_ShouldThrow_WhenNatsUrlIsNull() - { - var options = new NatsMessageOptions { NatsUrl = null, Subject = "foo" }; - Assert.Throws(() => options.ValidateOptions()); - } - - [Fact] - public void ValidateOptions_ShouldThrow_WhenSubjectIsNullOrWhitespace() - { - var options = new NatsMessageOptions { Subject = null }; - Assert.Throws(() => options.ValidateOptions()); - - options.Subject = ""; - Assert.Throws(() => options.ValidateOptions()); - - options.Subject = " "; - Assert.Throws(() => options.ValidateOptions()); - } - - [Fact] - public void ValidateOptions_ShouldNotThrow_WhenValid() - { - var options = new NatsMessageOptions { Subject = "foo" }; - var ex = Record.Exception(() => options.ValidateOptions()); - Assert.Null(ex); - } - - [Fact] - public void Validator_Should_Wrap_Invalid_Options() - { - var options = new NatsMessageOptions(); - - var exception = Assert.Throws(() => Validator.ThrowIfInvalidOptions(options)); - - Assert.Equal($"{nameof(NatsMessageOptions)} are not in a valid state. (Parameter '{nameof(options)}')", exception.Message); - Assert.IsType(exception.InnerException); - } + var options = new NatsMessageOptions { Subject = "foo" }; + var ex = Record.Exception(() => options.ValidateOptions()); + Assert.Null(ex); + } + + [Fact] + public void Validator_Should_Wrap_Invalid_Options() + { + var options = new NatsMessageOptions(); + + var exception = Assert.Throws(() => Validator.ThrowIfInvalidOptions(options)); + + Assert.Equal($"{nameof(NatsMessageOptions)} are not in a valid state. (Parameter '{nameof(options)}')", exception.Message); + Assert.IsType(exception.InnerException); } } diff --git a/test/Savvyio.Extensions.NATS.Tests/NatsMessageTest.cs b/test/Savvyio.Extensions.NATS.Tests/NatsMessageTest.cs index 1c1b182c..5909271f 100644 --- a/test/Savvyio.Extensions.NATS.Tests/NatsMessageTest.cs +++ b/test/Savvyio.Extensions.NATS.Tests/NatsMessageTest.cs @@ -5,79 +5,78 @@ using Savvyio.Extensions.Text.Json; using Xunit; -namespace Savvyio.Extensions.NATS +namespace Savvyio.Extensions.NATS; + +public class TestNatsMessage : NatsMessage { - public class TestNatsMessage : NatsMessage + public TestNatsMessage(IMarshaller marshaller, NatsMessageOptions options) + : base(marshaller, options) { } + + public Task CallOnDisposeManagedResourcesAsync() { - public TestNatsMessage(IMarshaller marshaller, NatsMessageOptions options) - : base(marshaller, options) { } + return DisposeAsync().AsTask(); + } + + public NatsClient ExposedNatsClient => NatsClient; + public IMarshaller ExposedMarshaller => Marshaller; +} - public Task CallOnDisposeManagedResourcesAsync() - { - return DisposeAsync().AsTask(); - } +public class NatsMessageTest : Test +{ + public NatsMessageTest(ITestOutputHelper output) : base(output) { } + + [Fact] + public void Constructor_ShouldInitializeProperties() + { + var marshaller = JsonMarshaller.Default; + var options = new NatsMessageOptions { Subject = "foo" }; - public NatsClient ExposedNatsClient => NatsClient; - public IMarshaller ExposedMarshaller => Marshaller; + var sut = new TestNatsMessage(marshaller, options); + + Assert.NotNull(sut.ExposedNatsClient); + Assert.Same(marshaller, sut.ExposedMarshaller); + } + + [Fact] + public void Constructor_ShouldThrow_WhenMarshallerIsNull() + { + var options = new NatsMessageOptions { Subject = "foo" }; + Assert.Throws(() => new TestNatsMessage(null, options)); } - public class NatsMessageTest : Test + [Fact] + public void Constructor_ShouldThrow_WhenOptionsIsNull() { - public NatsMessageTest(ITestOutputHelper output) : base(output) { } - - [Fact] - public void Constructor_ShouldInitializeProperties() - { - var marshaller = JsonMarshaller.Default; - var options = new NatsMessageOptions { Subject = "foo" }; - - var sut = new TestNatsMessage(marshaller, options); - - Assert.NotNull(sut.ExposedNatsClient); - Assert.Same(marshaller, sut.ExposedMarshaller); - } - - [Fact] - public void Constructor_ShouldThrow_WhenMarshallerIsNull() - { - var options = new NatsMessageOptions { Subject = "foo" }; - Assert.Throws(() => new TestNatsMessage(null, options)); - } - - [Fact] - public void Constructor_ShouldThrow_WhenOptionsIsNull() - { - Assert.Throws(() => new TestNatsMessage(JsonMarshaller.Default, null)); - } - - [Fact] - public void Constructor_ShouldThrow_WhenOptionsInvalid() - { - Assert.Throws(() => new TestNatsMessage(JsonMarshaller.Default, new NatsMessageOptions())); - } - - [Fact] - public void GetHealthCheckTarget_ShouldReturnNatsConnection() - { - var sut = new TestNatsMessage(JsonMarshaller.Default, new NatsMessageOptions { Subject = "foo" }); - - var connection = sut.GetHealthCheckTarget(); - - Assert.NotNull(connection); - Assert.Same(sut.ExposedNatsClient.Connection, connection); - } - - [Fact] - public async Task OnDisposeManagedResourcesAsync_ShouldDisposeNatsClient() - { - var sut = new TestNatsMessage(JsonMarshaller.Default, new NatsMessageOptions { Subject = "foo" }); - - // NatsClient should not be disposed before - Assert.False(sut.Disposed); - - await sut.CallOnDisposeManagedResourcesAsync(); - - Assert.True(sut.Disposed); - } + Assert.Throws(() => new TestNatsMessage(JsonMarshaller.Default, null)); + } + + [Fact] + public void Constructor_ShouldThrow_WhenOptionsInvalid() + { + Assert.Throws(() => new TestNatsMessage(JsonMarshaller.Default, new NatsMessageOptions())); + } + + [Fact] + public void GetHealthCheckTarget_ShouldReturnNatsConnection() + { + var sut = new TestNatsMessage(JsonMarshaller.Default, new NatsMessageOptions { Subject = "foo" }); + + var connection = sut.GetHealthCheckTarget(); + + Assert.NotNull(connection); + Assert.Same(sut.ExposedNatsClient.Connection, connection); + } + + [Fact] + public async Task OnDisposeManagedResourcesAsync_ShouldDisposeNatsClient() + { + var sut = new TestNatsMessage(JsonMarshaller.Default, new NatsMessageOptions { Subject = "foo" }); + + // NatsClient should not be disposed before + Assert.False(sut.Disposed); + + await sut.CallOnDisposeManagedResourcesAsync(); + + Assert.True(sut.Disposed); } -} \ No newline at end of file +} diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/AggregateRootConverterTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/AggregateRootConverterTest.cs index 056db83d..0ed6eb72 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/AggregateRootConverterTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/AggregateRootConverterTest.cs @@ -5,100 +5,99 @@ using Savvyio.Domain; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.Converters +namespace Savvyio.Extensions.Newtonsoft.Json.Converters; + +public class AggregateRootConverterTest : Test { - public class AggregateRootConverterTest : Test + public AggregateRootConverterTest(ITestOutputHelper output) : base(output) { - public AggregateRootConverterTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AggregateRootConverter_ShouldDeserializeUsingMatchingConstructor() - { - var id = Guid.NewGuid(); - var settings = CreateSettings(); - var json = JsonConvert.SerializeObject(new MatchingAggregate(id, "Jane Doe"), settings); + [Fact] + public void AggregateRootConverter_ShouldDeserializeUsingMatchingConstructor() + { + var id = Guid.NewGuid(); + var settings = CreateSettings(); + var json = JsonConvert.SerializeObject(new MatchingAggregate(id, "Jane Doe"), settings); - var sut = JsonConvert.DeserializeObject(json, settings); + var sut = JsonConvert.DeserializeObject(json, settings); - Assert.NotNull(sut); - Assert.Equal(id, sut.Id); - Assert.Equal("Jane Doe", sut.Name); - } + Assert.NotNull(sut); + Assert.Equal(id, sut.Id); + Assert.Equal("Jane Doe", sut.Name); + } - [Fact] - public void AggregateRootConverter_ShouldDeserializeUsingDefaultConstructorFallback() + [Fact] + public void AggregateRootConverter_ShouldDeserializeUsingDefaultConstructorFallback() + { + var id = Guid.NewGuid(); + var providerId = new PlatformProviderId(Guid.NewGuid()); + var settings = CreateSettings(); + var json = JsonConvert.SerializeObject(new FallbackAggregate(id, "Jane Doe") { - var id = Guid.NewGuid(); - var providerId = new PlatformProviderId(Guid.NewGuid()); - var settings = CreateSettings(); - var json = JsonConvert.SerializeObject(new FallbackAggregate(id, "Jane Doe") - { - PlatformProviderId = providerId - }, settings); - - var sut = JsonConvert.DeserializeObject(json, settings); - - Assert.NotNull(sut); - Assert.Equal(id, sut.Id); - Assert.Equal("Jane Doe", sut.Name); - Assert.Equal(providerId, sut.PlatformProviderId); - } + PlatformProviderId = providerId + }, settings); - [Fact] - public void AggregateRootConverter_ShouldThrowWhenNoSuitableConstructorExists() - { - var settings = CreateSettings(); - var json = JsonConvert.SerializeObject(new UnsupportedAggregate("Jane Doe"), settings); + var sut = JsonConvert.DeserializeObject(json, settings); + + Assert.NotNull(sut); + Assert.Equal(id, sut.Id); + Assert.Equal("Jane Doe", sut.Name); + Assert.Equal(providerId, sut.PlatformProviderId); + } + + [Fact] + public void AggregateRootConverter_ShouldThrowWhenNoSuitableConstructorExists() + { + var settings = CreateSettings(); + var json = JsonConvert.SerializeObject(new UnsupportedAggregate("Jane Doe"), settings); - var ex = Assert.Throws(() => JsonConvert.DeserializeObject(json, settings)); + var ex = Assert.Throws(() => JsonConvert.DeserializeObject(json, settings)); - Assert.StartsWith("Unable to deserialize", ex.Message); - } + Assert.StartsWith("Unable to deserialize", ex.Message); + } + + private static JsonSerializerSettings CreateSettings() + { + var settings = new JsonSerializerSettings(); + settings.Converters.Add(new AggregateRootConverter()); + settings.Converters.Add(new SingleValueObjectConverter()); + return settings; + } - private static JsonSerializerSettings CreateSettings() + private sealed class MatchingAggregate : AggregateRoot + { + public MatchingAggregate(Guid id, string name) : base(id) { - var settings = new JsonSerializerSettings(); - settings.Converters.Add(new AggregateRootConverter()); - settings.Converters.Add(new SingleValueObjectConverter()); - return settings; + Name = name; } - private sealed class MatchingAggregate : AggregateRoot - { - public MatchingAggregate(Guid id, string name) : base(id) - { - Name = name; - } + public string Name { get; } + } - public string Name { get; } + private sealed class FallbackAggregate : AggregateRoot + { + public FallbackAggregate() + { } - private sealed class FallbackAggregate : AggregateRoot + public FallbackAggregate(Guid id, string name) : base(id) { - public FallbackAggregate() - { - } - - public FallbackAggregate(Guid id, string name) : base(id) - { - Name = name; - } + Name = name; + } - public string Name { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; - public PlatformProviderId PlatformProviderId { get; set; } - } + public PlatformProviderId PlatformProviderId { get; set; } + } - private sealed class UnsupportedAggregate : AggregateRoot + private sealed class UnsupportedAggregate : AggregateRoot + { + public UnsupportedAggregate(string name) { - public UnsupportedAggregate(string name) - { - Name = name; - } - - public string Name { get; } + Name = name; } + + public string Name { get; } } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/CommandTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/CommandTest.cs index 3ad76df4..5e9d7ddc 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/CommandTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/CommandTest.cs @@ -1,45 +1,44 @@ -using System; +using System; using Cuemon.Extensions.IO; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.Commands +namespace Savvyio.Extensions.Newtonsoft.Json.Commands; + +public class CommandTest : Test { - public class CommandTest : Test + public CommandTest(ITestOutputHelper output) : base(output) { - public CommandTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void CreateAccount_ShouldUseCustomConverter() - { - var ca = new CreateAccount(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); + [Fact] + public void CreateAccount_ShouldUseCustomConverter() + { + var ca = new CreateAccount(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); - var json = new NewtonsoftJsonMarshaller().Serialize(ca); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(ca); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); + var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); - Assert.Equivalent(ca, rehydrated, true); - } + Assert.Equivalent(ca, rehydrated, true); + } - [Fact] - public void CreateAccountWithValueObjects_ShouldUseCustomConverter() - { - var ca = new CreateAccountWithValueObjects(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); + [Fact] + public void CreateAccountWithValueObjects_ShouldUseCustomConverter() + { + var ca = new CreateAccountWithValueObjects(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); - var json = new NewtonsoftJsonMarshaller().Serialize(ca); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(ca); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); + var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); - Assert.Equivalent(ca, rehydrated, true); - } + Assert.Equivalent(ca, rehydrated, true); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/CommandExtensionsTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/CommandExtensionsTest.cs index 6a08b053..c0c40faf 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/CommandExtensionsTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/CommandExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon.Extensions; using Cuemon.Extensions.IO; using Codebelt.Extensions.Xunit; @@ -7,35 +7,35 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.Commands.Messaging +namespace Savvyio.Extensions.Newtonsoft.Json.Commands.Messaging; + +public class CommandExtensionsTest : Test { - public class CommandExtensionsTest : Test + public CommandExtensionsTest(ITestOutputHelper output) : base(output) { - public CommandExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void EncloseToMessage_ShouldSerialize_WithoutSignature() + [Fact] + public void EncloseToMessage_ShouldSerialize_WithoutSignature() + { + var utcNow = DateTime.UtcNow; + var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => { - var utcNow = DateTime.UtcNow; - var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utcNow; - }); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utcNow; + }); - var json = new NewtonsoftJsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); + var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal($$""" + Assert.Equal($$""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -52,6 +52,5 @@ public void EncloseToMessage_ShouldSerialize_WithoutSignature() } } """.ReplaceLineEndings(), jsonString); - } } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/Cryptography/MessageExtensionsTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/Cryptography/MessageExtensionsTest.cs index b1032c49..9f369668 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/Cryptography/MessageExtensionsTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/Cryptography/MessageExtensionsTest.cs @@ -9,71 +9,70 @@ using Savvyio.Messaging.Cryptography; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.Commands.Messaging.Cryptography +namespace Savvyio.Extensions.Newtonsoft.Json.Commands.Messaging.Cryptography; + +public class MessageExtensionsTest : Test { - public class MessageExtensionsTest : Test + public MessageExtensionsTest(ITestOutputHelper output) : base(output) { - public MessageExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void EncloseToSignedMessage_ShouldSerializeAndDeserialize_WithSignature() + [Fact] + public void EncloseToSignedMessage_ShouldSerializeAndDeserialize_WithSignature() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }); - var sut3 = sut2.Sign(NewtonsoftJsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); + var sut3 = sut2.Sign(NewtonsoftJsonMarshaller.Default, o => + { + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); - var json = NewtonsoftJsonMarshaller.Default.Serialize(sut3); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = NewtonsoftJsonMarshaller.Default.Serialize(sut3); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - sut3.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + sut3.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var signatureException = Assert.Throws(() => - { - sut3.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); - }); - Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); + var signatureException = Assert.Throws(() => + { + sut3.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); + }); + Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); - var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); + var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"CreateMemberCommand","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","age":21,"emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.Commands.CreateMemberCommand, Savvyio.Assets.Tests","correlationId":"3eefdef050c340bfba100bd49c58c181"}},"signature":"0cb0f1b239d9a31fad1dcbf9819e105d841f860c818f389d487d0693ed014c5b"}""", jsonString); + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"CreateMemberCommand","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","age":21,"emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.Commands.CreateMemberCommand, Savvyio.Assets.Tests","correlationId":"3eefdef050c340bfba100bd49c58c181"}},"signature":"0cb0f1b239d9a31fad1dcbf9819e105d841f860c818f389d487d0693ed014c5b"}""", jsonString); - } + } - [Fact] - public void EncloseToSignedMessage_ShouldDeserializeSignedMessageInterface_WithSignature() + [Fact] + public void EncloseToSignedMessage_ShouldDeserializeSignedMessageInterface_WithSignature() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).Sign(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).Sign(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); - var sut3 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); + var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); + var sut3 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); - Assert.IsType>(sut3); - Assert.Equal(sut2.Id, sut3.Id); - Assert.Equal(sut2.Source, sut3.Source); - Assert.Equal(sut2.Type, sut3.Type); - Assert.Equal(sut2.Signature, sut3.Signature); - Assert.Equivalent(sut2.Data, sut3.Data, true); - } + Assert.IsType>(sut3); + Assert.Equal(sut2.Id, sut3.Id); + Assert.Equal(sut2.Source, sut3.Source); + Assert.Equal(sut2.Type, sut3.Type); + Assert.Equal(sut2.Signature, sut3.Signature); + Assert.Equivalent(sut2.Data, sut3.Data, true); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/InMemoryCommandQueueTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/InMemoryCommandQueueTest.cs index dbdd96e8..1b03957f 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/InMemoryCommandQueueTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Commands/Messaging/InMemoryCommandQueueTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using Cuemon; @@ -14,75 +14,74 @@ using Xunit; using Xunit.v3.Priority; -namespace Savvyio.Extensions.Newtonsoft.Json.Commands.Messaging +namespace Savvyio.Extensions.Newtonsoft.Json.Commands.Messaging; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class InMemoryCommandQueueTest : Test { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class InMemoryCommandQueueTest : Test - { - private static readonly InMemoryCommandQueue Queue = new(); - private static readonly InMemoryTestStore> Comparer = new(); + private static readonly InMemoryCommandQueue Queue = new(); + private static readonly InMemoryTestStore> Comparer = new(); - public InMemoryCommandQueueTest(ITestOutputHelper output) : base(output) - { - } + public InMemoryCommandQueueTest(ITestOutputHelper output) : base(output) + { + } - [Fact, Priority(0)] - public async Task SendAsync_CreateMemberCommand_OneTime() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); + [Fact, Priority(0)] + public async Task SendAsync_CreateMemberCommand_OneTime() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = Environment.NewLine)); - TestOutput.WriteLine(new NewtonsoftJsonMarshaller().Serialize(sut2).ToEncodedString()); + TestOutput.WriteLine(new NewtonsoftJsonMarshaller().Serialize(sut2).ToEncodedString()); - Comparer.Add(sut3); + Comparer.Add(sut3); - await Queue.SendAsync(sut3.Yield()); - } + await Queue.SendAsync(sut3.Yield()); + } - [Fact, Priority(1)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime() - { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); - var sut2 = await Queue.ReceiveAsync().SingleOrDefaultAsync(); + [Fact, Priority(1)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); + var sut2 = await Queue.ReceiveAsync().SingleOrDefaultAsync(); - Assert.Equal(sut1.Data, sut2.Data); - Assert.Equal(sut1.Time, sut2.Time); - Assert.Equal(sut1.Source, sut2.Source); - Assert.Equal(sut1.Id, sut2.Id); - Assert.Equal(sut1.Type, sut2.Type); - } + Assert.Equal(sut1.Data, sut2.Data); + Assert.Equal(sut1.Time, sut2.Time); + Assert.Equal(sut1.Source, sut2.Source); + Assert.Equal(sut1.Id, sut2.Id); + Assert.Equal(sut1.Type, sut2.Type); + } - [Fact, Priority(2)] - public async Task SendAsync_CreateMemberCommand_ThousandTimes() + [Fact, Priority(2)] + public async Task SendAsync_CreateMemberCommand_ThousandTimes() + { + var messages = Generate.RangeOf(1000, i => { - var messages = Generate.RangeOf(1000, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - }); + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + }); - await ParallelFactory.ForEachAsync(messages, (message, token) => - { - Comparer.Add(message); - return Queue.SendAsync(message.Yield(), o => o.CancellationToken = token); - }); - } - - [Fact, Priority(3)] - public async Task ReceiveAsync_CreateMemberCommand_All() + await ParallelFactory.ForEachAsync(messages, (message, token) => { - var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); - var sut2 = await Queue.ReceiveAsync().ToListAsync(); + Comparer.Add(message); + return Queue.SendAsync(message.Yield(), o => o.CancellationToken = token); + }); + } + + [Fact, Priority(3)] + public async Task ReceiveAsync_CreateMemberCommand_All() + { + var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); + var sut2 = await Queue.ReceiveAsync().ToListAsync(); - TestOutput.WriteLines(sut2.Take(10)); + TestOutput.WriteLines(sut2.Take(10)); - Assert.Equal(sut1.Count, sut2.Count); - Assert.Equal(sut1, sut2); - Assert.Equal(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); - Assert.Equal(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); - } + Assert.Equal(sut1.Count, sut2.Count); + Assert.Equal(sut1, sut2); + Assert.Equal(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); + Assert.Equal(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Converters/ValueObjectConverterTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Converters/ValueObjectConverterTest.cs index 0f29af40..5518e8b0 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Converters/ValueObjectConverterTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Converters/ValueObjectConverterTest.cs @@ -4,162 +4,161 @@ using Savvyio.Domain; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.Converters +namespace Savvyio.Extensions.Newtonsoft.Json.Converters; + +public class ValueObjectConverterTest : Test { - public class ValueObjectConverterTest : Test + public ValueObjectConverterTest(ITestOutputHelper output) : base(output) { - public ValueObjectConverterTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void WriteJson_ShouldSerializeValueObjectWithSimpleProperties() - { - var sut = new SimplePoint(3, 7); - var settings = CreateSettings(); + [Fact] + public void WriteJson_ShouldSerializeValueObjectWithSimpleProperties() + { + var sut = new SimplePoint(3, 7); + var settings = CreateSettings(); - var json = JsonConvert.SerializeObject(sut, settings); + var json = JsonConvert.SerializeObject(sut, settings); - TestOutput.WriteLine(json); + TestOutput.WriteLine(json); - Assert.Contains("X", json); - Assert.Contains("Y", json); - Assert.Contains("3", json); - Assert.Contains("7", json); - } + Assert.Contains("X", json); + Assert.Contains("Y", json); + Assert.Contains("3", json); + Assert.Contains("7", json); + } - [Fact] - public void WriteJson_ShouldSerializeSingleValueObjectProperty() - { - var sut = new LabeledPoint("origin", new PointLabel("O")); - var settings = CreateSettings(); + [Fact] + public void WriteJson_ShouldSerializeSingleValueObjectProperty() + { + var sut = new LabeledPoint("origin", new PointLabel("O")); + var settings = CreateSettings(); - var json = JsonConvert.SerializeObject(sut, settings); + var json = JsonConvert.SerializeObject(sut, settings); - TestOutput.WriteLine(json); + TestOutput.WriteLine(json); - Assert.Contains("Name", json); - Assert.Contains("Label", json); - Assert.Contains("origin", json); - Assert.Contains("O", json); - } + Assert.Contains("Name", json); + Assert.Contains("Label", json); + Assert.Contains("origin", json); + Assert.Contains("O", json); + } - [Fact] - public void ReadJson_ShouldDeserializeSingleValueObjectScalarWhenNested() - { - var original = new LabeledPoint("center", new PointLabel("C")); - var settings = CreateSettings(); + [Fact] + public void ReadJson_ShouldDeserializeSingleValueObjectScalarWhenNested() + { + var original = new LabeledPoint("center", new PointLabel("C")); + var settings = CreateSettings(); - var json = JsonConvert.SerializeObject(original, settings); - TestOutput.WriteLine(json); + var json = JsonConvert.SerializeObject(original, settings); + TestOutput.WriteLine(json); - var result = JsonConvert.DeserializeObject(json, settings); + var result = JsonConvert.DeserializeObject(json, settings); - Assert.NotNull(result); - Assert.Equal("center", result.Name); - Assert.NotNull(result.Label); - Assert.Equal("C", result.Label.Value); - } + Assert.NotNull(result); + Assert.Equal("center", result.Name); + Assert.NotNull(result.Label); + Assert.Equal("C", result.Label.Value); + } - [Fact] - public void ReadJson_ShouldDeserializeComplexValueObjectWithMatchingCtor() - { - var original = new SimplePoint(1, 2); - var settings = CreateSettings(); + [Fact] + public void ReadJson_ShouldDeserializeComplexValueObjectWithMatchingCtor() + { + var original = new SimplePoint(1, 2); + var settings = CreateSettings(); - var json = JsonConvert.SerializeObject(original, settings); - TestOutput.WriteLine(json); + var json = JsonConvert.SerializeObject(original, settings); + TestOutput.WriteLine(json); - var result = JsonConvert.DeserializeObject(json, settings); + var result = JsonConvert.DeserializeObject(json, settings); - Assert.NotNull(result); - Assert.Equal(1, result.X); - Assert.Equal(2, result.Y); - } + Assert.NotNull(result); + Assert.Equal(1, result.X); + Assert.Equal(2, result.Y); + } - [Fact] - public void ReadJson_ShouldDeserializeValueObjectWithDefaultCtor() - { - var original = new MutablePoint { X = 5, Y = 6 }; - var settings = CreateSettings(); + [Fact] + public void ReadJson_ShouldDeserializeValueObjectWithDefaultCtor() + { + var original = new MutablePoint { X = 5, Y = 6 }; + var settings = CreateSettings(); - var json = JsonConvert.SerializeObject(original, settings); - TestOutput.WriteLine(json); + var json = JsonConvert.SerializeObject(original, settings); + TestOutput.WriteLine(json); - var result = JsonConvert.DeserializeObject(json, settings); + var result = JsonConvert.DeserializeObject(json, settings); - Assert.NotNull(result); - Assert.Equal(5, result.X); - Assert.Equal(6, result.Y); - } + Assert.NotNull(result); + Assert.Equal(5, result.X); + Assert.Equal(6, result.Y); + } - [Fact] - public void ReadJson_ShouldThrowInvalidOperationException_WhenNoSuitableCtorExists() - { - var settings = CreateSettings(); - var json = JsonConvert.SerializeObject(new NoSuitableCtorPoint("a", "b"), settings); - TestOutput.WriteLine(json); + [Fact] + public void ReadJson_ShouldThrowInvalidOperationException_WhenNoSuitableCtorExists() + { + var settings = CreateSettings(); + var json = JsonConvert.SerializeObject(new NoSuitableCtorPoint("a", "b"), settings); + TestOutput.WriteLine(json); - var ex = Assert.Throws(() => - JsonConvert.DeserializeObject(json, settings)); + var ex = Assert.Throws(() => + JsonConvert.DeserializeObject(json, settings)); - Assert.Contains("Unable to deserialize", ex.Message); - } + Assert.Contains("Unable to deserialize", ex.Message); + } - private static JsonSerializerSettings CreateSettings() - { - var settings = new JsonSerializerSettings(); - settings.Converters.Add(new ValueObjectConverter()); - return settings; - } + private static JsonSerializerSettings CreateSettings() + { + var settings = new JsonSerializerSettings(); + settings.Converters.Add(new ValueObjectConverter()); + return settings; + } - private record SimplePoint : ValueObject + private record SimplePoint : ValueObject + { + public SimplePoint(int x, int y) { - public SimplePoint(int x, int y) - { - X = x; - Y = y; - } - - public int X { get; } - public int Y { get; } + X = x; + Y = y; } - private sealed record PointLabel : SingleValueObject - { - public PointLabel(string value) : base(value) { } - } + public int X { get; } + public int Y { get; } + } - private record LabeledPoint : ValueObject + private sealed record PointLabel : SingleValueObject + { + public PointLabel(string value) : base(value) { } + } + + private record LabeledPoint : ValueObject + { + public LabeledPoint(string name, PointLabel label) { - public LabeledPoint(string name, PointLabel label) - { - Name = name; - Label = label; - } - - public string Name { get; } - public PointLabel Label { get; } + Name = name; + Label = label; } - private record MutablePoint : ValueObject - { - public MutablePoint() { } + public string Name { get; } + public PointLabel Label { get; } + } - public int X { get; set; } - public int Y { get; set; } - } + private record MutablePoint : ValueObject + { + public MutablePoint() { } + + public int X { get; set; } + public int Y { get; set; } + } - private record NoSuitableCtorPoint : ValueObject + private record NoSuitableCtorPoint : ValueObject + { + public NoSuitableCtorPoint(string a, string b, string c = null) { - public NoSuitableCtorPoint(string a, string b, string c = null) - { - A = a; - B = b; - } - - public string A { get; } - public string B { get; } + A = a; + B = b; } + + public string A { get; } + public string B { get; } } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Domain/DomainTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Domain/DomainTest.cs index 4d485bd7..47d91e3d 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Domain/DomainTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/Domain/DomainTest.cs @@ -1,64 +1,63 @@ -using System; +using System; using Cuemon.Extensions.IO; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Domain; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.Domain +namespace Savvyio.Extensions.Newtonsoft.Json.Domain; + +public class DomainTest : Test { - public class DomainTest : Test + public DomainTest(ITestOutputHelper output) : base(output) { - public DomainTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Coordinates_ShouldBeAbleToBothSerializeAndDeserialize() - { - var sut = new Coordinates(10.1, 10.2); + [Fact] + public void Coordinates_ShouldBeAbleToBothSerializeAndDeserialize() + { + var sut = new Coordinates(10.1, 10.2); - var json = new NewtonsoftJsonMarshaller().Serialize(sut); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(sut); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); + var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); - Assert.Equivalent(sut, rehydrated, true); - } + Assert.Equivalent(sut, rehydrated, true); + } - [Fact] - public void Timestamp_ShouldBeAbleToBothSerializeAndDeserialize() + [Fact] + public void Timestamp_ShouldBeAbleToBothSerializeAndDeserialize() + { + var sut = new Timestamp(DateTimeOffset.UtcNow) { - var sut = new Timestamp(DateTimeOffset.UtcNow) - { - IsDaylightSavingTime = false, - TimeZone = "Europe/Berlin" - }; + IsDaylightSavingTime = false, + TimeZone = "Europe/Berlin" + }; - var json = new NewtonsoftJsonMarshaller().Serialize(sut); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(sut); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); + var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); - Assert.Equivalent(sut, rehydrated, true); - } + Assert.Equivalent(sut, rehydrated, true); + } - [Fact] - public void PlatformProviderId_ShouldBeAbleToBothSerializeAndDeserialize() - { - var sut = new PlatformProviderId(Guid.NewGuid()); + [Fact] + public void PlatformProviderId_ShouldBeAbleToBothSerializeAndDeserialize() + { + var sut = new PlatformProviderId(Guid.NewGuid()); - var json = new NewtonsoftJsonMarshaller().Serialize(sut); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(sut); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); + var rehydrated = new NewtonsoftJsonMarshaller().Deserialize(json); - Assert.Equivalent(sut, rehydrated, true); - } + Assert.Equivalent(sut, rehydrated, true); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/CloudEvents/Cryptography/IntegrationEventExtensionsTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/CloudEvents/Cryptography/IntegrationEventExtensionsTest.cs index fee98665..097865f7 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/CloudEvents/Cryptography/IntegrationEventExtensionsTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/CloudEvents/Cryptography/IntegrationEventExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using Cuemon.Extensions; using Cuemon.Extensions.IO; @@ -9,80 +9,79 @@ using Savvyio.EventDriven.Messaging.CloudEvents.Cryptography; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.EventDriven.Messaging.CloudEvents.Cryptography +namespace Savvyio.Extensions.Newtonsoft.Json.EventDriven.Messaging.CloudEvents.Cryptography; + +public class IntegrationEventExtensionsTest : Test { - public class IntegrationEventExtensionsTest : Test + public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ToMessage_ToCloudEvent_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() { - public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => + { + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).ToCloudEvent() + .SignCloudEvent(NewtonsoftJsonMarshaller.Default, o => { - } + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); + + var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - [Fact] - public void ToMessage_ToCloudEvent_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + TestOutput.WriteLine(jsonString); + + sut2.CheckCloudEventSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + + var signatureException = Assert.Throws(() => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).ToCloudEvent() - .SignCloudEvent(NewtonsoftJsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); - - var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - - TestOutput.WriteLine(jsonString); - - sut2.CheckCloudEventSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - - var signatureException = Assert.Throws(() => - { - sut2.CheckCloudEventSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); - }); - Assert.StartsWith("The signature of the cloud event does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); - - var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); - - Assert.Equivalent(sut2, sut4, true); - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"specversion":"1.0","signature":"049ca36b460e2ac4ee002c37e4c34b7963d9db1b14e1ff150eb8acbd7d7b8f6f"}""", jsonString); - } - - [Fact] - public void ToMessage_ToCloudEvent_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + sut2.CheckCloudEventSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); + }); + Assert.StartsWith("The signature of the cloud event does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); + + var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); + + Assert.Equivalent(sut2, sut4, true); + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"specversion":"1.0","signature":"049ca36b460e2ac4ee002c37e4c34b7963d9db1b14e1ff150eb8acbd7d7b8f6f"}""", jsonString); + } + + [Fact] + public void ToMessage_ToCloudEvent_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => + { + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).ToCloudEvent() + .SignCloudEvent(NewtonsoftJsonMarshaller.Default, o => + { + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); + + var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + + TestOutput.WriteLine(jsonString); + + sut2.CheckCloudEventSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + + var signatureException = Assert.Throws(() => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).ToCloudEvent() - .SignCloudEvent(NewtonsoftJsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); - - var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - - TestOutput.WriteLine(jsonString); - - sut2.CheckCloudEventSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - - var signatureException = Assert.Throws(() => - { - sut2.CheckCloudEventSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); - }); - Assert.StartsWith("The signature of the cloud event does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); - - var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); - - Assert.Equivalent(sut2, sut4, true); - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"specversion":"1.0","signature":"049ca36b460e2ac4ee002c37e4c34b7963d9db1b14e1ff150eb8acbd7d7b8f6f"}""", jsonString); - } + sut2.CheckCloudEventSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); + }); + Assert.StartsWith("The signature of the cloud event does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); + + var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); + + Assert.Equivalent(sut2, sut4, true); + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"specversion":"1.0","signature":"049ca36b460e2ac4ee002c37e4c34b7963d9db1b14e1ff150eb8acbd7d7b8f6f"}""", jsonString); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/CloudEvents/IntegrationEventExtensionsTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/CloudEvents/IntegrationEventExtensionsTest.cs index 3872f12b..55abf231 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/CloudEvents/IntegrationEventExtensionsTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/CloudEvents/IntegrationEventExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using Cuemon.Extensions; using Cuemon.Extensions.IO; @@ -8,35 +8,35 @@ using Savvyio.EventDriven.Messaging.CloudEvents; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.EventDriven.Messaging.CloudEvents +namespace Savvyio.Extensions.Newtonsoft.Json.EventDriven.Messaging.CloudEvents; + +public class IntegrationEventExtensionsTest : Test { - public class IntegrationEventExtensionsTest : Test + public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) { - public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + [Fact] + public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).ToCloudEvent(); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).ToCloudEvent(); - var json = new NewtonsoftJsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); + var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal(""" + Assert.Equal(""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -54,29 +54,29 @@ public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_U "specversion": "1.0" } """.ReplaceLineEndings(), jsonString); - } + } - [Fact] - public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + [Fact] + public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + { + var utcNow = DateTime.UtcNow; + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utcNow); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utcNow = DateTime.UtcNow; - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utcNow); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utcNow; - }).ToCloudEvent(); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utcNow; + }).ToCloudEvent(); - var json = new NewtonsoftJsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); + var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal($$""" + Assert.Equal($$""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -94,27 +94,26 @@ public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_U "specversion": "1.0" } """.ReplaceLineEndings(), jsonString); - } + } - [Fact] - public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_ExtensionAttributes_UsingInterface() + [Fact] + public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_ExtensionAttributes_UsingInterface() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).ToCloudEvent(); - sut2["tenant"] = "acme"; - sut2["sequence"] = 42; - - var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); - var sut3 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); - - Assert.Equal("acme", sut3["tenant"]); - Assert.Equal(42L, sut3["sequence"]); - Assert.Equivalent(sut2.Data, sut3.Data, true); - } + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).ToCloudEvent(); + sut2["tenant"] = "acme"; + sut2["sequence"] = 42; + + var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); + var sut3 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); + + Assert.Equal("acme", sut3["tenant"]); + Assert.Equal(42L, sut3["sequence"]); + Assert.Equivalent(sut2.Data, sut3.Data, true); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/Cryptography/IntegrationEventExtensionsTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/Cryptography/IntegrationEventExtensionsTest.cs index 908ad419..87997971 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/Cryptography/IntegrationEventExtensionsTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/Cryptography/IntegrationEventExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using Cuemon.Extensions; using Cuemon.Extensions.IO; @@ -8,76 +8,75 @@ using Savvyio.Messaging.Cryptography; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.EventDriven.Messaging.Cryptography +namespace Savvyio.Extensions.Newtonsoft.Json.EventDriven.Messaging.Cryptography; + +public class IntegrationEventExtensionsTest : Test { - public class IntegrationEventExtensionsTest : Test + public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) { - public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ToMessage_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + [Fact] + public void ToMessage_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => + { + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).Sign(NewtonsoftJsonMarshaller.Default, o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).Sign(NewtonsoftJsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); - var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); + var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - sut2.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + sut2.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var signatureException = Assert.Throws(() => - { - sut2.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); - }); - Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); + var signatureException = Assert.Throws(() => + { + sut2.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); + }); + Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); - var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); + var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"signature":"cf6710e4fc2877c87630fe94956a899240bfc7fdb1d6a0bb12446bd77890c448"}""", jsonString); - } + Assert.Equivalent(sut2, sut4, true); + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"signature":"cf6710e4fc2877c87630fe94956a899240bfc7fdb1d6a0bb12446bd77890c448"}""", jsonString); + } - [Fact] - public void ToMessage_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + [Fact] + public void ToMessage_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).Sign(NewtonsoftJsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); - var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).Sign(NewtonsoftJsonMarshaller.Default, o => + { + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); + var json = NewtonsoftJsonMarshaller.Default.Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - sut2.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + sut2.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var signatureException = Assert.Throws(() => - { - sut2.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); - }); - Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); + var signatureException = Assert.Throws(() => + { + sut2.CheckSignature(NewtonsoftJsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); + }); + Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); - var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); + var sut4 = NewtonsoftJsonMarshaller.Default.Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"signature":"cf6710e4fc2877c87630fe94956a899240bfc7fdb1d6a0bb12446bd77890c448"}""", jsonString); - } + Assert.Equivalent(sut2, sut4, true); + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"signature":"cf6710e4fc2877c87630fe94956a899240bfc7fdb1d6a0bb12446bd77890c448"}""", jsonString); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/IntegrationEventExtensionsTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/IntegrationEventExtensionsTest.cs index 80a45f27..4b9f3b27 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/IntegrationEventExtensionsTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/EventDriven/Messaging/IntegrationEventExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using Cuemon.Extensions; using Cuemon.Extensions.IO; @@ -8,35 +8,35 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.EventDriven.Messaging +namespace Savvyio.Extensions.Newtonsoft.Json.EventDriven.Messaging; + +public class IntegrationEventExtensionsTest : Test { - public class IntegrationEventExtensionsTest : Test + public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) { - public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + [Fact] + public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }); - var json = new NewtonsoftJsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); + var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal(""" + Assert.Equal(""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -53,29 +53,29 @@ public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface } } """.ReplaceLineEndings(), jsonString); - } + } - [Fact] - public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + [Fact] + public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + { + var utcNow = DateTime.UtcNow; + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utcNow); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utcNow = DateTime.UtcNow; - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utcNow); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utcNow; - }); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utcNow; + }); - var json = new NewtonsoftJsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new NewtonsoftJsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); + var sut4 = new NewtonsoftJsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal($$""" + Assert.Equal($$""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -92,6 +92,5 @@ public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteT } } """.ReplaceLineEndings(), jsonString); - } } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/JsonConverterExtensionsTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/JsonConverterExtensionsTest.cs index 548aa60a..76998207 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/JsonConverterExtensionsTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/JsonConverterExtensionsTest.cs @@ -5,46 +5,45 @@ using Savvyio.Extensions.Newtonsoft.Json.Converters; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json +namespace Savvyio.Extensions.Newtonsoft.Json; + +public class JsonConverterExtensionsTest : Test { - public class JsonConverterExtensionsTest : Test + public JsonConverterExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void JsonConverterExtensions_ShouldAddExpectedConverters() { - public JsonConverterExtensionsTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void JsonConverterExtensions_ShouldAddExpectedConverters() - { - ICollection sut = new List(); - - var chained = sut.AddValueObjectConverter() - .AddAggregateRootConverter() - .AddMetadataDictionaryConverter() - .AddRequestConverter() - .AddMessageConverter() - .AddSingleValueObjectConverter(); - - Assert.Same(sut, chained); - Assert.Contains(sut, c => c is ValueObjectConverter); - Assert.Contains(sut, c => c is AggregateRootConverter); - Assert.Contains(sut, c => c is RequestConverter); - Assert.Contains(sut, c => c is MessageConverter); - Assert.Contains(sut, c => c is SingleValueObjectConverter); - Assert.Equal(6, sut.Count); - } - - [Fact] - public void AddMetadataDictionaryConverter_ShouldDeserializeMetadataDictionary() - { - var settings = new JsonSerializerSettings(); - settings.Converters.AddMetadataDictionaryConverter(); - - var sut = JsonConvert.DeserializeObject("{\"memberType\":\"custom\",\"attempts\":42}", settings); - - Assert.IsType(sut); - Assert.Equal("custom", sut["memberType"]); - Assert.Equal(42L, sut["attempts"]); - } + ICollection sut = new List(); + + var chained = sut.AddValueObjectConverter() + .AddAggregateRootConverter() + .AddMetadataDictionaryConverter() + .AddRequestConverter() + .AddMessageConverter() + .AddSingleValueObjectConverter(); + + Assert.Same(sut, chained); + Assert.Contains(sut, c => c is ValueObjectConverter); + Assert.Contains(sut, c => c is AggregateRootConverter); + Assert.Contains(sut, c => c is RequestConverter); + Assert.Contains(sut, c => c is MessageConverter); + Assert.Contains(sut, c => c is SingleValueObjectConverter); + Assert.Equal(6, sut.Count); + } + + [Fact] + public void AddMetadataDictionaryConverter_ShouldDeserializeMetadataDictionary() + { + var settings = new JsonSerializerSettings(); + settings.Converters.AddMetadataDictionaryConverter(); + + var sut = JsonConvert.DeserializeObject("{\"memberType\":\"custom\",\"attempts\":42}", settings); + + Assert.IsType(sut); + Assert.Equal("custom", sut["memberType"]); + Assert.Equal(42L, sut["attempts"]); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/JsonSerializerExtensionsTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/JsonSerializerExtensionsTest.cs index e6d849e1..7dcdbfcd 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/JsonSerializerExtensionsTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/JsonSerializerExtensionsTest.cs @@ -3,36 +3,35 @@ using Newtonsoft.Json.Serialization; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json +namespace Savvyio.Extensions.Newtonsoft.Json; + +public class JsonSerializerExtensionsTest : Test { - public class JsonSerializerExtensionsTest : Test + public JsonSerializerExtensionsTest(ITestOutputHelper output) : base(output) { - public JsonSerializerExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void JsonSerializerExtensions_ShouldResolveKeysWithDefaultNamingStrategy() - { - var sut = JsonSerializer.CreateDefault(); + [Fact] + public void JsonSerializerExtensions_ShouldResolveKeysWithDefaultNamingStrategy() + { + var sut = JsonSerializer.CreateDefault(); - Assert.Equal("MemberType", sut.ResolvePropertyKeyByConvention("MemberType")); - Assert.Equal("MemberType", sut.ResolveDictionaryKeyByConvention("MemberType")); - } + Assert.Equal("MemberType", sut.ResolvePropertyKeyByConvention("MemberType")); + Assert.Equal("MemberType", sut.ResolveDictionaryKeyByConvention("MemberType")); + } - [Fact] - public void JsonSerializerExtensions_ShouldResolveKeysWithCamelCaseNamingStrategy() + [Fact] + public void JsonSerializerExtensions_ShouldResolveKeysWithCamelCaseNamingStrategy() + { + var sut = JsonSerializer.CreateDefault(new JsonSerializerSettings { - var sut = JsonSerializer.CreateDefault(new JsonSerializerSettings + ContractResolver = new DefaultContractResolver { - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy() - } - }); + NamingStrategy = new CamelCaseNamingStrategy() + } + }); - Assert.Equal("memberType", sut.ResolvePropertyKeyByConvention("MemberType")); - Assert.Equal("MemberType", sut.ResolveDictionaryKeyByConvention("MemberType")); - } + Assert.Equal("memberType", sut.ResolvePropertyKeyByConvention("MemberType")); + Assert.Equal("MemberType", sut.ResolveDictionaryKeyByConvention("MemberType")); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/MetadataDictionaryTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/MetadataDictionaryTest.cs index 4aa2fd54..af4b3865 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/MetadataDictionaryTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/MetadataDictionaryTest.cs @@ -1,40 +1,39 @@ -using System; +using System; using Cuemon.Extensions.IO; using Codebelt.Extensions.Newtonsoft.Json.Formatters; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json +namespace Savvyio.Extensions.Newtonsoft.Json; + +public class MetadataDictionaryTest : Test { - public class MetadataDictionaryTest : Test + public MetadataDictionaryTest(ITestOutputHelper output) : base(output) { - public MetadataDictionaryTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void MetadataDictionary_ShouldUseCustomConverter() + [Fact] + public void MetadataDictionary_ShouldUseCustomConverter() + { + var md = new MetadataDictionary() { - var md = new MetadataDictionary() - { - { "guid", $"{Guid.NewGuid()}" }, - { "int32", 2312313 }, - { "dateTime", DateTime.UtcNow } - }; + { "guid", $"{Guid.NewGuid()}" }, + { "int32", 2312313 }, + { "dateTime", DateTime.UtcNow } + }; - var json = NewtonsoftJsonFormatter.SerializeObject(md); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = NewtonsoftJsonFormatter.SerializeObject(md); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var mdRehydrated = NewtonsoftJsonFormatter.DeserializeObject(json, o => - { - o.Settings.Converters - .AddMessageConverter() - .AddMetadataDictionaryConverter(); - }); + var mdRehydrated = NewtonsoftJsonFormatter.DeserializeObject(json, o => + { + o.Settings.Converters + .AddMessageConverter() + .AddMetadataDictionaryConverter(); + }); - Assert.Equivalent(md, mdRehydrated, true); - } + Assert.Equivalent(md, mdRehydrated, true); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/NewtonsoftJsonMarshallerTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/NewtonsoftJsonMarshallerTest.cs index cd96cf9e..345cf6ed 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/NewtonsoftJsonMarshallerTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/NewtonsoftJsonMarshallerTest.cs @@ -5,46 +5,45 @@ using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json +namespace Savvyio.Extensions.Newtonsoft.Json; + +public class NewtonsoftJsonMarshallerTest : Test { - public class NewtonsoftJsonMarshallerTest : Test + public NewtonsoftJsonMarshallerTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void NewtonsoftJsonMarshaller_Default_ShouldUseCompactFormatting() + { + using var stream = NewtonsoftJsonMarshaller.Default.Serialize(new CreateMemberCommand("Jane Doe", 21, "jd@office.com")); + using var reader = new StreamReader(stream); + var json = reader.ReadToEnd(); + + Assert.DoesNotContain(Environment.NewLine, json); + } + + [Fact] + public void NewtonsoftJsonMarshaller_Create_ShouldHonorConfiguredFormatting() { - public NewtonsoftJsonMarshallerTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void NewtonsoftJsonMarshaller_Default_ShouldUseCompactFormatting() - { - using var stream = NewtonsoftJsonMarshaller.Default.Serialize(new CreateMemberCommand("Jane Doe", 21, "jd@office.com")); - using var reader = new StreamReader(stream); - var json = reader.ReadToEnd(); - - Assert.DoesNotContain(Environment.NewLine, json); - } - - [Fact] - public void NewtonsoftJsonMarshaller_Create_ShouldHonorConfiguredFormatting() - { - var sut = NewtonsoftJsonMarshaller.Create(o => o.Settings.Formatting = Formatting.Indented); - - using var stream = sut.Serialize(new CreateMemberCommand("Jane Doe", 21, "jd@office.com")); - using var reader = new StreamReader(stream); - var json = reader.ReadToEnd(); - - Assert.Contains(Environment.NewLine, json); - } - - [Fact] - public void NewtonsoftJsonMarshaller_ShouldRoundtripUsingTypeBasedMethods() - { - var marshaller = new NewtonsoftJsonMarshaller(); - using var stream = marshaller.Serialize(new CreateMemberCommand("Jane Doe", 21, "jd@office.com"), typeof(CreateMemberCommand)); - - var sut = marshaller.Deserialize(stream, typeof(CreateMemberCommand)); - - Assert.IsType(sut); - Assert.Equal("Jane Doe", ((CreateMemberCommand)sut).Name); - } + var sut = NewtonsoftJsonMarshaller.Create(o => o.Settings.Formatting = Formatting.Indented); + + using var stream = sut.Serialize(new CreateMemberCommand("Jane Doe", 21, "jd@office.com")); + using var reader = new StreamReader(stream); + var json = reader.ReadToEnd(); + + Assert.Contains(Environment.NewLine, json); + } + + [Fact] + public void NewtonsoftJsonMarshaller_ShouldRoundtripUsingTypeBasedMethods() + { + var marshaller = new NewtonsoftJsonMarshaller(); + using var stream = marshaller.Serialize(new CreateMemberCommand("Jane Doe", 21, "jd@office.com"), typeof(CreateMemberCommand)); + + var sut = marshaller.Deserialize(stream, typeof(CreateMemberCommand)); + + Assert.IsType(sut); + Assert.Equal("Jane Doe", ((CreateMemberCommand)sut).Name); } } diff --git a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/RequestConverterTest.cs b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/RequestConverterTest.cs index 0eeecf6c..7f9455d4 100644 --- a/test/Savvyio.Extensions.Newtonsoft.Json.Tests/RequestConverterTest.cs +++ b/test/Savvyio.Extensions.Newtonsoft.Json.Tests/RequestConverterTest.cs @@ -6,80 +6,79 @@ using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Extensions.Newtonsoft.Json.Converters +namespace Savvyio.Extensions.Newtonsoft.Json.Converters; + +public class RequestConverterTest : Test { - public class RequestConverterTest : Test + public RequestConverterTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void RequestConverter_ShouldExposeReadOnlyCapabilities() + { + var sut = new RequestConverter(); + + Assert.False(sut.CanWrite); + Assert.True(sut.CanConvert(typeof(CreateMemberCommand))); + Assert.False(sut.CanConvert(typeof(string))); + } + + [Fact] + public void RequestConverter_ShouldThrowWhenWritingJson() { - public RequestConverterTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void RequestConverter_ShouldExposeReadOnlyCapabilities() - { - var sut = new RequestConverter(); - - Assert.False(sut.CanWrite); - Assert.True(sut.CanConvert(typeof(CreateMemberCommand))); - Assert.False(sut.CanConvert(typeof(string))); - } - - [Fact] - public void RequestConverter_ShouldThrowWhenWritingJson() - { - var sut = new RequestConverter(); - using var writer = new JsonTextWriter(new StringWriter()); - - Assert.Throws(() => sut.WriteJson(writer, new CreateMemberCommand("Jane Doe", 21, "jd@office.com"), JsonSerializer.CreateDefault())); - } - - [Fact] - public void RequestConverter_ShouldRehydrateAutoPropertyRequests() - { - var settings = new JsonSerializerSettings(); - settings.Converters.Add(new RequestConverter()); - - var sut = JsonConvert.DeserializeObject("{\"name\":\"Jane Doe\",\"age\":21,\"emailAddress\":\"jd@office.com\"}", settings); - - Assert.NotNull(sut); - Assert.Equal("Jane Doe", sut.Name); - Assert.Equal((byte)21, sut.Age); - Assert.Equal("jd@office.com", sut.EmailAddress); - } - - [Fact] - public void RequestConverter_ShouldRehydrateWritableProperties() - { - var settings = new JsonSerializerSettings(); - settings.Converters.Add(new RequestConverter()); - - var sut = JsonConvert.DeserializeObject("{\"name\":\"Jane Doe\"}", settings); - - Assert.NotNull(sut); - Assert.Equal("Jane Doe", sut.Name); - } - - [Fact] - public void RequestConverter_ShouldFailWhenNoSupportedBackingFieldExists() - { - var settings = new JsonSerializerSettings(); - settings.Converters.Add(new RequestConverter()); - - var ex = Assert.Throws(() => JsonConvert.DeserializeObject("{\"name\":\"Jane Doe\"}", settings)); - - Assert.StartsWith("This deserializer only supports rehydration", ex.Message); - } - - private sealed class WritableRequest : IRequest - { - public string Name { get; set; } - } - - private sealed class UnsupportedRequest : IRequest - { - private readonly string _name = string.Empty; - - public string Name => _name; - } + var sut = new RequestConverter(); + using var writer = new JsonTextWriter(new StringWriter()); + + Assert.Throws(() => sut.WriteJson(writer, new CreateMemberCommand("Jane Doe", 21, "jd@office.com"), JsonSerializer.CreateDefault())); + } + + [Fact] + public void RequestConverter_ShouldRehydrateAutoPropertyRequests() + { + var settings = new JsonSerializerSettings(); + settings.Converters.Add(new RequestConverter()); + + var sut = JsonConvert.DeserializeObject("{\"name\":\"Jane Doe\",\"age\":21,\"emailAddress\":\"jd@office.com\"}", settings); + + Assert.NotNull(sut); + Assert.Equal("Jane Doe", sut.Name); + Assert.Equal((byte)21, sut.Age); + Assert.Equal("jd@office.com", sut.EmailAddress); + } + + [Fact] + public void RequestConverter_ShouldRehydrateWritableProperties() + { + var settings = new JsonSerializerSettings(); + settings.Converters.Add(new RequestConverter()); + + var sut = JsonConvert.DeserializeObject("{\"name\":\"Jane Doe\"}", settings); + + Assert.NotNull(sut); + Assert.Equal("Jane Doe", sut.Name); + } + + [Fact] + public void RequestConverter_ShouldFailWhenNoSupportedBackingFieldExists() + { + var settings = new JsonSerializerSettings(); + settings.Converters.Add(new RequestConverter()); + + var ex = Assert.Throws(() => JsonConvert.DeserializeObject("{\"name\":\"Jane Doe\"}", settings)); + + Assert.StartsWith("This deserializer only supports rehydration", ex.Message); + } + + private sealed class WritableRequest : IRequest + { + public string Name { get; set; } + } + + private sealed class UnsupportedRequest : IRequest + { + private readonly string _name = string.Empty; + + public string Name => _name; } } diff --git a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/CreateMemberCommand.cs b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/CreateMemberCommand.cs index d462cf82..7b6a20f7 100644 --- a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/CreateMemberCommand.cs +++ b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/CreateMemberCommand.cs @@ -1,20 +1,19 @@ -using Savvyio.Commands; +using Savvyio.Commands; -namespace Savvyio.Extensions.QueueStorage.Assets +namespace Savvyio.Extensions.QueueStorage.Assets; + +internal record CreateMemberCommand : Command { - internal record CreateMemberCommand : Command + public CreateMemberCommand(string name, byte age, string emailAddress) { - public CreateMemberCommand(string name, byte age, string emailAddress) - { - Name = name; - Age = age; - EmailAddress = emailAddress; - } + Name = name; + Age = age; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public byte Age { get; } + public byte Age { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/MarkerStructs.cs b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/MarkerStructs.cs index 89cac802..54f58304 100644 --- a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/MarkerStructs.cs +++ b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/MarkerStructs.cs @@ -1,18 +1,17 @@ -namespace Savvyio.Extensions.QueueStorage.Assets +namespace Savvyio.Extensions.QueueStorage.Assets; + +public struct WindowsDebug { - public struct WindowsDebug - { - } +} - public struct WindowsRelease - { - } +public struct WindowsRelease +{ +} - public struct LinuxDebug - { - } +public struct LinuxDebug +{ +} - public struct LinuxRelease - { - } +public struct LinuxRelease +{ } diff --git a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/MemberCreated.cs b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/MemberCreated.cs index 2d0acd98..aa0dda1d 100644 --- a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/MemberCreated.cs +++ b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Assets/MemberCreated.cs @@ -1,17 +1,16 @@ -using Savvyio.EventDriven; +using Savvyio.EventDriven; -namespace Savvyio.Extensions.QueueStorage.Assets +namespace Savvyio.Extensions.QueueStorage.Assets; + +public record MemberCreated : IntegrationEvent { - public record MemberCreated : IntegrationEvent + public MemberCreated(string name, string emailAddress) { - public MemberCreated(string name, string emailAddress) - { - Name = name; - EmailAddress = emailAddress; - } + Name = name; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Commands/AzureCommandQueueJsonSerializerContextTest.cs b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Commands/AzureCommandQueueJsonSerializerContextTest.cs index 7e1004fb..0f39bd25 100644 --- a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Commands/AzureCommandQueueJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Commands/AzureCommandQueueJsonSerializerContextTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -14,6 +14,7 @@ using Cuemon.Extensions.Text.Json.Formatters; using Codebelt.Extensions.Xunit; using Codebelt.Extensions.Xunit.Hosting; +using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Savvyio.Commands; using Savvyio.Commands.Messaging; @@ -26,153 +27,152 @@ using Xunit; using Xunit.v3.Priority; -namespace Savvyio.Extensions.QueueStorage.Commands +namespace Savvyio.Extensions.QueueStorage.Commands; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class AzureCommandQueueJsonSerializerContextTest : HostTest { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class AzureCommandQueueJsonSerializerContextTest : HostTest + private static readonly string Platform = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "windows"; + private static readonly string BuildType = typeof(AzureCommandQueue).Assembly.IsDebugBuild() ? "debug" : "release"; + private static readonly InMemoryTestStore> Comparer = new(); + private readonly AzureCommandQueue _queue; + private readonly IMarshaller _marshaller; + + public AzureCommandQueueJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) { - private static readonly string Platform = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "windows"; - private static readonly string BuildType = typeof(AzureCommandQueue).Assembly.IsDebugBuild() ? "debug" : "release"; - private static readonly InMemoryTestStore> Comparer = new(); - private readonly AzureCommandQueue _queue; - private readonly IMarshaller _marshaller; + _queue = fixture.Host.Services.GetRequiredService(); + _marshaller = fixture.Host.Services.GetRequiredService(); + } - public AzureCommandQueueJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - _queue = fixture.Host.Services.GetRequiredService(); - _marshaller = fixture.Host.Services.GetRequiredService(); - } + [Fact, Priority(0)] + public async Task SendAsync_CreateMemberCommand_OneTime() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); - [Fact, Priority(0)] - public async Task SendAsync_CreateMemberCommand_OneTime() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString()); - TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString()); + Comparer.Add(sut3); - Comparer.Add(sut3); + await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); + } - await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); - } + [Fact, Priority(1)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); - [Fact, Priority(1)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime() + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; + IMessage sut2 = null; + while (sut2 == null) { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); + sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false); + if (sut2 != null) { await sut2.AcknowledgeAsync().ConfigureAwait(false); } + } - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; - IMessage sut2 = null; - while (sut2 == null) - { - sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false); - if (sut2 != null) { await sut2.AcknowledgeAsync().ConfigureAwait(false); } - } + TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); - TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + } - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - } + [Fact, Priority(2)] + public async Task SendAsync_CreateMemberCommand_OneTime_Signed() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members/signed".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)).Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - [Fact, Priority(2)] - public async Task SendAsync_CreateMemberCommand_OneTime_Signed() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members/signed".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)).Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); - TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); + Comparer.Add(sut3); - Comparer.Add(sut3); + await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); + } - await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); - } + [Fact, Priority(3)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime_Signed() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members/signed").Single(); - [Fact, Priority(3)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime_Signed() + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; + ISignedMessage sut2 = null; + while (sut2 == null) { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members/signed").Single(); - - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; - ISignedMessage sut2 = null; - while (sut2 == null) - { - sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false) as ISignedMessage; - sut2?.CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - if (sut2 != null) { await sut2.AcknowledgeAsync().ConfigureAwait(false); } - } - - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); + sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false) as ISignedMessage; + sut2?.CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + if (sut2 != null) { await sut2.AcknowledgeAsync().ConfigureAwait(false); } } - [Fact, Priority(4)] - public async Task SendAsync_CreateMemberCommand_HundredTimes() + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + } + + [Fact, Priority(4)] + public async Task SendAsync_CreateMemberCommand_HundredTimes() + { + var messages = Generate.RangeOf(100, i => { - var messages = Generate.RangeOf(100, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - Comparer.Add(message); - return message; - }).ToList(); + var email = $"{Generate.RandomString(5)}@outlook.com"; + var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + Comparer.Add(message); + return message; + }).ToList(); - var profiler = await TimeMeasure.WithActionAsync(_ => _queue.SendAsync(messages)).ConfigureAwait(false); + var profiler = await TimeMeasure.WithActionAsync(_ => _queue.SendAsync(messages)).ConfigureAwait(false); - TestOutput.WriteLine(profiler.ToString()); - } + TestOutput.WriteLine(profiler.ToString()); + } - [Fact, Priority(5)] - public async Task ReceiveAsync_CreateMemberCommand_All() - { - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(90)).Token; - var realizedCommands = new List>(); - var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); + [Fact, Priority(5)] + public async Task ReceiveAsync_CreateMemberCommand_All() + { + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(90)).Token; + var realizedCommands = new List>(); + var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); - while (realizedCommands.Count < sut1.Count) + while (realizedCommands.Count < sut1.Count) + { + realizedCommands.AddRange(await _queue.ReceiveAsync(o => o.CancellationToken = ct).Select(message => { - realizedCommands.AddRange(await _queue.ReceiveAsync(o => o.CancellationToken = ct).Select(message => - { - message.AcknowledgeAsync(); - return message; - }).ToListAsync(ct).ConfigureAwait(false)); - } - - TestOutput.WriteLine(realizedCommands.Count.ToString()); - TestOutput.WriteLines(realizedCommands.Take(10)); - - Assert.Equivalent(sut1.Count, realizedCommands.Count); - Assert.Equivalent(sut1, realizedCommands); - Assert.Equivalent(sut1.Select(message => message.Data), realizedCommands.Select(message => message.Data)); - Assert.Equivalent(sut1.Select(message => message.Data.Metadata), realizedCommands.Select(message => message.Data.Metadata)); + message.AcknowledgeAsync(); + return message; + }).ToListAsync(ct).ConfigureAwait(false)); } - public override void ConfigureServices(IServiceCollection services) + TestOutput.WriteLine(realizedCommands.Count.ToString()); + TestOutput.WriteLines(realizedCommands.Take(10)); + + Assert.Equivalent(sut1.Count, realizedCommands.Count); + Assert.Equivalent(sut1, realizedCommands); + Assert.Equivalent(sut1.Select(message => message.Data), realizedCommands.Select(message => message.Data)); + Assert.Equivalent(sut1.Select(message => message.Data.Metadata), realizedCommands.Select(message => message.Data.Metadata)); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddMarshaller() + .AddConfiguredOptions(o => o.Settings.PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower); + services.AddAzureCommandQueue(o => { - services.AddMarshaller() - .AddConfiguredOptions(o => o.Settings.PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower); - services.AddAzureCommandQueue(o => + o.QueueName = $"savvyio-commands-{Platform}-{BuildType}"; + o.ConnectionString = Configuration["Azure:Storage:QueueConnectionString"]; + o.PostConfigureClient(client => { - o.QueueName = $"savvyio-commands-{Platform}-{BuildType}"; - o.ConnectionString = Configuration["Azure:Storage:QueueConnectionString"]; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); + client.CreateIfNotExists(); + client.ClearMessages(); }); - } + }); } } diff --git a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Commands/AzureCommandQueueNewtonsoftJsonSerializerContextTest.cs b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Commands/AzureCommandQueueNewtonsoftJsonSerializerContextTest.cs index fe936011..1d5430ae 100644 --- a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Commands/AzureCommandQueueNewtonsoftJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Commands/AzureCommandQueueNewtonsoftJsonSerializerContextTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -13,6 +13,7 @@ using Cuemon.Extensions.Reflection; using Codebelt.Extensions.Xunit; using Codebelt.Extensions.Xunit.Hosting; +using Cuemon.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Serialization; using Savvyio.Commands; @@ -26,153 +27,152 @@ using Xunit; using Xunit.v3.Priority; -namespace Savvyio.Extensions.QueueStorage.Commands +namespace Savvyio.Extensions.QueueStorage.Commands; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class AzureCommandQueueNewtonsoftJsonSerializerContextTest : HostTest { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class AzureCommandQueueNewtonsoftJsonSerializerContextTest : HostTest + private static readonly string Platform = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "windows"; + private static readonly string BuildType = typeof(AzureCommandQueue).Assembly.IsDebugBuild() ? "debug" : "release"; + private static readonly InMemoryTestStore> Comparer = new(); + private readonly AzureCommandQueue _queue; + private readonly IMarshaller _marshaller; + + public AzureCommandQueueNewtonsoftJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) { - private static readonly string Platform = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "windows"; - private static readonly string BuildType = typeof(AzureCommandQueue).Assembly.IsDebugBuild() ? "debug" : "release"; - private static readonly InMemoryTestStore> Comparer = new(); - private readonly AzureCommandQueue _queue; - private readonly IMarshaller _marshaller; + _queue = fixture.Host.Services.GetRequiredService(); + _marshaller = fixture.Host.Services.GetRequiredService(); + } - public AzureCommandQueueNewtonsoftJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - _queue = fixture.Host.Services.GetRequiredService(); - _marshaller = fixture.Host.Services.GetRequiredService(); - } + [Fact, Priority(0)] + public async Task SendAsync_CreateMemberCommand_OneTime() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); - [Fact, Priority(0)] - public async Task SendAsync_CreateMemberCommand_OneTime() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString()); - TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString()); + Comparer.Add(sut3); - Comparer.Add(sut3); + await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); + } - await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); - } + [Fact, Priority(1)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); - [Fact, Priority(1)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime() + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; + IMessage sut2 = null; + while (sut2 == null) { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); + sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false); + if (sut2 != null) { await sut2.AcknowledgeAsync().ConfigureAwait(false); } + } - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; - IMessage sut2 = null; - while (sut2 == null) - { - sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false); - if (sut2 != null) { await sut2.AcknowledgeAsync().ConfigureAwait(false); } - } + TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); - TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + } - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - } + [Fact, Priority(2)] + public async Task SendAsync_CreateMemberCommand_OneTime_Signed() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members/signed".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)).Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - [Fact, Priority(2)] - public async Task SendAsync_CreateMemberCommand_OneTime_Signed() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members/signed".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)).Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); - TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); + Comparer.Add(sut3); - Comparer.Add(sut3); + await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); + } - await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); - } + [Fact, Priority(3)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime_Signed() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members/signed").Single(); - [Fact, Priority(3)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime_Signed() + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; + ISignedMessage sut2 = null; + while (sut2 == null) { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members/signed").Single(); - - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; - ISignedMessage sut2 = null; - while (sut2 == null) - { - sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false) as ISignedMessage; - sut2?.CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - if (sut2 != null) { await sut2.AcknowledgeAsync().ConfigureAwait(false); } - } - - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); + sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false) as ISignedMessage; + sut2?.CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + if (sut2 != null) { await sut2.AcknowledgeAsync().ConfigureAwait(false); } } - [Fact, Priority(4)] - public async Task SendAsync_CreateMemberCommand_HundredTimes() + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + } + + [Fact, Priority(4)] + public async Task SendAsync_CreateMemberCommand_HundredTimes() + { + var messages = Generate.RangeOf(100, i => { - var messages = Generate.RangeOf(100, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - Comparer.Add(message); - return message; - }).ToList(); + var email = $"{Generate.RandomString(5)}@outlook.com"; + var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + Comparer.Add(message); + return message; + }).ToList(); - var profiler = await TimeMeasure.WithActionAsync(_ => _queue.SendAsync(messages)).ConfigureAwait(false); + var profiler = await TimeMeasure.WithActionAsync(_ => _queue.SendAsync(messages)).ConfigureAwait(false); - TestOutput.WriteLine(profiler.ToString()); - } + TestOutput.WriteLine(profiler.ToString()); + } - [Fact, Priority(5)] - public async Task ReceiveAsync_CreateMemberCommand_All() - { - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(90)).Token; - var realizedCommands = new List>(); - var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); + [Fact, Priority(5)] + public async Task ReceiveAsync_CreateMemberCommand_All() + { + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(90)).Token; + var realizedCommands = new List>(); + var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); - while (realizedCommands.Count < sut1.Count) + while (realizedCommands.Count < sut1.Count) + { + realizedCommands.AddRange(await _queue.ReceiveAsync(o => o.CancellationToken = ct).Select(message => { - realizedCommands.AddRange(await _queue.ReceiveAsync(o => o.CancellationToken = ct).Select(message => - { - message.AcknowledgeAsync(); - return message; - }).ToListAsync(ct).ConfigureAwait(false)); - } - - TestOutput.WriteLine(realizedCommands.Count.ToString()); - TestOutput.WriteLines(realizedCommands.Take(10)); - - Assert.Equivalent(sut1.Count, realizedCommands.Count); - Assert.Equivalent(sut1, realizedCommands); - Assert.Equivalent(sut1.Select(message => message.Data), realizedCommands.Select(message => message.Data)); - Assert.Equivalent(sut1.Select(message => message.Data.Metadata), realizedCommands.Select(message => message.Data.Metadata)); + message.AcknowledgeAsync(); + return message; + }).ToListAsync(ct).ConfigureAwait(false)); } - public override void ConfigureServices(IServiceCollection services) + TestOutput.WriteLine(realizedCommands.Count.ToString()); + TestOutput.WriteLines(realizedCommands.Take(10)); + + Assert.Equivalent(sut1.Count, realizedCommands.Count); + Assert.Equivalent(sut1, realizedCommands); + Assert.Equivalent(sut1.Select(message => message.Data), realizedCommands.Select(message => message.Data)); + Assert.Equivalent(sut1.Select(message => message.Data.Metadata), realizedCommands.Select(message => message.Data.Metadata)); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddMarshaller() + .AddConfiguredOptions(o => o.Settings.ContractResolver = new DefaultContractResolver() { NamingStrategy = new KebabCaseNamingStrategy() { ProcessDictionaryKeys = false } }); + services.AddAzureCommandQueue(o => { - services.AddMarshaller() - .AddConfiguredOptions(o => o.Settings.ContractResolver = new DefaultContractResolver() { NamingStrategy = new KebabCaseNamingStrategy() { ProcessDictionaryKeys = false } }); - services.AddAzureCommandQueue(o => + o.QueueName = $"newtonsoft-savvyio-commands-{Platform}-{BuildType}"; + o.ConnectionString = Configuration["Azure:Storage:QueueConnectionString"]; + o.PostConfigureClient(client => { - o.QueueName = $"newtonsoft-savvyio-commands-{Platform}-{BuildType}"; - o.ConnectionString = Configuration["Azure:Storage:QueueConnectionString"]; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); + client.CreateIfNotExists(); + client.ClearMessages(); }); - } + }); } } diff --git a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/EventDriven/AzureEventBusJsonSerializerContextTest.cs b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/EventDriven/AzureEventBusJsonSerializerContextTest.cs index c1de4b52..6b844361 100644 --- a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/EventDriven/AzureEventBusJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/EventDriven/AzureEventBusJsonSerializerContextTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -28,323 +28,322 @@ using Xunit; using Xunit.v3.Priority; -namespace Savvyio.Extensions.QueueStorage.EventDriven +namespace Savvyio.Extensions.QueueStorage.EventDriven; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class AzureEventBusJsonSerializerContextTest : HostTest { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class AzureEventBusJsonSerializerContextTest : HostTest - { - private static readonly string Platform = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "win"; - private static readonly string BuildType = typeof(AzureCommandQueue).Assembly.IsDebugBuild() ? "debug" : "release"; - private static readonly InMemoryTestStore> Comparer = new(); - private readonly AzureEventBus _bus; - private readonly IMarshaller _marshaller; + private static readonly string Platform = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "win"; + private static readonly string BuildType = typeof(AzureCommandQueue).Assembly.IsDebugBuild() ? "debug" : "release"; + private static readonly InMemoryTestStore> Comparer = new(); + private readonly AzureEventBus _bus; + private readonly IMarshaller _marshaller; - public AzureEventBusJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - _bus = ResolveEventBus(fixture); - _marshaller = fixture.Host.Services.GetRequiredService(); - } + public AzureEventBusJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) + { + _bus = ResolveEventBus(fixture); + _marshaller = fixture.Host.Services.GetRequiredService(); + } - private static AzureEventBus ResolveEventBus(ManagedHostFixture fixture) + private static AzureEventBus ResolveEventBus(ManagedHostFixture fixture) + { + return Platform switch { - return Platform switch + "win" => BuildType switch { - "win" => BuildType switch - { - "debug" => fixture.Host.Services.GetRequiredService>(), - "release" => fixture.Host.Services.GetRequiredService>(), - _ => throw new NotSupportedException() - }, - "linux" => BuildType switch - { - "debug" => fixture.Host.Services.GetRequiredService>(), - "release" => fixture.Host.Services.GetRequiredService>(), - _ => throw new NotSupportedException() - }, + "debug" => fixture.Host.Services.GetRequiredService>(), + "release" => fixture.Host.Services.GetRequiredService>(), _ => throw new NotSupportedException() - }; - } + }, + "linux" => BuildType switch + { + "debug" => fixture.Host.Services.GetRequiredService>(), + "release" => fixture.Host.Services.GetRequiredService>(), + _ => throw new NotSupportedException() + }, + _ => throw new NotSupportedException() + }; + } - [Fact, Priority(0)] - public async Task PublishAsync_MemberCreated_OneTime() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = "urn:member-events-one".ToUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{BuildType}.updated-event"); + [Fact, Priority(0)] + public async Task PublishAsync_MemberCreated_OneTime() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = "urn:member-events-one".ToUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{BuildType}.updated-event"); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut3, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut3, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(_marshaller.Serialize(sut3.ToCloudEvent()).ToEncodedString(o => o.LeaveOpen = true)); + TestOutput.WriteLine(_marshaller.Serialize(sut3.ToCloudEvent()).ToEncodedString(o => o.LeaveOpen = true)); - Comparer.Add(sut3); + Comparer.Add(sut3); - await _bus.PublishAsync(sut3); - } + await _bus.PublishAsync(sut3); + } - [Fact, Priority(1)] - public async Task SubscribeAsync_MemberCreated_OneTime() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{BuildType}.updated-event")).Single(); - var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + [Fact, Priority(1)] + public async Task SubscribeAsync_MemberCreated_OneTime() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{BuildType}.updated-event")).Single(); + var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - while (!cancellationToken.IsCancellationRequested) + while (!cancellationToken.IsCancellationRequested) + { + await _bus.SubscribeAsync((sut2, _) => { - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{BuildType}.updated-event")) { return Task.CompletedTask; } - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - sut2.AcknowledgeAsync(); - cancellationToken.Cancel(); - return Task.CompletedTask; - }, o => o.CancellationToken = cancellationToken.Token).ConfigureAwait(false); - } - - Assert.Equal(1, handlerInvocations); + if (!sut2.Type.Contains($"{BuildType}.updated-event")) { return Task.CompletedTask; } + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + sut2.AcknowledgeAsync(); + cancellationToken.Cancel(); + return Task.CompletedTask; + }, o => o.CancellationToken = cancellationToken.Token).ConfigureAwait(false); } - [Fact, Priority(2)] - public async Task PublishAsync_MemberCreated_OneTime_Signed() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = "urn:member-events-one".ToUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.signed").Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + Assert.Equal(1, handlerInvocations); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(2)] + public async Task PublishAsync_MemberCreated_OneTime_Signed() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = "urn:member-events-one".ToUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.signed").Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - Comparer.Add(sut3); + TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); - await _bus.PublishAsync(sut3); - } + Comparer.Add(sut3); - [Fact, Priority(3)] - public async Task SubscribeAsync_MemberCreated_OneTime_Signed() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.signed")).Single(); - var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + await _bus.PublishAsync(sut3); + } - while (!cancellationToken.IsCancellationRequested) + [Fact, Priority(3)] + public async Task SubscribeAsync_MemberCreated_OneTime_Signed() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.signed")).Single(); + var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + + while (!cancellationToken.IsCancellationRequested) + { + await _bus.SubscribeAsync((sut2, _) => { - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.signed")) { return Task.CompletedTask; } - ((ISignedMessage)sut2).CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - sut2.AcknowledgeAsync(); - cancellationToken.Cancel(); - return Task.CompletedTask; - }).ConfigureAwait(false); - } - Assert.Equal(1, handlerInvocations); + if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.signed")) { return Task.CompletedTask; } + ((ISignedMessage)sut2).CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + sut2.AcknowledgeAsync(); + cancellationToken.Cancel(); + return Task.CompletedTask; + }).ConfigureAwait(false); } + Assert.Equal(1, handlerInvocations); + } - [Fact, Priority(4)] - public async Task PublishAsync_MemberCreated_OneTime_CloudEvent() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = "urn:member-events-one".ToUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.cloud-event").ToCloudEvent(); + [Fact, Priority(4)] + public async Task PublishAsync_MemberCreated_OneTime_CloudEvent() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = "urn:member-events-one".ToUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.cloud-event").ToCloudEvent(); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); + TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); - Comparer.Add(sut3); + Comparer.Add(sut3); - await _bus.PublishAsync(sut3); - } + await _bus.PublishAsync(sut3); + } - [Fact, Priority(5)] - public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.cloud-event")).Single() as ICloudEvent; - var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + [Fact, Priority(5)] + public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.cloud-event")).Single() as ICloudEvent; + var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - while (!cancellationToken.IsCancellationRequested) + while (!cancellationToken.IsCancellationRequested) + { + await _bus.SubscribeAsync((sut2, _) => { - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.cloud-event")) { return Task.CompletedTask; } - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - Assert.Equivalent(sut1.Specversion, ((ICloudEvent)sut2).Specversion); - sut2.AcknowledgeAsync(); - cancellationToken.Cancel(); - return Task.CompletedTask; - }).ConfigureAwait(false); - - } - Assert.Equal(1, handlerInvocations); - } + if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.cloud-event")) { return Task.CompletedTask; } + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + Assert.Equivalent(sut1.Specversion, ((ICloudEvent)sut2).Specversion); + sut2.AcknowledgeAsync(); + cancellationToken.Cancel(); + return Task.CompletedTask; + }).ConfigureAwait(false); - [Fact, Priority(6)] - public async Task PublishAsync_MemberCreated_OneTime_CloudEvent_Signed() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = "urn:member-events-one".ToUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + } + Assert.Equal(1, handlerInvocations); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(6)] + public async Task PublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = "urn:member-events-one".ToUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - Comparer.Add(sut3); + TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); - await _bus.PublishAsync(sut3); - } + Comparer.Add(sut3); - [Fact, Priority(7)] - public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent_Signed() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.signed-cloud-event")).Single(); - var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + await _bus.PublishAsync(sut3); + } - while (!cancellationToken.IsCancellationRequested) - { - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.signed-cloud-event")) { return Task.CompletedTask; } - ((ISignedCloudEvent)sut2).CheckCloudEventSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - sut2.AcknowledgeAsync(); - cancellationToken.Cancel(); - return Task.CompletedTask; - }).ConfigureAwait(false); - } - Assert.Equal(1, handlerInvocations); - } + [Fact, Priority(7)] + public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.signed-cloud-event")).Single(); + var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - [Fact, Priority(8)] - public async Task PublishAsync_MemberCreated_HundredTimes() // reduced as SNS is not part of free tier (FIFO) + while (!cancellationToken.IsCancellationRequested) { - var messages = Generate.RangeOf(100, _ => + await _bus.SubscribeAsync((sut2, _) => { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}.{Platform}.{BuildType}"); - }); - - await ParallelFactory.ForEachAsync(messages, (message, token) => - { - Comparer.Add(message); - return _bus.PublishAsync(message, o => o.CancellationToken = token); + if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.signed-cloud-event")) { return Task.CompletedTask; } + ((ISignedCloudEvent)sut2).CheckCloudEventSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + sut2.AcknowledgeAsync(); + cancellationToken.Cancel(); + return Task.CompletedTask; }).ConfigureAwait(false); } + Assert.Equal(1, handlerInvocations); + } - [Fact, Priority(9)] - public async Task SubscribeAsync_MemberCreated_All() + [Fact, Priority(8)] + public async Task PublishAsync_MemberCreated_HundredTimes() // reduced as SNS is not part of free tier (FIFO) + { + var messages = Generate.RangeOf(100, _ => { - var sut1 = Comparer.Query(message => message.Source.Contains("member-events-many") && message.Type.Contains($"{Platform}.{BuildType}")).ToList(); - var sut2 = new List>(); - var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - - await _bus.SubscribeAsync((message, _) => - { - if (!message.Type.Contains($"{Platform}.{BuildType}")) { return Task.CompletedTask; } - sut2.Add(message); - message.AcknowledgeAsync(); - return Task.CompletedTask; - }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}.{Platform}.{BuildType}"); + }); - TestOutput.WriteLine(sut2.Count.ToString()); - TestOutput.WriteLines(sut2.Take(10)); + await ParallelFactory.ForEachAsync(messages, (message, token) => + { + Comparer.Add(message); + return _bus.PublishAsync(message, o => o.CancellationToken = token); + }).ConfigureAwait(false); + } - Assert.Equivalent(sut1.Count, sut2.Count); - Assert.Equivalent(sut1, sut2); - Assert.Equivalent(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); - Assert.Equivalent(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); - } + [Fact, Priority(9)] + public async Task SubscribeAsync_MemberCreated_All() + { + var sut1 = Comparer.Query(message => message.Source.Contains("member-events-many") && message.Type.Contains($"{Platform}.{BuildType}")).ToList(); + var sut2 = new List>(); + var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - public override void ConfigureServices(IServiceCollection services) + await _bus.SubscribeAsync((message, _) => { - var queueName = $"savvyio-events-{Platform}-{BuildType}"; - var queueConnection = Configuration["Azure:Storage:QueueConnectionString"]; + if (!message.Type.Contains($"{Platform}.{BuildType}")) { return Task.CompletedTask; } + sut2.Add(message); + message.AcknowledgeAsync(); + return Task.CompletedTask; + }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); + + TestOutput.WriteLine(sut2.Count.ToString()); + TestOutput.WriteLines(sut2.Take(10)); + + Assert.Equivalent(sut1.Count, sut2.Count); + Assert.Equivalent(sut1, sut2); + Assert.Equivalent(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); + Assert.Equivalent(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); + } - services.AddMarshaller(); + public override void ConfigureServices(IServiceCollection services) + { + var queueName = $"savvyio-events-{Platform}-{BuildType}"; + var queueConnection = Configuration["Azure:Storage:QueueConnectionString"]; - services.AddAzureEventBus(o => - { - o.QueueName = queueName; - o.ConnectionString = queueConnection; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); - }, o => + services.AddMarshaller(); + + services.AddAzureEventBus(o => + { + o.QueueName = queueName; + o.ConnectionString = queueConnection; + o.PostConfigureClient(client => { - o.TopicEndpoint = "https://savvyio-events-win-debug.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); - o.KeyCredential = new AzureKeyCredential(Configuration["Azure:EventGrid:Windows:Debug:Key"]!); + client.CreateIfNotExists(); + client.ClearMessages(); }); + }, o => + { + o.TopicEndpoint = "https://savvyio-events-win-debug.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); + o.KeyCredential = new AzureKeyCredential(Configuration["Azure:EventGrid:Windows:Debug:Key"]!); + }); - services.AddAzureEventBus(o => - { - o.QueueName = queueName; - o.ConnectionString = queueConnection; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); - }, o => + services.AddAzureEventBus(o => + { + o.QueueName = queueName; + o.ConnectionString = queueConnection; + o.PostConfigureClient(client => { - o.TopicEndpoint = "https://savvyio-events-win-release.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); - o.KeyCredential = new AzureKeyCredential(Configuration["Azure:EventGrid:Windows:Release:Key"]!); + client.CreateIfNotExists(); + client.ClearMessages(); }); + }, o => + { + o.TopicEndpoint = "https://savvyio-events-win-release.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); + o.KeyCredential = new AzureKeyCredential(Configuration["Azure:EventGrid:Windows:Release:Key"]!); + }); - services.AddAzureEventBus(o => - { - o.QueueName = queueName; - o.ConnectionString = queueConnection; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); - }, o => + services.AddAzureEventBus(o => + { + o.QueueName = queueName; + o.ConnectionString = queueConnection; + o.PostConfigureClient(client => { - o.TopicEndpoint = "https://savvyio-events-linux-debug.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); - o.KeyCredential = new AzureKeyCredential(Configuration["Azure:EventGrid:Linux:Debug:Key"]!); + client.CreateIfNotExists(); + client.ClearMessages(); }); + }, o => + { + o.TopicEndpoint = "https://savvyio-events-linux-debug.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); + o.KeyCredential = new AzureKeyCredential(Configuration["Azure:EventGrid:Linux:Debug:Key"]!); + }); - services.AddAzureEventBus(o => - { - o.QueueName = queueName; - o.ConnectionString = queueConnection; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); - }, o => + services.AddAzureEventBus(o => + { + o.QueueName = queueName; + o.ConnectionString = queueConnection; + o.PostConfigureClient(client => { - o.TopicEndpoint = "https://savvyio-events-linux-release.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); - o.KeyCredential = new AzureKeyCredential(Configuration["Azure:EventGrid:Linux:Release:Key"]!); + client.CreateIfNotExists(); + client.ClearMessages(); }); - } + }, o => + { + o.TopicEndpoint = "https://savvyio-events-linux-release.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); + o.KeyCredential = new AzureKeyCredential(Configuration["Azure:EventGrid:Linux:Release:Key"]!); + }); } } diff --git a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/EventDriven/AzureEventBusNewtonsoftJsonSerializerContextTest.cs b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/EventDriven/AzureEventBusNewtonsoftJsonSerializerContextTest.cs index d98c493d..e9776519 100644 --- a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/EventDriven/AzureEventBusNewtonsoftJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/EventDriven/AzureEventBusNewtonsoftJsonSerializerContextTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -28,323 +28,322 @@ using Xunit; using Xunit.v3.Priority; -namespace Savvyio.Extensions.QueueStorage.EventDriven +namespace Savvyio.Extensions.QueueStorage.EventDriven; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class AzureEventBusNewtonsoftJsonSerializerContextTest : HostTest { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class AzureEventBusNewtonsoftJsonSerializerContextTest : HostTest - { - private static readonly string Platform = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "win"; - private static readonly string BuildType = typeof(AzureCommandQueue).Assembly.IsDebugBuild() ? "debug" : "release"; - private static readonly InMemoryTestStore> Comparer = new(); - private readonly AzureEventBus _bus; - private readonly IMarshaller _marshaller; + private static readonly string Platform = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "win"; + private static readonly string BuildType = typeof(AzureCommandQueue).Assembly.IsDebugBuild() ? "debug" : "release"; + private static readonly InMemoryTestStore> Comparer = new(); + private readonly AzureEventBus _bus; + private readonly IMarshaller _marshaller; - public AzureEventBusNewtonsoftJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - _bus = ResolveEventBus(fixture); - _marshaller = fixture.Host.Services.GetRequiredService(); - } + public AzureEventBusNewtonsoftJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) + { + _bus = ResolveEventBus(fixture); + _marshaller = fixture.Host.Services.GetRequiredService(); + } - private static AzureEventBus ResolveEventBus(ManagedHostFixture fixture) + private static AzureEventBus ResolveEventBus(ManagedHostFixture fixture) + { + return Platform switch { - return Platform switch + "win" => BuildType switch { - "win" => BuildType switch - { - "debug" => fixture.Host.Services.GetRequiredService>(), - "release" => fixture.Host.Services.GetRequiredService>(), - _ => throw new NotSupportedException() - }, - "linux" => BuildType switch - { - "debug" => fixture.Host.Services.GetRequiredService>(), - "release" => fixture.Host.Services.GetRequiredService>(), - _ => throw new NotSupportedException() - }, + "debug" => fixture.Host.Services.GetRequiredService>(), + "release" => fixture.Host.Services.GetRequiredService>(), _ => throw new NotSupportedException() - }; - } + }, + "linux" => BuildType switch + { + "debug" => fixture.Host.Services.GetRequiredService>(), + "release" => fixture.Host.Services.GetRequiredService>(), + _ => throw new NotSupportedException() + }, + _ => throw new NotSupportedException() + }; + } - [Fact, Priority(0)] - public async Task PublishAsync_MemberCreated_OneTime() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = "urn:member-events-one".ToUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{BuildType}.updated-event"); + [Fact, Priority(0)] + public async Task PublishAsync_MemberCreated_OneTime() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = "urn:member-events-one".ToUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{BuildType}.updated-event"); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut3, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut3, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(_marshaller.Serialize(sut3.ToCloudEvent()).ToEncodedString(o => o.LeaveOpen = true)); + TestOutput.WriteLine(_marshaller.Serialize(sut3.ToCloudEvent()).ToEncodedString(o => o.LeaveOpen = true)); - Comparer.Add(sut3); + Comparer.Add(sut3); - await _bus.PublishAsync(sut3); - } + await _bus.PublishAsync(sut3); + } - [Fact, Priority(1)] - public async Task SubscribeAsync_MemberCreated_OneTime() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{BuildType}.updated-event")).Single(); - var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + [Fact, Priority(1)] + public async Task SubscribeAsync_MemberCreated_OneTime() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{BuildType}.updated-event")).Single(); + var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - while (!cancellationToken.IsCancellationRequested) + while (!cancellationToken.IsCancellationRequested) + { + await _bus.SubscribeAsync((sut2, _) => { - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{BuildType}.updated-event")) { return Task.CompletedTask; } - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - sut2.AcknowledgeAsync(); - cancellationToken.Cancel(); - return Task.CompletedTask; - }, o => o.CancellationToken = cancellationToken.Token).ConfigureAwait(false); - } - - Assert.Equal(1, handlerInvocations); + if (!sut2.Type.Contains($"{BuildType}.updated-event")) { return Task.CompletedTask; } + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + sut2.AcknowledgeAsync(); + cancellationToken.Cancel(); + return Task.CompletedTask; + }, o => o.CancellationToken = cancellationToken.Token).ConfigureAwait(false); } - [Fact, Priority(2)] - public async Task PublishAsync_MemberCreated_OneTime_Signed() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = "urn:member-events-one".ToUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.signed").Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + Assert.Equal(1, handlerInvocations); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(2)] + public async Task PublishAsync_MemberCreated_OneTime_Signed() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = "urn:member-events-one".ToUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.signed").Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - Comparer.Add(sut3); + TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); - await _bus.PublishAsync(sut3); - } + Comparer.Add(sut3); - [Fact, Priority(3)] - public async Task SubscribeAsync_MemberCreated_OneTime_Signed() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.signed")).Single(); - var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + await _bus.PublishAsync(sut3); + } - while (!cancellationToken.IsCancellationRequested) + [Fact, Priority(3)] + public async Task SubscribeAsync_MemberCreated_OneTime_Signed() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.signed")).Single(); + var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + + while (!cancellationToken.IsCancellationRequested) + { + await _bus.SubscribeAsync((sut2, _) => { - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.signed")) { return Task.CompletedTask; } - ((ISignedMessage)sut2).CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - sut2.AcknowledgeAsync(); - cancellationToken.Cancel(); - return Task.CompletedTask; - }).ConfigureAwait(false); - } - Assert.Equal(1, handlerInvocations); + if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.signed")) { return Task.CompletedTask; } + ((ISignedMessage)sut2).CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + sut2.AcknowledgeAsync(); + cancellationToken.Cancel(); + return Task.CompletedTask; + }).ConfigureAwait(false); } + Assert.Equal(1, handlerInvocations); + } - [Fact, Priority(4)] - public async Task PublishAsync_MemberCreated_OneTime_CloudEvent() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = "urn:member-events-one".ToUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.cloud-event").ToCloudEvent(); + [Fact, Priority(4)] + public async Task PublishAsync_MemberCreated_OneTime_CloudEvent() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = "urn:member-events-one".ToUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.cloud-event").ToCloudEvent(); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); + TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); - Comparer.Add(sut3); + Comparer.Add(sut3); - await _bus.PublishAsync(sut3); - } + await _bus.PublishAsync(sut3); + } - [Fact, Priority(5)] - public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.cloud-event")).Single() as ICloudEvent; - var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + [Fact, Priority(5)] + public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.cloud-event")).Single() as ICloudEvent; + var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - while (!cancellationToken.IsCancellationRequested) + while (!cancellationToken.IsCancellationRequested) + { + await _bus.SubscribeAsync((sut2, _) => { - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.cloud-event")) { return Task.CompletedTask; } - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - Assert.Equivalent(sut1.Specversion, ((ICloudEvent)sut2).Specversion); - sut2.AcknowledgeAsync(); - cancellationToken.Cancel(); - return Task.CompletedTask; - }).ConfigureAwait(false); - - } - Assert.Equal(1, handlerInvocations); - } + if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.cloud-event")) { return Task.CompletedTask; } + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + Assert.Equivalent(sut1.Specversion, ((ICloudEvent)sut2).Specversion); + sut2.AcknowledgeAsync(); + cancellationToken.Cancel(); + return Task.CompletedTask; + }).ConfigureAwait(false); - [Fact, Priority(6)] - public async Task PublishAsync_MemberCreated_OneTime_CloudEvent_Signed() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = "urn:member-events-one".ToUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + } + Assert.Equal(1, handlerInvocations); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(6)] + public async Task PublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = "urn:member-events-one".ToUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{Platform}.{BuildType}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - Comparer.Add(sut3); + TestOutput.WriteLine(_marshaller.Serialize(sut3).ToEncodedString(o => o.LeaveOpen = true)); - await _bus.PublishAsync(sut3); - } + Comparer.Add(sut3); - [Fact, Priority(7)] - public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent_Signed() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.signed-cloud-event")).Single(); - var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + await _bus.PublishAsync(sut3); + } - while (!cancellationToken.IsCancellationRequested) - { - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.signed-cloud-event")) { return Task.CompletedTask; } - ((ISignedCloudEvent)sut2).CheckCloudEventSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - sut2.AcknowledgeAsync(); - cancellationToken.Cancel(); - return Task.CompletedTask; - }).ConfigureAwait(false); - } - Assert.Equal(1, handlerInvocations); - } + [Fact, Priority(7)] + public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{Platform}.{BuildType}.updated-event.signed-cloud-event")).Single(); + var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - [Fact, Priority(8)] - public async Task PublishAsync_MemberCreated_HundredTimes() // reduced as SNS is not part of free tier (FIFO) + while (!cancellationToken.IsCancellationRequested) { - var messages = Generate.RangeOf(100, _ => + await _bus.SubscribeAsync((sut2, _) => { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}.{Platform}.{BuildType}"); - }); - - await ParallelFactory.ForEachAsync(messages, (message, token) => - { - Comparer.Add(message); - return _bus.PublishAsync(message, o => o.CancellationToken = token); + if (!sut2.Type.Contains($"{Platform}.{BuildType}.updated-event.signed-cloud-event")) { return Task.CompletedTask; } + ((ISignedCloudEvent)sut2).CheckCloudEventSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + sut2.AcknowledgeAsync(); + cancellationToken.Cancel(); + return Task.CompletedTask; }).ConfigureAwait(false); } + Assert.Equal(1, handlerInvocations); + } - [Fact, Priority(9)] - public async Task SubscribeAsync_MemberCreated_All() + [Fact, Priority(8)] + public async Task PublishAsync_MemberCreated_HundredTimes() // reduced as SNS is not part of free tier (FIFO) + { + var messages = Generate.RangeOf(100, _ => { - var sut1 = Comparer.Query(message => message.Source.Contains("member-events-many") && message.Type.Contains($"{Platform}.{BuildType}")).ToList(); - var sut2 = new List>(); - var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - - await _bus.SubscribeAsync((message, _) => - { - if (!message.Type.Contains($"{Platform}.{BuildType}")) { return Task.CompletedTask; } - sut2.Add(message); - message.AcknowledgeAsync(); - return Task.CompletedTask; - }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}.{Platform}.{BuildType}"); + }); - TestOutput.WriteLine(sut2.Count.ToString()); - TestOutput.WriteLines(sut2.Take(10)); + await ParallelFactory.ForEachAsync(messages, (message, token) => + { + Comparer.Add(message); + return _bus.PublishAsync(message, o => o.CancellationToken = token); + }).ConfigureAwait(false); + } - Assert.Equivalent(sut1.Count, sut2.Count); - Assert.Equivalent(sut1, sut2); - Assert.Equivalent(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); - Assert.Equivalent(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); - } + [Fact, Priority(9)] + public async Task SubscribeAsync_MemberCreated_All() + { + var sut1 = Comparer.Query(message => message.Source.Contains("member-events-many") && message.Type.Contains($"{Platform}.{BuildType}")).ToList(); + var sut2 = new List>(); + var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - public override void ConfigureServices(IServiceCollection services) + await _bus.SubscribeAsync((message, _) => { - var queueName = $"newtonsoft-savvyio-events-{Platform}-{BuildType}"; - var queueConnection = Configuration["Azure:Storage:QueueConnectionString"]; + if (!message.Type.Contains($"{Platform}.{BuildType}")) { return Task.CompletedTask; } + sut2.Add(message); + message.AcknowledgeAsync(); + return Task.CompletedTask; + }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); + + TestOutput.WriteLine(sut2.Count.ToString()); + TestOutput.WriteLines(sut2.Take(10)); + + Assert.Equivalent(sut1.Count, sut2.Count); + Assert.Equivalent(sut1, sut2); + Assert.Equivalent(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); + Assert.Equivalent(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); + } - services.AddMarshaller(); + public override void ConfigureServices(IServiceCollection services) + { + var queueName = $"newtonsoft-savvyio-events-{Platform}-{BuildType}"; + var queueConnection = Configuration["Azure:Storage:QueueConnectionString"]; - services.AddAzureEventBus(o => - { - o.QueueName = queueName; - o.ConnectionString = queueConnection; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); - }, o => + services.AddMarshaller(); + + services.AddAzureEventBus(o => + { + o.QueueName = queueName; + o.ConnectionString = queueConnection; + o.PostConfigureClient(client => { - o.TopicEndpoint = "https://newtonsoft-savvyio-events-win-debug.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); - o.KeyCredential = new AzureKeyCredential(Configuration["Azure:Newtonsoft:EventGrid:Windows:Debug:Key"]!); + client.CreateIfNotExists(); + client.ClearMessages(); }); + }, o => + { + o.TopicEndpoint = "https://newtonsoft-savvyio-events-win-debug.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); + o.KeyCredential = new AzureKeyCredential(Configuration["Azure:Newtonsoft:EventGrid:Windows:Debug:Key"]!); + }); - services.AddAzureEventBus(o => - { - o.QueueName = queueName; - o.ConnectionString = queueConnection; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); - }, o => + services.AddAzureEventBus(o => + { + o.QueueName = queueName; + o.ConnectionString = queueConnection; + o.PostConfigureClient(client => { - o.TopicEndpoint = "https://newtonsoft-savvyio-events-win-release.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); - o.KeyCredential = new AzureKeyCredential(Configuration["Azure:Newtonsoft:EventGrid:Windows:Release:Key"]!); + client.CreateIfNotExists(); + client.ClearMessages(); }); + }, o => + { + o.TopicEndpoint = "https://newtonsoft-savvyio-events-win-release.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); + o.KeyCredential = new AzureKeyCredential(Configuration["Azure:Newtonsoft:EventGrid:Windows:Release:Key"]!); + }); - services.AddAzureEventBus(o => - { - o.QueueName = queueName; - o.ConnectionString = queueConnection; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); - }, o => + services.AddAzureEventBus(o => + { + o.QueueName = queueName; + o.ConnectionString = queueConnection; + o.PostConfigureClient(client => { - o.TopicEndpoint = "https://newtonsoft-savvyio-events-linux-debug.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); - o.KeyCredential = new AzureKeyCredential(Configuration["Azure:Newtonsoft:EventGrid:Linux:Debug:Key"]!); + client.CreateIfNotExists(); + client.ClearMessages(); }); + }, o => + { + o.TopicEndpoint = "https://newtonsoft-savvyio-events-linux-debug.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); + o.KeyCredential = new AzureKeyCredential(Configuration["Azure:Newtonsoft:EventGrid:Linux:Debug:Key"]!); + }); - services.AddAzureEventBus(o => - { - o.QueueName = queueName; - o.ConnectionString = queueConnection; - o.PostConfigureClient(client => - { - client.CreateIfNotExists(); - client.ClearMessages(); - }); - }, o => + services.AddAzureEventBus(o => + { + o.QueueName = queueName; + o.ConnectionString = queueConnection; + o.PostConfigureClient(client => { - o.TopicEndpoint = "https://newtonsoft-savvyio-events-linux-release.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); - o.KeyCredential = new AzureKeyCredential(Configuration["Azure:Newtonsoft:EventGrid:Linux:Release:Key"]!); + client.CreateIfNotExists(); + client.ClearMessages(); }); - } + }, o => + { + o.TopicEndpoint = "https://newtonsoft-savvyio-events-linux-release.swedencentral-1.eventgrid.azure.net/api/events".ToUri(); + o.KeyCredential = new AzureKeyCredential(Configuration["Azure:Newtonsoft:EventGrid:Linux:Release:Key"]!); + }); } } diff --git a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Savvyio.Extensions.QueueStorage.FunctionalTests.csproj b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Savvyio.Extensions.QueueStorage.FunctionalTests.csproj index b4ea1ca2..01ce4ce3 100644 --- a/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Savvyio.Extensions.QueueStorage.FunctionalTests.csproj +++ b/test/Savvyio.Extensions.QueueStorage.FunctionalTests/Savvyio.Extensions.QueueStorage.FunctionalTests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Extensions.QueueStorage diff --git a/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueOptionsTest.cs b/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueOptionsTest.cs index 7844d133..87143536 100644 --- a/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueOptionsTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueOptionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Azure; using Azure.Identity; using Azure.Storage; @@ -8,144 +8,143 @@ using Savvyio.Extensions.Text.Json; using Xunit; -namespace Savvyio.Extensions.QueueStorage +namespace Savvyio.Extensions.QueueStorage; + +public class AzureQueueOptionsTest : Test { - public class AzureQueueOptionsTest : Test + public AzureQueueOptionsTest(ITestOutputHelper output) : base(output) { - public AzureQueueOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_ShouldInitializeWithDefaultValues() - { - var options = new AzureQueueOptions(); - - Assert.NotNull(options.Credential); - Assert.IsType(options.Credential); - Assert.NotNull(options.Settings); - Assert.NotNull(options.ReceiveContext); - Assert.NotNull(options.SendContext); - Assert.Null(options.StorageAccountName); - Assert.Null(options.QueueName); - Assert.Null(options.SasCredential); - Assert.Null(options.StorageKeyCredential); - Assert.Null(options.ConnectionString); - } - - [Fact] - public void ValidateOptions_ShouldThrowException_WhenOptionsAreInvalid() - { - var options = new AzureQueueOptions(); + [Fact] + public void Constructor_ShouldInitializeWithDefaultValues() + { + var options = new AzureQueueOptions(); + + Assert.NotNull(options.Credential); + Assert.IsType(options.Credential); + Assert.NotNull(options.Settings); + Assert.NotNull(options.ReceiveContext); + Assert.NotNull(options.SendContext); + Assert.Null(options.StorageAccountName); + Assert.Null(options.QueueName); + Assert.Null(options.SasCredential); + Assert.Null(options.StorageKeyCredential); + Assert.Null(options.ConnectionString); + } - Assert.Throws(() => options.ValidateOptions()); + [Fact] + public void ValidateOptions_ShouldThrowException_WhenOptionsAreInvalid() + { + var options = new AzureQueueOptions(); - options.ConnectionString = "DefaultEndpointsProtocol=https;AccountName=testaccount;AccountKey=testkey;EndpointSuffix=core.windows.net"; - options.QueueName = "testqueue"; - options.ValidateOptions(); // Should not throw + Assert.Throws(() => options.ValidateOptions()); - options.ConnectionString = null; - options.Credential = null; - options.SasCredential = null; - options.StorageKeyCredential = null; + options.ConnectionString = "DefaultEndpointsProtocol=https;AccountName=testaccount;AccountKey=testkey;EndpointSuffix=core.windows.net"; + options.QueueName = "testqueue"; + options.ValidateOptions(); // Should not throw - Assert.Throws(() => options.ValidateOptions()); - } + options.ConnectionString = null; + options.Credential = null; + options.SasCredential = null; + options.StorageKeyCredential = null; - [Fact] - public void ValidateOptions_ThrowsInvalidOperationException_WhenConnectionStringIsSetAndQueueNameIsNull() - { - var sut1 = new AzureQueueOptions - { - ConnectionString = "DefaultEndpointsProtocol=https;AccountName=testaccount;AccountKey=testkey;EndpointSuffix=core.windows.net", - QueueName = null - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - - TestOutput.WriteLine(sut2.Message); - - Assert.Equal($"{nameof(AzureQueueOptions.QueueName)} cannot be null, empty, or consists only of white-space when a {nameof(AzureQueueOptions.ConnectionString)} has been specified. (Expression '!string.IsNullOrWhiteSpace({nameof(AzureQueueOptions.ConnectionString)}) && string.IsNullOrWhiteSpace({nameof(AzureQueueOptions.QueueName)})')", sut2.Message); - Assert.Equal($"{nameof(AzureQueueOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } - - [Fact] - public void ValidateOptions_ThrowsInvalidOperationException_WhenAllCredentialPropertiesAreNull() + Assert.Throws(() => options.ValidateOptions()); + } + + [Fact] + public void ValidateOptions_ThrowsInvalidOperationException_WhenConnectionStringIsSetAndQueueNameIsNull() + { + var sut1 = new AzureQueueOptions { - var sut1 = new AzureQueueOptions - { - Credential = null, - SasCredential = null, - StorageKeyCredential = null - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - - TestOutput.WriteLine(sut2.Message); - - Assert.Equal($"A credential type has to be specified for Azure authentication. (Expression '{nameof(AzureQueueOptions.Credential)} == null && {nameof(AzureQueueOptions.SasCredential)} == null && {nameof(AzureQueueOptions.StorageKeyCredential)} == null')", sut2.Message); - Assert.Equal($"{nameof(AzureQueueOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } - - [Fact] - public void ValidateOptions_ThrowsInvalidOperationException_WhenStorageNameAndQueueNameIsNotSpecified() + ConnectionString = "DefaultEndpointsProtocol=https;AccountName=testaccount;AccountKey=testkey;EndpointSuffix=core.windows.net", + QueueName = null + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + + TestOutput.WriteLine(sut2.Message); + + Assert.Equal($"{nameof(AzureQueueOptions.QueueName)} cannot be null, empty, or consists only of white-space when a {nameof(AzureQueueOptions.ConnectionString)} has been specified. (Expression '!string.IsNullOrWhiteSpace({nameof(AzureQueueOptions.ConnectionString)}) && string.IsNullOrWhiteSpace({nameof(AzureQueueOptions.QueueName)})')", sut2.Message); + Assert.Equal($"{nameof(AzureQueueOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } + + [Fact] + public void ValidateOptions_ThrowsInvalidOperationException_WhenAllCredentialPropertiesAreNull() + { + var sut1 = new AzureQueueOptions { - var sut1 = new AzureQueueOptions(); - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + Credential = null, + SasCredential = null, + StorageKeyCredential = null + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + + TestOutput.WriteLine(sut2.Message); + + Assert.Equal($"A credential type has to be specified for Azure authentication. (Expression '{nameof(AzureQueueOptions.Credential)} == null && {nameof(AzureQueueOptions.SasCredential)} == null && {nameof(AzureQueueOptions.StorageKeyCredential)} == null')", sut2.Message); + Assert.Equal($"{nameof(AzureQueueOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } - TestOutput.WriteLine(sut2.Message); + [Fact] + public void ValidateOptions_ThrowsInvalidOperationException_WhenStorageNameAndQueueNameIsNotSpecified() + { + var sut1 = new AzureQueueOptions(); + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + + TestOutput.WriteLine(sut2.Message); + + Assert.Equal($"{nameof(AzureQueueOptions.StorageAccountName)} and {nameof(AzureQueueOptions.QueueName)} cannot be null, empty, or consists only of white-space characters. (Expression 'string.IsNullOrWhiteSpace({nameof(AzureQueueOptions.StorageAccountName)}) && string.IsNullOrWhiteSpace({nameof(AzureQueueOptions.QueueName)})')", sut2.Message); + Assert.Equal($"{nameof(AzureQueueOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } - Assert.Equal($"{nameof(AzureQueueOptions.StorageAccountName)} and {nameof(AzureQueueOptions.QueueName)} cannot be null, empty, or consists only of white-space characters. (Expression 'string.IsNullOrWhiteSpace({nameof(AzureQueueOptions.StorageAccountName)}) && string.IsNullOrWhiteSpace({nameof(AzureQueueOptions.QueueName)})')", sut2.Message); - Assert.Equal($"{nameof(AzureQueueOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } + [Fact] + public void Credential_Setters_Should_Be_Mutually_Exclusive() + { + var sut = new AzureQueueOptions + { + SasCredential = new AzureSasCredential("sig") + }; + + Assert.NotNull(sut.SasCredential); + Assert.Null(sut.Credential); + Assert.Null(sut.StorageKeyCredential); + + sut.StorageKeyCredential = new StorageSharedKeyCredential("account", Convert.ToBase64String(new byte[32])); + Assert.NotNull(sut.StorageKeyCredential); + Assert.Null(sut.Credential); + Assert.Null(sut.SasCredential); + + sut.Credential = new DefaultAzureCredential(); + Assert.NotNull(sut.Credential); + Assert.Null(sut.SasCredential); + Assert.Null(sut.StorageKeyCredential); + } - [Fact] - public void Credential_Setters_Should_Be_Mutually_Exclusive() + [Fact] + public void PostConfigureClient_ShouldInvokeCallback() + { + var callbackInvoked = false; + var options = new AzureQueueOptions() { - var sut = new AzureQueueOptions - { - SasCredential = new AzureSasCredential("sig") - }; - - Assert.NotNull(sut.SasCredential); - Assert.Null(sut.Credential); - Assert.Null(sut.StorageKeyCredential); - - sut.StorageKeyCredential = new StorageSharedKeyCredential("account", Convert.ToBase64String(new byte[32])); - Assert.NotNull(sut.StorageKeyCredential); - Assert.Null(sut.Credential); - Assert.Null(sut.SasCredential); - - sut.Credential = new DefaultAzureCredential(); - Assert.NotNull(sut.Credential); - Assert.Null(sut.SasCredential); - Assert.Null(sut.StorageKeyCredential); - } - - [Fact] - public void PostConfigureClient_ShouldInvokeCallback() + ConnectionString = "UseDevelopmentStorage=true", + QueueName = "testqueue" + }; + + var returned = options.PostConfigureClient(c => { - var callbackInvoked = false; - var options = new AzureQueueOptions() - { - ConnectionString = "UseDevelopmentStorage=true", - QueueName = "testqueue" - }; - - var returned = options.PostConfigureClient(c => - { - callbackInvoked = true; - Assert.Equal("testqueue", c.Name); - }); - - _ = new AzureCommandQueue(JsonMarshaller.Default, options); - - Assert.Same(options, returned); - Assert.True(callbackInvoked); - } + callbackInvoked = true; + Assert.Equal("testqueue", c.Name); + }); + + _ = new AzureCommandQueue(JsonMarshaller.Default, options); + + Assert.Same(options, returned); + Assert.True(callbackInvoked); } } diff --git a/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueReceiveOptionsTest.cs b/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueReceiveOptionsTest.cs index d3944872..3f5843a1 100644 --- a/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueReceiveOptionsTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueReceiveOptionsTest.cs @@ -1,74 +1,73 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.QueueStorage +namespace Savvyio.Extensions.QueueStorage; + +public class AzureQueueReceiveOptionsTest : Test { - public class AzureQueueReceiveOptionsTest : Test + public AzureQueueReceiveOptionsTest(ITestOutputHelper output) : base(output) { - public AzureQueueReceiveOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_ShouldInitializeWithDefaultValues() - { - var options = new AzureQueueOptions().ReceiveContext; + [Fact] + public void Constructor_ShouldInitializeWithDefaultValues() + { + var options = new AzureQueueOptions().ReceiveContext; - Assert.Equal(10, options.NumberOfMessagesToTakePerRequest); - Assert.Equal(TimeSpan.FromSeconds(30), options.VisibilityTimeout); - } + Assert.Equal(10, options.NumberOfMessagesToTakePerRequest); + Assert.Equal(TimeSpan.FromSeconds(30), options.VisibilityTimeout); + } - [Fact] - public void NumberOfMessagesToTakePerRequest_ShouldSetAndGetCorrectly() + [Fact] + public void NumberOfMessagesToTakePerRequest_ShouldSetAndGetCorrectly() + { + var options = new AzureQueueOptions() { - var options = new AzureQueueOptions() - { - ReceiveContext = { NumberOfMessagesToTakePerRequest = 5 } - }; + ReceiveContext = { NumberOfMessagesToTakePerRequest = 5 } + }; - Assert.Equal(5, options.ReceiveContext.NumberOfMessagesToTakePerRequest); - } + Assert.Equal(5, options.ReceiveContext.NumberOfMessagesToTakePerRequest); + } - [Fact] - public void NumberOfMessagesToTakePerRequest_ShouldClampToValidRange() + [Fact] + public void NumberOfMessagesToTakePerRequest_ShouldClampToValidRange() + { + var options = new AzureQueueOptions() { - var options = new AzureQueueOptions() - { - ReceiveContext = { NumberOfMessagesToTakePerRequest = 50 } - }; + ReceiveContext = { NumberOfMessagesToTakePerRequest = 50 } + }; - Assert.Equal(AzureQueueOptions.MaxNumberOfMessages, options.ReceiveContext.NumberOfMessagesToTakePerRequest); + Assert.Equal(AzureQueueOptions.MaxNumberOfMessages, options.ReceiveContext.NumberOfMessagesToTakePerRequest); - options.ReceiveContext.NumberOfMessagesToTakePerRequest = -1; + options.ReceiveContext.NumberOfMessagesToTakePerRequest = -1; - Assert.Equal(1, options.ReceiveContext.NumberOfMessagesToTakePerRequest); - } + Assert.Equal(1, options.ReceiveContext.NumberOfMessagesToTakePerRequest); + } - [Fact] - public void VisibilityTimeout_ShouldSetAndGetCorrectly() + [Fact] + public void VisibilityTimeout_ShouldSetAndGetCorrectly() + { + var options = new AzureQueueOptions() { - var options = new AzureQueueOptions() - { - ReceiveContext = { VisibilityTimeout = TimeSpan.FromMinutes(1) } - }; + ReceiveContext = { VisibilityTimeout = TimeSpan.FromMinutes(1) } + }; - Assert.Equal(TimeSpan.FromMinutes(1), options.ReceiveContext.VisibilityTimeout); - } + Assert.Equal(TimeSpan.FromMinutes(1), options.ReceiveContext.VisibilityTimeout); + } - [Fact] - public void VisibilityTimeout_ShouldClampToValidRange() + [Fact] + public void VisibilityTimeout_ShouldClampToValidRange() + { + var options = new AzureQueueOptions() { - var options = new AzureQueueOptions() - { - ReceiveContext = { VisibilityTimeout = TimeSpan.FromDays(10) } - }; + ReceiveContext = { VisibilityTimeout = TimeSpan.FromDays(10) } + }; - Assert.Equal(AzureQueueOptions.MaxVisibilityTimeout, options.ReceiveContext.VisibilityTimeout); + Assert.Equal(AzureQueueOptions.MaxVisibilityTimeout, options.ReceiveContext.VisibilityTimeout); - options.ReceiveContext.VisibilityTimeout = TimeSpan.FromMilliseconds(500); + options.ReceiveContext.VisibilityTimeout = TimeSpan.FromMilliseconds(500); - Assert.Equal(TimeSpan.FromSeconds(1), options.ReceiveContext.VisibilityTimeout); - } + Assert.Equal(TimeSpan.FromSeconds(1), options.ReceiveContext.VisibilityTimeout); } } diff --git a/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueSendOptionsTest.cs b/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueSendOptionsTest.cs index b2547a0e..e7fa2b4c 100644 --- a/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueSendOptionsTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.Tests/AzureQueueSendOptionsTest.cs @@ -1,59 +1,58 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.QueueStorage +namespace Savvyio.Extensions.QueueStorage; + +public class AzureQueueSendOptionsTest : Test { - public class AzureQueueSendOptionsTest : Test + public AzureQueueSendOptionsTest(ITestOutputHelper output) : base(output) { - public AzureQueueSendOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_ShouldInitializeWithDefaultValues() - { - var options = new AzureQueueOptions().SendContext; + [Fact] + public void Constructor_ShouldInitializeWithDefaultValues() + { + var options = new AzureQueueOptions().SendContext; - Assert.Equal(TimeSpan.Zero, options.VisibilityTimeout); - Assert.Equal(TimeSpan.FromDays(7), options.TimeToLive); - } + Assert.Equal(TimeSpan.Zero, options.VisibilityTimeout); + Assert.Equal(TimeSpan.FromDays(7), options.TimeToLive); + } - [Fact] - public void VisibilityTimeout_ShouldSetAndGetCorrectly() + [Fact] + public void VisibilityTimeout_ShouldSetAndGetCorrectly() + { + var options = new AzureQueueOptions() { - var options = new AzureQueueOptions() - { - SendContext = { VisibilityTimeout = TimeSpan.FromMinutes(1) } - }; + SendContext = { VisibilityTimeout = TimeSpan.FromMinutes(1) } + }; - Assert.Equal(TimeSpan.FromMinutes(1), options.SendContext.VisibilityTimeout); - } + Assert.Equal(TimeSpan.FromMinutes(1), options.SendContext.VisibilityTimeout); + } - [Fact] - public void TimeToLive_ShouldSetAndGetCorrectly() + [Fact] + public void TimeToLive_ShouldSetAndGetCorrectly() + { + var options = new AzureQueueOptions() { - var options = new AzureQueueOptions() - { - SendContext = { TimeToLive = TimeSpan.FromHours(1) } - }; + SendContext = { TimeToLive = TimeSpan.FromHours(1) } + }; - Assert.Equal(TimeSpan.FromHours(1), options.SendContext.TimeToLive); - } + Assert.Equal(TimeSpan.FromHours(1), options.SendContext.TimeToLive); + } - [Fact] - public void VisibilityTimeout_ShouldClampToValidRange() + [Fact] + public void VisibilityTimeout_ShouldClampToValidRange() + { + var options = new AzureQueueOptions() { - var options = new AzureQueueOptions() - { - SendContext = { VisibilityTimeout = TimeSpan.FromDays(10) } - }; + SendContext = { VisibilityTimeout = TimeSpan.FromDays(10) } + }; - Assert.Equal(AzureQueueOptions.MaxVisibilityTimeout, options.SendContext.VisibilityTimeout); + Assert.Equal(AzureQueueOptions.MaxVisibilityTimeout, options.SendContext.VisibilityTimeout); - options.SendContext.VisibilityTimeout = TimeSpan.FromMilliseconds(-1); + options.SendContext.VisibilityTimeout = TimeSpan.FromMilliseconds(-1); - Assert.Equal(TimeSpan.Zero, options.SendContext.VisibilityTimeout); - } + Assert.Equal(TimeSpan.Zero, options.SendContext.VisibilityTimeout); } } diff --git a/test/Savvyio.Extensions.QueueStorage.Tests/Commands/AzureCommandQueueTest.cs b/test/Savvyio.Extensions.QueueStorage.Tests/Commands/AzureCommandQueueTest.cs index 787e4879..8466eebe 100644 --- a/test/Savvyio.Extensions.QueueStorage.Tests/Commands/AzureCommandQueueTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.Tests/Commands/AzureCommandQueueTest.cs @@ -17,164 +17,163 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.QueueStorage.Commands +namespace Savvyio.Extensions.QueueStorage.Commands; + +public class AzureCommandQueueTest : Test { - public class AzureCommandQueueTest : Test + private readonly IMarshaller _marshaller; + private readonly AzureQueueOptions _options; + + public AzureCommandQueueTest(ITestOutputHelper output) : base(output) { - private readonly IMarshaller _marshaller; - private readonly AzureQueueOptions _options; + _marshaller = JsonMarshaller.Default; + _options = new AzureQueueOptions { QueueName = "test-queue", StorageAccountName = "test-account" }; + } - public AzureCommandQueueTest(ITestOutputHelper output) : base(output) - { - _marshaller = JsonMarshaller.Default; - _options = new AzureQueueOptions { QueueName = "test-queue", StorageAccountName = "test-account" }; - } + [Fact] + public void Constructor_ShouldThrowArgumentNullException_WhenMarshallerIsNull() + { + Assert.Throws(() => new AzureCommandQueue(null, _options)); + } - [Fact] - public void Constructor_ShouldThrowArgumentNullException_WhenMarshallerIsNull() - { - Assert.Throws(() => new AzureCommandQueue(null, _options)); - } + [Fact] + public void Constructor_ShouldThrowArgumentNullException_WhenOptionsIsNull() + { + Assert.Throws(() => new AzureCommandQueue(_marshaller, null)); + } - [Fact] - public void Constructor_ShouldThrowArgumentNullException_WhenOptionsIsNull() - { - Assert.Throws(() => new AzureCommandQueue(_marshaller, null)); - } + [Fact] + public void Constructor_ShouldThrowArgumentException_WhenOptionsAreInvalid() + { + Assert.Throws(() => new AzureCommandQueue(_marshaller, new AzureQueueOptions { Credential = null })); + } - [Fact] - public void Constructor_ShouldThrowArgumentException_WhenOptionsAreInvalid() - { - Assert.Throws(() => new AzureCommandQueue(_marshaller, new AzureQueueOptions { Credential = null })); - } + [Fact] + public void Constructor_ShouldCreateInstance_WhenArgumentsAreValid() + { + var queue = new AzureCommandQueue(_marshaller, _options); + Assert.NotNull(queue); + } - [Fact] - public void Constructor_ShouldCreateInstance_WhenArgumentsAreValid() - { - var queue = new AzureCommandQueue(_marshaller, _options); - Assert.NotNull(queue); - } + [Fact] + public async Task SendAsync_ShouldThrowArgumentNullException_WhenMessagesAreNull() + { + var queue = new AzureCommandQueue(_marshaller, _options); - [Fact] - public async Task SendAsync_ShouldThrowArgumentNullException_WhenMessagesAreNull() - { - var queue = new AzureCommandQueue(_marshaller, _options); + await Assert.ThrowsAsync(() => queue.SendAsync(null)); + } - await Assert.ThrowsAsync(() => queue.SendAsync(null)); - } + [Fact] + public void ReceiveAsync_ShouldReturnAsyncEnumerable() + { + var queue = new AzureCommandQueue(_marshaller, _options); + + var result = queue.ReceiveAsync(); - [Fact] - public void ReceiveAsync_ShouldReturnAsyncEnumerable() + Assert.NotNull(result); + Assert.IsAssignableFrom>>(result); + } + + [Fact] + public void GetHealthCheckTarget_ShouldReturnQueueServiceClient_ForConnectionString() + { + var queue = new AzureCommandQueue(_marshaller, new AzureQueueOptions { - var queue = new AzureCommandQueue(_marshaller, _options); + ConnectionString = "UseDevelopmentStorage=true", + QueueName = "testqueue" + }); - var result = queue.ReceiveAsync(); + var result = queue.GetHealthCheckTarget(); - Assert.NotNull(result); - Assert.IsAssignableFrom>>(result); - } + Assert.NotNull(result); + Assert.IsType(result); + Assert.StartsWith("http://127.0.0.1:10001/devstoreaccount1", result.Uri.AbsoluteUri, StringComparison.OrdinalIgnoreCase); + } - [Fact] - public void GetHealthCheckTarget_ShouldReturnQueueServiceClient_ForConnectionString() + [Fact] + public void GetHealthCheckTarget_ShouldReturnQueueServiceClient_ForStorageSharedKeyCredential() + { + var queue = new AzureCommandQueue(_marshaller, new AzureQueueOptions { - var queue = new AzureCommandQueue(_marshaller, new AzureQueueOptions - { - ConnectionString = "UseDevelopmentStorage=true", - QueueName = "testqueue" - }); + StorageAccountName = "testaccount", + QueueName = "testqueue", + StorageKeyCredential = new StorageSharedKeyCredential("testaccount", Convert.ToBase64String(new byte[32])) + }); - var result = queue.GetHealthCheckTarget(); + var result = queue.GetHealthCheckTarget(); - Assert.NotNull(result); - Assert.IsType(result); - Assert.StartsWith("http://127.0.0.1:10001/devstoreaccount1", result.Uri.AbsoluteUri, StringComparison.OrdinalIgnoreCase); - } + Assert.Equal(new Uri("https://testaccount.queue.core.windows.net/testqueue"), result.Uri); + } - [Fact] - public void GetHealthCheckTarget_ShouldReturnQueueServiceClient_ForStorageSharedKeyCredential() - { - var queue = new AzureCommandQueue(_marshaller, new AzureQueueOptions - { - StorageAccountName = "testaccount", - QueueName = "testqueue", - StorageKeyCredential = new StorageSharedKeyCredential("testaccount", Convert.ToBase64String(new byte[32])) - }); + [Fact] + public async Task SendMessageAsync_ShouldSendFormattedMessages() + { + var queueClient = new Mock(); + var sent = new List(); + queueClient.Setup(c => c.SendMessageAsync(It.IsAny(), It.IsAny(), It.IsAny(), default)) + .Callback((message, _, _, _) => sent.Add(message)) + .ReturnsAsync(Response.FromValue(QueuesModelFactory.SendReceipt("id", DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(1), "receipt", DateTimeOffset.UtcNow), Mock.Of())); + var queue = new TestAzureQueue(_marshaller, _options, queueClient.Object); - var result = queue.GetHealthCheckTarget(); + await queue.SendAsync([CreateMessage()]); - Assert.Equal(new Uri("https://testaccount.queue.core.windows.net/testqueue"), result.Uri); - } + Assert.Single(sent); + Assert.Contains(".", sent[0], StringComparison.Ordinal); + } - [Fact] - public async Task SendMessageAsync_ShouldSendFormattedMessages() + [Fact] + public async Task ReceiveMessagesAsync_ShouldYieldMessagesAndDeleteOnAcknowledge() + { + var message = CreateMessage(); + var encodedType = message.GetType().ToFullNameIncludingAssemblyName().ToByteArray().ToBase64String(); + var encodedMessage = _marshaller.Serialize(message).ToByteArray().ToBase64String(); + var raw = QueuesModelFactory.QueueMessage("message-id", "pop-receipt", $"{encodedType}.{encodedMessage}", 1, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(1), DateTimeOffset.UtcNow); + var queueClient = new Mock(); + queueClient.Setup(c => c.ReceiveMessagesAsync(It.IsAny(), It.IsAny(), default)) + .ReturnsAsync(Response.FromValue(new[] { raw }, Mock.Of())); + string deletedMessageId = null; + queueClient.Setup(c => c.DeleteMessageAsync(It.IsAny(), It.IsAny(), default)) + .Callback((messageId, _, _) => deletedMessageId = messageId) + .ReturnsAsync(Mock.Of()); + var queue = new TestAzureQueue(_marshaller, _options, queueClient.Object); + + var received = new List>(); + await foreach (var item in queue.ReceiveAsync()) { - var queueClient = new Mock(); - var sent = new List(); - queueClient.Setup(c => c.SendMessageAsync(It.IsAny(), It.IsAny(), It.IsAny(), default)) - .Callback((message, _, _, _) => sent.Add(message)) - .ReturnsAsync(Response.FromValue(QueuesModelFactory.SendReceipt("id", DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(1), "receipt", DateTimeOffset.UtcNow), Mock.Of())); - var queue = new TestAzureQueue(_marshaller, _options, queueClient.Object); + received.Add(item); + await item.AcknowledgeAsync(); + } - await queue.SendAsync([CreateMessage()]); + Assert.Single(received); + Assert.Equal(message.Id, received[0].Id); + Assert.Equal("message-id", deletedMessageId); + } - Assert.Single(sent); - Assert.Contains(".", sent[0], StringComparison.Ordinal); - } + private static IMessage CreateMessage() + { + return new Message( + Guid.NewGuid().ToString("N"), + new Uri("urn:test"), + "test", + new TestCommand()); + } - [Fact] - public async Task ReceiveMessagesAsync_ShouldYieldMessagesAndDeleteOnAcknowledge() + private sealed class TestAzureQueue : AzureQueue + { + public TestAzureQueue(IMarshaller marshaller, AzureQueueOptions options, QueueClient client) : base(marshaller, options, Mock.Of(), client) { - var message = CreateMessage(); - var encodedType = message.GetType().ToFullNameIncludingAssemblyName().ToByteArray().ToBase64String(); - var encodedMessage = _marshaller.Serialize(message).ToByteArray().ToBase64String(); - var raw = QueuesModelFactory.QueueMessage("message-id", "pop-receipt", $"{encodedType}.{encodedMessage}", 1, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(1), DateTimeOffset.UtcNow); - var queueClient = new Mock(); - queueClient.Setup(c => c.ReceiveMessagesAsync(It.IsAny(), It.IsAny(), default)) - .ReturnsAsync(Response.FromValue(new[] { raw }, Mock.Of())); - string deletedMessageId = null; - queueClient.Setup(c => c.DeleteMessageAsync(It.IsAny(), It.IsAny(), default)) - .Callback((messageId, _, _) => deletedMessageId = messageId) - .ReturnsAsync(Mock.Of()); - var queue = new TestAzureQueue(_marshaller, _options, queueClient.Object); - - var received = new List>(); - await foreach (var item in queue.ReceiveAsync()) - { - received.Add(item); - await item.AcknowledgeAsync(); - } - - Assert.Single(received); - Assert.Equal(message.Id, received[0].Id); - Assert.Equal("message-id", deletedMessageId); } - private static IMessage CreateMessage() + public Task SendAsync(IEnumerable> messages) { - return new Message( - Guid.NewGuid().ToString("N"), - new Uri("urn:test"), - "test", - new TestCommand()); + return SendMessageAsync(messages); } - private sealed class TestAzureQueue : AzureQueue + public IAsyncEnumerable> ReceiveAsync() { - public TestAzureQueue(IMarshaller marshaller, AzureQueueOptions options, QueueClient client) : base(marshaller, options, Mock.Of(), client) - { - } - - public Task SendAsync(IEnumerable> messages) - { - return SendMessageAsync(messages); - } - - public IAsyncEnumerable> ReceiveAsync() - { - return ReceiveMessagesAsync(); - } + return ReceiveMessagesAsync(); } - - private record TestCommand : Command { } } + + private record TestCommand : Command { } } diff --git a/test/Savvyio.Extensions.QueueStorage.Tests/EventDriven/AzureEventBusOptionsTest.cs b/test/Savvyio.Extensions.QueueStorage.Tests/EventDriven/AzureEventBusOptionsTest.cs index 05442c07..3d527eea 100644 --- a/test/Savvyio.Extensions.QueueStorage.Tests/EventDriven/AzureEventBusOptionsTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.Tests/EventDriven/AzureEventBusOptionsTest.cs @@ -5,89 +5,88 @@ using Cuemon; using Xunit; -namespace Savvyio.Extensions.QueueStorage.EventDriven +namespace Savvyio.Extensions.QueueStorage.EventDriven; + +public class AzureEventBusOptionsTest : Test { - public class AzureEventBusOptionsTest : Test + public AzureEventBusOptionsTest(ITestOutputHelper output) : base(output) { - public AzureEventBusOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_Should_Set_Defaults() - { - var sut = new AzureEventBusOptions(); + [Fact] + public void Constructor_Should_Set_Defaults() + { + var sut = new AzureEventBusOptions(); - Assert.NotNull(sut.Credential); - Assert.IsType(sut.Credential); - Assert.Null(sut.TopicEndpoint); - Assert.NotNull(sut.Settings); - Assert.Null(sut.KeyCredential); - Assert.Null(sut.SasCredential); - } + Assert.NotNull(sut.Credential); + Assert.IsType(sut.Credential); + Assert.Null(sut.TopicEndpoint); + Assert.NotNull(sut.Settings); + Assert.Null(sut.KeyCredential); + Assert.Null(sut.SasCredential); + } - [Fact] - public void Credential_Setters_Should_Be_Mutually_Exclusive() + [Fact] + public void Credential_Setters_Should_Be_Mutually_Exclusive() + { + var sut = new AzureEventBusOptions { - var sut = new AzureEventBusOptions - { - KeyCredential = new AzureKeyCredential("key") - }; + KeyCredential = new AzureKeyCredential("key") + }; - Assert.NotNull(sut.KeyCredential); - Assert.Null(sut.Credential); - Assert.Null(sut.SasCredential); + Assert.NotNull(sut.KeyCredential); + Assert.Null(sut.Credential); + Assert.Null(sut.SasCredential); - sut.SasCredential = new AzureSasCredential("sig"); - Assert.NotNull(sut.SasCredential); - Assert.Null(sut.Credential); - Assert.Null(sut.KeyCredential); + sut.SasCredential = new AzureSasCredential("sig"); + Assert.NotNull(sut.SasCredential); + Assert.Null(sut.Credential); + Assert.Null(sut.KeyCredential); - sut.Credential = new DefaultAzureCredential(); - Assert.NotNull(sut.Credential); - Assert.Null(sut.KeyCredential); - Assert.Null(sut.SasCredential); - } + sut.Credential = new DefaultAzureCredential(); + Assert.NotNull(sut.Credential); + Assert.Null(sut.KeyCredential); + Assert.Null(sut.SasCredential); + } - [Fact] - public void ValidateOptions_Should_Throw_When_Topic_Endpoint_Is_Missing() - { - var sut1 = new AzureEventBusOptions(); - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + [Fact] + public void ValidateOptions_Should_Throw_When_Topic_Endpoint_Is_Missing() + { + var sut1 = new AzureEventBusOptions(); + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - Assert.Equal($"A {nameof(AzureEventBusOptions.TopicEndpoint)} is required. (Expression '{nameof(AzureEventBusOptions.TopicEndpoint)} == null')", sut2.Message); - Assert.Equal($"{nameof(AzureEventBusOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } + Assert.Equal($"A {nameof(AzureEventBusOptions.TopicEndpoint)} is required. (Expression '{nameof(AzureEventBusOptions.TopicEndpoint)} == null')", sut2.Message); + Assert.Equal($"{nameof(AzureEventBusOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } - [Fact] - public void ValidateOptions_Should_Throw_When_All_Credentials_Are_Missing() + [Fact] + public void ValidateOptions_Should_Throw_When_All_Credentials_Are_Missing() + { + var sut1 = new AzureEventBusOptions { - var sut1 = new AzureEventBusOptions - { - TopicEndpoint = new Uri("https://test.topic") - }; - sut1.Credential = null; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + TopicEndpoint = new Uri("https://test.topic") + }; + sut1.Credential = null; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - Assert.Equal("A credential type has to be specified for Azure authentication. (Expression 'Credential == null && SasCredential == null && KeyCredential == null')", sut2.Message); - Assert.Equal($"{nameof(AzureEventBusOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } + Assert.Equal("A credential type has to be specified for Azure authentication. (Expression 'Credential == null && SasCredential == null && KeyCredential == null')", sut2.Message); + Assert.Equal($"{nameof(AzureEventBusOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } - [Fact] - public void ValidateOptions_Should_Not_Throw_When_Valid() + [Fact] + public void ValidateOptions_Should_Not_Throw_When_Valid() + { + var sut = new AzureEventBusOptions { - var sut = new AzureEventBusOptions - { - TopicEndpoint = new Uri("https://test.topic") - }; + TopicEndpoint = new Uri("https://test.topic") + }; - var exception = Record.Exception(() => sut.ValidateOptions()); + var exception = Record.Exception(() => sut.ValidateOptions()); - Assert.Null(exception); - } + Assert.Null(exception); } } diff --git a/test/Savvyio.Extensions.QueueStorage.Tests/EventDriven/AzureEventBusTest.cs b/test/Savvyio.Extensions.QueueStorage.Tests/EventDriven/AzureEventBusTest.cs index 3d757ae7..ddb6b369 100644 --- a/test/Savvyio.Extensions.QueueStorage.Tests/EventDriven/AzureEventBusTest.cs +++ b/test/Savvyio.Extensions.QueueStorage.Tests/EventDriven/AzureEventBusTest.cs @@ -24,232 +24,231 @@ using Savvyio.Extensions.Text.Json; using Xunit; -namespace Savvyio.Extensions.QueueStorage.EventDriven +namespace Savvyio.Extensions.QueueStorage.EventDriven; + +/// +/// Unit tests for . +/// +public class AzureEventBusTest : Test { - /// - /// Unit tests for . - /// - public class AzureEventBusTest : Test + public AzureEventBusTest(ITestOutputHelper output) : base(output) { - public AzureEventBusTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Ctor_Should_Throw_When_Options_Invalid() - { - var marshaller = new JsonMarshaller(); - var queueOptions = new AzureQueueOptions(); + [Fact] + public void Ctor_Should_Throw_When_Options_Invalid() + { + var marshaller = new JsonMarshaller(); + var queueOptions = new AzureQueueOptions(); - Assert.Throws(() => - new AzureEventBus(marshaller, queueOptions, null)); - } + Assert.Throws(() => + new AzureEventBus(marshaller, queueOptions, null)); + } - [Fact] - public void Ctor_Should_Use_Token_Credential() + [Fact] + public void Ctor_Should_Use_Token_Credential() + { + var marshaller = new JsonMarshaller(); + var queueOptions = new AzureQueueOptions { - var marshaller = new JsonMarshaller(); - var queueOptions = new AzureQueueOptions - { - StorageAccountName = "testaccount", - QueueName = "testqueue", - Credential = new Mock().Object - }; - var eventBusOptions = new AzureEventBusOptions - { - TopicEndpoint = new Uri("https://test.topic"), - Credential = new Mock().Object - }; - - var bus = new AzureEventBus(marshaller, queueOptions, eventBusOptions); - Assert.NotNull(bus); - } - - [Fact] - public void Ctor_Should_Use_Sas_And_Key_Credentials() + StorageAccountName = "testaccount", + QueueName = "testqueue", + Credential = new Mock().Object + }; + var eventBusOptions = new AzureEventBusOptions { - var marshaller = new JsonMarshaller(); - var queueOptions = new AzureQueueOptions - { - StorageAccountName = "testaccount", - QueueName = "testqueue", - SasCredential = new AzureSasCredential("sig") - }; - var eventBusOptions = new AzureEventBusOptions - { - TopicEndpoint = new Uri("https://test.topic"), - KeyCredential = new AzureKeyCredential("key") - }; - - var bus = new AzureEventBus(marshaller, queueOptions, eventBusOptions); - Assert.NotNull(bus); - } + TopicEndpoint = new Uri("https://test.topic"), + Credential = new Mock().Object + }; - [Fact] - public async Task PublishAsync_Should_Throw_When_Message_Null() - { - var bus = CreateSut(); - await Assert.ThrowsAsync(() => bus.PublishAsync(null)); - } + var bus = new AzureEventBus(marshaller, queueOptions, eventBusOptions); + Assert.NotNull(bus); + } - [Fact] - public async Task PublishAsync_Should_Invoke_EventGridClient() + [Fact] + public void Ctor_Should_Use_Sas_And_Key_Credentials() + { + var marshaller = new JsonMarshaller(); + var queueOptions = new AzureQueueOptions { - var marshaller = new JsonMarshaller(); + StorageAccountName = "testaccount", + QueueName = "testqueue", + SasCredential = new AzureSasCredential("sig") + }; + var eventBusOptions = new AzureEventBusOptions + { + TopicEndpoint = new Uri("https://test.topic"), + KeyCredential = new AzureKeyCredential("key") + }; - var eventGridClient = new Mock(); - eventGridClient - .Setup(c => c.SendEventAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(Mock.Of()) - .Verifiable(); + var bus = new AzureEventBus(marshaller, queueOptions, eventBusOptions); + Assert.NotNull(bus); + } - var bus = CreateSut(marshaller: marshaller); - SetPrivateClient(bus, eventGridClient.Object); + [Fact] + public async Task PublishAsync_Should_Throw_When_Message_Null() + { + var bus = CreateSut(); + await Assert.ThrowsAsync(() => bus.PublishAsync(null)); + } - var message = new Message("id", "https://source".ToUri(), "type", new DummyIntegrationEvent(), DateTime.UtcNow); + [Fact] + public async Task PublishAsync_Should_Invoke_EventGridClient() + { + var marshaller = new JsonMarshaller(); - await bus.PublishAsync(message); + var eventGridClient = new Mock(); + eventGridClient + .Setup(c => c.SendEventAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Mock.Of()) + .Verifiable(); - eventGridClient.Verify(); - } + var bus = CreateSut(marshaller: marshaller); + SetPrivateClient(bus, eventGridClient.Object); - [Fact] - public async Task PublishAsync_Should_Add_Signature_When_SignedMessage() - { - var marshaller = new JsonMarshaller(); - - var eventGridClient = new Mock(); - eventGridClient - .Setup(c => c.SendEventAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(Mock.Of()) - .Verifiable(); - - var bus = CreateSut(marshaller: marshaller); - SetPrivateClient(bus, eventGridClient.Object); - - // Use a real signed message implementation with non-null data - var signedMessage = new TestSignedMessage( - id: "id", - source: "https://source".ToUri(), - type: "type", - data: new DummyIntegrationEvent(), - time: DateTime.UtcNow, - signature: "signature" - ); - - await bus.PublishAsync(signedMessage); - - eventGridClient.Verify(); - } + var message = new Message("id", "https://source".ToUri(), "type", new DummyIntegrationEvent(), DateTime.UtcNow); - // Dummy event for test - private record DummyIntegrationEvent : IntegrationEvent - { - } + await bus.PublishAsync(message); - // Helper test double for ISignedMessage - private record TestSignedMessage : Message, ISignedMessage - { - public TestSignedMessage(string id, Uri source, string type, IIntegrationEvent data, DateTime? time, string signature) - : base(id, source, type, data, time) - { - Signature = signature; - } + eventGridClient.Verify(); + } - public string Signature { get; } - } + [Fact] + public async Task PublishAsync_Should_Add_Signature_When_SignedMessage() + { + var marshaller = new JsonMarshaller(); + + var eventGridClient = new Mock(); + eventGridClient + .Setup(c => c.SendEventAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Mock.Of()) + .Verifiable(); + + var bus = CreateSut(marshaller: marshaller); + SetPrivateClient(bus, eventGridClient.Object); + + // Use a real signed message implementation with non-null data + var signedMessage = new TestSignedMessage( + id: "id", + source: "https://source".ToUri(), + type: "type", + data: new DummyIntegrationEvent(), + time: DateTime.UtcNow, + signature: "signature" + ); + + await bus.PublishAsync(signedMessage); + + eventGridClient.Verify(); + } - [Fact] - public async Task SubscribeAsync_Should_Throw_When_Handler_Null() + // Dummy event for test + private record DummyIntegrationEvent : IntegrationEvent + { + } + + // Helper test double for ISignedMessage + private record TestSignedMessage : Message, ISignedMessage + { + public TestSignedMessage(string id, Uri source, string type, IIntegrationEvent data, DateTime? time, string signature) + : base(id, source, type, data, time) { - var bus = CreateSut(); - await Assert.ThrowsAsync(() => bus.SubscribeAsync(null)); + Signature = signature; } - [Fact] - public void GetHealthCheckTarget_Should_Return_Health_Uri() + public string Signature { get; } + } + + [Fact] + public async Task SubscribeAsync_Should_Throw_When_Handler_Null() + { + var bus = CreateSut(); + await Assert.ThrowsAsync(() => bus.SubscribeAsync(null)); + } + + [Fact] + public void GetHealthCheckTarget_Should_Return_Health_Uri() + { + var options = new AzureEventBusOptions { - var options = new AzureEventBusOptions - { - TopicEndpoint = new Uri("https://test.topic") - }; - var bus = CreateSut(eventBusOptions: options); + TopicEndpoint = new Uri("https://test.topic") + }; + var bus = CreateSut(eventBusOptions: options); - var uri = bus.GetHealthCheckTarget(); + var uri = bus.GetHealthCheckTarget(); - Assert.Equal(new Uri("https://test.topic/api/health"), uri); - } + Assert.Equal(new Uri("https://test.topic/api/health"), uri); + } - [Fact] - public async Task SubscribeAsync_ShouldReceiveQueuedCloudEventsAndInvokeHandler() + [Fact] + public async Task SubscribeAsync_ShouldReceiveQueuedCloudEventsAndInvokeHandler() + { + var marshaller = new JsonMarshaller(); + var message = new Message("id", "https://source".ToUri(), "type", new DummyIntegrationEvent(), DateTime.UtcNow); + var cloudEvent = message.ToCloudEvent(); + cloudEvent.Add(AzureEventBus.CloudEventTypeExtensionAttribute, message.GetType().ToFullNameIncludingAssemblyName()); + var raw = QueuesModelFactory.QueueMessage("message-id", "pop-receipt", marshaller.Serialize(cloudEvent).ToEncodedString().ToByteArray().ToBase64String(), 1, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(1), DateTimeOffset.UtcNow); + var queueClient = new Mock(); + queueClient.SetupSequence(c => c.ReceiveMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(Response.FromValue(new[] { raw }, Mock.Of())) + .ReturnsAsync(Response.FromValue(Array.Empty(), Mock.Of())); + queueClient.Setup(c => c.DeleteMessageAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(Mock.Of()); + var bus = new TestableAzureEventBus(marshaller, queueClient.Object); + IMessage received = null; + + await bus.SubscribeAsync((msg, _) => { - var marshaller = new JsonMarshaller(); - var message = new Message("id", "https://source".ToUri(), "type", new DummyIntegrationEvent(), DateTime.UtcNow); - var cloudEvent = message.ToCloudEvent(); - cloudEvent.Add(AzureEventBus.CloudEventTypeExtensionAttribute, message.GetType().ToFullNameIncludingAssemblyName()); - var raw = QueuesModelFactory.QueueMessage("message-id", "pop-receipt", marshaller.Serialize(cloudEvent).ToEncodedString().ToByteArray().ToBase64String(), 1, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(1), DateTimeOffset.UtcNow); - var queueClient = new Mock(); - queueClient.SetupSequence(c => c.ReceiveMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(Response.FromValue(new[] { raw }, Mock.Of())) - .ReturnsAsync(Response.FromValue(Array.Empty(), Mock.Of())); - queueClient.Setup(c => c.DeleteMessageAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(Mock.Of()); - var bus = new TestableAzureEventBus(marshaller, queueClient.Object); - IMessage received = null; - - await bus.SubscribeAsync((msg, _) => - { - received = msg; - return Task.CompletedTask; - }); - - Assert.NotNull(received); - Assert.Equal(message.Id, received.Id); - } + received = msg; + return Task.CompletedTask; + }); + + Assert.NotNull(received); + Assert.Equal(message.Id, received.Id); + } - // --- Helpers --- + // --- Helpers --- - private AzureEventBus CreateSut( - IMarshaller marshaller = null, - AzureEventBusOptions eventBusOptions = null) + private AzureEventBus CreateSut( + IMarshaller marshaller = null, + AzureEventBusOptions eventBusOptions = null) + { + marshaller ??= new JsonMarshaller(); + var queueOptions = new AzureQueueOptions { - marshaller ??= new JsonMarshaller(); - var queueOptions = new AzureQueueOptions - { - StorageAccountName = "testaccount", - QueueName = "testqueue", - Credential = new Mock().Object - }; - eventBusOptions ??= new AzureEventBusOptions - { - TopicEndpoint = new Uri("https://test.topic"), - Credential = new Mock().Object - }; - - return new AzureEventBus(marshaller, queueOptions, eventBusOptions); - } - - private void SetPrivateClient(AzureEventBus bus, EventGridPublisherClient client) + StorageAccountName = "testaccount", + QueueName = "testqueue", + Credential = new Mock().Object + }; + eventBusOptions ??= new AzureEventBusOptions { - var field = typeof(AzureEventBus).GetField("_client", BindingFlags.Instance | BindingFlags.NonPublic); - field.SetValue(bus, client); - } + TopicEndpoint = new Uri("https://test.topic"), + Credential = new Mock().Object + }; + + return new AzureEventBus(marshaller, queueOptions, eventBusOptions); + } - private sealed class TestableAzureEventBus : AzureEventBus + private void SetPrivateClient(AzureEventBus bus, EventGridPublisherClient client) + { + var field = typeof(AzureEventBus).GetField("_client", BindingFlags.Instance | BindingFlags.NonPublic); + field.SetValue(bus, client); + } + + private sealed class TestableAzureEventBus : AzureEventBus + { + public TestableAzureEventBus(IMarshaller marshaller, QueueClient queueClient) : base(marshaller, new AzureQueueOptions + { + StorageAccountName = "testaccount", + QueueName = "testqueue", + Credential = new Mock().Object + }, new AzureEventBusOptions + { + TopicEndpoint = new Uri("https://test.topic"), + Credential = new Mock().Object + }, Mock.Of(), queueClient, Mock.Of()) { - public TestableAzureEventBus(IMarshaller marshaller, QueueClient queueClient) : base(marshaller, new AzureQueueOptions - { - StorageAccountName = "testaccount", - QueueName = "testqueue", - Credential = new Mock().Object - }, new AzureEventBusOptions - { - TopicEndpoint = new Uri("https://test.topic"), - Credential = new Mock().Object - }, Mock.Of(), queueClient, Mock.Of()) - { - } } - } -} \ No newline at end of file + +} diff --git a/test/Savvyio.Extensions.QueueStorage.Tests/Savvyio.Extensions.QueueStorage.Tests.csproj b/test/Savvyio.Extensions.QueueStorage.Tests/Savvyio.Extensions.QueueStorage.Tests.csproj index 1721dc39..824ad05b 100644 --- a/test/Savvyio.Extensions.QueueStorage.Tests/Savvyio.Extensions.QueueStorage.Tests.csproj +++ b/test/Savvyio.Extensions.QueueStorage.Tests/Savvyio.Extensions.QueueStorage.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Extensions.QueueStorage diff --git a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Assets/CreateMemberCommand.cs b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Assets/CreateMemberCommand.cs index 18851b1e..e38b3ac3 100644 --- a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Assets/CreateMemberCommand.cs +++ b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Assets/CreateMemberCommand.cs @@ -1,20 +1,19 @@ -using Savvyio.Commands; +using Savvyio.Commands; -namespace Savvyio.Extensions.RabbitMQ.Assets +namespace Savvyio.Extensions.RabbitMQ.Assets; + +internal record CreateMemberCommand : Command { - internal record CreateMemberCommand : Command + public CreateMemberCommand(string name, byte age, string emailAddress) { - public CreateMemberCommand(string name, byte age, string emailAddress) - { - Name = name; - Age = age; - EmailAddress = emailAddress; - } + Name = name; + Age = age; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public byte Age { get; } + public byte Age { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Assets/MemberCreated.cs b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Assets/MemberCreated.cs index 0958bebc..41b00895 100644 --- a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Assets/MemberCreated.cs +++ b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Assets/MemberCreated.cs @@ -1,17 +1,16 @@ -using Savvyio.EventDriven; +using Savvyio.EventDriven; -namespace Savvyio.Extensions.RabbitMQ.Assets +namespace Savvyio.Extensions.RabbitMQ.Assets; + +public record MemberCreated : IntegrationEvent { - public record MemberCreated : IntegrationEvent + public MemberCreated(string name, string emailAddress) { - public MemberCreated(string name, string emailAddress) - { - Name = name; - EmailAddress = emailAddress; - } + Name = name; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Commands/RabbitMqCommandQueueJsonSerializerContextTest.cs b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Commands/RabbitMqCommandQueueJsonSerializerContextTest.cs index 0deddafc..3455c1b3 100644 --- a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Commands/RabbitMqCommandQueueJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Commands/RabbitMqCommandQueueJsonSerializerContextTest.cs @@ -7,6 +7,7 @@ using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.Collections.Generic; +using Cuemon.Extensions.DependencyInjection; using Cuemon.Extensions.IO; using Microsoft.Extensions.DependencyInjection; using Savvyio.Commands; @@ -19,166 +20,165 @@ using Savvyio.Messaging.Cryptography; using Xunit; -namespace Savvyio.Extensions.RabbitMQ.Commands +namespace Savvyio.Extensions.RabbitMQ.Commands; + +public class RabbitMqCommandQueueJsonSerializerContextTest : Test { - public class RabbitMqCommandQueueJsonSerializerContextTest : Test + public RabbitMqCommandQueueJsonSerializerContextTest(ITestOutputHelper output) : base(output) { - public RabbitMqCommandQueueJsonSerializerContextTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.AutoAcknowledge = true; - o.QueueName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.AutoAcknowledge = true; + o.QueueName = Generate.RandomString(10); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var urn = "https://fancy.io/members".ToUri(); - var message = member.ToMessage(urn, nameof(CreateMemberCommand)); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var urn = "https://fancy.io/members".ToUri(); + var message = member.ToMessage(urn, nameof(CreateMemberCommand)); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - Task.Run(async () => + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); - await queue.SendAsync(message.Yield()).ConfigureAwait(false); + await queue.SendAsync(message.Yield()).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime_Signed() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.QueueName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.QueueName = Generate.RandomString(10); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var urn = "https://fancy.io/members/signed".ToUri(); - var message = member.ToMessage(urn, nameof(CreateMemberCommand)).Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var urn = "https://fancy.io/members/signed".ToUri(); + var message = member.ToMessage(urn, nameof(CreateMemberCommand)).Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - Task.Run(async () => + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); - await queue.SendAsync(message.Yield()).ConfigureAwait(false); + await queue.SendAsync(message.Yield()).ConfigureAwait(false); - var received = (await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10))) as ISignedMessage; - received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var received = (await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10))) as ISignedMessage; + received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_HundredTimes() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_HundredTimes() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.QueueName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.QueueName = Generate.RandomString(10); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var messages = Generate.RangeOf(100, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - return message; - }).ToList(); + var messages = Generate.RangeOf(100, i => + { + var email = $"{Generate.RandomString(5)}@outlook.com"; + var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + return message; + }).ToList(); - var receivedMessages = Channel.CreateUnbounded>(); + var receivedMessages = Channel.CreateUnbounded>(); - var count1 = 0; - Task.Run(async () => + var count1 = 0; + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - count1++; - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + count1++; + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - var count2 = 0; - Task.Run(async () => + var count2 = 0; + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - count2++; - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + count2++; + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); - await queue.SendAsync(messages).ConfigureAwait(false); + await queue.SendAsync(messages).ConfigureAwait(false); - var received = await MessageTestHelper.ReadAsync(receivedMessages.Reader, messages.Count); + var received = await MessageTestHelper.ReadAsync(receivedMessages.Reader, messages.Count); - TestOutput.WriteLine(count1.ToString()); - TestOutput.WriteLine(count2.ToString()); + TestOutput.WriteLine(count1.ToString()); + TestOutput.WriteLine(count2.ToString()); - TestOutput.WriteLine(received.Count.ToString()); - TestOutput.WriteLines(received.Take(10)); + TestOutput.WriteLine(received.Count.ToString()); + TestOutput.WriteLines(received.Take(10)); - Assert.Equivalent(messages.Count, received.Count); - Assert.Equivalent(messages, received); - Assert.Equivalent(messages.Select(message => message.Data), received.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received.Select(message => message.Data.Metadata)); - } + Assert.Equivalent(messages.Count, received.Count); + Assert.Equivalent(messages, received); + Assert.Equivalent(messages.Select(message => message.Data), received.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Commands/RabbitMqCommandQueueNewtonsoftJsonSerializerContextTest.cs b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Commands/RabbitMqCommandQueueNewtonsoftJsonSerializerContextTest.cs index af2fda11..64705dfa 100644 --- a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Commands/RabbitMqCommandQueueNewtonsoftJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/Commands/RabbitMqCommandQueueNewtonsoftJsonSerializerContextTest.cs @@ -7,6 +7,7 @@ using Cuemon; using Cuemon.Extensions; using Cuemon.Extensions.Collections.Generic; +using Cuemon.Extensions.DependencyInjection; using Cuemon.Extensions.IO; using Microsoft.Extensions.DependencyInjection; using Savvyio.Commands; @@ -19,149 +20,148 @@ using Savvyio.Messaging.Cryptography; using Xunit; -namespace Savvyio.Extensions.RabbitMQ.Commands +namespace Savvyio.Extensions.RabbitMQ.Commands; + +public class RabbitMqCommandQueueNewtonsoftJsonSerializerContextTest : Test { - public class RabbitMqCommandQueueNewtonsoftJsonSerializerContextTest : Test + public RabbitMqCommandQueueNewtonsoftJsonSerializerContextTest(ITestOutputHelper output) : base(output) { - public RabbitMqCommandQueueNewtonsoftJsonSerializerContextTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.QueueName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.QueueName = Generate.RandomString(10); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var urn = "https://fancy.io/members".ToUri(); - var message = member.ToMessage(urn, nameof(CreateMemberCommand)); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var urn = "https://fancy.io/members".ToUri(); + var message = member.ToMessage(urn, nameof(CreateMemberCommand)); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - Task.Run(async () => + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); - await queue.SendAsync(message.Yield()).ConfigureAwait(false); + await queue.SendAsync(message.Yield()).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime_Signed() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_OneTime_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.QueueName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.QueueName = Generate.RandomString(10); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var urn = "https://fancy.io/members/signed".ToUri(); - var message = member.ToMessage(urn, nameof(CreateMemberCommand)).Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var urn = "https://fancy.io/members/signed".ToUri(); + var message = member.ToMessage(urn, nameof(CreateMemberCommand)).Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - Task.Run(async () => + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); - await queue.SendAsync(message.Yield()).ConfigureAwait(false); + await queue.SendAsync(message.Yield()).ConfigureAwait(false); - var received = (await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10))) as ISignedMessage; - received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var received = (await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10))) as ISignedMessage; + received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task ReceiveAndSendAsync_CreateMemberCommand_HundredTimes() + [Fact] + public async Task ReceiveAndSendAsync_CreateMemberCommand_HundredTimes() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageQueue().AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageQueue().AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.QueueName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.QueueName = Generate.RandomString(10); }); + }); - var queue = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var queue = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var messages = Generate.RangeOf(100, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - return message; - }).ToList(); + var messages = Generate.RangeOf(100, i => + { + var email = $"{Generate.RandomString(5)}@outlook.com"; + var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + return message; + }).ToList(); - var receivedMessages = Channel.CreateUnbounded>(); + var receivedMessages = Channel.CreateUnbounded>(); - Task.Run(async () => + Task.Run(async () => + { + await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) { - await foreach (var msg in queue.ReceiveAsync().ConfigureAwait(false)) - { - await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); - } - }); + await receivedMessages.Writer.WriteAsync(msg).ConfigureAwait(false); + } + }); - await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForCommandSubscriptionAsync(managed.Host.Services); - await queue.SendAsync(messages).ConfigureAwait(false); + await queue.SendAsync(messages).ConfigureAwait(false); - var received = await MessageTestHelper.ReadAsync(receivedMessages.Reader, messages.Count); + var received = await MessageTestHelper.ReadAsync(receivedMessages.Reader, messages.Count); - TestOutput.WriteLine(received.Count.ToString()); - TestOutput.WriteLines(received.Take(10)); + TestOutput.WriteLine(received.Count.ToString()); + TestOutput.WriteLines(received.Take(10)); - Assert.Equivalent(messages.Count, received.Count); - Assert.Equivalent(messages, received); - Assert.Equivalent(messages.Select(message => message.Data), received.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received.Select(message => message.Data.Metadata)); - } + Assert.Equivalent(messages.Count, received.Count); + Assert.Equivalent(messages, received); + Assert.Equivalent(messages.Select(message => message.Data), received.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/EventDriven/RabbitMqEventBusJsonSerializerContextTest.cs b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/EventDriven/RabbitMqEventBusJsonSerializerContextTest.cs index 8a086c8b..493e9036 100644 --- a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/EventDriven/RabbitMqEventBusJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/EventDriven/RabbitMqEventBusJsonSerializerContextTest.cs @@ -18,283 +18,283 @@ using System.Linq; using System.Threading.Channels; using System.Threading.Tasks; +using Cuemon.Extensions.DependencyInjection; using Cuemon.Extensions.IO; using Xunit; -namespace Savvyio.Extensions.RabbitMQ.EventDriven +namespace Savvyio.Extensions.RabbitMQ.EventDriven; + +public class RabbitMqEventBusJsonSerializerContextTest : Test { - public class RabbitMqEventBusJsonSerializerContextTest : Test + public RabbitMqEventBusJsonSerializerContextTest(ITestOutputHelper output) : base(output) { - public RabbitMqEventBusJsonSerializerContextTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event"); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event"); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); - await bus.PublishAsync(message).ConfigureAwait(false); + await bus.PublishAsync(message).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_Signed() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed").Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed").Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); - await bus.PublishAsync(message).ConfigureAwait(false); + await bus.PublishAsync(message).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedMessage; - received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedMessage; + received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Signature, received.Signature); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Signature, received.Signature); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-cloud-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.cloud-event").ToCloudEvent(); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-cloud-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.cloud-event").ToCloudEvent(); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); - await bus.PublishAsync(message); + await bus.PublishAsync(message); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ICloudEvent; + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ICloudEvent; - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Specversion, received.Specversion); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Specversion, received.Specversion); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); - await bus.PublishAsync(message); + await bus.PublishAsync(message); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedCloudEvent; + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedCloudEvent; - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Specversion, received.Specversion); - Assert.Equivalent(message.Signature, received.Signature); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Specversion, received.Specversion); + Assert.Equivalent(message.Signature, received.Signature); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_HundredTimes() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_HundredTimes() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); - - var messages = Generate.RangeOf(100, _ => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}"); - }).ToList(); - var receivedMessages1 = Channel.CreateUnbounded>(); - var receivedMessages2 = Channel.CreateUnbounded>(); - - var handlerInvocations1 = 0; - Task.Run(async () => - { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations1++; - await receivedMessages1.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var handlerInvocations2 = 0; - Task.Run(async () => + var messages = Generate.RangeOf(100, _ => + { + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}"); + }).ToList(); + var receivedMessages1 = Channel.CreateUnbounded>(); + var receivedMessages2 = Channel.CreateUnbounded>(); + + var handlerInvocations1 = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations2++; - await receivedMessages2.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); - - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + handlerInvocations1++; + await receivedMessages1.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await ParallelFactory.ForEachAsync(messages, (message, token) => + var handlerInvocations2 = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - return bus.PublishAsync(message, o => o.CancellationToken = token); + handlerInvocations2++; + await receivedMessages2.Writer.WriteAsync(msg, token).ConfigureAwait(false); }).ConfigureAwait(false); + }); - var received1Task = MessageTestHelper.ReadAsync(receivedMessages1.Reader, messages.Count); - var received2Task = MessageTestHelper.ReadAsync(receivedMessages2.Reader, messages.Count); - await Task.WhenAll(received1Task, received2Task); - var received1 = await received1Task; - var received2 = await received2Task; - - TestOutput.WriteLine(received1.Count.ToString()); - TestOutput.WriteLines(received1.Take(10).Select(m => marshaller.Serialize(m).ToEncodedString())); - - Assert.Equal(100, handlerInvocations1); - Assert.Equal(100, handlerInvocations2); - Assert.Equivalent(messages.Count, received1.Count); - Assert.Equivalent(messages, received1); - Assert.Equivalent(messages, received2); - Assert.Equivalent(messages.Select(message => message.Data), received1.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received1.Select(message => message.Data.Metadata)); - Assert.Equivalent(messages.Select(message => message.Data), received2.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received2.Select(message => message.Data.Metadata)); - } + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + + await ParallelFactory.ForEachAsync(messages, (message, token) => + { + return bus.PublishAsync(message, o => o.CancellationToken = token); + }).ConfigureAwait(false); + + var received1Task = MessageTestHelper.ReadAsync(receivedMessages1.Reader, messages.Count); + var received2Task = MessageTestHelper.ReadAsync(receivedMessages2.Reader, messages.Count); + await Task.WhenAll(received1Task, received2Task); + var received1 = await received1Task; + var received2 = await received2Task; + + TestOutput.WriteLine(received1.Count.ToString()); + TestOutput.WriteLines(received1.Take(10).Select(m => marshaller.Serialize(m).ToEncodedString())); + + Assert.Equal(100, handlerInvocations1); + Assert.Equal(100, handlerInvocations2); + Assert.Equivalent(messages.Count, received1.Count); + Assert.Equivalent(messages, received1); + Assert.Equivalent(messages, received2); + Assert.Equivalent(messages.Select(message => message.Data), received1.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received1.Select(message => message.Data.Metadata)); + Assert.Equivalent(messages.Select(message => message.Data), received2.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received2.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/EventDriven/RabbitMqEventBusNewtonsoftJsonSerializerContextTest.cs b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/EventDriven/RabbitMqEventBusNewtonsoftJsonSerializerContextTest.cs index f6338a84..519828b5 100644 --- a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/EventDriven/RabbitMqEventBusNewtonsoftJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/EventDriven/RabbitMqEventBusNewtonsoftJsonSerializerContextTest.cs @@ -19,282 +19,282 @@ using System.Linq; using System.Threading.Channels; using System.Threading.Tasks; +using Cuemon.Extensions.DependencyInjection; using Xunit; -namespace Savvyio.Extensions.RabbitMQ.EventDriven +namespace Savvyio.Extensions.RabbitMQ.EventDriven; + +public class RabbitMqEventBusNewtonsoftJsonSerializerContextTest : Test { - public class RabbitMqEventBusNewtonsoftJsonSerializerContextTest : Test + public RabbitMqEventBusNewtonsoftJsonSerializerContextTest(ITestOutputHelper output) : base(output) { - public RabbitMqEventBusNewtonsoftJsonSerializerContextTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event"); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event"); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); - await bus.PublishAsync(message).ConfigureAwait(false); + await bus.PublishAsync(message).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)); - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_Signed() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed").Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed").Sign(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); - await bus.PublishAsync(message).ConfigureAwait(false); + await bus.PublishAsync(message).ConfigureAwait(false); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedMessage; - received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedMessage; + received?.CheckSignature(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Signature, received.Signature); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Signature, received.Signature); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.cloud-event").ToCloudEvent(); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.cloud-event").ToCloudEvent(); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); - await bus.PublishAsync(message); + await bus.PublishAsync(message); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ICloudEvent; + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ICloudEvent; - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Specversion, received.Specversion); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Specversion, received.Specversion); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var member = new MemberCreated("John Doe", "jd@outlook.com"); - var urn = "urn:member-events-one".ToUri(); - var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var receivedMessages = Channel.CreateUnbounded>(); + var member = new MemberCreated("John Doe", "jd@outlook.com"); + var urn = "urn:member-events-one".ToUri(); + var message = member.ToMessage(urn, $"{nameof(MemberCreated)}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + var receivedMessages = Channel.CreateUnbounded>(); - TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); - TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(urn).ToEncodedString()); + TestOutput.WriteLine(marshaller.Serialize(message).ToEncodedString()); - var handlerInvocations = 0; - Task.Run(async () => + var handlerInvocations = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations++; - await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + handlerInvocations++; + await receivedMessages.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); - await bus.PublishAsync(message); + await bus.PublishAsync(message); - var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedCloudEvent; + var received = await receivedMessages.Reader.ReadAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(10)) as ISignedCloudEvent; - Assert.Equal(1, handlerInvocations); - Assert.Equivalent(message.Data, received.Data); - Assert.Equivalent(message.Time, received.Time); - Assert.Equivalent(message.Source, received.Source); - Assert.Equivalent(message.Id, received.Id); - Assert.Equivalent(message.Type, received.Type); - Assert.Equivalent(message.Specversion, received.Specversion); - Assert.Equivalent(message.Signature, received.Signature); - } + Assert.Equal(1, handlerInvocations); + Assert.Equivalent(message.Data, received.Data); + Assert.Equivalent(message.Time, received.Time); + Assert.Equivalent(message.Source, received.Source); + Assert.Equivalent(message.Id, received.Id); + Assert.Equivalent(message.Type, received.Type); + Assert.Equivalent(message.Specversion, received.Specversion); + Assert.Equivalent(message.Signature, received.Signature); + } - [Fact] - public async Task SubscribeAndPublishAsync_MemberCreated_HundredTimes() + [Fact] + public async Task SubscribeAndPublishAsync_MemberCreated_HundredTimes() + { + var managed = HostTestFactory.Create(services => { - var managed = HostTestFactory.Create(services => + services.AddMarshaller(); + services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => { - services.AddMarshaller(); - services.AddMessageBus(o => o.Lifetime = ServiceLifetime.Singleton).AddConfiguredOptions(o => - { - o.AmqpUrl = RabbitMqTestEnvironment.Url; - o.ExchangeName = Generate.RandomString(10); - }); + o.AmqpUrl = RabbitMqTestEnvironment.Url; + o.ExchangeName = Generate.RandomString(10); }); + }); - var bus = managed.Host.Services.GetRequiredService(); - var marshaller = managed.Host.Services.GetRequiredService(); - - var messages = Generate.RangeOf(100, _ => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}"); - }).ToList(); - var receivedMessages1 = Channel.CreateUnbounded>(); - var receivedMessages2 = Channel.CreateUnbounded>(); - - var handlerInvocations1 = 0; - Task.Run(async () => - { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations1++; - await receivedMessages1.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); + var bus = managed.Host.Services.GetRequiredService(); + var marshaller = managed.Host.Services.GetRequiredService(); - var handlerInvocations2 = 0; - Task.Run(async () => + var messages = Generate.RangeOf(100, _ => + { + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new MemberCreated(Generate.RandomString(10), email).ToMessage("urn:member-events-many".ToUri(), $"{nameof(MemberCreated)}"); + }).ToList(); + var receivedMessages1 = Channel.CreateUnbounded>(); + var receivedMessages2 = Channel.CreateUnbounded>(); + + var handlerInvocations1 = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - await bus.SubscribeAsync(async (msg, token) => - { - handlerInvocations2++; - await receivedMessages2.Writer.WriteAsync(msg, token).ConfigureAwait(false); - }).ConfigureAwait(false); - }); - - await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + handlerInvocations1++; + await receivedMessages1.Writer.WriteAsync(msg, token).ConfigureAwait(false); + }).ConfigureAwait(false); + }); - await ParallelFactory.ForEachAsync(messages, (message, token) => + var handlerInvocations2 = 0; + Task.Run(async () => + { + await bus.SubscribeAsync(async (msg, token) => { - return bus.PublishAsync(message, o => o.CancellationToken = token); + handlerInvocations2++; + await receivedMessages2.Writer.WriteAsync(msg, token).ConfigureAwait(false); }).ConfigureAwait(false); + }); - var received1Task = MessageTestHelper.ReadAsync(receivedMessages1.Reader, messages.Count); - var received2Task = MessageTestHelper.ReadAsync(receivedMessages2.Reader, messages.Count); - await Task.WhenAll(received1Task, received2Task); - var received1 = await received1Task; - var received2 = await received2Task; - - TestOutput.WriteLine(received1.Count.ToString()); - TestOutput.WriteLines(received1.Take(10)); - - Assert.Equal(100, handlerInvocations1); - Assert.Equal(100, handlerInvocations2); - Assert.Equivalent(messages.Count, received1.Count); - Assert.Equivalent(messages, received1); - Assert.Equivalent(messages, received2); - Assert.Equivalent(messages.Select(message => message.Data), received1.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received1.Select(message => message.Data.Metadata)); - Assert.Equivalent(messages.Select(message => message.Data), received2.Select(message => message.Data)); - Assert.Equivalent(messages.Select(message => message.Data.Metadata), received2.Select(message => message.Data.Metadata)); - } + await RabbitMqTestEnvironment.WaitForEventSubscriptionAsync(managed.Host.Services); + + await ParallelFactory.ForEachAsync(messages, (message, token) => + { + return bus.PublishAsync(message, o => o.CancellationToken = token); + }).ConfigureAwait(false); + + var received1Task = MessageTestHelper.ReadAsync(receivedMessages1.Reader, messages.Count); + var received2Task = MessageTestHelper.ReadAsync(receivedMessages2.Reader, messages.Count); + await Task.WhenAll(received1Task, received2Task); + var received1 = await received1Task; + var received2 = await received2Task; + + TestOutput.WriteLine(received1.Count.ToString()); + TestOutput.WriteLines(received1.Take(10)); + + Assert.Equal(100, handlerInvocations1); + Assert.Equal(100, handlerInvocations2); + Assert.Equivalent(messages.Count, received1.Count); + Assert.Equivalent(messages, received1); + Assert.Equivalent(messages, received2); + Assert.Equivalent(messages.Select(message => message.Data), received1.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received1.Select(message => message.Data.Metadata)); + Assert.Equivalent(messages.Select(message => message.Data), received2.Select(message => message.Data)); + Assert.Equivalent(messages.Select(message => message.Data.Metadata), received2.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/MessageTestHelper.cs b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/MessageTestHelper.cs index 341599d2..fd711fb3 100644 --- a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/MessageTestHelper.cs +++ b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/MessageTestHelper.cs @@ -4,19 +4,18 @@ using System.Threading.Channels; using System.Threading.Tasks; -namespace Savvyio.Extensions.RabbitMQ +namespace Savvyio.Extensions.RabbitMQ; + +internal static class MessageTestHelper { - internal static class MessageTestHelper + internal static async Task> ReadAsync(ChannelReader reader, int count) { - internal static async Task> ReadAsync(ChannelReader reader, int count) + using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var messages = new List(count); + while (messages.Count < count) { - using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - var messages = new List(count); - while (messages.Count < count) - { - messages.Add(await reader.ReadAsync(timeout.Token)); - } - return messages; + messages.Add(await reader.ReadAsync(timeout.Token)); } + return messages; } } diff --git a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/RabbitMqTestEnvironment.cs b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/RabbitMqTestEnvironment.cs index 2d721842..57c4dcee 100644 --- a/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/RabbitMqTestEnvironment.cs +++ b/test/Savvyio.Extensions.RabbitMQ.FunctionalTests/RabbitMqTestEnvironment.cs @@ -10,77 +10,76 @@ using Savvyio.Extensions.RabbitMQ.Commands; using Savvyio.Extensions.RabbitMQ.EventDriven; -namespace Savvyio.Extensions.RabbitMQ +namespace Savvyio.Extensions.RabbitMQ; + +internal static class RabbitMqTestEnvironment { - internal static class RabbitMqTestEnvironment - { - internal static Uri Url { get; } = new(Environment.GetEnvironmentVariable("SAVVYIO_RABBITMQ_URL") ?? "amqp://localhost:5672"); + internal static Uri Url { get; } = new(Environment.GetEnvironmentVariable("SAVVYIO_RABBITMQ_URL") ?? "amqp://localhost:5672"); - private static Uri ManagementUrl { get; } = CreateManagementUrl(); + private static Uri ManagementUrl { get; } = CreateManagementUrl(); - internal static Task WaitForCommandSubscriptionAsync(IServiceProvider services) - { - var queueName = services.GetRequiredService().QueueName; - return WaitUntilAsync("api/queues", root => root.EnumerateArray().Any(queue => - queue.TryGetProperty("name", out var name) && name.GetString() == queueName && - queue.TryGetProperty("consumers", out var consumers) && consumers.GetInt32() > 0), queueName); - } + internal static Task WaitForCommandSubscriptionAsync(IServiceProvider services) + { + var queueName = services.GetRequiredService().QueueName; + return WaitUntilAsync("api/queues", root => root.EnumerateArray().Any(queue => + queue.TryGetProperty("name", out var name) && name.GetString() == queueName && + queue.TryGetProperty("consumers", out var consumers) && consumers.GetInt32() > 0), queueName); + } - internal static Task WaitForEventSubscriptionAsync(IServiceProvider services) - { - var exchangeName = services.GetRequiredService().ExchangeName; - return WaitUntilAsync("api/bindings", root => root.EnumerateArray().Any(binding => - binding.TryGetProperty("source", out var source) && source.GetString() == exchangeName), exchangeName); - } + internal static Task WaitForEventSubscriptionAsync(IServiceProvider services) + { + var exchangeName = services.GetRequiredService().ExchangeName; + return WaitUntilAsync("api/bindings", root => root.EnumerateArray().Any(binding => + binding.TryGetProperty("source", out var source) && source.GetString() == exchangeName), exchangeName); + } + + private static Uri CreateManagementUrl() + { + var configured = Environment.GetEnvironmentVariable("SAVVYIO_RABBITMQ_MANAGEMENT_URL"); + if (!string.IsNullOrWhiteSpace(configured)) { return new Uri(configured.TrimEnd('/') + "/"); } - private static Uri CreateManagementUrl() + return new UriBuilder(Url) { - var configured = Environment.GetEnvironmentVariable("SAVVYIO_RABBITMQ_MANAGEMENT_URL"); - if (!string.IsNullOrWhiteSpace(configured)) { return new Uri(configured.TrimEnd('/') + "/"); } + Scheme = Uri.UriSchemeHttp, + Port = 15672, + Path = "/", + UserName = "", + Password = "" + }.Uri; + } - return new UriBuilder(Url) - { - Scheme = Uri.UriSchemeHttp, - Port = 15672, - Path = "/", - UserName = "", - Password = "" - }.Uri; - } + private static async Task WaitUntilAsync(string relativePath, Func predicate, string resourceName) + { + using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + using var client = new HttpClient { BaseAddress = ManagementUrl }; + var credentials = string.IsNullOrWhiteSpace(Url.UserInfo) ? "guest:guest" : Uri.UnescapeDataString(Url.UserInfo); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials))); - private static async Task WaitUntilAsync(string relativePath, Func predicate, string resourceName) + try { - using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - using var client = new HttpClient { BaseAddress = ManagementUrl }; - var credentials = string.IsNullOrWhiteSpace(Url.UserInfo) ? "guest:guest" : Uri.UnescapeDataString(Url.UserInfo); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials))); - - try + while (!timeout.IsCancellationRequested) { - while (!timeout.IsCancellationRequested) + try { - try - { - using var response = await client.GetAsync(relativePath, timeout.Token).ConfigureAwait(false); - if (response.IsSuccessStatusCode) - { - await using var content = await response.Content.ReadAsStreamAsync(timeout.Token).ConfigureAwait(false); - using var document = await JsonDocument.ParseAsync(content, cancellationToken: timeout.Token).ConfigureAwait(false); - if (predicate(document.RootElement)) { return; } - } - } - catch (HttpRequestException) when (!timeout.IsCancellationRequested) + using var response = await client.GetAsync(relativePath, timeout.Token).ConfigureAwait(false); + if (response.IsSuccessStatusCode) { + await using var content = await response.Content.ReadAsStreamAsync(timeout.Token).ConfigureAwait(false); + using var document = await JsonDocument.ParseAsync(content, cancellationToken: timeout.Token).ConfigureAwait(false); + if (predicate(document.RootElement)) { return; } } - - await Task.Delay(50, timeout.Token).ConfigureAwait(false); } - } - catch (OperationCanceledException) when (timeout.IsCancellationRequested) - { - } + catch (HttpRequestException) when (!timeout.IsCancellationRequested) + { + } - throw new TimeoutException($"RabbitMQ subscription for '{resourceName}' was not ready within 10 seconds."); + await Task.Delay(50, timeout.Token).ConfigureAwait(false); + } } + catch (OperationCanceledException) when (timeout.IsCancellationRequested) + { + } + + throw new TimeoutException($"RabbitMQ subscription for '{resourceName}' was not ready within 10 seconds."); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.Tests/Commands/RabbitMqCommandQueueOptionsTest.cs b/test/Savvyio.Extensions.RabbitMQ.Tests/Commands/RabbitMqCommandQueueOptionsTest.cs index 99110cda..6323f632 100644 --- a/test/Savvyio.Extensions.RabbitMQ.Tests/Commands/RabbitMqCommandQueueOptionsTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.Tests/Commands/RabbitMqCommandQueueOptionsTest.cs @@ -1,61 +1,60 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.RabbitMQ.Commands +namespace Savvyio.Extensions.RabbitMQ.Commands; + +public class RabbitMqCommandQueueOptionsTest : Test { - public class RabbitMqCommandQueueOptionsTest : Test + public RabbitMqCommandQueueOptionsTest(ITestOutputHelper output) : base(output) { - public RabbitMqCommandQueueOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_Should_Set_Defaults() - { - var options = new RabbitMqCommandQueueOptions(); - - Assert.Null(options.QueueName); - Assert.False(options.AutoAcknowledge); - Assert.True(options.Durable); - Assert.False(options.Exclusive); - Assert.False(options.AutoDelete); - Assert.NotNull(options.AmqpUrl); - } - - [Fact] - public void ValidateOptions_Should_Throw_When_QueueName_Is_Null_Or_Empty() - { - var options = new RabbitMqCommandQueueOptions(); + [Fact] + public void Constructor_Should_Set_Defaults() + { + var options = new RabbitMqCommandQueueOptions(); + + Assert.Null(options.QueueName); + Assert.False(options.AutoAcknowledge); + Assert.True(options.Durable); + Assert.False(options.Exclusive); + Assert.False(options.AutoDelete); + Assert.NotNull(options.AmqpUrl); + } - Assert.Throws(() => options.ValidateOptions()); + [Fact] + public void ValidateOptions_Should_Throw_When_QueueName_Is_Null_Or_Empty() + { + var options = new RabbitMqCommandQueueOptions(); + + Assert.Throws(() => options.ValidateOptions()); - options.QueueName = ""; - Assert.Throws(() => options.ValidateOptions()); - } + options.QueueName = ""; + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_Should_Also_Throw_When_AmqpUrl_Is_Null() + [Fact] + public void ValidateOptions_Should_Also_Throw_When_AmqpUrl_Is_Null() + { + var options = new RabbitMqCommandQueueOptions { - var options = new RabbitMqCommandQueueOptions - { - QueueName = "test-queue", - AmqpUrl = null - }; + QueueName = "test-queue", + AmqpUrl = null + }; - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_Should_Not_Throw_When_QueueName_Is_Set() + [Fact] + public void ValidateOptions_Should_Not_Throw_When_QueueName_Is_Set() + { + var options = new RabbitMqCommandQueueOptions { - var options = new RabbitMqCommandQueueOptions - { - QueueName = "test-queue" - }; - - var exception = Record.Exception(() => options.ValidateOptions()); - Assert.Null(exception); - } + QueueName = "test-queue" + }; + + var exception = Record.Exception(() => options.ValidateOptions()); + Assert.Null(exception); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.Tests/Commands/RabbitMqCommandQueueTest.cs b/test/Savvyio.Extensions.RabbitMQ.Tests/Commands/RabbitMqCommandQueueTest.cs index 747c099d..0b8312c9 100644 --- a/test/Savvyio.Extensions.RabbitMQ.Tests/Commands/RabbitMqCommandQueueTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.Tests/Commands/RabbitMqCommandQueueTest.cs @@ -14,154 +14,153 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.RabbitMQ.Commands +namespace Savvyio.Extensions.RabbitMQ.Commands; + +public class RabbitMqCommandQueueTest : Test { - public class RabbitMqCommandQueueTest : Test + public RabbitMqCommandQueueTest(ITestOutputHelper output) : base(output) { - public RabbitMqCommandQueueTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Constructor_Should_Throw_When_Marshaller_Is_Null() - { - Assert.Throws(() => new RabbitMqCommandQueue(null, CreateOptions())); - } - - [Fact] - public void Constructor_Should_Throw_When_Options_Are_Invalid() - { - Assert.Throws(() => new RabbitMqCommandQueue(JsonMarshaller.Default, new RabbitMqCommandQueueOptions())); - } - - [Fact] - public async Task SendAsync_Should_Declare_Queue_And_Publish_Each_Message() - { - var options = CreateOptions(o => o.Persistent = true); - var channel = new Mock(); - BasicProperties publishedProperties = null; - - channel.Setup(c => c.QueueDeclareAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new QueueDeclareOk(options.QueueName, 0, 0)); - channel.Setup(c => c.BasicPublishAsync("", options.QueueName, true, It.IsAny(), It.IsAny>(), It.IsAny())) - .Callback, CancellationToken>((_, _, _, properties, _, _) => publishedProperties = properties) - .Returns(ValueTask.CompletedTask); - - var sut = CreateSut(channel.Object, options); - var messages = new IMessage[] - { - new Message("id-1", new Uri("urn:command:test"), "command.test", new TestCommand(), DateTime.UtcNow), - new Message("id-2", new Uri("urn:command:test"), "command.test", new TestCommand(), DateTime.UtcNow) - }; + } - await sut.SendAsync(messages); + [Fact] + public void Constructor_Should_Throw_When_Marshaller_Is_Null() + { + Assert.Throws(() => new RabbitMqCommandQueue(null, CreateOptions())); + } - channel.Verify(c => c.QueueDeclareAsync(options.QueueName, options.Durable, options.Exclusive, options.AutoDelete, null, false, false, It.IsAny()), Times.Once); - channel.Verify(c => c.BasicPublishAsync("", options.QueueName, true, It.IsAny(), It.IsAny>(), It.IsAny()), Times.Exactly(2)); - Assert.True(publishedProperties.Persistent); - Assert.Equal($"{typeof(Message).FullName}, {typeof(Message).Assembly.GetName().Name}", publishedProperties.Headers["type"]); - } + [Fact] + public void Constructor_Should_Throw_When_Options_Are_Invalid() + { + Assert.Throws(() => new RabbitMqCommandQueue(JsonMarshaller.Default, new RabbitMqCommandQueueOptions())); + } - [Fact] - public async Task ReceiveAsync_Should_Acknowledge_Message_When_Requested() + [Fact] + public async Task SendAsync_Should_Declare_Queue_And_Publish_Each_Message() + { + var options = CreateOptions(o => o.Persistent = true); + var channel = new Mock(); + BasicProperties publishedProperties = null; + + channel.Setup(c => c.QueueDeclareAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new QueueDeclareOk(options.QueueName, 0, 0)); + channel.Setup(c => c.BasicPublishAsync("", options.QueueName, true, It.IsAny(), It.IsAny>(), It.IsAny())) + .Callback, CancellationToken>((_, _, _, properties, _, _) => publishedProperties = properties) + .Returns(ValueTask.CompletedTask); + + var sut = CreateSut(channel.Object, options); + var messages = new IMessage[] { - var options = CreateOptions(); - var channel = new Mock(); - var consumerSource = new TaskCompletionSource(); - - channel.Setup(c => c.QueueDeclareAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new QueueDeclareOk(options.QueueName, 0, 0)); - channel.Setup(c => c.BasicConsumeAsync(options.QueueName, false, string.Empty, false, false, null, It.IsAny(), It.IsAny())) - .Callback, IAsyncBasicConsumer, CancellationToken>((_, _, _, _, _, _, consumer, _) => consumerSource.TrySetResult(consumer)) - .ReturnsAsync("consumer-tag"); - channel.Setup(c => c.BasicAckAsync(77UL, false, It.IsAny())).Returns(ValueTask.CompletedTask); + new Message("id-1", new Uri("urn:command:test"), "command.test", new TestCommand(), DateTime.UtcNow), + new Message("id-2", new Uri("urn:command:test"), "command.test", new TestCommand(), DateTime.UtcNow) + }; - var sut = CreateSut(channel.Object, options); - await using var enumerator = sut.ReceiveAsync().GetAsyncEnumerator(); - var moveNext = enumerator.MoveNextAsync().AsTask(); - var consumer = await consumerSource.Task.ConfigureAwait(false); + await sut.SendAsync(messages); - await PublishDeliveredMessageAsync((AsyncEventingBasicConsumer)consumer, new Message("id-1", new Uri("urn:command:test"), "command.test", new TestCommand(), DateTime.UtcNow), 77UL, options.QueueName).ConfigureAwait(false); + channel.Verify(c => c.QueueDeclareAsync(options.QueueName, options.Durable, options.Exclusive, options.AutoDelete, null, false, false, It.IsAny()), Times.Once); + channel.Verify(c => c.BasicPublishAsync("", options.QueueName, true, It.IsAny(), It.IsAny>(), It.IsAny()), Times.Exactly(2)); + Assert.True(publishedProperties.Persistent); + Assert.Equal($"{typeof(Message).FullName}, {typeof(Message).Assembly.GetName().Name}", publishedProperties.Headers["type"]); + } - Assert.True(await moveNext.ConfigureAwait(false)); - var message = enumerator.Current; + [Fact] + public async Task ReceiveAsync_Should_Acknowledge_Message_When_Requested() + { + var options = CreateOptions(); + var channel = new Mock(); + var consumerSource = new TaskCompletionSource(); - Assert.Equal(77UL, message.Properties[nameof(BasicDeliverEventArgs.DeliveryTag)]); - Assert.Equal(options.QueueName, message.Properties[nameof(QueueDeclareOk.QueueName)]); + channel.Setup(c => c.QueueDeclareAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new QueueDeclareOk(options.QueueName, 0, 0)); + channel.Setup(c => c.BasicConsumeAsync(options.QueueName, false, string.Empty, false, false, null, It.IsAny(), It.IsAny())) + .Callback, IAsyncBasicConsumer, CancellationToken>((_, _, _, _, _, _, consumer, _) => consumerSource.TrySetResult(consumer)) + .ReturnsAsync("consumer-tag"); + channel.Setup(c => c.BasicAckAsync(77UL, false, It.IsAny())).Returns(ValueTask.CompletedTask); - await message.AcknowledgeAsync().ConfigureAwait(false); + var sut = CreateSut(channel.Object, options); + await using var enumerator = sut.ReceiveAsync().GetAsyncEnumerator(); + var moveNext = enumerator.MoveNextAsync().AsTask(); + var consumer = await consumerSource.Task.ConfigureAwait(false); - channel.Verify(c => c.BasicAckAsync(77UL, false, It.IsAny()), Times.Once); - channel.Verify(c => c.QueueDeclareAsync(options.QueueName, options.Durable, options.Exclusive, options.AutoDelete, null, false, false, It.IsAny()), Times.Exactly(2)); - } + await PublishDeliveredMessageAsync((AsyncEventingBasicConsumer)consumer, new Message("id-1", new Uri("urn:command:test"), "command.test", new TestCommand(), DateTime.UtcNow), 77UL, options.QueueName).ConfigureAwait(false); - [Fact] - public async Task ReceiveAsync_Should_Auto_Acknowledge_Message_When_Configured() - { - var options = CreateOptions(o => o.AutoAcknowledge = true); - var channel = new Mock(); - var consumerSource = new TaskCompletionSource(); + Assert.True(await moveNext.ConfigureAwait(false)); + var message = enumerator.Current; - channel.Setup(c => c.QueueDeclareAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new QueueDeclareOk(options.QueueName, 0, 0)); - channel.Setup(c => c.BasicConsumeAsync(options.QueueName, false, string.Empty, false, false, null, It.IsAny(), It.IsAny())) - .Callback, IAsyncBasicConsumer, CancellationToken>((_, _, _, _, _, _, consumer, _) => consumerSource.TrySetResult(consumer)) - .ReturnsAsync("consumer-tag"); - channel.Setup(c => c.BasicAckAsync(88UL, false, It.IsAny())).Returns(ValueTask.CompletedTask); + Assert.Equal(77UL, message.Properties[nameof(BasicDeliverEventArgs.DeliveryTag)]); + Assert.Equal(options.QueueName, message.Properties[nameof(QueueDeclareOk.QueueName)]); - var sut = CreateSut(channel.Object, options); - await using var enumerator = sut.ReceiveAsync().GetAsyncEnumerator(); - var moveNext = enumerator.MoveNextAsync().AsTask(); - var consumer = await consumerSource.Task.ConfigureAwait(false); + await message.AcknowledgeAsync().ConfigureAwait(false); - await PublishDeliveredMessageAsync((AsyncEventingBasicConsumer)consumer, new Message("id-2", new Uri("urn:command:test"), "command.test", new TestCommand(), DateTime.UtcNow), 88UL, options.QueueName).ConfigureAwait(false); + channel.Verify(c => c.BasicAckAsync(77UL, false, It.IsAny()), Times.Once); + channel.Verify(c => c.QueueDeclareAsync(options.QueueName, options.Durable, options.Exclusive, options.AutoDelete, null, false, false, It.IsAny()), Times.Exactly(2)); + } - Assert.True(await moveNext.ConfigureAwait(false)); - channel.Verify(c => c.BasicAckAsync(88UL, false, It.IsAny()), Times.Once); - } + [Fact] + public async Task ReceiveAsync_Should_Auto_Acknowledge_Message_When_Configured() + { + var options = CreateOptions(o => o.AutoAcknowledge = true); + var channel = new Mock(); + var consumerSource = new TaskCompletionSource(); + + channel.Setup(c => c.QueueDeclareAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new QueueDeclareOk(options.QueueName, 0, 0)); + channel.Setup(c => c.BasicConsumeAsync(options.QueueName, false, string.Empty, false, false, null, It.IsAny(), It.IsAny())) + .Callback, IAsyncBasicConsumer, CancellationToken>((_, _, _, _, _, _, consumer, _) => consumerSource.TrySetResult(consumer)) + .ReturnsAsync("consumer-tag"); + channel.Setup(c => c.BasicAckAsync(88UL, false, It.IsAny())).Returns(ValueTask.CompletedTask); + + var sut = CreateSut(channel.Object, options); + await using var enumerator = sut.ReceiveAsync().GetAsyncEnumerator(); + var moveNext = enumerator.MoveNextAsync().AsTask(); + var consumer = await consumerSource.Task.ConfigureAwait(false); + + await PublishDeliveredMessageAsync((AsyncEventingBasicConsumer)consumer, new Message("id-2", new Uri("urn:command:test"), "command.test", new TestCommand(), DateTime.UtcNow), 88UL, options.QueueName).ConfigureAwait(false); + + Assert.True(await moveNext.ConfigureAwait(false)); + channel.Verify(c => c.BasicAckAsync(88UL, false, It.IsAny()), Times.Once); + } - private static RabbitMqCommandQueue CreateSut(IChannel channel, RabbitMqCommandQueueOptions options) - { - var connection = new Mock(); - connection.Setup(c => c.CreateChannelAsync(It.IsAny(), It.IsAny())).ReturnsAsync(channel); + private static RabbitMqCommandQueue CreateSut(IChannel channel, RabbitMqCommandQueueOptions options) + { + var connection = new Mock(); + connection.Setup(c => c.CreateChannelAsync(It.IsAny(), It.IsAny())).ReturnsAsync(channel); - var factory = new Mock(); - factory.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connection.Object); + var factory = new Mock(); + factory.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connection.Object); - var sut = new RabbitMqCommandQueue(JsonMarshaller.Default, options); - typeof(RabbitMqMessage) - .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)! - .SetValue(sut, factory.Object); - return sut; - } + var sut = new RabbitMqCommandQueue(JsonMarshaller.Default, options); + typeof(RabbitMqMessage) + .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)! + .SetValue(sut, factory.Object); + return sut; + } - private static RabbitMqCommandQueueOptions CreateOptions(Action setup = null) + private static RabbitMqCommandQueueOptions CreateOptions(Action setup = null) + { + var options = new RabbitMqCommandQueueOptions { - var options = new RabbitMqCommandQueueOptions - { - QueueName = "orders" - }; - setup?.Invoke(options); - return options; - } + QueueName = "orders" + }; + setup?.Invoke(options); + return options; + } - private static async Task PublishDeliveredMessageAsync(AsyncEventingBasicConsumer consumer, Message message, ulong deliveryTag, string queueName) + private static async Task PublishDeliveredMessageAsync(AsyncEventingBasicConsumer consumer, Message message, ulong deliveryTag, string queueName) + { + var body = await JsonMarshaller.Default.Serialize(message).ToByteArrayAsync().ConfigureAwait(false); + var properties = new BasicProperties { - var body = await JsonMarshaller.Default.Serialize(message).ToByteArrayAsync().ConfigureAwait(false); - var properties = new BasicProperties + Headers = new Dictionary { - Headers = new Dictionary - { - ["type"] = Encoding.UTF8.GetBytes($"{typeof(Message).FullName}, {typeof(Message).Assembly.GetName().Name}") - } - }; + ["type"] = Encoding.UTF8.GetBytes($"{typeof(Message).FullName}, {typeof(Message).Assembly.GetName().Name}") + } + }; - await consumer.HandleBasicDeliverAsync("consumer-tag", deliveryTag, false, string.Empty, queueName, properties, body, CancellationToken.None).ConfigureAwait(false); - } + await consumer.HandleBasicDeliverAsync("consumer-tag", deliveryTag, false, string.Empty, queueName, properties, body, CancellationToken.None).ConfigureAwait(false); + } - private sealed class TestCommand : ICommand - { - public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); - } + private sealed class TestCommand : ICommand + { + public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.Tests/EventDriven/RabbitMqEventBusOptionsTest.cs b/test/Savvyio.Extensions.RabbitMQ.Tests/EventDriven/RabbitMqEventBusOptionsTest.cs index e3775f39..9d58e9f9 100644 --- a/test/Savvyio.Extensions.RabbitMQ.Tests/EventDriven/RabbitMqEventBusOptionsTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.Tests/EventDriven/RabbitMqEventBusOptionsTest.cs @@ -1,58 +1,57 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.RabbitMQ.EventDriven +namespace Savvyio.Extensions.RabbitMQ.EventDriven; + +public class RabbitMqEventBusOptionsTest : Test { - public class RabbitMqEventBusOptionsTest : Test + public RabbitMqEventBusOptionsTest(ITestOutputHelper output) : base(output) { - public RabbitMqEventBusOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_Should_Set_Defaults() - { - var options = new RabbitMqEventBusOptions(); + [Fact] + public void Constructor_Should_Set_Defaults() + { + var options = new RabbitMqEventBusOptions(); - Assert.Null(options.ExchangeName); - Assert.NotNull(options.AmqpUrl); - Assert.Equal(new Uri("amqp://localhost:5672"), options.AmqpUrl); - } + Assert.Null(options.ExchangeName); + Assert.NotNull(options.AmqpUrl); + Assert.Equal(new Uri("amqp://localhost:5672"), options.AmqpUrl); + } - [Fact] - public void ValidateOptions_Should_Throw_When_ExchangeName_Is_Null_Or_Empty() - { - var options = new RabbitMqEventBusOptions(); + [Fact] + public void ValidateOptions_Should_Throw_When_ExchangeName_Is_Null_Or_Empty() + { + var options = new RabbitMqEventBusOptions(); - Assert.Throws(() => options.ValidateOptions()); + Assert.Throws(() => options.ValidateOptions()); - options.ExchangeName = ""; - Assert.Throws(() => options.ValidateOptions()); - } + options.ExchangeName = ""; + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_Should_Also_Throw_When_AmqpUrl_Is_Null() + [Fact] + public void ValidateOptions_Should_Also_Throw_When_AmqpUrl_Is_Null() + { + var options = new RabbitMqEventBusOptions { - var options = new RabbitMqEventBusOptions - { - ExchangeName = "test-exchange", - AmqpUrl = null - }; + ExchangeName = "test-exchange", + AmqpUrl = null + }; - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_Should_Not_Throw_When_ExchangeName_Is_Set() + [Fact] + public void ValidateOptions_Should_Not_Throw_When_ExchangeName_Is_Set() + { + var options = new RabbitMqEventBusOptions { - var options = new RabbitMqEventBusOptions - { - ExchangeName = "test-exchange" - }; - - var exception = Record.Exception(() => options.ValidateOptions()); - Assert.Null(exception); - } + ExchangeName = "test-exchange" + }; + + var exception = Record.Exception(() => options.ValidateOptions()); + Assert.Null(exception); } -} \ No newline at end of file +} diff --git a/test/Savvyio.Extensions.RabbitMQ.Tests/EventDriven/RabbitMqEventBusTest.cs b/test/Savvyio.Extensions.RabbitMQ.Tests/EventDriven/RabbitMqEventBusTest.cs index b6b4f504..afe62393 100644 --- a/test/Savvyio.Extensions.RabbitMQ.Tests/EventDriven/RabbitMqEventBusTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.Tests/EventDriven/RabbitMqEventBusTest.cs @@ -14,139 +14,138 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.RabbitMQ.EventDriven +namespace Savvyio.Extensions.RabbitMQ.EventDriven; + +public class RabbitMqEventBusTest : Test { - public class RabbitMqEventBusTest : Test + public RabbitMqEventBusTest(ITestOutputHelper output) : base(output) { - public RabbitMqEventBusTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_Should_Throw_When_Marshaller_Is_Null() - { - Assert.Throws(() => new RabbitMqEventBus(null, CreateOptions())); - } + [Fact] + public void Constructor_Should_Throw_When_Marshaller_Is_Null() + { + Assert.Throws(() => new RabbitMqEventBus(null, CreateOptions())); + } - [Fact] - public void Constructor_Should_Throw_When_Options_Are_Invalid() - { - Assert.Throws(() => new RabbitMqEventBus(JsonMarshaller.Default, new RabbitMqEventBusOptions())); - } + [Fact] + public void Constructor_Should_Throw_When_Options_Are_Invalid() + { + Assert.Throws(() => new RabbitMqEventBus(JsonMarshaller.Default, new RabbitMqEventBusOptions())); + } - [Fact] - public async Task PublishAsync_Should_Declare_Exchange_And_Publish_Message() - { - var options = CreateOptions(o => o.Persistent = true); - var channel = new Mock(); - BasicProperties publishedProperties = null; + [Fact] + public async Task PublishAsync_Should_Declare_Exchange_And_Publish_Message() + { + var options = CreateOptions(o => o.Persistent = true); + var channel = new Mock(); + BasicProperties publishedProperties = null; - channel.Setup(c => c.ExchangeDeclareAsync(options.ExchangeName, ExchangeType.Fanout, false, false, null, false, false, It.IsAny())) - .Returns(Task.CompletedTask); - channel.Setup(c => c.BasicPublishAsync(options.ExchangeName, string.Empty, false, It.IsAny(), It.IsAny>(), It.IsAny())) - .Callback, CancellationToken>((_, _, _, properties, _, _) => publishedProperties = properties) - .Returns(ValueTask.CompletedTask); + channel.Setup(c => c.ExchangeDeclareAsync(options.ExchangeName, ExchangeType.Fanout, false, false, null, false, false, It.IsAny())) + .Returns(Task.CompletedTask); + channel.Setup(c => c.BasicPublishAsync(options.ExchangeName, string.Empty, false, It.IsAny(), It.IsAny>(), It.IsAny())) + .Callback, CancellationToken>((_, _, _, properties, _, _) => publishedProperties = properties) + .Returns(ValueTask.CompletedTask); - var sut = CreateSut(channel.Object, options); - var message = new Message("id-1", new Uri("urn:event:test"), "event.test", new TestIntegrationEvent(), DateTime.UtcNow); + var sut = CreateSut(channel.Object, options); + var message = new Message("id-1", new Uri("urn:event:test"), "event.test", new TestIntegrationEvent(), DateTime.UtcNow); - await sut.PublishAsync(message); + await sut.PublishAsync(message); - channel.Verify(c => c.ExchangeDeclareAsync(options.ExchangeName, ExchangeType.Fanout, false, false, null, false, false, It.IsAny()), Times.Once); - channel.Verify(c => c.BasicPublishAsync(options.ExchangeName, string.Empty, false, It.IsAny(), It.IsAny>(), It.IsAny()), Times.Once); - Assert.True(publishedProperties.Persistent); - Assert.Equal($"{typeof(Message).FullName}, {typeof(Message).Assembly.GetName().Name}", publishedProperties.Headers["type"]); - } + channel.Verify(c => c.ExchangeDeclareAsync(options.ExchangeName, ExchangeType.Fanout, false, false, null, false, false, It.IsAny()), Times.Once); + channel.Verify(c => c.BasicPublishAsync(options.ExchangeName, string.Empty, false, It.IsAny(), It.IsAny>(), It.IsAny()), Times.Once); + Assert.True(publishedProperties.Persistent); + Assert.Equal($"{typeof(Message).FullName}, {typeof(Message).Assembly.GetName().Name}", publishedProperties.Headers["type"]); + } - [Fact] - public async Task SubscribeAsync_Should_Invoke_Handler_For_Received_Message() + [Fact] + public async Task SubscribeAsync_Should_Invoke_Handler_For_Received_Message() + { + var options = CreateOptions(); + var channel = new Mock(); + var consumerSource = new TaskCompletionSource(); + var handled = 0; + IMessage received = null; + using var cts = new CancellationTokenSource(); + + channel.Setup(c => c.ExchangeDeclareAsync(options.ExchangeName, ExchangeType.Fanout, false, false, null, false, false, It.IsAny())) + .Returns(Task.CompletedTask); + channel.Setup(c => c.QueueDeclareAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new QueueDeclareOk("events-queue", 0, 0)); + channel.Setup(c => c.QueueBindAsync("events-queue", options.ExchangeName, string.Empty, null, false, It.IsAny())) + .Returns(Task.CompletedTask); + channel.Setup(c => c.BasicConsumeAsync("events-queue", true, string.Empty, false, false, null, It.IsAny(), It.IsAny())) + .Callback, IAsyncBasicConsumer, CancellationToken>((_, _, _, _, _, _, consumer, _) => consumerSource.TrySetResult(consumer)) + .ReturnsAsync("consumer-tag"); + + var sut = CreateSut(channel.Object, options); + var subscribeTask = sut.SubscribeAsync((message, token) => { - var options = CreateOptions(); - var channel = new Mock(); - var consumerSource = new TaskCompletionSource(); - var handled = 0; - IMessage received = null; - using var cts = new CancellationTokenSource(); - - channel.Setup(c => c.ExchangeDeclareAsync(options.ExchangeName, ExchangeType.Fanout, false, false, null, false, false, It.IsAny())) - .Returns(Task.CompletedTask); - channel.Setup(c => c.QueueDeclareAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new QueueDeclareOk("events-queue", 0, 0)); - channel.Setup(c => c.QueueBindAsync("events-queue", options.ExchangeName, string.Empty, null, false, It.IsAny())) - .Returns(Task.CompletedTask); - channel.Setup(c => c.BasicConsumeAsync("events-queue", true, string.Empty, false, false, null, It.IsAny(), It.IsAny())) - .Callback, IAsyncBasicConsumer, CancellationToken>((_, _, _, _, _, _, consumer, _) => consumerSource.TrySetResult(consumer)) - .ReturnsAsync("consumer-tag"); - - var sut = CreateSut(channel.Object, options); - var subscribeTask = sut.SubscribeAsync((message, token) => - { - handled++; - received = message; - cts.Cancel(); - return Task.CompletedTask; - }, o => o.CancellationToken = cts.Token); + handled++; + received = message; + cts.Cancel(); + return Task.CompletedTask; + }, o => o.CancellationToken = cts.Token); - var consumer = await consumerSource.Task.ConfigureAwait(false); - await PublishDeliveredMessageAsync((AsyncEventingBasicConsumer)consumer, new Message("id-2", new Uri("urn:event:test"), "event.test", new TestIntegrationEvent(), DateTime.UtcNow), 91UL).ConfigureAwait(false); + var consumer = await consumerSource.Task.ConfigureAwait(false); + await PublishDeliveredMessageAsync((AsyncEventingBasicConsumer)consumer, new Message("id-2", new Uri("urn:event:test"), "event.test", new TestIntegrationEvent(), DateTime.UtcNow), 91UL).ConfigureAwait(false); - try - { - await subscribeTask.ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // expected: the subscription loop exits when the token is cancelled inside the handler - } - - Assert.Equal(1, handled); - Assert.NotNull(received); - Assert.Equal(91UL, received.Properties[nameof(BasicDeliverEventArgs.DeliveryTag)]); - Assert.Equal("events-queue", received.Properties[nameof(QueueDeclareOk.QueueName)]); + try + { + await subscribeTask.ConfigureAwait(false); } - - private static RabbitMqEventBus CreateSut(IChannel channel, RabbitMqEventBusOptions options) + catch (OperationCanceledException) { - var connection = new Mock(); - connection.Setup(c => c.CreateChannelAsync(It.IsAny(), It.IsAny())).ReturnsAsync(channel); + // expected: the subscription loop exits when the token is cancelled inside the handler + } - var factory = new Mock(); - factory.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connection.Object); + Assert.Equal(1, handled); + Assert.NotNull(received); + Assert.Equal(91UL, received.Properties[nameof(BasicDeliverEventArgs.DeliveryTag)]); + Assert.Equal("events-queue", received.Properties[nameof(QueueDeclareOk.QueueName)]); + } - var sut = new RabbitMqEventBus(JsonMarshaller.Default, options); - typeof(RabbitMqMessage) - .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)! - .SetValue(sut, factory.Object); - return sut; - } + private static RabbitMqEventBus CreateSut(IChannel channel, RabbitMqEventBusOptions options) + { + var connection = new Mock(); + connection.Setup(c => c.CreateChannelAsync(It.IsAny(), It.IsAny())).ReturnsAsync(channel); + + var factory = new Mock(); + factory.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connection.Object); - private static RabbitMqEventBusOptions CreateOptions(Action setup = null) + var sut = new RabbitMqEventBus(JsonMarshaller.Default, options); + typeof(RabbitMqMessage) + .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)! + .SetValue(sut, factory.Object); + return sut; + } + + private static RabbitMqEventBusOptions CreateOptions(Action setup = null) + { + var options = new RabbitMqEventBusOptions { - var options = new RabbitMqEventBusOptions - { - ExchangeName = "events" - }; - setup?.Invoke(options); - return options; - } + ExchangeName = "events" + }; + setup?.Invoke(options); + return options; + } - private static async Task PublishDeliveredMessageAsync(AsyncEventingBasicConsumer consumer, Message message, ulong deliveryTag) + private static async Task PublishDeliveredMessageAsync(AsyncEventingBasicConsumer consumer, Message message, ulong deliveryTag) + { + var body = await JsonMarshaller.Default.Serialize(message).ToByteArrayAsync().ConfigureAwait(false); + var properties = new BasicProperties { - var body = await JsonMarshaller.Default.Serialize(message).ToByteArrayAsync().ConfigureAwait(false); - var properties = new BasicProperties + Headers = new Dictionary { - Headers = new Dictionary - { - ["type"] = Encoding.UTF8.GetBytes($"{typeof(Message).FullName}, {typeof(Message).Assembly.GetName().Name}") - } - }; + ["type"] = Encoding.UTF8.GetBytes($"{typeof(Message).FullName}, {typeof(Message).Assembly.GetName().Name}") + } + }; - await consumer.HandleBasicDeliverAsync("consumer-tag", deliveryTag, false, string.Empty, string.Empty, properties, body, CancellationToken.None).ConfigureAwait(false); - } + await consumer.HandleBasicDeliverAsync("consumer-tag", deliveryTag, false, string.Empty, string.Empty, properties, body, CancellationToken.None).ConfigureAwait(false); + } - private sealed class TestIntegrationEvent : IIntegrationEvent - { - public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); - } + private sealed class TestIntegrationEvent : IIntegrationEvent + { + public IMetadataDictionary Metadata { get; } = new MetadataDictionary(); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageOptionsTest.cs b/test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageOptionsTest.cs index 4e7a5201..c7eb757f 100644 --- a/test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageOptionsTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageOptionsTest.cs @@ -1,57 +1,56 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.RabbitMQ +namespace Savvyio.Extensions.RabbitMQ; + +public class RabbitMqMessageOptionsTest : Test { - public class RabbitMqMessageOptionsTest : Test + public RabbitMqMessageOptionsTest(ITestOutputHelper output) : base(output) { - public RabbitMqMessageOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_Should_Set_Default_AmqpUrl() - { - var options = new RabbitMqMessageOptions(); + [Fact] + public void Constructor_Should_Set_Default_AmqpUrl() + { + var options = new RabbitMqMessageOptions(); - Assert.NotNull(options.AmqpUrl); - Assert.False(options.Persistent); - Assert.Equal(new Uri("amqp://localhost:5672"), options.AmqpUrl); - } + Assert.NotNull(options.AmqpUrl); + Assert.False(options.Persistent); + Assert.Equal(new Uri("amqp://localhost:5672"), options.AmqpUrl); + } - [Fact] - public void Persistent_Should_Be_Settable() + [Fact] + public void Persistent_Should_Be_Settable() + { + var options = new RabbitMqMessageOptions { - var options = new RabbitMqMessageOptions - { - Persistent = true - }; + Persistent = true + }; - Assert.True(options.Persistent); - } + Assert.True(options.Persistent); + } - [Fact] - public void ValidateOptions_Should_Throw_When_AmqpUrl_Is_Null() + [Fact] + public void ValidateOptions_Should_Throw_When_AmqpUrl_Is_Null() + { + var options = new RabbitMqMessageOptions { - var options = new RabbitMqMessageOptions - { - AmqpUrl = null - }; + AmqpUrl = null + }; - Assert.Throws(() => options.ValidateOptions()); - } + Assert.Throws(() => options.ValidateOptions()); + } - [Fact] - public void ValidateOptions_Should_Not_Throw_When_AmqpUrl_Is_Set() + [Fact] + public void ValidateOptions_Should_Not_Throw_When_AmqpUrl_Is_Set() + { + var options = new RabbitMqMessageOptions { - var options = new RabbitMqMessageOptions - { - AmqpUrl = new Uri("amqp://localhost:5672") - }; - - var exception = Record.Exception(() => options.ValidateOptions()); - Assert.Null(exception); - } + AmqpUrl = new Uri("amqp://localhost:5672") + }; + + var exception = Record.Exception(() => options.ValidateOptions()); + Assert.Null(exception); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageTest.cs b/test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageTest.cs index 4723f6c1..2f58d57f 100644 --- a/test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageTest.cs +++ b/test/Savvyio.Extensions.RabbitMQ.Tests/RabbitMqMessageTest.cs @@ -7,155 +7,154 @@ using System.Threading.Tasks; using Xunit; -namespace Savvyio.Extensions.RabbitMQ +namespace Savvyio.Extensions.RabbitMQ; + +public class RabbitMqMessageTest : Test { - public class RabbitMqMessageTest : Test + public RabbitMqMessageTest(ITestOutputHelper output) : base(output) { - public RabbitMqMessageTest(ITestOutputHelper output) : base(output) - { - } + } - private sealed class TestRabbitMqMessage : RabbitMqMessage + private sealed class TestRabbitMqMessage : RabbitMqMessage + { + public TestRabbitMqMessage(IMarshaller marshaller, RabbitMqMessageOptions options) : base(marshaller, options) { - public TestRabbitMqMessage(IMarshaller marshaller, RabbitMqMessageOptions options) : base(marshaller, options) - { - } - - public Task CallEnsureConnectivityAsync(CancellationToken ct = default) => EnsureConnectivityAsync(ct); - - public ValueTask CallOnDisposeManagedResourcesAsync() => OnDisposeManagedResourcesAsync(); - - public void SetConnection(IConnection connection) - { - typeof(RabbitMqMessage) - .GetProperty("RabbitMqConnection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! - .SetValue(this, connection); - } - - public void SetChannel(IChannel channel) - { - typeof(RabbitMqMessage) - .GetProperty("RabbitMqChannel", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! - .SetValue(this, channel); - } } - [Fact] - public void Constructor_Throws_WhenMarshallerIsNull() + public Task CallEnsureConnectivityAsync(CancellationToken ct = default) => EnsureConnectivityAsync(ct); + + public ValueTask CallOnDisposeManagedResourcesAsync() => OnDisposeManagedResourcesAsync(); + + public void SetConnection(IConnection connection) { - var options = new RabbitMqMessageOptions(); - Assert.Throws(() => new TestRabbitMqMessage(null, options)); + typeof(RabbitMqMessage) + .GetProperty("RabbitMqConnection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! + .SetValue(this, connection); } - [Fact] - public void Constructor_Throws_WhenOptionsIsNull() + public void SetChannel(IChannel channel) { - Assert.Throws(() => new TestRabbitMqMessage(JsonMarshaller.Default, null)); + typeof(RabbitMqMessage) + .GetProperty("RabbitMqChannel", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! + .SetValue(this, channel); } + } - [Fact] - public void Constructor_SetsProperties() - { - var options = new RabbitMqMessageOptions { AmqpUrl = new Uri("amqp://localhost:5672") }; - var sut = new TestRabbitMqMessage(JsonMarshaller.Default, options); + [Fact] + public void Constructor_Throws_WhenMarshallerIsNull() + { + var options = new RabbitMqMessageOptions(); + Assert.Throws(() => new TestRabbitMqMessage(null, options)); + } - Assert.NotNull(sut); - Assert.NotNull(typeof(RabbitMqMessage).GetProperty("Marshaller", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.GetValue(sut)); - Assert.NotNull(typeof(RabbitMqMessage).GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.GetValue(sut)); - } + [Fact] + public void Constructor_Throws_WhenOptionsIsNull() + { + Assert.Throws(() => new TestRabbitMqMessage(JsonMarshaller.Default, null)); + } - [Fact] - public async Task EnsureConnectivityAsync_InitializesConnectionAndChannel_Once() - { - var options = new RabbitMqMessageOptions { AmqpUrl = new Uri("amqp://localhost:5672") }; - var connectionMock = new Mock(); - var channelMock = new Mock(); - connectionMock.Setup(c => c.CreateChannelAsync(It.IsAny(), It.IsAny())).ReturnsAsync(channelMock.Object); + [Fact] + public void Constructor_SetsProperties() + { + var options = new RabbitMqMessageOptions { AmqpUrl = new Uri("amqp://localhost:5672") }; + var sut = new TestRabbitMqMessage(JsonMarshaller.Default, options); - var factoryMock = new Mock(); - factoryMock.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connectionMock.Object); + Assert.NotNull(sut); + Assert.NotNull(typeof(RabbitMqMessage).GetProperty("Marshaller", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.GetValue(sut)); + Assert.NotNull(typeof(RabbitMqMessage).GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.GetValue(sut)); + } - var sut = new TestRabbitMqMessage(JsonMarshaller.Default, options); - typeof(RabbitMqMessage) - .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! - .SetValue(sut, factoryMock.Object); + [Fact] + public async Task EnsureConnectivityAsync_InitializesConnectionAndChannel_Once() + { + var options = new RabbitMqMessageOptions { AmqpUrl = new Uri("amqp://localhost:5672") }; + var connectionMock = new Mock(); + var channelMock = new Mock(); + connectionMock.Setup(c => c.CreateChannelAsync(It.IsAny(), It.IsAny())).ReturnsAsync(channelMock.Object); - await sut.CallEnsureConnectivityAsync(); - await sut.CallEnsureConnectivityAsync(); + var factoryMock = new Mock(); + factoryMock.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connectionMock.Object); - Assert.Same(connectionMock.Object, typeof(RabbitMqMessage).GetProperty("RabbitMqConnection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.GetValue(sut)); - Assert.Same(channelMock.Object, typeof(RabbitMqMessage).GetProperty("RabbitMqChannel", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.GetValue(sut)); - factoryMock.Verify(f => f.CreateConnectionAsync(It.IsAny()), Times.Once); - } + var sut = new TestRabbitMqMessage(JsonMarshaller.Default, options); + typeof(RabbitMqMessage) + .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! + .SetValue(sut, factoryMock.Object); - [Fact] - public async Task EnsureConnectivityAsync_IsThreadSafe_WhenCalledConcurrently() - { - var options = new RabbitMqMessageOptions { AmqpUrl = new Uri("amqp://localhost:5672") }; - var connectionMock = new Mock(); - var channelMock = new Mock(); - connectionMock.Setup(c => c.CreateChannelAsync(It.IsAny(), It.IsAny())).ReturnsAsync(channelMock.Object); + await sut.CallEnsureConnectivityAsync(); + await sut.CallEnsureConnectivityAsync(); - var factoryMock = new Mock(); - factoryMock.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connectionMock.Object); + Assert.Same(connectionMock.Object, typeof(RabbitMqMessage).GetProperty("RabbitMqConnection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.GetValue(sut)); + Assert.Same(channelMock.Object, typeof(RabbitMqMessage).GetProperty("RabbitMqChannel", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.GetValue(sut)); + factoryMock.Verify(f => f.CreateConnectionAsync(It.IsAny()), Times.Once); + } - var sut = new TestRabbitMqMessage(JsonMarshaller.Default, options); - typeof(RabbitMqMessage) - .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! - .SetValue(sut, factoryMock.Object); + [Fact] + public async Task EnsureConnectivityAsync_IsThreadSafe_WhenCalledConcurrently() + { + var options = new RabbitMqMessageOptions { AmqpUrl = new Uri("amqp://localhost:5672") }; + var connectionMock = new Mock(); + var channelMock = new Mock(); + connectionMock.Setup(c => c.CreateChannelAsync(It.IsAny(), It.IsAny())).ReturnsAsync(channelMock.Object); - await Task.WhenAll(sut.CallEnsureConnectivityAsync(), sut.CallEnsureConnectivityAsync()); + var factoryMock = new Mock(); + factoryMock.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connectionMock.Object); - factoryMock.Verify(f => f.CreateConnectionAsync(It.IsAny()), Times.Once); - } + var sut = new TestRabbitMqMessage(JsonMarshaller.Default, options); + typeof(RabbitMqMessage) + .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! + .SetValue(sut, factoryMock.Object); - [Fact] - public async Task OnDisposeManagedResourcesAsync_DisposesChannelAndConnection() - { - var channelMock = new Mock(); - channelMock.Setup(c => c.DisposeAsync()).Returns(ValueTask.CompletedTask).Verifiable(); + await Task.WhenAll(sut.CallEnsureConnectivityAsync(), sut.CallEnsureConnectivityAsync()); - var connectionMock = new Mock(); - connectionMock.Setup(c => c.DisposeAsync()).Returns(ValueTask.CompletedTask).Verifiable(); + factoryMock.Verify(f => f.CreateConnectionAsync(It.IsAny()), Times.Once); + } - var sut = new TestRabbitMqMessage(JsonMarshaller.Default, new RabbitMqMessageOptions()); - sut.SetChannel(channelMock.Object); - sut.SetConnection(connectionMock.Object); + [Fact] + public async Task OnDisposeManagedResourcesAsync_DisposesChannelAndConnection() + { + var channelMock = new Mock(); + channelMock.Setup(c => c.DisposeAsync()).Returns(ValueTask.CompletedTask).Verifiable(); - await sut.CallOnDisposeManagedResourcesAsync(); + var connectionMock = new Mock(); + connectionMock.Setup(c => c.DisposeAsync()).Returns(ValueTask.CompletedTask).Verifiable(); - channelMock.Verify(c => c.DisposeAsync(), Times.Once); - connectionMock.Verify(c => c.DisposeAsync(), Times.Once); - } + var sut = new TestRabbitMqMessage(JsonMarshaller.Default, new RabbitMqMessageOptions()); + sut.SetChannel(channelMock.Object); + sut.SetConnection(connectionMock.Object); - [Fact] - public async Task GetHealthCheckTargetAsync_ReturnsExistingConnection_IfInitialized() - { - var connectionMock = new Mock().Object; - var sut = new TestRabbitMqMessage(JsonMarshaller.Default, new RabbitMqMessageOptions()); - sut.SetConnection(connectionMock); + await sut.CallOnDisposeManagedResourcesAsync(); - var result = await sut.GetHealthCheckTargetAsync(); + channelMock.Verify(c => c.DisposeAsync(), Times.Once); + connectionMock.Verify(c => c.DisposeAsync(), Times.Once); + } - Assert.Same(connectionMock, result); - } + [Fact] + public async Task GetHealthCheckTargetAsync_ReturnsExistingConnection_IfInitialized() + { + var connectionMock = new Mock().Object; + var sut = new TestRabbitMqMessage(JsonMarshaller.Default, new RabbitMqMessageOptions()); + sut.SetConnection(connectionMock); - [Fact] - public async Task GetHealthCheckTargetAsync_CreatesConnection_IfNotInitialized() - { - var connectionMock = new Mock().Object; - var factoryMock = new Mock(); - factoryMock.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connectionMock); + var result = await sut.GetHealthCheckTargetAsync(); - var sut = new TestRabbitMqMessage(JsonMarshaller.Default, new RabbitMqMessageOptions()); - typeof(RabbitMqMessage) - .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! - .SetValue(sut, factoryMock.Object); + Assert.Same(connectionMock, result); + } - var result = await sut.GetHealthCheckTargetAsync(); + [Fact] + public async Task GetHealthCheckTargetAsync_CreatesConnection_IfNotInitialized() + { + var connectionMock = new Mock().Object; + var factoryMock = new Mock(); + factoryMock.Setup(f => f.CreateConnectionAsync(It.IsAny())).ReturnsAsync(connectionMock); - Assert.Same(connectionMock, result); - factoryMock.Verify(f => f.CreateConnectionAsync(It.IsAny()), Times.Once); - } + var sut = new TestRabbitMqMessage(JsonMarshaller.Default, new RabbitMqMessageOptions()); + typeof(RabbitMqMessage) + .GetProperty("RabbitMqFactory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)! + .SetValue(sut, factoryMock.Object); + + var result = await sut.GetHealthCheckTargetAsync(); + + Assert.Same(connectionMock, result); + factoryMock.Verify(f => f.CreateConnectionAsync(It.IsAny()), Times.Once); } } diff --git a/test/Savvyio.Extensions.RabbitMQ.Tests/Savvyio.Extensions.RabbitMQ.Tests.csproj b/test/Savvyio.Extensions.RabbitMQ.Tests/Savvyio.Extensions.RabbitMQ.Tests.csproj index a98d7561..0e0661db 100644 --- a/test/Savvyio.Extensions.RabbitMQ.Tests/Savvyio.Extensions.RabbitMQ.Tests.csproj +++ b/test/Savvyio.Extensions.RabbitMQ.Tests/Savvyio.Extensions.RabbitMQ.Tests.csproj @@ -3,7 +3,7 @@ Savvyio.Extensions.RabbitMQ - + @@ -17,5 +17,5 @@ - + diff --git a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Assets/CreateMemberCommand.cs b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Assets/CreateMemberCommand.cs index d3fa9beb..e8fb4ca2 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Assets/CreateMemberCommand.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Assets/CreateMemberCommand.cs @@ -1,20 +1,19 @@ -using Savvyio.Commands; +using Savvyio.Commands; -namespace Savvyio.Extensions.SimpleQueueService.Assets +namespace Savvyio.Extensions.SimpleQueueService.Assets; + +internal record CreateMemberCommand : Command { - internal record CreateMemberCommand : Command + public CreateMemberCommand(string name, byte age, string emailAddress) { - public CreateMemberCommand(string name, byte age, string emailAddress) - { - Name = name; - Age = age; - EmailAddress = emailAddress; - } + Name = name; + Age = age; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public byte Age { get; } + public byte Age { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Assets/MemberCreated.cs b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Assets/MemberCreated.cs index 66336e24..2bc5c112 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Assets/MemberCreated.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Assets/MemberCreated.cs @@ -1,17 +1,16 @@ -using Savvyio.EventDriven; +using Savvyio.EventDriven; -namespace Savvyio.Extensions.SimpleQueueService.Assets +namespace Savvyio.Extensions.SimpleQueueService.Assets; + +public record MemberCreated : IntegrationEvent { - public record MemberCreated : IntegrationEvent + public MemberCreated(string name, string emailAddress) { - public MemberCreated(string name, string emailAddress) - { - Name = name; - EmailAddress = emailAddress; - } + Name = name; + EmailAddress = emailAddress; + } - public string Name { get; } + public string Name { get; } - public string EmailAddress { get; } - } + public string EmailAddress { get; } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Commands/AmazonCommandQueueJsonSerializerContextTest.cs b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Commands/AmazonCommandQueueJsonSerializerContextTest.cs index 0bba7769..b87d8e7b 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Commands/AmazonCommandQueueJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Commands/AmazonCommandQueueJsonSerializerContextTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -25,154 +25,153 @@ using Savvyio.Messaging.Cryptography; using Xunit.v3.Priority; -namespace Savvyio.Extensions.SimpleQueueService.Commands +namespace Savvyio.Extensions.SimpleQueueService.Commands; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class AmazonCommandQueueJsonSerializerContextTest : HostTest { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class AmazonCommandQueueJsonSerializerContextTest : HostTest + private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + private readonly AmazonCommandQueue _queue; + private static readonly InMemoryTestStore> Comparer = new(); + private readonly IMarshaller _marshaller; + + public AmazonCommandQueueJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) { - private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - private readonly AmazonCommandQueue _queue; - private static readonly InMemoryTestStore> Comparer = new(); - private readonly IMarshaller _marshaller; + _queue = fixture.Host.Services.GetRequiredService(); + _marshaller = fixture.Host.Services.GetRequiredService(); + } - public AmazonCommandQueueJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - _queue = fixture.Host.Services.GetRequiredService(); - _marshaller = fixture.Host.Services.GetRequiredService(); - } + [Fact, Priority(0)] + public async Task SendAsync_CreateMemberCommand_OneTime() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); - [Fact, Priority(0)] - public async Task SendAsync_CreateMemberCommand_OneTime() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); - TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); + Comparer.Add(sut3); - Comparer.Add(sut3); + await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); + } - await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); - } + [Fact, Priority(1)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); - [Fact, Priority(1)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime() + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; + IMessage sut2 = null; + while (sut2 == null) { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); - - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; - IMessage sut2 = null; - while (sut2 == null) - { - sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false); - } - - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); + sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false); } - [Fact, Priority(2)] - public async Task SendAsync_CreateMemberCommand_OneTime_Signed() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members/signed".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)).Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(2)] + public async Task SendAsync_CreateMemberCommand_OneTime_Signed() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members/signed".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)).Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - Comparer.Add(sut3); + TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); - await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); - } + Comparer.Add(sut3); - [Fact, Priority(3)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime_Signed() - { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members/signed").Single(); + await _queue.SendAsync(sut3.Yield()).ConfigureAwait(false); + } - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; - ISignedMessage sut2 = null; - while (sut2 == null) - { - sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false) as ISignedMessage; - sut2?.CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - } + [Fact, Priority(3)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime_Signed() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members/signed").Single(); - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; + ISignedMessage sut2 = null; + while (sut2 == null) + { + sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false) as ISignedMessage; + sut2?.CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); } - [Fact, Priority(4)] - public async Task SendAsync_CreateMemberCommand_HundredTimes() + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + } + + [Fact, Priority(4)] + public async Task SendAsync_CreateMemberCommand_HundredTimes() + { + var messages = Generate.RangeOf(100, i => { - var messages = Generate.RangeOf(100, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - Comparer.Add(message); - return message; - }).ToList(); + var email = $"{Generate.RandomString(5)}@outlook.com"; + var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + Comparer.Add(message); + return message; + }).ToList(); - var profiler = await TimeMeasure.WithActionAsync(_ => _queue.SendAsync(messages)).ConfigureAwait(false); + var profiler = await TimeMeasure.WithActionAsync(_ => _queue.SendAsync(messages)).ConfigureAwait(false); - TestOutput.WriteLine(profiler.ToString()); - } + TestOutput.WriteLine(profiler.ToString()); + } - [Fact, Priority(5)] - public async Task ReceiveAsync_CreateMemberCommand_All() - { - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(90)).Token; - var realizedCommands = new List>(); - var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); + [Fact, Priority(5)] + public async Task ReceiveAsync_CreateMemberCommand_All() + { + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(90)).Token; + var realizedCommands = new List>(); + var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); - while (realizedCommands.Count < sut1.Count) - { - realizedCommands.AddRange(await _queue.ReceiveAsync(o => o.CancellationToken = ct).ToListAsync(ct).ConfigureAwait(false)); - } + while (realizedCommands.Count < sut1.Count) + { + realizedCommands.AddRange(await _queue.ReceiveAsync(o => o.CancellationToken = ct).ToListAsync(ct).ConfigureAwait(false)); + } - TestOutput.WriteLine(realizedCommands.Count.ToString()); - TestOutput.WriteLines(realizedCommands.Take(10)); + TestOutput.WriteLine(realizedCommands.Count.ToString()); + TestOutput.WriteLines(realizedCommands.Take(10)); - Assert.Equivalent(sut1.Count, realizedCommands.Count); - Assert.Equivalent(sut1, realizedCommands); - Assert.Equivalent(sut1.Select(message => message.Data), realizedCommands.Select(message => message.Data)); - Assert.Equivalent(sut1.Select(message => message.Data.Metadata), realizedCommands.Select(message => message.Data.Metadata)); - } + Assert.Equivalent(sut1.Count, realizedCommands.Count); + Assert.Equivalent(sut1, realizedCommands); + Assert.Equivalent(sut1.Select(message => message.Data), realizedCommands.Select(message => message.Data)); + Assert.Equivalent(sut1.Select(message => message.Data.Metadata), realizedCommands.Select(message => message.Data.Metadata)); + } - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(IServiceCollection services) + { + services.AddMarshaller(); + services.AddAmazonCommandQueue(o => { - services.AddMarshaller(); - services.AddAmazonCommandQueue(o => + var queue = IsLinux ? "savvyio-commands" : "savvyio-commands.fifo"; + o.Endpoint = RegionEndpoint.EUWest1; + if (Configuration["AWS:LocalStack"] != null) { - var queue = IsLinux ? "savvyio-commands" : "savvyio-commands.fifo"; - o.Endpoint = RegionEndpoint.EUWest1; - if (Configuration["AWS:LocalStack"] != null) + o.Credentials = new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); + o.SourceQueue = new Uri($"http://sqs.eu-west-1.localhost.localstack.cloud:4566/000000000000/{queue}"); + o.ConfigureClient(client => { - o.Credentials = new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); - o.SourceQueue = new Uri($"http://sqs.eu-west-1.localhost.localstack.cloud:4566/000000000000/{queue}"); - o.ConfigureClient(client => - { - client.ServiceURL = Configuration["AWS:LocalStack"]; - client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; - }); - } - else - { - o.Credentials = new BasicAWSCredentials(Configuration["AWS:IAM:AccessKey"], Configuration["AWS:IAM:SecretKey"]); - o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{Configuration["AWS:CallerIdentity"]}/{queue}"); - } - o.ReceiveContext.UseApproximateNumberOfMessages = true; - }); - } + client.ServiceURL = Configuration["AWS:LocalStack"]; + client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; + }); + } + else + { + o.Credentials = new BasicAWSCredentials(Configuration["AWS:IAM:AccessKey"], Configuration["AWS:IAM:SecretKey"]); + o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{Configuration["AWS:CallerIdentity"]}/{queue}"); + } + o.ReceiveContext.UseApproximateNumberOfMessages = true; + }); } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Commands/AmazonCommandQueueNewtonsoftJsonSerializerContextTest.cs b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Commands/AmazonCommandQueueNewtonsoftJsonSerializerContextTest.cs index 4488ba9a..b701aa49 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Commands/AmazonCommandQueueNewtonsoftJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Commands/AmazonCommandQueueNewtonsoftJsonSerializerContextTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -25,154 +25,153 @@ using Savvyio.Messaging.Cryptography; using Xunit.v3.Priority; -namespace Savvyio.Extensions.SimpleQueueService.Commands +namespace Savvyio.Extensions.SimpleQueueService.Commands; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class AmazonCommandQueueNewtonsoftJsonSerializerContextTest : HostTest { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class AmazonCommandQueueNewtonsoftJsonSerializerContextTest : HostTest + private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + private readonly AmazonCommandQueue _queue; + private static readonly InMemoryTestStore> Comparer = new(); + private readonly IMarshaller _marshaller; + + public AmazonCommandQueueNewtonsoftJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) { - private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - private readonly AmazonCommandQueue _queue; - private static readonly InMemoryTestStore> Comparer = new(); - private readonly IMarshaller _marshaller; + _queue = fixture.Host.Services.GetRequiredService(); + _marshaller = fixture.Host.Services.GetRequiredService(); + } - public AmazonCommandQueueNewtonsoftJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - _queue = fixture.Host.Services.GetRequiredService(); - _marshaller = fixture.Host.Services.GetRequiredService(); - } + [Fact, Priority(0)] + public async Task SendAsync_CreateMemberCommand_OneTime() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); - [Fact, Priority(0)] - public async Task SendAsync_CreateMemberCommand_OneTime() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); - TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); + Comparer.Add(sut3); - Comparer.Add(sut3); + await _queue.SendAsync(sut3.Yield()); + } - await _queue.SendAsync(sut3.Yield()); - } + [Fact, Priority(1)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); - [Fact, Priority(1)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime() + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; + IMessage sut2 = null; + while (sut2 == null) { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); - - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; - IMessage sut2 = null; - while (sut2 == null) - { - sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false); - } - - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); + sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false); } - [Fact, Priority(2)] - public async Task SendAsync_CreateMemberCommand_OneTime_Signed() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members/signed".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)).Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(2)] + public async Task SendAsync_CreateMemberCommand_OneTime_Signed() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members/signed".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)).Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - Comparer.Add(sut3); + TestOutput.WriteLine(_marshaller.Serialize(sut2).ToEncodedString()); - await _queue.SendAsync(sut3.Yield()); - } + Comparer.Add(sut3); - [Fact, Priority(3)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime_Signed() - { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members/signed").Single(); + await _queue.SendAsync(sut3.Yield()); + } - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; - ISignedMessage sut2 = null; - while (sut2 == null) - { - sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false) as ISignedMessage; - sut2?.CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - } + [Fact, Priority(3)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime_Signed() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members/signed").Single(); - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token; + ISignedMessage sut2 = null; + while (sut2 == null) + { + sut2 = await _queue.ReceiveAsync().SingleOrDefaultAsync(ct).ConfigureAwait(false) as ISignedMessage; + sut2?.CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); } - [Fact, Priority(5)] - public async Task SendAsync_CreateMemberCommand_HundredTimes() + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + } + + [Fact, Priority(5)] + public async Task SendAsync_CreateMemberCommand_HundredTimes() + { + var messages = Generate.RangeOf(100, i => { - var messages = Generate.RangeOf(100, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - Comparer.Add(message); - return message; - }).ToList(); + var email = $"{Generate.RandomString(5)}@outlook.com"; + var message = new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + Comparer.Add(message); + return message; + }).ToList(); - var profiler = await TimeMeasure.WithActionAsync(_ => _queue.SendAsync(messages)).ConfigureAwait(false); + var profiler = await TimeMeasure.WithActionAsync(_ => _queue.SendAsync(messages)).ConfigureAwait(false); - TestOutput.WriteLine(profiler.ToString()); - } + TestOutput.WriteLine(profiler.ToString()); + } - [Fact, Priority(6)] - public async Task ReceiveAsync_CreateMemberCommand_All() - { - var ct = new CancellationTokenSource(TimeSpan.FromSeconds(90)).Token; - var realizedCommands = new List>(); - var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); + [Fact, Priority(6)] + public async Task ReceiveAsync_CreateMemberCommand_All() + { + var ct = new CancellationTokenSource(TimeSpan.FromSeconds(90)).Token; + var realizedCommands = new List>(); + var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); - while (realizedCommands.Count < sut1.Count) - { - realizedCommands.AddRange(await _queue.ReceiveAsync(o => o.CancellationToken = ct).ToListAsync(ct).ConfigureAwait(false)); - } + while (realizedCommands.Count < sut1.Count) + { + realizedCommands.AddRange(await _queue.ReceiveAsync(o => o.CancellationToken = ct).ToListAsync(ct).ConfigureAwait(false)); + } - TestOutput.WriteLine(realizedCommands.Count.ToString()); - TestOutput.WriteLines(realizedCommands.Take(10)); + TestOutput.WriteLine(realizedCommands.Count.ToString()); + TestOutput.WriteLines(realizedCommands.Take(10)); - Assert.Equivalent(sut1.Count, realizedCommands.Count); - Assert.Equivalent(sut1, realizedCommands); - Assert.Equivalent(sut1.Select(message => message.Data), realizedCommands.Select(message => message.Data)); - Assert.Equivalent(sut1.Select(message => message.Data.Metadata), realizedCommands.Select(message => message.Data.Metadata)); - } + Assert.Equivalent(sut1.Count, realizedCommands.Count); + Assert.Equivalent(sut1, realizedCommands); + Assert.Equivalent(sut1.Select(message => message.Data), realizedCommands.Select(message => message.Data)); + Assert.Equivalent(sut1.Select(message => message.Data.Metadata), realizedCommands.Select(message => message.Data.Metadata)); + } - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(IServiceCollection services) + { + services.AddMarshaller(); + services.AddAmazonCommandQueue(o => { - services.AddMarshaller(); - services.AddAmazonCommandQueue(o => + var queue = IsLinux ? "newtonsoft-savvyio-commands" : "newtonsoft-savvyio-commands.fifo"; + o.Endpoint = RegionEndpoint.EUWest1; + if (Configuration["AWS:LocalStack"] != null) { - var queue = IsLinux ? "newtonsoft-savvyio-commands" : "newtonsoft-savvyio-commands.fifo"; - o.Endpoint = RegionEndpoint.EUWest1; - if (Configuration["AWS:LocalStack"] != null) + o.Credentials = new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); + o.SourceQueue = new Uri($"http://sqs.eu-west-1.localhost.localstack.cloud:4566/000000000000/{queue}"); + o.ConfigureClient(client => { - o.Credentials = new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); - o.SourceQueue = new Uri($"http://sqs.eu-west-1.localhost.localstack.cloud:4566/000000000000/{queue}"); - o.ConfigureClient(client => - { - client.ServiceURL = Configuration["AWS:LocalStack"]; - client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; - }); - } - else - { - o.Credentials = new BasicAWSCredentials(Configuration["AWS:IAM:AccessKey"], Configuration["AWS:IAM:SecretKey"]); - o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{Configuration["AWS:CallerIdentity"]}/{queue}"); - } - o.ReceiveContext.UseApproximateNumberOfMessages = true; - }); - } + client.ServiceURL = Configuration["AWS:LocalStack"]; + client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; + }); + } + else + { + o.Credentials = new BasicAWSCredentials(Configuration["AWS:IAM:AccessKey"], Configuration["AWS:IAM:SecretKey"]); + o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{Configuration["AWS:CallerIdentity"]}/{queue}"); + } + o.ReceiveContext.UseApproximateNumberOfMessages = true; + }); } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/EventDriven/AmazonEventBusJsonSerializerContextTest.cs b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/EventDriven/AmazonEventBusJsonSerializerContextTest.cs index 927e4162..664d5b19 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/EventDriven/AmazonEventBusJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/EventDriven/AmazonEventBusJsonSerializerContextTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -24,225 +24,224 @@ using Xunit; using Xunit.v3.Priority; -namespace Savvyio.Extensions.SimpleQueueService.EventDriven +namespace Savvyio.Extensions.SimpleQueueService.EventDriven; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class AmazonEventBusJsonSerializerContextTest : HostTest { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class AmazonEventBusJsonSerializerContextTest : HostTest + private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + private readonly AmazonEventBus _bus; + private static readonly InMemoryTestStore> Comparer = new(); + private readonly IMarshaller _marshaller; + private static readonly string UniqueType = $"{Guid.NewGuid():N}"; + + public AmazonEventBusJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) { - private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - private readonly AmazonEventBus _bus; - private static readonly InMemoryTestStore> Comparer = new(); - private readonly IMarshaller _marshaller; - private static readonly string UniqueType = $"{Guid.NewGuid():N}"; + _bus = fixture.Host.Services.GetRequiredService(); + _marshaller = fixture.Host.Services.GetRequiredService(); + } - public AmazonEventBusJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - _bus = fixture.Host.Services.GetRequiredService(); - _marshaller = fixture.Host.Services.GetRequiredService(); - } + [Fact, Priority(0)] + public async Task PublishAsync_MemberCreated_OneTime() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = (IsLinux ? "member-events-one" : "member-events-one.fifo").ToSnsUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event"); - [Fact, Priority(0)] - public async Task PublishAsync_MemberCreated_OneTime() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = (IsLinux ? "member-events-one" : "member-events-one.fifo").ToSnsUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event"); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut3, o => o.Delimiter = System.Environment.NewLine)); + Comparer.Add(sut3); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut3, o => o.Delimiter = System.Environment.NewLine)); - Comparer.Add(sut3); + await _bus.PublishAsync(sut3); + } - await _bus.PublishAsync(sut3); - } + [Fact, Priority(1)] + public async Task SubscribeAsync_MemberCreated_OneTime() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event")).Single(); - [Fact, Priority(1)] - public async Task SubscribeAsync_MemberCreated_OneTime() + await _bus.SubscribeAsync((sut2, _) => { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event")).Single(); + if (!sut2.Type.Contains($"{UniqueType}.updated-event")) { return Task.CompletedTask; } + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + return Task.CompletedTask; + }).ConfigureAwait(false); + Assert.Equal(1, handlerInvocations); + } - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{UniqueType}.updated-event")) { return Task.CompletedTask; } - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - return Task.CompletedTask; - }).ConfigureAwait(false); - Assert.Equal(1, handlerInvocations); - } - - [Fact, Priority(2)] - public async Task PublishAsync_MemberCreated_OneTime_Signed() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = (IsLinux ? "member-events-one" : "member-events-one.fifo").ToSnsUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.signed").Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + [Fact, Priority(2)] + public async Task PublishAsync_MemberCreated_OneTime_Signed() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = (IsLinux ? "member-events-one" : "member-events-one.fifo").ToSnsUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.signed").Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - Comparer.Add(sut3); + Comparer.Add(sut3); - await _bus.PublishAsync(sut3); - } + await _bus.PublishAsync(sut3); + } - [Fact, Priority(3)] - public async Task SubscribeAsync_MemberCreated_OneTime_Signed() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.signed")).Single(); + [Fact, Priority(3)] + public async Task SubscribeAsync_MemberCreated_OneTime_Signed() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.signed")).Single(); - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{UniqueType}.updated-event.signed")) { return Task.CompletedTask; } - ((ISignedMessage)sut2).CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - return Task.CompletedTask; - }).ConfigureAwait(false); - Assert.Equal(1, handlerInvocations); - } - - [Fact, Priority(4)] - public async Task PublishAsync_MemberCreated_OneTime_CloudEvent() + await _bus.SubscribeAsync((sut2, _) => { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = (IsLinux ? "member-events-one" : "member-events-one.fifo").ToSnsUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.cloud-event").ToCloudEvent(); + if (!sut2.Type.Contains($"{UniqueType}.updated-event.signed")) { return Task.CompletedTask; } + ((ISignedMessage)sut2).CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + return Task.CompletedTask; + }).ConfigureAwait(false); + Assert.Equal(1, handlerInvocations); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(4)] + public async Task PublishAsync_MemberCreated_OneTime_CloudEvent() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = (IsLinux ? "member-events-one" : "member-events-one.fifo").ToSnsUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.cloud-event").ToCloudEvent(); - Comparer.Add(sut3); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - await _bus.PublishAsync(sut3); - } + Comparer.Add(sut3); - [Fact, Priority(5)] - public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.cloud-event")).Single() as ICloudEvent; + await _bus.PublishAsync(sut3); + } - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{UniqueType}.updated-event.cloud-event")) { return Task.CompletedTask; } - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - Assert.Equivalent(sut1.Specversion, ((ICloudEvent)sut2).Specversion); - return Task.CompletedTask; - }).ConfigureAwait(false); - Assert.Equal(1, handlerInvocations); - } - - [Fact, Priority(6)] - public async Task PublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + [Fact, Priority(5)] + public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.cloud-event")).Single() as ICloudEvent; + + await _bus.SubscribeAsync((sut2, _) => { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = (IsLinux ? "member-events-one" : "member-events-one.fifo").ToSnsUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + if (!sut2.Type.Contains($"{UniqueType}.updated-event.cloud-event")) { return Task.CompletedTask; } + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + Assert.Equivalent(sut1.Specversion, ((ICloudEvent)sut2).Specversion); + return Task.CompletedTask; + }).ConfigureAwait(false); + Assert.Equal(1, handlerInvocations); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(6)] + public async Task PublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = (IsLinux ? "member-events-one" : "member-events-one.fifo").ToSnsUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Comparer.Add(sut3); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - await _bus.PublishAsync(sut3); - } + Comparer.Add(sut3); - [Fact, Priority(7)] - public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent_Signed() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.signed-cloud-event")).Single(); + await _bus.PublishAsync(sut3); + } - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{UniqueType}.updated-event.signed-cloud-event")) { return Task.CompletedTask; } - ((ISignedCloudEvent)sut2).CheckCloudEventSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - return Task.CompletedTask; - }).ConfigureAwait(false); - Assert.Equal(1, handlerInvocations); - } - - [Fact, Priority(8)] - public async Task PublishAsync_MemberCreated_HundredTimes() // reduced as SNS is not part of free tier (FIFO) - { - var messages = Generate.RangeOf(100, _ => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new MemberCreated(Generate.RandomString(10), email).ToMessage((IsLinux ? "member-events-many" : "member-events-many.fifo").ToSnsUri(), $"{nameof(MemberCreated)}.{UniqueType}"); - }); + [Fact, Priority(7)] + public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.signed-cloud-event")).Single(); - await ParallelFactory.ForEachAsync(messages, (message, token) => - { - Comparer.Add(message); - return _bus.PublishAsync(message, o => o.CancellationToken = token); - }).ConfigureAwait(false); - } + await _bus.SubscribeAsync((sut2, _) => + { + if (!sut2.Type.Contains($"{UniqueType}.updated-event.signed-cloud-event")) { return Task.CompletedTask; } + ((ISignedCloudEvent)sut2).CheckCloudEventSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + return Task.CompletedTask; + }).ConfigureAwait(false); + Assert.Equal(1, handlerInvocations); + } - [Fact, Priority(9)] - public async Task SubscribeAsync_MemberCreated_All() + [Fact, Priority(8)] + public async Task PublishAsync_MemberCreated_HundredTimes() // reduced as SNS is not part of free tier (FIFO) + { + var messages = Generate.RangeOf(100, _ => { - var sut1 = Comparer.Query(message => message.Source.Contains("member-events-many") && message.Type.Contains($"{UniqueType}")).ToList(); - var sut2 = new List>(); - var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new MemberCreated(Generate.RandomString(10), email).ToMessage((IsLinux ? "member-events-many" : "member-events-many.fifo").ToSnsUri(), $"{nameof(MemberCreated)}.{UniqueType}"); + }); - await _bus.SubscribeAsync((message, _) => - { - if (!message.Type.Contains($"{UniqueType}")) { return Task.CompletedTask; } - sut2.Add(message); - return Task.CompletedTask; - }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); + await ParallelFactory.ForEachAsync(messages, (message, token) => + { + Comparer.Add(message); + return _bus.PublishAsync(message, o => o.CancellationToken = token); + }).ConfigureAwait(false); + } - TestOutput.WriteLine(sut2.Count.ToString()); - TestOutput.WriteLines(sut2.Take(10)); + [Fact, Priority(9)] + public async Task SubscribeAsync_MemberCreated_All() + { + var sut1 = Comparer.Query(message => message.Source.Contains("member-events-many") && message.Type.Contains($"{UniqueType}")).ToList(); + var sut2 = new List>(); + var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - Assert.Equivalent(sut1.Count, sut2.Count); - Assert.Equivalent(sut1, sut2); - Assert.Equivalent(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); - Assert.Equivalent(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); - } + await _bus.SubscribeAsync((message, _) => + { + if (!message.Type.Contains($"{UniqueType}")) { return Task.CompletedTask; } + sut2.Add(message); + return Task.CompletedTask; + }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); + + TestOutput.WriteLine(sut2.Count.ToString()); + TestOutput.WriteLines(sut2.Take(10)); + + Assert.Equivalent(sut1.Count, sut2.Count); + Assert.Equivalent(sut1, sut2); + Assert.Equivalent(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); + Assert.Equivalent(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); + } - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(IServiceCollection services) + { + services.AddMarshaller(); + services.AddAmazonEventBus(o => { - services.AddMarshaller(); - services.AddAmazonEventBus(o => + var queue = IsLinux ? "savvyio-events" : "savvyio-events.fifo"; + o.Endpoint = RegionEndpoint.EUWest1; + if (Configuration["AWS:LocalStack"] != null) { - var queue = IsLinux ? "savvyio-events" : "savvyio-events.fifo"; - o.Endpoint = RegionEndpoint.EUWest1; - if (Configuration["AWS:LocalStack"] != null) + o.Credentials = new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); + o.SourceQueue = new Uri($"http://sqs.eu-west-1.localhost.localstack.cloud:4566/000000000000/{queue}"); + o.ConfigureClient(client => { - o.Credentials = new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); - o.SourceQueue = new Uri($"http://sqs.eu-west-1.localhost.localstack.cloud:4566/000000000000/{queue}"); - o.ConfigureClient(client => - { - client.ServiceURL = Configuration["AWS:LocalStack"]; - client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; - }); - } - else - { - AmazonResourceNameOptions.DefaultAccountId = Configuration["AWS:CallerIdentity"]; - o.Credentials = new BasicAWSCredentials(Configuration["AWS:IAM:AccessKey"], Configuration["AWS:IAM:SecretKey"]); - o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{Configuration["AWS:CallerIdentity"]}/{queue}"); - } - }); - } + client.ServiceURL = Configuration["AWS:LocalStack"]; + client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; + }); + } + else + { + AmazonResourceNameOptions.DefaultAccountId = Configuration["AWS:CallerIdentity"]; + o.Credentials = new BasicAWSCredentials(Configuration["AWS:IAM:AccessKey"], Configuration["AWS:IAM:SecretKey"]); + o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{Configuration["AWS:CallerIdentity"]}/{queue}"); + } + }); } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/EventDriven/AmazonEventBusNewtonsoftJsonSerializerContextTest.cs b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/EventDriven/AmazonEventBusNewtonsoftJsonSerializerContextTest.cs index 2c400d6c..e4ce0556 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/EventDriven/AmazonEventBusNewtonsoftJsonSerializerContextTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/EventDriven/AmazonEventBusNewtonsoftJsonSerializerContextTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -24,225 +24,224 @@ using Savvyio.Extensions.DependencyInjection; using Xunit.v3.Priority; -namespace Savvyio.Extensions.SimpleQueueService.EventDriven +namespace Savvyio.Extensions.SimpleQueueService.EventDriven; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class AmazonEventBusNewtonsoftJsonSerializerContextTest : HostTest { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class AmazonEventBusNewtonsoftJsonSerializerContextTest : HostTest + private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + private readonly AmazonEventBus _bus; + private static readonly InMemoryTestStore> Comparer = new(); + private readonly IMarshaller _marshaller; + private static readonly string UniqueType = $"{Guid.NewGuid():N}"; + + public AmazonEventBusNewtonsoftJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) { - private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - private readonly AmazonEventBus _bus; - private static readonly InMemoryTestStore> Comparer = new(); - private readonly IMarshaller _marshaller; - private static readonly string UniqueType = $"{Guid.NewGuid():N}"; + _bus = fixture.Host.Services.GetRequiredService(); + _marshaller = fixture.Host.Services.GetRequiredService(); + } - public AmazonEventBusNewtonsoftJsonSerializerContextTest(ManagedHostFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - _bus = fixture.Host.Services.GetRequiredService(); - _marshaller = fixture.Host.Services.GetRequiredService(); - } + [Fact, Priority(0)] + public async Task PublishAsync_MemberCreated_OneTime() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = (IsLinux ? "newtonsoft-member-events-one" : "newtonsoft-member-events-one.fifo").ToSnsUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event"); - [Fact, Priority(0)] - public async Task PublishAsync_MemberCreated_OneTime() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = (IsLinux ? "newtonsoft-member-events-one" : "newtonsoft-member-events-one.fifo").ToSnsUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event"); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + Comparer.Add(sut3); - Comparer.Add(sut3); + await _bus.PublishAsync(sut3); + } - await _bus.PublishAsync(sut3); - } + [Fact, Priority(1)] + public async Task SubscribeAsync_MemberCreated_OneTime() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event")).Single(); - [Fact, Priority(1)] - public async Task SubscribeAsync_MemberCreated_OneTime() + await _bus.SubscribeAsync((sut2, _) => { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event")).Single(); + if (!sut2.Type.Contains($"{UniqueType}.updated-event")) { return Task.CompletedTask; } + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + return Task.CompletedTask; + }).ConfigureAwait(false); + Assert.Equal(1, handlerInvocations); + } - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{UniqueType}.updated-event")) { return Task.CompletedTask; } - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - return Task.CompletedTask; - }).ConfigureAwait(false); - Assert.Equal(1, handlerInvocations); - } - - [Fact, Priority(2)] - public async Task PublishAsync_MemberCreated_OneTime_Signed() - { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = (IsLinux ? "newtonsoft-member-events-one" : "newtonsoft-member-events-one.fifo").ToSnsUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.signed").Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + [Fact, Priority(2)] + public async Task PublishAsync_MemberCreated_OneTime_Signed() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = (IsLinux ? "newtonsoft-member-events-one" : "newtonsoft-member-events-one.fifo").ToSnsUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.signed").Sign(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - Comparer.Add(sut3); + Comparer.Add(sut3); - await _bus.PublishAsync(sut3); - } + await _bus.PublishAsync(sut3); + } - [Fact, Priority(3)] - public async Task SubscribeAsync_MemberCreated_OneTime_Signed() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.signed")).Single(); + [Fact, Priority(3)] + public async Task SubscribeAsync_MemberCreated_OneTime_Signed() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.signed")).Single(); - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{UniqueType}.updated-event.signed")) { return Task.CompletedTask; } - ((ISignedMessage)sut2).CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - return Task.CompletedTask; - }).ConfigureAwait(false); - Assert.Equal(1, handlerInvocations); - } - - [Fact, Priority(4)] - public async Task PublishAsync_MemberCreated_OneTime_CloudEvent() + await _bus.SubscribeAsync((sut2, _) => { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = (IsLinux ? "newtonsoft-member-events-one" : "newtonsoft-member-events-one.fifo").ToSnsUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.cloud-event").ToCloudEvent(); + if (!sut2.Type.Contains($"{UniqueType}.updated-event.signed")) { return Task.CompletedTask; } + ((ISignedMessage)sut2).CheckSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + return Task.CompletedTask; + }).ConfigureAwait(false); + Assert.Equal(1, handlerInvocations); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(4)] + public async Task PublishAsync_MemberCreated_OneTime_CloudEvent() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = (IsLinux ? "newtonsoft-member-events-one" : "newtonsoft-member-events-one.fifo").ToSnsUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.cloud-event").ToCloudEvent(); - Comparer.Add(sut3); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - await _bus.PublishAsync(sut3); - } + Comparer.Add(sut3); - [Fact, Priority(5)] - public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.cloud-event")).Single() as ICloudEvent; + await _bus.PublishAsync(sut3); + } - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{UniqueType}.updated-event.cloud-event")) { return Task.CompletedTask; } - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - Assert.Equivalent(sut1.Specversion, ((ICloudEvent)sut2).Specversion); - return Task.CompletedTask; - }).ConfigureAwait(false); - Assert.Equal(1, handlerInvocations); - } - - [Fact, Priority(6)] - public async Task PublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + [Fact, Priority(5)] + public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.cloud-event")).Single() as ICloudEvent; + + await _bus.SubscribeAsync((sut2, _) => { - var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); - var sut2 = (IsLinux ? "newtonsoft-member-events-one" : "newtonsoft-member-events-one.fifo").ToSnsUri(); - var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + if (!sut2.Type.Contains($"{UniqueType}.updated-event.cloud-event")) { return Task.CompletedTask; } + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + Assert.Equivalent(sut1.Specversion, ((ICloudEvent)sut2).Specversion); + return Task.CompletedTask; + }).ConfigureAwait(false); + Assert.Equal(1, handlerInvocations); + } - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); + [Fact, Priority(6)] + public async Task PublishAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var sut1 = new MemberCreated("John Doe", "jd@outlook.com"); + var sut2 = (IsLinux ? "newtonsoft-member-events-one" : "newtonsoft-member-events-one.fifo").ToSnsUri(); + var sut3 = sut1.ToMessage(sut2, $"{nameof(MemberCreated)}.{UniqueType}.updated-event.signed-cloud-event").ToCloudEvent().SignCloudEvent(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - Comparer.Add(sut3); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = System.Environment.NewLine)); - await _bus.PublishAsync(sut3); - } + Comparer.Add(sut3); - [Fact, Priority(7)] - public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent_Signed() - { - var handlerInvocations = 0; - var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.signed-cloud-event")).Single(); + await _bus.PublishAsync(sut3); + } - await _bus.SubscribeAsync((sut2, _) => - { - if (!sut2.Type.Contains($"{UniqueType}.updated-event.signed-cloud-event")) { return Task.CompletedTask; } - ((ISignedCloudEvent)sut2).CheckCloudEventSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - handlerInvocations++; - Assert.Equivalent(sut1.Data, sut2.Data); - Assert.Equivalent(sut1.Time, sut2.Time); - Assert.Equivalent(sut1.Source, sut2.Source); - Assert.Equivalent(sut1.Id, sut2.Id); - Assert.Equivalent(sut1.Type, sut2.Type); - return Task.CompletedTask; - }).ConfigureAwait(false); - Assert.Equal(1, handlerInvocations); - } - - [Fact, Priority(8)] - public async Task PublishAsync_MemberCreated_HundredTimes() // reduced as SNS is not part of free tier (FIFO) - { - var messages = Generate.RangeOf(100, _ => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new MemberCreated(Generate.RandomString(10), email).ToMessage((IsLinux ? "newtonsoft-member-events-many" : "newtonsoft-member-events-many.fifo").ToSnsUri(), $"{nameof(MemberCreated)}.{UniqueType}"); - }); + [Fact, Priority(7)] + public async Task SubscribeAsync_MemberCreated_OneTime_CloudEvent_Signed() + { + var handlerInvocations = 0; + var sut1 = Comparer.Query(message => message.Type.Contains($"{UniqueType}.updated-event.signed-cloud-event")).Single(); - await ParallelFactory.ForEachAsync(messages, (message, token) => - { - Comparer.Add(message); - return _bus.PublishAsync(message, o => o.CancellationToken = token); - }).ConfigureAwait(false); - } + await _bus.SubscribeAsync((sut2, _) => + { + if (!sut2.Type.Contains($"{UniqueType}.updated-event.signed-cloud-event")) { return Task.CompletedTask; } + ((ISignedCloudEvent)sut2).CheckCloudEventSignature(_marshaller, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + handlerInvocations++; + Assert.Equivalent(sut1.Data, sut2.Data); + Assert.Equivalent(sut1.Time, sut2.Time); + Assert.Equivalent(sut1.Source, sut2.Source); + Assert.Equivalent(sut1.Id, sut2.Id); + Assert.Equivalent(sut1.Type, sut2.Type); + return Task.CompletedTask; + }).ConfigureAwait(false); + Assert.Equal(1, handlerInvocations); + } - [Fact, Priority(9)] - public async Task SubscribeAsync_MemberCreated_All() + [Fact, Priority(8)] + public async Task PublishAsync_MemberCreated_HundredTimes() // reduced as SNS is not part of free tier (FIFO) + { + var messages = Generate.RangeOf(100, _ => { - var sut1 = Comparer.Query(message => message.Source.Contains("newtonsoft-member-events-many") && message.Type.Contains($"{UniqueType}")).ToList(); - var sut2 = new List>(); - var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new MemberCreated(Generate.RandomString(10), email).ToMessage((IsLinux ? "newtonsoft-member-events-many" : "newtonsoft-member-events-many.fifo").ToSnsUri(), $"{nameof(MemberCreated)}.{UniqueType}"); + }); - await _bus.SubscribeAsync((message, _) => - { - if (!message.Type.Contains($"{UniqueType}")) { return Task.CompletedTask; } - sut2.Add(message); - return Task.CompletedTask; - }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); + await ParallelFactory.ForEachAsync(messages, (message, token) => + { + Comparer.Add(message); + return _bus.PublishAsync(message, o => o.CancellationToken = token); + }).ConfigureAwait(false); + } - TestOutput.WriteLine(sut2.Count.ToString()); - TestOutput.WriteLines(sut2.Take(10)); + [Fact, Priority(9)] + public async Task SubscribeAsync_MemberCreated_All() + { + var sut1 = Comparer.Query(message => message.Source.Contains("newtonsoft-member-events-many") && message.Type.Contains($"{UniqueType}")).ToList(); + var sut2 = new List>(); + var sut3 = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - Assert.Equivalent(sut1.Count, sut2.Count); - Assert.Equivalent(sut1, sut2); - Assert.Equivalent(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); - Assert.Equivalent(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); - } + await _bus.SubscribeAsync((message, _) => + { + if (!message.Type.Contains($"{UniqueType}")) { return Task.CompletedTask; } + sut2.Add(message); + return Task.CompletedTask; + }, o => o.CancellationToken = sut3.Token).ConfigureAwait(false); + + TestOutput.WriteLine(sut2.Count.ToString()); + TestOutput.WriteLines(sut2.Take(10)); + + Assert.Equivalent(sut1.Count, sut2.Count); + Assert.Equivalent(sut1, sut2); + Assert.Equivalent(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); + Assert.Equivalent(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); + } - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(IServiceCollection services) + { + services.AddMarshaller(); + services.AddAmazonEventBus(o => { - services.AddMarshaller(); - services.AddAmazonEventBus(o => + var queue = IsLinux ? "newtonsoft-savvyio-events" : "newtonsoft-savvyio-events.fifo"; + o.Endpoint = RegionEndpoint.EUWest1; + if (Configuration["AWS:LocalStack"] != null) { - var queue = IsLinux ? "newtonsoft-savvyio-events" : "newtonsoft-savvyio-events.fifo"; - o.Endpoint = RegionEndpoint.EUWest1; - if (Configuration["AWS:LocalStack"] != null) + o.Credentials = new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); + o.SourceQueue = new Uri($"http://sqs.eu-west-1.localhost.localstack.cloud:4566/000000000000/{queue}"); + o.ConfigureClient(client => { - o.Credentials = new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); - o.SourceQueue = new Uri($"http://sqs.eu-west-1.localhost.localstack.cloud:4566/000000000000/{queue}"); - o.ConfigureClient(client => - { - client.ServiceURL = Configuration["AWS:LocalStack"]; - client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; - }); - } - else - { - AmazonResourceNameOptions.DefaultAccountId = Configuration["AWS:CallerIdentity"]; - o.Credentials = new BasicAWSCredentials(Configuration["AWS:IAM:AccessKey"], Configuration["AWS:IAM:SecretKey"]); - o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{Configuration["AWS:CallerIdentity"]}/{queue}"); - } - }); - } + client.ServiceURL = Configuration["AWS:LocalStack"]; + client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; + }); + } + else + { + AmazonResourceNameOptions.DefaultAccountId = Configuration["AWS:CallerIdentity"]; + o.Credentials = new BasicAWSCredentials(Configuration["AWS:IAM:AccessKey"], Configuration["AWS:IAM:SecretKey"]); + o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{Configuration["AWS:CallerIdentity"]}/{queue}"); + } + }); } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Savvyio.Extensions.SimpleQueueService.FunctionalTests.csproj b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Savvyio.Extensions.SimpleQueueService.FunctionalTests.csproj index 76211483..0ca44ebc 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Savvyio.Extensions.SimpleQueueService.FunctionalTests.csproj +++ b/test/Savvyio.Extensions.SimpleQueueService.FunctionalTests/Savvyio.Extensions.SimpleQueueService.FunctionalTests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Extensions.SimpleQueueService @@ -17,5 +17,5 @@ - + diff --git a/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageOptionsTest.cs b/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageOptionsTest.cs index a530012c..855daabb 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageOptionsTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageOptionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using Amazon; @@ -9,146 +9,145 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +public class AmazonMessageOptionsTest : Test { - public class AmazonMessageOptionsTest : Test + public AmazonMessageOptionsTest(ITestOutputHelper output) : base(output) { - public AmazonMessageOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Ctor_ShouldHaveDefaultValues() - { - var sut = new AmazonMessageOptions(); + [Fact] + public void Ctor_ShouldHaveDefaultValues() + { + var sut = new AmazonMessageOptions(); - Assert.Null(sut.Endpoint); - Assert.Null(sut.SourceQueue); - Assert.Null(sut.Credentials); - Assert.Empty(sut.ClientConfigurations); - } + Assert.Null(sut.Endpoint); + Assert.Null(sut.SourceQueue); + Assert.Null(sut.Credentials); + Assert.Empty(sut.ClientConfigurations); + } - [Fact] - public void ValidateOptions_ThrowsInvalidOperationException_WhenEndpointIsNull() + [Fact] + public void ValidateOptions_ThrowsInvalidOperationException_WhenEndpointIsNull() + { + var sut1 = new AmazonMessageOptions { - var sut1 = new AmazonMessageOptions - { - SourceQueue = new Uri("urn:null"), - Credentials = new AnonymousAWSCredentials() - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - - Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonMessageOptions.Endpoint)} == null')", sut2.Message); - Assert.Equal($"{nameof(AmazonMessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } - - [Fact] - public void ValidateOptions_ThrowsInvalidOperationException_WhenSourceQueueIsNull() + SourceQueue = new Uri("urn:null"), + Credentials = new AnonymousAWSCredentials() + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + + Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonMessageOptions.Endpoint)} == null')", sut2.Message); + Assert.Equal($"{nameof(AmazonMessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } + + [Fact] + public void ValidateOptions_ThrowsInvalidOperationException_WhenSourceQueueIsNull() + { + var sut1 = new AmazonMessageOptions { - var sut1 = new AmazonMessageOptions - { - Endpoint = RegionEndpoint.EUWest1, - Credentials = new AnonymousAWSCredentials() - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - - Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonMessageOptions.SourceQueue)} == null')", sut2.Message); - Assert.Equal($"{nameof(AmazonMessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } - - [Fact] - public void ValidateOptions_ThrowsInvalidOperationException_WhenCredentialsIsNull() + Endpoint = RegionEndpoint.EUWest1, + Credentials = new AnonymousAWSCredentials() + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + + Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonMessageOptions.SourceQueue)} == null')", sut2.Message); + Assert.Equal($"{nameof(AmazonMessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } + + [Fact] + public void ValidateOptions_ThrowsInvalidOperationException_WhenCredentialsIsNull() + { + var sut1 = new AmazonMessageOptions { - var sut1 = new AmazonMessageOptions - { - Endpoint = RegionEndpoint.EUWest1, - SourceQueue = new Uri("urn:null"), - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - - Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonMessageOptions.Credentials)} == null')", sut2.Message); - Assert.Equal($"{nameof(AmazonMessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } - - [Fact] - public void ValidateOptions_ShouldPass() + Endpoint = RegionEndpoint.EUWest1, + SourceQueue = new Uri("urn:null"), + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + + Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonMessageOptions.Credentials)} == null')", sut2.Message); + Assert.Equal($"{nameof(AmazonMessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } + + [Fact] + public void ValidateOptions_ShouldPass() + { + var sut1 = new AmazonMessageOptions { - var sut1 = new AmazonMessageOptions - { - Credentials = new AnonymousAWSCredentials(), - Endpoint = RegionEndpoint.EUWest1, - SourceQueue = new Uri("urn:null") - }; - - sut1.ValidateOptions(); - - Assert.Equivalent(new AnonymousAWSCredentials(), sut1.Credentials, true); - Assert.Equal(RegionEndpoint.EUWest1, sut1.Endpoint); - Assert.Equal(new Uri("urn:null"), sut1.SourceQueue); - Assert.Equal(0, sut1.ClientConfigurations.Length); - } - - [Fact] - public void ValidateOptions_ShouldPassWithConfiguredClient() + Credentials = new AnonymousAWSCredentials(), + Endpoint = RegionEndpoint.EUWest1, + SourceQueue = new Uri("urn:null") + }; + + sut1.ValidateOptions(); + + Assert.Equivalent(new AnonymousAWSCredentials(), sut1.Credentials, true); + Assert.Equal(RegionEndpoint.EUWest1, sut1.Endpoint); + Assert.Equal(new Uri("urn:null"), sut1.SourceQueue); + Assert.Equal(0, sut1.ClientConfigurations.Length); + } + + [Fact] + public void ValidateOptions_ShouldPassWithConfiguredClient() + { + var sut1 = new AmazonMessageOptions { - var sut1 = new AmazonMessageOptions - { - Credentials = new AnonymousAWSCredentials(), - Endpoint = RegionEndpoint.EUWest1, - SourceQueue = new Uri("urn:null") - }.ConfigureClient(client => - { - client.ServiceURL = "http://localhost:4566"; - client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; - }); - - sut1.ValidateOptions(); - - Assert.Equivalent(new AnonymousAWSCredentials(), sut1.Credentials, true); - Assert.Equal(RegionEndpoint.EUWest1, sut1.Endpoint); - Assert.Equal(new Uri("urn:null"), sut1.SourceQueue); - Assert.True(sut1.ClientConfigurations.All(config => config is AmazonSQSConfig || config is AmazonSimpleNotificationServiceConfig)); - Assert.Equal(2, sut1.ClientConfigurations.Length); - Assert.Equal("http://localhost:4566/", sut1.ClientConfigurations.SimpleQueueService().ServiceURL); - Assert.Equal("eu-west-1", sut1.ClientConfigurations.SimpleQueueService().AuthenticationRegion); - Assert.Equal(sut1.ClientConfigurations.SimpleQueueService().ServiceURL, sut1.ClientConfigurations.SimpleNotificationService().ServiceURL); - Assert.Equal(sut1.ClientConfigurations.SimpleQueueService().AuthenticationRegion, sut1.ClientConfigurations.SimpleNotificationService().AuthenticationRegion); - } - - [Fact] - public void ConfigureClient_Should_Throw_When_Setup_Is_Null() + Credentials = new AnonymousAWSCredentials(), + Endpoint = RegionEndpoint.EUWest1, + SourceQueue = new Uri("urn:null") + }.ConfigureClient(client => { - var sut = new AmazonMessageOptions(); + client.ServiceURL = "http://localhost:4566"; + client.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; + }); + + sut1.ValidateOptions(); + + Assert.Equivalent(new AnonymousAWSCredentials(), sut1.Credentials, true); + Assert.Equal(RegionEndpoint.EUWest1, sut1.Endpoint); + Assert.Equal(new Uri("urn:null"), sut1.SourceQueue); + Assert.True(sut1.ClientConfigurations.All(config => config is AmazonSQSConfig || config is AmazonSimpleNotificationServiceConfig)); + Assert.Equal(2, sut1.ClientConfigurations.Length); + Assert.Equal("http://localhost:4566/", sut1.ClientConfigurations.SimpleQueueService().ServiceURL); + Assert.Equal("eu-west-1", sut1.ClientConfigurations.SimpleQueueService().AuthenticationRegion); + Assert.Equal(sut1.ClientConfigurations.SimpleQueueService().ServiceURL, sut1.ClientConfigurations.SimpleNotificationService().ServiceURL); + Assert.Equal(sut1.ClientConfigurations.SimpleQueueService().AuthenticationRegion, sut1.ClientConfigurations.SimpleNotificationService().AuthenticationRegion); + } - Assert.Throws(() => sut.ConfigureClient(null)); - } + [Fact] + public void ConfigureClient_Should_Throw_When_Setup_Is_Null() + { + var sut = new AmazonMessageOptions(); + + Assert.Throws(() => sut.ConfigureClient(null)); + } - [Fact] - public void ValidateOptions_ThrowsInvalidOperationException_WhenClientConfigurationsAreInvalid() + [Fact] + public void ValidateOptions_ThrowsInvalidOperationException_WhenClientConfigurationsAreInvalid() + { + var sut1 = new AmazonMessageOptions { - var sut1 = new AmazonMessageOptions - { - Credentials = new AnonymousAWSCredentials(), - Endpoint = RegionEndpoint.EUWest1, - SourceQueue = new Uri("urn:null") - }; - - typeof(AmazonMessageOptions) - .GetProperty(nameof(AmazonMessageOptions.ClientConfigurations), BindingFlags.Instance | BindingFlags.Public)! - .SetValue(sut1, new ClientConfig[] { new AmazonSQSConfig() }); - - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - - Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonMessageOptions.ClientConfigurations)}.Length > 0 && ({nameof(AmazonMessageOptions.ClientConfigurations)}.Length != 2 || !({nameof(AmazonMessageOptions.ClientConfigurations)}[0] is AmazonSQSConfig && {nameof(AmazonMessageOptions.ClientConfigurations)}[1] is AmazonSimpleNotificationServiceConfig))')", sut2.Message); - Assert.Equal($"{nameof(AmazonMessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } + Credentials = new AnonymousAWSCredentials(), + Endpoint = RegionEndpoint.EUWest1, + SourceQueue = new Uri("urn:null") + }; + + typeof(AmazonMessageOptions) + .GetProperty(nameof(AmazonMessageOptions.ClientConfigurations), BindingFlags.Instance | BindingFlags.Public)! + .SetValue(sut1, new ClientConfig[] { new AmazonSQSConfig() }); + + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + + Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonMessageOptions.ClientConfigurations)}.Length > 0 && ({nameof(AmazonMessageOptions.ClientConfigurations)}.Length != 2 || !({nameof(AmazonMessageOptions.ClientConfigurations)}[0] is AmazonSQSConfig && {nameof(AmazonMessageOptions.ClientConfigurations)}[1] is AmazonSimpleNotificationServiceConfig))')", sut2.Message); + Assert.Equal($"{nameof(AmazonMessageOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageReceiveOptionsTest.cs b/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageReceiveOptionsTest.cs index 238b6391..18de632a 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageReceiveOptionsTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageReceiveOptionsTest.cs @@ -1,62 +1,61 @@ -using System; +using System; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +public class AmazonMessageReceiveOptionsTest : Test { - public class AmazonMessageReceiveOptionsTest : Test + public AmazonMessageReceiveOptionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Ctor_ShouldHaveDefaultValues() + { + var sut = new AmazonMessageReceiveOptions(); + + Assert.Equal(10, sut.NumberOfMessagesToTakePerRequest); + Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.MaxPollingWaitTimeInSeconds), sut.PollingTimeout); + Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.DefaultVisibilityTimeoutInSeconds), sut.VisibilityTimeout); + Assert.True(sut.AssumeMessageProcessed); + Assert.True(sut.RemoveProcessedMessages); + Assert.False(sut.UseApproximateNumberOfMessages); + } + + [Fact] + public void NumberOfMessagesToTakePerRequest_ShouldClampValue_WhenOutsideRangeOfAllowedLimits() { - public AmazonMessageReceiveOptionsTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Ctor_ShouldHaveDefaultValues() - { - var sut = new AmazonMessageReceiveOptions(); - - Assert.Equal(10, sut.NumberOfMessagesToTakePerRequest); - Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.MaxPollingWaitTimeInSeconds), sut.PollingTimeout); - Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.DefaultVisibilityTimeoutInSeconds), sut.VisibilityTimeout); - Assert.True(sut.AssumeMessageProcessed); - Assert.True(sut.RemoveProcessedMessages); - Assert.False(sut.UseApproximateNumberOfMessages); - } - - [Fact] - public void NumberOfMessagesToTakePerRequest_ShouldClampValue_WhenOutsideRangeOfAllowedLimits() - { - var sut = new AmazonMessageReceiveOptions(); - - sut.NumberOfMessagesToTakePerRequest = int.MinValue; - Assert.Equal(1, sut.NumberOfMessagesToTakePerRequest); - - sut.NumberOfMessagesToTakePerRequest = int.MaxValue; - Assert.Equal(AmazonMessageOptions.MaxNumberOfMessages, sut.NumberOfMessagesToTakePerRequest); - } - - [Fact] - public void PollingTimeout_ShouldClampValue_WhenOutsideRangeOfAllowedLimits() - { - var sut = new AmazonMessageReceiveOptions(); - - sut.PollingTimeout = TimeSpan.MinValue; - Assert.Equal(TimeSpan.Zero, sut.PollingTimeout); - - sut.PollingTimeout = TimeSpan.MaxValue; - Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.MaxPollingWaitTimeInSeconds), sut.PollingTimeout); - } - - [Fact] - public void VisibilityTimeout_ShouldClampValue_WhenOutsideRangeOfAllowedLimits() - { - var sut = new AmazonMessageReceiveOptions(); - - sut.VisibilityTimeout = TimeSpan.MinValue; - Assert.Equal(TimeSpan.Zero, sut.VisibilityTimeout); - - sut.VisibilityTimeout = TimeSpan.FromSeconds(AmazonMessageOptions.MaxVisibilityTimeoutInSeconds + 1); - Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.MaxVisibilityTimeoutInSeconds), sut.VisibilityTimeout); - } + var sut = new AmazonMessageReceiveOptions(); + + sut.NumberOfMessagesToTakePerRequest = int.MinValue; + Assert.Equal(1, sut.NumberOfMessagesToTakePerRequest); + + sut.NumberOfMessagesToTakePerRequest = int.MaxValue; + Assert.Equal(AmazonMessageOptions.MaxNumberOfMessages, sut.NumberOfMessagesToTakePerRequest); + } + + [Fact] + public void PollingTimeout_ShouldClampValue_WhenOutsideRangeOfAllowedLimits() + { + var sut = new AmazonMessageReceiveOptions(); + + sut.PollingTimeout = TimeSpan.MinValue; + Assert.Equal(TimeSpan.Zero, sut.PollingTimeout); + + sut.PollingTimeout = TimeSpan.MaxValue; + Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.MaxPollingWaitTimeInSeconds), sut.PollingTimeout); + } + + [Fact] + public void VisibilityTimeout_ShouldClampValue_WhenOutsideRangeOfAllowedLimits() + { + var sut = new AmazonMessageReceiveOptions(); + + sut.VisibilityTimeout = TimeSpan.MinValue; + Assert.Equal(TimeSpan.Zero, sut.VisibilityTimeout); + + sut.VisibilityTimeout = TimeSpan.FromSeconds(AmazonMessageOptions.MaxVisibilityTimeoutInSeconds + 1); + Assert.Equal(TimeSpan.FromSeconds(AmazonMessageOptions.MaxVisibilityTimeoutInSeconds), sut.VisibilityTimeout); } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageTest.cs b/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageTest.cs index 6636451b..368fea2e 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonMessageTest.cs @@ -9,64 +9,63 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +public class AmazonMessageTest : Test { - public class AmazonMessageTest : Test + public AmazonMessageTest(ITestOutputHelper output) : base(output) { - public AmazonMessageTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Constructor_Should_Set_Protected_Properties_For_Standard_Queue() - { - var marshaller = JsonMarshaller.Default; - var options = CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/orders"); + [Fact] + public void Constructor_Should_Set_Protected_Properties_For_Standard_Queue() + { + var marshaller = JsonMarshaller.Default; + var options = CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/orders"); - var sut = new TestAmazonQueue(marshaller, options); + var sut = new TestAmazonQueue(marshaller, options); - Assert.Same(marshaller, sut.ExposedMarshaller); - Assert.Same(options, sut.Options); - Assert.False(sut.UseFirstInFirstOut); - } + Assert.Same(marshaller, sut.ExposedMarshaller); + Assert.Same(options, sut.Options); + Assert.False(sut.UseFirstInFirstOut); + } - [Fact] - public void Constructor_Should_Detect_First_In_First_Out_Queue() - { - var sut = new TestAmazonQueue(JsonMarshaller.Default, CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/orders.fifo")); + [Fact] + public void Constructor_Should_Detect_First_In_First_Out_Queue() + { + var sut = new TestAmazonQueue(JsonMarshaller.Default, CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/orders.fifo")); - Assert.True(sut.UseFirstInFirstOut); - } + Assert.True(sut.UseFirstInFirstOut); + } - private static AmazonMessageOptions CreateOptions(string queueUrl) + private static AmazonMessageOptions CreateOptions(string queueUrl) + { + return new AmazonMessageOptions { - return new AmazonMessageOptions - { - Credentials = new AnonymousAWSCredentials(), - Endpoint = RegionEndpoint.EUWest1, - SourceQueue = new Uri(queueUrl) - }; - } + Credentials = new AnonymousAWSCredentials(), + Endpoint = RegionEndpoint.EUWest1, + SourceQueue = new Uri(queueUrl) + }; + } - private sealed class TestAmazonQueue : AmazonQueue + private sealed class TestAmazonQueue : AmazonQueue + { + public TestAmazonQueue(IMarshaller marshaller, AmazonMessageOptions options) : base(marshaller, options) { - public TestAmazonQueue(IMarshaller marshaller, AmazonMessageOptions options) : base(marshaller, options) - { - } + } - public bool UseFirstInFirstOut => base.UseFirstInFirstOut; + public bool UseFirstInFirstOut => base.UseFirstInFirstOut; - public IMarshaller ExposedMarshaller => base.Marshaller; + public IMarshaller ExposedMarshaller => base.Marshaller; - public override Task SendAsync(IEnumerable> messages, Action setup = null) - { - return Task.CompletedTask; - } + public override Task SendAsync(IEnumerable> messages, Action setup = null) + { + return Task.CompletedTask; + } - public override async IAsyncEnumerable> ReceiveAsync(Action setup = null) - { - yield break; - } + public override async IAsyncEnumerable> ReceiveAsync(Action setup = null) + { + yield break; } } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonResourceNameOptionsTest.cs b/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonResourceNameOptionsTest.cs index 6fc48c1e..7c462dbe 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonResourceNameOptionsTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.Tests/AmazonResourceNameOptionsTest.cs @@ -1,81 +1,80 @@ -using System; +using System; using Cuemon; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +public class AmazonResourceNameOptionsTest : Test { - public class AmazonResourceNameOptionsTest : Test + public AmazonResourceNameOptionsTest(ITestOutputHelper output) : base(output) { - public AmazonResourceNameOptionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Ctor_ShouldHaveDefaultValues() - { - var sut = new AmazonResourceNameOptions(); + [Fact] + public void Ctor_ShouldHaveDefaultValues() + { + var sut = new AmazonResourceNameOptions(); - Assert.Equal(AmazonResourceNameOptions.DefaultAccountId, sut.AccountId); - Assert.Equal(AmazonResourceNameOptions.DefaultPartition, sut.Partition); - Assert.Equal(AmazonResourceNameOptions.DefaultRegion, sut.Region); - } + Assert.Equal(AmazonResourceNameOptions.DefaultAccountId, sut.AccountId); + Assert.Equal(AmazonResourceNameOptions.DefaultPartition, sut.Partition); + Assert.Equal(AmazonResourceNameOptions.DefaultRegion, sut.Region); + } - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - [InlineData("0123456789")] - [InlineData("A0123456789Z")] - [InlineData("1012345678910")] - public void ValidateOptions_ThrowsInvalidOperationException_WhenAccountIdIsNullOrEmptyOrConsistOfWhiteSpaceOnlyOrNotFixedLengthOrNotANumber(string accountId) + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + [InlineData("0123456789")] + [InlineData("A0123456789Z")] + [InlineData("1012345678910")] + public void ValidateOptions_ThrowsInvalidOperationException_WhenAccountIdIsNullOrEmptyOrConsistOfWhiteSpaceOnlyOrNotFixedLengthOrNotANumber(string accountId) + { + var sut1 = new AmazonResourceNameOptions { - var sut1 = new AmazonResourceNameOptions - { - AccountId = accountId - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + AccountId = accountId + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonResourceNameOptions.AccountId)}.IsNullOrWhiteSpace() || {nameof(AmazonResourceNameOptions.AccountId)}.Length != 12 || !{nameof(AmazonResourceNameOptions.AccountId)}.IsNumeric(NumberStyles.Integer, CultureInfo.InvariantCulture)')", sut2.Message); - Assert.Equal($"{nameof(AmazonResourceNameOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } + Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonResourceNameOptions.AccountId)}.IsNullOrWhiteSpace() || {nameof(AmazonResourceNameOptions.AccountId)}.Length != 12 || !{nameof(AmazonResourceNameOptions.AccountId)}.IsNumeric(NumberStyles.Integer, CultureInfo.InvariantCulture)')", sut2.Message); + Assert.Equal($"{nameof(AmazonResourceNameOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - public void ValidateOptions_ThrowsInvalidOperationException_WhenPartitionIsNullOrEmptyOrConsistOfWhiteSpaceOnly(string partition) + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void ValidateOptions_ThrowsInvalidOperationException_WhenPartitionIsNullOrEmptyOrConsistOfWhiteSpaceOnly(string partition) + { + var sut1 = new AmazonResourceNameOptions { - var sut1 = new AmazonResourceNameOptions - { - Partition = partition - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + Partition = partition + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonResourceNameOptions.Partition)}.IsNullOrWhiteSpace()')", sut2.Message); - Assert.Equal($"{nameof(AmazonResourceNameOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } + Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonResourceNameOptions.Partition)}.IsNullOrWhiteSpace()')", sut2.Message); + Assert.Equal($"{nameof(AmazonResourceNameOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); + } - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - public void ValidateOptions_ThrowsInvalidOperationException_WhenRegionIsNullOrEmptyOrConsistOfWhiteSpaceOnly(string region) + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void ValidateOptions_ThrowsInvalidOperationException_WhenRegionIsNullOrEmptyOrConsistOfWhiteSpaceOnly(string region) + { + var sut1 = new AmazonResourceNameOptions { - var sut1 = new AmazonResourceNameOptions - { - Region = region - }; - var sut2 = Assert.Throws(() => sut1.ValidateOptions()); - var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); + Region = region + }; + var sut2 = Assert.Throws(() => sut1.ValidateOptions()); + var sut3 = Assert.Throws(() => Validator.ThrowIfInvalidOptions(sut1)); - Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonResourceNameOptions.Region)}.IsNullOrWhiteSpace()')", sut2.Message); - Assert.Equal($"{nameof(AmazonResourceNameOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); - Assert.IsType(sut3.InnerException); - } + Assert.Equal($"Operation is not valid due to the current state of the object. (Expression '{nameof(AmazonResourceNameOptions.Region)}.IsNullOrWhiteSpace()')", sut2.Message); + Assert.Equal($"{nameof(AmazonResourceNameOptions)} are not in a valid state. (Parameter '{nameof(sut1)}')", sut3.Message); + Assert.IsType(sut3.InnerException); } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.Tests/ClientConfigExtensionsTest.cs b/test/Savvyio.Extensions.SimpleQueueService.Tests/ClientConfigExtensionsTest.cs index 2f3faaa9..9af6950f 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.Tests/ClientConfigExtensionsTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.Tests/ClientConfigExtensionsTest.cs @@ -4,32 +4,31 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.SimpleQueueService +namespace Savvyio.Extensions.SimpleQueueService; + +public class ClientConfigExtensionsTest : Test { - public class ClientConfigExtensionsTest : Test + public ClientConfigExtensionsTest(ITestOutputHelper output) : base(output) { - public ClientConfigExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void IsValid_Should_Return_True_When_Both_Aws_Client_Configurations_Are_Present() - { - ClientConfig[] configurations = [ new AmazonSQSConfig(), new AmazonSimpleNotificationServiceConfig() ]; + [Fact] + public void IsValid_Should_Return_True_When_Both_Aws_Client_Configurations_Are_Present() + { + ClientConfig[] configurations = [new AmazonSQSConfig(), new AmazonSimpleNotificationServiceConfig()]; - Assert.True(configurations.IsValid()); - Assert.IsType(configurations.SimpleQueueService()); - Assert.IsType(configurations.SimpleNotificationService()); - } + Assert.True(configurations.IsValid()); + Assert.IsType(configurations.SimpleQueueService()); + Assert.IsType(configurations.SimpleNotificationService()); + } - [Fact] - public void IsValid_Should_Return_False_When_Configurations_Are_Missing_Expected_Types() - { - ClientConfig[] invalid = [ new AmazonSQSConfig() ]; + [Fact] + public void IsValid_Should_Return_False_When_Configurations_Are_Missing_Expected_Types() + { + ClientConfig[] invalid = [new AmazonSQSConfig()]; - Assert.False(invalid.IsValid()); - Assert.NotNull(invalid.SimpleQueueService()); - Assert.Null(invalid.SimpleNotificationService()); - } + Assert.False(invalid.IsValid()); + Assert.NotNull(invalid.SimpleQueueService()); + Assert.Null(invalid.SimpleNotificationService()); } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.Tests/Commands/AmazonCommandQueueTest.cs b/test/Savvyio.Extensions.SimpleQueueService.Tests/Commands/AmazonCommandQueueTest.cs index 99f48689..0ba51565 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.Tests/Commands/AmazonCommandQueueTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.Tests/Commands/AmazonCommandQueueTest.cs @@ -16,244 +16,243 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.SimpleQueueService.Commands +namespace Savvyio.Extensions.SimpleQueueService.Commands; + +/// +/// Unit tests for . +/// +public class AmazonCommandQueueTest : Test { - /// - /// Unit tests for . - /// - public class AmazonCommandQueueTest : Test - { - private readonly IMarshaller _marshaller; - private readonly AmazonCommandQueueOptions _options; + private readonly IMarshaller _marshaller; + private readonly AmazonCommandQueueOptions _options; - public AmazonCommandQueueTest(ITestOutputHelper output) : base(output) + public AmazonCommandQueueTest(ITestOutputHelper output) : base(output) + { + _marshaller = new JsonMarshaller(); + _options = new AmazonCommandQueueOptions { - _marshaller = new JsonMarshaller(); - _options = new AmazonCommandQueueOptions - { - Credentials = new AnonymousAWSCredentials(), - Endpoint = Amazon.RegionEndpoint.USEast1, - SourceQueue = new Uri("https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue") - }; - } + Credentials = new AnonymousAWSCredentials(), + Endpoint = Amazon.RegionEndpoint.USEast1, + SourceQueue = new Uri("https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue") + }; + } - [Fact] - public void Ctor_Should_Throw_ArgumentNullException_When_Marshaller_Is_Null() - { - Assert.Throws(() => new AmazonCommandQueue(null, _options)); - } + [Fact] + public void Ctor_Should_Throw_ArgumentNullException_When_Marshaller_Is_Null() + { + Assert.Throws(() => new AmazonCommandQueue(null, _options)); + } - [Fact] - public void Ctor_Should_Throw_ArgumentNullException_When_Options_Is_Null() - { - Assert.Throws(() => new AmazonCommandQueue(_marshaller, null)); - } + [Fact] + public void Ctor_Should_Throw_ArgumentNullException_When_Options_Is_Null() + { + Assert.Throws(() => new AmazonCommandQueue(_marshaller, null)); + } - [Fact] - public void Ctor_Should_Throw_ArgumentException_When_Options_Invalid() - { - var invalidOptions = new AmazonCommandQueueOptions(); - Assert.Throws(() => new AmazonCommandQueue(_marshaller, invalidOptions)); - } + [Fact] + public void Ctor_Should_Throw_ArgumentException_When_Options_Invalid() + { + var invalidOptions = new AmazonCommandQueueOptions(); + Assert.Throws(() => new AmazonCommandQueue(_marshaller, invalidOptions)); + } - [Fact] - public async Task SendAsync_Should_Throw_ArgumentNullException_When_Messages_Is_Null() - { - var sut = new AmazonCommandQueue(_marshaller, _options); - await Assert.ThrowsAsync(() => sut.SendAsync(null)); - } + [Fact] + public async Task SendAsync_Should_Throw_ArgumentNullException_When_Messages_Is_Null() + { + var sut = new AmazonCommandQueue(_marshaller, _options); + await Assert.ThrowsAsync(() => sut.SendAsync(null)); + } - [Fact] - public async Task ReceiveAsync_Should_Call_RetrieveMessagesAsync() - { - var sut = new AmazonCommandQueue(_marshaller, _options); + [Fact] + public async Task ReceiveAsync_Should_Call_RetrieveMessagesAsync() + { + var sut = new AmazonCommandQueue(_marshaller, _options); - // Act - var result = sut.ReceiveAsync(); + // Act + var result = sut.ReceiveAsync(); - // Assert - Assert.NotNull(result); - // We can't enumerate without a real SQS, but we can check the type - Assert.IsAssignableFrom>>(result); - } + // Assert + Assert.NotNull(result); + // We can't enumerate without a real SQS, but we can check the type + Assert.IsAssignableFrom>>(result); + } - [Fact] - public void GetHealthCheckTarget_Should_Return_IAmazonSQS_Instance() - { - var sut = new AmazonCommandQueue(_marshaller, _options); + [Fact] + public void GetHealthCheckTarget_Should_Return_IAmazonSQS_Instance() + { + var sut = new AmazonCommandQueue(_marshaller, _options); - var sqs = sut.GetHealthCheckTarget(); + var sqs = sut.GetHealthCheckTarget(); - Assert.NotNull(sqs); - Assert.IsAssignableFrom(sqs); - } + Assert.NotNull(sqs); + Assert.IsAssignableFrom(sqs); + } - [Fact] - public void GetHealthCheckTarget_Should_Use_Configured_Client_When_Available() + [Fact] + public void GetHealthCheckTarget_Should_Use_Configured_Client_When_Available() + { + var options = new AmazonCommandQueueOptions { - var options = new AmazonCommandQueueOptions - { - Credentials = new AnonymousAWSCredentials(), - Endpoint = Amazon.RegionEndpoint.USEast1, - SourceQueue = new Uri("https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue") - }; - options.ConfigureClient(config => - { - config.ServiceURL = "http://localhost:4566"; - config.AuthenticationRegion = Amazon.RegionEndpoint.USEast1.SystemName; - }); + Credentials = new AnonymousAWSCredentials(), + Endpoint = Amazon.RegionEndpoint.USEast1, + SourceQueue = new Uri("https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue") + }; + options.ConfigureClient(config => + { + config.ServiceURL = "http://localhost:4566"; + config.AuthenticationRegion = Amazon.RegionEndpoint.USEast1.SystemName; + }); - var sut = new AmazonCommandQueue(_marshaller, options); + var sut = new AmazonCommandQueue(_marshaller, options); - var sqs = sut.GetHealthCheckTarget(); + var sqs = sut.GetHealthCheckTarget(); - Assert.Equal("http://localhost:4566/", sqs.Config.ServiceURL); - Assert.Equal(Amazon.RegionEndpoint.USEast1.SystemName, sqs.Config.AuthenticationRegion); - } + Assert.Equal("http://localhost:4566/", sqs.Config.ServiceURL); + Assert.Equal(Amazon.RegionEndpoint.USEast1.SystemName, sqs.Config.AuthenticationRegion); + } - [Fact] - public async Task SendAsync_ShouldSendMessagesInBatches() - { - var sqs = new Mock(); - var requests = new List(); - sqs.Setup(s => s.SendMessageBatchAsync(It.IsAny(), It.IsAny())) - .Callback((request, _) => requests.Add(request)) - .ReturnsAsync(new SendMessageBatchResponse()); - var queue = new TestableAmazonCommandQueue(_marshaller, _options, sqs.Object); - var messages = Enumerable.Range(0, 11).Select(_ => CreateMessage()).ToArray(); + [Fact] + public async Task SendAsync_ShouldSendMessagesInBatches() + { + var sqs = new Mock(); + var requests = new List(); + sqs.Setup(s => s.SendMessageBatchAsync(It.IsAny(), It.IsAny())) + .Callback((request, _) => requests.Add(request)) + .ReturnsAsync(new SendMessageBatchResponse()); + var queue = new TestableAmazonCommandQueue(_marshaller, _options, sqs.Object); + var messages = Enumerable.Range(0, 11).Select(_ => CreateMessage()).ToArray(); - await queue.SendAsync(messages); + await queue.SendAsync(messages); - Assert.Equal(2, requests.Count); - Assert.Equal(10, requests[0].Entries.Count); - Assert.Single(requests[1].Entries); - Assert.All(requests.SelectMany(request => request.Entries), entry => - { - Assert.NotNull(entry.MessageBody); - Assert.True(entry.MessageAttributes.ContainsKey("type")); - }); - } + Assert.Equal(2, requests.Count); + Assert.Equal(10, requests[0].Entries.Count); + Assert.Single(requests[1].Entries); + Assert.All(requests.SelectMany(request => request.Entries), entry => + { + Assert.NotNull(entry.MessageBody); + Assert.True(entry.MessageAttributes.ContainsKey("type")); + }); + } - [Fact] - public async Task ReceiveAsync_ShouldDeserializeMessagesAndDeleteAcknowledgedMessages() + [Fact] + public async Task ReceiveAsync_ShouldDeserializeMessagesAndDeleteAcknowledgedMessages() + { + var message = CreateMessage(); + var awsMessage = new Message { - var message = CreateMessage(); - var awsMessage = new Message + Body = _marshaller.Serialize(message).ToEncodedString(), + MessageId = "message-id", + ReceiptHandle = "receipt-handle", + MessageAttributes = new Dictionary { - Body = _marshaller.Serialize(message).ToEncodedString(), - MessageId = "message-id", - ReceiptHandle = "receipt-handle", - MessageAttributes = new Dictionary + ["type"] = new MessageAttributeValue { - ["type"] = new MessageAttributeValue - { - DataType = "String", - StringValue = message.GetType().ToFullNameIncludingAssemblyName() - } + DataType = "String", + StringValue = message.GetType().ToFullNameIncludingAssemblyName() } - }; - var sqs = new Mock(); - sqs.Setup(s => s.ReceiveMessageAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new ReceiveMessageResponse - { - Messages = [awsMessage] - }); - DeleteMessageBatchRequest deleteRequest = null; - sqs.Setup(s => s.DeleteMessageBatchAsync(It.IsAny(), It.IsAny())) - .Callback((request, _) => deleteRequest = request) - .ReturnsAsync(new DeleteMessageBatchResponse()); - var queue = new TestableAmazonCommandQueue(_marshaller, _options, sqs.Object); - - var received = new List>(); - await foreach (var item in queue.ReceiveAsync()) - { - received.Add(item); } + }; + var sqs = new Mock(); + sqs.Setup(s => s.ReceiveMessageAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new ReceiveMessageResponse + { + Messages = [awsMessage] + }); + DeleteMessageBatchRequest deleteRequest = null; + sqs.Setup(s => s.DeleteMessageBatchAsync(It.IsAny(), It.IsAny())) + .Callback((request, _) => deleteRequest = request) + .ReturnsAsync(new DeleteMessageBatchResponse()); + var queue = new TestableAmazonCommandQueue(_marshaller, _options, sqs.Object); - Assert.Single(received); - Assert.Equal(message.Id, received[0].Id); - Assert.NotNull(deleteRequest); - Assert.Equal("message-id", deleteRequest.Entries[0].Id); - Assert.Equal("receipt-handle", deleteRequest.Entries[0].ReceiptHandle); + var received = new List>(); + await foreach (var item in queue.ReceiveAsync()) + { + received.Add(item); } - [Fact] - public async Task ReceiveAsync_ShouldUseApproximateNumberOfMessages_WhenConfigured() + Assert.Single(received); + Assert.Equal(message.Id, received[0].Id); + Assert.NotNull(deleteRequest); + Assert.Equal("message-id", deleteRequest.Entries[0].Id); + Assert.Equal("receipt-handle", deleteRequest.Entries[0].ReceiptHandle); + } + + [Fact] + public async Task ReceiveAsync_ShouldUseApproximateNumberOfMessages_WhenConfigured() + { + var message = CreateMessage(); + var awsMessage = new Message { - var message = CreateMessage(); - var awsMessage = new Message + Body = _marshaller.Serialize(message).ToEncodedString(), + MessageId = "message-id", + ReceiptHandle = "receipt-handle", + MessageAttributes = new Dictionary { - Body = _marshaller.Serialize(message).ToEncodedString(), - MessageId = "message-id", - ReceiptHandle = "receipt-handle", - MessageAttributes = new Dictionary + ["type"] = new MessageAttributeValue { - ["type"] = new MessageAttributeValue - { - DataType = "String", - StringValue = message.GetType().ToFullNameIncludingAssemblyName() - } + DataType = "String", + StringValue = message.GetType().ToFullNameIncludingAssemblyName() } - }; - var options = new AmazonCommandQueueOptions + } + }; + var options = new AmazonCommandQueueOptions + { + Credentials = new AnonymousAWSCredentials(), + Endpoint = Amazon.RegionEndpoint.USEast1, + SourceQueue = new Uri("https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue") + }; + options.ReceiveContext.UseApproximateNumberOfMessages = true; + var sqs = new Mock(); + sqs.Setup(s => s.GetQueueAttributesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetQueueAttributesResponse { - Credentials = new AnonymousAWSCredentials(), - Endpoint = Amazon.RegionEndpoint.USEast1, - SourceQueue = new Uri("https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue") - }; - options.ReceiveContext.UseApproximateNumberOfMessages = true; - var sqs = new Mock(); - sqs.Setup(s => s.GetQueueAttributesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new GetQueueAttributesResponse - { - Attributes = new Dictionary - { - ["ApproximateNumberOfMessages"] = "1" - } - }); - sqs.Setup(s => s.ReceiveMessageAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new ReceiveMessageResponse + Attributes = new Dictionary { - Messages = [awsMessage] - }); - sqs.Setup(s => s.DeleteMessageBatchAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new DeleteMessageBatchResponse()); - var queue = new TestableAmazonCommandQueue(_marshaller, options, sqs.Object); - - var received = new List>(); - await foreach (var item in queue.ReceiveAsync()) + ["ApproximateNumberOfMessages"] = "1" + } + }); + sqs.Setup(s => s.ReceiveMessageAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new ReceiveMessageResponse { - received.Add(item); - } - - Assert.Single(received); - sqs.Verify(s => s.GetQueueAttributesAsync(It.Is(request => request.QueueUrl == options.SourceQueue.OriginalString), It.IsAny()), Times.Once); - } + Messages = [awsMessage] + }); + sqs.Setup(s => s.DeleteMessageBatchAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DeleteMessageBatchResponse()); + var queue = new TestableAmazonCommandQueue(_marshaller, options, sqs.Object); - private static IMessage CreateMessage() + var received = new List>(); + await foreach (var item in queue.ReceiveAsync()) { - return new Message( - Guid.NewGuid().ToString("N"), - new Uri("urn:test"), - "test", - new TestCommand()); + received.Add(item); } - private sealed class TestableAmazonCommandQueue : AmazonCommandQueue - { - private readonly IAmazonSQS _sqs; + Assert.Single(received); + sqs.Verify(s => s.GetQueueAttributesAsync(It.Is(request => request.QueueUrl == options.SourceQueue.OriginalString), It.IsAny()), Times.Once); + } - public TestableAmazonCommandQueue(IMarshaller marshaller, AmazonCommandQueueOptions options, IAmazonSQS sqs) : base(marshaller, options) - { - _sqs = sqs; - } + private static IMessage CreateMessage() + { + return new Message( + Guid.NewGuid().ToString("N"), + new Uri("urn:test"), + "test", + new TestCommand()); + } - protected override IAmazonSQS CreateSimpleQueueServiceClient() - { - return _sqs; - } + private sealed class TestableAmazonCommandQueue : AmazonCommandQueue + { + private readonly IAmazonSQS _sqs; + + public TestableAmazonCommandQueue(IMarshaller marshaller, AmazonCommandQueueOptions options, IAmazonSQS sqs) : base(marshaller, options) + { + _sqs = sqs; } - private record TestCommand : Command { } + protected override IAmazonSQS CreateSimpleQueueServiceClient() + { + return _sqs; + } } + + private record TestCommand : Command { } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.Tests/EventDriven/AmazonEventBusTest.cs b/test/Savvyio.Extensions.SimpleQueueService.Tests/EventDriven/AmazonEventBusTest.cs index 10cdede6..fcf1b2d2 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.Tests/EventDriven/AmazonEventBusTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.Tests/EventDriven/AmazonEventBusTest.cs @@ -23,200 +23,199 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.SimpleQueueService.EventDriven +namespace Savvyio.Extensions.SimpleQueueService.EventDriven; + +/// +/// Unit tests for . +/// +public class AmazonEventBusTest : Test { - /// - /// Unit tests for . - /// - public class AmazonEventBusTest : Test + public AmazonEventBusTest(ITestOutputHelper output) : base(output) { - public AmazonEventBusTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Ctor_Should_ThrowArgumentNullException_When_MarshallerIsNull() + [Fact] + public void Ctor_Should_ThrowArgumentNullException_When_MarshallerIsNull() + { + var options = new AmazonEventBusOptions { - var options = new AmazonEventBusOptions - { - Credentials = new BasicAWSCredentials("key", "secret"), - Endpoint = RegionEndpoint.EUWest1 - }; + Credentials = new BasicAWSCredentials("key", "secret"), + Endpoint = RegionEndpoint.EUWest1 + }; - Assert.Throws(() => new AmazonEventBus(null, options)); - } + Assert.Throws(() => new AmazonEventBus(null, options)); + } - [Fact] - public void Ctor_Should_ThrowArgumentNullException_When_OptionsIsNull() - { - var marshaller = new JsonMarshaller(); + [Fact] + public void Ctor_Should_ThrowArgumentNullException_When_OptionsIsNull() + { + var marshaller = new JsonMarshaller(); - Assert.Throws(() => new AmazonEventBus(marshaller, null)); - } + Assert.Throws(() => new AmazonEventBus(marshaller, null)); + } - [Fact] - public void Ctor_Should_ThrowArgumentException_When_OptionsInvalid() - { - var marshaller = new JsonMarshaller(); - var options = new AmazonEventBusOptions(); // Missing required properties + [Fact] + public void Ctor_Should_ThrowArgumentException_When_OptionsInvalid() + { + var marshaller = new JsonMarshaller(); + var options = new AmazonEventBusOptions(); // Missing required properties - Assert.Throws(() => new AmazonEventBus(marshaller, options)); - } + Assert.Throws(() => new AmazonEventBus(marshaller, options)); + } - [Fact] - public void GetHealthCheckTarget_Should_Return_IAmazonSimpleNotificationService_Instance() + [Fact] + public void GetHealthCheckTarget_Should_Return_IAmazonSimpleNotificationService_Instance() + { + var marshaller = new JsonMarshaller(); + var options = new AmazonEventBusOptions { - var marshaller = new JsonMarshaller(); - var options = new AmazonEventBusOptions - { - Credentials = new BasicAWSCredentials("key", "secret"), - Endpoint = RegionEndpoint.EUWest1, - SourceQueue = new Uri("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue") - }; + Credentials = new BasicAWSCredentials("key", "secret"), + Endpoint = RegionEndpoint.EUWest1, + SourceQueue = new Uri("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue") + }; - var sut = new AmazonEventBus(marshaller, options); + var sut = new AmazonEventBus(marshaller, options); - var sns = sut.GetHealthCheckTarget(); + var sns = sut.GetHealthCheckTarget(); - Assert.NotNull(sns); - Assert.IsAssignableFrom(sns); - } + Assert.NotNull(sns); + Assert.IsAssignableFrom(sns); + } - [Fact] - public void GetHealthCheckTarget_Should_Use_Configured_Client_When_Available() + [Fact] + public void GetHealthCheckTarget_Should_Use_Configured_Client_When_Available() + { + var marshaller = new JsonMarshaller(); + var options = new AmazonEventBusOptions { - var marshaller = new JsonMarshaller(); - var options = new AmazonEventBusOptions - { - Credentials = new BasicAWSCredentials("key", "secret"), - Endpoint = RegionEndpoint.EUWest1, - SourceQueue = new Uri("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue") - }; - options.ConfigureClient(config => - { - config.ServiceURL = "http://localhost:4566"; - config.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; - }); + Credentials = new BasicAWSCredentials("key", "secret"), + Endpoint = RegionEndpoint.EUWest1, + SourceQueue = new Uri("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue") + }; + options.ConfigureClient(config => + { + config.ServiceURL = "http://localhost:4566"; + config.AuthenticationRegion = RegionEndpoint.EUWest1.SystemName; + }); - var sut = new AmazonEventBus(marshaller, options); + var sut = new AmazonEventBus(marshaller, options); - var sns = sut.GetHealthCheckTarget(); + var sns = sut.GetHealthCheckTarget(); - Assert.Equal("http://localhost:4566/", sns.Config.ServiceURL); - Assert.Equal(RegionEndpoint.EUWest1.SystemName, sns.Config.AuthenticationRegion); - } + Assert.Equal("http://localhost:4566/", sns.Config.ServiceURL); + Assert.Equal(RegionEndpoint.EUWest1.SystemName, sns.Config.AuthenticationRegion); + } - [Fact] - public async Task PublishAsync_ShouldSendSerializedEvent() - { - var sns = new Mock(); - PublishRequest request = null; - sns.Setup(s => s.PublishAsync(It.IsAny(), It.IsAny())) - .Callback((r, _) => request = r) - .ReturnsAsync(new PublishResponse()); - var bus = new TestableAmazonEventBus(new JsonMarshaller(), CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue.fifo"), sns.Object); - var message = CreateMessage(); - - await bus.PublishAsync(message); - - Assert.NotNull(request); - Assert.Equal(message.Source, request.TopicArn); - Assert.Equal(message.Source, request.MessageGroupId); - Assert.Equal(message.Id, request.MessageDeduplicationId); - Assert.Equal(message.GetType().ToFullNameIncludingAssemblyName(), request.MessageAttributes["type"].StringValue); - } + [Fact] + public async Task PublishAsync_ShouldSendSerializedEvent() + { + var sns = new Mock(); + PublishRequest request = null; + sns.Setup(s => s.PublishAsync(It.IsAny(), It.IsAny())) + .Callback((r, _) => request = r) + .ReturnsAsync(new PublishResponse()); + var bus = new TestableAmazonEventBus(new JsonMarshaller(), CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue.fifo"), sns.Object); + var message = CreateMessage(); + + await bus.PublishAsync(message); + + Assert.NotNull(request); + Assert.Equal(message.Source, request.TopicArn); + Assert.Equal(message.Source, request.MessageGroupId); + Assert.Equal(message.Id, request.MessageDeduplicationId); + Assert.Equal(message.GetType().ToFullNameIncludingAssemblyName(), request.MessageAttributes["type"].StringValue); + } - [Fact] - public async Task SubscribeAsync_ShouldReceiveMessagesAndInvokeHandler() + [Fact] + public async Task SubscribeAsync_ShouldReceiveMessagesAndInvokeHandler() + { + var message = CreateMessage(); + var sqsMessage = new Message { - var message = CreateMessage(); - var sqsMessage = new Message + Body = new JsonMarshaller().Serialize(message).ToEncodedString(), + MessageId = "message-id", + ReceiptHandle = "receipt-handle", + MessageAttributes = new Dictionary { - Body = new JsonMarshaller().Serialize(message).ToEncodedString(), - MessageId = "message-id", - ReceiptHandle = "receipt-handle", - MessageAttributes = new Dictionary + ["type"] = new Amazon.SQS.Model.MessageAttributeValue { - ["type"] = new Amazon.SQS.Model.MessageAttributeValue - { - DataType = "String", - StringValue = message.GetType().ToFullNameIncludingAssemblyName() - } + DataType = "String", + StringValue = message.GetType().ToFullNameIncludingAssemblyName() } - }; - var sqs = new Mock(); - sqs.SetupSequence(s => s.ReceiveMessageAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new ReceiveMessageResponse { Messages = [sqsMessage] }) - .ReturnsAsync(new ReceiveMessageResponse { Messages = [] }); - sqs.Setup(s => s.DeleteMessageBatchAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new DeleteMessageBatchResponse()); - var bus = new TestableAmazonEventBus(new JsonMarshaller(), CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue"), Mock.Of(), sqs.Object); - IMessage received = null; - - await bus.SubscribeAsync((msg, _) => - { - received = msg; - return Task.CompletedTask; - }); + } + }; + var sqs = new Mock(); + sqs.SetupSequence(s => s.ReceiveMessageAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new ReceiveMessageResponse { Messages = [sqsMessage] }) + .ReturnsAsync(new ReceiveMessageResponse { Messages = [] }); + sqs.Setup(s => s.DeleteMessageBatchAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DeleteMessageBatchResponse()); + var bus = new TestableAmazonEventBus(new JsonMarshaller(), CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue"), Mock.Of(), sqs.Object); + IMessage received = null; + + await bus.SubscribeAsync((msg, _) => + { + received = msg; + return Task.CompletedTask; + }); - Assert.NotNull(received); - Assert.Equal(message.Id, received.Id); - } + Assert.NotNull(received); + Assert.Equal(message.Id, received.Id); + } - [Fact] - public async Task SubscribeAsync_ShouldSwallowCancellation_WhenThrowIfCancellationWasRequestedIsFalse() - { - using var cts = new CancellationTokenSource(); - cts.Cancel(); - var sqs = new Mock(); - sqs.Setup(s => s.ReceiveMessageAsync(It.IsAny(), It.IsAny())) - .ThrowsAsync(new OperationCanceledException(cts.Token)); - var bus = new TestableAmazonEventBus(new JsonMarshaller(), CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue"), Mock.Of(), sqs.Object); + [Fact] + public async Task SubscribeAsync_ShouldSwallowCancellation_WhenThrowIfCancellationWasRequestedIsFalse() + { + using var cts = new CancellationTokenSource(); + cts.Cancel(); + var sqs = new Mock(); + sqs.Setup(s => s.ReceiveMessageAsync(It.IsAny(), It.IsAny())) + .ThrowsAsync(new OperationCanceledException(cts.Token)); + var bus = new TestableAmazonEventBus(new JsonMarshaller(), CreateOptions("https://sqs.eu-west-1.amazonaws.com/123456789012/MyQueue"), Mock.Of(), sqs.Object); - await bus.SubscribeAsync((_, _) => Task.CompletedTask, o => o.CancellationToken = cts.Token); + await bus.SubscribeAsync((_, _) => Task.CompletedTask, o => o.CancellationToken = cts.Token); - sqs.Verify(s => s.ReceiveMessageAsync(It.IsAny(), cts.Token), Times.Once); - } + sqs.Verify(s => s.ReceiveMessageAsync(It.IsAny(), cts.Token), Times.Once); + } - private static AmazonEventBusOptions CreateOptions(string queueUrl) + private static AmazonEventBusOptions CreateOptions(string queueUrl) + { + return new AmazonEventBusOptions { - return new AmazonEventBusOptions - { - Credentials = new AnonymousAWSCredentials(), - Endpoint = RegionEndpoint.EUWest1, - SourceQueue = new Uri(queueUrl) - }; - } + Credentials = new AnonymousAWSCredentials(), + Endpoint = RegionEndpoint.EUWest1, + SourceQueue = new Uri(queueUrl) + }; + } + + private static IMessage CreateMessage() + { + return new Message("id", new Uri("arn:aws:sns:eu-west-1:123456789012:topic.fifo"), "type", new DummyIntegrationEvent(), DateTime.UtcNow); + } + + private sealed record DummyIntegrationEvent : IntegrationEvent + { + } - private static IMessage CreateMessage() + private sealed class TestableAmazonEventBus : AmazonEventBus + { + private readonly IAmazonSimpleNotificationService _sns; + private readonly IAmazonSQS _sqs; + + public TestableAmazonEventBus(IMarshaller marshaller, AmazonEventBusOptions options, IAmazonSimpleNotificationService sns, IAmazonSQS sqs = null) : base(marshaller, options) { - return new Message("id", new Uri("arn:aws:sns:eu-west-1:123456789012:topic.fifo"), "type", new DummyIntegrationEvent(), DateTime.UtcNow); + _sns = sns; + _sqs = sqs; } - private sealed record DummyIntegrationEvent : IntegrationEvent + protected override IAmazonSimpleNotificationService CreateSimpleNotificationServiceClient() { + return _sns; } - private sealed class TestableAmazonEventBus : AmazonEventBus + protected override IAmazonSQS CreateSimpleQueueServiceClient() { - private readonly IAmazonSimpleNotificationService _sns; - private readonly IAmazonSQS _sqs; - - public TestableAmazonEventBus(IMarshaller marshaller, AmazonEventBusOptions options, IAmazonSimpleNotificationService sns, IAmazonSQS sqs = null) : base(marshaller, options) - { - _sns = sns; - _sqs = sqs; - } - - protected override IAmazonSimpleNotificationService CreateSimpleNotificationServiceClient() - { - return _sns; - } - - protected override IAmazonSQS CreateSimpleQueueServiceClient() - { - return _sqs ?? base.CreateSimpleQueueServiceClient(); - } + return _sqs ?? base.CreateSimpleQueueServiceClient(); } } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.Tests/EventDriven/StringExtensionsTest.cs b/test/Savvyio.Extensions.SimpleQueueService.Tests/EventDriven/StringExtensionsTest.cs index f3d1802c..c36b9bee 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.Tests/EventDriven/StringExtensionsTest.cs +++ b/test/Savvyio.Extensions.SimpleQueueService.Tests/EventDriven/StringExtensionsTest.cs @@ -2,39 +2,38 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.SimpleQueueService.EventDriven +namespace Savvyio.Extensions.SimpleQueueService.EventDriven; + +public class StringExtensionsTest : Test { - public class StringExtensionsTest : Test + public StringExtensionsTest(ITestOutputHelper output) : base(output) { - public StringExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ToSnsUri_Should_Use_Default_Amazon_Resource_Name_Options() - { - var result = "order-created".ToSnsUri(); + [Fact] + public void ToSnsUri_Should_Use_Default_Amazon_Resource_Name_Options() + { + var result = "order-created".ToSnsUri(); - Assert.Equal($"arn:{AmazonResourceNameOptions.DefaultPartition}:sns:{AmazonResourceNameOptions.DefaultRegion}:{AmazonResourceNameOptions.DefaultAccountId}:order-created", result.OriginalString); - } + Assert.Equal($"arn:{AmazonResourceNameOptions.DefaultPartition}:sns:{AmazonResourceNameOptions.DefaultRegion}:{AmazonResourceNameOptions.DefaultAccountId}:order-created", result.OriginalString); + } - [Fact] - public void ToSnsUri_Should_Apply_Custom_Amazon_Resource_Name_Options() + [Fact] + public void ToSnsUri_Should_Apply_Custom_Amazon_Resource_Name_Options() + { + var result = "order-created".ToSnsUri(o => { - var result = "order-created".ToSnsUri(o => - { - o.Partition = "aws-cn"; - o.Region = "cn-north-1"; - o.AccountId = "123456789012"; - }); + o.Partition = "aws-cn"; + o.Region = "cn-north-1"; + o.AccountId = "123456789012"; + }); - Assert.Equal("arn:aws-cn:sns:cn-north-1:123456789012:order-created", result.OriginalString); - } + Assert.Equal("arn:aws-cn:sns:cn-north-1:123456789012:order-created", result.OriginalString); + } - [Fact] - public void ToSnsUri_Should_Throw_When_Configurator_Produces_Invalid_Options() - { - Assert.Throws(() => "order-created".ToSnsUri(o => o.AccountId = "bad")); - } + [Fact] + public void ToSnsUri_Should_Throw_When_Configurator_Produces_Invalid_Options() + { + Assert.Throws(() => "order-created".ToSnsUri(o => o.AccountId = "bad")); } } diff --git a/test/Savvyio.Extensions.SimpleQueueService.Tests/Savvyio.Extensions.SimpleQueueService.Tests.csproj b/test/Savvyio.Extensions.SimpleQueueService.Tests/Savvyio.Extensions.SimpleQueueService.Tests.csproj index 2eee987f..bdead28d 100644 --- a/test/Savvyio.Extensions.SimpleQueueService.Tests/Savvyio.Extensions.SimpleQueueService.Tests.csproj +++ b/test/Savvyio.Extensions.SimpleQueueService.Tests/Savvyio.Extensions.SimpleQueueService.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Extensions.SimpleQueueService diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Commands/CommandTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Commands/CommandTest.cs index af5a07fe..185560f3 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Commands/CommandTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Commands/CommandTest.cs @@ -1,45 +1,44 @@ -using System; +using System; using Cuemon.Extensions.IO; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Extensions.Text.Json.Commands +namespace Savvyio.Extensions.Text.Json.Commands; + +public class CommandTest : Test { - public class CommandTest : Test + public CommandTest(ITestOutputHelper output) : base(output) { - public CommandTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void CreateAccount_ShouldUseCustomConverter() - { - var ca = new CreateAccount(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); + [Fact] + public void CreateAccount_ShouldUseCustomConverter() + { + var ca = new CreateAccount(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); - var json = new JsonMarshaller().Serialize(ca); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(ca); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new JsonMarshaller().Deserialize(json); + var rehydrated = new JsonMarshaller().Deserialize(json); - Assert.Equivalent(ca, rehydrated, true); - } + Assert.Equivalent(ca, rehydrated, true); + } - [Fact] - public void CreateAccountWithValueObjects_ShouldUseCustomConverter() - { - var ca = new CreateAccountWithValueObjects(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); + [Fact] + public void CreateAccountWithValueObjects_ShouldUseCustomConverter() + { + var ca = new CreateAccountWithValueObjects(Guid.NewGuid(), "Michael Mortensen", "root@gimlichael.dev"); - var json = new JsonMarshaller().Serialize(ca); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(ca); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new JsonMarshaller().Deserialize(json); + var rehydrated = new JsonMarshaller().Deserialize(json); - Assert.Equivalent(ca, rehydrated, true); - } + Assert.Equivalent(ca, rehydrated, true); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/CommandExtensionsTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/CommandExtensionsTest.cs index b46455c7..cc3622f2 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/CommandExtensionsTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/CommandExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using Cuemon.Extensions; using Cuemon.Extensions.IO; using Codebelt.Extensions.Xunit; @@ -7,35 +7,35 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.Text.Json.Commands.Messaging +namespace Savvyio.Extensions.Text.Json.Commands.Messaging; + +public class CommandExtensionsTest : Test { - public class CommandExtensionsTest : Test + public CommandExtensionsTest(ITestOutputHelper output) : base(output) { - public CommandExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ToMessage_ShouldSerializeAndDeserialize_CreateMemberCommand_UsingInterface() + [Fact] + public void ToMessage_ShouldSerializeAndDeserialize_CreateMemberCommand_UsingInterface() + { + var utcNow = DateTime.UtcNow; + var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => { - var utcNow = DateTime.UtcNow; - var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utcNow; - }); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utcNow; + }); - var json = new JsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new JsonMarshaller().Deserialize>(json); + var sut4 = new JsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal($$""" + Assert.Equal($$""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -52,6 +52,5 @@ public void ToMessage_ShouldSerializeAndDeserialize_CreateMemberCommand_UsingInt } } """.ReplaceLineEndings(), jsonString); - } } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/Cryptography/MessageExtensionsTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/Cryptography/MessageExtensionsTest.cs index 06adf5a9..0403c4cf 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/Cryptography/MessageExtensionsTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/Cryptography/MessageExtensionsTest.cs @@ -8,47 +8,46 @@ using Savvyio.Messaging.Cryptography; using Xunit; -namespace Savvyio.Extensions.Text.Json.Commands.Messaging.Cryptography +namespace Savvyio.Extensions.Text.Json.Commands.Messaging.Cryptography; + +public class MessageExtensionsTest : Test { - public class MessageExtensionsTest : Test + public MessageExtensionsTest(ITestOutputHelper output) : base(output) { - public MessageExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void EncloseToSignedMessage_ShouldSerialize_WithSignature() + [Fact] + public void EncloseToSignedMessage_ShouldSerialize_WithSignature() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => + { + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).Sign(JsonMarshaller.Default, o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new CreateMemberCommand("Jane Doe", 21, "jd@office.com").SetCorrelationId("3eefdef050c340bfba100bd49c58c181"); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(CreateMemberCommand), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).Sign(JsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); - var json = JsonMarshaller.Default.Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = JsonMarshaller.Default.Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - var signatureException = Assert.Throws(() => - { - sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); - }); - Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); + var signatureException = Assert.Throws(() => + { + sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); + }); + Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); - var sut4 = JsonMarshaller.Default.Deserialize>(json); + var sut4 = JsonMarshaller.Default.Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"CreateMemberCommand","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","age":21,"emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.Commands.CreateMemberCommand, Savvyio.Assets.Tests","correlationId":"3eefdef050c340bfba100bd49c58c181"}},"signature":"76df78c14ee668b2812a4c8c8ab58738de6b595623938e9c8aa893e9630ff5ad"}""", jsonString); - } + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"CreateMemberCommand","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","age":21,"emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.Commands.CreateMemberCommand, Savvyio.Assets.Tests","correlationId":"3eefdef050c340bfba100bd49c58c181"}},"signature":"76df78c14ee668b2812a4c8c8ab58738de6b595623938e9c8aa893e9630ff5ad"}""", jsonString); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/InMemoryCommandQueueTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/InMemoryCommandQueueTest.cs index 4efd9806..09a5a8b8 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/InMemoryCommandQueueTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Commands/Messaging/InMemoryCommandQueueTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using Cuemon; @@ -14,75 +14,74 @@ using Xunit; using Xunit.v3.Priority; -namespace Savvyio.Extensions.Text.Json.Commands.Messaging +namespace Savvyio.Extensions.Text.Json.Commands.Messaging; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class InMemoryCommandQueueTest : Test { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class InMemoryCommandQueueTest : Test - { - private static readonly InMemoryCommandQueue Queue = new(); - private static readonly InMemoryTestStore> Comparer = new(); + private static readonly InMemoryCommandQueue Queue = new(); + private static readonly InMemoryTestStore> Comparer = new(); - public InMemoryCommandQueueTest(ITestOutputHelper output) : base(output) - { - } + public InMemoryCommandQueueTest(ITestOutputHelper output) : base(output) + { + } - [Fact, Priority(0)] - public async Task SendAsync_CreateMemberCommand_OneTime() - { - var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); - var sut2 = "https://fancy.io/members".ToUri(); - var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); + [Fact, Priority(0)] + public async Task SendAsync_CreateMemberCommand_OneTime() + { + var sut1 = new CreateMemberCommand("John Doe", 44, "jd@outlook.com"); + var sut2 = "https://fancy.io/members".ToUri(); + var sut3 = sut1.ToMessage(sut2, nameof(CreateMemberCommand)); - TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = Environment.NewLine)); + TestOutput.WriteLine(Generate.ObjectPortrayal(sut2, o => o.Delimiter = Environment.NewLine)); - TestOutput.WriteLine(new JsonMarshaller().Serialize(sut2).ToEncodedString()); + TestOutput.WriteLine(new JsonMarshaller().Serialize(sut2).ToEncodedString()); - Comparer.Add(sut3); + Comparer.Add(sut3); - await Queue.SendAsync(sut3.Yield()); - } + await Queue.SendAsync(sut3.Yield()); + } - [Fact, Priority(1)] - public async Task ReceiveAsync_CreateMemberCommand_OneTime() - { - var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); - var sut2 = await Queue.ReceiveAsync().SingleOrDefaultAsync(); + [Fact, Priority(1)] + public async Task ReceiveAsync_CreateMemberCommand_OneTime() + { + var sut1 = Comparer.Query(message => message.Source == "https://fancy.io/members").Single(); + var sut2 = await Queue.ReceiveAsync().SingleOrDefaultAsync(); - Assert.Equal(sut1.Data, sut2.Data); - Assert.Equal(sut1.Time, sut2.Time); - Assert.Equal(sut1.Source, sut2.Source); - Assert.Equal(sut1.Id, sut2.Id); - Assert.Equal(sut1.Type, sut2.Type); - } + Assert.Equal(sut1.Data, sut2.Data); + Assert.Equal(sut1.Time, sut2.Time); + Assert.Equal(sut1.Source, sut2.Source); + Assert.Equal(sut1.Id, sut2.Id); + Assert.Equal(sut1.Type, sut2.Type); + } - [Fact, Priority(2)] - public async Task SendAsync_CreateMemberCommand_ThousandTimes() + [Fact, Priority(2)] + public async Task SendAsync_CreateMemberCommand_ThousandTimes() + { + var messages = Generate.RangeOf(1000, i => { - var messages = Generate.RangeOf(1000, i => - { - var email = $"{Generate.RandomString(5)}@outlook.com"; - return new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); - }); + var email = $"{Generate.RandomString(5)}@outlook.com"; + return new CreateMemberCommand(Generate.RandomString(10), (byte)Generate.RandomNumber(byte.MaxValue), email).ToMessage($"urn:{i}:{email}".ToUri(), nameof(CreateMemberCommand)); + }); - await ParallelFactory.ForEachAsync(messages, (message, token) => - { - Comparer.Add(message); - return Queue.SendAsync(message.Yield(), o => o.CancellationToken = token); - }); - } - - [Fact, Priority(3)] - public async Task ReceiveAsync_CreateMemberCommand_All() + await ParallelFactory.ForEachAsync(messages, (message, token) => { - var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); - var sut2 = await Queue.ReceiveAsync().ToListAsync(); + Comparer.Add(message); + return Queue.SendAsync(message.Yield(), o => o.CancellationToken = token); + }); + } + + [Fact, Priority(3)] + public async Task ReceiveAsync_CreateMemberCommand_All() + { + var sut1 = Comparer.Query(message => message.Source.StartsWith("urn")).ToList(); + var sut2 = await Queue.ReceiveAsync().ToListAsync(); - TestOutput.WriteLines(sut2.Take(10)); + TestOutput.WriteLines(sut2.Take(10)); - Assert.Equal(sut1.Count, sut2.Count); - Assert.Equal(sut1, sut2); - Assert.Equal(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); - Assert.Equal(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); - } + Assert.Equal(sut1.Count, sut2.Count); + Assert.Equal(sut1, sut2); + Assert.Equal(sut1.Select(message => message.Data), sut2.Select(message => message.Data)); + Assert.Equal(sut1.Select(message => message.Data.Metadata), sut2.Select(message => message.Data.Metadata)); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Converters/DateTimeConverterTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Converters/DateTimeConverterTest.cs index 70a0ee89..245b7178 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Converters/DateTimeConverterTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Converters/DateTimeConverterTest.cs @@ -5,59 +5,58 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +public class DateTimeConverterTest : Test { - public class DateTimeConverterTest : Test + public DateTimeConverterTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void CanConvert_ShouldReturnTrue_ForDateTime() + { + var converter = new DateTimeConverter(); + Assert.True(converter.CanConvert(typeof(DateTime))); + } + + [Fact] + public void CanConvert_ShouldReturnFalse_ForOtherTypes() + { + var converter = new DateTimeConverter(); + Assert.False(converter.CanConvert(typeof(string))); + Assert.False(converter.CanConvert(typeof(DateTimeOffset))); + } + + [Fact] + public void Write_ShouldSerializeDateTime_AsIso8601() { - public DateTimeConverterTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void CanConvert_ShouldReturnTrue_ForDateTime() - { - var converter = new DateTimeConverter(); - Assert.True(converter.CanConvert(typeof(DateTime))); - } - - [Fact] - public void CanConvert_ShouldReturnFalse_ForOtherTypes() - { - var converter = new DateTimeConverter(); - Assert.False(converter.CanConvert(typeof(string))); - Assert.False(converter.CanConvert(typeof(DateTimeOffset))); - } - - [Fact] - public void Write_ShouldSerializeDateTime_AsIso8601() - { - var utc = new DateTime(2023, 11, 16, 23, 24, 17, DateTimeKind.Utc); - var converter = new DateTimeConverter(); - var buffer = new ArrayBufferWriter(); - using var writer = new Utf8JsonWriter(buffer); - - converter.Write(writer, utc, new JsonSerializerOptions()); - writer.Flush(); - - var json = Encoding.UTF8.GetString(buffer.WrittenSpan); - TestOutput.WriteLine(json); - - Assert.Contains("2023-11-16", json); - } - - [Fact] - public void Read_ShouldDeserializeDateTime_FromIso8601() - { - var utc = new DateTime(2023, 11, 16, 23, 24, 17, DateTimeKind.Utc); - var converter = new DateTimeConverter(); - var jsonString = $"\"{utc:O}\""; - var bytes = Encoding.UTF8.GetBytes(jsonString); - var reader = new Utf8JsonReader(bytes); - reader.Read(); - - var result = converter.Read(ref reader, typeof(DateTime), new JsonSerializerOptions()); - - Assert.Equal(utc, result); - } + var utc = new DateTime(2023, 11, 16, 23, 24, 17, DateTimeKind.Utc); + var converter = new DateTimeConverter(); + var buffer = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(buffer); + + converter.Write(writer, utc, new JsonSerializerOptions()); + writer.Flush(); + + var json = Encoding.UTF8.GetString(buffer.WrittenSpan); + TestOutput.WriteLine(json); + + Assert.Contains("2023-11-16", json); + } + + [Fact] + public void Read_ShouldDeserializeDateTime_FromIso8601() + { + var utc = new DateTime(2023, 11, 16, 23, 24, 17, DateTimeKind.Utc); + var converter = new DateTimeConverter(); + var jsonString = $"\"{utc:O}\""; + var bytes = Encoding.UTF8.GetBytes(jsonString); + var reader = new Utf8JsonReader(bytes); + reader.Read(); + + var result = converter.Read(ref reader, typeof(DateTime), new JsonSerializerOptions()); + + Assert.Equal(utc, result); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Converters/DateTimeOffsetConverterTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Converters/DateTimeOffsetConverterTest.cs index 3a11fb6f..bc957afa 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Converters/DateTimeOffsetConverterTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Converters/DateTimeOffsetConverterTest.cs @@ -5,52 +5,51 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +public class DateTimeOffsetConverterTest : Test { - public class DateTimeOffsetConverterTest : Test + public DateTimeOffsetConverterTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void CanConvert_ReturnsTrue_ForDateTime() { - public DateTimeOffsetConverterTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void CanConvert_ReturnsTrue_ForDateTime() - { - var converter = new DateTimeOffsetConverter(); - // Note: CanConvert checks for DateTime (not DateTimeOffset) by design in this implementation - Assert.True(converter.CanConvert(typeof(DateTime))); - } - - [Fact] - public void Write_ShouldSerializeDateTimeOffset_AsIso8601() - { - var dto = new DateTimeOffset(2023, 11, 16, 23, 24, 17, TimeSpan.Zero); - var converter = new DateTimeOffsetConverter(); - var buffer = new ArrayBufferWriter(); - using var writer = new Utf8JsonWriter(buffer); - - converter.Write(writer, dto, new JsonSerializerOptions()); - writer.Flush(); - - var json = Encoding.UTF8.GetString(buffer.WrittenSpan); - TestOutput.WriteLine(json); - - Assert.Contains("2023-11-16", json); - } - - [Fact] - public void Read_ShouldDeserializeDateTimeOffset_FromIso8601() - { - var dto = new DateTimeOffset(2023, 11, 16, 23, 24, 17, TimeSpan.Zero); - var converter = new DateTimeOffsetConverter(); - var jsonString = $"\"{dto:O}\""; - var bytes = Encoding.UTF8.GetBytes(jsonString); - var reader = new Utf8JsonReader(bytes); - reader.Read(); - - var result = converter.Read(ref reader, typeof(DateTimeOffset), new JsonSerializerOptions()); - - Assert.Equal(dto, result); - } + var converter = new DateTimeOffsetConverter(); + // Note: CanConvert checks for DateTime (not DateTimeOffset) by design in this implementation + Assert.True(converter.CanConvert(typeof(DateTime))); + } + + [Fact] + public void Write_ShouldSerializeDateTimeOffset_AsIso8601() + { + var dto = new DateTimeOffset(2023, 11, 16, 23, 24, 17, TimeSpan.Zero); + var converter = new DateTimeOffsetConverter(); + var buffer = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(buffer); + + converter.Write(writer, dto, new JsonSerializerOptions()); + writer.Flush(); + + var json = Encoding.UTF8.GetString(buffer.WrittenSpan); + TestOutput.WriteLine(json); + + Assert.Contains("2023-11-16", json); + } + + [Fact] + public void Read_ShouldDeserializeDateTimeOffset_FromIso8601() + { + var dto = new DateTimeOffset(2023, 11, 16, 23, 24, 17, TimeSpan.Zero); + var converter = new DateTimeOffsetConverter(); + var jsonString = $"\"{dto:O}\""; + var bytes = Encoding.UTF8.GetBytes(jsonString); + var reader = new Utf8JsonReader(bytes); + reader.Read(); + + var result = converter.Read(ref reader, typeof(DateTimeOffset), new JsonSerializerOptions()); + + Assert.Equal(dto, result); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Converters/MessageConverterTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Converters/MessageConverterTest.cs index ea6a5514..5b5618ee 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Converters/MessageConverterTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Converters/MessageConverterTest.cs @@ -10,77 +10,76 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +public class MessageConverterTest : Test { - public class MessageConverterTest : Test + public MessageConverterTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void MessageConverter_ShouldConvertOnlyMessages() + { + var sut = new MessageConverter(); + + Assert.True(sut.CanConvert(typeof(Message))); + Assert.False(sut.CanConvert(typeof(MemberCreated))); + Assert.False(sut.CanConvert(typeof(string))); + } + + [Fact] + public void MessageConverter_ShouldRoundtripCloudEventExtensionAttributes() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var cloudEvent = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc) + .ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => + { + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).ToCloudEvent(); + + ((IDictionary)cloudEvent).Add("traceparent", "00-abc-def-01"); + + var marshaller = new JsonMarshaller(); + var json = marshaller.Serialize(cloudEvent); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + + TestOutput.WriteLine(jsonString); + + var result = marshaller.Deserialize>(json); + + Assert.Contains("traceparent", jsonString); + Assert.True(((IDictionary)result).ContainsKey("traceparent")); + } + + [Fact] + public void MessageConverter_ShouldRoundtripDataWithWritableMetadata() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var data = new WritableMetadataRequest { Name = "Jane Doe" }; + data.Metadata.Add("custom", "value"); + var message = new Message("2d4030d32a254ee8a27046e5bafe696a", "https://fancy.api/members".ToUri(), nameof(WritableMetadataRequest), data, utc); + + var marshaller = new JsonMarshaller(); + var json = marshaller.Serialize(message); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + + TestOutput.WriteLine(jsonString); + + var result = marshaller.Deserialize>(json); + + Assert.NotNull(result); + Assert.Equal("Jane Doe", result.Data.Name); + Assert.NotNull(result.Data.Metadata); + Assert.True(result.Data.Metadata.ContainsKey("custom")); + Assert.Equal("value", result.Data.Metadata["custom"].ToString()); + } + + private sealed record WritableMetadataRequest : IRequest, IMetadata { - public MessageConverterTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void MessageConverter_ShouldConvertOnlyMessages() - { - var sut = new MessageConverter(); - - Assert.True(sut.CanConvert(typeof(Message))); - Assert.False(sut.CanConvert(typeof(MemberCreated))); - Assert.False(sut.CanConvert(typeof(string))); - } - - [Fact] - public void MessageConverter_ShouldRoundtripCloudEventExtensionAttributes() - { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var cloudEvent = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc) - .ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).ToCloudEvent(); - - ((IDictionary)cloudEvent).Add("traceparent", "00-abc-def-01"); - - var marshaller = new JsonMarshaller(); - var json = marshaller.Serialize(cloudEvent); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - - TestOutput.WriteLine(jsonString); - - var result = marshaller.Deserialize>(json); - - Assert.Contains("traceparent", jsonString); - Assert.True(((IDictionary)result).ContainsKey("traceparent")); - } - - [Fact] - public void MessageConverter_ShouldRoundtripDataWithWritableMetadata() - { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var data = new WritableMetadataRequest { Name = "Jane Doe" }; - data.Metadata.Add("custom", "value"); - var message = new Message("2d4030d32a254ee8a27046e5bafe696a", "https://fancy.api/members".ToUri(), nameof(WritableMetadataRequest), data, utc); - - var marshaller = new JsonMarshaller(); - var json = marshaller.Serialize(message); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - - TestOutput.WriteLine(jsonString); - - var result = marshaller.Deserialize>(json); - - Assert.NotNull(result); - Assert.Equal("Jane Doe", result.Data.Name); - Assert.NotNull(result.Data.Metadata); - Assert.True(result.Data.Metadata.ContainsKey("custom")); - Assert.Equal("value", result.Data.Metadata["custom"].ToString()); - } - - private sealed record WritableMetadataRequest : IRequest, IMetadata - { - public string Name { get; set; } - - public IMetadataDictionary Metadata { get; set; } = new MetadataDictionary(); - } + public string Name { get; set; } + + public IMetadataDictionary Metadata { get; set; } = new MetadataDictionary(); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Converters/MetadataDictionaryConverterTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Converters/MetadataDictionaryConverterTest.cs index 75800b56..12a13b35 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Converters/MetadataDictionaryConverterTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Converters/MetadataDictionaryConverterTest.cs @@ -5,167 +5,166 @@ using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +public class MetadataDictionaryConverterTest : Test { - public class MetadataDictionaryConverterTest : Test + public MetadataDictionaryConverterTest(ITestOutputHelper output) : base(output) { - public MetadataDictionaryConverterTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void CanConvert_ShouldReturnTrue_ForIMetadataDictionary() - { - var converter = new MetadataDictionaryConverter(); + [Fact] + public void CanConvert_ShouldReturnTrue_ForIMetadataDictionary() + { + var converter = new MetadataDictionaryConverter(); - Assert.True(converter.CanConvert(typeof(IMetadataDictionary))); - Assert.True(converter.CanConvert(typeof(MetadataDictionary))); - } + Assert.True(converter.CanConvert(typeof(IMetadataDictionary))); + Assert.True(converter.CanConvert(typeof(MetadataDictionary))); + } - [Fact] - public void CanConvert_ShouldReturnFalse_ForUnrelatedTypes() - { - var converter = new MetadataDictionaryConverter(); + [Fact] + public void CanConvert_ShouldReturnFalse_ForUnrelatedTypes() + { + var converter = new MetadataDictionaryConverter(); - Assert.False(converter.CanConvert(typeof(string))); - Assert.False(converter.CanConvert(typeof(int))); - } + Assert.False(converter.CanConvert(typeof(string))); + Assert.False(converter.CanConvert(typeof(int))); + } - [Fact] - public void Read_ShouldHandleBooleanFalse() - { - var json = """{"flag": false}"""; - var options = CreateOptions(); + [Fact] + public void Read_ShouldHandleBooleanFalse() + { + var json = """{"flag": false}"""; + var options = CreateOptions(); - var result = JsonSerializer.Deserialize(json, options); + var result = JsonSerializer.Deserialize(json, options); - Assert.NotNull(result); - Assert.Equal(false, result["flag"]); - } + Assert.NotNull(result); + Assert.Equal(false, result["flag"]); + } - [Fact] - public void Read_ShouldHandleBooleanTrue() - { - var json = """{"flag": true}"""; - var options = CreateOptions(); + [Fact] + public void Read_ShouldHandleBooleanTrue() + { + var json = """{"flag": true}"""; + var options = CreateOptions(); - var result = JsonSerializer.Deserialize(json, options); + var result = JsonSerializer.Deserialize(json, options); - Assert.NotNull(result); - Assert.Equal(true, result["flag"]); - } + Assert.NotNull(result); + Assert.Equal(true, result["flag"]); + } - [Fact] - public void Read_ShouldHandleNullValue() - { - var json = """{"key": null}"""; - var options = CreateOptions(); + [Fact] + public void Read_ShouldHandleNullValue() + { + var json = """{"key": null}"""; + var options = CreateOptions(); - var result = JsonSerializer.Deserialize(json, options); + var result = JsonSerializer.Deserialize(json, options); - Assert.NotNull(result); - Assert.Null(result["key"]); - } + Assert.NotNull(result); + Assert.Null(result["key"]); + } - [Fact] - public void Read_ShouldHandleInt64Number() - { - var json = """{"count": 9876543210}"""; - var options = CreateOptions(); + [Fact] + public void Read_ShouldHandleInt64Number() + { + var json = """{"count": 9876543210}"""; + var options = CreateOptions(); - var result = JsonSerializer.Deserialize(json, options); + var result = JsonSerializer.Deserialize(json, options); - Assert.NotNull(result); - Assert.Equal(9876543210L, result["count"]); - } + Assert.NotNull(result); + Assert.Equal(9876543210L, result["count"]); + } - [Fact] - public void Read_ShouldHandleDecimalNumber() - { - var json = """{"price": 3.14}"""; - var options = CreateOptions(); + [Fact] + public void Read_ShouldHandleDecimalNumber() + { + var json = """{"price": 3.14}"""; + var options = CreateOptions(); - var result = JsonSerializer.Deserialize(json, options); + var result = JsonSerializer.Deserialize(json, options); - Assert.NotNull(result); - Assert.Equal(3.14m, result["price"]); - } + Assert.NotNull(result); + Assert.Equal(3.14m, result["price"]); + } - [Fact] - public void Read_ShouldHandleNestedObject() - { - var json = """{"outer": {"inner": "value"}}"""; - var options = CreateOptions(); + [Fact] + public void Read_ShouldHandleNestedObject() + { + var json = """{"outer": {"inner": "value"}}"""; + var options = CreateOptions(); - var result = JsonSerializer.Deserialize(json, options); + var result = JsonSerializer.Deserialize(json, options); - Assert.NotNull(result); - var nested = Assert.IsAssignableFrom(result["outer"]); - Assert.Equal("value", nested["inner"]); - } + Assert.NotNull(result); + var nested = Assert.IsAssignableFrom(result["outer"]); + Assert.Equal("value", nested["inner"]); + } - [Fact] - public void Read_ShouldHandleArrayValue() - { - var json = """{"items": ["a", "b", "c"]}"""; - var options = CreateOptions(); + [Fact] + public void Read_ShouldHandleArrayValue() + { + var json = """{"items": ["a", "b", "c"]}"""; + var options = CreateOptions(); - var result = JsonSerializer.Deserialize(json, options); + var result = JsonSerializer.Deserialize(json, options); - Assert.NotNull(result); - var list = Assert.IsAssignableFrom>(result["items"]); - Assert.Equal(3, list.Count); - Assert.Equal("a", list[0]); - Assert.Equal("b", list[1]); - Assert.Equal("c", list[2]); + Assert.NotNull(result); + var list = Assert.IsAssignableFrom>(result["items"]); + Assert.Equal(3, list.Count); + Assert.Equal("a", list[0]); + Assert.Equal("b", list[1]); + Assert.Equal("c", list[2]); - TestOutput.WriteLine(string.Join(", ", list)); - } + TestOutput.WriteLine(string.Join(", ", list)); + } - [Fact] - public void Read_ShouldDeserializeDateTimeString() - { - var dt = new DateTime(2023, 11, 16, 23, 24, 17, DateTimeKind.Utc); - var json = $"{{\"ts\": \"{dt:O}\"}}"; - var options = CreateOptions(); + [Fact] + public void Read_ShouldDeserializeDateTimeString() + { + var dt = new DateTime(2023, 11, 16, 23, 24, 17, DateTimeKind.Utc); + var json = $"{{\"ts\": \"{dt:O}\"}}"; + var options = CreateOptions(); - var result = JsonSerializer.Deserialize(json, options); + var result = JsonSerializer.Deserialize(json, options); - Assert.NotNull(result); - Assert.IsType(result["ts"]); - Assert.Equal(dt, (DateTime)result["ts"]); + Assert.NotNull(result); + Assert.IsType(result["ts"]); + Assert.Equal(dt, (DateTime)result["ts"]); - TestOutput.WriteLine(result["ts"].ToString()); - } + TestOutput.WriteLine(result["ts"].ToString()); + } - [Fact] - public void Write_ShouldSerializeMetadataDictionary() - { - var md = new MetadataDictionary - { - { "key1", "value1" }, - { "key2", 99L }, - { "key3", false } - }; - - var options = CreateOptions(); - var json = JsonSerializer.Serialize(md, options); - - TestOutput.WriteLine(json); - - Assert.Contains("key1", json); - Assert.Contains("value1", json); - Assert.Contains("key2", json); - Assert.Contains("99", json); - Assert.Contains("key3", json); - Assert.Contains("false", json); - } - - private static JsonSerializerOptions CreateOptions() + [Fact] + public void Write_ShouldSerializeMetadataDictionary() + { + var md = new MetadataDictionary { - var options = new JsonSerializerOptions(); - options.Converters.Add(new MetadataDictionaryConverter()); - return options; - } + { "key1", "value1" }, + { "key2", 99L }, + { "key3", false } + }; + + var options = CreateOptions(); + var json = JsonSerializer.Serialize(md, options); + + TestOutput.WriteLine(json); + + Assert.Contains("key1", json); + Assert.Contains("value1", json); + Assert.Contains("key2", json); + Assert.Contains("99", json); + Assert.Contains("key3", json); + Assert.Contains("false", json); + } + + private static JsonSerializerOptions CreateOptions() + { + var options = new JsonSerializerOptions(); + options.Converters.Add(new MetadataDictionaryConverter()); + return options; } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Converters/RequestConverterTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Converters/RequestConverterTest.cs index 15e417a8..f1624deb 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Converters/RequestConverterTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Converters/RequestConverterTest.cs @@ -5,85 +5,84 @@ using Savvyio.Assets.Commands; using Xunit; -namespace Savvyio.Extensions.Text.Json.Converters +namespace Savvyio.Extensions.Text.Json.Converters; + +public class RequestConverterTest : Test { - public class RequestConverterTest : Test + public RequestConverterTest(ITestOutputHelper output) : base(output) { - public RequestConverterTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void RequestConverter_ShouldConvertOnlyRequests() - { - var sut = new RequestConverter(); + [Fact] + public void RequestConverter_ShouldConvertOnlyRequests() + { + var sut = new RequestConverter(); - Assert.True(sut.CanConvert(typeof(CreateMemberCommand))); - Assert.False(sut.CanConvert(typeof(string))); - } + Assert.True(sut.CanConvert(typeof(CreateMemberCommand))); + Assert.False(sut.CanConvert(typeof(string))); + } - [Fact] - public void RequestConverter_ShouldRehydrateAutoPropertyRequests() - { - var sut = JsonSerializer.Deserialize("{\"name\":\"Jane Doe\",\"age\":21,\"emailAddress\":\"jd@office.com\"}", CreateOptions()); + [Fact] + public void RequestConverter_ShouldRehydrateAutoPropertyRequests() + { + var sut = JsonSerializer.Deserialize("{\"name\":\"Jane Doe\",\"age\":21,\"emailAddress\":\"jd@office.com\"}", CreateOptions()); - Assert.NotNull(sut); - Assert.Equal("Jane Doe", sut.Name); - Assert.Equal((byte)21, sut.Age); - Assert.Equal("jd@office.com", sut.EmailAddress); - } + Assert.NotNull(sut); + Assert.Equal("Jane Doe", sut.Name); + Assert.Equal((byte)21, sut.Age); + Assert.Equal("jd@office.com", sut.EmailAddress); + } - [Fact] - public void RequestConverter_ShouldRehydrateWritableProperties() - { - var sut = JsonSerializer.Deserialize("{\"name\":\"Jane Doe\"}", CreateOptions()); + [Fact] + public void RequestConverter_ShouldRehydrateWritableProperties() + { + var sut = JsonSerializer.Deserialize("{\"name\":\"Jane Doe\"}", CreateOptions()); - Assert.NotNull(sut); - Assert.Equal("Jane Doe", sut.Name); - } + Assert.NotNull(sut); + Assert.Equal("Jane Doe", sut.Name); + } - [Fact] - public void RequestConverter_ShouldFailWhenNoSupportedBackingFieldExists() - { - var ex = Assert.Throws(() => JsonSerializer.Deserialize("{\"name\":\"Jane Doe\"}", CreateOptions())); + [Fact] + public void RequestConverter_ShouldFailWhenNoSupportedBackingFieldExists() + { + var ex = Assert.Throws(() => JsonSerializer.Deserialize("{\"name\":\"Jane Doe\"}", CreateOptions())); - Assert.StartsWith("This deserializer only supports rehydration", ex.Message); - } + Assert.StartsWith("This deserializer only supports rehydration", ex.Message); + } - [Fact] - public void RequestConverter_ShouldRoundtripThroughWrite() - { - var options = CreateOptions(); - var json = JsonSerializer.Serialize(new WritableRequest { Name = "Jane Doe" }, options); + [Fact] + public void RequestConverter_ShouldRoundtripThroughWrite() + { + var options = CreateOptions(); + var json = JsonSerializer.Serialize(new WritableRequest { Name = "Jane Doe" }, options); - TestOutput.WriteLine(json); + TestOutput.WriteLine(json); - var sut = JsonSerializer.Deserialize(json, options); + var sut = JsonSerializer.Deserialize(json, options); - Assert.NotNull(sut); - Assert.Equal("Jane Doe", sut.Name); - } + Assert.NotNull(sut); + Assert.Equal("Jane Doe", sut.Name); + } - private static JsonSerializerOptions CreateOptions() - { - var options = new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }; - options.Converters.Add(new RequestConverter()); - return options; - } - - private sealed class WritableRequest : IRequest + private static JsonSerializerOptions CreateOptions() + { + var options = new JsonSerializerOptions { - public string Name { get; set; } - } + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + options.Converters.Add(new RequestConverter()); + return options; + } - private sealed class UnsupportedRequest : IRequest - { - private readonly string _name = string.Empty; + private sealed class WritableRequest : IRequest + { + public string Name { get; set; } + } + + private sealed class UnsupportedRequest : IRequest + { + private readonly string _name = string.Empty; - public string Name => _name; - } + public string Name => _name; } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Domain/DomainTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/Domain/DomainTest.cs index 61b33205..68f5cc3b 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Domain/DomainTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/Domain/DomainTest.cs @@ -1,64 +1,63 @@ -using System; +using System; using Cuemon.Extensions.IO; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Domain; using Xunit; -namespace Savvyio.Extensions.Text.Json.Domain +namespace Savvyio.Extensions.Text.Json.Domain; + +public class DomainTest : Test { - public class DomainTest : Test + public DomainTest(ITestOutputHelper output) : base(output) { - public DomainTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Coordinates_ShouldBeAbleToBothSerializeAndDeserialize() - { - var sut = new Coordinates(10.1, 10.2); + [Fact] + public void Coordinates_ShouldBeAbleToBothSerializeAndDeserialize() + { + var sut = new Coordinates(10.1, 10.2); - var json = new JsonMarshaller().Serialize(sut); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(sut); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new JsonMarshaller().Deserialize(json); + var rehydrated = new JsonMarshaller().Deserialize(json); - Assert.Equivalent(sut, rehydrated, true); - } + Assert.Equivalent(sut, rehydrated, true); + } - [Fact] - public void Timestamp_ShouldBeAbleToBothSerializeAndDeserialize() + [Fact] + public void Timestamp_ShouldBeAbleToBothSerializeAndDeserialize() + { + var sut = new Timestamp(DateTimeOffset.UtcNow) { - var sut = new Timestamp(DateTimeOffset.UtcNow) - { - IsDaylightSavingTime = false, - TimeZone = "Europe/Berlin" - }; + IsDaylightSavingTime = false, + TimeZone = "Europe/Berlin" + }; - var json = new JsonMarshaller().Serialize(sut); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(sut); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new JsonMarshaller().Deserialize(json); + var rehydrated = new JsonMarshaller().Deserialize(json); - Assert.Equivalent(sut, rehydrated, true); - } + Assert.Equivalent(sut, rehydrated, true); + } - [Fact] - public void PlatformProviderId_ShouldBeAbleToBothSerializeAndDeserialize() - { - var sut = new PlatformProviderId(Guid.NewGuid()); + [Fact] + public void PlatformProviderId_ShouldBeAbleToBothSerializeAndDeserialize() + { + var sut = new PlatformProviderId(Guid.NewGuid()); - var json = new JsonMarshaller().Serialize(sut); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(sut); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var rehydrated = new JsonMarshaller().Deserialize(json); + var rehydrated = new JsonMarshaller().Deserialize(json); - Assert.Equivalent(sut, rehydrated, true); - } + Assert.Equivalent(sut, rehydrated, true); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/CloudEvents/Cryptography/IntegrationEventExtensionsTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/CloudEvents/Cryptography/IntegrationEventExtensionsTest.cs index f8b1ea93..7f6c18b1 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/CloudEvents/Cryptography/IntegrationEventExtensionsTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/CloudEvents/Cryptography/IntegrationEventExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using Cuemon.Extensions; using Cuemon.Extensions.IO; @@ -9,64 +9,63 @@ using Savvyio.EventDriven.Messaging.CloudEvents.Cryptography; using Xunit; -namespace Savvyio.Extensions.Text.Json.EventDriven.Messaging.CloudEvents.Cryptography +namespace Savvyio.Extensions.Text.Json.EventDriven.Messaging.CloudEvents.Cryptography; + +public class IntegrationEventExtensionsTest : Test { - public class IntegrationEventExtensionsTest : Test + public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) { - public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ToMessage_ToCloudEvent_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + [Fact] + public void ToMessage_ToCloudEvent_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).ToCloudEvent() - .SignCloudEvent(JsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).ToCloudEvent() + .SignCloudEvent(JsonMarshaller.Default, o => + { + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); - var json = JsonMarshaller.Default.Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = JsonMarshaller.Default.Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = JsonMarshaller.Default.Deserialize>(json); + var sut4 = JsonMarshaller.Default.Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"specversion":"1.0","signature":"da3badf039aa727878add5d098cb98817382496720b5ef03f64882a50e05a3c8"}""", jsonString); - } + Assert.Equivalent(sut2, sut4, true); + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"specversion":"1.0","signature":"da3badf039aa727878add5d098cb98817382496720b5ef03f64882a50e05a3c8"}""", jsonString); + } - [Fact] - public void ToMessage_ToCloudEvent_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + [Fact] + public void ToMessage_ToCloudEvent_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => + { + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).ToCloudEvent() + .SignCloudEvent(JsonMarshaller.Default, o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).ToCloudEvent() - .SignCloudEvent(JsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); - var json = JsonMarshaller.Default.Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = JsonMarshaller.Default.Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = JsonMarshaller.Default.Deserialize>(json); + var sut4 = JsonMarshaller.Default.Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"specversion":"1.0","signature":"da3badf039aa727878add5d098cb98817382496720b5ef03f64882a50e05a3c8"}""", jsonString); - } + Assert.Equivalent(sut2, sut4, true); + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"specversion":"1.0","signature":"da3badf039aa727878add5d098cb98817382496720b5ef03f64882a50e05a3c8"}""", jsonString); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/CloudEvents/IntegrationEventExtensionsTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/CloudEvents/IntegrationEventExtensionsTest.cs index 923571e2..00364357 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/CloudEvents/IntegrationEventExtensionsTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/CloudEvents/IntegrationEventExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using Cuemon.Extensions; using Cuemon.Extensions.IO; @@ -8,35 +8,35 @@ using Savvyio.EventDriven.Messaging.CloudEvents; using Xunit; -namespace Savvyio.Extensions.Text.Json.EventDriven.Messaging.CloudEvents +namespace Savvyio.Extensions.Text.Json.EventDriven.Messaging.CloudEvents; + +public class IntegrationEventExtensionsTest : Test { - public class IntegrationEventExtensionsTest : Test + public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) { - public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + [Fact] + public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).ToCloudEvent(); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).ToCloudEvent(); - var json = new JsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new JsonMarshaller().Deserialize>(json); + var sut4 = new JsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal(""" + Assert.Equal(""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -54,29 +54,29 @@ public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_U "specversion": "1.0" } """.ReplaceLineEndings(), jsonString); - } + } - [Fact] - public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + [Fact] + public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + { + var utcNow = DateTime.UtcNow; + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utcNow); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utcNow = DateTime.UtcNow; - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utcNow); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utcNow; - }).ToCloudEvent(); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utcNow; + }).ToCloudEvent(); - var json = new JsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new JsonMarshaller().Deserialize>(json); + var sut4 = new JsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal($$""" + Assert.Equal($$""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -94,6 +94,5 @@ public void ToMessage_ToCloudEvent_ShouldSerializeAndDeserialize_MemberCreated_U "specversion": "1.0" } """.ReplaceLineEndings(), jsonString); - } } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/Cryptography/IntegrationEventExtensionsTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/Cryptography/IntegrationEventExtensionsTest.cs index 00a20252..de5f6219 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/Cryptography/IntegrationEventExtensionsTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/Cryptography/IntegrationEventExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using Cuemon.Extensions; using Cuemon.Extensions.IO; @@ -8,78 +8,77 @@ using Savvyio.Messaging.Cryptography; using Xunit; -namespace Savvyio.Extensions.Text.Json.EventDriven.Messaging.Cryptography +namespace Savvyio.Extensions.Text.Json.EventDriven.Messaging.Cryptography; + +public class IntegrationEventExtensionsTest : Test { - public class IntegrationEventExtensionsTest : Test + public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ToMessage_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() { - public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => + { + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).Sign(JsonMarshaller.Default, o => { - } + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); + var json = JsonMarshaller.Default.Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + + TestOutput.WriteLine(jsonString); - [Fact] - public void ToMessage_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + + var signatureException = Assert.Throws(() => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).Sign(JsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); - var json = JsonMarshaller.Default.Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - - TestOutput.WriteLine(jsonString); - - sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - - var signatureException = Assert.Throws(() => - { - sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); - }); - Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); - - var sut4 = JsonMarshaller.Default.Deserialize>(json); - - Assert.Equivalent(sut2, sut4, true); - - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"signature":"e0b6ca2328019e236d9cd5590c20d12a2a92797f8feb34c044b92f36df013fac"}""", jsonString); - } - - [Fact] - public void ToMessage_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); + }); + Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); + + var sut4 = JsonMarshaller.Default.Deserialize>(json); + + Assert.Equivalent(sut2, sut4, true); + + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"signature":"e0b6ca2328019e236d9cd5590c20d12a2a92797f8feb34c044b92f36df013fac"}""", jsonString); + } + + [Fact] + public void ToMessage_Sign_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => + { + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }).Sign(JsonMarshaller.Default, o => + { + o.SignatureSecret = new byte[] { 1, 2, 3 }; + }); + var json = JsonMarshaller.Default.Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + + TestOutput.WriteLine(jsonString); + + sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); + + var signatureException = Assert.Throws(() => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }).Sign(JsonMarshaller.Default, o => - { - o.SignatureSecret = new byte[] { 1, 2, 3 }; - }); - var json = JsonMarshaller.Default.Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - - TestOutput.WriteLine(jsonString); - - sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 1, 2, 3 }); - - var signatureException = Assert.Throws(() => - { - sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); - }); - Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); - - var sut4 = JsonMarshaller.Default.Deserialize>(json); - - Assert.Equivalent(sut2, sut4, true); - - Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"signature":"e0b6ca2328019e236d9cd5590c20d12a2a92797f8feb34c044b92f36df013fac"}""", jsonString); - } + sut2.CheckSignature(JsonMarshaller.Default, o => o.SignatureSecret = new byte[] { 3, 2, 1 }); + }); + Assert.StartsWith("The signature of the message does not match the cryptographically calculated value. Either you are using an incorrect secret and/or algorithm or the message has been tampered with.", signatureException.Message); + + var sut4 = JsonMarshaller.Default.Deserialize>(json); + + Assert.Equivalent(sut2, sut4, true); + + Assert.Equal("""{"id":"2d4030d32a254ee8a27046e5bafe696a","source":"https://fancy.api/members","type":"MemberCreated","time":"2023-11-16T23:24:17.8414532Z","data":{"name":"Jane Doe","emailAddress":"jd@office.com","metadata":{"memberType":"Savvyio.Assets.EventDriven.MemberCreated, Savvyio.Assets.Tests","eventId":"69bccf3b1117425397c5ed9ed757bb0f","timestamp":"2023-11-16T23:24:17.8414532Z"}},"signature":"e0b6ca2328019e236d9cd5590c20d12a2a92797f8feb34c044b92f36df013fac"}""", jsonString); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/IntegrationEventExtensionsTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/IntegrationEventExtensionsTest.cs index 7a1e8cc3..18b1efb6 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/IntegrationEventExtensionsTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/EventDriven/Messaging/IntegrationEventExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using Cuemon.Extensions; using Cuemon.Extensions.IO; @@ -8,35 +8,35 @@ using Savvyio.Messaging; using Xunit; -namespace Savvyio.Extensions.Text.Json.EventDriven.Messaging +namespace Savvyio.Extensions.Text.Json.EventDriven.Messaging; + +public class IntegrationEventExtensionsTest : Test { - public class IntegrationEventExtensionsTest : Test + public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) { - public IntegrationEventExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + [Fact] + public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface() + { + var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utc = DateTime.Parse("2023-11-16T23:24:17.8414532Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utc); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utc; - }); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utc; + }); - var json = new JsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new JsonMarshaller().Deserialize>(json); + var sut4 = new JsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal(""" + Assert.Equal(""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -53,29 +53,29 @@ public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingInterface } } """.ReplaceLineEndings(), jsonString); - } + } - [Fact] - public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + [Fact] + public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteType() + { + var utcNow = DateTime.UtcNow; + var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utcNow); + var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => { - var utcNow = DateTime.UtcNow; - var sut1 = new MemberCreated("Jane Doe", "jd@office.com").SetEventId("69bccf3b1117425397c5ed9ed757bb0f").SetTimestamp(utcNow); - var sut2 = sut1.ToMessage("https://fancy.api/members".ToUri(), nameof(MemberCreated), o => - { - o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; - o.Time = utcNow; - }); + o.MessageId = "2d4030d32a254ee8a27046e5bafe696a"; + o.Time = utcNow; + }); - var json = new JsonMarshaller().Serialize(sut2); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = new JsonMarshaller().Serialize(sut2); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var sut4 = new JsonMarshaller().Deserialize>(json); + var sut4 = new JsonMarshaller().Deserialize>(json); - Assert.Equivalent(sut2, sut4, true); + Assert.Equivalent(sut2, sut4, true); - Assert.Equal($$""" + Assert.Equal($$""" { "id": "2d4030d32a254ee8a27046e5bafe696a", "source": "https://fancy.api/members", @@ -92,6 +92,5 @@ public void ToMessage_ShouldSerializeAndDeserialize_MemberCreated_UsingConcreteT } } """.ReplaceLineEndings(), jsonString); - } } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/JsonConverterExtensionsTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/JsonConverterExtensionsTest.cs index 110c857b..a884c25e 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/JsonConverterExtensionsTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/JsonConverterExtensionsTest.cs @@ -6,113 +6,112 @@ using Savvyio.Extensions.Text.Json.Converters; using Xunit; -namespace Savvyio.Extensions.Text.Json +namespace Savvyio.Extensions.Text.Json; + +public class JsonConverterExtensionsTest : Test { - public class JsonConverterExtensionsTest : Test + public JsonConverterExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void AddDateTimeConverter_ShouldAddConverter() + { + var converters = new List(); + converters.AddDateTimeConverter(); + + Assert.Single(converters, c => c is DateTimeConverter); + } + + [Fact] + public void AddDateTimeOffsetConverter_ShouldAddConverter() + { + var converters = new List(); + converters.AddDateTimeOffsetConverter(); + + Assert.Single(converters, c => c is DateTimeOffsetConverter); + } + + [Fact] + public void AddSingleValueObjectConverter_ShouldAddConverter() + { + var converters = new List(); + converters.AddSingleValueObjectConverter(); + + Assert.Single(converters, c => c is SingleValueObjectConverter); + } + + [Fact] + public void AddMetadataDictionaryConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() + { + Assert.Throws(() => ((ICollection)null).AddMetadataDictionaryConverter()); + } + + [Fact] + public void AddMessageConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() + { + Assert.Throws(() => ((ICollection)null).AddMessageConverter()); + } + + [Fact] + public void AddRequestConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() + { + Assert.Throws(() => ((ICollection)null).AddRequestConverter()); + } + + [Fact] + public void AddDateTimeConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() + { + Assert.Throws(() => ((ICollection)null).AddDateTimeConverter()); + } + + [Fact] + public void AddDateTimeOffsetConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() + { + Assert.Throws(() => ((ICollection)null).AddDateTimeOffsetConverter()); + } + + [Fact] + public void AddSingleValueObjectConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() + { + Assert.Throws(() => ((ICollection)null).AddSingleValueObjectConverter()); + } + + [Fact] + public void RemoveAllOf_Generic_ShouldRemoveMatchingConverter() + { + var converters = new List(); + converters.AddDateTimeConverter(); + converters.AddDateTimeOffsetConverter(); + + Assert.Equal(2, converters.Count); + + converters.RemoveAllOf(); + + Assert.DoesNotContain(converters, c => c is DateTimeConverter); + } + + [Fact] + public void RemoveAllOf_Generic_ShouldThrowArgumentNullException_WhenConvertersIsNull() + { + Assert.Throws(() => ((ICollection)null).RemoveAllOf()); + } + + [Fact] + public void RemoveAllOf_Params_ShouldRemoveMatchingConverters() + { + var converters = new List(); + converters.AddDateTimeConverter(); + converters.AddDateTimeOffsetConverter(); + + converters.RemoveAllOf(typeof(DateTime), typeof(DateTimeOffset)); + + Assert.Empty(converters); + } + + [Fact] + public void RemoveAllOf_Params_ShouldThrowArgumentNullException_WhenConvertersIsNull() { - public JsonConverterExtensionsTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void AddDateTimeConverter_ShouldAddConverter() - { - var converters = new List(); - converters.AddDateTimeConverter(); - - Assert.Single(converters, c => c is DateTimeConverter); - } - - [Fact] - public void AddDateTimeOffsetConverter_ShouldAddConverter() - { - var converters = new List(); - converters.AddDateTimeOffsetConverter(); - - Assert.Single(converters, c => c is DateTimeOffsetConverter); - } - - [Fact] - public void AddSingleValueObjectConverter_ShouldAddConverter() - { - var converters = new List(); - converters.AddSingleValueObjectConverter(); - - Assert.Single(converters, c => c is SingleValueObjectConverter); - } - - [Fact] - public void AddMetadataDictionaryConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() - { - Assert.Throws(() => ((ICollection)null).AddMetadataDictionaryConverter()); - } - - [Fact] - public void AddMessageConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() - { - Assert.Throws(() => ((ICollection)null).AddMessageConverter()); - } - - [Fact] - public void AddRequestConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() - { - Assert.Throws(() => ((ICollection)null).AddRequestConverter()); - } - - [Fact] - public void AddDateTimeConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() - { - Assert.Throws(() => ((ICollection)null).AddDateTimeConverter()); - } - - [Fact] - public void AddDateTimeOffsetConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() - { - Assert.Throws(() => ((ICollection)null).AddDateTimeOffsetConverter()); - } - - [Fact] - public void AddSingleValueObjectConverter_ShouldThrowArgumentNullException_WhenConvertersIsNull() - { - Assert.Throws(() => ((ICollection)null).AddSingleValueObjectConverter()); - } - - [Fact] - public void RemoveAllOf_Generic_ShouldRemoveMatchingConverter() - { - var converters = new List(); - converters.AddDateTimeConverter(); - converters.AddDateTimeOffsetConverter(); - - Assert.Equal(2, converters.Count); - - converters.RemoveAllOf(); - - Assert.DoesNotContain(converters, c => c is DateTimeConverter); - } - - [Fact] - public void RemoveAllOf_Generic_ShouldThrowArgumentNullException_WhenConvertersIsNull() - { - Assert.Throws(() => ((ICollection)null).RemoveAllOf()); - } - - [Fact] - public void RemoveAllOf_Params_ShouldRemoveMatchingConverters() - { - var converters = new List(); - converters.AddDateTimeConverter(); - converters.AddDateTimeOffsetConverter(); - - converters.RemoveAllOf(typeof(DateTime), typeof(DateTimeOffset)); - - Assert.Empty(converters); - } - - [Fact] - public void RemoveAllOf_Params_ShouldThrowArgumentNullException_WhenConvertersIsNull() - { - Assert.Throws(() => ((ICollection)null).RemoveAllOf(typeof(DateTime))); - } + Assert.Throws(() => ((ICollection)null).RemoveAllOf(typeof(DateTime))); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/JsonSerializerOptionsExtensionsTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/JsonSerializerOptionsExtensionsTest.cs index ba2678fd..93fdb3c6 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/JsonSerializerOptionsExtensionsTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/JsonSerializerOptionsExtensionsTest.cs @@ -4,39 +4,38 @@ using Savvyio.Extensions.Text.Json.Converters; using Xunit; -namespace Savvyio.Extensions.Text.Json +namespace Savvyio.Extensions.Text.Json; + +public class JsonSerializerOptionsExtensionsTest : Test { - public class JsonSerializerOptionsExtensionsTest : Test + public JsonSerializerOptionsExtensionsTest(ITestOutputHelper output) : base(output) { - public JsonSerializerOptionsExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void Clone_ShouldThrowArgumentNullException_WhenOptionsIsNull() - { - Assert.Throws(() => ((JsonSerializerOptions)null).Clone()); - } + [Fact] + public void Clone_ShouldThrowArgumentNullException_WhenOptionsIsNull() + { + Assert.Throws(() => ((JsonSerializerOptions)null).Clone()); + } - [Fact] - public void Clone_ShouldReturnNewInstance_WithoutSetup() - { - var original = new JsonSerializerOptions { WriteIndented = true }; - var cloned = original.Clone(); + [Fact] + public void Clone_ShouldReturnNewInstance_WithoutSetup() + { + var original = new JsonSerializerOptions { WriteIndented = true }; + var cloned = original.Clone(); - Assert.NotSame(original, cloned); - Assert.True(cloned.WriteIndented); - } + Assert.NotSame(original, cloned); + Assert.True(cloned.WriteIndented); + } - [Fact] - public void Clone_ShouldApplySetup_WhenProvided() - { - var original = new JsonSerializerOptions { WriteIndented = true }; - var cloned = original.Clone(o => o.WriteIndented = false); + [Fact] + public void Clone_ShouldApplySetup_WhenProvided() + { + var original = new JsonSerializerOptions { WriteIndented = true }; + var cloned = original.Clone(o => o.WriteIndented = false); - Assert.NotSame(original, cloned); - Assert.True(original.WriteIndented); - Assert.False(cloned.WriteIndented); - } + Assert.NotSame(original, cloned); + Assert.True(original.WriteIndented); + Assert.False(cloned.WriteIndented); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/MetadataDictionaryTest.cs b/test/Savvyio.Extensions.Text.Json.Tests/MetadataDictionaryTest.cs index 6dd71f27..86237a5f 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/MetadataDictionaryTest.cs +++ b/test/Savvyio.Extensions.Text.Json.Tests/MetadataDictionaryTest.cs @@ -1,40 +1,39 @@ -using System; +using System; using Cuemon.Extensions.IO; using Cuemon.Extensions.Text.Json.Formatters; using Codebelt.Extensions.Xunit; using Xunit; -namespace Savvyio.Extensions.Text.Json +namespace Savvyio.Extensions.Text.Json; + +public class MetadataDictionaryTest : Test { - public class MetadataDictionaryTest : Test + public MetadataDictionaryTest(ITestOutputHelper output) : base(output) { - public MetadataDictionaryTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void MetadataDictionary_ShouldUseCustomConverter() + [Fact] + public void MetadataDictionary_ShouldUseCustomConverter() + { + var md = new MetadataDictionary() { - var md = new MetadataDictionary() - { - { "guid", $"{Guid.NewGuid()}" }, - { "int32", 2312313 }, - { "dateTime", DateTime.UtcNow } - }; + { "guid", $"{Guid.NewGuid()}" }, + { "int32", 2312313 }, + { "dateTime", DateTime.UtcNow } + }; - var json = JsonFormatter.SerializeObject(md); - var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); + var json = JsonFormatter.SerializeObject(md); + var jsonString = json.ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(jsonString); + TestOutput.WriteLine(jsonString); - var mdRehydrated = JsonFormatter.DeserializeObject(json, o => - { - o.Settings.Converters - .AddMessageConverter() - .AddMetadataDictionaryConverter(); - }); + var mdRehydrated = JsonFormatter.DeserializeObject(json, o => + { + o.Settings.Converters + .AddMessageConverter() + .AddMetadataDictionaryConverter(); + }); - Assert.Equivalent(md, mdRehydrated, true); - } + Assert.Equivalent(md, mdRehydrated, true); } } diff --git a/test/Savvyio.Extensions.Text.Json.Tests/Savvyio.Extensions.Text.Json.Tests.csproj b/test/Savvyio.Extensions.Text.Json.Tests/Savvyio.Extensions.Text.Json.Tests.csproj index d879bb94..b75ce740 100644 --- a/test/Savvyio.Extensions.Text.Json.Tests/Savvyio.Extensions.Text.Json.Tests.csproj +++ b/test/Savvyio.Extensions.Text.Json.Tests/Savvyio.Extensions.Text.Json.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Extensions.Text.Json diff --git a/test/Savvyio.FunctionalTests/Assets/AccountData.cs b/test/Savvyio.FunctionalTests/Assets/AccountData.cs index 72b7b622..45af3d31 100644 --- a/test/Savvyio.FunctionalTests/Assets/AccountData.cs +++ b/test/Savvyio.FunctionalTests/Assets/AccountData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Cuemon.Extensions; @@ -7,37 +7,36 @@ using Savvyio.Assets.Queries; using Savvyio.Extensions.Dapper; -namespace Savvyio.Assets +namespace Savvyio.Assets; + +public class AccountData : DapperDataStore { - public class AccountData : DapperDataStore + public AccountData(IDapperDataSource source) : base(source) { - public AccountData(IDapperDataSource source) : base(source) - { - } + } - public override Task CreateAsync(AccountProjection dto, Action setup = null) - { - return Source.ExecuteAsync("INSERT INTO AccountProjection (Id, FullName, EmailAddress) VALUES (@Id, @FullName, @EmailAddress)", dto); - } + public override Task CreateAsync(AccountProjection dto, Action setup = null) + { + return Source.ExecuteAsync("INSERT INTO AccountProjection (Id, FullName, EmailAddress) VALUES (@Id, @FullName, @EmailAddress)", dto); + } - public override Task UpdateAsync(AccountProjection dto, Action setup = null) - { - throw new NotImplementedException(); - } + public override Task UpdateAsync(AccountProjection dto, Action setup = null) + { + throw new NotImplementedException(); + } - public override Task GetByIdAsync(object id, Action setup) - { - return Source.QuerySingleOrDefaultAsync("SELECT * FROM AccountProjection WHERE Id = @Id", new { Id = id }); - } + public override Task GetByIdAsync(object id, Action setup) + { + return Source.QuerySingleOrDefaultAsync("SELECT * FROM AccountProjection WHERE Id = @Id", new { Id = id }); + } - public override Task> FindAllAsync(Action setup = null) - { - return setup == null ? Source.QueryAsync("SELECT * FROM AccountProjection") : Source.QueryAsync(setup.Configure()); - } + public override Task> FindAllAsync(Action setup = null) + { + return setup == null ? Source.QueryAsync("SELECT * FROM AccountProjection") : Source.QueryAsync(setup.Configure()); + } - public override Task DeleteAsync(AccountProjection dto, Action setup = null) - { - throw new NotImplementedException(); - } + public override Task DeleteAsync(AccountProjection dto, Action setup = null) + { + throw new NotImplementedException(); } } diff --git a/test/Savvyio.FunctionalTests/DistributedMediatorTest.cs b/test/Savvyio.FunctionalTests/DistributedMediatorTest.cs index f99f3802..5aa2f2f9 100644 --- a/test/Savvyio.FunctionalTests/DistributedMediatorTest.cs +++ b/test/Savvyio.FunctionalTests/DistributedMediatorTest.cs @@ -1,4 +1,4 @@ -using Amazon; +using Amazon; using Amazon.Runtime; using Codebelt.Extensions.Newtonsoft.Json.Formatters; using Codebelt.Extensions.Xunit; @@ -38,170 +38,169 @@ using Xunit; using Xunit.v3.Priority; -namespace Savvyio +namespace Savvyio; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class DistributedMediatorTest : Test { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class DistributedMediatorTest : Test - { - private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - private static readonly string UniqueId = $"{Guid.NewGuid():N}"; + private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + private static readonly string UniqueId = $"{Guid.NewGuid():N}"; - public DistributedMediatorTest() - { - NewtonsoftJsonFormatterOptions.DefaultConverters += list => list.Add(new ValueObjectConverter()); - } + public DistributedMediatorTest() + { + NewtonsoftJsonFormatterOptions.DefaultConverters += list => list.Add(new ValueObjectConverter()); + } - [Fact, Priority(0)] - public async Task EmulateWebApi_Controller_SendCommand() + [Fact, Priority(0)] + public async Task EmulateWebApi_Controller_SendCommand() + { + await using var test = WebHostTestFactory.CreateWithHostBuilderContext((context, services) => { - await using var test = WebHostTestFactory.CreateWithHostBuilderContext((context, services) => + services.AddMarshaller(); + services.AddAmazonCommandQueue(o => { - services.AddMarshaller(); - services.AddAmazonCommandQueue(o => - { - o.Credentials = new BasicAWSCredentials(context.Configuration["AWS:IAM:AccessKey"], context.Configuration["AWS:IAM:SecretKey"]); - o.Endpoint = RegionEndpoint.EUWest1; - o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{context.Configuration["AWS:CallerIdentity"]}/distribute-mediator-test"); - }); - - AmazonResourceNameOptions.DefaultAccountId = context.Configuration["AWS:CallerIdentity"]; + o.Credentials = new BasicAWSCredentials(context.Configuration["AWS:IAM:AccessKey"], context.Configuration["AWS:IAM:SecretKey"]); + o.Endpoint = RegionEndpoint.EUWest1; + o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{context.Configuration["AWS:CallerIdentity"]}/distribute-mediator-test"); }); - var correlationId = Guid.NewGuid().ToString("N"); - var platformProviderId = Guid.NewGuid(); - var fullName = "Michael Amazortensen"; - var emailAddress = IsLinux ? $"linux-{UniqueId}@aws.dev" : $"windows-{UniqueId}@aws.dev"; - var createAccount = new CreateAccount(platformProviderId, fullName, emailAddress).SetCorrelationId(correlationId); + AmazonResourceNameOptions.DefaultAccountId = context.Configuration["AWS:CallerIdentity"]; + }); + var correlationId = Guid.NewGuid().ToString("N"); + var platformProviderId = Guid.NewGuid(); + var fullName = "Michael Amazortensen"; + var emailAddress = IsLinux ? $"linux-{UniqueId}@aws.dev" : $"windows-{UniqueId}@aws.dev"; - var commandQueue = test.Host.Services.GetRequiredService>(); + var createAccount = new CreateAccount(platformProviderId, fullName, emailAddress).SetCorrelationId(correlationId); - await commandQueue.SendAsync(createAccount.ToMessage("urn:command:create-account".ToUri(), nameof(createAccount)).Yield()).ConfigureAwait(false); - } + var commandQueue = test.Host.Services.GetRequiredService>(); - [Fact, Priority(1)] - public async Task EmulateWorker_ReceiveCommand() + await commandQueue.SendAsync(createAccount.ToMessage("urn:command:create-account".ToUri(), nameof(createAccount)).Yield()).ConfigureAwait(false); + } + + [Fact, Priority(1)] + public async Task EmulateWorker_ReceiveCommand() + { + await using var test = WebHostTestFactory.CreateWithHostBuilderContext((context, services) => { - await using var test = WebHostTestFactory.CreateWithHostBuilderContext((context, services) => + services.AddMarshaller(); + services.AddEfCoreAggregateDataSource(o => { - services.AddMarshaller(); - services.AddEfCoreAggregateDataSource(o => - { - o.ContextConfigurator = b => b.UseInMemoryDatabase($"AWS{nameof(Account)}").EnableDetailedErrors().LogTo(Console.WriteLine); - o.ModelConstructor = mb => mb.AddAccount(); - }).AddEfCoreRepository(); + o.ContextConfigurator = b => b.UseInMemoryDatabase($"AWS{nameof(Account)}").EnableDetailedErrors().LogTo(Console.WriteLine); + o.ModelConstructor = mb => mb.AddAccount(); + }).AddEfCoreRepository(); - services.AddEfCoreAggregateDataSource(o => - { - o.ContextConfigurator = b => b.UseInMemoryDatabase($"AWS{nameof(PlatformProvider)}").EnableDetailedErrors().LogTo(Console.WriteLine); - o.ModelConstructor = mb => mb.AddPlatformProvider(); - }).AddEfCoreRepository(); + services.AddEfCoreAggregateDataSource(o => + { + o.ContextConfigurator = b => b.UseInMemoryDatabase($"AWS{nameof(PlatformProvider)}").EnableDetailedErrors().LogTo(Console.WriteLine); + o.ModelConstructor = mb => mb.AddPlatformProvider(); + }).AddEfCoreRepository(); - services.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection().SetDefaults().AddAccountTable().AddPlatformProviderTable()) - .AddDapperDataStore() - .AddDapperExtensionsDataStore(); + services.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection().SetDefaults().AddAccountTable().AddPlatformProviderTable()) + .AddDapperDataStore() + .AddDapperExtensionsDataStore(); - services.AddAmazonCommandQueue(o => - { - o.Credentials = new BasicAWSCredentials(context.Configuration["AWS:IAM:AccessKey"], context.Configuration["AWS:IAM:SecretKey"]); - o.Endpoint = RegionEndpoint.EUWest1; - o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{context.Configuration["AWS:CallerIdentity"]}/distribute-mediator-test"); - o.ReceiveContext.AssumeMessageProcessed = false; - o.ReceiveContext.VisibilityTimeout = TimeSpan.FromSeconds(5); - }); - - services.AddAmazonEventBus(o => - { - o.Credentials = new BasicAWSCredentials(context.Configuration["AWS:IAM:AccessKey"], context.Configuration["AWS:IAM:SecretKey"]); - o.Endpoint = RegionEndpoint.EUWest1; - o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{context.Configuration["AWS:CallerIdentity"]}/distribute-mediator-test.fifo"); - }); + services.AddAmazonCommandQueue(o => + { + o.Credentials = new BasicAWSCredentials(context.Configuration["AWS:IAM:AccessKey"], context.Configuration["AWS:IAM:SecretKey"]); + o.Endpoint = RegionEndpoint.EUWest1; + o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{context.Configuration["AWS:CallerIdentity"]}/distribute-mediator-test"); + o.ReceiveContext.AssumeMessageProcessed = false; + o.ReceiveContext.VisibilityTimeout = TimeSpan.FromSeconds(5); + }); - services.AddSavvyIO(o => - { - o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true).AddMediator(); - }); + services.AddAmazonEventBus(o => + { + o.Credentials = new BasicAWSCredentials(context.Configuration["AWS:IAM:AccessKey"], context.Configuration["AWS:IAM:SecretKey"]); + o.Endpoint = RegionEndpoint.EUWest1; + o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{context.Configuration["AWS:CallerIdentity"]}/distribute-mediator-test.fifo"); + }); - AmazonResourceNameOptions.DefaultAccountId = context.Configuration["AWS:CallerIdentity"]; + services.AddSavvyIO(o => + { + o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true).AddMediator(); }); - await Task.Delay(TimeSpan.FromSeconds(5)); - using var scope = test.Host.Services.GetService().CreateScope(); + AmazonResourceNameOptions.DefaultAccountId = context.Configuration["AWS:CallerIdentity"]; + }); + await Task.Delay(TimeSpan.FromSeconds(5)); - var commandQueue = scope.ServiceProvider.GetRequiredService>(); - var mediator = scope.ServiceProvider.GetRequiredService(); + using var scope = test.Host.Services.GetService().CreateScope(); - IMessage createAccountMessage = null; - var cmdDeadline = DateTime.UtcNow.AddSeconds(90); - while (createAccountMessage == null && DateTime.UtcNow < cmdDeadline) + var commandQueue = scope.ServiceProvider.GetRequiredService>(); + var mediator = scope.ServiceProvider.GetRequiredService(); + + IMessage createAccountMessage = null; + var cmdDeadline = DateTime.UtcNow.AddSeconds(90); + while (createAccountMessage == null && DateTime.UtcNow < cmdDeadline) + { + await foreach (var msg in commandQueue.ReceiveAsync()) { - await foreach (var msg in commandQueue.ReceiveAsync()) + if (msg.Data is CreateAccount ca && ca.EmailAddress.Contains(UniqueId)) { - if (msg.Data is CreateAccount ca && ca.EmailAddress.Contains(UniqueId)) - { - await msg.AcknowledgeAsync().ConfigureAwait(false); - createAccountMessage = msg; - } + await msg.AcknowledgeAsync().ConfigureAwait(false); + createAccountMessage = msg; } } + } - Assert.NotNull(createAccountMessage); - var receivedCommand = Assert.IsType(createAccountMessage.Data); + Assert.NotNull(createAccountMessage); + var receivedCommand = Assert.IsType(createAccountMessage.Data); - await mediator.CommitAsync(receivedCommand); + await mediator.CommitAsync(receivedCommand); - var accounts = scope.ServiceProvider.GetRequiredService>(); + var accounts = scope.ServiceProvider.GetRequiredService>(); - var entity = await accounts.FindAllAsync(account => account.EmailAddress == receivedCommand.EmailAddress).SingleOrDefaultAsync(); + var entity = await accounts.FindAllAsync(account => account.EmailAddress == receivedCommand.EmailAddress).SingleOrDefaultAsync(); - var dao = await mediator.QueryAsync(new GetAccount(entity.Id)).ConfigureAwait(false); + var dao = await mediator.QueryAsync(new GetAccount(entity.Id)).ConfigureAwait(false); - Assert.Equal(entity.Id, dao.Id); - Assert.Equal(entity.EmailAddress, dao.EmailAddress); - Assert.Equal(entity.FullName, dao.FullName); + Assert.Equal(entity.Id, dao.Id); + Assert.Equal(entity.EmailAddress, dao.EmailAddress); + Assert.Equal(entity.FullName, dao.FullName); - await Task.Delay(TimeSpan.FromSeconds(5)); - } + await Task.Delay(TimeSpan.FromSeconds(5)); + } - [Fact, Priority(2)] - public async Task EmulateAnotherWorker_SubscribingToAccountCreated() + [Fact, Priority(2)] + public async Task EmulateAnotherWorker_SubscribingToAccountCreated() + { + await using var test = WebHostTestFactory.CreateWithHostBuilderContext((context, services) => { - await using var test = WebHostTestFactory.CreateWithHostBuilderContext((context, services) => + services.Configure(o => o.Settings.Converters.AddMessageConverter().AddMetadataDictionaryConverter()); + services.AddMarshaller(); + services.AddAmazonEventBus(o => { - services.Configure(o => o.Settings.Converters.AddMessageConverter().AddMetadataDictionaryConverter()); - services.AddMarshaller(); - services.AddAmazonEventBus(o => - { - o.Credentials = new BasicAWSCredentials(context.Configuration["AWS:IAM:AccessKey"], context.Configuration["AWS:IAM:SecretKey"]); - o.Endpoint = RegionEndpoint.EUWest1; - o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{context.Configuration["AWS:CallerIdentity"]}/distribute-mediator-test.fifo"); - o.ReceiveContext.AssumeMessageProcessed = false; - o.ReceiveContext.VisibilityTimeout = TimeSpan.FromSeconds(5); - }); - - AmazonResourceNameOptions.DefaultAccountId = context.Configuration["AWS:CallerIdentity"]; + o.Credentials = new BasicAWSCredentials(context.Configuration["AWS:IAM:AccessKey"], context.Configuration["AWS:IAM:SecretKey"]); + o.Endpoint = RegionEndpoint.EUWest1; + o.SourceQueue = new Uri($"https://sqs.eu-west-1.amazonaws.com/{context.Configuration["AWS:CallerIdentity"]}/distribute-mediator-test.fifo"); + o.ReceiveContext.AssumeMessageProcessed = false; + o.ReceiveContext.VisibilityTimeout = TimeSpan.FromSeconds(5); }); - var eventBus = test.Host.Services.GetRequiredService>(); - var invocationCount = 0; - var deadline = DateTime.UtcNow.AddSeconds(90); - while (invocationCount == 0 && DateTime.UtcNow < deadline) + AmazonResourceNameOptions.DefaultAccountId = context.Configuration["AWS:CallerIdentity"]; + }); + var eventBus = test.Host.Services.GetRequiredService>(); + + var invocationCount = 0; + var deadline = DateTime.UtcNow.AddSeconds(90); + while (invocationCount == 0 && DateTime.UtcNow < deadline) + { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + await eventBus.SubscribeAsync(async (message, token) => { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); - await eventBus.SubscribeAsync(async (message, token) => + if (message.Data is AccountCreated ac && ac.EmailAddress.Contains(UniqueId)) { - if (message.Data is AccountCreated ac && ac.EmailAddress.Contains(UniqueId)) - { - invocationCount++; - await message.AcknowledgeAsync().ConfigureAwait(false); - cts.Cancel(); - } - }, o => - { - o.CancellationToken = cts.Token; - o.ThrowIfCancellationWasRequested = false; - }).ConfigureAwait(false); - } - Assert.Equal(1, invocationCount); + invocationCount++; + await message.AcknowledgeAsync().ConfigureAwait(false); + cts.Cancel(); + } + }, o => + { + o.CancellationToken = cts.Token; + o.ThrowIfCancellationWasRequested = false; + }).ConfigureAwait(false); } + Assert.Equal(1, invocationCount); } } diff --git a/test/Savvyio.FunctionalTests/Extensions/DependencyInjection/ServiceCollectionExtensionsTest.cs b/test/Savvyio.FunctionalTests/Extensions/DependencyInjection/ServiceCollectionExtensionsTest.cs index 1f1b72c3..1f8cb819 100644 --- a/test/Savvyio.FunctionalTests/Extensions/DependencyInjection/ServiceCollectionExtensionsTest.cs +++ b/test/Savvyio.FunctionalTests/Extensions/DependencyInjection/ServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Codebelt.Extensions.Xunit; using Codebelt.Extensions.Xunit.Hosting; @@ -6,36 +6,36 @@ using Microsoft.Extensions.Logging; using Xunit; -namespace Savvyio.Extensions.DependencyInjection +namespace Savvyio.Extensions.DependencyInjection; + +public class ServiceCollectionExtensionsTest : Test { - public class ServiceCollectionExtensionsTest : Test + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) { - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddHandlerServicesDescriptor_ShouldAddHandlerServiceDescriptorAsIHandlerServicesDescriptor() - { - using var test = HostTestFactory.Create( - services => - { - services.AddXunitTestLogging(TestOutput); - services.AddSavvyIO(o => o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true)); - services.AddHandlerServicesDescriptor(); - }); + [Fact] + public void AddHandlerServicesDescriptor_ShouldAddHandlerServiceDescriptorAsIHandlerServicesDescriptor() + { + using var test = HostTestFactory.Create( + services => + { + services.AddXunitTestLogging(TestOutput); + services.AddSavvyIO(o => o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true)); + services.AddHandlerServicesDescriptor(); + }); - test.Host.Services.WriteHandlerDiscoveriesToLog(); + test.Host.Services.WriteHandlerDiscoveriesToLog(); - var loggerEntry = test.Host.Services.GetRequiredService>() - .GetTestStore() - .Query(entry => entry.Severity == LogLevel.Information && entry.Message.Contains("Discovered 2 ICommandHandler implementations")) - .SingleOrDefault(); + var loggerEntry = test.Host.Services.GetRequiredService>() + .GetTestStore() + .Query(entry => entry.Severity == LogLevel.Information && entry.Message.Contains("Discovered 2 ICommandHandler implementations")) + .SingleOrDefault(); - Assert.NotNull(loggerEntry); - Assert.NotNull(test.Host.Services.GetService()); - Assert.IsType(test.Host.Services.GetService()); - Assert.Equal(""" + Assert.NotNull(loggerEntry); + Assert.NotNull(test.Host.Services.GetService()); + Assert.IsType(test.Host.Services.GetService()); + Assert.Equal(""" Information: Discovered 2 ICommandHandler implementations covering a total of 5 ICommand methods Assembly: Savvyio.Assets.Tests @@ -102,21 +102,20 @@ Discovered 1 IQueryHandler implementation covering a total of 4 IQuery methods ------------------------------------------------------------------------------ """.ReplaceLineEndings(), loggerEntry.Message); - } + } - [Fact] - public void AddHandlerServicesDescriptor_ShouldThrowInvalidOperationException_SinceEnableHandlerServicesDescriptorWasNotSetupInAddSavvyIO() - { - using var test = HostTestFactory.Create( - services => - { - services.AddXunitTestLogging(TestOutput); - services.AddSavvyIO(o => o.UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true)); - services.AddHandlerServicesDescriptor(); - }); + [Fact] + public void AddHandlerServicesDescriptor_ShouldThrowInvalidOperationException_SinceEnableHandlerServicesDescriptorWasNotSetupInAddSavvyIO() + { + using var test = HostTestFactory.Create( + services => + { + services.AddXunitTestLogging(TestOutput); + services.AddSavvyIO(o => o.UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true)); + services.AddHandlerServicesDescriptor(); + }); - Assert.Throws(() => test.Host.Services.WriteHandlerDiscoveriesToLog()); - Assert.Null(test.Host.Services.GetService()); - } + Assert.Throws(() => test.Host.Services.WriteHandlerDiscoveriesToLog()); + Assert.Null(test.Host.Services.GetService()); } } diff --git a/test/Savvyio.FunctionalTests/MediatorTest.cs b/test/Savvyio.FunctionalTests/MediatorTest.cs index c9440895..11d32bee 100644 --- a/test/Savvyio.FunctionalTests/MediatorTest.cs +++ b/test/Savvyio.FunctionalTests/MediatorTest.cs @@ -28,54 +28,54 @@ using Xunit.v3.Priority; using YamlDotNet.Serialization.NamingConventions; -namespace Savvyio +namespace Savvyio; + +[TestMethodOrderer(typeof(PriorityMethodOrderer))] +public class MediatorTest : HostTest { - [TestMethodOrderer(typeof(PriorityMethodOrderer))] - public class MediatorTest : HostTest - { - private IServiceProvider _provider; + private IServiceProvider _provider; - public MediatorTest(ManagedHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - _provider = hostFixture.Host.Services.CreateScope().ServiceProvider; - } + public MediatorTest(ManagedHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _provider = hostFixture.Host.Services.CreateScope().ServiceProvider; + } - [Fact, Priority(0)] - public async Task RegisterNewUser() - { - var correlationId = Guid.NewGuid().ToString("N"); - var caPpId = Guid.NewGuid(); - var caFullName = "Michael Mortensen"; - var caEmailAddress = "root@gimlichael.dev"; + [Fact, Priority(0)] + public async Task RegisterNewUser() + { + var correlationId = Guid.NewGuid().ToString("N"); + var caPpId = Guid.NewGuid(); + var caFullName = "Michael Mortensen"; + var caEmailAddress = "root@gimlichael.dev"; - var mediator = _provider.GetRequiredService(); + var mediator = _provider.GetRequiredService(); - var hsd = _provider.GetRequiredService(); - var plaintext = hsd.ToString(); + var hsd = _provider.GetRequiredService(); + var plaintext = hsd.ToString(); - var mappings = hsd.GenerateHandlerDiscoveries(); - var json = JsonMarshaller.Create().Serialize(mappings).ToEncodedString(o => o.LeaveOpen = true); - var newtonsoftJson = NewtonsoftJsonMarshaller.Create().Serialize(mappings).ToEncodedString(o => o.LeaveOpen = true); - var yaml = YamlFormatter.SerializeObject(mappings, o => o.Settings.NamingConvention = CamelCaseNamingConvention.Instance).ToEncodedString(o => o.LeaveOpen = true); + var mappings = hsd.GenerateHandlerDiscoveries(); + var json = JsonMarshaller.Create().Serialize(mappings).ToEncodedString(o => o.LeaveOpen = true); + var newtonsoftJson = NewtonsoftJsonMarshaller.Create().Serialize(mappings).ToEncodedString(o => o.LeaveOpen = true); + var yaml = YamlFormatter.SerializeObject(mappings, o => o.Settings.NamingConvention = CamelCaseNamingConvention.Instance).ToEncodedString(o => o.LeaveOpen = true); - TestOutput.WriteLine(json); + TestOutput.WriteLine(json); - TestOutput.WriteLine(System.Environment.NewLine); - TestOutput.WriteLine("---"); - TestOutput.WriteLine(System.Environment.NewLine); + TestOutput.WriteLine(System.Environment.NewLine); + TestOutput.WriteLine("---"); + TestOutput.WriteLine(System.Environment.NewLine); - TestOutput.WriteLine(yaml); + TestOutput.WriteLine(yaml); - var accountRepo = _provider.GetRequiredService>(); + var accountRepo = _provider.GetRequiredService>(); - await mediator.CommitAsync(new CreateAccount(caPpId, caFullName, caEmailAddress).SetCorrelationId(correlationId)); + await mediator.CommitAsync(new CreateAccount(caPpId, caFullName, caEmailAddress).SetCorrelationId(correlationId)); - var entity = await accountRepo.FindAllAsync(a => a.Metadata.Contains(new KeyValuePair(MetadataDictionary.CorrelationId, correlationId))).SingleOrDefaultAsync(); + var entity = await accountRepo.FindAllAsync(a => a.Metadata.Contains(new KeyValuePair(MetadataDictionary.CorrelationId, correlationId))).SingleOrDefaultAsync(); - var dao = await mediator.QueryAsync(new GetAccount(entity.Id)).ConfigureAwait(false); + var dao = await mediator.QueryAsync(new GetAccount(entity.Id)).ConfigureAwait(false); - Assert.True(Match(""" + Assert.True(Match(""" Discovered 2 ICommandHandler implementations covering a total of 5 ICommand methods Assembly: Savvyio.Assets.Tests @@ -142,8 +142,8 @@ Discovered 1 IQueryHandler implementation covering a total of 4 IQuery methods ------------------------------------------------------------------------------ """.ReplaceLineEndings(), plaintext, o => o.ThrowOnNoMatch = true)); - Assert.Equal(json, newtonsoftJson); - Assert.True(Match(""" + Assert.Equal(json, newtonsoftJson); + Assert.True(Match(""" [ { "abstractionType": "ICommandHandler", @@ -335,7 +335,7 @@ Discovered 1 IQueryHandler implementation covering a total of 4 IQuery methods ] """.ReplaceLineEndings(), json, o => o.ThrowOnNoMatch = true)); - Assert.True(Match(""" + Assert.True(Match(""" - abstractionType: ICommandHandler implementationsCount: 2 delegateType: ICommand @@ -433,79 +433,78 @@ Discovered 1 IQueryHandler implementation covering a total of 4 IQuery methods """.ReplaceLineEndings(), yaml, o => o.ThrowOnNoMatch = true)); - Assert.Equal(entity.Id, dao.Id); - Assert.Equal(entity.EmailAddress, dao.EmailAddress); - Assert.Equal(entity.FullName, dao.FullName); + Assert.Equal(entity.Id, dao.Id); + Assert.Equal(entity.EmailAddress, dao.EmailAddress); + Assert.Equal(entity.FullName, dao.FullName); - } + } - [Fact, Priority(1)] - public async Task RegisterNewUser_ShouldFailWithValidationException_BecauseOfUniqueEmailAddress() - { - var correlationId = Guid.NewGuid().ToString("N"); - var caPpId = Guid.NewGuid(); - var caFullName = "El Presidente"; - var caEmailAddress = "root@gimlichael.dev"; + [Fact, Priority(1)] + public async Task RegisterNewUser_ShouldFailWithValidationException_BecauseOfUniqueEmailAddress() + { + var correlationId = Guid.NewGuid().ToString("N"); + var caPpId = Guid.NewGuid(); + var caFullName = "El Presidente"; + var caEmailAddress = "root@gimlichael.dev"; - var mediator = _provider.GetRequiredService(); + var mediator = _provider.GetRequiredService(); - await Assert.ThrowsAsync(() => mediator.CommitAsync(new CreateAccount(caPpId, caFullName, caEmailAddress).SetCorrelationId(correlationId))); - } + await Assert.ThrowsAsync(() => mediator.CommitAsync(new CreateAccount(caPpId, caFullName, caEmailAddress).SetCorrelationId(correlationId))); + } - [Fact, Priority(2)] - public async Task RegisterAnotherUser() - { - var caPpId = Guid.NewGuid(); - var caFullName = "El Presidento"; - var caEmailAddress = "makemyday@us.gov"; + [Fact, Priority(2)] + public async Task RegisterAnotherUser() + { + var caPpId = Guid.NewGuid(); + var caFullName = "El Presidento"; + var caEmailAddress = "makemyday@us.gov"; - var mediator = _provider.GetRequiredService(); + var mediator = _provider.GetRequiredService(); - await mediator.CommitAsync(new CreateAccount(caPpId, caFullName, caEmailAddress)); - } + await mediator.CommitAsync(new CreateAccount(caPpId, caFullName, caEmailAddress)); + } - [Fact, Priority(3)] - public async Task VerifyUsers() + [Fact, Priority(3)] + public async Task VerifyUsers() + { + var accountRepo = _provider.GetRequiredService>(); + var accountDao = _provider.GetRequiredService>(); + var daos = new List(await accountDao.FindAllAsync().ConfigureAwait(false)); + var entities = new List(await accountRepo.FindAllAsync().ConfigureAwait(false)); + foreach (var entity in entities) { - var accountRepo = _provider.GetRequiredService>(); - var accountDao = _provider.GetRequiredService>(); - var daos = new List(await accountDao.FindAllAsync().ConfigureAwait(false)); - var entities = new List(await accountRepo.FindAllAsync().ConfigureAwait(false)); - foreach (var entity in entities) - { - TestOutput.WriteLine(entity.EmailAddress); - Assert.Equal(entity.Id, daos.Single(ac => ac.Id == entity.Id).Id); - Assert.Equal(entity.EmailAddress, daos.Single(ac => ac.Id == entity.Id).EmailAddress); - Assert.Equal(entity.FullName, daos.Single(ac => ac.Id == entity.Id).FullName); - } - - Assert.Equal(2, entities.Count); - Assert.Equal(2, daos.Count); + TestOutput.WriteLine(entity.EmailAddress); + Assert.Equal(entity.Id, daos.Single(ac => ac.Id == entity.Id).Id); + Assert.Equal(entity.EmailAddress, daos.Single(ac => ac.Id == entity.Id).EmailAddress); + Assert.Equal(entity.FullName, daos.Single(ac => ac.Id == entity.Id).FullName); } - public override void ConfigureServices(IServiceCollection services) + Assert.Equal(2, entities.Count); + Assert.Equal(2, daos.Count); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddEfCoreAggregateDataSource(o => { - services.AddEfCoreAggregateDataSource(o => - { - o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(Account)).EnableDetailedErrors().LogTo(Console.WriteLine); - o.ModelConstructor = mb => mb.AddAccount(); - }).AddEfCoreRepository(); + o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(Account)).EnableDetailedErrors().LogTo(Console.WriteLine); + o.ModelConstructor = mb => mb.AddAccount(); + }).AddEfCoreRepository(); - services.AddEfCoreAggregateDataSource(o => - { - o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(PlatformProvider)).EnableDetailedErrors().LogTo(Console.WriteLine); - o.ModelConstructor = mb => mb.AddPlatformProvider(); - }).AddEfCoreRepository(); + services.AddEfCoreAggregateDataSource(o => + { + o.ContextConfigurator = b => b.UseInMemoryDatabase(nameof(PlatformProvider)).EnableDetailedErrors().LogTo(Console.WriteLine); + o.ModelConstructor = mb => mb.AddPlatformProvider(); + }).AddEfCoreRepository(); - services.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection().SetDefaults().AddAccountTable().AddPlatformProviderTable()) - .AddDapperDataStore() - .AddDapperExtensionsDataStore(); + services.AddDapperDataSource(o => o.ConnectionFactory = () => new SqliteConnection().SetDefaults().AddAccountTable().AddPlatformProviderTable()) + .AddDapperDataStore() + .AddDapperExtensionsDataStore(); - services.AddSavvyIO(o => - { - o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true).AddMediator(); - }) - .AddHandlerServicesDescriptor(); - } + services.AddSavvyIO(o => + { + o.EnableHandlerServicesDescriptor().UseAutomaticDispatcherDiscovery(true).UseAutomaticHandlerDiscovery(true).AddMediator(); + }) + .AddHandlerServicesDescriptor(); } } diff --git a/test/Savvyio.FunctionalTests/Savvyio.FunctionalTests.csproj b/test/Savvyio.FunctionalTests/Savvyio.FunctionalTests.csproj index 12a5f955..0bbe5f5e 100644 --- a/test/Savvyio.FunctionalTests/Savvyio.FunctionalTests.csproj +++ b/test/Savvyio.FunctionalTests/Savvyio.FunctionalTests.csproj @@ -1,4 +1,4 @@ - + Savvyio diff --git a/test/Savvyio.Queries.Tests/Assets/DefaultQuery.cs b/test/Savvyio.Queries.Tests/Assets/DefaultQuery.cs index 6d9f1547..e10f026b 100644 --- a/test/Savvyio.Queries.Tests/Assets/DefaultQuery.cs +++ b/test/Savvyio.Queries.Tests/Assets/DefaultQuery.cs @@ -1,6 +1,5 @@ -namespace Savvyio.Queries.Assets +namespace Savvyio.Queries.Assets; + +internal record DefaultQuery : Query { - internal record DefaultQuery : Query - { - } } diff --git a/test/Savvyio.Queries.Tests/Assets/DefaultQueryHandler.cs b/test/Savvyio.Queries.Tests/Assets/DefaultQueryHandler.cs index c8e098c2..341a13ea 100644 --- a/test/Savvyio.Queries.Tests/Assets/DefaultQueryHandler.cs +++ b/test/Savvyio.Queries.Tests/Assets/DefaultQueryHandler.cs @@ -1,19 +1,18 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Savvyio.Handlers; -namespace Savvyio.Queries.Assets +namespace Savvyio.Queries.Assets; + +internal class DefaultQueryHandler : QueryHandler { - internal class DefaultQueryHandler : QueryHandler + public DefaultQueryHandler() { - public DefaultQueryHandler() - { - } + } - protected override void RegisterDelegates(IRequestReplyRegistry handlers) - { - handlers.Register, string>(_ => nameof(DefaultQuery)); - handlers.RegisterAsync, string>(_ => Task.FromResult(nameof(DefaultQuery) + "Async")); - } + protected override void RegisterDelegates(IRequestReplyRegistry handlers) + { + handlers.Register, string>(_ => nameof(DefaultQuery)); + handlers.RegisterAsync, string>(_ => Task.FromResult(nameof(DefaultQuery) + "Async")); } } diff --git a/test/Savvyio.Queries.Tests/QueryDispatcherTest.cs b/test/Savvyio.Queries.Tests/QueryDispatcherTest.cs index ad858222..6e9a4097 100644 --- a/test/Savvyio.Queries.Tests/QueryDispatcherTest.cs +++ b/test/Savvyio.Queries.Tests/QueryDispatcherTest.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Microsoft.Extensions.DependencyInjection; using Savvyio.Dispatchers; @@ -7,40 +7,39 @@ using Savvyio.Queries.Assets; using Xunit; -namespace Savvyio.Queries +namespace Savvyio.Queries; + +public class QueryDispatcherTest : Test { - public class QueryDispatcherTest : Test + public QueryDispatcherTest(ITestOutputHelper output) : base(output) { - public QueryDispatcherTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void QueryDispatcher_Ensure_Initialization_Defaults() - { - var sp = new ServiceCollection().AddServiceLocator().BuildServiceProvider(); - var sut = new QueryDispatcher(sp.GetRequiredService()); + [Fact] + public void QueryDispatcher_Ensure_Initialization_Defaults() + { + var sp = new ServiceCollection().AddServiceLocator().BuildServiceProvider(); + var sut = new QueryDispatcher(sp.GetRequiredService()); - Assert.Throws(() => sut.Query(new DefaultQuery())); - Assert.ThrowsAsync(() => sut.QueryAsync(new DefaultQuery())); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - } + Assert.Throws(() => sut.Query(new DefaultQuery())); + Assert.ThrowsAsync(() => sut.QueryAsync(new DefaultQuery())); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + } - [Fact] - public async Task QueryDispatcher_ShouldQuery_DefaultQuery() - { - var sp = new ServiceCollection().AddServiceLocator().AddTransient().AddSingleton, InMemoryTestStore>().BuildServiceProvider(); - var sut = new QueryDispatcher(sp.GetRequiredService()); - var ts = sp.GetRequiredService>(); + [Fact] + public async Task QueryDispatcher_ShouldQuery_DefaultQuery() + { + var sp = new ServiceCollection().AddServiceLocator().AddTransient().AddSingleton, InMemoryTestStore>().BuildServiceProvider(); + var sut = new QueryDispatcher(sp.GetRequiredService()); + var ts = sp.GetRequiredService>(); - var qr = sut.Query(new DefaultQuery()); - var aqr = await sut.QueryAsync(new DefaultQuery()); + var qr = sut.Query(new DefaultQuery()); + var aqr = await sut.QueryAsync(new DefaultQuery()); - Assert.Equal(qr, nameof(DefaultQuery)); - Assert.Equal(aqr, nameof(DefaultQuery) + "Async"); - } + Assert.Equal(qr, nameof(DefaultQuery)); + Assert.Equal(aqr, nameof(DefaultQuery) + "Async"); } } diff --git a/test/Savvyio.Queries.Tests/QueryHandlerTest.cs b/test/Savvyio.Queries.Tests/QueryHandlerTest.cs index dcb26350..a068d882 100644 --- a/test/Savvyio.Queries.Tests/QueryHandlerTest.cs +++ b/test/Savvyio.Queries.Tests/QueryHandlerTest.cs @@ -1,28 +1,27 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Codebelt.Extensions.Xunit; using Savvyio.Assets.Queries; using Savvyio.Queries.Assets; using Xunit; -namespace Savvyio.Queries +namespace Savvyio.Queries; + +public class QueryHandlerTest : Test { - public class QueryHandlerTest : Test + public QueryHandlerTest(ITestOutputHelper output) : base(output) { - public QueryHandlerTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public async Task QueryHandler_Ensure_Initialization_Defaults() - { - var sut = new DefaultQueryHandler(); + [Fact] + public async Task QueryHandler_Ensure_Initialization_Defaults() + { + var sut = new DefaultQueryHandler(); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.True(sut.Delegates.TryInvoke(new DefaultQuery(), out _)); - Assert.True((await sut.Delegates.TryInvokeAsync(new DefaultQuery())).Succeeded); - Assert.False(sut.Delegates.TryInvoke(new GetAccount(1), out _)); - Assert.False((await sut.Delegates.TryInvokeAsync(new GetAccount(1))).Succeeded); - } + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.True(sut.Delegates.TryInvoke(new DefaultQuery(), out _)); + Assert.True((await sut.Delegates.TryInvokeAsync(new DefaultQuery())).Succeeded); + Assert.False(sut.Delegates.TryInvoke(new GetAccount(1), out _)); + Assert.False((await sut.Delegates.TryInvokeAsync(new GetAccount(1))).Succeeded); } } diff --git a/test/Savvyio.Queries.Tests/QueryTest.cs b/test/Savvyio.Queries.Tests/QueryTest.cs index 1bf96cb2..ee3998a2 100644 --- a/test/Savvyio.Queries.Tests/QueryTest.cs +++ b/test/Savvyio.Queries.Tests/QueryTest.cs @@ -1,32 +1,31 @@ -using Cuemon.Extensions.Reflection; +using Cuemon.Extensions.Reflection; using Codebelt.Extensions.Xunit; using Savvyio.Queries.Assets; using Xunit; -namespace Savvyio.Queries +namespace Savvyio.Queries; + +public class QueryTest : Test { - public class QueryTest : Test + public QueryTest(ITestOutputHelper output) : base(output) { - public QueryTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void DefaultQuery_Ensure_Initialization_Defaults() - { - var sut = new DefaultQuery(); + [Fact] + public void DefaultQuery_Ensure_Initialization_Defaults() + { + var sut = new DefaultQuery(); - Assert.IsAssignableFrom>(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom>(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.IsAssignableFrom(sut); - Assert.Collection(sut.Metadata, pair => - { - Assert.Equal(MetadataDictionary.MemberType, pair.Key); - Assert.Equal(typeof(DefaultQuery).ToFullNameIncludingAssemblyName(), pair.Value); - }); - } + Assert.IsAssignableFrom>(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom>(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.IsAssignableFrom(sut); + Assert.Collection(sut.Metadata, pair => + { + Assert.Equal(MetadataDictionary.MemberType, pair.Key); + Assert.Equal(typeof(DefaultQuery).ToFullNameIncludingAssemblyName(), pair.Value); + }); } } diff --git a/test/Savvyio.Queries.Tests/Savvyio.Queries.Tests.csproj b/test/Savvyio.Queries.Tests/Savvyio.Queries.Tests.csproj index 5f1fdd55..d2cc5cf1 100644 --- a/test/Savvyio.Queries.Tests/Savvyio.Queries.Tests.csproj +++ b/test/Savvyio.Queries.Tests/Savvyio.Queries.Tests.csproj @@ -1,4 +1,4 @@ - + Savvyio.Queries diff --git a/test/Savvyio.Queries.Tests/SavvyioOptionsExtensionsTest.cs b/test/Savvyio.Queries.Tests/SavvyioOptionsExtensionsTest.cs index 2bafe65f..59620494 100644 --- a/test/Savvyio.Queries.Tests/SavvyioOptionsExtensionsTest.cs +++ b/test/Savvyio.Queries.Tests/SavvyioOptionsExtensionsTest.cs @@ -1,43 +1,42 @@ -using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit; using Savvyio.Assets; using Xunit; -namespace Savvyio.Queries +namespace Savvyio.Queries; + +public class SavvyioOptionsExtensionsTest : Test { - public class SavvyioOptionsExtensionsTest : Test + public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) { - public SavvyioOptionsExtensionsTest(ITestOutputHelper output) : base(output) - { - } + } - [Fact] - public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() - { - var sut = new SavvyioOptions().AddHandlers(); - Assert.Empty(sut.HandlerServiceTypes); - Assert.Empty(sut.HandlerImplementationTypes); - } + [Fact] + public void AddHandlers_ShouldNotAddAnyHandlersFromCurrentDomain() + { + var sut = new SavvyioOptions().AddHandlers(); + Assert.Empty(sut.HandlerServiceTypes); + Assert.Empty(sut.HandlerImplementationTypes); + } - [Fact] - public void AddQueryHandler_ShouldAddQueryHandler() - { - var sut = new SavvyioOptions().AddQueryHandler(); + [Fact] + public void AddQueryHandler_ShouldAddQueryHandler() + { + var sut = new SavvyioOptions().AddQueryHandler(); - Assert.NotEmpty(sut.HandlerServiceTypes); - Assert.NotEmpty(sut.HandlerImplementationTypes); - Assert.Collection(sut.HandlerServiceTypes, type => Assert.Equal(typeof(IQueryHandler), type)); - Assert.Collection(sut.HandlerImplementationTypes, type => Assert.Equal(typeof(AccountQueryHandler), type)); - } + Assert.NotEmpty(sut.HandlerServiceTypes); + Assert.NotEmpty(sut.HandlerImplementationTypes); + Assert.Collection(sut.HandlerServiceTypes, type => Assert.Equal(typeof(IQueryHandler), type)); + Assert.Collection(sut.HandlerImplementationTypes, type => Assert.Equal(typeof(AccountQueryHandler), type)); + } - [Fact] - public void AddQueryDispatcher_ShouldAddQueryDispatcher() - { - var sut = new SavvyioOptions().AddQueryDispatcher(); + [Fact] + public void AddQueryDispatcher_ShouldAddQueryDispatcher() + { + var sut = new SavvyioOptions().AddQueryDispatcher(); - Assert.NotEmpty(sut.DispatcherServiceTypes); - Assert.NotEmpty(sut.DispatcherImplementationTypes); - Assert.Collection(sut.DispatcherServiceTypes, type => Assert.Equal(typeof(IQueryDispatcher), type)); - Assert.Collection(sut.DispatcherImplementationTypes, type => Assert.Equal(typeof(QueryDispatcher), type)); - } + Assert.NotEmpty(sut.DispatcherServiceTypes); + Assert.NotEmpty(sut.DispatcherImplementationTypes); + Assert.Collection(sut.DispatcherServiceTypes, type => Assert.Equal(typeof(IQueryDispatcher), type)); + Assert.Collection(sut.DispatcherImplementationTypes, type => Assert.Equal(typeof(QueryDispatcher), type)); } } diff --git a/test/Shared/ConfiguredOptionsAssertions.cs b/test/Shared/ConfiguredOptionsAssertions.cs index a3a8f0e3..19b40c0f 100644 --- a/test/Shared/ConfiguredOptionsAssertions.cs +++ b/test/Shared/ConfiguredOptionsAssertions.cs @@ -17,10 +17,8 @@ internal static class ConfiguredOptionsAssertions var manual = new TOptions(); callback(manual); - Assert.Equivalent(direct, microsoft, strict: true); + Assert.Same(direct, microsoft); Assert.Equivalent(direct, manual, strict: true); - // Characterize the current separate eager instance before the Cuemon migration. - Assert.NotSame(direct, microsoft); using var scope = provider.CreateScope(); Assert.Same(direct, scope.ServiceProvider.GetRequiredService()); Assert.Same(callback, scope.ServiceProvider.GetRequiredService>()); diff --git a/testenvironments.json b/testenvironments.json index 66f3fcdb..7f57f177 100644 --- a/testenvironments.json +++ b/testenvironments.json @@ -4,12 +4,18 @@ { "name": "WSL-Ubuntu", "type": "wsl", - "wslDistribution": "Ubuntu-24.04" + "wslDistribution": "Ubuntu-26.04" }, { - "name": "Docker-Ubuntu", + "name": "Docker-Ubuntu (net10.0)", "type": "docker", - "dockerImage": "codebeltnet/ubuntu-testrunner:8-9-10-11", + "dockerImage": "codebeltnet/ubuntu-testrunner:10", + "dockerArgs": "--network savvyio-test -e AWS__CallerIdentity -e AWS__IAM__AccessKey -e AWS__IAM__SecretKey -e SAVVYIO_NATS_URL=nats://nats:4222 -e SAVVYIO_RABBITMQ_URL=amqp://rabbitmq:5672 -e SAVVYIO_RABBITMQ_MANAGEMENT_URL=http://rabbitmq:15672" + }, + { + "name": "Docker-Ubuntu (net11.0)", + "type": "docker", + "dockerImage": "codebeltnet/ubuntu-testrunner:11", "dockerArgs": "--network savvyio-test -e AWS__CallerIdentity -e AWS__IAM__AccessKey -e AWS__IAM__SecretKey -e SAVVYIO_NATS_URL=nats://nats:4222 -e SAVVYIO_RABBITMQ_URL=amqp://rabbitmq:5672 -e SAVVYIO_RABBITMQ_MANAGEMENT_URL=http://rabbitmq:15672" } ]