Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace EvolutionaryArchitecture.Fitnet.IntegrationTests.Common.Events.EventBus.InMemory;

using Fitnet.Common.Events;
using Fitnet.Common.Events.EventBus;

internal sealed class NotificationDecorator<TNotification>(IEventBus eventBus, INotificationHandler<TNotification>? innerHandler)
: INotificationHandler<TNotification>
where TNotification : INotification
{
public async Task Handle(TNotification notification, CancellationToken cancellationToken)
{
var @event = notification as IIntegrationEvent;
await eventBus.PublishAsync(@event!, cancellationToken);

if (innerHandler is not null)
{
await innerHandler.Handle(notification, cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
namespace EvolutionaryArchitecture.Fitnet.IntegrationTests.Common.TestEngine.Configuration;

using System.Reflection;
using Events.EventBus.InMemory;
using EvolutionaryArchitecture.Fitnet.Common.Events.EventBus;
using EvolutionaryArchitecture.Fitnet.Common.Events.EventBus.InMemory;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Time.Testing;

internal static class ConfigurationExtensions
{
Expand Down Expand Up @@ -41,10 +39,73 @@ internal static WebApplicationFactory<T> SetFakeSystemClock<T>(this WebApplicati
internal static WebApplicationFactory<T> WithFakeEventBus<T>(this WebApplicationFactory<T> webApplicationFactory, IEventBus eventBusMock)
where T : class =>
webApplicationFactory.WithWebHostBuilder(webHostBuilder => webHostBuilder.ConfigureTestServices(services =>
services.AddSingleton(eventBusMock)));
{
AddNotificationDecorator(eventBusMock, services, true);
}));

internal static WebApplicationFactory<T> WithoutEventHandlers<T>(this WebApplicationFactory<T> webApplicationFactory, IEventBus eventBusMock)
where T : class =>
webApplicationFactory.WithWebHostBuilder(webHostBuilder => webHostBuilder.ConfigureTestServices(services =>
{
AddNotificationDecorator(eventBusMock, services, false);
}));

internal static WebApplicationFactory<T> WithFakeConsumers<T>(this WebApplicationFactory<T> webApplicationFactory)
where T : class =>
webApplicationFactory.WithWebHostBuilder(webHostBuilder => webHostBuilder.ConfigureTestServices(services =>
services.AddInMemoryEventBus(Assembly.GetExecutingAssembly())));
{
services.AddInMemoryEventBus(Assembly.GetExecutingAssembly());
var decorator = services.FirstOrDefault(s => s.ImplementationType == typeof(NotificationDecorator<>));
if (decorator is not null)
{
services.Remove(decorator);
}
}));

private static void AddNotificationDecorator(IEventBus eventBusMock, IServiceCollection services, bool hasSingleEventHandling)
{
var notificationHandlerServices = services.Where(s =>
s.ServiceType.IsGenericType &&
s.ServiceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>))
.ToList();

foreach (var notificationHandlerService in notificationHandlerServices)
{
var serviceType = notificationHandlerService.ServiceType;
var implementationType = notificationHandlerService.ImplementationType;

services.Remove(notificationHandlerService);
services.AddTransient(implementationType!);

services.AddTransient(serviceType, serviceProvider =>
{
var notificationHandler = hasSingleEventHandling
? NotificationHandlerWithFakeEventBus(eventBusMock, implementationType, serviceProvider)
: null;

var decoratorType = typeof(NotificationDecorator<>).MakeGenericType(serviceType.GenericTypeArguments[0]);
return Activator.CreateInstance(decoratorType, eventBusMock, notificationHandler)!;
});
}
}

private static object NotificationHandlerWithFakeEventBus(IEventBus eventBusMock, Type? implementationType,
IServiceProvider serviceProvider)
{
var constructor = implementationType!.GetConstructors().FirstOrDefault()!;
var parameters = constructor.GetParameters();
var handlerDependencies = new object[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
if (parameters[i].ParameterType == typeof(IEventBus))
{
handlerDependencies[i] = eventBusMock;
continue;
}
handlerDependencies[i] = serviceProvider.GetService(parameters[i]!.ParameterType)!;
}

var notificationHandler = Activator.CreateInstance(implementationType, handlerDependencies)!;
return notificationHandler;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace EvolutionaryArchitecture.Fitnet.IntegrationTests.Common.TestEngine.IntegrationEvents.Handlers;

using EvolutionaryArchitecture.Fitnet.Common.Events;
using MediatR;

internal sealed class IntegrationEventHandlerScope<TIntegrationEvent> : IDisposable
where TIntegrationEvent : IIntegrationEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ namespace EvolutionaryArchitecture.Fitnet.IntegrationTests.Contracts.PrepareCont
using EvolutionaryArchitecture.Fitnet.Contracts.PrepareContract;
using Common.TestEngine;
using Common.TestEngine.Configuration;
using Microsoft.AspNetCore.Mvc;

public sealed class PrepareContractTests(
WebApplicationFactory<Program> applicationInMemoryFactory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace EvolutionaryArchitecture.Fitnet.IntegrationTests.Contracts.SignContrac
using Common.TestEngine.Configuration;
using Fitnet.Contracts.SignContract.Events;
using EvolutionaryArchitecture.Fitnet.Common.Events.EventBus;
using Microsoft.AspNetCore.Mvc;

public sealed class SignContractTests : IClassFixture<WebApplicationFactory<Program>>, IClassFixture<DatabaseContainer>
{
Expand All @@ -17,7 +16,7 @@ public sealed class SignContractTests : IClassFixture<WebApplicationFactory<Prog
public SignContractTests(WebApplicationFactory<Program> applicationInMemoryFactory,
DatabaseContainer database) =>
_applicationHttpClient = applicationInMemoryFactory
.WithFakeEventBus(_fakeEventBus)
.WithoutEventHandlers(_fakeEventBus)
.WithContainerDatabaseConfigured(database.ConnectionString!)
.CreateClient();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
global using System.Collections;
global using System.Net;
global using FluentAssertions;
global using Xunit;
global using MediatR;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.AspNetCore.Mvc.Testing;
global using Microsoft.AspNetCore.TestHost;
global using Microsoft.Extensions.Time.Testing;
global using Bogus;
global using JetBrains.Annotations;
global using NSubstitute;
global using EvolutionaryArchitecture.Fitnet.IntegrationTests.Common.TestEngine;
global using EvolutionaryArchitecture.Fitnet.IntegrationTests.Common.TestEngine;
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
namespace EvolutionaryArchitecture.Fitnet.IntegrationTests.Offers.Prepare;

using Common.TestEngine.Configuration;
using Common.TestEngine.IntegrationEvents.Handlers;
using Fitnet.Offers.Prepare;
using Fitnet.Passes.MarkPassAsExpired.Events;
using EvolutionaryArchitecture.Fitnet.Common.Events.EventBus;


public sealed class PrepareOfferTests : IClassFixture<WebApplicationFactory<Program>>,
IClassFixture<DatabaseContainer>
{
Expand All @@ -27,18 +24,16 @@ public PrepareOfferTests(WebApplicationFactory<Program> applicationInMemoryFacto
internal async Task Given_pass_expired_event_published_Then_new_offer_should_be_prepared()
{
// Arrange
using var integrationEventHandlerScope =
new IntegrationEventHandlerScope<PassExpiredEvent>(_applicationInMemory);
var integrationEventHandler = integrationEventHandlerScope.IntegrationEventHandler;
using var serviceScope = _applicationInMemory.Services.CreateScope();
var eventBus = serviceScope.ServiceProvider.GetRequiredService<IEventBus>();
var @event = PassExpiredEventFaker.CreateValid();

// Act
await integrationEventHandler.Handle(@event, CancellationToken.None);
await eventBus.PublishAsync(@event);

// Assert
EnsureThatOfferPreparedEventWasPublished();
}

private void EnsureThatOfferPreparedEventWasPublished() => _fakeEventBus.Received(1)
.PublishAsync(Arg.Any<OfferPrepareEvent>(), Arg.Any<CancellationToken>());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ namespace EvolutionaryArchitecture.Fitnet.IntegrationTests.Passes.MarkPassAsExpi
using Fitnet.Passes;
using RegisterPass;
using Common.TestEngine.Configuration;
using Common.TestEngine.IntegrationEvents.Handlers;
using EvolutionaryArchitecture.Fitnet.Common.Events.EventBus;
using Fitnet.Contracts.SignContract.Events;
using Fitnet.Passes.GetAllPasses;
Expand Down Expand Up @@ -74,9 +73,9 @@ internal async Task Given_mark_pass_as_expired_request_with_not_existing_id_Then
private async Task<ContractSignedEvent> RegisterPass()
{
var @event = ContractSignedEventFaker.Create();
using var integrationEventHandlerScope =
new IntegrationEventHandlerScope<ContractSignedEvent>(_applicationInMemoryFactory);
await integrationEventHandlerScope.Consume(@event);
using var serviceScope = _applicationInMemoryFactory.Services.CreateScope();
var eventBus = serviceScope.ServiceProvider.GetRequiredService<IEventBus>();
await eventBus.PublishAsync(@event);

return @event;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
namespace EvolutionaryArchitecture.Fitnet.IntegrationTests.Passes.RegisterPass;

using Common.TestEngine.Configuration;
using Common.TestEngine.IntegrationEvents.Handlers;
using Fitnet.Contracts.SignContract.Events;
using Fitnet.Passes.RegisterPass.Events;
using EvolutionaryArchitecture.Fitnet.Common.Events.EventBus;

Expand All @@ -25,12 +23,12 @@ public RegisterPassTests(WebApplicationFactory<Program> applicationInMemoryFacto
internal async Task Given_contract_signed_event_Then_should_register_pass()
{
// Arrange
using var integrationEventHandlerScope =
new IntegrationEventHandlerScope<ContractSignedEvent>(_applicationInMemory);
using var serviceScope = _applicationInMemory.Services.CreateScope();
var eventBus = serviceScope.ServiceProvider.GetRequiredService<IEventBus>();
var @event = ContractSignedEventFaker.Create();

// Act
await integrationEventHandlerScope.Consume(@event);
await eventBus.PublishAsync(@event);

// Assert
EnsureThatPassRegisteredEventWasPublished();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
namespace EvolutionaryArchitecture.Fitnet.IntegrationTests.Reports.GenerateNewPassesPerMonthReport;

using Common.TestEngine.Configuration;
using Common.TestEngine.IntegrationEvents.Handlers;
using Fitnet.Contracts.SignContract.Events;
using Fitnet.Common.Events.EventBus;
using Fitnet.Reports;
using Fitnet.Reports.GenerateNewPassesRegistrationsPerMonthReport.Dtos;
using Passes.RegisterPass;
Expand Down Expand Up @@ -51,10 +50,11 @@ private async Task RegisterPasses(List<PassRegistrationDateRange> reportTestData

private async Task RegisterPass(DateTimeOffset from, DateTimeOffset to)
{
using var integrationEventHandlerScope =
new IntegrationEventHandlerScope<ContractSignedEvent>(_applicationInMemoryFactory);
var integrationEventHandler = integrationEventHandlerScope.IntegrationEventHandler;
using var serviceScope = _applicationInMemoryFactory.Services.CreateScope();
var eventBus = serviceScope.ServiceProvider.GetRequiredService<IEventBus>();
var @event = ContractSignedEventFaker.Create(from, to);
await integrationEventHandler.Handle(@event, CancellationToken.None);

// Act
await eventBus.PublishAsync(@event);
}
}