- use test-container for integration-test

- improvement test base
This commit is contained in:
meysamhadeli 2022-12-09 17:12:13 +03:30
parent 27c63a4efa
commit cfab34e3ae
75 changed files with 651 additions and 624 deletions

View File

@ -251,7 +251,7 @@ services:
# Mongo # Mongo
####################################################### #######################################################
mongo: mongo:
image: mongo image: mongo:4
container_name: mongo container_name: mongo
restart: unless-stopped restart: unless-stopped
# environment: # environment:

View File

@ -91,7 +91,7 @@ services:
# Mongo # Mongo
####################################################### #######################################################
mongo: mongo:
image: mongo image: mongo:4
container_name: mongo container_name: mongo
restart: unless-stopped restart: unless-stopped
# environment: # environment:

View File

@ -20,6 +20,7 @@
<PackageReference Include="Ben.BlockingDetector" Version="0.0.4" /> <PackageReference Include="Ben.BlockingDetector" Version="0.0.4" />
<PackageReference Include="EasyCaching.Core" Version="1.7.0" /> <PackageReference Include="EasyCaching.Core" Version="1.7.0" />
<PackageReference Include="EasyCaching.InMemory" Version="1.7.0" /> <PackageReference Include="EasyCaching.InMemory" Version="1.7.0" />
<PackageReference Include="EasyNetQ.Management.Client" Version="1.4.2" />
<PackageReference Include="EFCore.NamingConventions" Version="7.0.0" /> <PackageReference Include="EFCore.NamingConventions" Version="7.0.0" />
<PackageReference Include="EntityFrameworkCore.Triggered" Version="3.2.1" /> <PackageReference Include="EntityFrameworkCore.Triggered" Version="3.2.1" />
<PackageReference Include="Figgle" Version="0.4.1" /> <PackageReference Include="Figgle" Version="0.4.1" />
@ -149,6 +150,7 @@
<ItemGroup> <ItemGroup>
<Folder Include="Contracts" /> <Folder Include="Contracts" />
<Folder Include="EventStoreDB\BackgroundWorkers" /> <Folder Include="EventStoreDB\BackgroundWorkers" />
<Folder Include="PersistMessageProcessor\Data\Migrations" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -2,8 +2,8 @@ using System.Collections.Immutable;
using System.Data; using System.Data;
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.Core.Model; using BuildingBlocks.Core.Model;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Utils; using BuildingBlocks.Utils;
using BuildingBlocks.Web;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
@ -71,7 +71,7 @@ public abstract class AppDbContextBase : DbContext, IDbContext
OnBeforeSaving(); OnBeforeSaving();
return base.SaveChangesAsync(cancellationToken); return base.SaveChangesAsync(cancellationToken);
} }
public IReadOnlyList<IDomainEvent> GetDomainEvents() public IReadOnlyList<IDomainEvent> GetDomainEvents()
{ {
var domainEntities = ChangeTracker var domainEntities = ChangeTracker
@ -119,6 +119,7 @@ public abstract class AppDbContextBase : DbContext, IDbContext
entry.Entity.LastModifiedBy = userId; entry.Entity.LastModifiedBy = userId;
entry.Entity.LastModified = DateTime.Now; entry.Entity.LastModified = DateTime.Now;
entry.Entity.IsDeleted = true; entry.Entity.IsDeleted = true;
entry.Entity.Version++;
break; break;
} }
} }

View File

