mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-13 12:15:46 +08:00
add some integration test for grpc services
This commit is contained in:
parent
e2a03daf46
commit
0467daee32
@ -22,7 +22,6 @@
|
||||
<PackageReference Include="MagicOnion.Abstractions" Version="4.4.0" />
|
||||
<PackageReference Include="MagicOnion.Client" Version="4.4.0" />
|
||||
<PackageReference Include="MagicOnion.Server" Version="4.4.0" />
|
||||
<PackageReference Include="MagicOnion.Server" Version="4.4.0" />
|
||||
<PackageReference Include="NSubstitute" Version="4.3.0" />
|
||||
<PackageReference Include="Polly" Version="7.2.3" />
|
||||
<PackageReference Include="protobuf-net.BuildTools" Version="3.0.115">
|
||||
@ -51,7 +50,6 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.14.1" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
|
||||
<PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" />
|
||||
@ -120,6 +118,10 @@
|
||||
<PackageReference Include="FluentAssertions" Version="6.2.0" />
|
||||
<PackageReference Include="MediatR" Version="9.0.0" />
|
||||
<PackageReference Include="Respawn" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="6.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.41.0" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ namespace BuildingBlocks.Contracts.Grpc;
|
||||
{
|
||||
UnaryResult<FlightResponseDto> GetById(long id);
|
||||
UnaryResult<IEnumerable<SeatResponseDto>> GetAvailableSeats(long flightId);
|
||||
UnaryResult<FlightResponseDto> ReserveSeat(ReserveSeatRequestDto request);
|
||||
UnaryResult<SeatResponseDto> ReserveSeat(ReserveSeatRequestDto request);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -26,10 +26,13 @@ public class CreateBookingCommandHandler : IRequestHandler<CreateBookingCommand,
|
||||
_eventStoreDbRepository = eventStoreDbRepository;
|
||||
|
||||
var channelFlight = GrpcChannel.ForAddress(grpcOptions.Value.FlightAddress);
|
||||
_flightGrpcService = new Lazy<IFlightGrpcService>(() => MagicOnionClient.Create<IFlightGrpcService>(channelFlight)).Value;
|
||||
_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;
|
||||
_passengerGrpcService =
|
||||
new Lazy<IPassengerGrpcService>(() => MagicOnionClient.Create<IPassengerGrpcService>(channelPassenger))
|
||||
.Value;
|
||||
}
|
||||
|
||||
public async Task<ulong> Handle(CreateBookingCommand command,
|
||||
@ -38,8 +41,10 @@ public class CreateBookingCommandHandler : IRequestHandler<CreateBookingCommand,
|
||||
Guard.Against.Null(command, nameof(command));
|
||||
|
||||
var flight = await _flightGrpcService.GetById(command.FlightId);
|
||||
|
||||
if (flight is null)
|
||||
throw new FlightNotFoundException();
|
||||
|
||||
var passenger = await _passengerGrpcService.GetById(command.PassengerId);
|
||||
|
||||
var emptySeat = (await _flightGrpcService.GetAvailableSeats(command.FlightId))?.First();
|
||||
|
||||
@ -32,9 +32,9 @@ public class FlightGrpcService : ServiceBase<IFlightGrpcService>, IFlightGrpcSer
|
||||
return result.Adapt<IEnumerable<SeatResponseDto>>();
|
||||
}
|
||||
|
||||
public async UnaryResult<FlightResponseDto> ReserveSeat(ReserveSeatRequestDto request)
|
||||
public async UnaryResult<SeatResponseDto> ReserveSeat(ReserveSeatRequestDto request)
|
||||
{
|
||||
var result = await _mediator.Send(new ReserveSeatCommand(request.FlightId, request.SeatNumber));
|
||||
return result.Adapt<FlightResponseDto>();
|
||||
return result.Adapt<SeatResponseDto>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
using AutoBogus;
|
||||
using BuildingBlocks.IdsGenerator;
|
||||
using Flight.Seats.Features.CreateSeat;
|
||||
|
||||
namespace Integration.Test.Fakes;
|
||||
|
||||
public class FakeCreateSeatCommand : AutoFaker<CreateSeatCommand>
|
||||
{
|
||||
public FakeCreateSeatCommand(long flightId)
|
||||
{
|
||||
RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId());
|
||||
RuleFor(r => r.FlightId, _ => flightId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
using Flight.Seats.Features.CreateSeat;
|
||||
using Flight.Seats.Models;
|
||||
|
||||
namespace Integration.Test.Fakes;
|
||||
|
||||
public static class FakeSeatCreated
|
||||
{
|
||||
public static Seat Generate(CreateSeatCommand command)
|
||||
{
|
||||
return Seat.Create(command.Id, command.SeatNumber, command.Type, command.Class, command.FlightId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Contracts.Grpc;
|
||||
using Flight.Flights.Features.GetAvailableFlights;
|
||||
using FluentAssertions;
|
||||
using Grpc.Net.Client;
|
||||
using Integration.Test.Fakes;
|
||||
using MagicOnion.Client;
|
||||
using Xunit;
|
||||
|
||||
namespace Integration.Test.Flight.Features;
|
||||
@ -11,10 +14,12 @@ namespace Integration.Test.Flight.Features;
|
||||
public class GetAvailableFlightsTests
|
||||
{
|
||||
private readonly TestFixture _fixture;
|
||||
private readonly GrpcChannel _channel;
|
||||
|
||||
public GetAvailableFlightsTests(TestFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
_channel = fixture.Channel;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Contracts.Grpc;
|
||||
using Flight.Flights.Features.GetAvailableFlights;
|
||||
using FluentAssertions;
|
||||
using Grpc.Net.Client;
|
||||
using Integration.Test.Fakes;
|
||||
using MagicOnion.Client;
|
||||
using Xunit;
|
||||
|
||||
namespace Integration.Test.Flight.Features;
|
||||
|
||||
[Collection(nameof(TestFixture))]
|
||||
public class GetAvailableSeatsTests
|
||||
{
|
||||
private readonly TestFixture _fixture;
|
||||
private readonly GrpcChannel _channel;
|
||||
|
||||
public GetAvailableSeatsTests(TestFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
_channel = fixture.Channel;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task should_return_available_seats_from_grpc_service()
|
||||
{
|
||||
// Arrange
|
||||
var flightCommand = new FakeCreateFlightCommand().Generate();
|
||||
var flightEntity = FakeFlightCreated.Generate(flightCommand);
|
||||
|
||||
await _fixture.InsertAsync(flightEntity);
|
||||
|
||||
var seatCommand1 = new FakeCreateSeatCommand(flightEntity.Id).Generate();
|
||||
var seatCommand2 = new FakeCreateSeatCommand(flightEntity.Id).Generate();
|
||||
var seatEntity1 = FakeSeatCreated.Generate(seatCommand1);
|
||||
var seatEntity2 = FakeSeatCreated.Generate(seatCommand2);
|
||||
|
||||
await _fixture.InsertAsync(seatEntity1, seatEntity2);
|
||||
|
||||
var flightGrpcClient = MagicOnionClient.Create<IFlightGrpcService>(_channel);
|
||||
|
||||
// Act
|
||||
var response = await flightGrpcClient.GetAvailableSeats(flightEntity.Id);
|
||||
|
||||
// Assert
|
||||
response?.Should().NotBeNull();
|
||||
response?.Count().Should().BeGreaterOrEqualTo(2);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Contracts.Grpc;
|
||||
using Flight.Flights.Features.GetFlightById;
|
||||
using FluentAssertions;
|
||||
using Grpc.Net.Client;
|
||||
using Integration.Test.Fakes;
|
||||
using MagicOnion.Client;
|
||||
using Xunit;
|
||||
|
||||
namespace Integration.Test.Flight.Features;
|
||||
@ -10,10 +13,12 @@ namespace Integration.Test.Flight.Features;
|
||||
public class GetFlightByIdTests
|
||||
{
|
||||
private readonly TestFixture _fixture;
|
||||
private readonly GrpcChannel _channel;
|
||||
|
||||
public GetFlightByIdTests(TestFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
_channel = fixture.Channel;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -33,4 +38,22 @@ public class GetFlightByIdTests
|
||||
response.Should().NotBeNull();
|
||||
response?.Id.Should().Be(flightEntity.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task should_retrive_a_flight_by_id_from_grpc_service()
|
||||
{
|
||||
// Arrange
|
||||
var command = new FakeCreateFlightCommand().Generate();
|
||||
var flightEntity = FakeFlightCreated.Generate(command);
|
||||
await _fixture.InsertAsync(flightEntity);
|
||||
|
||||
var flightGrpcClient = MagicOnionClient.Create<IFlightGrpcService>(_channel);
|
||||
|
||||
// Act
|
||||
var response = await flightGrpcClient.GetById(flightEntity.Id);
|
||||
|
||||
// Assert
|
||||
response?.Should().NotBeNull();
|
||||
response?.Id.Should().Be(flightEntity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Contracts.Grpc;
|
||||
using FluentAssertions;
|
||||
using Grpc.Net.Client;
|
||||
using Integration.Test.Fakes;
|
||||
using MagicOnion.Client;
|
||||
using Xunit;
|
||||
|
||||
namespace Integration.Test.Flight.Features;
|
||||
|
||||
[Collection(nameof(TestFixture))]
|
||||
public class ReserveSeatTests
|
||||
{
|
||||
private readonly TestFixture _fixture;
|
||||
private readonly GrpcChannel _channel;
|
||||
|
||||
public ReserveSeatTests(TestFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
_channel = fixture.Channel;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task should_return_valid_reserve_seat_from_grpc_service()
|
||||
{
|
||||
// Arrange
|
||||
var flightCommand = new FakeCreateFlightCommand().Generate();
|
||||
var flightEntity = FakeFlightCreated.Generate(flightCommand);
|
||||
|
||||
await _fixture.InsertAsync(flightEntity);
|
||||
|
||||
var seatCommand = new FakeCreateSeatCommand(flightEntity.Id).Generate();
|
||||
var seatEntity = FakeSeatCreated.Generate(seatCommand);
|
||||
|
||||
await _fixture.InsertAsync(seatEntity);
|
||||
|
||||
var flightGrpcClient = MagicOnionClient.Create<IFlightGrpcService>(_channel);
|
||||
|
||||
// Act
|
||||
var response = await flightGrpcClient.ReserveSeat(new ReserveSeatRequestDto{ FlightId = seatEntity.FlightId, SeatNumber = seatEntity.SeatNumber });
|
||||
|
||||
// Assert
|
||||
response?.Should().NotBeNull();
|
||||
response?.SeatNumber.Should().Be(seatEntity.SeatNumber);
|
||||
response?.FlightId.Should().Be(seatEntity.FlightId);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Domain.Model;
|
||||
using BuildingBlocks.EFCore;
|
||||
@ -7,6 +8,8 @@ using BuildingBlocks.MassTransit;
|
||||
using BuildingBlocks.Web;
|
||||
using Flight.Data;
|
||||
using Flight.Data.Seed;
|
||||
using Flight.GrpcServer;
|
||||
using Grpc.Net.Client;
|
||||
using MassTransit;
|
||||
using MassTransit.Testing;
|
||||
using MediatR;
|
||||
@ -43,9 +46,10 @@ public class TestFixture : IAsyncLifetime
|
||||
private IServiceScopeFactory _scopeFactory;
|
||||
private HttpClient _httpClient;
|
||||
private ITestHarness _testHarness;
|
||||
private GrpcChannel _channel;
|
||||
|
||||
public ITestHarness TestHarness => _testHarness;
|
||||
|
||||
public GrpcChannel Channel => _channel;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
@ -83,6 +87,11 @@ public class TestFixture : IAsyncLifetime
|
||||
|
||||
_httpClient = _factory.CreateClient(new WebApplicationFactoryClientOptions {AllowAutoRedirect = false});
|
||||
|
||||
_channel = GrpcChannel.ForAddress(_httpClient.BaseAddress!, new GrpcChannelOptions
|
||||
{
|
||||
HttpClient = _httpClient
|
||||
});
|
||||
|
||||
_checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
|
||||
|
||||
await EnsureDatabaseAsync();
|
||||
|
||||
@ -5,6 +5,7 @@ using BuildingBlocks.Domain.Model;
|
||||
using BuildingBlocks.EFCore;
|
||||
using BuildingBlocks.MassTransit;
|
||||
using BuildingBlocks.Web;
|
||||
using Grpc.Net.Client;
|
||||
using Identity.Data;
|
||||
using MassTransit;
|
||||
using MassTransit.Testing;
|
||||
@ -40,9 +41,9 @@ public class TestFixture : IAsyncLifetime
|
||||
private WebApplicationFactory<Program> _factory;
|
||||
private HttpClient _httpClient;
|
||||
private IServiceScopeFactory _scopeFactory;
|
||||
|
||||
private GrpcChannel _channel;
|
||||
public ITestHarness TestHarness { get; private set; }
|
||||
|
||||
public GrpcChannel Channel => _channel;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
@ -80,6 +81,8 @@ public class TestFixture : IAsyncLifetime
|
||||
|
||||
_httpClient = _factory.CreateClient(new WebApplicationFactoryClientOptions {AllowAutoRedirect = false});
|
||||
|
||||
_channel = GrpcChannel.ForAddress(_httpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = _httpClient});
|
||||
|
||||
_checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
|
||||
|
||||
await EnsureDatabaseAsync();
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
using AutoMapper;
|
||||
using Mapster;
|
||||
using Passenger.Passengers.Dtos;
|
||||
|
||||
namespace Passenger.Passengers.Features;
|
||||
|
||||
public class PassengerMappings : IRegister
|
||||
{
|
||||
public void Register(TypeAdapterConfig config)
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Passenger.Passengers.Dtos;
|
||||
|
||||
namespace Passenger.Passengers.Features;
|
||||
|
||||
public class ReservationMappings: Profile
|
||||
{
|
||||
public ReservationMappings()
|
||||
{
|
||||
CreateMap<Models.Passenger, PassengerResponseDto>();
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using BuildingBlocks.Contracts.Grpc;
|
||||
using FluentAssertions;
|
||||
using Grpc.Net.Client;
|
||||
using Integration.Test.Fakes;
|
||||
using MagicOnion.Client;
|
||||
using MassTransit.Testing;
|
||||
using Passenger.Passengers.Features.GetPassengerById;
|
||||
using Xunit;
|
||||
@ -13,11 +16,13 @@ public class GetPassengerByIdTests
|
||||
{
|
||||
private readonly TestFixture _fixture;
|
||||
private readonly ITestHarness _testHarness;
|
||||
private readonly GrpcChannel _channel;
|
||||
|
||||
public GetPassengerByIdTests(TestFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
_testHarness = _fixture.TestHarness;
|
||||
_channel = _fixture.Channel;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -39,4 +44,24 @@ public class GetPassengerByIdTests
|
||||
response.Should().NotBeNull();
|
||||
response?.Id.Should().Be(passengerEntity.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
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(passengerEntity.Id);
|
||||
|
||||
// Assert
|
||||
response?.Should().NotBeNull();
|
||||
response?.Id.Should().Be(passengerEntity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ using BuildingBlocks.Domain.Model;
|
||||
using BuildingBlocks.EFCore;
|
||||
using BuildingBlocks.MassTransit;
|
||||
using BuildingBlocks.Web;
|
||||
using Grpc.Net.Client;
|
||||
using MassTransit;
|
||||
using MassTransit.Testing;
|
||||
using MediatR;
|
||||
@ -40,9 +41,9 @@ public class TestFixture : IAsyncLifetime
|
||||
private WebApplicationFactory<Program> _factory;
|
||||
private HttpClient _httpClient;
|
||||
private IServiceScopeFactory _scopeFactory;
|
||||
|
||||
private GrpcChannel _channel;
|
||||
public ITestHarness TestHarness { get; private set; }
|
||||
|
||||
public GrpcChannel Channel => _channel;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
@ -79,6 +80,8 @@ public class TestFixture : IAsyncLifetime
|
||||
|
||||
_httpClient = _factory.CreateClient(new WebApplicationFactoryClientOptions {AllowAutoRedirect = false});
|
||||
|
||||
_channel = GrpcChannel.ForAddress(_httpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = _httpClient});
|
||||
|
||||
_checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
|
||||
|
||||
await EnsureDatabaseAsync();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user