mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-28 00:20:24 +08:00
refactor: refactor analyzer configs and add formating to cli
This commit is contained in:
parent
f9eb00c880
commit
dda4a3f92b
1079
.editorconfig
1079
.editorconfig
File diff suppressed because it is too large
Load Diff
12
.github/actions/build/action.yml
vendored
12
.github/actions/build/action.yml
vendored
@ -50,6 +50,18 @@ runs:
|
|||||||
# restore root solution
|
# restore root solution
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
|
|
||||||
|
# npm install, runs `prepare` script automatically in the initialize step
|
||||||
|
- name: Install NPM Dependencies
|
||||||
|
shell: bash
|
||||||
|
if: success()
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Format Service
|
||||||
|
shell: bash
|
||||||
|
if: ${{ success()}}
|
||||||
|
run: |
|
||||||
|
npm run ci-format
|
||||||
|
|
||||||
- name: Build Service
|
- name: Build Service
|
||||||
shell: bash
|
shell: bash
|
||||||
if: ${{ success()}}
|
if: ${{ success()}}
|
||||||
|
|||||||
@ -39,7 +39,9 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
|
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
|
||||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||||
<AnalysisMode>All</AnalysisMode>
|
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
|
||||||
|
<AnalysisLevel>latest-Recommended</AnalysisLevel>
|
||||||
|
<AnalysisMode>Recommended</AnalysisMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
22
README.md
22
README.md
@ -26,6 +26,8 @@
|
|||||||
- [Structure of Project](#structure-of-project)
|
- [Structure of Project](#structure-of-project)
|
||||||
- [Development Setup](#development-setup)
|
- [Development Setup](#development-setup)
|
||||||
- [Dotnet Tools Packages](#dotnet-tools-packages)
|
- [Dotnet Tools Packages](#dotnet-tools-packages)
|
||||||
|
- [Husky](#husky)
|
||||||
|
- [Upgrade Nuget Packages](#upgrade-nuget-packages)
|
||||||
- [How to Run](#how-to-run)
|
- [How to Run](#how-to-run)
|
||||||
- [Config Certificate](#config-certificate)
|
- [Config Certificate](#config-certificate)
|
||||||
- [Docker Compose](#docker-compose)
|
- [Docker Compose](#docker-compose)
|
||||||
@ -159,7 +161,7 @@ Using the CQRS pattern, we cut each business functionality into vertical slices,
|
|||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
### Dotnet Tools Packages
|
### Dotnet Tools Packages
|
||||||
For installing our requirement package with .NET cli tools, we need to install `dotnet tool manifest`.
|
For installing our requirement packages with .NET cli tools, we need to install `dotnet tool manifest`.
|
||||||
```bash
|
```bash
|
||||||
dotnet new tool-manifest
|
dotnet new tool-manifest
|
||||||
```
|
```
|
||||||
@ -168,6 +170,24 @@ And after that we can restore our dotnet tools packages with .NET cli tools from
|
|||||||
dotnet tool restore
|
dotnet tool restore
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Husky
|
||||||
|
Here we use `husky` to handel some pre commit rules and we used `conventional commits` rules and `formatting` as pre commit rules, here in [package.json](./package.json). of course, we can add more rules for pre commit in future. (find more about husky in the [documentation](https://typicode.github.io/husky/get-started.html))
|
||||||
|
We need to install `husky` package for `manage` `pre commits hooks` and also I add two packages `@commitlint/cli` and `@commitlint/config-conventional` for handling conventional commits rules in [package.json](./package.json).
|
||||||
|
Run the command bellow in the root of project to install all npm dependencies related to husky:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: In the root of project we have `.husky` folder and it has `commit-msg` file for handling conventional commits rules with provide user friendly message and `pre-commit` file that we can run our `scripts` as a `pre-commit` hooks. that here we call `format` script from [package.json](./package.json) for formatting purpose.
|
||||||
|
|
||||||
|
### Upgrade Nuget Packages
|
||||||
|
For upgrading our nuget packages to last version, we use the great package [dotnet-outdated](https://github.com/dotnet-outdated/dotnet-outdated).
|
||||||
|
Run the command below in the root of project to upgrade all of packages to last version:
|
||||||
|
```bash
|
||||||
|
dotnet outdated -u
|
||||||
|
```
|
||||||
|
|
||||||
## How to Run
|
## How to Run
|
||||||
|
|
||||||
> ### Config Certificate
|
> ### Config Certificate
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "husky && dotnet tool restore",
|
"prepare": "husky && dotnet tool restore",
|
||||||
"format": "dotnet format booking-microservices-sample.sln --severity error --verbosity detailed",
|
"format": "dotnet format booking-microservices-sample.sln --severity error --verbosity detailed",
|
||||||
|
"ci-format": "dotnet format booking-microservices-sample.sln --verify-no-changes --severity error --verbosity detailed",
|
||||||
"upgrade-packages": "dotnet outdated --upgrade"
|
"upgrade-packages": "dotnet outdated --upgrade"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
<PackageReference Include="Grpc.Core.Testing" Version="2.46.6" />
|
<PackageReference Include="Grpc.Core.Testing" Version="2.46.6" />
|
||||||
<PackageReference Include="EasyCaching.Core" Version="1.9.2" />
|
<PackageReference Include="EasyCaching.Core" Version="1.9.2" />
|
||||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
|
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
|
||||||
<PackageReference Include="EasyNetQ.Management.Client" Version="2.0.0" />
|
<PackageReference Include="EasyNetQ.Management.Client" Version="3.0.0" />
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
||||||
<PackageReference Include="Figgle" Version="0.5.1" />
|
<PackageReference Include="Figgle" Version="0.5.1" />
|
||||||
<PackageReference Include="FluentValidation" Version="11.10.0" />
|
<PackageReference Include="FluentValidation" Version="11.10.0" />
|
||||||
@ -51,7 +51,7 @@
|
|||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
|
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
|
||||||
<PackageReference Include="Scrutor" Version="4.2.2" />
|
<PackageReference Include="Scrutor" Version="4.2.2" />
|
||||||
<PackageReference Include="Sentry.Serilog" Version="4.9.0" />
|
<PackageReference Include="Sentry.Serilog" Version="4.10.2" />
|
||||||
<PackageReference Include="Serilog" Version="4.0.1" />
|
<PackageReference Include="Serilog" Version="4.0.1" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
|
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
|
||||||
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
|
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
|
||||||
|
|||||||
@ -16,21 +16,22 @@ using Microsoft.EntityFrameworkCore.Metadata;
|
|||||||
|
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddCustomDbContext<TContext>(
|
public static IServiceCollection AddCustomDbContext<TContext>(this IServiceCollection services)
|
||||||
this IServiceCollection services)
|
|
||||||
where TContext : DbContext, IDbContext
|
where TContext : DbContext, IDbContext
|
||||||
{
|
{
|
||||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||||
|
|
||||||
services.AddValidateOptions<PostgresOptions>();
|
services.AddValidateOptions<PostgresOptions>();
|
||||||
|
|
||||||
services.AddDbContext<TContext>((sp, options) =>
|
services.AddDbContext<TContext>(
|
||||||
|
(sp, options) =>
|
||||||
{
|
{
|
||||||
var postgresOptions = sp.GetRequiredService<PostgresOptions>();
|
var postgresOptions = sp.GetRequiredService<PostgresOptions>();
|
||||||
|
|
||||||
Guard.Against.Null(options, nameof(postgresOptions));
|
Guard.Against.Null(options, nameof(postgresOptions));
|
||||||
|
|
||||||
options.UseNpgsql(postgresOptions?.ConnectionString,
|
options.UseNpgsql(
|
||||||
|
postgresOptions?.ConnectionString,
|
||||||
dbOptions =>
|
dbOptions =>
|
||||||
{
|
{
|
||||||
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
|
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
|
||||||
@ -44,7 +45,10 @@ public static class Extensions
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IApplicationBuilder UseMigration<TContext>(this IApplicationBuilder app, IWebHostEnvironment env)
|
public static IApplicationBuilder UseMigration<TContext>(
|
||||||
|
this IApplicationBuilder app,
|
||||||
|
IWebHostEnvironment env
|
||||||
|
)
|
||||||
where TContext : DbContext, IDbContext
|
where TContext : DbContext, IDbContext
|
||||||
{
|
{
|
||||||
MigrateDatabaseAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();
|
MigrateDatabaseAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();
|
||||||
@ -62,13 +66,16 @@ public static class Extensions
|
|||||||
public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder)
|
public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
Expression<Func<IAggregate, bool>> filterExpr = e => !e.IsDeleted;
|
Expression<Func<IAggregate, bool>> filterExpr = e => !e.IsDeleted;
|
||||||
|
|
||||||
foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes()
|
foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes()
|
||||||
.Where(m => m.ClrType.IsAssignableTo(typeof(IEntity))))
|
.Where(m => m.ClrType.IsAssignableTo(typeof(IEntity))))
|
||||||
{
|
{
|
||||||
// modify expression to handle correct child type
|
// modify expression to handle correct child type
|
||||||
var parameter = Expression.Parameter(mutableEntityType.ClrType);
|
var parameter = Expression.Parameter(mutableEntityType.ClrType);
|
||||||
|
|
||||||
var body = ReplacingExpressionVisitor
|
var body = ReplacingExpressionVisitor
|
||||||
.Replace(filterExpr.Parameters.First(), parameter, filterExpr.Body);
|
.Replace(filterExpr.Parameters.First(), parameter, filterExpr.Body);
|
||||||
|
|
||||||
var lambdaExpression = Expression.Lambda(body, parameter);
|
var lambdaExpression = Expression.Lambda(body, parameter);
|
||||||
|
|
||||||
// set filter
|
// set filter
|
||||||
@ -76,8 +83,7 @@ public static class Extensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ref: https://andrewlock.net/customising-asp-net-core-identity-ef-core-naming-conventions-for-postgresql/
|
||||||
//ref: https://andrewlock.net/customising-asp-net-core-identity-ef-core-naming-conventions-for-postgresql/
|
|
||||||
public static void ToSnakeCaseTables(this ModelBuilder modelBuilder)
|
public static void ToSnakeCaseTables(this ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
foreach (var entity in modelBuilder.Model.GetEntityTypes())
|
foreach (var entity in modelBuilder.Model.GetEntityTypes())
|
||||||
@ -86,7 +92,9 @@ public static class Extensions
|
|||||||
entity.SetTableName(entity.GetTableName()?.Underscore());
|
entity.SetTableName(entity.GetTableName()?.Underscore());
|
||||||
|
|
||||||
var tableObjectIdentifier =
|
var tableObjectIdentifier =
|
||||||
StoreObjectIdentifier.Table(entity.GetTableName()?.Underscore()!, entity.GetSchema());
|
StoreObjectIdentifier.Table(
|
||||||
|
entity.GetTableName()?.Underscore()!,
|
||||||
|
entity.GetSchema());
|
||||||
|
|
||||||
// Replace column names
|
// Replace column names
|
||||||
foreach (var property in entity.GetProperties())
|
foreach (var property in entity.GetProperties())
|
||||||
@ -119,6 +127,7 @@ public static class Extensions
|
|||||||
{
|
{
|
||||||
using var scope = serviceProvider.CreateScope();
|
using var scope = serviceProvider.CreateScope();
|
||||||
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
||||||
|
|
||||||
foreach (var seeder in seeders)
|
foreach (var seeder in seeders)
|
||||||
{
|
{
|
||||||
await seeder.SeedAllAsync();
|
await seeder.SeedAllAsync();
|
||||||
|
|||||||
@ -22,7 +22,7 @@ public class EventTypeMapper
|
|||||||
|
|
||||||
public static string ToName(Type eventType) => Instance.typeNameMap.GetOrAdd(eventType, _ =>
|
public static string ToName(Type eventType) => Instance.typeNameMap.GetOrAdd(eventType, _ =>
|
||||||
{
|
{
|
||||||
var eventTypeName = eventType.FullName!.Replace(".", "_");
|
var eventTypeName = eventType.FullName!.Replace(".", "_", StringComparison.CurrentCulture);
|
||||||
|
|
||||||
Instance.typeMap.AddOrUpdate(eventTypeName, eventType, (_, _) => eventType);
|
Instance.typeMap.AddOrUpdate(eventTypeName, eventType, (_, _) => eventType);
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ public class EventTypeMapper
|
|||||||
|
|
||||||
public static Type? ToType(string eventTypeName) => Instance.typeMap.GetOrAdd(eventTypeName, _ =>
|
public static Type? ToType(string eventTypeName) => Instance.typeMap.GetOrAdd(eventTypeName, _ =>
|
||||||
{
|
{
|
||||||
var type = TypeProvider.GetFirstMatchingTypeFromCurrentDomainAssembly(eventTypeName.Replace("_", "."));
|
var type = TypeProvider.GetFirstMatchingTypeFromCurrentDomainAssembly(eventTypeName.Replace("_", ".", StringComparison.CurrentCulture));
|
||||||
|
|
||||||
if (type == null)
|
if (type == null)
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -17,7 +17,7 @@ public class AuthHeaderHandler : DelegatingHandler
|
|||||||
{
|
{
|
||||||
var token = (_httpContext?.HttpContext?.Request.Headers["Authorization"])?.ToString();
|
var token = (_httpContext?.HttpContext?.Request.Headers["Authorization"])?.ToString();
|
||||||
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", ""));
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", "", StringComparison.CurrentCulture));
|
||||||
|
|
||||||
return base.SendAsync(request, cancellationToken);
|
return base.SendAsync(request, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using BuildingBlocks.Web;
|
using BuildingBlocks.Web;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
@ -44,7 +45,7 @@ namespace BuildingBlocks.Logging
|
|||||||
new ElasticsearchSinkOptions(new Uri(logOptions.Elastic.ElasticServiceUrl))
|
new ElasticsearchSinkOptions(new Uri(logOptions.Elastic.ElasticServiceUrl))
|
||||||
{
|
{
|
||||||
AutoRegisterTemplate = true,
|
AutoRegisterTemplate = true,
|
||||||
IndexFormat = $"{appOptions.Name}-{environment?.ToLower()}"
|
IndexFormat = $"{appOptions.Name}-{environment?.ToLower(CultureInfo.CurrentCulture)}"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -67,7 +67,7 @@ namespace BuildingBlocks.Mongo
|
|||||||
|
|
||||||
private static bool ParameterMatchProperty(ParameterInfo parameter, PropertyInfo property)
|
private static bool ParameterMatchProperty(ParameterInfo parameter, PropertyInfo property)
|
||||||
{
|
{
|
||||||
return string.Equals(property.Name, parameter.Name, System.StringComparison.InvariantCultureIgnoreCase)
|
return string.Equals(property.Name, parameter.Name, StringComparison.OrdinalIgnoreCase)
|
||||||
&& parameter.ParameterType == property.PropertyType;
|
&& parameter.ParameterType == property.PropertyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using System.Globalization;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using MongoDB.Bson;
|
using MongoDB.Bson;
|
||||||
using MongoDB.Bson.Serialization.Conventions;
|
using MongoDB.Bson.Serialization.Conventions;
|
||||||
@ -42,7 +43,7 @@ public class MongoDbContext : IMongoDbContext
|
|||||||
|
|
||||||
public IMongoCollection<T> GetCollection<T>(string? name = null)
|
public IMongoCollection<T> GetCollection<T>(string? name = null)
|
||||||
{
|
{
|
||||||
return Database.GetCollection<T>(name ?? typeof(T).Name.ToLower());
|
return Database.GetCollection<T>(name ?? typeof(T).Name.ToLower(CultureInfo.CurrentCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@ -37,7 +37,7 @@ using Testcontainers.PostgreSql;
|
|||||||
using Testcontainers.RabbitMq;
|
using Testcontainers.RabbitMq;
|
||||||
|
|
||||||
public class TestFixture<TEntryPoint> : IAsyncLifetime
|
public class TestFixture<TEntryPoint> : IAsyncLifetime
|
||||||
where TEntryPoint : class
|
where TEntryPoint : class
|
||||||
{
|
{
|
||||||
private readonly WebApplicationFactory<TEntryPoint> _factory;
|
private readonly WebApplicationFactory<TEntryPoint> _factory;
|
||||||
private int Timeout => 120; // Second
|
private int Timeout => 120; // Second
|
||||||
@ -60,10 +60,11 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
var claims =
|
var claims =
|
||||||
new Dictionary<string, object>
|
new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
{ ClaimTypes.Name, "test@sample.com" },
|
{ClaimTypes.Name, "test@sample.com"},
|
||||||
{ ClaimTypes.Role, "admin" },
|
{ClaimTypes.Role, "admin"},
|
||||||
{"scope", "flight-api"}
|
{"scope", "flight-api"}
|
||||||
};
|
};
|
||||||
|
|
||||||
var httpClient = _factory?.CreateClient();
|
var httpClient = _factory?.CreateClient();
|
||||||
httpClient.SetFakeBearerToken(claims);
|
httpClient.SetFakeBearerToken(claims);
|
||||||
return httpClient;
|
return httpClient;
|
||||||
@ -71,7 +72,9 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GrpcChannel Channel =>
|
public GrpcChannel Channel =>
|
||||||
GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions { HttpClient = HttpClient });
|
GrpcChannel.ForAddress(
|
||||||
|
HttpClient.BaseAddress!,
|
||||||
|
new GrpcChannelOptions { HttpClient = HttpClient });
|
||||||
|
|
||||||
public IServiceProvider ServiceProvider => _factory?.Services;
|
public IServiceProvider ServiceProvider => _factory?.Services;
|
||||||
public IConfiguration Configuration => _factory?.Services.GetRequiredService<IConfiguration>();
|
public IConfiguration Configuration => _factory?.Services.GetRequiredService<IConfiguration>();
|
||||||
@ -80,12 +83,15 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
protected TestFixture()
|
protected TestFixture()
|
||||||
{
|
{
|
||||||
_factory = new WebApplicationFactory<TEntryPoint>()
|
_factory = new WebApplicationFactory<TEntryPoint>()
|
||||||
.WithWebHostBuilder(builder =>
|
.WithWebHostBuilder(
|
||||||
|
builder =>
|
||||||
{
|
{
|
||||||
builder.ConfigureAppConfiguration(AddCustomAppSettings);
|
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);
|
||||||
@ -95,11 +101,16 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
// add authentication using a fake jwt bearer - we can use SetAdminUser method to set authenticate user to existing HttContextAccessor
|
// add authentication using a fake jwt bearer - we can use SetAdminUser method to set authenticate user to existing HttContextAccessor
|
||||||
// https://github.com/webmotions/fake-authentication-jwtbearer
|
// https://github.com/webmotions/fake-authentication-jwtbearer
|
||||||
// https://github.com/webmotions/fake-authentication-jwtbearer/issues/14
|
// https://github.com/webmotions/fake-authentication-jwtbearer/issues/14
|
||||||
services.AddAuthentication(options =>
|
services.AddAuthentication(
|
||||||
|
options =>
|
||||||
{
|
{
|
||||||
options.DefaultAuthenticateScheme = FakeJwtBearerDefaults.AuthenticationScheme;
|
options.DefaultAuthenticateScheme =
|
||||||
options.DefaultChallengeScheme = FakeJwtBearerDefaults.AuthenticationScheme;
|
FakeJwtBearerDefaults.AuthenticationScheme;
|
||||||
}).AddFakeJwtBearer();
|
|
||||||
|
options.DefaultChallengeScheme =
|
||||||
|
FakeJwtBearerDefaults.AuthenticationScheme;
|
||||||
|
})
|
||||||
|
.AddFakeJwtBearer();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -114,7 +125,7 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
{
|
{
|
||||||
await StopTestContainerAsync();
|
await StopTestContainerAsync();
|
||||||
await _factory.DisposeAsync();
|
await _factory.DisposeAsync();
|
||||||
CancellationTokenSource.Cancel();
|
await CancellationTokenSource.CancelAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void RegisterServices(Action<IServiceCollection> services)
|
public virtual void RegisterServices(Action<IServiceCollection> services)
|
||||||
@ -153,7 +164,8 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
|
|
||||||
public Task<TResponse> SendAsync<TResponse>(IRequest<TResponse> request)
|
public Task<TResponse> SendAsync<TResponse>(IRequest<TResponse> request)
|
||||||
{
|
{
|
||||||
return ExecuteScopeAsync(sp =>
|
return ExecuteScopeAsync(
|
||||||
|
sp =>
|
||||||
{
|
{
|
||||||
var mediator = sp.GetRequiredService<IMediator>();
|
var mediator = sp.GetRequiredService<IMediator>();
|
||||||
|
|
||||||
@ -163,38 +175,57 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
|
|
||||||
public Task SendAsync(IRequest request)
|
public Task SendAsync(IRequest request)
|
||||||
{
|
{
|
||||||
return ExecuteScopeAsync(sp =>
|
return ExecuteScopeAsync(
|
||||||
|
sp =>
|
||||||
{
|
{
|
||||||
var mediator = sp.GetRequiredService<IMediator>();
|
var mediator = sp.GetRequiredService<IMediator>();
|
||||||
return mediator.Send(request);
|
return mediator.Send(request);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Publish<TMessage>(TMessage message, CancellationToken cancellationToken = default)
|
public async Task Publish<TMessage>(
|
||||||
|
TMessage message,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
where TMessage : class, IEvent
|
where TMessage : class, IEvent
|
||||||
{
|
{
|
||||||
await TestHarness.Bus.Publish(message, cancellationToken);
|
await TestHarness.Bus.Publish(message, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> WaitForPublishing<TMessage>(CancellationToken cancellationToken = default)
|
public async Task<bool> WaitForPublishing<TMessage>(
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
where TMessage : class, IEvent
|
where TMessage : class, IEvent
|
||||||
{
|
{
|
||||||
var result = await WaitUntilConditionMet(async () =>
|
var result = await WaitUntilConditionMet(
|
||||||
|
async () =>
|
||||||
{
|
{
|
||||||
var published = await TestHarness.Published.Any<TMessage>(cancellationToken);
|
var published =
|
||||||
var faulty = await TestHarness.Published.Any<Fault<TMessage>>(cancellationToken);
|
await TestHarness.Published.Any<TMessage>(cancellationToken);
|
||||||
|
|
||||||
|
var faulty =
|
||||||
|
await TestHarness.Published.Any<Fault<TMessage>>(
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
return published && faulty == false;
|
return published && faulty == false;
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> WaitForConsuming<TMessage>(CancellationToken cancellationToken = default)
|
public async Task<bool> WaitForConsuming<TMessage>(
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
where TMessage : class, IEvent
|
where TMessage : class, IEvent
|
||||||
{
|
{
|
||||||
var result = await WaitUntilConditionMet(async () =>
|
var result = await WaitUntilConditionMet(
|
||||||
|
async () =>
|
||||||
{
|
{
|
||||||
var consumed = await TestHarness.Consumed.Any<TMessage>(cancellationToken);
|
var consumed =
|
||||||
var faulty = await TestHarness.Consumed.Any<Fault<TMessage>>(cancellationToken);
|
await TestHarness.Consumed.Any<TMessage>(cancellationToken);
|
||||||
|
|
||||||
|
var faulty =
|
||||||
|
await TestHarness.Consumed.Any<Fault<TMessage>>(cancellationToken);
|
||||||
|
|
||||||
return consumed && faulty == false;
|
return consumed && faulty == false;
|
||||||
});
|
});
|
||||||
@ -203,21 +234,33 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ShouldProcessedPersistInternalCommand<TInternalCommand>(
|
public async Task<bool> ShouldProcessedPersistInternalCommand<TInternalCommand>(
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
where TInternalCommand : class, IInternalCommand
|
where TInternalCommand : class, IInternalCommand
|
||||||
{
|
{
|
||||||
var result = await WaitUntilConditionMet(async () =>
|
var result = await WaitUntilConditionMet(
|
||||||
|
async () =>
|
||||||
{
|
{
|
||||||
return await ExecuteScopeAsync(async sp =>
|
return await ExecuteScopeAsync(
|
||||||
|
async sp =>
|
||||||
{
|
{
|
||||||
var persistMessageProcessor = sp.GetService<IPersistMessageProcessor>();
|
var persistMessageProcessor =
|
||||||
Guard.Against.Null(persistMessageProcessor, nameof(persistMessageProcessor));
|
sp.GetService<IPersistMessageProcessor>();
|
||||||
|
|
||||||
var filter = await persistMessageProcessor.GetByFilterAsync(x =>
|
Guard.Against.Null(
|
||||||
x.DeliveryType == MessageDeliveryType.Internal &&
|
persistMessageProcessor,
|
||||||
typeof(TInternalCommand).ToString() == x.DataType);
|
nameof(persistMessageProcessor));
|
||||||
|
|
||||||
var res = filter.Any(x => x.MessageStatus == MessageStatus.Processed);
|
var filter =
|
||||||
|
await persistMessageProcessor.GetByFilterAsync(
|
||||||
|
x =>
|
||||||
|
x.DeliveryType ==
|
||||||
|
MessageDeliveryType.Internal &&
|
||||||
|
typeof(TInternalCommand).ToString() ==
|
||||||
|
x.DataType);
|
||||||
|
|
||||||
|
var res = filter.Any(
|
||||||
|
x => x.MessageStatus == MessageStatus.Processed);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
@ -227,13 +270,17 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ref: https://tech.energyhelpline.com/in-memory-testing-with-masstransit/
|
// Ref: https://tech.energyhelpline.com/in-memory-testing-with-masstransit/
|
||||||
private async Task<bool> WaitUntilConditionMet(Func<Task<bool>> conditionToMet, int? timeoutSecond = null)
|
private async Task<bool> WaitUntilConditionMet(
|
||||||
|
Func<Task<bool>> conditionToMet,
|
||||||
|
int? timeoutSecond = null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var time = timeoutSecond ?? Timeout;
|
var time = timeoutSecond ?? Timeout;
|
||||||
|
|
||||||
var startTime = DateTime.Now;
|
var startTime = DateTime.Now;
|
||||||
var timeoutExpired = false;
|
var timeoutExpired = false;
|
||||||
var meet = await conditionToMet.Invoke();
|
var meet = await conditionToMet.Invoke();
|
||||||
|
|
||||||
while (!meet)
|
while (!meet)
|
||||||
{
|
{
|
||||||
if (timeoutExpired)
|
if (timeoutExpired)
|
||||||
@ -275,19 +322,32 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
|
|
||||||
private void AddCustomAppSettings(IConfigurationBuilder configuration)
|
private void AddCustomAppSettings(IConfigurationBuilder configuration)
|
||||||
{
|
{
|
||||||
configuration.AddInMemoryCollection(new KeyValuePair<string, string>[]
|
configuration.AddInMemoryCollection(
|
||||||
|
new KeyValuePair<string, string>[]
|
||||||
{
|
{
|
||||||
new("PostgresOptions:ConnectionString", PostgresTestcontainer.GetConnectionString()),
|
new(
|
||||||
new("PersistMessageOptions:ConnectionString", PostgresPersistTestContainer.GetConnectionString()),
|
"PostgresOptions:ConnectionString",
|
||||||
|
PostgresTestcontainer.GetConnectionString()),
|
||||||
|
new(
|
||||||
|
"PersistMessageOptions:ConnectionString",
|
||||||
|
PostgresPersistTestContainer.GetConnectionString()),
|
||||||
new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname),
|
new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname),
|
||||||
new("RabbitMqOptions:UserName", TestContainers.RabbitMqContainerConfiguration.UserName),
|
new(
|
||||||
new("RabbitMqOptions:Password", TestContainers.RabbitMqContainerConfiguration.Password), new(
|
"RabbitMqOptions:UserName",
|
||||||
|
TestContainers.RabbitMqContainerConfiguration.UserName),
|
||||||
|
new(
|
||||||
|
"RabbitMqOptions:Password",
|
||||||
|
TestContainers.RabbitMqContainerConfiguration.Password),
|
||||||
|
new(
|
||||||
"RabbitMqOptions:Port",
|
"RabbitMqOptions:Port",
|
||||||
RabbitMqTestContainer.GetMappedPublicPort(TestContainers.RabbitMqContainerConfiguration.Port)
|
RabbitMqTestContainer.GetMappedPublicPort(
|
||||||
|
TestContainers.RabbitMqContainerConfiguration.Port)
|
||||||
.ToString(NumberFormatInfo.InvariantInfo)),
|
.ToString(NumberFormatInfo.InvariantInfo)),
|
||||||
new("MongoOptions:ConnectionString", MongoDbTestContainer.GetConnectionString()),
|
new("MongoOptions:ConnectionString", MongoDbTestContainer.GetConnectionString()),
|
||||||
new("MongoOptions:DatabaseName", TestContainers.MongoContainerConfiguration.Name),
|
new("MongoOptions:DatabaseName", TestContainers.MongoContainerConfiguration.Name),
|
||||||
new("EventStoreOptions:ConnectionString", EventStoreDbTestContainer.GetConnectionString())
|
new(
|
||||||
|
"EventStoreOptions:ConnectionString",
|
||||||
|
EventStoreDbTestContainer.GetConnectionString())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +355,9 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
{
|
{
|
||||||
var httpContextAccessorMock = Substitute.For<IHttpContextAccessor>();
|
var httpContextAccessorMock = Substitute.For<IHttpContextAccessor>();
|
||||||
using var scope = serviceProvider.CreateScope();
|
using var scope = serviceProvider.CreateScope();
|
||||||
httpContextAccessorMock.HttpContext = new DefaultHttpContext { RequestServices = scope.ServiceProvider };
|
|
||||||
|
httpContextAccessorMock.HttpContext = new DefaultHttpContext
|
||||||
|
{ RequestServices = scope.ServiceProvider };
|
||||||
|
|
||||||
httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 6012);
|
httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 6012);
|
||||||
httpContextAccessorMock.HttpContext.Request.Scheme = "http";
|
httpContextAccessorMock.HttpContext.Request.Scheme = "http";
|
||||||
@ -305,8 +367,8 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
||||||
where TEntryPoint : class
|
where TEntryPoint : class
|
||||||
where TWContext : DbContext
|
where TWContext : DbContext
|
||||||
{
|
{
|
||||||
public Task ExecuteDbContextAsync(Func<TWContext, Task> action)
|
public Task ExecuteDbContextAsync(Func<TWContext, Task> action)
|
||||||
{
|
{
|
||||||
@ -320,7 +382,8 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
|||||||
|
|
||||||
public Task ExecuteDbContextAsync(Func<TWContext, IMediator, Task> action)
|
public Task ExecuteDbContextAsync(Func<TWContext, IMediator, Task> action)
|
||||||
{
|
{
|
||||||
return ExecuteScopeAsync(sp => action(sp.GetService<TWContext>(), sp.GetService<IMediator>()));
|
return ExecuteScopeAsync(
|
||||||
|
sp => action(sp.GetService<TWContext>(), sp.GetService<IMediator>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<T> ExecuteDbContextAsync<T>(Func<TWContext, Task<T>> action)
|
public Task<T> ExecuteDbContextAsync<T>(Func<TWContext, Task<T>> action)
|
||||||
@ -335,12 +398,15 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
|||||||
|
|
||||||
public Task<T> ExecuteDbContextAsync<T>(Func<TWContext, IMediator, Task<T>> action)
|
public Task<T> ExecuteDbContextAsync<T>(Func<TWContext, IMediator, Task<T>> action)
|
||||||
{
|
{
|
||||||
return ExecuteScopeAsync(sp => action(sp.GetService<TWContext>(), sp.GetService<IMediator>()));
|
return ExecuteScopeAsync(
|
||||||
|
sp => action(sp.GetService<TWContext>(), sp.GetService<IMediator>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task InsertAsync<T>(params T[] entities) where T : class
|
public Task InsertAsync<T>(params T[] entities)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
return ExecuteDbContextAsync(db =>
|
return ExecuteDbContextAsync(
|
||||||
|
db =>
|
||||||
{
|
{
|
||||||
foreach (var entity in entities)
|
foreach (var entity in entities)
|
||||||
{
|
{
|
||||||
@ -351,9 +417,11 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InsertAsync<TEntity>(TEntity entity) where TEntity : class
|
public async Task InsertAsync<TEntity>(TEntity entity)
|
||||||
|
where TEntity : class
|
||||||
{
|
{
|
||||||
await ExecuteDbContextAsync(db =>
|
await ExecuteDbContextAsync(
|
||||||
|
db =>
|
||||||
{
|
{
|
||||||
db.Set<TEntity>().Add(entity);
|
db.Set<TEntity>().Add(entity);
|
||||||
|
|
||||||
@ -365,7 +433,8 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
|||||||
where TEntity : class
|
where TEntity : class
|
||||||
where TEntity2 : class
|
where TEntity2 : class
|
||||||
{
|
{
|
||||||
return ExecuteDbContextAsync(db =>
|
return ExecuteDbContextAsync(
|
||||||
|
db =>
|
||||||
{
|
{
|
||||||
db.Set<TEntity>().Add(entity);
|
db.Set<TEntity>().Add(entity);
|
||||||
db.Set<TEntity2>().Add(entity2);
|
db.Set<TEntity2>().Add(entity2);
|
||||||
@ -374,12 +443,17 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task InsertAsync<TEntity, TEntity2, TEntity3>(TEntity entity, TEntity2 entity2, TEntity3 entity3)
|
public Task InsertAsync<TEntity, TEntity2, TEntity3>(
|
||||||
|
TEntity entity,
|
||||||
|
TEntity2 entity2,
|
||||||
|
TEntity3 entity3
|
||||||
|
)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
where TEntity2 : class
|
where TEntity2 : class
|
||||||
where TEntity3 : class
|
where TEntity3 : class
|
||||||
{
|
{
|
||||||
return ExecuteDbContextAsync(db =>
|
return ExecuteDbContextAsync(
|
||||||
|
db =>
|
||||||
{
|
{
|
||||||
db.Set<TEntity>().Add(entity);
|
db.Set<TEntity>().Add(entity);
|
||||||
db.Set<TEntity2>().Add(entity2);
|
db.Set<TEntity2>().Add(entity2);
|
||||||
@ -389,14 +463,19 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task InsertAsync<TEntity, TEntity2, TEntity3, TEntity4>(TEntity entity, TEntity2 entity2, TEntity3 entity3,
|
public Task InsertAsync<TEntity, TEntity2, TEntity3, TEntity4>(
|
||||||
TEntity4 entity4)
|
TEntity entity,
|
||||||
|
TEntity2 entity2,
|
||||||
|
TEntity3 entity3,
|
||||||
|
TEntity4 entity4
|
||||||
|
)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
where TEntity2 : class
|
where TEntity2 : class
|
||||||
where TEntity3 : class
|
where TEntity3 : class
|
||||||
where TEntity4 : class
|
where TEntity4 : class
|
||||||
{
|
{
|
||||||
return ExecuteDbContextAsync(db =>
|
return ExecuteDbContextAsync(
|
||||||
|
db =>
|
||||||
{
|
{
|
||||||
db.Set<TEntity>().Add(entity);
|
db.Set<TEntity>().Add(entity);
|
||||||
db.Set<TEntity2>().Add(entity2);
|
db.Set<TEntity2>().Add(entity2);
|
||||||
@ -421,8 +500,8 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class TestReadFixture<TEntryPoint, TRContext> : TestFixture<TEntryPoint>
|
public class TestReadFixture<TEntryPoint, TRContext> : TestFixture<TEntryPoint>
|
||||||
where TEntryPoint : class
|
where TEntryPoint : class
|
||||||
where TRContext : MongoDbContext
|
where TRContext : MongoDbContext
|
||||||
{
|
{
|
||||||
public Task ExecuteReadContextAsync(Func<TRContext, Task> action)
|
public Task ExecuteReadContextAsync(Func<TRContext, Task> action)
|
||||||
{
|
{
|
||||||
@ -434,19 +513,22 @@ public class TestReadFixture<TEntryPoint, TRContext> : TestFixture<TEntryPoint>
|
|||||||
return ExecuteScopeAsync(sp => action(sp.GetRequiredService<TRContext>()));
|
return ExecuteScopeAsync(sp => action(sp.GetRequiredService<TRContext>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InsertMongoDbContextAsync<T>(string collectionName, params T[] entities) where T : class
|
public async Task InsertMongoDbContextAsync<T>(string collectionName, params T[] entities)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
await ExecuteReadContextAsync(async db =>
|
await ExecuteReadContextAsync(
|
||||||
|
async db =>
|
||||||
{
|
{
|
||||||
await db.GetCollection<T>(collectionName).InsertManyAsync(entities.ToList());
|
await db.GetCollection<T>(collectionName).InsertManyAsync(entities.ToList());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestFixture<TEntryPoint, TWContext, TRContext> : TestWriteFixture<TEntryPoint, TWContext>
|
public class TestFixture<TEntryPoint, TWContext, TRContext>
|
||||||
where TEntryPoint : class
|
: TestWriteFixture<TEntryPoint, TWContext>
|
||||||
where TWContext : DbContext
|
where TEntryPoint : class
|
||||||
where TRContext : MongoDbContext
|
where TWContext : DbContext
|
||||||
|
where TRContext : MongoDbContext
|
||||||
{
|
{
|
||||||
public Task ExecuteReadContextAsync(Func<TRContext, Task> action)
|
public Task ExecuteReadContextAsync(Func<TRContext, Task> action)
|
||||||
{
|
{
|
||||||
@ -458,9 +540,11 @@ public class TestFixture<TEntryPoint, TWContext, TRContext> : TestWriteFixture<T
|
|||||||
return ExecuteScopeAsync(sp => action(sp.GetRequiredService<TRContext>()));
|
return ExecuteScopeAsync(sp => action(sp.GetRequiredService<TRContext>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InsertMongoDbContextAsync<T>(string collectionName, params T[] entities) where T : class
|
public async Task InsertMongoDbContextAsync<T>(string collectionName, params T[] entities)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
await ExecuteReadContextAsync(async db =>
|
await ExecuteReadContextAsync(
|
||||||
|
async db =>
|
||||||
{
|
{
|
||||||
await db.GetCollection<T>(collectionName).InsertManyAsync(entities.ToList());
|
await db.GetCollection<T>(collectionName).InsertManyAsync(entities.ToList());
|
||||||
});
|
});
|
||||||
@ -468,7 +552,7 @@ public class TestFixture<TEntryPoint, TWContext, TRContext> : TestWriteFixture<T
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
||||||
where TEntryPoint : class
|
where TEntryPoint : class
|
||||||
{
|
{
|
||||||
private Respawner _reSpawnerDefaultDb;
|
private Respawner _reSpawnerDefaultDb;
|
||||||
private Respawner _reSpawnerPersistDb;
|
private Respawner _reSpawnerPersistDb;
|
||||||
@ -476,7 +560,10 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
|||||||
private NpgsqlConnection PersistDbConnection { get; set; }
|
private NpgsqlConnection PersistDbConnection { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public TestFixtureCore(TestFixture<TEntryPoint> integrationTestFixture, ITestOutputHelper outputHelper)
|
public TestFixtureCore(
|
||||||
|
TestFixture<TEntryPoint> integrationTestFixture,
|
||||||
|
ITestOutputHelper outputHelper
|
||||||
|
)
|
||||||
{
|
{
|
||||||
Fixture = integrationTestFixture;
|
Fixture = integrationTestFixture;
|
||||||
integrationTestFixture.RegisterServices(RegisterTestsServices);
|
integrationTestFixture.RegisterServices(RegisterTestsServices);
|
||||||
@ -505,12 +592,14 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(persistOptions?.ConnectionString))
|
if (!string.IsNullOrEmpty(persistOptions?.ConnectionString))
|
||||||
{
|
{
|
||||||
await Fixture.PersistMessageBackgroundService.StartAsync(Fixture.CancellationTokenSource.Token);
|
await Fixture.PersistMessageBackgroundService.StartAsync(
|
||||||
|
Fixture.CancellationTokenSource.Token);
|
||||||
|
|
||||||
PersistDbConnection = new NpgsqlConnection(persistOptions.ConnectionString);
|
PersistDbConnection = new NpgsqlConnection(persistOptions.ConnectionString);
|
||||||
await PersistDbConnection.OpenAsync();
|
await PersistDbConnection.OpenAsync();
|
||||||
|
|
||||||
_reSpawnerPersistDb = await Respawner.CreateAsync(PersistDbConnection,
|
_reSpawnerPersistDb = await Respawner.CreateAsync(
|
||||||
|
PersistDbConnection,
|
||||||
new RespawnerOptions { DbAdapter = DbAdapter.Postgres });
|
new RespawnerOptions { DbAdapter = DbAdapter.Postgres });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,7 +608,8 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
|||||||
DefaultDbConnection = new NpgsqlConnection(postgresOptions.ConnectionString);
|
DefaultDbConnection = new NpgsqlConnection(postgresOptions.ConnectionString);
|
||||||
await DefaultDbConnection.OpenAsync();
|
await DefaultDbConnection.OpenAsync();
|
||||||
|
|
||||||
_reSpawnerDefaultDb = await Respawner.CreateAsync(DefaultDbConnection,
|
_reSpawnerDefaultDb = await Respawner.CreateAsync(
|
||||||
|
DefaultDbConnection,
|
||||||
new RespawnerOptions { DbAdapter = DbAdapter.Postgres });
|
new RespawnerOptions { DbAdapter = DbAdapter.Postgres });
|
||||||
|
|
||||||
await SeedDataAsync();
|
await SeedDataAsync();
|
||||||
@ -532,7 +622,8 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
|||||||
{
|
{
|
||||||
await _reSpawnerPersistDb.ResetAsync(PersistDbConnection);
|
await _reSpawnerPersistDb.ResetAsync(PersistDbConnection);
|
||||||
|
|
||||||
await Fixture.PersistMessageBackgroundService.StopAsync(Fixture.CancellationTokenSource.Token);
|
await Fixture.PersistMessageBackgroundService.StopAsync(
|
||||||
|
Fixture.CancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DefaultDbConnection is not null)
|
if (DefaultDbConnection is not null)
|
||||||
@ -545,7 +636,9 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
|||||||
{
|
{
|
||||||
//https://stackoverflow.com/questions/3366397/delete-everything-in-a-mongodb-database
|
//https://stackoverflow.com/questions/3366397/delete-everything-in-a-mongodb-database
|
||||||
var dbClient = new MongoClient(Fixture.MongoDbTestContainer?.GetConnectionString());
|
var dbClient = new MongoClient(Fixture.MongoDbTestContainer?.GetConnectionString());
|
||||||
var collections = await dbClient.GetDatabase(TestContainers.MongoContainerConfiguration.Name)
|
|
||||||
|
var collections = await dbClient
|
||||||
|
.GetDatabase(TestContainers.MongoContainerConfiguration.Name)
|
||||||
.ListCollectionsAsync(cancellationToken: cancellationToken);
|
.ListCollectionsAsync(cancellationToken: cancellationToken);
|
||||||
|
|
||||||
foreach (var collection in collections.ToList())
|
foreach (var collection in collections.ToList())
|
||||||
@ -557,27 +650,30 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
|||||||
|
|
||||||
private async Task ResetRabbitMqAsync(CancellationToken cancellationToken = default)
|
private async Task ResetRabbitMqAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var port = Fixture.RabbitMqTestContainer?.GetMappedPublicPort(TestContainers.RabbitMqContainerConfiguration
|
var port = Fixture.RabbitMqTestContainer?.GetMappedPublicPort(
|
||||||
.ApiPort)
|
TestContainers.RabbitMqContainerConfiguration
|
||||||
?? TestContainers.RabbitMqContainerConfiguration.ApiPort;
|
.ApiPort) ??
|
||||||
|
TestContainers.RabbitMqContainerConfiguration.ApiPort;
|
||||||
|
|
||||||
var managementClient = new ManagementClient(Fixture.RabbitMqTestContainer?.Hostname,
|
var managementClient = new ManagementClient(Fixture.RabbitMqTestContainer?.Hostname,
|
||||||
TestContainers.RabbitMqContainerConfiguration?.UserName,
|
TestContainers.RabbitMqContainerConfiguration?.UserName,
|
||||||
TestContainers.RabbitMqContainerConfiguration?.Password, port);
|
TestContainers.RabbitMqContainerConfiguration?.Password, port);
|
||||||
|
|
||||||
var bd = await managementClient.GetBindingsAsync(cancellationToken);
|
var bd = await managementClient.GetBindingsAsync(cancellationToken);
|
||||||
var bindings = bd.Where(x => !string.IsNullOrEmpty(x.Source) && !string.IsNullOrEmpty(x.Destination));
|
|
||||||
|
var bindings = bd.Where(
|
||||||
|
x => !string.IsNullOrEmpty(x.Source) && !string.IsNullOrEmpty(x.Destination));
|
||||||
|
|
||||||
foreach (var binding in bindings)
|
foreach (var binding in bindings)
|
||||||
{
|
{
|
||||||
await managementClient.DeleteBindingAsync(binding, cancellationToken);
|
await managementClient.DeleteBindingAsync(binding, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
var queues = await managementClient.GetQueuesAsync(cancellationToken);
|
var queues = await managementClient.GetQueuesAsync(cancellationToken: cancellationToken);
|
||||||
|
|
||||||
foreach (var queue in queues)
|
foreach (var queue in queues)
|
||||||
{
|
{
|
||||||
await managementClient.DeleteQueueAsync(queue, cancellationToken);
|
await managementClient.PurgeAsync(queue, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,6 +686,7 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
|||||||
using var scope = Fixture.ServiceProvider.CreateScope();
|
using var scope = Fixture.ServiceProvider.CreateScope();
|
||||||
|
|
||||||
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
||||||
|
|
||||||
foreach (var seeder in seeders)
|
foreach (var seeder in seeders)
|
||||||
{
|
{
|
||||||
await seeder.SeedAllAsync();
|
await seeder.SeedAllAsync();
|
||||||
@ -598,13 +695,14 @@ public class TestFixtureCore<TEntryPoint> : IAsyncLifetime
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract class TestReadBase<TEntryPoint, TRContext> : TestFixtureCore<TEntryPoint>
|
public abstract class TestReadBase<TEntryPoint, TRContext> : TestFixtureCore<TEntryPoint>
|
||||||
//,IClassFixture<IntegrationTestFactory<TEntryPoint, TWContext>>
|
// ,IClassFixture<IntegrationTestFactory<TEntryPoint, TWContext>>
|
||||||
where TEntryPoint : class
|
where TEntryPoint : class
|
||||||
where TRContext : MongoDbContext
|
where TRContext : MongoDbContext
|
||||||
{
|
{
|
||||||
protected TestReadBase(
|
protected TestReadBase(
|
||||||
TestReadFixture<TEntryPoint, TRContext> integrationTestFixture, ITestOutputHelper outputHelper = null) : base(
|
TestReadFixture<TEntryPoint, TRContext> integrationTestFixture,
|
||||||
integrationTestFixture, outputHelper)
|
ITestOutputHelper outputHelper = null
|
||||||
|
) : base(integrationTestFixture, outputHelper)
|
||||||
{
|
{
|
||||||
Fixture = integrationTestFixture;
|
Fixture = integrationTestFixture;
|
||||||
}
|
}
|
||||||
@ -613,13 +711,14 @@ public abstract class TestReadBase<TEntryPoint, TRContext> : TestFixtureCore<TEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract class TestWriteBase<TEntryPoint, TWContext> : TestFixtureCore<TEntryPoint>
|
public abstract class TestWriteBase<TEntryPoint, TWContext> : TestFixtureCore<TEntryPoint>
|
||||||
//,IClassFixture<IntegrationTestFactory<TEntryPoint, TWContext>>
|
//,IClassFixture<IntegrationTestFactory<TEntryPoint, TWContext>>
|
||||||
where TEntryPoint : class
|
where TEntryPoint : class
|
||||||
where TWContext : DbContext
|
where TWContext : DbContext
|
||||||
{
|
{
|
||||||
protected TestWriteBase(
|
protected TestWriteBase(
|
||||||
TestWriteFixture<TEntryPoint, TWContext> integrationTestFixture, ITestOutputHelper outputHelper = null) : base(
|
TestWriteFixture<TEntryPoint, TWContext> integrationTestFixture,
|
||||||
integrationTestFixture, outputHelper)
|
ITestOutputHelper outputHelper = null
|
||||||
|
) : base(integrationTestFixture, outputHelper)
|
||||||
{
|
{
|
||||||
Fixture = integrationTestFixture;
|
Fixture = integrationTestFixture;
|
||||||
}
|
}
|
||||||
@ -628,13 +727,15 @@ public abstract class TestWriteBase<TEntryPoint, TWContext> : TestFixtureCore<TE
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract class TestBase<TEntryPoint, TWContext, TRContext> : TestFixtureCore<TEntryPoint>
|
public abstract class TestBase<TEntryPoint, TWContext, TRContext> : TestFixtureCore<TEntryPoint>
|
||||||
//,IClassFixture<IntegrationTestFactory<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 TestBase(
|
protected TestBase(
|
||||||
TestFixture<TEntryPoint, TWContext, TRContext> integrationTestFixture, ITestOutputHelper outputHelper = null) :
|
TestFixture<TEntryPoint, TWContext, TRContext> integrationTestFixture,
|
||||||
|
ITestOutputHelper outputHelper = null
|
||||||
|
) :
|
||||||
base(integrationTestFixture, outputHelper)
|
base(integrationTestFixture, outputHelper)
|
||||||
{
|
{
|
||||||
Fixture = integrationTestFixture;
|
Fixture = integrationTestFixture;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using System.Globalization;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
@ -10,6 +11,6 @@ public class SlugifyParameterTransformer : IOutboundParameterTransformer
|
|||||||
// Slugify value
|
// Slugify value
|
||||||
return value == null
|
return value == null
|
||||||
? null
|
? null
|
||||||
: Regex.Replace(value.ToString() ?? string.Empty, "([a-z])([A-Z])", "$1-$2").ToLower();
|
: Regex.Replace(value.ToString() ?? string.Empty, "([a-z])([A-Z])", "$1-$2").ToLower(CultureInfo.CurrentCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ public static class InitialData
|
|||||||
ArriveDate.Of(new DateTime(2022, 1, 31, 14, 0, 0)),
|
ArriveDate.Of(new DateTime(2022, 1, 31, 14, 0, 0)),
|
||||||
AirportId.Of(Airports.Last().Id), DurationMinutes.Of(120m),
|
AirportId.Of(Airports.Last().Id), DurationMinutes.Of(120m),
|
||||||
FlightDate.Of(new DateTime(2022, 1, 31, 13, 0, 0)), global::Flight.Flights.Enums.FlightStatus.Completed,
|
FlightDate.Of(new DateTime(2022, 1, 31, 13, 0, 0)), global::Flight.Flights.Enums.FlightStatus.Completed,
|
||||||
Price.Of((decimal)8000))
|
Price.Of(8000))
|
||||||
};
|
};
|
||||||
|
|
||||||
Seats = new List<Seat>
|
Seats = new List<Seat>
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public class GetFlightByIdTests : FlightEndToEndTestBase
|
|||||||
await Fixture.SendAsync(command);
|
await Fixture.SendAsync(command);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var route = ApiRoutes.Flight.GetFlightById.Replace(ApiRoutes.Flight.Id, command.Id.ToString());
|
var route = ApiRoutes.Flight.GetFlightById.Replace(ApiRoutes.Flight.Id, command.Id.ToString(), StringComparison.CurrentCulture);
|
||||||
var result = await Fixture.HttpClient.GetAsync(route);
|
var result = await Fixture.HttpClient.GetAsync(route);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|||||||
@ -65,7 +65,7 @@ public static class DbContextFactory
|
|||||||
ArriveDate.Of( new DateTime(2022, 1, 31, 14, 0, 0)),
|
ArriveDate.Of( new DateTime(2022, 1, 31, 14, 0, 0)),
|
||||||
AirportId.Of( _airportId2), DurationMinutes.Of(120m),
|
AirportId.Of( _airportId2), DurationMinutes.Of(120m),
|
||||||
FlightDate.Of( new DateTime(2022, 1, 31)), FlightStatus.Completed,
|
FlightDate.Of( new DateTime(2022, 1, 31)), FlightStatus.Completed,
|
||||||
Price.Of((decimal)8000))
|
Price.Of(8000))
|
||||||
};
|
};
|
||||||
context.Flights.AddRange(flights);
|
context.Flights.AddRange(flights);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user