@ -1,12 +1,11 @@
using System.Linq.Expressions; using System.Linq.Expressions;
using BuildingBlocks.Core.Model; using BuildingBlocks.Core.Model;
using BuildingBlocks.PersistMessageProcessor; using BuildingBlocks.PersistMessageProcessor.Data;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@ -15,12 +14,12 @@ namespace BuildingBlocks.EFCore;
public static class Extensions public static class Extensions
{ {
public static IServiceCollection AddCustomDbContext<TContext>( public static IServiceCollection AddCustomDbContext<TContext>(
this IServiceCollection services, this IServiceCollection services)
IConfiguration configuration)
where TContext : DbContext, IDbContext where TContext : DbContext, IDbContext
{ {
services.AddOptions<DatabaseOptions>() services.AddOptions<DatabaseOptions>()
.Bind(configuration.GetSection(nameof(DatabaseOptions))) .BindConfiguration(nameof(DatabaseOptions))
.ValidateDataAnnotations(); .ValidateDataAnnotations();
services.AddDbContext<TContext>((sp, options) => services.AddDbContext<TContext>((sp, options) =>
@ -28,7 +27,10 @@ public static class Extensions
var databaseOptions = services.GetOptions<DatabaseOptions>(nameof(DatabaseOptions)); var databaseOptions = services.GetOptions<DatabaseOptions>(nameof(DatabaseOptions));
options.UseSqlServer(databaseOptions?.DefaultConnection, options.UseSqlServer(databaseOptions?.DefaultConnection,
dbOptions => dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name)); dbOptions =>
{
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
});
}); });
services.AddScoped<IDbContext>(provider => provider.GetService<TContext>()); services.AddScoped<IDbContext>(provider => provider.GetService<TContext>());
@ -71,8 +73,10 @@ public static class Extensions
{ {
using var scope = serviceProvider.CreateScope(); using var scope = serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<TContext>(); var persistMessageContext = scope.ServiceProvider.GetRequiredService<PersistMessageDbContext>();
await persistMessageContext.Database.MigrateAsync();
var context = scope.ServiceProvider.GetRequiredService<TContext>();
await context.Database.MigrateAsync(); await context.Database.MigrateAsync();
} }

View File

@ -1,5 +1,4 @@
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.PersistMessageProcessor;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace BuildingBlocks.EFCore; namespace BuildingBlocks.EFCore;
@ -7,8 +6,6 @@ namespace BuildingBlocks.EFCore;
public interface IDbContext public interface IDbContext
{ {
DbSet<TEntity> Set<TEntity>() where TEntity : class; DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbSet<PersistMessage> PersistMessages => Set<PersistMessage>();
IReadOnlyList<IDomainEvent> GetDomainEvents(); IReadOnlyList<IDomainEvent> GetDomainEvents();
Task BeginTransactionAsync(CancellationToken cancellationToken = default); Task BeginTransactionAsync(CancellationToken cancellationToken = default);
Task CommitTransactionAsync(CancellationToken cancellationToken = default); Task CommitTransactionAsync(CancellationToken cancellationToken = default);

View File

@ -21,6 +21,10 @@ public static class Extensions
public static IServiceCollection AddCustomMassTransit(this IServiceCollection services, Assembly assembly, public static IServiceCollection AddCustomMassTransit(this IServiceCollection services, Assembly assembly,
IWebHostEnvironment env) IWebHostEnvironment env)
{ {
services.AddOptions<RabbitMqOptions>()
.BindConfiguration(nameof(RabbitMqOptions))
.ValidateDataAnnotations();
if (env.IsEnvironment("test")) if (env.IsEnvironment("test"))
{ {
services.AddMassTransitTestHarness(configure => services.AddMassTransitTestHarness(configure =>
@ -47,12 +51,13 @@ public static class Extensions
configure.UsingRabbitMq((context, configurator) => configure.UsingRabbitMq((context, configurator) =>
{ {
var rabbitMqOptions = services.GetOptions<RabbitMqOptions>(nameof(RabbitMqOptions)); var rabbitMqOptions = services.GetOptions<RabbitMqOptions>(nameof(RabbitMqOptions));
var host = IsRunningInContainer ? "rabbitmq" : rabbitMqOptions.HostName; var host = IsRunningInContainer ? "rabbitmq" : rabbitMqOptions.HostName;
configurator.Host(host, rabbitMqOptions?.Port ?? 5672, "/", h => configurator.Host(host, rabbitMqOptions?.Port ?? 5672, "/", h =>
{ {
h.Username(rabbitMqOptions.UserName); h.Username(rabbitMqOptions?.UserName);
h.Password(rabbitMqOptions.Password); h.Password(rabbitMqOptions?.Password);
}); });
var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()) var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())

View File

@ -1,9 +1,8 @@
using BuildingBlocks.EFCore; using BuildingBlocks.EFCore;
using BuildingBlocks.PersistMessageProcessor;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Passenger.Data.Configurations; namespace BuildingBlocks.PersistMessageProcessor.Data.Configurations;
public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage> public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage>
{ {
@ -13,8 +12,8 @@ public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessa
builder.HasKey(x => x.Id); builder.HasKey(x => x.Id);
builder.Property(x => x.Id) builder.Property(r => r.Id)
.IsRequired(); .IsRequired().ValueGeneratedNever();
builder.Property(x => x.DeliveryType) builder.Property(x => x.DeliveryType)
.HasMaxLength(50) .HasMaxLength(50)

View File

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
namespace BuildingBlocks.PersistMessageProcessor.Data;
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<PersistMessageDbContext>
{
public PersistMessageDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<PersistMessageDbContext>();
builder.UseSqlServer(
"Data Source=.\\sqlexpress;Initial Catalog=PersistMessageDB;Persist Security Info=False;Integrated Security=SSPI;TrustServerCertificate=True");
return new PersistMessageDbContext(builder.Options);
}
}

View File

@ -0,0 +1,64 @@
// <auto-generated />
using System;
using BuildingBlocks.PersistMessageProcessor.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BuildingBlocks.PersistMessageProcessor.Data.Migrations
{
[DbContext(typeof(PersistMessageDbContext))]
[Migration("20221206184130_initial")]
partial class initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BuildingBlocks.PersistMessageProcessor.Data.Migrations
{
/// <inheritdoc />
public partial class initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "dbo");
migrationBuilder.CreateTable(
name: "PersistMessage",
schema: "dbo",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false),
DataType = table.Column<string>(type: "nvarchar(max)", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
RetryCount = table.Column<int>(type: "int", nullable: false),
MessageStatus = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false),
DeliveryType = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistMessage", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PersistMessage",
schema: "dbo");
}
}
}

View File

@ -0,0 +1,61 @@
// <auto-generated />
using System;
using BuildingBlocks.PersistMessageProcessor.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BuildingBlocks.PersistMessageProcessor.Data.Migrations
{
[DbContext(typeof(PersistMessageDbContext))]
partial class PersistMessageDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,19 @@
using BuildingBlocks.EFCore;
using BuildingBlocks.PersistMessageProcessor.Data.Configurations;
using Microsoft.EntityFrameworkCore;
namespace BuildingBlocks.PersistMessageProcessor.Data;
public class PersistMessageDbContext : AppDbContextBase, IPersistMessageDbContext
{
public PersistMessageDbContext(DbContextOptions<PersistMessageDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfiguration(new PersistMessageConfiguration());
base.OnModelCreating(builder);
}
}

View File

@ -0,0 +1,2 @@
dotnet ef migrations add initial --context PersistMessageDbContext -o "PersistMessageProcessor\Data\Migrations"
dotnet ef database update --context PersistMessageDbContext

View File

@ -1,4 +1,7 @@
using Microsoft.Extensions.DependencyInjection; using BuildingBlocks.PersistMessageProcessor.Data;
using BuildingBlocks.Web;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace BuildingBlocks.PersistMessageProcessor; namespace BuildingBlocks.PersistMessageProcessor;
@ -6,6 +9,20 @@ public static class Extensions
{ {
public static IServiceCollection AddPersistMessageProcessor(this IServiceCollection services) public static IServiceCollection AddPersistMessageProcessor(this IServiceCollection services)
{ {
services.AddOptions<PersistMessageOptions>()
.BindConfiguration(nameof(PersistMessageOptions))
.ValidateDataAnnotations();
services.AddDbContext<PersistMessageDbContext>(options =>
{
var persistMessageOptions = services.GetOptions<PersistMessageOptions>(nameof(PersistMessageOptions));
options.UseSqlServer(persistMessageOptions.ConnectionString,
x => x.MigrationsAssembly(typeof(PersistMessageDbContext).Assembly.GetName().Name));
});
services.AddScoped<IPersistMessageDbContext>(provider => provider.GetService<PersistMessageDbContext>());
services.AddScoped<IPersistMessageProcessor, PersistMessageProcessor>(); services.AddScoped<IPersistMessageProcessor, PersistMessageProcessor>();
services.AddHostedService<PersistMessageBackgroundService>(); services.AddHostedService<PersistMessageBackgroundService>();

View File

@ -0,0 +1,9 @@
using BuildingBlocks.EFCore;
using Microsoft.EntityFrameworkCore;
namespace BuildingBlocks.PersistMessageProcessor;
public interface IPersistMessageDbContext : IDbContext
{
DbSet<PersistMessage> PersistMessages => Set<PersistMessage>();
}

View File

@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -8,18 +7,18 @@ namespace BuildingBlocks.PersistMessageProcessor;
public class PersistMessageBackgroundService : BackgroundService public class PersistMessageBackgroundService : BackgroundService
{ {
private readonly ILogger<PersistMessageBackgroundService> _logger; private readonly ILogger<PersistMessageBackgroundService> _logger;
private readonly IServiceProvider _serviceProvider; private readonly IPersistMessageProcessor _persistMessageProcessor;
private PersistMessageOptions _options; private PersistMessageOptions _options;
private Task? _executingTask; private Task? _executingTask;
public PersistMessageBackgroundService( public PersistMessageBackgroundService(
ILogger<PersistMessageBackgroundService> logger, ILogger<PersistMessageBackgroundService> logger,
IServiceProvider serviceProvider, IPersistMessageProcessor persistMessageProcessor,
IOptions<PersistMessageOptions> options) IOptions<PersistMessageOptions> options)
{ {
_logger = logger; _logger = logger;
_serviceProvider = serviceProvider; _persistMessageProcessor = persistMessageProcessor;
_options = options.Value; _options = options.Value;
} }
@ -45,11 +44,7 @@ public class PersistMessageBackgroundService : BackgroundService
{ {
try try
{ {
await using (var scope = _serviceProvider.CreateAsyncScope()) await _persistMessageProcessor.ProcessAllAsync(stoppingToken);
{
var service = scope.ServiceProvider.GetRequiredService<IPersistMessageProcessor>();
await service.ProcessAllAsync(stoppingToken);
}
var delay = _options.Interval is { } var delay = _options.Interval is { }
? TimeSpan.FromSeconds((int)_options.Interval) ? TimeSpan.FromSeconds((int)_options.Interval)

View File

@ -2,7 +2,6 @@
using System.Text.Json; using System.Text.Json;
using Ardalis.GuardClauses; using Ardalis.GuardClauses;
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.EFCore;
using BuildingBlocks.IdsGenerator; using BuildingBlocks.IdsGenerator;
using BuildingBlocks.Utils; using BuildingBlocks.Utils;
using MassTransit; using MassTransit;
@ -16,18 +15,18 @@ public class PersistMessageProcessor : IPersistMessageProcessor
{ {
private readonly ILogger<PersistMessageProcessor> _logger; private readonly ILogger<PersistMessageProcessor> _logger;
private readonly IMediator _mediator; private readonly IMediator _mediator;
private readonly IDbContext _dbContext; private readonly IPersistMessageDbContext _persistMessageDbContext;
private readonly IPublishEndpoint _publishEndpoint; private readonly IPublishEndpoint _publishEndpoint;
public PersistMessageProcessor( public PersistMessageProcessor(
ILogger<PersistMessageProcessor> logger, ILogger<PersistMessageProcessor> logger,
IMediator mediator, IMediator mediator,
IDbContext dbContext, IPersistMessageDbContext persistMessageDbContext,
IPublishEndpoint publishEndpoint) IPublishEndpoint publishEndpoint)
{ {
_logger = logger; _logger = logger;
_mediator = mediator; _mediator = mediator;
_dbContext = dbContext; _persistMessageDbContext = persistMessageDbContext;
_publishEndpoint = publishEndpoint; _publishEndpoint = publishEndpoint;
} }
@ -55,13 +54,13 @@ public class PersistMessageProcessor : IPersistMessageProcessor
public async Task<IReadOnlyList<PersistMessage>> GetByFilterAsync(Expression<Func<PersistMessage, bool>> predicate, public async Task<IReadOnlyList<PersistMessage>> GetByFilterAsync(Expression<Func<PersistMessage, bool>> predicate,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return (await _dbContext.PersistMessages.Where(predicate).ToListAsync(cancellationToken)) return (await _persistMessageDbContext.PersistMessages.Where(predicate).ToListAsync(cancellationToken))
.AsReadOnly(); .AsReadOnly();
} }
public Task<PersistMessage> ExistMessageAsync(long messageId, CancellationToken cancellationToken = default) public Task<PersistMessage> ExistMessageAsync(long messageId, CancellationToken cancellationToken = default)
{ {
return _dbContext.PersistMessages.FirstOrDefaultAsync(x => return _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync(x =>
x.Id == messageId && x.Id == messageId &&
x.DeliveryType == MessageDeliveryType.Inbox && x.DeliveryType == MessageDeliveryType.Inbox &&
x.MessageStatus == MessageStatus.Processed, x.MessageStatus == MessageStatus.Processed,
@ -74,7 +73,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var message = var message =
await _dbContext.PersistMessages.FirstOrDefaultAsync( await _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync(
x => x.Id == messageId && x.DeliveryType == deliveryType, cancellationToken); x => x.Id == messageId && x.DeliveryType == deliveryType, cancellationToken);
if (message is null) if (message is null)
@ -111,7 +110,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
public async Task ProcessAllAsync(CancellationToken cancellationToken = default) public async Task ProcessAllAsync(CancellationToken cancellationToken = default)
{ {
var messages = await _dbContext.PersistMessages var messages = await _persistMessageDbContext.PersistMessages
.Where(x => x.MessageStatus != MessageStatus.Processed) .Where(x => x.MessageStatus != MessageStatus.Processed)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@ -120,7 +119,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
public async Task ProcessInboxAsync(long messageId, CancellationToken cancellationToken = default) public async Task ProcessInboxAsync(long messageId, CancellationToken cancellationToken = default)
{ {
var message = await _dbContext.PersistMessages.FirstOrDefaultAsync( var message = await _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync(
x => x.Id == messageId && x => x.Id == messageId &&
x.DeliveryType == MessageDeliveryType.Inbox && x.DeliveryType == MessageDeliveryType.Inbox &&
x.MessageStatus == MessageStatus.InProgress, x.MessageStatus == MessageStatus.InProgress,
@ -191,7 +190,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
else else
id = SnowFlakIdGenerator.NewId(); id = SnowFlakIdGenerator.NewId();
await _dbContext.PersistMessages.AddAsync( await _persistMessageDbContext.PersistMessages.AddAsync(
new PersistMessage( new PersistMessage(
id, id,
messageEnvelope.Message.GetType().ToString(), messageEnvelope.Message.GetType().ToString(),
@ -199,7 +198,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
deliveryType), deliveryType),
cancellationToken); cancellationToken);
await _dbContext.SaveChangesAsync(cancellationToken); await _persistMessageDbContext.SaveChangesAsync(cancellationToken);
_logger.LogInformation( _logger.LogInformation(
"Message with id: {MessageID} and delivery type: {DeliveryType} saved in persistence message store.", "Message with id: {MessageID} and delivery type: {DeliveryType} saved in persistence message store.",
@ -213,8 +212,8 @@ public class PersistMessageProcessor : IPersistMessageProcessor
{ {
message.ChangeState(MessageStatus.Processed); message.ChangeState(MessageStatus.Processed);
_dbContext.PersistMessages.Update(message); _persistMessageDbContext.PersistMessages.Update(message);
await _dbContext.SaveChangesAsync(cancellationToken); await _persistMessageDbContext.SaveChangesAsync(cancellationToken);
} }
} }

View File

@ -2,10 +2,12 @@
using BuildingBlocks.Core.Event; using BuildingBlocks.Core.Event;
using BuildingBlocks.Core.Model; using BuildingBlocks.Core.Model;
using BuildingBlocks.EFCore; using BuildingBlocks.EFCore;
using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo; using BuildingBlocks.Mongo;
using BuildingBlocks.PersistMessageProcessor; using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using DotNet.Testcontainers.Containers; using DotNet.Testcontainers.Containers;
using EasyNetQ.Management.Client;
using Grpc.Net.Client; using Grpc.Net.Client;
using MassTransit; using MassTransit;
using MassTransit.Testing; using MassTransit.Testing;
@ -18,7 +20,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Mongo2Go; using MongoDB.Driver;
using NSubstitute; using NSubstitute;
using Respawn; using Respawn;
using Respawn.Graph; using Respawn.Graph;
@ -28,42 +30,43 @@ using Xunit.Abstractions;
namespace BuildingBlocks.TestBase; namespace BuildingBlocks.TestBase;
public class IntegrationTestFixture<TEntryPoint> : IAsyncLifetime public class IntegrationTestFactory<TEntryPoint> : IAsyncLifetime
where TEntryPoint : class where TEntryPoint : class
{ {
private readonly WebApplicationFactory<TEntryPoint> _factory; private readonly WebApplicationFactory<TEntryPoint> _factory;
private int Timeout => 60; // Second private int Timeout => 120; // Second
public MsSqlTestcontainer MsSqlTestContainer; public MsSqlTestcontainer MsSqlTestContainer;
public MsSqlTestcontainer MsSqlPersistTestContainer;
public RabbitMqTestcontainer RabbitMqTestContainer; public RabbitMqTestcontainer RabbitMqTestContainer;
public MongoDbTestcontainer MongoDbTestContainer;
private ITestHarness TestHarness => ServiceProvider?.GetTestHarness(); private ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
public HttpClient HttpClient => _factory?.CreateClient(); public HttpClient HttpClient => _factory?.CreateClient();
public GrpcChannel Channel => public GrpcChannel Channel => GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions { HttpClient = HttpClient });
GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions { HttpClient = HttpClient });
public Action<IServiceCollection> TestRegistrationServices { get; set; } public Action<IServiceCollection> TestRegistrationServices { get; set; }
public IServiceProvider ServiceProvider => _factory?.Services; public IServiceProvider ServiceProvider => _factory?.Services;
public IConfiguration Configuration => _factory?.Services.GetRequiredService<IConfiguration>(); public IConfiguration Configuration => _factory?.Services.GetRequiredService<IConfiguration>();
public ILogger Logger { get; set; }
public IntegrationTestFixture() public IntegrationTestFactory()
{ {
_factory = new WebApplicationFactory<TEntryPoint>() _factory = new WebApplicationFactory<TEntryPoint>()
.WithWebHostBuilder(builder => .WithWebHostBuilder(builder =>
{ {
builder.ConfigureAppConfiguration(AddCustomAppSettings);
builder.UseEnvironment("test"); builder.UseEnvironment("test");
builder.ConfigureServices(services => builder.ConfigureServices(services =>
{ {
TestRegistrationServices?.Invoke(services); TestRegistrationServices?.Invoke(services);
services.ReplaceSingleton(AddHttpContextAccessorMock); services.ReplaceSingleton(AddHttpContextAccessorMock);
}); });
builder.ConfigureAppConfiguration(AddCustomAppSettings);
}); });
} }
public async Task InitializeAsync() public async Task InitializeAsync()
{ {
await StartTestContainerAsync(); await StartTestContainerAsync();
@ -134,32 +137,35 @@ public class IntegrationTestFixture<TEntryPoint> : IAsyncLifetime
await TestHarness.Bus.Publish<TMessage>(message, cancellationToken); await TestHarness.Bus.Publish<TMessage>(message, cancellationToken);
} }
public async Task WaitForPublishing<TMessage>(CancellationToken cancellationToken = default) public async Task<bool> WaitForPublishing<TMessage>(CancellationToken cancellationToken = default)
where TMessage : class, IEvent where TMessage : class, IEvent
{ {
await WaitUntilConditionMet(async () => var result = await WaitUntilConditionMet(async () =>
{ {
var published = await TestHarness.Published.Any<TMessage>(cancellationToken); var published = await TestHarness.Published.Any<TMessage>(cancellationToken);
var faulty = await TestHarness.Published.Any<Fault<TMessage>>(cancellationToken); var faulty = await TestHarness.Published.Any<Fault<TMessage>>(cancellationToken);
return published && faulty == false; return published && faulty == false;
}); });
return result;
} }
public async Task WaitForConsuming<TMessage>(CancellationToken cancellationToken = default) public async Task<bool> WaitForConsuming<TMessage>(CancellationToken cancellationToken = default)
where TMessage : class, IEvent where TMessage : class, IEvent
{ {
await WaitUntilConditionMet(async () => var result = await WaitUntilConditionMet(async () =>
{ {
var consumed = await TestHarness.Consumed.Any<TMessage>(cancellationToken); var consumed = await TestHarness.Consumed.Any<TMessage>(cancellationToken);
var faulty = await TestHarness.Consumed.Any<Fault<TMessage>>(cancellationToken); var faulty = await TestHarness.Consumed.Any<Fault<TMessage>>(cancellationToken);
return consumed && faulty == false; return consumed && faulty == false;
}); });
return result;
} }
// Ref: https://tech.energyhelpline.com/in-memory-testing-with-masstransit/ // Ref: https://tech.energyhelpline.com/in-memory-testing-with-masstransit/
public async ValueTask WaitUntilConditionMet(Func<Task<bool>> conditionToMet, int? timeoutSecond = null) public async Task<bool> WaitUntilConditionMet(Func<Task<bool>> conditionToMet, int? timeoutSecond = null)
{ {
var time = timeoutSecond ?? Timeout; var time = timeoutSecond ?? Timeout;
@ -168,18 +174,20 @@ public class IntegrationTestFixture<TEntryPoint> : IAsyncLifetime
var meet = await conditionToMet.Invoke(); var meet = await conditionToMet.Invoke();
while (!meet) while (!meet)
{ {
if (timeoutExpired) throw new TimeoutException("Condition not met for the test."); if (timeoutExpired) return false;
await Task.Delay(100); await Task.Delay(100);
meet = await conditionToMet.Invoke(); meet = await conditionToMet.Invoke();
timeoutExpired = DateTime.Now - startTime > TimeSpan.FromSeconds(time); timeoutExpired = DateTime.Now - startTime > TimeSpan.FromSeconds(time);
} }
return true;
} }
public async ValueTask ShouldProcessedPersistInternalCommand<TInternalCommand>() public async Task<bool> ShouldProcessedPersistInternalCommand<TInternalCommand>()
where TInternalCommand : class, IInternalCommand where TInternalCommand : class, IInternalCommand
{ {
await WaitUntilConditionMet(async () => var result = await WaitUntilConditionMet(async () =>
{ {
return await ExecuteScopeAsync(async sp => return await ExecuteScopeAsync(async sp =>
{ {
@ -195,22 +203,30 @@ public class IntegrationTestFixture<TEntryPoint> : IAsyncLifetime
return res; return res;
}); });
}); });
return result;
} }
private async Task StartTestContainerAsync() private async Task StartTestContainerAsync()
{ {
MsSqlTestContainer = TestContainers.MsSqlTestContainer; MsSqlTestContainer = TestContainers.MsSqlTestContainer;
MsSqlPersistTestContainer = TestContainers.MsSqlPersistTestContainer;
RabbitMqTestContainer = TestContainers.RabbitMqTestContainer; RabbitMqTestContainer = TestContainers.RabbitMqTestContainer;
MongoDbTestContainer = TestContainers.MongoTestContainer;
await MongoDbTestContainer.StartAsync();
await MsSqlTestContainer.StartAsync(); await MsSqlTestContainer.StartAsync();
await MsSqlPersistTestContainer.StartAsync();
await RabbitMqTestContainer.StartAsync(); await RabbitMqTestContainer.StartAsync();
} }
private async Task StopTestContainerAsync() private async Task StopTestContainerAsync()
{ {
await MsSqlTestContainer.StopAsync(); await MsSqlTestContainer.StopAsync();
await MsSqlPersistTestContainer.StopAsync();
await RabbitMqTestContainer.StopAsync(); await RabbitMqTestContainer.StopAsync();
await MongoDbTestContainer.StopAsync();
} }
private void AddCustomAppSettings(IConfigurationBuilder configuration) private void AddCustomAppSettings(IConfigurationBuilder configuration)
@ -218,10 +234,13 @@ public class IntegrationTestFixture<TEntryPoint> : IAsyncLifetime
configuration.AddInMemoryCollection(new KeyValuePair<string, string>[] configuration.AddInMemoryCollection(new KeyValuePair<string, string>[]
{ {
new("DatabaseOptions:DefaultConnection", MsSqlTestContainer.ConnectionString + "TrustServerCertificate=True"), new("DatabaseOptions:DefaultConnection", MsSqlTestContainer.ConnectionString + "TrustServerCertificate=True"),
new("PersistMessageOptions:ConnectionString", MsSqlPersistTestContainer.ConnectionString + "TrustServerCertificate=True"),
new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname), new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname),
new("RabbitMqOptions:UserName", RabbitMqTestContainer.Username), new("RabbitMqOptions:UserName", RabbitMqTestContainer.Username),
new("RabbitMqOptions:Password", RabbitMqTestContainer.Password), new("RabbitMqOptions:Password", RabbitMqTestContainer.Password),
new("RabbitMqOptions:Port", RabbitMqTestContainer.Port.ToString()) new("RabbitMqOptions:Port", RabbitMqTestContainer.Port.ToString()),
new("MongoOptions:ConnectionString", MongoDbTestContainer.ConnectionString),
new("MongoOptions:DatabaseName", MongoDbTestContainer.Database)
}); });
} }
@ -238,7 +257,7 @@ public class IntegrationTestFixture<TEntryPoint> : IAsyncLifetime
} }
} }
public class IntegrationTestFixture<TEntryPoint, TWContext> : IntegrationTestFixture<TEntryPoint> public class IntegrationTestFactory<TEntryPoint, TWContext> : IntegrationTestFactory<TEntryPoint>
where TEntryPoint : class where TEntryPoint : class
where TWContext : DbContext where TWContext : DbContext
{ {
@ -345,7 +364,7 @@ public class IntegrationTestFixture<TEntryPoint, TWContext> : IntegrationTestFix
} }
} }
public class IntegrationTestFixture<TEntryPoint, TWContext, TRContext> : IntegrationTestFixture<TEntryPoint, TWContext> public class IntegrationTestFactory<TEntryPoint, TWContext, TRContext> : IntegrationTestFactory<TEntryPoint, TWContext>
where TEntryPoint : class where TEntryPoint : class
where TWContext : DbContext where TWContext : DbContext
where TRContext : MongoDbContext where TRContext : MongoDbContext
@ -365,48 +384,93 @@ public class IntegrationTestFixtureCore<TEntryPoint> : IAsyncLifetime
where TEntryPoint : class where TEntryPoint : class
{ {
private Respawner _reSpawnerDefaultDb; private Respawner _reSpawnerDefaultDb;
private MongoDbRunner _mongoRunner; private Respawner _reSpawnerPersistDb;
private SqlConnection DefaultDbConnection { get; set; }
private SqlConnection PersistDbConnection { get; set; }
public IntegrationTestFixtureCore(IntegrationTestFactory<TEntryPoint> integrationTestFixture, ITestOutputHelper outputHelper)
public IntegrationTestFixtureCore(IntegrationTestFixture<TEntryPoint> integrationTestFixture)
{ {
Fixture = integrationTestFixture; Fixture = integrationTestFixture;
integrationTestFixture.RegisterServices(services => RegisterTestsServices(services)); integrationTestFixture.RegisterServices(services => RegisterTestsServices(services));
integrationTestFixture.Logger = integrationTestFixture.CreateLogger(outputHelper);
} }
public IntegrationTestFixture<TEntryPoint> Fixture { get; } public IntegrationTestFactory<TEntryPoint> Fixture { get; }
public async Task InitializeAsync() public async Task InitializeAsync()
{ {
var databaseOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<DatabaseOptions>>()?.Value; var databaseOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<DatabaseOptions>>()?.Value;
var mongoOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<MongoOptions>>()?.Value; var persistOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<PersistMessageOptions>>()?.Value;
if (!string.IsNullOrEmpty(persistOptions?.ConnectionString))
{
PersistDbConnection = new SqlConnection(persistOptions?.ConnectionString);
await PersistDbConnection.OpenAsync();
_reSpawnerPersistDb = await Respawner.CreateAsync(PersistDbConnection,
new RespawnerOptions { TablesToIgnore = new Table[] { "__EFMigrationsHistory" }, });
await _reSpawnerPersistDb.ResetAsync(PersistDbConnection);
}
if (!string.IsNullOrEmpty(databaseOptions?.DefaultConnection)) if (!string.IsNullOrEmpty(databaseOptions?.DefaultConnection))
{ {
var dbConnection = new SqlConnection(databaseOptions.DefaultConnection); DefaultDbConnection = new SqlConnection(databaseOptions.DefaultConnection);
await dbConnection.OpenAsync(); await DefaultDbConnection.OpenAsync();
_reSpawnerDefaultDb = await Respawner.CreateAsync(dbConnection, _reSpawnerDefaultDb = await Respawner.CreateAsync(DefaultDbConnection,
new RespawnerOptions new RespawnerOptions { TablesToIgnore = new Table[] { "__EFMigrationsHistory" }, });
{
TablesToIgnore = new Table[] { "__EFMigrationsHistory" },
});
await _reSpawnerDefaultDb.ResetAsync(dbConnection); await _reSpawnerDefaultDb.ResetAsync(DefaultDbConnection);
await SeedDataAsync();
} }
_mongoRunner = MongoDbRunner.Start();
if (!string.IsNullOrEmpty(mongoOptions?.ConnectionString))
mongoOptions.ConnectionString = _mongoRunner.ConnectionString;
await SeedDataAsync();
} }
public async Task DisposeAsync() public async Task DisposeAsync()
{ {
_mongoRunner.Dispose(); await ResetMongoAsync();
await ResetRabbitMqAsync();
}
private async Task ResetMongoAsync(CancellationToken cancellationToken = default)
{
//https://stackoverflow.com/questions/3366397/delete-everything-in-a-mongodb-database
MongoClient dbClient = new MongoClient(Fixture.MongoDbTestContainer?.ConnectionString);
var collections = await dbClient.GetDatabase(Fixture.MongoDbTestContainer?.Database)
.ListCollectionsAsync(cancellationToken: cancellationToken);
foreach (var collection in collections.ToList())
{
await dbClient.GetDatabase(Fixture.MongoDbTestContainer?.Database)
.DropCollectionAsync(collection["name"].AsString, cancellationToken);
}
}
private async Task ResetRabbitMqAsync(CancellationToken cancellationToken = default)
{
var port = Fixture.RabbitMqTestContainer?.GetMappedPublicPort(15672) ?? 15672;
var rabbitmqOptions = Fixture.ServiceProvider.GetRequiredService<IOptions<RabbitMqOptions>>()?.Value;
var managementClient = new ManagementClient(rabbitmqOptions?.HostName, rabbitmqOptions?.UserName,
rabbitmqOptions?.Password, port);
var bd = await managementClient.GetBindingsAsync(cancellationToken);
var bindings = bd.Where(x => !string.IsNullOrEmpty(x.Source) && !string.IsNullOrEmpty(x.Destination));
foreach (var binding in bindings)
{
await managementClient.DeleteBindingAsync(binding, cancellationToken);
}
var queues = await managementClient.GetQueuesAsync(cancellationToken);
foreach (var queue in queues)
{
await managementClient.DeleteQueueAsync(queue, cancellationToken);
}
} }
protected virtual void RegisterTestsServices(IServiceCollection services) protected virtual void RegisterTestsServices(IServiceCollection services)
@ -422,44 +486,32 @@ public class IntegrationTestFixtureCore<TEntryPoint> : IAsyncLifetime
} }
} }
public abstract class IntegrationTestBase<TEntryPoint> : IntegrationTestFixtureCore<TEntryPoint>, public abstract class IntegrationTestBase<TEntryPoint, TWContext> : IntegrationTestFixtureCore<TEntryPoint>
IClassFixture<IntegrationTestFixture<TEntryPoint>> //,IClassFixture<IntegrationTestFactory<TEntryPoint, TWContext>>
where TEntryPoint : class
{
protected IntegrationTestBase(
IntegrationTestFixture<TEntryPoint> integrationTestFixture) : base(integrationTestFixture)
{
Fixture = integrationTestFixture;
}
public IntegrationTestFixture<TEntryPoint> Fixture { get; }
}
public abstract class IntegrationTestBase<TEntryPoint, TWContext> : IntegrationTestFixtureCore<TEntryPoint>,
IClassFixture<IntegrationTestFixture<TEntryPoint, TWContext>>
where TEntryPoint : class where TEntryPoint : class
where TWContext : DbContext where TWContext : DbContext
{ {
protected IntegrationTestBase( protected IntegrationTestBase(
IntegrationTestFixture<TEntryPoint, TWContext> integrationTestFixture) : base(integrationTestFixture) IntegrationTestFactory<TEntryPoint, TWContext> integrationTestFixture, ITestOutputHelper outputHelper = null) : base(integrationTestFixture, outputHelper)
{ {
Fixture = integrationTestFixture; Fixture = integrationTestFixture;
} }
public IntegrationTestFixture<TEntryPoint, TWContext> Fixture { get; } public IntegrationTestFactory<TEntryPoint, TWContext> Fixture { get; }
} }
public abstract class IntegrationTestBase<TEntryPoint, TWContext, TRContext> : IntegrationTestFixtureCore<TEntryPoint>, public abstract class IntegrationTestBase<TEntryPoint, TWContext, TRContext> : IntegrationTestFixtureCore<TEntryPoint>
IClassFixture<IntegrationTestFixture<TEntryPoint, TWContext, TRContext>> //,IClassFixture<IntegrationTestFactory<TEntryPoint, TWContext, TRContext>>
where TEntryPoint : class where TEntryPoint : class
where TWContext : DbContext where TWContext : DbContext
where TRContext : MongoDbContext where TRContext : MongoDbContext
{ {
protected IntegrationTestBase( protected IntegrationTestBase(
IntegrationTestFixture<TEntryPoint, TWContext, TRContext> integrationTestFixture) : base(integrationTestFixture) IntegrationTestFactory<TEntryPoint, TWContext, TRContext> integrationTestFixture, ITestOutputHelper outputHelper = null) : base(integrationTestFixture, outputHelper)
{ {
Fixture = integrationTestFixture; Fixture = integrationTestFixture;
} }
public IntegrationTestFixture<TEntryPoint, TWContext, TRContext> Fixture { get; } public IntegrationTestFactory<TEntryPoint, TWContext, TRContext> Fixture { get; }
} }

View File

@ -1,4 +1,5 @@
using DotNet.Testcontainers.Builders; using System;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers; using DotNet.Testcontainers.Containers;
@ -15,18 +16,28 @@ public static class TestContainers
Username = Guid.NewGuid().ToString("D") Username = Guid.NewGuid().ToString("D")
}) })
.WithImage("postgres:latest") .WithImage("postgres:latest")
.WithCleanUp(true)
.Build(); .Build();
// issue ref: https://github.com/testcontainers/testcontainers-dotnet/discussions/533
public static MsSqlTestcontainer MsSqlTestContainer = new TestcontainersBuilder<MsSqlTestcontainer>() public static MsSqlTestcontainer MsSqlTestContainer = new TestcontainersBuilder<MsSqlTestcontainer>()
.WithDatabase(new MsSqlTestcontainerConfiguration() .WithDatabase(new MsSqlTestcontainerConfiguration()
{ {
Password = Guid.NewGuid().ToString("D") Password = Guid.NewGuid().ToString("D")
}) })
.WithImage("mcr.microsoft.com/mssql/server:2022-latest") .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
.WithExposedPort(1433) .WithPortBinding(1433, true)
.WithPortBinding(1433, true) // Add this line for issue in hangup MsSqlTestContainer in docker desktop .WithCleanUp(true)
.Build();
public static MsSqlTestcontainer MsSqlPersistTestContainer = new TestcontainersBuilder<MsSqlTestcontainer>()
.WithDatabase(new MsSqlTestcontainerConfiguration()
{
Password = Guid.NewGuid().ToString("D")
})
.WithImage("mcr.microsoft.com/mssql/server:2022-latest")
.WithPortBinding(1433, true)
.WithCleanUp(true)
.Build(); .Build();
@ -35,14 +46,24 @@ public static class TestContainers
{ {
Database = Guid.NewGuid().ToString("D"), Database = Guid.NewGuid().ToString("D"),
Username = Guid.NewGuid().ToString("D"), Username = Guid.NewGuid().ToString("D"),
Password = Guid.NewGuid().ToString("D") Password = Guid.NewGuid().ToString("D"),
}) })
.WithImage("mongo") .WithImage("mongo:4")
.WithCleanUp(true)
.Build(); .Build();
public static RabbitMqTestcontainer RabbitMqTestContainer => new TestcontainersBuilder<RabbitMqTestcontainer>() public static RabbitMqTestcontainer RabbitMqTestContainer => new TestcontainersBuilder<RabbitMqTestcontainer>()
.WithMessageBroker(new RabbitMqTestcontainerConfiguration() { Password = "guest", Username = "guest" }) .WithMessageBroker(new RabbitMqTestcontainerConfiguration()
{
Password = "guest",
Username = "guest"
})
.WithImage("rabbitmq:3-management") .WithImage("rabbitmq:3-management")
.WithPortBinding(15672, true)
.WithPortBinding(5672, true)
.WithCleanUp(true)
.Build(); .Build();
} }

View File

@ -1,7 +1,7 @@
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace BuildingBlocks.Utils; namespace BuildingBlocks.Web;
public interface ICurrentUserProvider public interface ICurrentUserProvider
{ {

View File

@ -0,0 +1,30 @@
using Microsoft.Extensions.Hosting;
namespace BuildingBlocks.Web;
public static class ServiceProviderExtensions
{
public static async Task StartTestHostedServices(
this IServiceProvider serviceProvider,
Type[] hostedServiceTypes,
CancellationToken cancellationToken = default)
{
foreach (var hostedServiceType in hostedServiceTypes)
{
if (serviceProvider.GetService(hostedServiceType) is IHostedService hostedService)
await hostedService.StartAsync(cancellationToken);
}
}
public static async Task StopTestHostedServices(
this IServiceProvider serviceProvider,
Type[] hostedServiceTypes,
CancellationToken cancellationToken = default)
{
foreach (var hostedServiceType in hostedServiceTypes)
{
if (serviceProvider.GetService(hostedServiceType) is IHostedService hostedService)
await hostedService.StopAsync(cancellationToken);
}
}
}

View File

@ -1,7 +1,7 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
namespace BuildingBlocks.Utils; namespace BuildingBlocks.Web;
public class SlugifyParameterTransformer : IOutboundParameterTransformer public class SlugifyParameterTransformer : IOutboundParameterTransformer
{ {

View File

@ -1,8 +1,2 @@
{ {
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
} }

View File

@ -6,6 +6,11 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"PersistMessageOptions": {
"Interval": 30,
"Enabled": true,
"ConnectionString": "Server=db;Database=PersistMessageDB;User ID=sa;Password=@Aa123456"
},
"RabbitMqOptions": { "RabbitMqOptions": {
"HostName": "rabbitmq", "HostName": "rabbitmq",
"ExchangeName": "booking", "ExchangeName": "booking",

View File

@ -40,7 +40,7 @@
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 30, "Interval": 30,
"Enabled": true, "Enabled": true,
"ConnectionString": "Server=localhost;Port=5432;Database=persist_message_db;User Id=postgres;Password=postgres;Include Error Detail=true" "ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -14,16 +14,16 @@
"Microsoft.EntityFrameworkCore.Database.Command": "Debug" "Microsoft.EntityFrameworkCore.Database.Command": "Debug"
} }
}, },
"PersistMessageOptions": {
"Interval": 1,
"Enabled": true,
"ConnectionString": "Server=localhost;Port=5432;Database=persist_message_db_test;User Id=postgres;Password=postgres;Include Error Detail=true"
},
"MongoOptions": { "MongoOptions": {
"ConnectionString": "mongodb://localhost:27017", "ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "booking-db-test" "DatabaseName": "booking-db-test"
}, },
"EventStore": { "EventStore": {
"ConnectionString": "esdb://localhost:2113?tls=false" "ConnectionString": "esdb://localhost:2113?tls=false"
},
"PersistMessageOptions": {
"Interval": 30,
"Enabled": true,
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
} }
} }

View File

@ -4,7 +4,7 @@ using Booking.Booking.Models.ValueObjects;
using BuildingBlocks.Core; using BuildingBlocks.Core;
using BuildingBlocks.Core.CQRS; using BuildingBlocks.Core.CQRS;
using BuildingBlocks.EventStoreDB.Repository; using BuildingBlocks.EventStoreDB.Repository;
using BuildingBlocks.Utils; using BuildingBlocks.Web;
using Flight; using Flight;
using Passenger; using Passenger;

View File

@ -1,6 +1,7 @@
using System.Threading.RateLimiting; using System.Threading.RateLimiting;
using Booking.Data; using Booking.Data;
using BuildingBlocks.Core; using BuildingBlocks.Core;
using BuildingBlocks.EFCore;
using BuildingBlocks.EventStoreDB; using BuildingBlocks.EventStoreDB;
using BuildingBlocks.HealthCheck; using BuildingBlocks.HealthCheck;
using BuildingBlocks.IdsGenerator; using BuildingBlocks.IdsGenerator;
@ -11,8 +12,8 @@ using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo; using BuildingBlocks.Mongo;
using BuildingBlocks.OpenTelemetry; using BuildingBlocks.OpenTelemetry;
using BuildingBlocks.PersistMessageProcessor; using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.PersistMessageProcessor.Data;
using BuildingBlocks.Swagger; using BuildingBlocks.Swagger;
using BuildingBlocks.Utils;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Figgle; using Figgle;
using FluentValidation; using FluentValidation;
@ -97,6 +98,7 @@ public static class InfrastructureExtensions
app.UseCorrelationId(); app.UseCorrelationId();
app.UseRouting(); app.UseRouting();
app.UseHttpMetrics(); app.UseHttpMetrics();
app.UseMigration<PersistMessageDbContext>(env);
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseCustomHealthCheck(); app.UseCustomHealthCheck();
app.MapMetrics(); app.MapMetrics();

View File

@ -3,7 +3,7 @@ using System.Threading.Tasks;
using Booking.Api; using Booking.Api;
using Booking.Data; using Booking.Data;
using BuildingBlocks.Contracts.EventBus.Messages; using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.EFCore; using BuildingBlocks.PersistMessageProcessor.Data;
using BuildingBlocks.TestBase; using BuildingBlocks.TestBase;
using Flight; using Flight;
using FluentAssertions; using FluentAssertions;
@ -19,11 +19,9 @@ using GetByIdRequest = Flight.GetByIdRequest;
namespace Integration.Test.Booking.Features; namespace Integration.Test.Booking.Features;
public class CreateBookingTests : IntegrationTestBase<Program, AppDbContextBase, BookingReadDbContext> public class CreateBookingTests : BookingIntegrationTestBase
{ {
public CreateBookingTests( public CreateBookingTests(IntegrationTestFactory<Program, PersistMessageDbContext, BookingReadDbContext> integrationTestFixture) : base(integrationTestFixture)
IntegrationTestFixture<Program, AppDbContextBase, BookingReadDbContext> integrationTestFixture) : base(
integrationTestFixture)
{ {
} }
@ -46,7 +44,7 @@ public class CreateBookingTests : IntegrationTestBase<Program, AppDbContextBase,
// Assert // Assert
response.Should().BeGreaterOrEqualTo(0); response.Should().BeGreaterOrEqualTo(0);
await Fixture.WaitForPublishing<BookingCreated>(); (await Fixture.WaitForPublishing<BookingCreated>()).Should().Be(true);
} }

View File

@ -0,0 +1,21 @@
using Booking.Api;
using Booking.Data;
using BuildingBlocks.PersistMessageProcessor.Data;
using BuildingBlocks.TestBase;
using Xunit;
namespace Integration.Test;
[Collection(IntegrationTestCollection.Name)]
public class BookingIntegrationTestBase: IntegrationTestBase<Program, PersistMessageDbContext, BookingReadDbContext>
{
public BookingIntegrationTestBase(IntegrationTestFactory<Program, PersistMessageDbContext, BookingReadDbContext> integrationTestFixture) : base(integrationTestFixture)
{
}
}
[CollectionDefinition(Name)]
public class IntegrationTestCollection : ICollectionFixture<IntegrationTestFactory<Program, PersistMessageDbContext, BookingReadDbContext>>
{
public const string Name = "Booking Integration Test";
}

View File

@ -1,8 +1,2 @@
{ {
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft.AspNetCore": "Warning"
}
}
} }

View File

@ -6,7 +6,7 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"ConnectionStrings": { "DatabaseOptions": {
"DefaultConnection": "Server=db;Database=FlightDB;User ID=sa;Password=@Aa123456" "DefaultConnection": "Server=db;Database=FlightDB;User ID=sa;Password=@Aa123456"
}, },
"Jwt": { "Jwt": {
@ -20,5 +20,10 @@
"Password": "guest", "Password": "guest",
"Port": 5672 "Port": 5672
}, },
"PersistMessageOptions": {
"Interval": 30,
"Enabled": true,
"ConnectionString": "Server=db;Database=PersistMessageDB;User ID=sa;Password=@Aa123456"
},
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -35,7 +35,8 @@
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 30, "Interval": 30,
"Enabled": true "Enabled": true,
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -18,7 +18,8 @@
} }
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 1, "Interval": 2,
"Enabled": true "Enabled": true,
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
} }
} }

View File

@ -1,45 +0,0 @@
using System;
using BuildingBlocks.EFCore;
using BuildingBlocks.PersistMessageProcessor;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Flight.Data.Configurations;
public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage>
{
public void Configure(EntityTypeBuilder<PersistMessage> builder)
{
builder.ToTable("PersistMessage", AppDbContextBase.DefaultSchema);
builder.HasKey(x => x.Id);
builder.Property(r => r.Id).ValueGeneratedNever();
builder.Property(x => x.Id)
.IsRequired();
builder.Property(x => x.DeliveryType)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), v))
.IsRequired()
.IsUnicode(false);
builder.Property(x => x.DeliveryType)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), v))
.IsRequired()
.IsUnicode(false);
builder.Property(x => x.MessageStatus)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageStatus)Enum.Parse(typeof(MessageStatus), v))
.IsRequired()
.IsUnicode(false);
}
}

