mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-27 16:10:52 +08:00
complete integration tests
This commit is contained in:
parent
e62d9ea815
commit
c79be5a171
@ -17,7 +17,6 @@
|
|||||||
<PackageReference Include="Figgle" Version="0.4.0" />
|
<PackageReference Include="Figgle" Version="0.4.0" />
|
||||||
<PackageReference Include="FluentValidation" Version="10.3.6" />
|
<PackageReference Include="FluentValidation" Version="10.3.6" />
|
||||||
<PackageReference Include="FluentValidation.AspNetCore" 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" Version="4.4.0" />
|
||||||
<PackageReference Include="MagicOnion.Abstractions" Version="4.4.0" />
|
<PackageReference Include="MagicOnion.Abstractions" Version="4.4.0" />
|
||||||
<PackageReference Include="MagicOnion.Client" Version="4.4.0" />
|
<PackageReference Include="MagicOnion.Client" Version="4.4.0" />
|
||||||
@ -28,7 +27,6 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="protobuf-net.Grpc" Version="1.0.152" />
|
|
||||||
<PackageReference Include="Hellang.Middleware.ProblemDetails" Version="6.3.0" />
|
<PackageReference Include="Hellang.Middleware.ProblemDetails" Version="6.3.0" />
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||||
<PackageReference Include="IdGen" Version="3.0.0" />
|
<PackageReference Include="IdGen" Version="3.0.0" />
|
||||||
@ -52,7 +50,6 @@
|
|||||||
<PackageReference Include="MongoDB.Driver" Version="2.14.1" />
|
<PackageReference Include="MongoDB.Driver" Version="2.14.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
|
<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" Version="3.3.0" />
|
||||||
<PackageReference Include="Scrutor.AspNetCore" Version="3.3.0" />
|
<PackageReference Include="Scrutor.AspNetCore" Version="3.3.0" />
|
||||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||||
@ -121,7 +118,6 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="6.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="6.0.0" />
|
||||||
<PackageReference Include="Moq" Version="4.16.1" />
|
<PackageReference Include="Moq" Version="4.16.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
|
||||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.41.0" />
|
|
||||||
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@ namespace BuildingBlocks.Contracts.Grpc;
|
|||||||
public interface IPassengerGrpcService : IService<IPassengerGrpcService>
|
public interface IPassengerGrpcService : IService<IPassengerGrpcService>
|
||||||
{
|
{
|
||||||
UnaryResult<PassengerResponseDto> GetById(long id);
|
UnaryResult<PassengerResponseDto> GetById(long id);
|
||||||
UnaryResult<string> TTT(long id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using NSubstitute;
|
||||||
|
|
||||||
namespace BuildingBlocks.Web;
|
namespace BuildingBlocks.Web;
|
||||||
|
|
||||||
@ -57,4 +60,14 @@ public static class ServiceCollectionExtensions
|
|||||||
var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(TService));
|
var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(TService));
|
||||||
services.Remove(descriptor);
|
services.Remove(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection ReplaceServiceWithSingletonMock<TService>(this IServiceCollection services)
|
||||||
|
where TService : class
|
||||||
|
{
|
||||||
|
var service = services.FirstOrDefault(d => d.ServiceType == typeof(TService));
|
||||||
|
services.Remove(service);
|
||||||
|
|
||||||
|
services.AddSingleton(_ => Substitute.For<TService>());
|
||||||
|
return services;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,6 +50,9 @@ builder.Services.AddTransient<IBusPublisher, BusPublisher>();
|
|||||||
builder.Services.AddCustomMassTransit(typeof(BookingRoot).Assembly, env);
|
builder.Services.AddCustomMassTransit(typeof(BookingRoot).Assembly, env);
|
||||||
builder.Services.AddCustomOpenTelemetry();
|
builder.Services.AddCustomOpenTelemetry();
|
||||||
builder.Services.AddTransient<AuthHeaderHandler>();
|
builder.Services.AddTransient<AuthHeaderHandler>();
|
||||||
|
|
||||||
|
builder.Services.AddMagicOnionClients();
|
||||||
|
|
||||||
SnowFlakIdGenerator.Configure(3);
|
SnowFlakIdGenerator.Configure(3);
|
||||||
|
|
||||||
// ref: https://github.com/oskardudycz/EventSourcing.NetCore/tree/main/Sample/EventStoreDB/ECommerce
|
// ref: https://github.com/oskardudycz/EventSourcing.NetCore/tree/main/Sample/EventStoreDB/ECommerce
|
||||||
@ -84,4 +87,6 @@ app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name));
|
|||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
public partial class Program {}
|
public partial class Program
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@ -6,29 +6,16 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Grpc.AspNetCore" Version="2.46.0" />
|
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.46.3">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.21.1" />
|
|
||||||
<PackageReference Include="Grpc.Net.Client" Version="2.46.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Data\Migrations" />
|
<Folder Include="Data\Migrations" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Protobuf Include="Protos\foo.proto" GrpcServices="Client">
|
|
||||||
<Link>Protos\foo.proto</Link>
|
|
||||||
</Protobuf>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\..\BuildingBlocks\BuildingBlocks.csproj" />
|
<ProjectReference Include="..\..\..\..\BuildingBlocks\BuildingBlocks.csproj" />
|
||||||
|
|||||||
@ -1,14 +1,9 @@
|
|||||||
using Ardalis.GuardClauses;
|
using Ardalis.GuardClauses;
|
||||||
using Booking.Booking.Exceptions;
|
using Booking.Booking.Exceptions;
|
||||||
using Booking.Booking.Models.ValueObjects;
|
using Booking.Booking.Models.ValueObjects;
|
||||||
using Booking.Configuration;
|
|
||||||
using BuildingBlocks.Contracts.Grpc;
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
using BuildingBlocks.EventStoreDB.Repository;
|
using BuildingBlocks.EventStoreDB.Repository;
|
||||||
using Grpc.Net.Client;
|
|
||||||
using MagicOnion.Client;
|
|
||||||
using MapsterMapper;
|
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace Booking.Booking.Features.CreateBooking;
|
namespace Booking.Booking.Features.CreateBooking;
|
||||||
|
|
||||||
@ -18,52 +13,47 @@ public class CreateBookingCommandHandler : IRequestHandler<CreateBookingCommand,
|
|||||||
private readonly IFlightGrpcService _flightGrpcService;
|
private readonly IFlightGrpcService _flightGrpcService;
|
||||||
private readonly IPassengerGrpcService _passengerGrpcService;
|
private readonly IPassengerGrpcService _passengerGrpcService;
|
||||||
|
|
||||||
|
public CreateBookingCommandHandler(IEventStoreDBRepository<Models.Booking> eventStoreDbRepository,
|
||||||
public CreateBookingCommandHandler(
|
IPassengerGrpcService passengerGrpcService,
|
||||||
IOptions<GrpcOptions> grpcOptions,
|
IFlightGrpcService flightGrpcService)
|
||||||
IEventStoreDBRepository<Models.Booking> eventStoreDbRepository)
|
|
||||||
{
|
{
|
||||||
_eventStoreDbRepository = eventStoreDbRepository;
|
_eventStoreDbRepository = eventStoreDbRepository;
|
||||||
|
_passengerGrpcService = passengerGrpcService;
|
||||||
var channelFlight = GrpcChannel.ForAddress(grpcOptions.Value.FlightAddress);
|
_flightGrpcService = flightGrpcService;
|
||||||
_flightGrpcService = new Lazy<IFlightGrpcService>(() => MagicOnionClient.Create<IFlightGrpcService>(channelFlight)).Value;
|
|
||||||
|
|
||||||
var channelPassenger = GrpcChannel.ForAddress(grpcOptions.Value.PassengerAddress);
|
|
||||||
_passengerGrpcService = new Lazy<IPassengerGrpcService>(() => MagicOnionClient.Create<IPassengerGrpcService>(channelPassenger)).Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ulong> Handle(CreateBookingCommand command,
|
public async Task<ulong> Handle(CreateBookingCommand command,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Guard.Against.Null(command, nameof(command));
|
Guard.Against.Null(command, nameof(command));
|
||||||
//
|
|
||||||
// var flight = await _flightGrpcService.GetById(command.FlightId);
|
var flight = await _flightGrpcService.GetById(command.FlightId);
|
||||||
//
|
|
||||||
// if (flight is null)
|
if (flight is null)
|
||||||
// throw new FlightNotFoundException();
|
throw new FlightNotFoundException();
|
||||||
|
|
||||||
var passenger = await _passengerGrpcService.GetById(command.PassengerId);
|
var passenger = await _passengerGrpcService.GetById(command.PassengerId);
|
||||||
|
|
||||||
// var emptySeat = (await _flightGrpcService.GetAvailableSeats(command.FlightId))?.First();
|
var emptySeat = (await _flightGrpcService.GetAvailableSeats(command.FlightId))?.First();
|
||||||
//
|
|
||||||
// var reservation = await _eventStoreDbRepository.Find(command.Id, cancellationToken);
|
|
||||||
//
|
|
||||||
// if (reservation is not null && !reservation.IsDeleted)
|
|
||||||
// throw new BookingAlreadyExistException();
|
|
||||||
//
|
|
||||||
// var aggrigate = Models.Booking.Create(command.Id, new PassengerInfo(passenger.Name), new Trip(
|
|
||||||
// flight.FlightNumber, flight.AircraftId, flight.DepartureAirportId,
|
|
||||||
// flight.ArriveAirportId, flight.FlightDate, flight.Price, command.Description, emptySeat?.SeatNumber));
|
|
||||||
//
|
|
||||||
// await _flightGrpcService.ReserveSeat(new ReserveSeatRequestDto
|
|
||||||
// {
|
|
||||||
// FlightId = flight.Id, SeatNumber = emptySeat?.SeatNumber
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// var result = await _eventStoreDbRepository.Add(
|
|
||||||
// aggrigate,
|
|
||||||
// cancellationToken);
|
|
||||||
|
|
||||||
return 2;
|
var reservation = await _eventStoreDbRepository.Find(command.Id, cancellationToken);
|
||||||
|
|
||||||
|
if (reservation is not null && !reservation.IsDeleted)
|
||||||
|
throw new BookingAlreadyExistException();
|
||||||
|
|
||||||
|
var aggrigate = Models.Booking.Create(command.Id, new PassengerInfo(passenger.Name), new Trip(
|
||||||
|
flight.FlightNumber, flight.AircraftId, flight.DepartureAirportId,
|
||||||
|
flight.ArriveAirportId, flight.FlightDate, flight.Price, command.Description, emptySeat?.SeatNumber));
|
||||||
|
|
||||||
|
await _flightGrpcService.ReserveSeat(new ReserveSeatRequestDto
|
||||||
|
{
|
||||||
|
FlightId = flight.Id, SeatNumber = emptySeat?.SeatNumber
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await _eventStoreDbRepository.Add(
|
||||||
|
aggrigate,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
namespace Booking.Configuration;
|
|
||||||
|
|
||||||
public class RefitOptions
|
|
||||||
{
|
|
||||||
public string FlightAddress { get; set; }
|
|
||||||
public string PassengerAddress { get; set; }
|
|
||||||
}
|
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
using Booking.Configuration;
|
||||||
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
|
using BuildingBlocks.Web;
|
||||||
|
using Grpc.Net.Client;
|
||||||
|
using MagicOnion.Client;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Booking.Extensions;
|
||||||
|
|
||||||
|
public static class MagicOnionClientExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddMagicOnionClients(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
var grpcOptions = services.GetOptions<GrpcOptions>("Grpc");
|
||||||
|
|
||||||
|
services.AddSingleton(x => MagicOnionClient.Create<IPassengerGrpcService>(GrpcChannel.ForAddress(grpcOptions.PassengerAddress)));
|
||||||
|
services.AddSingleton(x => MagicOnionClient.Create<IFlightGrpcService>(GrpcChannel.ForAddress(grpcOptions.FlightAddress)));
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,25 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
option csharp_namespace = "GrpcSamples";
|
|
||||||
|
|
||||||
service FooService {
|
|
||||||
rpc GetFoo (FooRequest) returns (FooResponse);
|
|
||||||
|
|
||||||
rpc GetFoos(FooServerStreamingRequest) returns (stream FooResponse);
|
|
||||||
|
|
||||||
rpc SendFoos(stream FooRequest) returns (FooResponse);
|
|
||||||
|
|
||||||
rpc SendAndGetFoos(stream FooRequest) returns (stream FooResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
message FooRequest {
|
|
||||||
string message = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message FooServerStreamingRequest {
|
|
||||||
string message = 1;
|
|
||||||
int32 messageCount = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message FooResponse {
|
|
||||||
string message = 1;
|
|
||||||
}
|
|
||||||
@ -1,15 +1,14 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Collections.Generic;
|
||||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using BuildingBlocks.Contracts.Grpc;
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Grpc.Core;
|
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
using GrpcSamples;
|
|
||||||
using Integration.Test.Fakes;
|
using Integration.Test.Fakes;
|
||||||
using MagicOnion;
|
using MagicOnion;
|
||||||
using MagicOnion.Client;
|
|
||||||
using MassTransit.Testing;
|
using MassTransit.Testing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -18,91 +17,67 @@ namespace Integration.Test.Booking.Features;
|
|||||||
[Collection(nameof(IntegrationTestFixture))]
|
[Collection(nameof(IntegrationTestFixture))]
|
||||||
public class CreateBookingTests
|
public class CreateBookingTests
|
||||||
{
|
{
|
||||||
|
private readonly GrpcChannel _channel;
|
||||||
private readonly IntegrationTestFixture _fixture;
|
private readonly IntegrationTestFixture _fixture;
|
||||||
private readonly ITestHarness _testHarness;
|
private readonly ITestHarness _testHarness;
|
||||||
private readonly GrpcChannel _channel;
|
|
||||||
private FooService.FooServiceClient _fooServiceClient;
|
|
||||||
|
|
||||||
public CreateBookingTests(IntegrationTestFixture fixture)
|
public CreateBookingTests(IntegrationTestFixture fixture)
|
||||||
{
|
{
|
||||||
_fixture = fixture;
|
_fixture = fixture;
|
||||||
|
|
||||||
|
_fixture.RegisterTestServices(services =>
|
||||||
|
{
|
||||||
|
MockFlightGrpcServices(services);
|
||||||
|
MockPassengerGrpcServices(services);
|
||||||
|
});
|
||||||
|
|
||||||
_testHarness = fixture.TestHarness;
|
_testHarness = fixture.TestHarness;
|
||||||
_channel = fixture.Channel;
|
_channel = fixture.Channel;
|
||||||
_fooServiceClient = Substitute.For<FooService.FooServiceClient>();
|
|
||||||
|
|
||||||
_fooServiceClient.GetFoo(Arg.Any<FooRequest>())
|
|
||||||
.Returns(new FooResponse() {Message = "vvvvvvvvvvv"});
|
|
||||||
|
|
||||||
fixture.RegisterTestServices(services =>
|
|
||||||
{
|
|
||||||
services.AddSingleton(_fooServiceClient);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: add support test for event-store
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task should_create_booking_currectly()
|
public async Task should_create_booking_to_event_store_currectly()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var command = new FakeCreateBookingCommand().Generate();
|
var command = new FakeCreateBookingCommand().Generate();
|
||||||
|
|
||||||
// var passengerGrpcService = MagicOnionClient.Create<IPassengerGrpcService2>(_channel) as IPassengerGrpcService2;
|
|
||||||
// var b = await passengerGrpcService.GetById(1);
|
|
||||||
|
|
||||||
// var client = new FooService.FooServiceClient(_channel);
|
|
||||||
//
|
|
||||||
// var b = client.GetFoo(new FooRequest {Message = "tesssssssssst"});
|
|
||||||
_fooServiceClient = Substitute.For<FooService.FooServiceClient>();
|
|
||||||
|
|
||||||
_fooServiceClient.GetFooAsync(Arg.Any<FooRequest>())
|
|
||||||
.Returns(new AsyncUnaryCall<FooResponse>(Task.FromResult(new FooResponse() {Message = "uuuuuuuuu"}), null, null, null, null));
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var response = await _fixture.SendAsync(command);
|
var response = await _fixture.SendAsync(command);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
response.Should();
|
response.Should().BeGreaterOrEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MockPassengerGrpcServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.Replace(ServiceDescriptor.Singleton(x =>
|
||||||
|
{
|
||||||
|
var mock = Substitute.For<IPassengerGrpcService>();
|
||||||
|
mock.GetById(Arg.Any<long>())
|
||||||
|
.Returns(new UnaryResult<PassengerResponseDto>(new FakePassengerResponseDto().Generate()));
|
||||||
|
|
||||||
// [Fact]
|
return mock;
|
||||||
// public async Task should_retrive_a_passenger_by_id_currectly()
|
}));
|
||||||
// {
|
}
|
||||||
// // Arrange
|
|
||||||
// var userCreated = new FakeUserCreated().Generate();
|
private void MockFlightGrpcServices(IServiceCollection services)
|
||||||
// await _testHarness.Bus.Publish(userCreated);
|
{
|
||||||
// await _testHarness.Consumed.Any<UserCreated>();
|
services.Replace(ServiceDescriptor.Singleton(x =>
|
||||||
// var passengerEntity = FakePassengerCreated.Generate(userCreated);
|
{
|
||||||
// await _fixture.InsertAsync(passengerEntity);
|
var mock = Substitute.For<IFlightGrpcService>();
|
||||||
//
|
|
||||||
// var query = new GetPassengerQueryById(passengerEntity.Id);
|
mock.GetById(Arg.Any<long>())
|
||||||
//
|
.Returns(new UnaryResult<FlightResponseDto>(Task.FromResult(new FakeFlightResponseDto().Generate())));
|
||||||
// // Act
|
|
||||||
// var response = await _fixture.SendAsync(query);
|
mock.GetAvailableSeats(Arg.Any<long>())
|
||||||
//
|
.Returns(
|
||||||
// // Assert
|
new UnaryResult<IEnumerable<SeatResponseDto>>(Task.FromResult(FakeSeatsResponseDto.Generate())));
|
||||||
// response.Should().NotBeNull();
|
|
||||||
// response?.Id.Should().Be(passengerEntity.Id);
|
mock.ReserveSeat(new FakeReserveSeatRequestDto().Generate())
|
||||||
// }
|
.Returns(new UnaryResult<SeatResponseDto>(Task.FromResult(FakeSeatsResponseDto.Generate().First())));
|
||||||
//
|
|
||||||
// [Fact]
|
return mock;
|
||||||
// public async Task should_retrive_a_passenger_by_id_from_grpc_service()
|
}));
|
||||||
// {
|
}
|
||||||
// // Arrange
|
|
||||||
// var userCreated = new FakeUserCreated().Generate();
|
|
||||||
// await _testHarness.Bus.Publish(userCreated);
|
|
||||||
// await _testHarness.Consumed.Any<UserCreated>();
|
|
||||||
// var passengerEntity = FakePassengerCreated.Generate(userCreated);
|
|
||||||
// await _fixture.InsertAsync(passengerEntity);
|
|
||||||
//
|
|
||||||
// var passengerGrpcClient = MagicOnionClient.Create<IPassengerGrpcService>(_channel);
|
|
||||||
//
|
|
||||||
// // Act
|
|
||||||
// var response = await passengerGrpcClient.GetById(1111111111);
|
|
||||||
//
|
|
||||||
// // Assert
|
|
||||||
// response?.Should().NotBeNull();
|
|
||||||
// response?.Id.Should().Be(passengerEntity.Id);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,15 +55,6 @@ public class CustomWebApplicationFactory : WebApplicationFactory<Program>
|
|||||||
|
|
||||||
TestRegistrationServices?.Invoke(services);
|
TestRegistrationServices?.Invoke(services);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.UseDefaultServiceProvider((env, c) =>
|
|
||||||
{
|
|
||||||
// Handling Captive Dependency Problem
|
|
||||||
// https://ankitvijay.net/2020/03/17/net-core-and-di-beware-of-captive-dependency/
|
|
||||||
// https://blog.ploeh.dk/2014/06/02/captive-dependency/
|
|
||||||
if (env.HostingEnvironment.IsEnvironment("test") || env.HostingEnvironment.IsDevelopment())
|
|
||||||
c.ValidateScopes = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
|
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using AutoBogus;
|
||||||
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
|
|
||||||
|
namespace Integration.Test.Fakes;
|
||||||
|
|
||||||
|
public class FakeFlightResponseDto : AutoFaker<FlightResponseDto>
|
||||||
|
{
|
||||||
|
public FakeFlightResponseDto()
|
||||||
|
{
|
||||||
|
RuleFor(r => r.Id, _ => 1);
|
||||||
|
RuleFor(r => r.Price, _ => 100);
|
||||||
|
RuleFor(r => r.Status, _ => FlightStatus.Completed);
|
||||||
|
RuleFor(r => r.AircraftId, _ => 1);
|
||||||
|
RuleFor(r => r.ArriveAirportId, _ => 1);
|
||||||
|
RuleFor(r => r.ArriveDate, _ => DateTime.Now);
|
||||||
|
RuleFor(r => r.DepartureDate, _ => DateTime.Now);
|
||||||
|
RuleFor(r => r.FlightDate, _ => DateTime.Now);
|
||||||
|
RuleFor(r => r.FlightNumber, _ => "121LP");
|
||||||
|
RuleFor(r => r.DepartureAirportId, _ => 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
using AutoBogus;
|
||||||
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
|
using BuildingBlocks.IdsGenerator;
|
||||||
|
|
||||||
|
namespace Integration.Test.Fakes;
|
||||||
|
|
||||||
|
public class FakePassengerResponseDto : AutoFaker<PassengerResponseDto>
|
||||||
|
{
|
||||||
|
public FakePassengerResponseDto()
|
||||||
|
{
|
||||||
|
RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
using AutoBogus;
|
||||||
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
|
|
||||||
|
namespace Integration.Test.Fakes;
|
||||||
|
|
||||||
|
public class FakeReserveSeatRequestDto : AutoFaker<ReserveSeatRequestDto>
|
||||||
|
{
|
||||||
|
public FakeReserveSeatRequestDto()
|
||||||
|
{
|
||||||
|
RuleFor(r => r.FlightId, _ => 1);
|
||||||
|
RuleFor(r => r.SeatNumber, _ => "33F");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
|
|
||||||
|
namespace Integration.Test.Fakes;
|
||||||
|
|
||||||
|
public static class FakeSeatsResponseDto
|
||||||
|
{
|
||||||
|
public static IEnumerable<SeatResponseDto> Generate()
|
||||||
|
{
|
||||||
|
return new List<SeatResponseDto>()
|
||||||
|
{
|
||||||
|
new SeatResponseDto()
|
||||||
|
{
|
||||||
|
FlightId = 1,
|
||||||
|
Class = SeatClass.Economy,
|
||||||
|
Type = SeatType.Aisle,
|
||||||
|
SeatNumber = "33F",
|
||||||
|
Id = 1
|
||||||
|
},
|
||||||
|
new SeatResponseDto()
|
||||||
|
{
|
||||||
|
FlightId = 1,
|
||||||
|
Class = SeatClass.Economy,
|
||||||
|
Type = SeatType.Window,
|
||||||
|
SeatNumber = "22D",
|
||||||
|
Id = 2
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,15 +3,11 @@ using System.Net.Http;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Booking.Data;
|
using Booking.Data;
|
||||||
using BuildingBlocks.Domain.Model;
|
using BuildingBlocks.Domain.Model;
|
||||||
using BuildingBlocks.EFCore;
|
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
using MassTransit.Testing;
|
using MassTransit.Testing;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NSubstitute;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
@ -19,7 +15,9 @@ using Xunit.Abstractions;
|
|||||||
namespace Integration.Test;
|
namespace Integration.Test;
|
||||||
|
|
||||||
[CollectionDefinition(nameof(IntegrationTestFixture))]
|
[CollectionDefinition(nameof(IntegrationTestFixture))]
|
||||||
public class SliceFixtureCollection : ICollectionFixture<IntegrationTestFixture> { }
|
public class SliceFixtureCollection : ICollectionFixture<IntegrationTestFixture>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public class IntegrationTestFixture : IAsyncLifetime
|
public class IntegrationTestFixture : IAsyncLifetime
|
||||||
{
|
{
|
||||||
@ -37,21 +35,6 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
public ITestHarness TestHarness => CreateHarness();
|
public ITestHarness TestHarness => CreateHarness();
|
||||||
public GrpcChannel Channel => CreateChannel();
|
public GrpcChannel Channel => CreateChannel();
|
||||||
|
|
||||||
// ref: https://github.com/trbenning/serilog-sinks-xunit
|
|
||||||
public ILogger CreateLogger(ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
if (output != null)
|
|
||||||
{
|
|
||||||
return new LoggerConfiguration()
|
|
||||||
.WriteTo.TestOutput(output)
|
|
||||||
.CreateLogger();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterTestServices(Action<IServiceCollection> services) => _factory.TestRegistrationServices = services;
|
|
||||||
|
|
||||||
public virtual Task InitializeAsync()
|
public virtual Task InitializeAsync()
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@ -65,6 +48,22 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
await _factory.DisposeAsync();
|
await _factory.DisposeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ref: https://github.com/trbenning/serilog-sinks-xunit
|
||||||
|
public ILogger CreateLogger(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
if (output != null)
|
||||||
|
return new LoggerConfiguration()
|
||||||
|
.WriteTo.TestOutput(output)
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterTestServices(Action<IServiceCollection> services)
|
||||||
|
{
|
||||||
|
_factory.TestRegistrationServices = services;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ExecuteScopeAsync(Func<IServiceProvider, Task> action)
|
public async Task ExecuteScopeAsync(Func<IServiceProvider, Task> action)
|
||||||
{
|
{
|
||||||
using var scope = ServiceProvider.CreateScope();
|
using var scope = ServiceProvider.CreateScope();
|
||||||
@ -202,18 +201,6 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ITestHarness CreateHarness()
|
private ITestHarness CreateHarness()
|
||||||
{
|
{
|
||||||
var harness = ServiceProvider.GetTestHarness();
|
var harness = ServiceProvider.GetTestHarness();
|
||||||
@ -225,16 +212,4 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
{
|
{
|
||||||
return GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient});
|
return GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EnsureDatabaseAsync()
|
|
||||||
{
|
|
||||||
using var scope = ServiceProvider.CreateScope();
|
|
||||||
|
|
||||||
var context = scope.ServiceProvider.GetRequiredService<BookingDbContext>();
|
|
||||||
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
|
||||||
|
|
||||||
await context.Database.MigrateAsync();
|
|
||||||
|
|
||||||
foreach (var seeder in seeders) await seeder.SeedAllAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,15 +55,6 @@ public class CustomWebApplicationFactory : WebApplicationFactory<Program>
|
|||||||
|
|
||||||
TestRegistrationServices?.Invoke(services);
|
TestRegistrationServices?.Invoke(services);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.UseDefaultServiceProvider((env, c) =>
|
|
||||||
{
|
|
||||||
// Handling Captive Dependency Problem
|
|
||||||
// https://ankitvijay.net/2020/03/17/net-core-and-di-beware-of-captive-dependency/
|
|
||||||
// https://blog.ploeh.dk/2014/06/02/captive-dependency/
|
|
||||||
if (env.HostingEnvironment.IsEnvironment("test") || env.HostingEnvironment.IsDevelopment())
|
|
||||||
c.ValidateScopes = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
|
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
|
||||||
|
|||||||
@ -202,18 +202,6 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ITestHarness CreateHarness()
|
private ITestHarness CreateHarness()
|
||||||
{
|
{
|
||||||
var harness = ServiceProvider.GetTestHarness();
|
var harness = ServiceProvider.GetTestHarness();
|
||||||
@ -225,16 +213,4 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
{
|
{
|
||||||
return GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient});
|
return GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EnsureDatabaseAsync()
|
|
||||||
{
|
|
||||||
using var scope = ServiceProvider.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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,15 +55,6 @@ public class CustomWebApplicationFactory : WebApplicationFactory<Program>
|
|||||||
|
|
||||||
TestRegistrationServices?.Invoke(services);
|
TestRegistrationServices?.Invoke(services);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.UseDefaultServiceProvider((env, c) =>
|
|
||||||
{
|
|
||||||
// Handling Captive Dependency Problem
|
|
||||||
// https://ankitvijay.net/2020/03/17/net-core-and-di-beware-of-captive-dependency/
|
|
||||||
// https://blog.ploeh.dk/2014/06/02/captive-dependency/
|
|
||||||
if (env.HostingEnvironment.IsEnvironment("test") || env.HostingEnvironment.IsDevelopment())
|
|
||||||
c.ValidateScopes = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
|
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
|
||||||
|
|||||||
@ -202,18 +202,6 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ITestHarness CreateHarness()
|
private ITestHarness CreateHarness()
|
||||||
{
|
{
|
||||||
var harness = ServiceProvider.GetTestHarness();
|
var harness = ServiceProvider.GetTestHarness();
|
||||||
@ -225,16 +213,4 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
{
|
{
|
||||||
return GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient});
|
return GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EnsureDatabaseAsync()
|
|
||||||
{
|
|
||||||
using var scope = ServiceProvider.CreateScope();
|
|
||||||
|
|
||||||
var context = scope.ServiceProvider.GetRequiredService<IdentityContext>();
|
|
||||||
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
|
||||||
|
|
||||||
await context.Database.MigrateAsync();
|
|
||||||
|
|
||||||
foreach (var seeder in seeders) await seeder.SeedAllAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ using BuildingBlocks.Mapster;
|
|||||||
using BuildingBlocks.MassTransit;
|
using BuildingBlocks.MassTransit;
|
||||||
using BuildingBlocks.OpenTelemetry;
|
using BuildingBlocks.OpenTelemetry;
|
||||||
using BuildingBlocks.Swagger;
|
using BuildingBlocks.Swagger;
|
||||||
using BuildingBlocks.Utils;
|
|
||||||
using BuildingBlocks.Web;
|
using BuildingBlocks.Web;
|
||||||
using Figgle;
|
using Figgle;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
@ -17,10 +16,8 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
|||||||
using Passenger;
|
using Passenger;
|
||||||
using Passenger.Data;
|
using Passenger.Data;
|
||||||
using Passenger.Extensions;
|
using Passenger.Extensions;
|
||||||
using Passenger.Services;
|
|
||||||
using Prometheus;
|
using Prometheus;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Server;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
var configuration = builder.Configuration;
|
var configuration = builder.Configuration;
|
||||||
@ -41,8 +38,6 @@ builder.Services.AddValidatorsFromAssembly(typeof(PassengerRoot).Assembly);
|
|||||||
builder.Services.AddCustomProblemDetails();
|
builder.Services.AddCustomProblemDetails();
|
||||||
builder.Services.AddCustomMapster(typeof(PassengerRoot).Assembly);
|
builder.Services.AddCustomMapster(typeof(PassengerRoot).Assembly);
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddHttpContextAccessor();
|
||||||
builder.Services.AddScoped<IGreeter, Greeter>();
|
|
||||||
|
|
||||||
builder.Services.AddTransient<IEventMapper, EventMapper>();
|
builder.Services.AddTransient<IEventMapper, EventMapper>();
|
||||||
|
|
||||||
builder.Services.AddCustomMassTransit(typeof(PassengerRoot).Assembly, env);
|
builder.Services.AddCustomMassTransit(typeof(PassengerRoot).Assembly, env);
|
||||||
@ -77,7 +72,6 @@ app.UseEndpoints(endpoints =>
|
|||||||
{
|
{
|
||||||
endpoints.MapControllers();
|
endpoints.MapControllers();
|
||||||
endpoints.MapMetrics();
|
endpoints.MapMetrics();
|
||||||
endpoints.MapGrpcService<TesterService>();
|
|
||||||
endpoints.MapMagicOnionService();
|
endpoints.MapMagicOnionService();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,4 +79,6 @@ app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name));
|
|||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
public partial class Program {}
|
public partial class Program
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@ -21,9 +21,4 @@ public class PassengerGrpcService : ServiceBase<IPassengerGrpcService>, IPasseng
|
|||||||
var result = await _mediator.Send(new GetPassengerQueryById(id));
|
var result = await _mediator.Send(new GetPassengerQueryById(id));
|
||||||
return result.Adapt<PassengerResponseDto>();
|
return result.Adapt<PassengerResponseDto>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnaryResult<string> TTT(long id)
|
|
||||||
{
|
|
||||||
return new UnaryResult<string>("hiiiiiiiiiiiiiiii");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,13 +6,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.21.1" />
|
|
||||||
<PackageReference Include="Grpc.AspNetCore" Version="2.46.0" />
|
|
||||||
<PackageReference Include="Grpc.Net.Client" Version="2.46.0" />
|
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.46.3">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
@ -22,18 +15,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Data\Migrations" />
|
<Folder Include="Data\Migrations" />
|
||||||
<Folder Include="Extensions" />
|
<Folder Include="Extensions" />
|
||||||
<Folder Include="Protos" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\..\BuildingBlocks\BuildingBlocks.csproj" />
|
<ProjectReference Include="..\..\..\..\BuildingBlocks\BuildingBlocks.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Protobuf Include="Protos\test.proto" GrpcServices="Both">
|
|
||||||
<Link>Protos\test.proto</Link>
|
|
||||||
</Protobuf>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
// Copyright 2019 The gRPC Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package test;
|
|
||||||
|
|
||||||
service Tester {
|
|
||||||
rpc SayHelloUnary (HelloRequest) returns (HelloReply);
|
|
||||||
rpc SayHelloServerStreaming (HelloRequest) returns (stream HelloReply);
|
|
||||||
rpc SayHelloClientStreaming (stream HelloRequest) returns (HelloReply);
|
|
||||||
rpc SayHelloBidirectionalStreaming (stream HelloRequest) returns (stream HelloReply);
|
|
||||||
}
|
|
||||||
|
|
||||||
message HelloRequest {
|
|
||||||
string name = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message HelloReply {
|
|
||||||
string message = 1;
|
|
||||||
}
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
#region Copyright notice and license
|
|
||||||
|
|
||||||
// Copyright 2019 The gRPC Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Server
|
|
||||||
{
|
|
||||||
public class Greeter : IGreeter
|
|
||||||
{
|
|
||||||
private readonly ILogger<Greeter> _logger;
|
|
||||||
|
|
||||||
public Greeter(ILoggerFactory loggerFactory)
|
|
||||||
{
|
|
||||||
_logger = loggerFactory.CreateLogger<Greeter>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Greet(string name)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"Creating greeting to {name}");
|
|
||||||
return $"Hello {name}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
#region Copyright notice and license
|
|
||||||
|
|
||||||
// Copyright 2019 The gRPC Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
namespace Server
|
|
||||||
{
|
|
||||||
public interface IGreeter
|
|
||||||
{
|
|
||||||
string Greet(string name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
using Grpc.Core;
|
|
||||||
using Server;
|
|
||||||
using Test;
|
|
||||||
|
|
||||||
namespace Passenger.Services;
|
|
||||||
|
|
||||||
public class TesterService: Tester.TesterBase
|
|
||||||
{
|
|
||||||
private readonly IGreeter _greeter;
|
|
||||||
|
|
||||||
public TesterService(IGreeter greeter)
|
|
||||||
{
|
|
||||||
_greeter = greeter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<HelloReply> SayHelloUnary(HelloRequest request,
|
|
||||||
ServerCallContext context)
|
|
||||||
{
|
|
||||||
var message = _greeter.Greet(request.Name);
|
|
||||||
return Task.FromResult(new HelloReply { Message = message });
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task SayHelloServerStreaming(HelloRequest request,
|
|
||||||
IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
|
|
||||||
{
|
|
||||||
var i = 0;
|
|
||||||
while (!context.CancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
var message = _greeter.Greet($"{request.Name} {++i}");
|
|
||||||
await responseStream.WriteAsync(new HelloReply { Message = message });
|
|
||||||
|
|
||||||
await Task.Delay(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<HelloReply> SayHelloClientStreaming(
|
|
||||||
IAsyncStreamReader<HelloRequest> requestStream, ServerCallContext context)
|
|
||||||
{
|
|
||||||
var names = new List<string>();
|
|
||||||
|
|
||||||
await foreach (var request in requestStream.ReadAllAsync())
|
|
||||||
{
|
|
||||||
names.Add(request.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = _greeter.Greet(string.Join(", ", names));
|
|
||||||
return new HelloReply { Message = message };
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task SayHelloBidirectionalStreaming(
|
|
||||||
IAsyncStreamReader<HelloRequest> requestStream,
|
|
||||||
IServerStreamWriter<HelloReply> responseStream,
|
|
||||||
ServerCallContext context)
|
|
||||||
{
|
|
||||||
await foreach (var request in requestStream.ReadAllAsync())
|
|
||||||
{
|
|
||||||
await responseStream.WriteAsync(
|
|
||||||
new HelloReply { Message = _greeter.Greet(request.Name) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
using BuildingBlocks.MassTransit;
|
using BuildingBlocks.MassTransit;
|
||||||
using BuildingBlocks.Web;
|
using BuildingBlocks.Web;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
@ -49,19 +50,12 @@ public class CustomWebApplicationFactory : WebApplicationFactory<Program>
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.ReplaceServiceWithSingletonMock<IPassengerGrpcService>();
|
||||||
|
|
||||||
Checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
|
Checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
|
||||||
|
|
||||||
TestRegistrationServices?.Invoke(services);
|
TestRegistrationServices?.Invoke(services);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.UseDefaultServiceProvider((env, c) =>
|
|
||||||
{
|
|
||||||
// Handling Captive Dependency Problem
|
|
||||||
// https://ankitvijay.net/2020/03/17/net-core-and-di-beware-of-captive-dependency/
|
|
||||||
// https://blog.ploeh.dk/2014/06/02/captive-dependency/
|
|
||||||
if (env.HostingEnvironment.IsEnvironment("test") || env.HostingEnvironment.IsDevelopment())
|
|
||||||
c.ValidateScopes = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
|
private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
|
||||||
|
|||||||
@ -7,6 +7,6 @@ public static class FakePassengerCreated
|
|||||||
{
|
{
|
||||||
public static global::Passenger.Passengers.Models.Passenger Generate(UserCreated userCreated)
|
public static global::Passenger.Passengers.Models.Passenger Generate(UserCreated userCreated)
|
||||||
{
|
{
|
||||||
return global::Passenger.Passengers.Models.Passenger.Create(1000, userCreated.Name, userCreated.PassportNumber);
|
return global::Passenger.Passengers.Models.Passenger.Create(SnowFlakIdGenerator.NewId(), userCreated.Name, userCreated.PassportNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
using AutoBogus;
|
||||||
|
using Passenger.Passengers.Dtos;
|
||||||
|
|
||||||
|
namespace Integration.Test.Fakes;
|
||||||
|
|
||||||
|
public class FakePassengerResponseDto : AutoFaker<PassengerResponseDto>
|
||||||
|
{
|
||||||
|
public FakePassengerResponseDto(long id)
|
||||||
|
{
|
||||||
|
RuleFor(r => r.Id, _ => id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ public class FakeUserCreated : AutoFaker<UserCreated>
|
|||||||
{
|
{
|
||||||
public FakeUserCreated()
|
public FakeUserCreated()
|
||||||
{
|
{
|
||||||
RuleFor(r => r.Id, _ => _.Random.Number(50, 100000));
|
RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId());
|
||||||
RuleFor(r => r.Name, _ => "Meysam");
|
RuleFor(r => r.Name, _ => "Meysam");
|
||||||
RuleFor(r => r.PassportNumber, _ => "1299878");
|
RuleFor(r => r.PassportNumber, _ => "1299878");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,23 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BuildingBlocks.Domain.Model;
|
using BuildingBlocks.Domain.Model;
|
||||||
using BuildingBlocks.EFCore;
|
using BuildingBlocks.EFCore;
|
||||||
|
using Grpc.Core;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
|
using MagicOnion;
|
||||||
|
using MagicOnion.Client;
|
||||||
|
using MagicOnion.Server;
|
||||||
using MassTransit.Testing;
|
using MassTransit.Testing;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Passenger.Data;
|
using Passenger.Data;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@ -202,18 +210,6 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ITestHarness CreateHarness()
|
private ITestHarness CreateHarness()
|
||||||
{
|
{
|
||||||
var harness = ServiceProvider.GetTestHarness();
|
var harness = ServiceProvider.GetTestHarness();
|
||||||
@ -225,16 +221,4 @@ public class IntegrationTestFixture : IAsyncLifetime
|
|||||||
{
|
{
|
||||||
return GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient});
|
return GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EnsureDatabaseAsync()
|
|
||||||
{
|
|
||||||
using var scope = ServiceProvider.CreateScope();
|
|
||||||
|
|
||||||
var context = scope.ServiceProvider.GetRequiredService<PassengerDbContext>();
|
|
||||||
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
|
||||||
|
|
||||||
await context.Database.MigrateAsync();
|
|
||||||
|
|
||||||
foreach (var seeder in seeders) await seeder.SeedAllAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,12 @@
|
|||||||
using System;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Channels;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||||
using BuildingBlocks.Contracts.Grpc;
|
using BuildingBlocks.Contracts.Grpc;
|
||||||
using BuildingBlocks.IdsGenerator;
|
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Grpc.Core;
|
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
using Integration.Test.Fakes;
|
using Integration.Test.Fakes;
|
||||||
using MagicOnion;
|
|
||||||
using MagicOnion.Client;
|
using MagicOnion.Client;
|
||||||
using MassTransit.Testing;
|
using MassTransit.Testing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Moq;
|
|
||||||
using NSubstitute;
|
|
||||||
using Passenger.Passengers.Features.GetPassengerById;
|
using Passenger.Passengers.Features.GetPassengerById;
|
||||||
using Server;
|
|
||||||
using Test;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Integration.Test.Passenger.Features;
|
namespace Integration.Test.Passenger.Features;
|
||||||
@ -25,24 +15,12 @@ namespace Integration.Test.Passenger.Features;
|
|||||||
public class GetPassengerByIdTests
|
public class GetPassengerByIdTests
|
||||||
{
|
{
|
||||||
private readonly IntegrationTestFixture _fixture;
|
private readonly IntegrationTestFixture _fixture;
|
||||||
private readonly GrpcChannel _channel;
|
|
||||||
private readonly ITestHarness _testHarness;
|
private readonly ITestHarness _testHarness;
|
||||||
private IPassengerGrpcService _passengerGrpcService;
|
private readonly GrpcChannel _channel;
|
||||||
|
|
||||||
public GetPassengerByIdTests(IntegrationTestFixture fixture)
|
public GetPassengerByIdTests(IntegrationTestFixture fixture)
|
||||||
{
|
{
|
||||||
_fixture = fixture;
|
_fixture = fixture;
|
||||||
var mockGreeter = Substitute.For<IGreeter>();
|
|
||||||
|
|
||||||
mockGreeter.Greet(Arg.Any<string>())
|
|
||||||
.Returns("heyyyyyyyyyyyyyyy");
|
|
||||||
|
|
||||||
|
|
||||||
_fixture.RegisterTestServices(services =>
|
|
||||||
{
|
|
||||||
services.AddSingleton(mockGreeter);
|
|
||||||
});
|
|
||||||
|
|
||||||
_testHarness = _fixture.TestHarness;
|
_testHarness = _fixture.TestHarness;
|
||||||
_channel = _fixture.Channel;
|
_channel = _fixture.Channel;
|
||||||
}
|
}
|
||||||
@ -67,7 +45,6 @@ public class GetPassengerByIdTests
|
|||||||
response?.Id.Should().Be(passengerEntity.Id);
|
response?.Id.Should().Be(passengerEntity.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task should_retrive_a_passenger_by_id_from_grpc_service()
|
public async Task should_retrive_a_passenger_by_id_from_grpc_service()
|
||||||
{
|
{
|
||||||
@ -87,18 +64,4 @@ public class GetPassengerByIdTests
|
|||||||
response?.Should().NotBeNull();
|
response?.Should().NotBeNull();
|
||||||
response?.Id.Should().Be(passengerEntity.Id);
|
response?.Id.Should().Be(passengerEntity.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task should_retrive_a_passenger_by_id_from_grpc_service_2()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var client = new Tester.TesterClient(_fixture.Channel);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var response = await client.SayHelloUnaryAsync(
|
|
||||||
new HelloRequest {Name = "Joe"});
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("heyyyyyyyyyyyyyyy", response.Message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user