diff --git a/assets/microservices.drawio b/assets/microservices.drawio
new file mode 100644
index 0000000..5a0fae2
--- /dev/null
+++ b/assets/microservices.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/src/BuildingBlocks/BuildingBlocks.csproj b/src/BuildingBlocks/BuildingBlocks.csproj
index 6c4d765..542d6b7 100644
--- a/src/BuildingBlocks/BuildingBlocks.csproj
+++ b/src/BuildingBlocks/BuildingBlocks.csproj
@@ -25,6 +25,7 @@
+
diff --git a/src/BuildingBlocks/Core/Event/EventType.cs b/src/BuildingBlocks/Core/Event/EventType.cs
index 1ec808c..9c0de49 100644
--- a/src/BuildingBlocks/Core/Event/EventType.cs
+++ b/src/BuildingBlocks/Core/Event/EventType.cs
@@ -4,5 +4,5 @@ namespace BuildingBlocks.Core.Event;
public enum EventType
{
IntegrationEvent = 1,
- DomainEvent = 2,
+ DomainEvent = 2
}
diff --git a/src/BuildingBlocks/Core/Event/IDomainEvent.cs b/src/BuildingBlocks/Core/Event/IDomainEvent.cs
index 3f3afed..65bc80d 100644
--- a/src/BuildingBlocks/Core/Event/IDomainEvent.cs
+++ b/src/BuildingBlocks/Core/Event/IDomainEvent.cs
@@ -2,5 +2,4 @@ namespace BuildingBlocks.Core.Event;
public interface IDomainEvent : IEvent
{
-
}
diff --git a/src/BuildingBlocks/Core/EventDispatcher.cs b/src/BuildingBlocks/Core/EventDispatcher.cs
index fb23629..b41f3cb 100644
--- a/src/BuildingBlocks/Core/EventDispatcher.cs
+++ b/src/BuildingBlocks/Core/EventDispatcher.cs
@@ -30,36 +30,43 @@ public sealed class EventDispatcher : IEventDispatcher
_httpContextAccessor = httpContextAccessor;
}
- public async Task SendAsync(IDomainEvent domainEvent,
- CancellationToken cancellationToken = default) => await SendAsync(new[] {domainEvent}, cancellationToken);
- public async Task SendAsync(IReadOnlyList domainEvents, CancellationToken cancellationToken = default)
+ public async Task SendAsync(IReadOnlyList events, CancellationToken cancellationToken = default)
+ where T : IEvent
{
- if (domainEvents is null) return;
-
- var integrationEvents = await MapDomainEventToIntegrationEventAsync(domainEvents).ConfigureAwait(false);
-
- if (integrationEvents.Count == 0) return;
-
- foreach (var integrationEvent in integrationEvents)
+ async Task PublishIntegrationEvent(IReadOnlyList integrationEvents)
{
- await _persistMessageProcessor.PublishMessageAsync(new MessageEnvelope(integrationEvent, SetHeaders()),
- cancellationToken);
+ foreach (var integrationEvent in integrationEvents)
+ {
+ await _persistMessageProcessor.PublishMessageAsync(new MessageEnvelope(integrationEvent, SetHeaders()),
+ cancellationToken);
+ }
+ }
+
+ if (events.Count > 0)
+ {
+ switch (events)
+ {
+ case IReadOnlyList domainEvents:
+ {
+ var integrationEvents = await MapDomainEventToIntegrationEventAsync(domainEvents)
+ .ConfigureAwait(false);
+
+ await PublishIntegrationEvent(integrationEvents);
+ break;
+ }
+
+ case IReadOnlyList integrationEvents:
+ await PublishIntegrationEvent(integrationEvents);
+ break;
+ }
}
}
+ public async Task SendAsync(T @event, CancellationToken cancellationToken = default)
+ where T : IEvent =>
+ await SendAsync(new[] {@event}, cancellationToken);
- public async Task SendAsync(IIntegrationEvent integrationEvent,
- CancellationToken cancellationToken = default) => await SendAsync(new[] {integrationEvent}, cancellationToken);
-
- public async Task SendAsync(IReadOnlyList integrationEvents,
- CancellationToken cancellationToken = default)
- {
- if (integrationEvents is null) return;
-
- await _persistMessageProcessor.PublishMessageAsync(new MessageEnvelope(integrationEvents, SetHeaders()),
- cancellationToken);
- }
private Task> MapDomainEventToIntegrationEventAsync(
IReadOnlyList events)
diff --git a/src/BuildingBlocks/Core/IEventDispatcher.cs b/src/BuildingBlocks/Core/IEventDispatcher.cs
index 348fd10..44a4f94 100644
--- a/src/BuildingBlocks/Core/IEventDispatcher.cs
+++ b/src/BuildingBlocks/Core/IEventDispatcher.cs
@@ -4,9 +4,8 @@ namespace BuildingBlocks.Core;
public interface IEventDispatcher
{
- public Task SendAsync(IReadOnlyList domainEvents, CancellationToken cancellationToken = default);
- public Task SendAsync(IDomainEvent domainEvent, CancellationToken cancellationToken = default);
-
- public Task SendAsync(IIntegrationEvent integrationEvent, CancellationToken cancellationToken = default);
- public Task SendAsync(IReadOnlyList integrationEvents, CancellationToken cancellationToken = default);
+ public Task SendAsync(IReadOnlyList events, CancellationToken cancellationToken = default)
+ where T : IEvent;
+ public Task SendAsync(T @event, CancellationToken cancellationToken = default)
+ where T : IEvent;
}
diff --git a/src/BuildingBlocks/EFCore/EfTxBehavior.cs b/src/BuildingBlocks/EFCore/EfTxBehavior.cs
index 4a84a39..5226651 100644
--- a/src/BuildingBlocks/EFCore/EfTxBehavior.cs
+++ b/src/BuildingBlocks/EFCore/EfTxBehavior.cs
@@ -1,6 +1,7 @@
using System.Data;
using System.Text.Json;
using BuildingBlocks.Core;
+using BuildingBlocks.Core.Event;
using MediatR;
using Microsoft.Extensions.Logging;
diff --git a/src/BuildingBlocks/MessageProcessor/Extensions.cs b/src/BuildingBlocks/MessageProcessor/Extensions.cs
index d7329ae..f2c8fab 100644
--- a/src/BuildingBlocks/MessageProcessor/Extensions.cs
+++ b/src/BuildingBlocks/MessageProcessor/Extensions.cs
@@ -1,4 +1,5 @@
using BuildingBlocks.Core;
+using BuildingBlocks.Mongo;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
diff --git a/src/BuildingBlocks/Mongo/MongoDbContext.cs b/src/BuildingBlocks/Mongo/MongoDbContext.cs
index 1c59a41..4ef4e5b 100644
--- a/src/BuildingBlocks/Mongo/MongoDbContext.cs
+++ b/src/BuildingBlocks/Mongo/MongoDbContext.cs
@@ -1,3 +1,4 @@
+using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Driver;
@@ -13,12 +14,12 @@ public class MongoDbContext : IMongoDbContext
public IMongoClient MongoClient { get; }
protected readonly IList> _commands;
- public MongoDbContext(MongoOptions options)
+ public MongoDbContext(IOptions options)
{
RegisterConventions();
- MongoClient = new MongoClient(options.ConnectionString);
- var databaseName = options.DatabaseName;
+ MongoClient = new MongoClient(options.Value.ConnectionString);
+ var databaseName = options.Value.DatabaseName;
Database = MongoClient.GetDatabase(databaseName);
// Every command will be stored and it'll be processed at SaveChanges
diff --git a/src/Services/Booking/src/Booking/Data/BookingDbContext.cs b/src/Services/Booking/src/Booking/Data/BookingDbContext.cs
index 9a1c9b9..14b59b9 100644
--- a/src/Services/Booking/src/Booking/Data/BookingDbContext.cs
+++ b/src/Services/Booking/src/Booking/Data/BookingDbContext.cs
@@ -9,7 +9,7 @@ public class BookingDbContext : AppDbContextBase
{
public const string DefaultSchema = "dbo";
- public BookingDbContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options, httpContextAccessor)
+ public BookingDbContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options, httpContextAccessor)
{
}
diff --git a/src/Services/Booking/tests/IntegrationTest/Booking/Features/CreateBookingTests.cs b/src/Services/Booking/tests/IntegrationTest/Booking/Features/CreateBookingTests.cs
index 0c16d0b..89f3213 100644
--- a/src/Services/Booking/tests/IntegrationTest/Booking/Features/CreateBookingTests.cs
+++ b/src/Services/Booking/tests/IntegrationTest/Booking/Features/CreateBookingTests.cs
@@ -24,13 +24,6 @@ public class CreateBookingTests
public CreateBookingTests(IntegrationTestFixture fixture)
{
_fixture = fixture;
-
- _fixture.RegisterTestServices(services =>
- {
- MockFlightGrpcServices(services);
- MockPassengerGrpcServices(services);
- });
-
_testHarness = fixture.TestHarness;
_channel = fixture.Channel;
}
@@ -48,36 +41,4 @@ public class CreateBookingTests
// Assert
response.Should().BeGreaterOrEqualTo(0);
}
-
- private void MockPassengerGrpcServices(IServiceCollection services)
- {
- services.Replace(ServiceDescriptor.Singleton(x =>
- {
- var mock = Substitute.For();
- mock.GetById(Arg.Any())
- .Returns(new UnaryResult(new FakePassengerResponseDto().Generate()));
-
- return mock;
- }));
- }
-
- private void MockFlightGrpcServices(IServiceCollection services)
- {
- services.Replace(ServiceDescriptor.Singleton(x =>
- {
- var mock = Substitute.For();
-
- mock.GetById(Arg.Any())
- .Returns(new UnaryResult(Task.FromResult(new FakeFlightResponseDto().Generate())));
-
- mock.GetAvailableSeats(Arg.Any())
- .Returns(
- new UnaryResult>(Task.FromResult(FakeSeatsResponseDto.Generate())));
-
- mock.ReserveSeat(new FakeReserveSeatRequestDto().Generate())
- .Returns(new UnaryResult(Task.FromResult(FakeSeatsResponseDto.Generate().First())));
-
- return mock;
- }));
- }
}
diff --git a/src/Services/Booking/tests/IntegrationTest/IntegrationTestFixture.cs b/src/Services/Booking/tests/IntegrationTest/IntegrationTestFixture.cs
index b1e8679..0ee813c 100644
--- a/src/Services/Booking/tests/IntegrationTest/IntegrationTestFixture.cs
+++ b/src/Services/Booking/tests/IntegrationTest/IntegrationTestFixture.cs
@@ -1,11 +1,17 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Booking.Data;
+using BuildingBlocks.Contracts.Grpc;
using BuildingBlocks.Core.Model;
using BuildingBlocks.MassTransit;
+using BuildingBlocks.Mongo;
using BuildingBlocks.Web;
using Grpc.Net.Client;
+using Integration.Test.Fakes;
+using MagicOnion;
using MassTransit;
using MassTransit.Testing;
using MediatR;
@@ -16,6 +22,8 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Options;
+using Mongo2Go;
using NSubstitute;
using Respawn;
using Serilog;
@@ -32,10 +40,11 @@ public class FixtureCollection : ICollectionFixture
public class IntegrationTestFixture : IAsyncLifetime
{
private WebApplicationFactory _factory;
- public Checkpoint Checkpoint { get; set; }
- public Action? TestRegistrationServices { get; set; }
- public IServiceProvider ServiceProvider => _factory.Services;
- public IConfiguration Configuration => _factory.Services.GetRequiredService();
+ private Checkpoint _checkpoint;
+ private MongoDbRunner _mongoRunner;
+ private IServiceProvider _serviceProvider;
+ private Action? _testRegistrationServices;
+ private IConfiguration _configuration;
public HttpClient HttpClient => _factory.CreateClient();
public ITestHarness TestHarness => CreateHarness();
public GrpcChannel Channel => CreateChannel();
@@ -48,66 +57,78 @@ public class IntegrationTestFixture : IAsyncLifetime
builder.UseEnvironment("test");
builder.ConfigureServices(services =>
{
- services.ReplaceSingleton(AddHttpContextAccessorMock);
- TestRegistrationServices?.Invoke(services);
- services.AddMassTransitTestHarness(x =>
- {
- x.UsingRabbitMq((context, cfg) =>
- {
- var rabbitMqOptions = services.GetOptions("RabbitMq");
- var host = rabbitMqOptions.HostName;
-
- cfg.Host(host, h =>
- {
- h.Username(rabbitMqOptions.UserName);
- h.Password(rabbitMqOptions.Password);
- });
- cfg.ConfigureEndpoints(context);
- });
- });
-
- Checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
-
- TestRegistrationServices?.Invoke(services);
+ _testRegistrationServices?.Invoke(services);
});
});
+ RegisterServices(services =>
+ {
+ MockFlightGrpcServices(services);
+ MockPassengerGrpcServices(services);
+ services.ReplaceSingleton(AddHttpContextAccessorMock);
+ services.AddMassTransitTestHarness(x =>
+ {
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ var rabbitMqOptions = services.GetOptions("RabbitMq");
+ var host = rabbitMqOptions.HostName;
+
+ cfg.Host(host, h =>
+ {
+ h.Username(rabbitMqOptions.UserName);
+ h.Password(rabbitMqOptions.Password);
+ });
+ cfg.ConfigureEndpoints(context);
+ });
+ });
+ });
+
+ _serviceProvider = _factory.Services;
+ _configuration = _factory.Services.GetRequiredService();
+
+ _checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
+
+ _mongoRunner = MongoDbRunner.Start();
+ var mongoOptions = _factory.Services.GetRequiredService>();
+ if (mongoOptions.Value.ConnectionString != null)
+ mongoOptions.Value.ConnectionString = _mongoRunner.ConnectionString;
+
return Task.CompletedTask;
}
+ public void RegisterServices(Action services) => _testRegistrationServices = services;
+
public virtual async Task DisposeAsync()
{
- if (!string.IsNullOrEmpty(Configuration?.GetConnectionString("DefaultConnection")))
- await Checkpoint.Reset(Configuration?.GetConnectionString("DefaultConnection"));
+ if (!string.IsNullOrEmpty(_configuration?.GetConnectionString("DefaultConnection")))
+ await _checkpoint.Reset(_configuration?.GetConnectionString("DefaultConnection"));
await _factory.DisposeAsync();
+ _mongoRunner.Dispose();
}
// ref: https://github.com/trbenning/serilog-sinks-xunit
public ILogger CreateLogger(ITestOutputHelper output)
{
if (output != null)
+ {
return new LoggerConfiguration()
.WriteTo.TestOutput(output)
.CreateLogger();
+ }
return null;
}
- public void RegisterTestServices(Action services)
- {
- TestRegistrationServices = services;
- }
-
public async Task ExecuteScopeAsync(Func action)
{
- using var scope = ServiceProvider.CreateScope();
+ using var scope = _serviceProvider.CreateScope();
await action(scope.ServiceProvider);
}
public async Task ExecuteScopeAsync(Func> action)
{
- using var scope = ServiceProvider.CreateScope();
+ using var scope = _serviceProvider.CreateScope();
var result = await action(scope.ServiceProvider);
@@ -238,7 +259,7 @@ public class IntegrationTestFixture : IAsyncLifetime
private ITestHarness CreateHarness()
{
- var harness = ServiceProvider.GetTestHarness();
+ var harness = _serviceProvider.GetTestHarness();
harness.Start().GetAwaiter().GetResult();
return harness;
}
@@ -259,4 +280,36 @@ public class IntegrationTestFixture : IAsyncLifetime
return httpContextAccessorMock;
}
+
+ private void MockPassengerGrpcServices(IServiceCollection services)
+ {
+ services.Replace(ServiceDescriptor.Singleton(x =>
+ {
+ var mock = Substitute.For();
+ mock.GetById(Arg.Any())
+ .Returns(new UnaryResult(new FakePassengerResponseDto().Generate()));
+
+ return mock;
+ }));
+ }
+
+ private void MockFlightGrpcServices(IServiceCollection services)
+ {
+ services.Replace(ServiceDescriptor.Singleton(x =>
+ {
+ var mock = Substitute.For();
+
+ mock.GetById(Arg.Any())
+ .Returns(new UnaryResult(Task.FromResult(new FakeFlightResponseDto().Generate())));
+
+ mock.GetAvailableSeats(Arg.Any())
+ .Returns(
+ new UnaryResult>(Task.FromResult(FakeSeatsResponseDto.Generate())));
+
+ mock.ReserveSeat(new FakeReserveSeatRequestDto().Generate())
+ .Returns(new UnaryResult(Task.FromResult(FakeSeatsResponseDto.Generate().First())));
+
+ return mock;
+ }));
+ }
}
diff --git a/src/Services/Flight/src/Flight/Data/FlightReadDbContext.cs b/src/Services/Flight/src/Flight/Data/FlightReadDbContext.cs
index 951394d..1b18e7b 100644
--- a/src/Services/Flight/src/Flight/Data/FlightReadDbContext.cs
+++ b/src/Services/Flight/src/Flight/Data/FlightReadDbContext.cs
@@ -8,7 +8,7 @@ namespace Flight.Data;
public class FlightReadDbContext : MongoDbContext
{
- public FlightReadDbContext(IOptions options) : base(options.Value)
+ public FlightReadDbContext(IOptions options) : base(options)
{
Flight = GetCollection(nameof(Flight).Underscore());
}
diff --git a/src/Services/Flight/src/Flight/Flights/Features/CreateFlight/Reads/CreateFlightMongoCommandHandler.cs b/src/Services/Flight/src/Flight/Flights/Features/CreateFlight/Reads/CreateFlightMongoCommandHandler.cs
index aa92e58..e48549f 100644
--- a/src/Services/Flight/src/Flight/Flights/Features/CreateFlight/Reads/CreateFlightMongoCommandHandler.cs
+++ b/src/Services/Flight/src/Flight/Flights/Features/CreateFlight/Reads/CreateFlightMongoCommandHandler.cs
@@ -1,11 +1,15 @@
-using System.Threading;
+using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.CQRS;
using Flight.Data;
+using Flight.Flights.Exceptions;
using Flight.Flights.Models.Reads;
using MapsterMapper;
using MediatR;
+using MongoDB.Driver;
+using MongoDB.Driver.Linq;
namespace Flight.Flights.Features.CreateFlight.Reads;
@@ -28,6 +32,12 @@ public class CreateFlightMongoCommandHandler : ICommandHandler(command);
+ var flight = await _flightReadDbContext.Flight.AsQueryable()
+ .FirstOrDefaultAsync(x => x.Id == command.Id, cancellationToken);
+
+ if (flight is not null)
+ throw new FlightAlreadyExistException();
+
await _flightReadDbContext.Flight.InsertOneAsync(flightReadModel, cancellationToken: cancellationToken);
return Unit.Value;
diff --git a/src/Services/Flight/tests/IntegrationTest/IntegrationTestFixture.cs b/src/Services/Flight/tests/IntegrationTest/IntegrationTestFixture.cs
index 5010761..89ec2c1 100644
--- a/src/Services/Flight/tests/IntegrationTest/IntegrationTestFixture.cs
+++ b/src/Services/Flight/tests/IntegrationTest/IntegrationTestFixture.cs
@@ -2,12 +2,10 @@
using System.Net.Http;
using System.Threading.Tasks;
using BuildingBlocks.Core.Model;
-using BuildingBlocks.EFCore;
using BuildingBlocks.MassTransit;
-using BuildingBlocks.MessageProcessor;
+using BuildingBlocks.Mongo;
using BuildingBlocks.Web;
using Flight.Data;
-using FluentAssertions.Common;
using Grpc.Net.Client;
using MassTransit;
using MassTransit.Testing;
@@ -15,11 +13,10 @@ using MediatR;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing;
-using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Options;
+using Mongo2Go;
using NSubstitute;
using Respawn;
using Serilog;
@@ -35,15 +32,76 @@ public class FixtureCollection : ICollectionFixture
public class IntegrationTestFixture : IAsyncLifetime
{
+ private Checkpoint _checkpoint;
+ private IConfiguration _configuration;
private WebApplicationFactory _factory;
- public Checkpoint Checkpoint { get; set; }
- public Action? TestRegistrationServices { get; set; }
- public IServiceProvider ServiceProvider => _factory.Services;
- public IConfiguration Configuration => _factory.Services.GetRequiredService();
+ private MongoDbRunner _mongoRunner;
+ private IServiceProvider _serviceProvider;
+ private Action? _testRegistrationServices;
public HttpClient HttpClient => _factory.CreateClient();
public ITestHarness TestHarness => CreateHarness();
public GrpcChannel Channel => CreateChannel();
+
+ public virtual Task InitializeAsync()
+ {
+ _factory = new WebApplicationFactory()
+ .WithWebHostBuilder(builder =>
+ {
+ builder.UseEnvironment("test");
+ builder.ConfigureServices(services =>
+ {
+ _testRegistrationServices?.Invoke(services);
+ });
+ });
+
+ RegisterServices(services =>
+ {
+ services.ReplaceSingleton(AddHttpContextAccessorMock);
+ services.AddMassTransitTestHarness(x =>
+ {
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ var rabbitMqOptions = services.GetOptions("RabbitMq");
+ var host = rabbitMqOptions.HostName;
+
+ cfg.Host(host, h =>
+ {
+ h.Username(rabbitMqOptions.UserName);
+ h.Password(rabbitMqOptions.Password);
+ });
+ cfg.ConfigureEndpoints(context);
+ });
+ });
+ });
+
+ _serviceProvider = _factory.Services;
+ _configuration = _factory.Services.GetRequiredService();
+
+ _checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
+
+ _mongoRunner = MongoDbRunner.Start();
+ var mongoOptions = _factory.Services.GetRequiredService>();
+ if (mongoOptions.Value.ConnectionString != null)
+ mongoOptions.Value.ConnectionString = _mongoRunner.ConnectionString;
+
+ return Task.CompletedTask;
+ }
+
+ public virtual async Task DisposeAsync()
+ {
+ if (!string.IsNullOrEmpty(_configuration?.GetConnectionString("DefaultConnection")))
+ await _checkpoint.Reset(_configuration?.GetConnectionString("DefaultConnection"));
+
+ await _factory.DisposeAsync();
+ _mongoRunner.Dispose();
+ }
+
+ public void RegisterServices(Action services)
+ {
+ _testRegistrationServices = services;
+ }
+
// ref: https://github.com/trbenning/serilog-sinks-xunit
public ILogger CreateLogger(ITestOutputHelper output)
{
@@ -57,59 +115,15 @@ public class IntegrationTestFixture : IAsyncLifetime
return null;
}
- public void RegisterTestServices(Action services) => TestRegistrationServices = services;
-
- public virtual Task InitializeAsync()
- {
- _factory = new WebApplicationFactory()
- .WithWebHostBuilder(builder =>
- {
- builder.UseEnvironment("test");
- builder.ConfigureServices(services =>
- {
- TestRegistrationServices?.Invoke(services);
- services.AddMassTransitTestHarness(x =>
- {
- x.UsingRabbitMq((context, cfg) =>
- {
- var rabbitMqOptions = services.GetOptions("RabbitMq");
- var host = rabbitMqOptions.HostName;
-
- cfg.Host(host, h =>
- {
- h.Username(rabbitMqOptions.UserName);
- h.Password(rabbitMqOptions.Password);
- });
- cfg.ConfigureEndpoints(context);
- });
- });
-
- Checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
-
- TestRegistrationServices?.Invoke(services);
- });
- });
-
- return Task.CompletedTask;
- }
-
- public virtual async Task DisposeAsync()
- {
- if (!string.IsNullOrEmpty(Configuration?.GetConnectionString("DefaultConnection")))
- await Checkpoint.Reset(Configuration?.GetConnectionString("DefaultConnection"));
-
- await _factory.DisposeAsync();
- }
-
public async Task ExecuteScopeAsync(Func action)
{
- using var scope = ServiceProvider.CreateScope();
+ using var scope = _serviceProvider.CreateScope();
await action(scope.ServiceProvider);
}
public async Task ExecuteScopeAsync(Func> action)
{
- using var scope = ServiceProvider.CreateScope();
+ using var scope = _serviceProvider.CreateScope();
var result = await action(scope.ServiceProvider);
@@ -240,7 +254,7 @@ public class IntegrationTestFixture : IAsyncLifetime
private ITestHarness CreateHarness()
{
- var harness = ServiceProvider.GetTestHarness();
+ var harness = _serviceProvider.GetTestHarness();
harness.Start().GetAwaiter().GetResult();
return harness;
}
diff --git a/src/Services/Identity/tests/IntegrationTest/IntegrationTestFixture.cs b/src/Services/Identity/tests/IntegrationTest/IntegrationTestFixture.cs
index da891f6..cbce2d3 100644
--- a/src/Services/Identity/tests/IntegrationTest/IntegrationTestFixture.cs
+++ b/src/Services/Identity/tests/IntegrationTest/IntegrationTestFixture.cs
@@ -3,6 +3,7 @@ using System.Net.Http;
using System.Threading.Tasks;
using BuildingBlocks.Core.Model;
using BuildingBlocks.MassTransit;
+using BuildingBlocks.Mongo;
using BuildingBlocks.Web;
using Grpc.Net.Client;
using Identity.Data;
@@ -14,8 +15,8 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Options;
+using Mongo2Go;
using NSubstitute;
using Respawn;
using Serilog;
@@ -31,15 +32,17 @@ public class FixtureCollection : ICollectionFixture
public class IntegrationTestFixture : IAsyncLifetime
{
+ private Checkpoint _checkpoint;
+ private IConfiguration _configuration;
private WebApplicationFactory _factory;
- public Checkpoint Checkpoint { get; set; }
- public Action? TestRegistrationServices { get; set; }
- public IServiceProvider ServiceProvider => _factory.Services;
- public IConfiguration Configuration => _factory.Services.GetRequiredService();
+ private MongoDbRunner _mongoRunner;
+ private IServiceProvider _serviceProvider;
+ private Action? _testRegistrationServices;
public HttpClient HttpClient => _factory.CreateClient();
public ITestHarness TestHarness => CreateHarness();
public GrpcChannel Channel => CreateChannel();
+
public virtual Task InitializeAsync()
{
_factory = new WebApplicationFactory()
@@ -48,66 +51,79 @@ public class IntegrationTestFixture : IAsyncLifetime
builder.UseEnvironment("test");
builder.ConfigureServices(services =>
{
- services.ReplaceSingleton(AddHttpContextAccessorMock);
- TestRegistrationServices?.Invoke(services);
- services.AddMassTransitTestHarness(x =>
- {
- x.UsingRabbitMq((context, cfg) =>
- {
- var rabbitMqOptions = services.GetOptions("RabbitMq");
- var host = rabbitMqOptions.HostName;
-
- cfg.Host(host, h =>
- {
- h.Username(rabbitMqOptions.UserName);
- h.Password(rabbitMqOptions.Password);
- });
- cfg.ConfigureEndpoints(context);
- });
- });
-
- Checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
-
- TestRegistrationServices?.Invoke(services);
+ _testRegistrationServices?.Invoke(services);
});
});
+ RegisterServices(services =>
+ {
+ services.ReplaceSingleton(AddHttpContextAccessorMock);
+ services.AddMassTransitTestHarness(x =>
+ {
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ var rabbitMqOptions = services.GetOptions("RabbitMq");
+ var host = rabbitMqOptions.HostName;
+
+ cfg.Host(host, h =>
+ {
+ h.Username(rabbitMqOptions.UserName);
+ h.Password(rabbitMqOptions.Password);
+ });
+ cfg.ConfigureEndpoints(context);
+ });
+ });
+ });
+
+ _serviceProvider = _factory.Services;
+ _configuration = _factory.Services.GetRequiredService();
+
+ _checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
+
+ _mongoRunner = MongoDbRunner.Start();
+ var mongoOptions = _factory.Services.GetRequiredService>();
+ if (mongoOptions.Value.ConnectionString != null)
+ mongoOptions.Value.ConnectionString = _mongoRunner.ConnectionString;
+
return Task.CompletedTask;
}
public virtual async Task DisposeAsync()
{
- if (!string.IsNullOrEmpty(Configuration?.GetConnectionString("DefaultConnection")))
- await Checkpoint.Reset(Configuration?.GetConnectionString("DefaultConnection"));
+ if (!string.IsNullOrEmpty(_configuration?.GetConnectionString("DefaultConnection")))
+ await _checkpoint.Reset(_configuration?.GetConnectionString("DefaultConnection"));
await _factory.DisposeAsync();
+ _mongoRunner.Dispose();
+ }
+
+ public void RegisterServices(Action services)
+ {
+ _testRegistrationServices = services;
}
// ref: https://github.com/trbenning/serilog-sinks-xunit
public ILogger CreateLogger(ITestOutputHelper output)
{
if (output != null)
+ {
return new LoggerConfiguration()
.WriteTo.TestOutput(output)
.CreateLogger();
+ }
return null;
}
- public void RegisterTestServices(Action services)
- {
- TestRegistrationServices = services;
- }
-
public async Task ExecuteScopeAsync(Func action)
{
- using var scope = ServiceProvider.CreateScope();
+ using var scope = _serviceProvider.CreateScope();
await action(scope.ServiceProvider);
}
public async Task ExecuteScopeAsync(Func> action)
{
- using var scope = ServiceProvider.CreateScope();
+ using var scope = _serviceProvider.CreateScope();
var result = await action(scope.ServiceProvider);
@@ -238,7 +254,7 @@ public class IntegrationTestFixture : IAsyncLifetime
private ITestHarness CreateHarness()
{
- var harness = ServiceProvider.GetTestHarness();
+ var harness = _serviceProvider.GetTestHarness();
harness.Start().GetAwaiter().GetResult();
return harness;
}
diff --git a/src/Services/Passenger/src/Passenger/Data/PassengerDbContext.cs b/src/Services/Passenger/src/Passenger/Data/PassengerDbContext.cs
index 007f2c9..f2c48ea 100644
--- a/src/Services/Passenger/src/Passenger/Data/PassengerDbContext.cs
+++ b/src/Services/Passenger/src/Passenger/Data/PassengerDbContext.cs
@@ -9,7 +9,7 @@ public sealed class PassengerDbContext : AppDbContextBase
{
public const string DefaultSchema = "dbo";
- public PassengerDbContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options, httpContextAccessor)
+ public PassengerDbContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options, httpContextAccessor)
{
}
diff --git a/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs b/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs
index 11ef517..3e00083 100644
--- a/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs
+++ b/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs
@@ -3,6 +3,7 @@ using System.Net.Http;
using System.Threading.Tasks;
using BuildingBlocks.Core.Model;
using BuildingBlocks.MassTransit;
+using BuildingBlocks.Mongo;
using BuildingBlocks.Web;
using Grpc.Net.Client;
using MassTransit;
@@ -13,8 +14,8 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Options;
+using Mongo2Go;
using NSubstitute;
using Passenger.Data;
using Respawn;
@@ -31,15 +32,17 @@ public class FixtureCollection : ICollectionFixture
public class IntegrationTestFixture : IAsyncLifetime
{
+ private Checkpoint _checkpoint;
+ private IConfiguration _configuration;
private WebApplicationFactory _factory;
- public Checkpoint Checkpoint { get; set; }
- public Action? TestRegistrationServices { get; set; }
- public IServiceProvider ServiceProvider => _factory.Services;
- public IConfiguration Configuration => _factory.Services.GetRequiredService();
+ private MongoDbRunner _mongoRunner;
+ private IServiceProvider _serviceProvider;
+ private Action? _testRegistrationServices;
public HttpClient HttpClient => _factory.CreateClient();
public ITestHarness TestHarness => CreateHarness();
public GrpcChannel Channel => CreateChannel();
+
public virtual Task InitializeAsync()
{
_factory = new WebApplicationFactory()
@@ -48,66 +51,79 @@ public class IntegrationTestFixture : IAsyncLifetime
builder.UseEnvironment("test");
builder.ConfigureServices(services =>
{
- services.ReplaceSingleton(AddHttpContextAccessorMock);
- TestRegistrationServices?.Invoke(services);
- services.AddMassTransitTestHarness(x =>
- {
- x.UsingRabbitMq((context, cfg) =>
- {
- var rabbitMqOptions = services.GetOptions("RabbitMq");
- var host = rabbitMqOptions.HostName;
-
- cfg.Host(host, h =>
- {
- h.Username(rabbitMqOptions.UserName);
- h.Password(rabbitMqOptions.Password);
- });
- cfg.ConfigureEndpoints(context);
- });
- });
-
- Checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
-
- TestRegistrationServices?.Invoke(services);
+ _testRegistrationServices?.Invoke(services);
});
});
+ RegisterServices(services =>
+ {
+ services.ReplaceSingleton(AddHttpContextAccessorMock);
+ services.AddMassTransitTestHarness(x =>
+ {
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ var rabbitMqOptions = services.GetOptions("RabbitMq");
+ var host = rabbitMqOptions.HostName;
+
+ cfg.Host(host, h =>
+ {
+ h.Username(rabbitMqOptions.UserName);
+ h.Password(rabbitMqOptions.Password);
+ });
+ cfg.ConfigureEndpoints(context);
+ });
+ });
+ });
+
+ _serviceProvider = _factory.Services;
+ _configuration = _factory.Services.GetRequiredService();
+
+ _checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
+
+ _mongoRunner = MongoDbRunner.Start();
+ var mongoOptions = _factory.Services.GetRequiredService>();
+ if (mongoOptions.Value.ConnectionString != null)
+ mongoOptions.Value.ConnectionString = _mongoRunner.ConnectionString;
+
return Task.CompletedTask;
}
public virtual async Task DisposeAsync()
{
- if (!string.IsNullOrEmpty(Configuration?.GetConnectionString("DefaultConnection")))
- await Checkpoint.Reset(Configuration?.GetConnectionString("DefaultConnection"));
+ if (!string.IsNullOrEmpty(_configuration?.GetConnectionString("DefaultConnection")))
+ await _checkpoint.Reset(_configuration?.GetConnectionString("DefaultConnection"));
await _factory.DisposeAsync();
+ _mongoRunner.Dispose();
+ }
+
+ public void RegisterServices(Action services)
+ {
+ _testRegistrationServices = services;
}
// ref: https://github.com/trbenning/serilog-sinks-xunit
public ILogger CreateLogger(ITestOutputHelper output)
{
if (output != null)
+ {
return new LoggerConfiguration()
.WriteTo.TestOutput(output)
.CreateLogger();
+ }
return null;
}
- public void RegisterTestServices(Action services)
- {
- TestRegistrationServices = services;
- }
-
public async Task ExecuteScopeAsync(Func action)
{
- using var scope = ServiceProvider.CreateScope();
+ using var scope = _serviceProvider.CreateScope();
await action(scope.ServiceProvider);
}
public async Task ExecuteScopeAsync(Func> action)
{
- using var scope = ServiceProvider.CreateScope();
+ using var scope = _serviceProvider.CreateScope();
var result = await action(scope.ServiceProvider);
@@ -238,7 +254,7 @@ public class IntegrationTestFixture : IAsyncLifetime
private ITestHarness CreateHarness()
{
- var harness = ServiceProvider.GetTestHarness();
+ var harness = _serviceProvider.GetTestHarness();
harness.Start().GetAwaiter().GetResult();
return harness;
}