View File

@ -1,5 +1,6 @@
using BuildingBlocks.EFCore; using BuildingBlocks.EFCore;
using BuildingBlocks.Utils; using BuildingBlocks.Utils;
using BuildingBlocks.Web;
using Flight.Aircrafts.Models; using Flight.Aircrafts.Models;
using Flight.Airports.Models; using Flight.Airports.Models;
using Flight.Seats.Models; using Flight.Seats.Models;

View File

@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Flight.Data.Migrations namespace Flight.Data.Migrations
{ {
[DbContext(typeof(FlightDbContext))] [DbContext(typeof(FlightDbContext))]
[Migration("20221203201848_Initial")] [Migration("20221206180723_Initial")]
partial class Initial partial class Initial
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -25,40 +25,6 @@ namespace Flight.Data.Migrations
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Flight.Aircrafts.Models.Aircraft", b => modelBuilder.Entity("Flight.Aircrafts.Models.Aircraft", b =>
{ {
b.Property<long>("Id") b.Property<long>("Id")

View File

@ -56,24 +56,6 @@ namespace Flight.Data.Migrations
table.PrimaryKey("PK_Airport", x => x.Id); table.PrimaryKey("PK_Airport", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "PersistMessage",
schema: "dbo",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false),
DataType = table.Column<string>(type: "nvarchar(max)", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
RetryCount = table.Column<int>(type: "int", nullable: false),
MessageStatus = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false),
DeliveryType = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistMessage", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Flight", name: "Flight",
schema: "dbo", schema: "dbo",
@ -167,10 +149,6 @@ namespace Flight.Data.Migrations
/// <inheritdoc /> /// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropTable(
name: "PersistMessage",
schema: "dbo");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Seat", name: "Seat",
schema: "dbo"); schema: "dbo");

View File

@ -22,40 +22,6 @@ namespace Flight.Data.Migrations
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Flight.Aircrafts.Models.Aircraft", b => modelBuilder.Entity("Flight.Aircrafts.Models.Aircraft", b =>
{ {
b.Property<long>("Id") b.Property<long>("Id")

View File

@ -1,13 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.EFCore; using BuildingBlocks.EFCore;
using Flight.Aircrafts.Models; using Flight.Aircrafts.Models;
using Flight.Aircrafts.Models.Reads; using Flight.Aircrafts.Models.Reads;
using Flight.Airports.Models; using Flight.Airports.Models;
using Flight.Airports.Models.Reads; using Flight.Airports.Models.Reads;
using Flight.Flights.Models;
using Flight.Flights.Models.Reads; using Flight.Flights.Models.Reads;
using Flight.Seats.Models; using Flight.Seats.Models;
using Flight.Seats.Models.Reads; using Flight.Seats.Models.Reads;

View File

@ -66,11 +66,10 @@ public static class InfrastructureExtensions
})); }));
}); });
builder.Services.AddPersistMessageProcessor(); builder.Services.AddCustomDbContext<FlightDbContext>();
builder.Services.AddCustomDbContext<FlightDbContext>(configuration);
builder.Services.AddScoped<IDataSeeder, FlightDataSeeder>(); builder.Services.AddScoped<IDataSeeder, FlightDataSeeder>();
builder.Services.AddMongoDbContext<FlightReadDbContext>(configuration); builder.Services.AddMongoDbContext<FlightReadDbContext>(configuration);
builder.Services.AddPersistMessageProcessor();
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);
builder.Services.AddJwt(); builder.Services.AddJwt();

