mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-14 04:28:38 +08:00
AddMassTransitTestHarness with Rabbitmq
This commit is contained in:
parent
7213b37db3
commit
ff65f5daeb
@ -7,114 +7,117 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Ardalis.GuardClauses" Version="3.3.0"/>
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.SQLite.Storage" Version="5.0.1"/>
|
||||
<PackageReference Include="Ben.BlockingDetector" Version="0.0.4"/>
|
||||
<PackageReference Include="EasyCaching.Core" Version="1.4.1"/>
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.4.1"/>
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="6.0.0"/>
|
||||
<PackageReference Include="EntityFrameworkCore.Triggered" Version="3.0.0"/>
|
||||
<PackageReference Include="Figgle" Version="0.4.0"/>
|
||||
<PackageReference Include="FluentValidation" Version="10.3.6"/>
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="10.3.6"/>
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.44.0"/>
|
||||
<PackageReference Include="MagicOnion" Version="4.4.0"/>
|
||||
<PackageReference Include="MagicOnion.Abstractions" Version="4.4.0"/>
|
||||
<PackageReference Include="MagicOnion.Client" Version="4.4.0"/>
|
||||
<PackageReference Include="MagicOnion.Server" Version="4.4.0"/>
|
||||
<PackageReference Include="MagicOnion.Server" Version="4.4.0"/>
|
||||
<PackageReference Include="Polly" Version="7.2.3"/>
|
||||
<PackageReference Include="Ardalis.GuardClauses" Version="3.3.0" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.SQLite.Storage" Version="5.0.1" />
|
||||
<PackageReference Include="Ben.BlockingDetector" Version="0.0.4" />
|
||||
<PackageReference Include="EasyCaching.Core" Version="1.4.1" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.4.1" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
|
||||
<PackageReference Include="EntityFrameworkCore.Triggered" Version="3.0.0" />
|
||||
<PackageReference Include="Figgle" Version="0.4.0" />
|
||||
<PackageReference Include="FluentValidation" Version="10.3.6" />
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="10.3.6" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.44.0" />
|
||||
<PackageReference Include="MagicOnion" Version="4.4.0" />
|
||||
<PackageReference Include="MagicOnion.Abstractions" Version="4.4.0" />
|
||||
<PackageReference Include="MagicOnion.Client" Version="4.4.0" />
|
||||
<PackageReference Include="MagicOnion.Server" Version="4.4.0" />
|
||||
<PackageReference Include="MagicOnion.Server" Version="4.4.0" />
|
||||
<PackageReference Include="MartinCostello.Logging.XUnit" Version="0.2.0" />
|
||||
<PackageReference Include="NSubstitute" Version="4.3.0" />
|
||||
<PackageReference Include="Polly" Version="7.2.3" />
|
||||
<PackageReference Include="protobuf-net.BuildTools" Version="3.0.115">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="protobuf-net.Grpc" Version="1.0.152"/>
|
||||
<PackageReference Include="Hellang.Middleware.ProblemDetails" Version="6.3.0"/>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1"/>
|
||||
<PackageReference Include="IdGen" Version="3.0.0"/>
|
||||
<PackageReference Include="Mapster" Version="7.3.0"/>
|
||||
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.0"/>
|
||||
<PackageReference Include="MediatR" Version="9.0.0"/>
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.1"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="6.0.1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0"/>
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.14.1"/>
|
||||
<PackageReference Include="Moq" Version="4.16.1"/>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
|
||||
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2"/>
|
||||
<PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152"/>
|
||||
<PackageReference Include="Scrutor" Version="3.3.0"/>
|
||||
<PackageReference Include="Scrutor.AspNetCore" Version="3.3.0"/>
|
||||
<PackageReference Include="Serilog" Version="2.10.0"/>
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0"/>
|
||||
<PackageReference Include="Serilog.Enrichers.Span" Version="2.2.0"/>
|
||||
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="8.4.1"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1"/>
|
||||
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="8.4.1"/>
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.0"/>
|
||||
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.1.1"/>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.2.3"/>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.2.3"/>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.2.3"/>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3"/>
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="5.0.1"/>
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="5.0.1"/>
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="5.0.1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="6.0.1"/>
|
||||
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="6.0.1"/>
|
||||
<PackageReference Include="System.Interactive.Async" Version="5.1.0"/>
|
||||
<PackageReference Include="MassTransit" Version="8.0.0"/>
|
||||
<PackageReference Include="MassTransit.RabbitMQ" Version="8.0.0"/>
|
||||
<PackageReference Include="DotNetCore.CAP" Version="6.0.0"/>
|
||||
<PackageReference Include="DotNetCore.CAP.Dashboard" Version="6.0.0"/>
|
||||
<PackageReference Include="DotNetCore.CAP.MongoDB" Version="6.0.0"/>
|
||||
<PackageReference Include="DotNetCore.CAP.OpenTelemetry" Version="6.0.0"/>
|
||||
<PackageReference Include="DotNetCore.CAP.RabbitMQ" Version="6.0.0"/>
|
||||
<PackageReference Include="DotNetCore.CAP.SqlServer" Version="6.0.0"/>
|
||||
<PackageReference Include="Duende.IdentityServer" Version="6.0.0"/>
|
||||
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="6.0.0"/>
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="6.0.0"/>
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="6.0.3"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.0"/>
|
||||
<PackageReference Include="protobuf-net.Grpc" Version="1.0.152" />
|
||||
<PackageReference Include="Hellang.Middleware.ProblemDetails" Version="6.3.0" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="IdGen" Version="3.0.0" />
|
||||
<PackageReference Include="Mapster" Version="7.3.0" />
|
||||
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.0" />
|
||||
<PackageReference Include="MediatR" Version="9.0.0" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.14.1" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
|
||||
<PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" />
|
||||
<PackageReference Include="Scrutor" Version="3.3.0" />
|
||||
<PackageReference Include="Scrutor.AspNetCore" Version="3.3.0" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Span" Version="2.2.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="8.4.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="8.4.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Xunit2" Version="1.1.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.2.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.2.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.2.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="5.0.1" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="5.0.1" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="5.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="6.0.1" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="6.0.1" />
|
||||
<PackageReference Include="System.Interactive.Async" Version="5.1.0" />
|
||||
<PackageReference Include="MassTransit" Version="8.0.0" />
|
||||
<PackageReference Include="MassTransit.RabbitMQ" Version="8.0.0" />
|
||||
<PackageReference Include="DotNetCore.CAP" Version="6.0.0" />
|
||||
<PackageReference Include="DotNetCore.CAP.Dashboard" Version="6.0.0" />
|
||||
<PackageReference Include="DotNetCore.CAP.MongoDB" Version="6.0.0" />
|
||||
<PackageReference Include="DotNetCore.CAP.OpenTelemetry" Version="6.0.0" />
|
||||
<PackageReference Include="DotNetCore.CAP.RabbitMQ" Version="6.0.0" />
|
||||
<PackageReference Include="DotNetCore.CAP.SqlServer" Version="6.0.0" />
|
||||
<PackageReference Include="Duende.IdentityServer" Version="6.0.0" />
|
||||
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="6.0.0" />
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="6.0.0" />
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.0" />
|
||||
|
||||
<PackageReference Include="Jaeger" Version="0.3.7"/>
|
||||
<PackageReference Include="OpenTracing" Version="0.12.1"/>
|
||||
<PackageReference Include="prometheus-net" Version="6.0.0"/>
|
||||
<PackageReference Include="prometheus-net.AspNetCore" Version="6.0.0"/>
|
||||
<PackageReference Include="Jaeger" Version="0.3.7" />
|
||||
<PackageReference Include="OpenTracing" Version="0.12.1" />
|
||||
<PackageReference Include="prometheus-net" Version="6.0.0" />
|
||||
<PackageReference Include="prometheus-net.AspNetCore" Version="6.0.0" />
|
||||
|
||||
<PackageReference Include="OpenTelemetry" Version="1.2.0-rc3"/>
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Jaeger" Version="1.2.0-rc3"/>
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9"/>
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9"/>
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9"/>
|
||||
<PackageReference Include="OpenTelemetry" Version="1.2.0-rc3" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Jaeger" Version="1.2.0-rc3" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9" />
|
||||
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.0.64">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="22.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1"/>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.3"/>
|
||||
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="22.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.3" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Contracts"/>
|
||||
<Folder Include="EventStoreDB\BackgroundWorkers"/>
|
||||
<Folder Include="Contracts" />
|
||||
<Folder Include="EventStoreDB\BackgroundWorkers" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Unicode;
|
||||
using BuildingBlocks.Utils;
|
||||
using BuildingBlocks.Web;
|
||||
using DotNetCore.CAP;
|
||||
using DotNetCore.CAP.Messages;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
namespace BuildingBlocks.Domain.Model;
|
||||
|
||||
public interface IAuditable
|
||||
public interface IAuditable : IEntity
|
||||
{
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
public long? CreatedBy { get; set; }
|
||||
|
||||
@ -2,7 +2,6 @@ namespace BuildingBlocks.Domain.Model;
|
||||
|
||||
public interface IEntity
|
||||
{
|
||||
long Id { get; }
|
||||
}
|
||||
|
||||
public interface IEntity<out TId>
|
||||
|
||||
@ -44,7 +44,7 @@ public abstract class AppDbContextBase : DbContext, IDbContext
|
||||
await SaveChangesAsync(cancellationToken);
|
||||
await _currentTransaction?.CommitAsync(cancellationToken)!;
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
catch
|
||||
{
|
||||
await RollbackTransactionAsync(cancellationToken);
|
||||
throw;
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@ -9,16 +8,15 @@ public static class Extensions
|
||||
{
|
||||
public static IServiceCollection AddCustomDbContext<TContext>(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration,
|
||||
Assembly migrationAssembly)
|
||||
IConfiguration configuration)
|
||||
where TContext : AppDbContextBase
|
||||
{
|
||||
services.AddScoped<IDbContext>(provider => provider.GetService<TContext>());
|
||||
|
||||
services.AddDbContext<TContext>(options =>
|
||||
options.UseSqlServer(
|
||||
configuration.GetConnectionString("DefaultConnection"),
|
||||
x => x.MigrationsAssembly(migrationAssembly.GetName().Name)));
|
||||
x => x.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name)));
|
||||
|
||||
services.AddScoped<IDbContext>(provider => provider.GetService<TContext>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using BuildingBlocks.Utils;
|
||||
using BuildingBlocks.Web;
|
||||
using Duende.IdentityServer.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Reflection;
|
||||
using BuildingBlocks.Domain.Event;
|
||||
using BuildingBlocks.Utils;
|
||||
using BuildingBlocks.Web;
|
||||
using Humanizer;
|
||||
using MassTransit;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace BuildingBlocks.Utils
|
||||
{
|
||||
//https://stackoverflow.com/a/19621488/581476
|
||||
public static class EnumExtensions
|
||||
{
|
||||
// This extension method is broken out so you can use a similar pattern with
|
||||
// other MetaData elements in the future. This is your base method for each.
|
||||
public static T GetAttribute<T>(this Enum value) where T : Attribute {
|
||||
var type = value.GetType();
|
||||
var memberInfo = type.GetMember(value.ToString());
|
||||
var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false);
|
||||
return attributes.Length > 0
|
||||
? (T)attributes[0]
|
||||
: null;
|
||||
}
|
||||
|
||||
// This method creates a specific call to the above method, requesting the
|
||||
// Description MetaData attribute.
|
||||
public static string ToName(this Enum value) {
|
||||
var attribute = value.GetAttribute<DescriptionAttribute>();
|
||||
return attribute == null ? value.ToString() : attribute.Description;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace BuildingBlocks.Utils
|
||||
{
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
public static string GetQueryString(this object obj)
|
||||
{
|
||||
var properties = from p in obj.GetType().GetProperties()
|
||||
where p.GetValue(obj, null) != null
|
||||
select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString());
|
||||
|
||||
return String.Join("&", properties.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BuildingBlocks.Utils
|
||||
{
|
||||
public static class ReflectionHelpers
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Type, string> TypeCacheKeys = new();
|
||||
private static readonly ConcurrentDictionary<Type, string> PrettyPrintCache = new();
|
||||
|
||||
public static string GetCacheKey(this Type type)
|
||||
{
|
||||
return TypeCacheKeys.GetOrAdd(type, t => $"{t.PrettyPrint()}");
|
||||
}
|
||||
public static string PrettyPrint(this Type type)
|
||||
{
|
||||
return PrettyPrintCache.GetOrAdd(
|
||||
type,
|
||||
t =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return PrettyPrintRecursive(t, 0);
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return t.Name;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static bool IsActionDelegate(this Type sourceType)
|
||||
{
|
||||
if (sourceType.IsSubclassOf(typeof(MulticastDelegate)) &&
|
||||
sourceType.GetMethod("Invoke").ReturnType == typeof(void))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
private static string PrettyPrintRecursive(Type type, int depth)
|
||||
{
|
||||
if (depth > 3)
|
||||
{
|
||||
return type.Name;
|
||||
}
|
||||
|
||||
var nameParts = type.Name.Split('`');
|
||||
if (nameParts.Length == 1)
|
||||
{
|
||||
return nameParts[0];
|
||||
}
|
||||
|
||||
var genericArguments = type.GetTypeInfo().GetGenericArguments();
|
||||
return !type.IsConstructedGenericType
|
||||
? $"{nameParts[0]}<{new string(',', genericArguments.Length - 1)}>"
|
||||
: $"{nameParts[0]}<{string.Join(",", genericArguments.Select(t => PrettyPrintRecursive(t, depth + 1)))}>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace BuildingBlocks.Web;
|
||||
namespace BuildingBlocks.Utils;
|
||||
|
||||
public class SlugifyParameterTransformer : IOutboundParameterTransformer
|
||||
{
|
||||
34
src/BuildingBlocks/Web/ApiVersioningExtensions.cs
Normal file
34
src/BuildingBlocks/Web/ApiVersioningExtensions.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Versioning;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BuildingBlocks.Web;
|
||||
|
||||
public static class ApiVersioningExtensions
|
||||
{
|
||||
public static void AddCustomVersioning(this IServiceCollection services,
|
||||
Action<ApiVersioningOptions> configurator = null)
|
||||
{
|
||||
//https://www.meziantou.net/versioning-an-asp-net-core-api.htm
|
||||
//https://exceptionnotfound.net/overview-of-api-versioning-in-asp-net-core-3-0/
|
||||
services.AddApiVersioning(options =>
|
||||
{
|
||||
// Add the headers "api-supported-versions" and "api-deprecated-versions"
|
||||
// This is better for discoverability
|
||||
options.ReportApiVersions = true;
|
||||
|
||||
// AssumeDefaultVersionWhenUnspecified should only be enabled when supporting legacy services that did not previously
|
||||
// support API versioning. Forcing existing clients to specify an explicit API version for an
|
||||
// existing service introduces a breaking change. Conceptually, clients in this situation are
|
||||
// bound to some API version of a service, but they don't know what it is and never explicit request it.
|
||||
options.AssumeDefaultVersionWhenUnspecified = true;
|
||||
options.DefaultApiVersion = new ApiVersion(1, 0);
|
||||
|
||||
// // Defines how an API version is read from the current HTTP request
|
||||
options.ApiVersionReader = ApiVersionReader.Combine(new HeaderApiVersionReader("api-version"),
|
||||
new UrlSegmentApiVersionReader());
|
||||
|
||||
configurator?.Invoke(options);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BuildingBlocks.Utils;
|
||||
namespace BuildingBlocks.Web;
|
||||
|
||||
public static class ConfigurationExtensions
|
||||
{
|
||||
@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace BuildingBlocks.Utils
|
||||
namespace BuildingBlocks.Web
|
||||
{
|
||||
public static class ConfigurationHelper
|
||||
{
|
||||
@ -17,4 +17,4 @@ namespace BuildingBlocks.Utils
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/BuildingBlocks/Web/CorrelationExtensions.cs
Normal file
26
src/BuildingBlocks/Web/CorrelationExtensions.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BuildingBlocks.Web;
|
||||
|
||||
public static class CorrelationExtensions
|
||||
{
|
||||
private const string CorrelationId = "correlationId";
|
||||
|
||||
public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder app)
|
||||
{
|
||||
return app.Use(async (ctx, next) =>
|
||||
{
|
||||
if (!ctx.Request.Headers.TryGetValue(CorrelationId, out var correlationId))
|
||||
correlationId = Guid.NewGuid().ToString("N");
|
||||
|
||||
ctx.Items[CorrelationId] = correlationId.ToString();
|
||||
await next();
|
||||
});
|
||||
}
|
||||
|
||||
public static string GetCorrelationId(this HttpContext context)
|
||||
{
|
||||
return context.Items.TryGetValue(CorrelationId, out var correlationId) ? correlationId as string : null;
|
||||
}
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Versioning;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BuildingBlocks.Web
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
private const string CorrelationId = "correlationId";
|
||||
|
||||
public static void AddCustomVersioning(this IServiceCollection services,
|
||||
Action<ApiVersioningOptions> configurator = null)
|
||||
{
|
||||
//https://www.meziantou.net/versioning-an-asp-net-core-api.htm
|
||||
//https://exceptionnotfound.net/overview-of-api-versioning-in-asp-net-core-3-0/
|
||||
services.AddApiVersioning(options =>
|
||||
{
|
||||
// Add the headers "api-supported-versions" and "api-deprecated-versions"
|
||||
// This is better for discoverability
|
||||
options.ReportApiVersions = true;
|
||||
|
||||
// AssumeDefaultVersionWhenUnspecified should only be enabled when supporting legacy services that did not previously
|
||||
// support API versioning. Forcing existing clients to specify an explicit API version for an
|
||||
// existing service introduces a breaking change. Conceptually, clients in this situation are
|
||||
// bound to some API version of a service, but they don't know what it is and never explicit request it.
|
||||
options.AssumeDefaultVersionWhenUnspecified = true;
|
||||
options.DefaultApiVersion = new ApiVersion(1, 0);
|
||||
|
||||
// // Defines how an API version is read from the current HTTP request
|
||||
options.ApiVersionReader = ApiVersionReader.Combine(new HeaderApiVersionReader("api-version"),
|
||||
new UrlSegmentApiVersionReader());
|
||||
|
||||
configurator?.Invoke(options);
|
||||
});
|
||||
}
|
||||
|
||||
public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder app)
|
||||
=> app.Use(async (ctx, next) =>
|
||||
{
|
||||
if (!ctx.Request.Headers.TryGetValue(CorrelationId, out var correlationId))
|
||||
{
|
||||
correlationId = Guid.NewGuid().ToString("N");
|
||||
}
|
||||
|
||||
ctx.Items[CorrelationId] = correlationId.ToString();
|
||||
await next();
|
||||
});
|
||||
|
||||
public static string GetCorrelationId(this HttpContext context)
|
||||
{
|
||||
return context.Items.TryGetValue(CorrelationId, out var correlationId) ? correlationId as string : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/BuildingBlocks/Web/ServiceCollectionExtensions.cs
Normal file
60
src/BuildingBlocks/Web/ServiceCollectionExtensions.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BuildingBlocks.Web;
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static void ReplaceScoped<TService, TImplementation>(this IServiceCollection services)
|
||||
where TService : class
|
||||
where TImplementation : class, TService
|
||||
{
|
||||
services.Unregister<TService>();
|
||||
services.AddScoped<TService, TImplementation>();
|
||||
}
|
||||
|
||||
public static void ReplaceScoped<TService>(this IServiceCollection services,
|
||||
Func<IServiceProvider, TService> implementationFactory)
|
||||
where TService : class
|
||||
{
|
||||
services.Unregister<TService>();
|
||||
services.AddScoped(implementationFactory);
|
||||
}
|
||||
|
||||
public static void ReplaceTransient<TService, TImplementation>(this IServiceCollection services)
|
||||
where TService : class
|
||||
where TImplementation : class, TService
|
||||
{
|
||||
services.Unregister<TService>();
|
||||
services.AddTransient<TService, TImplementation>();
|
||||
}
|
||||
|
||||
public static void ReplaceTransient<TService>(this IServiceCollection services,
|
||||
Func<IServiceProvider, TService> implementationFactory)
|
||||
where TService : class
|
||||
{
|
||||
services.Unregister<TService>();
|
||||
services.AddTransient(implementationFactory);
|
||||
}
|
||||
|
||||
public static void ReplaceSingleton<TService, TImplementation>(this IServiceCollection services)
|
||||
where TService : class
|
||||
where TImplementation : class, TService
|
||||
{
|
||||
services.Unregister<TService>();
|
||||
services.AddSingleton<TService, TImplementation>();
|
||||
}
|
||||
|
||||
public static void ReplaceSingleton<TService>(this IServiceCollection services,
|
||||
Func<IServiceProvider, TService> implementationFactory)
|
||||
where TService : class
|
||||
{
|
||||
services.Unregister<TService>();
|
||||
services.AddSingleton(implementationFactory);
|
||||
}
|
||||
|
||||
public static void Unregister<TService>(this IServiceCollection services)
|
||||
{
|
||||
var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(TService));
|
||||
services.Remove(descriptor);
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,7 @@ builder.Services.Configure<GrpcOptions>(options => configuration.GetSection("Grp
|
||||
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
|
||||
|
||||
builder.Services.AddTransient<IBusPublisher, BusPublisher>();
|
||||
builder.Services.AddCustomDbContext<BookingDbContext>(configuration, typeof(BookingRoot).Assembly);
|
||||
builder.Services.AddCustomDbContext<BookingDbContext>(configuration);
|
||||
|
||||
builder.AddCustomSerilog();
|
||||
builder.Services.AddJwt();
|
||||
|
||||
@ -8,10 +8,8 @@ using BuildingBlocks.Jwt;
|
||||
using BuildingBlocks.Logging;
|
||||
using BuildingBlocks.Mapster;
|
||||
using BuildingBlocks.MassTransit;
|
||||
using BuildingBlocks.Mongo;
|
||||
using BuildingBlocks.OpenTelemetry;
|
||||
using BuildingBlocks.Swagger;
|
||||
using BuildingBlocks.Utils;
|
||||
using BuildingBlocks.Web;
|
||||
using Figgle;
|
||||
using Flight;
|
||||
@ -32,12 +30,10 @@ var env = builder.Environment;
|
||||
var appOptions = builder.Services.GetOptions<AppOptions>("AppOptions");
|
||||
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
|
||||
|
||||
|
||||
builder.Services.AddTransient<IBusPublisher, BusPublisher>();
|
||||
builder.Services.AddCustomDbContext<FlightDbContext>(configuration, typeof(FlightRoot).Assembly);
|
||||
builder.Services.AddMongoDbContext<FlightReadDbContext>(configuration);
|
||||
|
||||
builder.Services.AddCustomDbContext<FlightDbContext>(configuration);
|
||||
builder.Services.AddScoped<IDataSeeder, FlightDataSeeder>();
|
||||
|
||||
builder.AddCustomSerilog();
|
||||
builder.Services.AddJwt();
|
||||
builder.Services.AddControllers();
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=.\\sqlexpress;Database=FlightDB;Trusted_Connection=True;MultipleActiveResultSets=true"
|
||||
"DefaultConnection": "Server=.\\sqlexpress;Database=FlightDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true"
|
||||
},
|
||||
"RabbitMq": {
|
||||
"HostName": "rabbitmq",
|
||||
"HostName": "localhost",
|
||||
"ExchangeName": "flight",
|
||||
"UserName": "guest",
|
||||
"Password": "guest"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Microsoft": "Debug",
|
||||
"Microsoft.Hosting.Lifetime": "Debug",
|
||||
"Microsoft.EntityFrameworkCore.Database.Command": "Debug"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,23 +6,23 @@ using Flight.Seats.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Flight.Data
|
||||
namespace Flight.Data;
|
||||
|
||||
public sealed class FlightDbContext : AppDbContextBase
|
||||
{
|
||||
public sealed class FlightDbContext : AppDbContextBase
|
||||
public FlightDbContext(DbContextOptions<FlightDbContext> options, IHttpContextAccessor httpContextAccessor) : base(
|
||||
options, httpContextAccessor)
|
||||
{
|
||||
public FlightDbContext(DbContextOptions<FlightDbContext> options, IHttpContextAccessor httpContextAccessor) : base(options, httpContextAccessor)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public DbSet<Flights.Models.Flight> Flights => Set<Flights.Models.Flight>();
|
||||
public DbSet<Airport> Airports => Set<Airport>();
|
||||
public DbSet<Aircraft> Aircraft => Set<Aircraft>();
|
||||
public DbSet<Seat> Seats => Set<Seat>();
|
||||
public DbSet<Flights.Models.Flight> Flights => Set<Flights.Models.Flight>();
|
||||
public DbSet<Airport> Airports => Set<Airport>();
|
||||
public DbSet<Aircraft> Aircraft => Set<Aircraft>();
|
||||
public DbSet<Seat> Seats => Set<Seat>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
|
||||
base.OnModelCreating(builder);
|
||||
}
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
|
||||
base.OnModelCreating(builder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
namespace Flight.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(FlightDbContext))]
|
||||
[Migration("20220509213249_Init")]
|
||||
[Migration("20220511215248_Init")]
|
||||
partial class Init
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
@ -1,4 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using Flight.Airports.Models;
|
||||
using Flight.Flights.Features.GetFlightById;
|
||||
using MassTransit.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Integration.Test;
|
||||
@ -8,12 +12,16 @@ public class DeleteTests
|
||||
{
|
||||
private readonly TestFixture _fixture;
|
||||
|
||||
public DeleteTests(TestFixture fixture) => _fixture = fixture;
|
||||
|
||||
[Fact]
|
||||
public Task Should_delete_flight()
|
||||
public DeleteTests(TestFixture fixture)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_get_flight()
|
||||
{
|
||||
var query = new GetFlightByIdQuery(1);
|
||||
var flight = await _fixture.SendAsync(query);
|
||||
var airport = await _fixture.FindAsync<Airport>(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,21 +1,24 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Domain.Model;
|
||||
using BuildingBlocks.EFCore;
|
||||
using BuildingBlocks.MassTransit;
|
||||
using BuildingBlocks.Web;
|
||||
using Flight.Data;
|
||||
using Flight.Data.Seed;
|
||||
using MassTransit;
|
||||
using MassTransit.Testing;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NSubstitute;
|
||||
using Respawn;
|
||||
using Xunit;
|
||||
|
||||
@ -26,114 +29,84 @@ public class TestFixtureCollection : ICollectionFixture<TestFixture>
|
||||
{
|
||||
}
|
||||
|
||||
// ref: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0
|
||||
// ref: https://github.com/jbogard/ContosoUniversityDotNetCore-Pages/blob/master/ContosoUniversity.IntegrationTests/SliceFixture.cs
|
||||
// ref: https://github.com/MassTransit/MassTransit/blob/00d6992286911a437b63b93c89a56e920b053c11/src/MassTransit.TestFramework/InMemoryTestFixture.cs
|
||||
// ref: https://wrapt.dev/blog/building-an-event-driven-dotnet-application-integration-testing
|
||||
// ref: https://github.com/jasontaylordev/CleanArchitecture/blob/main/tests/Application.IntegrationTests/Testing.cs
|
||||
public class TestFixture : IAsyncLifetime
|
||||
{
|
||||
private readonly Checkpoint _checkpoint;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private static InMemoryTestHarness _harness;
|
||||
private Checkpoint _checkpoint;
|
||||
private HttpClient _client;
|
||||
private IConfiguration _configuration;
|
||||
private WebApplicationFactory<Program> _factory;
|
||||
private ITestHarness _harness;
|
||||
private IServiceScopeFactory _scopeFactory;
|
||||
|
||||
public TestFixture()
|
||||
public ILogger<TestFixture> Logger =>
|
||||
_scopeFactory.CreateScope().ServiceProvider.GetRequiredService<ILogger<TestFixture>>();
|
||||
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_factory = FlightTestApplicationFactory();
|
||||
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "test");
|
||||
|
||||
_factory = new WebApplicationFactory<Program>()
|
||||
.WithWebHostBuilder(builder => builder.ConfigureServices(services =>
|
||||
{
|
||||
services.RemoveAll(typeof(IHostedService));
|
||||
services.ReplaceSingleton(AddHttpContextAccessorMock);
|
||||
services.ReplaceScoped<IDataSeeder, FlightDataSeeder>();
|
||||
services.AddMassTransitTestHarness(x =>
|
||||
{
|
||||
x.UsingRabbitMq((context, cfg) =>
|
||||
{
|
||||
var rabbitMqOptions = services.GetOptions<RabbitMqOptions>("RabbitMq");
|
||||
var host = rabbitMqOptions.HostName;
|
||||
|
||||
cfg.Host(host, h =>
|
||||
{
|
||||
h.Username(rabbitMqOptions.UserName);
|
||||
h.Password(rabbitMqOptions.Password);
|
||||
});
|
||||
cfg.ConfigureEndpoints(context);
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
_harness = _factory.Services.GetTestHarness();
|
||||
|
||||
await _harness.Start();
|
||||
|
||||
_configuration = _factory.Services.GetRequiredService<IConfiguration>();
|
||||
_scopeFactory = _factory.Services.GetRequiredService<IServiceScopeFactory>();
|
||||
|
||||
_checkpoint = new Checkpoint();
|
||||
}
|
||||
_client = _factory.CreateClient(new WebApplicationFactoryClientOptions {AllowAutoRedirect = false});
|
||||
|
||||
public WebApplicationFactory<Program> FlightTestApplicationFactory()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "test");
|
||||
_checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
|
||||
|
||||
return new WebApplicationFactory<Program>()
|
||||
.WithWebHostBuilder(builder =>
|
||||
{
|
||||
builder.ConfigureTestServices((services) =>
|
||||
{
|
||||
services.RemoveAll(typeof(IHostedService));
|
||||
});
|
||||
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
builder.ConfigureLogging(logging =>
|
||||
{
|
||||
logging.ClearProviders(); // Remove other loggers
|
||||
});
|
||||
|
||||
var httpContextAccessorService = services.FirstOrDefault(d =>
|
||||
d.ServiceType == typeof(IHttpContextAccessor));
|
||||
|
||||
services.Remove(httpContextAccessorService);
|
||||
services.AddSingleton(_ => Mock.Of<IHttpContextAccessor>());
|
||||
|
||||
services.AddScoped<InMemoryTestHarness>();
|
||||
var provider = services.BuildServiceProvider();
|
||||
var serviceScopeFactory = provider.GetService<IServiceScopeFactory>();
|
||||
|
||||
// MassTransit Start Setup -- Do Not Delete Comment
|
||||
_harness = serviceScopeFactory?.CreateScope().ServiceProvider.GetRequiredService<InMemoryTestHarness>();
|
||||
_harness?.Start().GetAwaiter().GetResult();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
return _checkpoint.Reset(_configuration.GetConnectionString("DefaultConnection"));
|
||||
await EnsureDatabaseAsync();
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
await _harness.Stop();
|
||||
_harness.Cancel();
|
||||
await _factory.DisposeAsync();
|
||||
await _checkpoint.Reset(_configuration.GetConnectionString("DefaultConnection"));
|
||||
}
|
||||
|
||||
|
||||
public async Task ExecuteScopeAsync(Func<IServiceProvider, Task> action)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<FlightDbContext>();
|
||||
|
||||
try
|
||||
{
|
||||
await dbContext.BeginTransactionAsync();
|
||||
|
||||
await action(scope.ServiceProvider);
|
||||
|
||||
await dbContext.CommitTransactionAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await dbContext.RollbackTransactionAsync();
|
||||
throw;
|
||||
}
|
||||
await action(scope.ServiceProvider);
|
||||
}
|
||||
|
||||
public async Task<T> ExecuteScopeAsync<T>(Func<IServiceProvider, Task<T>> action)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<FlightDbContext>();
|
||||
|
||||
try
|
||||
{
|
||||
await dbContext.BeginTransactionAsync();
|
||||
var result = await action(scope.ServiceProvider);
|
||||
|
||||
var result = await action(scope.ServiceProvider);
|
||||
|
||||
await dbContext.CommitTransactionAsync();
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await dbContext.RollbackTransactionAsync();
|
||||
throw;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task ExecuteDbContextAsync(Func<FlightDbContext, Task> action)
|
||||
@ -171,6 +144,7 @@ public class TestFixture : IAsyncLifetime
|
||||
return ExecuteDbContextAsync(db =>
|
||||
{
|
||||
foreach (var entity in entities) db.Set<T>().Add(entity);
|
||||
|
||||
return db.SaveChangesAsync();
|
||||
});
|
||||
}
|
||||
@ -231,7 +205,7 @@ public class TestFixture : IAsyncLifetime
|
||||
});
|
||||
}
|
||||
|
||||
public Task<T> FindAsync<T>(int id)
|
||||
public Task<T> FindAsync<T>(long id)
|
||||
where T : class, IEntity
|
||||
{
|
||||
return ExecuteDbContextAsync(db => db.Set<T>().FindAsync(id).AsTask());
|
||||
@ -258,13 +232,15 @@ public class TestFixture : IAsyncLifetime
|
||||
}
|
||||
|
||||
|
||||
// MassTransit Methods -- Do Not Delete Comment
|
||||
// ref: https://github.com/MassTransit/MassTransit/blob/00d6992286911a437b63b93c89a56e920b053c11/src/MassTransit.TestFramework/InMemoryTestFixture.cs
|
||||
// ref: https://wrapt.dev/blog/building-an-event-driven-dotnet-application-integration-testing
|
||||
|
||||
/// <summary>
|
||||
/// Publishes a message to the bus, and waits for the specified response.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that should be published.</param>
|
||||
/// <typeparam name="TMessage">The message that should be published.</typeparam>
|
||||
public static async Task PublishMessage<TMessage>(object message)
|
||||
public async Task PublishMessage<TMessage>(object message)
|
||||
where TMessage : class
|
||||
{
|
||||
await _harness.Bus.Publish<TMessage>(message);
|
||||
@ -317,4 +293,34 @@ public class TestFixture : IAsyncLifetime
|
||||
var consumerHarness = scope.ServiceProvider.GetRequiredService<IConsumerTestHarness<TConsumedBy>>();
|
||||
return consumerHarness.Consumed.Any<TMessage>();
|
||||
}
|
||||
|
||||
private async Task EnsureDatabaseAsync()
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
|
||||
var context = scope.ServiceProvider.GetRequiredService<FlightDbContext>();
|
||||
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
||||
|
||||
await context.Database.MigrateAsync();
|
||||
|
||||
foreach (var seeder in seeders) await seeder.SeedAllAsync();
|
||||
}
|
||||
|
||||
// private async Task AddInMemoryHarnessAsync()
|
||||
// {
|
||||
// _harness = _factory.Services.GetRequiredService<InMemoryTestHarness>();
|
||||
// await _harness.Start();
|
||||
// }
|
||||
|
||||
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
|
||||
{
|
||||
var httpContextAccessorMock = Substitute.For<IHttpContextAccessor>();
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
httpContextAccessorMock.HttpContext = new DefaultHttpContext {RequestServices = scope.ServiceProvider};
|
||||
|
||||
httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 5000);
|
||||
httpContextAccessorMock.HttpContext.Request.Scheme = "http";
|
||||
|
||||
return httpContextAccessorMock;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ var appOptions = builder.Services.GetOptions<AppOptions>("AppOptions");
|
||||
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
|
||||
|
||||
builder.Services.AddTransient<IBusPublisher, BusPublisher>();
|
||||
builder.Services.AddCustomDbContext<PassengerDbContext>(configuration, typeof(PassengerRoot).Assembly);
|
||||
builder.Services.AddCustomDbContext<PassengerDbContext>(configuration);
|
||||
builder.AddCustomSerilog();
|
||||
builder.Services.AddJwt();
|
||||
builder.Services.AddControllers();
|
||||
@ -41,7 +41,6 @@ builder.Services.AddCustomMapster(typeof(PassengerRoot).Assembly);
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
|
||||
builder.Services.AddTransient<IEventMapper, EventMapper>();
|
||||
builder.Services.AddTransient<IBusPublisher, BusPublisher>();
|
||||
|
||||
builder.Services.AddCustomMassTransit(typeof(PassengerRoot).Assembly, env);
|
||||
builder.Services.AddCustomOpenTelemetry();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user