View File

@ -10,11 +10,10 @@ using Xunit;
namespace Integration.Test.Aircraft.Features; namespace Integration.Test.Aircraft.Features;
public class CreateAircraftTests : IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext> public class CreateAircraftTests : FlightIntegrationTestBase
{ {
public CreateAircraftTests( public CreateAircraftTests(
IntegrationTestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base( IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
integrationTestFixture)
{ {
} }
@ -31,8 +30,8 @@ public class CreateAircraftTests : IntegrationTestBase<Program, FlightDbContext,
response?.Should().NotBeNull(); response?.Should().NotBeNull();
response?.Name.Should().Be(command.Name); response?.Name.Should().Be(command.Name);
await Fixture.WaitForPublishing<AircraftCreated>(); (await Fixture.WaitForPublishing<AircraftCreated>()).Should().Be(true);
await Fixture.ShouldProcessedPersistInternalCommand<CreateAircraftMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateAircraftMongoCommand>()).Should().Be(true);
} }
} }

View File

@ -10,11 +10,10 @@ using Xunit;
namespace Integration.Test.Airport.Features; namespace Integration.Test.Airport.Features;
public class CreateAirportTests : IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext> public class CreateAirportTests : FlightIntegrationTestBase
{ {
public CreateAirportTests( public CreateAirportTests(
IntegrationTestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base( IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
integrationTestFixture)
{ {
} }
@ -31,8 +30,8 @@ public class CreateAirportTests : IntegrationTestBase<Program, FlightDbContext,
response?.Should().NotBeNull(); response?.Should().NotBeNull();
response?.Name.Should().Be(command.Name); response?.Name.Should().Be(command.Name);
await Fixture.WaitForPublishing<AirportCreated>(); (await Fixture.WaitForPublishing<AirportCreated>()).Should().Be(true);
await Fixture.ShouldProcessedPersistInternalCommand<CreateAirportMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateAirportMongoCommand>()).Should().Be(true);
} }
} }

View File

@ -10,17 +10,17 @@ using Xunit;
namespace Integration.Test.Flight.Features; namespace Integration.Test.Flight.Features;
public class CreateFlightTests : IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext> public class CreateFlightTests : FlightIntegrationTestBase
{ {
public CreateFlightTests( public CreateFlightTests(
IntegrationTestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base( IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
integrationTestFixture) {
{ } }
[Fact] [Fact]
public async Task should_create_new_flight_to_db_and_publish_message_to_broker() public async Task should_create_new_flight_to_db_and_publish_message_to_broker()
{ {
// Arrange //Arrange
var command = new FakeCreateFlightCommand().Generate(); var command = new FakeCreateFlightCommand().Generate();
// Act // Act
@ -30,9 +30,9 @@ public class CreateFlightTests : IntegrationTestBase<Program, FlightDbContext, F
response.Should().NotBeNull(); response.Should().NotBeNull();
response?.FlightNumber.Should().Be(command.FlightNumber); response?.FlightNumber.Should().Be(command.FlightNumber);
await Fixture.WaitForPublishing<FlightCreated>(); (await Fixture.WaitForPublishing<FlightCreated>()).Should().Be(true);
await Fixture.WaitForConsuming<FlightCreated>(); (await Fixture.WaitForConsuming<FlightCreated>()).Should().Be(true);
await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>()).Should().Be(true);
} }
} }

View File

@ -12,11 +12,10 @@ using Xunit;
namespace Integration.Test.Flight.Features; namespace Integration.Test.Flight.Features;
public class DeleteFlightTests : IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext> public class DeleteFlightTests : FlightIntegrationTestBase
{ {
public DeleteFlightTests( public DeleteFlightTests(
IntegrationTestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base( IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
integrationTestFixture)
{ {
} }
@ -38,8 +37,8 @@ public class DeleteFlightTests : IntegrationTestBase<Program, FlightDbContext, F
// Assert // Assert
deletedFlight?.IsDeleted.Should().BeTrue(); deletedFlight?.IsDeleted.Should().BeTrue();
await Fixture.WaitForPublishing<FlightDeleted>(); (await Fixture.WaitForPublishing<FlightDeleted>()).Should().Be(true);
await Fixture.ShouldProcessedPersistInternalCommand<DeleteFlightMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<DeleteFlightMongoCommand>()).Should().Be(true);
} }
} }

View File

@ -11,11 +11,10 @@ using Xunit;
namespace Integration.Test.Flight.Features; namespace Integration.Test.Flight.Features;
public class GetAvailableFlightsTests : IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext> public class GetAvailableFlightsTests : FlightIntegrationTestBase
{ {
public GetAvailableFlightsTests( public GetAvailableFlightsTests(
IntegrationTestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
: base(integrationTestFixture)
{ {
} }
@ -27,7 +26,7 @@ public class GetAvailableFlightsTests : IntegrationTestBase<Program, FlightDbCon
await Fixture.SendAsync(flightCommand); await Fixture.SendAsync(flightCommand);
await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>()).Should().Be(true);
var query = new GetAvailableFlightsQuery(); var query = new GetAvailableFlightsQuery();

View File

@ -6,21 +6,16 @@ using Flight.Data;
using Flight.Flights.Features.CreateFlight.Commands.V1.Reads; using Flight.Flights.Features.CreateFlight.Commands.V1.Reads;
using Flight.Flights.Features.GetFlightById.Queries.V1; using Flight.Flights.Features.GetFlightById.Queries.V1;
using FluentAssertions; using FluentAssertions;
using Grpc.Net.Client;
using Integration.Test.Fakes; using Integration.Test.Fakes;
using Xunit; using Xunit;
namespace Integration.Test.Flight.Features; namespace Integration.Test.Flight.Features;
public class GetFlightByIdTests : IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext> public class GetFlightByIdTests : FlightIntegrationTestBase
{ {
private readonly GrpcChannel _channel;
public GetFlightByIdTests( public GetFlightByIdTests(
IntegrationTestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base( IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
integrationTestFixture)
{ {
_channel = Fixture.Channel;
} }
[Fact] [Fact]
@ -30,7 +25,7 @@ public class GetFlightByIdTests : IntegrationTestBase<Program, FlightDbContext,
var command = new FakeCreateFlightCommand().Generate(); var command = new FakeCreateFlightCommand().Generate();
await Fixture.SendAsync(command); await Fixture.SendAsync(command);
await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>()).Should().Be(true);
var query = new GetFlightByIdQuery(command.Id); var query = new GetFlightByIdQuery(command.Id);
@ -49,9 +44,9 @@ public class GetFlightByIdTests : IntegrationTestBase<Program, FlightDbContext,
var command = new FakeCreateFlightCommand().Generate(); var command = new FakeCreateFlightCommand().Generate();
await Fixture.SendAsync(command); await Fixture.SendAsync(command);
await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>()).Should().Be(true);
var flightGrpcClient = new FlightGrpcService.FlightGrpcServiceClient(_channel); var flightGrpcClient = new FlightGrpcService.FlightGrpcServiceClient(Fixture.Channel);
// Act // Act
var response = await flightGrpcClient.GetByIdAsync(new GetByIdRequest {Id = command.Id}); var response = await flightGrpcClient.GetByIdAsync(new GetByIdRequest {Id = command.Id});

View File

@ -10,11 +10,10 @@ using Xunit;
namespace Integration.Test.Flight.Features; namespace Integration.Test.Flight.Features;
public class UpdateFlightTests : IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext> public class UpdateFlightTests : FlightIntegrationTestBase
{ {
public UpdateFlightTests( public UpdateFlightTests(
IntegrationTestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base( IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
integrationTestFixture)
{ {
} }
@ -33,8 +32,8 @@ public class UpdateFlightTests : IntegrationTestBase<Program, FlightDbContext, F
response?.Id.Should().Be(flightEntity?.Id); response?.Id.Should().Be(flightEntity?.Id);
response?.Price.Should().NotBe(flightEntity?.Price); response?.Price.Should().NotBe(flightEntity?.Price);
await Fixture.WaitForPublishing<FlightUpdated>(); (await Fixture.WaitForPublishing<FlightUpdated>()).Should().Be(true);
await Fixture.ShouldProcessedPersistInternalCommand<UpdateFlightMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<UpdateFlightMongoCommand>()).Should().Be(true);
} }
} }

View File

@ -0,0 +1,20 @@
using BuildingBlocks.TestBase;
using Flight.Api;
using Flight.Data;
using Xunit;
namespace Integration.Test;
[Collection(IntegrationTestCollection.Name)]
public class FlightIntegrationTestBase: IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext>
{
public FlightIntegrationTestBase(IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base(integrationTestFixture)
{
}
}
[CollectionDefinition(Name)]
public class IntegrationTestCollection : ICollectionFixture<IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext>>
{
public const string Name = "Flight Integration Test";
}

View File

@ -6,19 +6,16 @@ using Flight.Data;
using Flight.Flights.Features.CreateFlight.Commands.V1.Reads; using Flight.Flights.Features.CreateFlight.Commands.V1.Reads;
using Flight.Seats.Features.CreateSeat.Commands.V1.Reads; using Flight.Seats.Features.CreateSeat.Commands.V1.Reads;
using FluentAssertions; using FluentAssertions;
using Grpc.Net.Client;
using Integration.Test.Fakes; using Integration.Test.Fakes;
using Xunit; using Xunit;
namespace Integration.Test.Seat.Features; namespace Integration.Test.Seat.Features;
public class GetAvailableSeatsTests : IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext> public class GetAvailableSeatsTests : FlightIntegrationTestBase
{ {
private readonly GrpcChannel _channel; public GetAvailableSeatsTests(
IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
public GetAvailableSeatsTests(IntegrationTestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base(integrationTestFixture)
{ {
_channel = Fixture.Channel;
} }
[Fact] [Fact]
@ -29,15 +26,15 @@ public class GetAvailableSeatsTests : IntegrationTestBase<Program, FlightDbConte
await Fixture.SendAsync(flightCommand); await Fixture.SendAsync(flightCommand);
await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>()).Should().Be(true);
var seatCommand = new FakeCreateSeatCommand(flightCommand.Id).Generate(); var seatCommand = new FakeCreateSeatCommand(flightCommand.Id).Generate();
await Fixture.SendAsync(seatCommand); await Fixture.SendAsync(seatCommand);
await Fixture.ShouldProcessedPersistInternalCommand<CreateSeatMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateSeatMongoCommand>()).Should().Be(true);
var flightGrpcClient = new FlightGrpcService.FlightGrpcServiceClient(_channel); var flightGrpcClient = new FlightGrpcService.FlightGrpcServiceClient(Fixture.Channel);
// Act // Act
var response = await flightGrpcClient.GetAvailableSeatsAsync(new GetAvailableSeatsRequest{FlightId = flightCommand.Id}); var response = await flightGrpcClient.GetAvailableSeatsAsync(new GetAvailableSeatsRequest{FlightId = flightCommand.Id});

View File

@ -6,21 +6,16 @@ using Flight.Data;
using Flight.Flights.Features.CreateFlight.Commands.V1.Reads; using Flight.Flights.Features.CreateFlight.Commands.V1.Reads;
using Flight.Seats.Features.CreateSeat.Commands.V1.Reads; using Flight.Seats.Features.CreateSeat.Commands.V1.Reads;
using FluentAssertions; using FluentAssertions;
using Grpc.Net.Client;
using Integration.Test.Fakes; using Integration.Test.Fakes;
using Xunit; using Xunit;
namespace Integration.Test.Seat.Features; namespace Integration.Test.Seat.Features;
public class ReserveSeatTests : IntegrationTestBase<Program, FlightDbContext, FlightReadDbContext> public class ReserveSeatTests : FlightIntegrationTestBase
{ {
private readonly GrpcChannel _channel;
public ReserveSeatTests( public ReserveSeatTests(
IntegrationTestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base( IntegrationTestFactory<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
integrationTestFixture)
{ {
_channel = Fixture.Channel;
} }
[Fact] [Fact]
@ -31,15 +26,15 @@ public class ReserveSeatTests : IntegrationTestBase<Program, FlightDbContext, Fl
await Fixture.SendAsync(flightCommand); await Fixture.SendAsync(flightCommand);
await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateFlightMongoCommand>()).Should().Be(true);
var seatCommand = new FakeCreateSeatCommand(flightCommand.Id).Generate(); var seatCommand = new FakeCreateSeatCommand(flightCommand.Id).Generate();
await Fixture.SendAsync(seatCommand); await Fixture.SendAsync(seatCommand);
await Fixture.ShouldProcessedPersistInternalCommand<CreateSeatMongoCommand>(); (await Fixture.ShouldProcessedPersistInternalCommand<CreateSeatMongoCommand>()).Should().Be(true);
var flightGrpcClient = new FlightGrpcService.FlightGrpcServiceClient(_channel); var flightGrpcClient = new FlightGrpcService.FlightGrpcServiceClient(Fixture.Channel);
// Act // Act
var response = await flightGrpcClient.ReserveSeatAsync(new ReserveSeatRequest() var response = await flightGrpcClient.ReserveSeatAsync(new ReserveSeatRequest()

View File

@ -3,6 +3,11 @@
"DatabaseOptions": { "DatabaseOptions": {
"DefaultConnection": "Server=db;Database=IdentityDB;User ID=sa;Password=@Aa123456" "DefaultConnection": "Server=db;Database=IdentityDB;User ID=sa;Password=@Aa123456"
}, },
"PersistMessageOptions": {
"Interval": 30,
"Enabled": true,
"ConnectionString": "Server=db;Database=PersistMessageDB;User ID=sa;Password=@Aa123456"
},
"RabbitMqOptions": { "RabbitMqOptions": {
"HostName": "rabbitmq", "HostName": "rabbitmq",
"ExchangeName": "identity", "ExchangeName": "identity",

View File

@ -31,7 +31,8 @@
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 30, "Interval": 30,
"Enabled": true "Enabled": true,
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -18,7 +18,8 @@
} }
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 1, "Interval": 30,
"Enabled": true "Enabled": true,
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
} }
} }

View File

@ -1,44 +0,0 @@
using System;
using BuildingBlocks.EFCore;
using BuildingBlocks.PersistMessageProcessor;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Identity.Data.Configurations;
public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage>
{
public void Configure(EntityTypeBuilder<PersistMessage> builder)
{
builder.ToTable("PersistMessage", AppDbContextBase.DefaultSchema);
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.IsRequired();
builder.Property(x => x.DeliveryType)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), v))
.IsRequired()
.IsUnicode(false);
builder.Property(x => x.DeliveryType)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), v))
.IsRequired()
.IsUnicode(false);
builder.Property(x => x.MessageStatus)
.HasMaxLength(50)
.HasConversion(
v => v.ToString(),
v => (MessageStatus)Enum.Parse(typeof(MessageStatus), v))
.IsRequired()
.IsUnicode(false);
}
}

View File

@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Identity.Data.Migrations namespace Identity.Data.Migrations
{ {
[DbContext(typeof(IdentityContext))] [DbContext(typeof(IdentityContext))]
[Migration("20221203211306_initial")] [Migration("20221206180831_initial")]
partial class initial partial class initial
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -25,43 +25,6 @@ namespace Identity.Data.Migrations
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Identity.Identity.Models.ApplicationUser", b => modelBuilder.Entity("Identity.Identity.Models.ApplicationUser", b =>
{ {
b.Property<long>("Id") b.Property<long>("Id")

View File

@ -60,25 +60,6 @@ namespace Identity.Data.Migrations
table.PrimaryKey("PK_AspNetUsers", x => x.Id); table.PrimaryKey("PK_AspNetUsers", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "PersistMessage",
schema: "dbo",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
DataType = table.Column<string>(type: "nvarchar(max)", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
RetryCount = table.Column<int>(type: "int", nullable: false),
MessageStatus = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false),
DeliveryType = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistMessage", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetRoleClaims", name: "AspNetRoleClaims",
schema: "dbo", schema: "dbo",
@ -266,10 +247,6 @@ namespace Identity.Data.Migrations
name: "AspNetUserTokens", name: "AspNetUserTokens",
schema: "dbo"); schema: "dbo");
migrationBuilder.DropTable(
name: "PersistMessage",
schema: "dbo");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetRoles", name: "AspNetRoles",
schema: "dbo"); schema: "dbo");

View File

@ -22,43 +22,6 @@ namespace Identity.Data.Migrations
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Identity.Identity.Models.ApplicationUser", b => modelBuilder.Entity("Identity.Identity.Models.ApplicationUser", b =>
{ {
b.Property<long>("Id") b.Property<long>("Id")

View File

@ -57,7 +57,7 @@ public static class InfrastructureExtensions
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddPersistMessageProcessor(); builder.Services.AddPersistMessageProcessor();
builder.Services.AddCustomDbContext<IdentityContext>(configuration); builder.Services.AddCustomDbContext<IdentityContext>();
builder.Services.AddScoped<IDataSeeder, IdentityDataSeeder>(); builder.Services.AddScoped<IDataSeeder, IdentityDataSeeder>();
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);
builder.Services.AddCustomSwagger(configuration, typeof(IdentityRoot).Assembly); builder.Services.AddCustomSwagger(configuration, typeof(IdentityRoot).Assembly);

View File

@ -16,6 +16,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Data\Configurations" />
<Folder Include="Data\Migrations" /> <Folder Include="Data\Migrations" />
</ItemGroup> </ItemGroup>

View File

@ -5,16 +5,14 @@ using FluentAssertions;
using Identity.Api; using Identity.Api;
using Identity.Data; using Identity.Data;
using Integration.Test.Fakes; using Integration.Test.Fakes;
using MassTransit;
using MassTransit.Testing;
using Xunit; using Xunit;
namespace Integration.Test.Identity.Features; namespace Integration.Test.Identity.Features;
public class RegisterNewUserTests : IntegrationTestBase<Program, IdentityContext> public class RegisterNewUserTests : IdentityIntegrationTestBase
{ {
public RegisterNewUserTests(IntegrationTestFixture<Program, IdentityContext> integrationTestFixture) : base( public RegisterNewUserTests(
integrationTestFixture) IntegrationTestFactory<Program, IdentityContext> integrationTestFactory) : base(integrationTestFactory)
{ {
} }
@ -31,6 +29,6 @@ public class RegisterNewUserTests : IntegrationTestBase<Program, IdentityContext
response?.Should().NotBeNull(); response?.Should().NotBeNull();
response?.Username.Should().Be(command.Username); response?.Username.Should().Be(command.Username);
await Fixture.WaitForPublishing<UserCreated>(); (await Fixture.WaitForPublishing<UserCreated>()).Should().Be(true);
} }
} }

View File

@ -0,0 +1,21 @@
using BuildingBlocks.TestBase;
using Identity.Api;
using Identity.Data;
using Xunit;
namespace Integration.Test;
[Collection(IntegrationTestCollection.Name)]
public class IdentityIntegrationTestBase: IntegrationTestBase<Program, IdentityContext>
{
public IdentityIntegrationTestBase(IntegrationTestFactory<Program, IdentityContext> integrationTestFactory)
: base(integrationTestFactory)
{
}
}
[CollectionDefinition(Name)]
public class IntegrationTestCollection : ICollectionFixture<IntegrationTestFactory<Program, IdentityContext>>
{
public const string Name = "Identity Integration Test";
}

View File

@ -3,6 +3,11 @@
"DatabaseOptions": { "DatabaseOptions": {
"DefaultConnection": "Server=db;Database=PassengerDB;User ID=sa;Password=@Aa123456" "DefaultConnection": "Server=db;Database=PassengerDB;User ID=sa;Password=@Aa123456"
}, },
"PersistMessageOptions": {
"Interval": 30,
"Enabled": true,
"ConnectionString": "Server=db;Database=PersistMessageDB;User ID=sa;Password=@Aa123456"
},
"Jwt": { "Jwt": {
"Authority": "https://localhost:5005", "Authority": "https://localhost:5005",
"Audience": "passenger-api" "Audience": "passenger-api"

View File

@ -35,7 +35,8 @@
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 30, "Interval": 30,
"Enabled": true "Enabled": true,
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -18,7 +18,8 @@
} }
}, },
"PersistMessageOptions": { "PersistMessageOptions": {
"Interval": 1, "Interval": 30,
"Enabled": true "Enabled": true,
"ConnectionString": "Server=.\\sqlexpress;Database=PersistMessageDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
} }
} }

View File

@ -12,7 +12,7 @@ using Passenger.Data;
namespace Passenger.Data.Migrations namespace Passenger.Data.Migrations
{ {
[DbContext(typeof(PassengerDbContext))] [DbContext(typeof(PassengerDbContext))]
[Migration("20221203211633_initial")] [Migration("20221206180929_initial")]
partial class initial partial class initial
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -25,43 +25,6 @@ namespace Passenger.Data.Migrations
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Passenger.Passengers.Models.Passenger", b => modelBuilder.Entity("Passenger.Passengers.Models.Passenger", b =>
{ {
b.Property<long>("Id") b.Property<long>("Id")

View File

@ -35,25 +35,6 @@ namespace Passenger.Data.Migrations
{ {
table.PrimaryKey("PK_Passenger", x => x.Id); table.PrimaryKey("PK_Passenger", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "PersistMessage",
schema: "dbo",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
DataType = table.Column<string>(type: "nvarchar(max)", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
RetryCount = table.Column<int>(type: "int", nullable: false),
MessageStatus = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false),
DeliveryType = table.Column<string>(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistMessage", x => x.Id);
});
} }
/// <inheritdoc /> /// <inheritdoc />
@ -62,10 +43,6 @@ namespace Passenger.Data.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Passenger", name: "Passenger",
schema: "dbo"); schema: "dbo");
migrationBuilder.DropTable(
name: "PersistMessage",
schema: "dbo");
} }
} }
} }

View File

@ -22,43 +22,6 @@ namespace Passenger.Data.Migrations
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BuildingBlocks.PersistMessageProcessor.PersistMessage", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasColumnType("nvarchar(max)");
b.Property<string>("DeliveryType")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<string>("MessageStatus")
.IsRequired()
.HasMaxLength(50)
.IsUnicode(false)
.HasColumnType("varchar(50)");
b.Property<int>("RetryCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PersistMessage", "dbo");
});
modelBuilder.Entity("Passenger.Passengers.Models.Passenger", b => modelBuilder.Entity("Passenger.Passengers.Models.Passenger", b =>
{ {
b.Property<long>("Id") b.Property<long>("Id")

View File

@ -1,15 +1,12 @@
using System.Reflection; using System.Reflection;
using BuildingBlocks.EFCore; using BuildingBlocks.EFCore;
using BuildingBlocks.Utils; using BuildingBlocks.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Passenger.Data; namespace Passenger.Data;
public sealed class PassengerDbContext : AppDbContextBase public sealed class PassengerDbContext : AppDbContextBase
{ {
public const string DefaultSchema = "dbo";
public PassengerDbContext(DbContextOptions<PassengerDbContext> options, ICurrentUserProvider currentUserProvider) : base(options, currentUserProvider) public PassengerDbContext(DbContextOptions<PassengerDbContext> options, ICurrentUserProvider currentUserProvider) : base(options, currentUserProvider)
{ {
} }

View File

@ -60,7 +60,7 @@ public static class InfrastructureExtensions
}); });
builder.Services.AddPersistMessageProcessor(); builder.Services.AddPersistMessageProcessor();
builder.Services.AddCustomDbContext<PassengerDbContext>(configuration); builder.Services.AddCustomDbContext<PassengerDbContext>();
builder.Services.AddMongoDbContext<PassengerReadDbContext>(configuration); builder.Services.AddMongoDbContext<PassengerReadDbContext>(configuration);
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);

View File

@ -9,7 +9,7 @@ public class FakeUserCreated : AutoFaker<UserCreated>
public FakeUserCreated() public FakeUserCreated()
{ {
RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId()); RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId());
RuleFor(r => r.Name, _ => "Meysam"); RuleFor(r => r.Name, _ => "Sam");
RuleFor(r => r.PassportNumber, _ => "1299878"); RuleFor(r => r.PassportNumber, _ => "123456789");
} }
} }

View File

@ -3,18 +3,16 @@ using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.TestBase; using BuildingBlocks.TestBase;
using FluentAssertions; using FluentAssertions;
using Integration.Test.Fakes; using Integration.Test.Fakes;
using MassTransit.Testing;
using Passenger.Api; using Passenger.Api;
using Passenger.Data; using Passenger.Data;
using Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1.Reads;
using Xunit; using Xunit;
namespace Integration.Test.Passenger.Features; namespace Integration.Test.Passenger.Features;
public class CompleteRegisterPassengerTests : IntegrationTestBase<Program, PassengerDbContext> public class CompleteRegisterPassengerTests : PassengerIntegrationTestBase
{ {
public CompleteRegisterPassengerTests(IntegrationTestFixture<Program, PassengerDbContext> integrationTestFixture) : public CompleteRegisterPassengerTests(
base(integrationTestFixture) IntegrationTestFactory<Program, PassengerDbContext> integrationTestFactory) : base(integrationTestFactory)
{ {
} }

View File

@ -1,11 +1,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.TestBase; using BuildingBlocks.TestBase;
using FluentAssertions; using FluentAssertions;
using Grpc.Net.Client;
using Integration.Test.Fakes; using Integration.Test.Fakes;
using MassTransit.Testing;
using Microsoft.Extensions.DependencyInjection;
using Passenger; using Passenger;
using Passenger.Api; using Passenger.Api;
using Passenger.Data; using Passenger.Data;
@ -14,17 +10,13 @@ using Xunit;
namespace Integration.Test.Passenger.Features; namespace Integration.Test.Passenger.Features;
public class GetPassengerByIdTests : IntegrationTestBase<Program, PassengerDbContext> public class GetPassengerByIdTests : PassengerIntegrationTestBase
{ {
private readonly GrpcChannel _channel; public GetPassengerByIdTests(
IntegrationTestFactory<Program, PassengerDbContext> integrationTestFactory) : base(integrationTestFactory)
public GetPassengerByIdTests(IntegrationTestFixture<Program, PassengerDbContext> integrationTestFixture) : base(
integrationTestFixture)
{ {
_channel = Fixture.Channel;
} }
[Fact] [Fact]
public async Task should_retrive_a_passenger_by_id_currectly() public async Task should_retrive_a_passenger_by_id_currectly()
{ {
@ -53,7 +45,7 @@ public class GetPassengerByIdTests : IntegrationTestBase<Program, PassengerDbCon
var passengerEntity = FakePassengerCreated.Generate(userCreated); var passengerEntity = FakePassengerCreated.Generate(userCreated);
await Fixture.InsertAsync(passengerEntity); await Fixture.InsertAsync(passengerEntity);
var passengerGrpcClient = new PassengerGrpcService.PassengerGrpcServiceClient(_channel); var passengerGrpcClient = new PassengerGrpcService.PassengerGrpcServiceClient(Fixture.Channel);
// Act // Act
var response = await passengerGrpcClient.GetByIdAsync(new GetByIdRequest {Id = passengerEntity.Id}); var response = await passengerGrpcClient.GetByIdAsync(new GetByIdRequest {Id = passengerEntity.Id});

View File

@ -0,0 +1,21 @@
using BuildingBlocks.TestBase;
using Passenger.Api;
using Passenger.Data;
using Xunit;
namespace Integration.Test;
[Collection(IntegrationTestCollection.Name)]
public class PassengerIntegrationTestBase: IntegrationTestBase<Program, PassengerDbContext>
{
public PassengerIntegrationTestBase(IntegrationTestFactory<Program, PassengerDbContext> integrationTestFactory)
: base(integrationTestFactory)
{
}
}
[CollectionDefinition(Name)]
public class IntegrationTestCollection : ICollectionFixture<IntegrationTestFactory<Program, PassengerDbContext>>
{
public const string Name = "Passenger Integration Test";
}