diff --git a/booking-microservices-sample.sln b/booking-microservices-sample.sln index cbb185a..5599825 100644 --- a/booking-microservices-sample.sln +++ b/booking-microservices-sample.sln @@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Ser EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Passenger\tests\IntegrationTest\Integration.Test.csproj", "{539364C8-88B1-48A3-8406-D0B19FF30509}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Booking\tests\IntegrationTest\Integration.Test.csproj", "{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -96,6 +98,7 @@ Global {6B6603C8-D8B6-4775-9C7A-FFE6058070C2} = {C6EE337B-91EA-472A-87C7-E9528408CE59} {BC7871B8-BB18-4BCC-96A8-7324C11BF4A2} = {295284BA-D4E4-40AA-A2C2-BE36343F7DE6} {539364C8-88B1-48A3-8406-D0B19FF30509} = {C1EBE17D-BFAD-47DA-88EB-BB073B84593E} + {50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC} = {5185D5C5-0EAD-49D5-B405-93B939F3639B} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -150,5 +153,9 @@ Global {539364C8-88B1-48A3-8406-D0B19FF30509}.Debug|Any CPU.Build.0 = Debug|Any CPU {539364C8-88B1-48A3-8406-D0B19FF30509}.Release|Any CPU.ActiveCfg = Release|Any CPU {539364C8-88B1-48A3-8406-D0B19FF30509}.Release|Any CPU.Build.0 = Release|Any CPU + {50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/BuildingBlocks/Contracts/Grpc/PassengerGrpcContracts.cs b/src/BuildingBlocks/Contracts/Grpc/PassengerGrpcContracts.cs index 6bf08ed..5e8e7b1 100644 --- a/src/BuildingBlocks/Contracts/Grpc/PassengerGrpcContracts.cs +++ b/src/BuildingBlocks/Contracts/Grpc/PassengerGrpcContracts.cs @@ -6,6 +6,7 @@ namespace BuildingBlocks.Contracts.Grpc; public interface IPassengerGrpcService : IService { UnaryResult GetById(long id); + UnaryResult TTT(long id); } diff --git a/src/Services/Booking/src/Booking.Api/Program.cs b/src/Services/Booking/src/Booking.Api/Program.cs index 674b2e2..71dc7fb 100644 --- a/src/Services/Booking/src/Booking.Api/Program.cs +++ b/src/Services/Booking/src/Booking.Api/Program.cs @@ -12,7 +12,6 @@ using BuildingBlocks.Mapster; using BuildingBlocks.MassTransit; using BuildingBlocks.OpenTelemetry; using BuildingBlocks.Swagger; -using BuildingBlocks.Utils; using BuildingBlocks.Web; using Figgle; using FluentValidation; @@ -66,7 +65,7 @@ if (app.Environment.IsDevelopment()) } app.UseSerilogRequestLogging(); -app.UseMigrations(env); +app.UseMigrations(); app.UseCorrelationId(); app.UseRouting(); app.UseHttpMetrics(); @@ -84,3 +83,5 @@ app.UseEndpoints(endpoints => app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name)); app.Run(); + +public partial class Program {} diff --git a/src/Services/Booking/src/Booking/Booking.csproj b/src/Services/Booking/src/Booking/Booking.csproj index 620f3da..17ff60f 100644 --- a/src/Services/Booking/src/Booking/Booking.csproj +++ b/src/Services/Booking/src/Booking/Booking.csproj @@ -6,18 +6,32 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive + + - + - + + Protos\foo.proto + + + + + + diff --git a/src/Services/Booking/src/Booking/Booking/Features/CreateBooking/CreateBookingCommandHandler.cs b/src/Services/Booking/src/Booking/Booking/Features/CreateBooking/CreateBookingCommandHandler.cs index 0776d67..0ef29a5 100644 --- a/src/Services/Booking/src/Booking/Booking/Features/CreateBooking/CreateBookingCommandHandler.cs +++ b/src/Services/Booking/src/Booking/Booking/Features/CreateBooking/CreateBookingCommandHandler.cs @@ -26,47 +26,44 @@ public class CreateBookingCommandHandler : IRequestHandler(() => MagicOnionClient.Create(channelFlight)).Value; + _flightGrpcService = new Lazy(() => MagicOnionClient.Create(channelFlight)).Value; var channelPassenger = GrpcChannel.ForAddress(grpcOptions.Value.PassengerAddress); - _passengerGrpcService = - new Lazy(() => MagicOnionClient.Create(channelPassenger)) - .Value; + _passengerGrpcService = new Lazy(() => MagicOnionClient.Create(channelPassenger)).Value; } public async Task Handle(CreateBookingCommand command, CancellationToken cancellationToken) { - Guard.Against.Null(command, nameof(command)); - - var flight = await _flightGrpcService.GetById(command.FlightId); - - if (flight is null) - throw new FlightNotFoundException(); + // 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(); + // 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); - 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; + return 2; } } diff --git a/src/Services/Booking/src/Booking/Extensions/MigrationsExtensions.cs b/src/Services/Booking/src/Booking/Extensions/MigrationsExtensions.cs index e27c6e8..b7f67b9 100644 --- a/src/Services/Booking/src/Booking/Extensions/MigrationsExtensions.cs +++ b/src/Services/Booking/src/Booking/Extensions/MigrationsExtensions.cs @@ -10,13 +10,11 @@ namespace Booking.Extensions; public static class MigrationsExtensions { - public static IApplicationBuilder UseMigrations(this IApplicationBuilder app, IWebHostEnvironment env) + public static IApplicationBuilder UseMigrations(this IApplicationBuilder app) { - if (!env.IsEnvironment("test")) - { - MigrateDatabase(app.ApplicationServices); - SeedData(app.ApplicationServices); - } + MigrateDatabase(app.ApplicationServices); + SeedData(app.ApplicationServices); + return app; } diff --git a/src/Services/Booking/tests/IntegrationTest/Booking/Features/CreateBookingTests.cs b/src/Services/Booking/tests/IntegrationTest/Booking/Features/CreateBookingTests.cs new file mode 100644 index 0000000..6869128 --- /dev/null +++ b/src/Services/Booking/tests/IntegrationTest/Booking/Features/CreateBookingTests.cs @@ -0,0 +1,108 @@ +using System.Threading.Tasks; +using BuildingBlocks.Contracts.EventBus.Messages; +using BuildingBlocks.Contracts.Grpc; +using FluentAssertions; +using Grpc.Core; +using Grpc.Net.Client; +using GrpcSamples; +using Integration.Test.Fakes; +using MagicOnion; +using MagicOnion.Client; +using MassTransit.Testing; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Xunit; + +namespace Integration.Test.Booking.Features; + +[Collection(nameof(IntegrationTestFixture))] +public class CreateBookingTests +{ + private readonly IntegrationTestFixture _fixture; + private readonly ITestHarness _testHarness; + private readonly GrpcChannel _channel; + private FooService.FooServiceClient _fooServiceClient; + + public CreateBookingTests(IntegrationTestFixture fixture) + { + _fixture = fixture; + _testHarness = fixture.TestHarness; + _channel = fixture.Channel; + _fooServiceClient = Substitute.For(); + + _fooServiceClient.GetFoo(Arg.Any()) + .Returns(new FooResponse() {Message = "vvvvvvvvvvv"}); + + fixture.RegisterTestServices(services => + { + services.AddSingleton(_fooServiceClient); + }); + } + + [Fact] + public async Task should_create_booking_currectly() + { + // Arrange + var command = new FakeCreateBookingCommand().Generate(); + + // var passengerGrpcService = MagicOnionClient.Create(_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(); + + _fooServiceClient.GetFooAsync(Arg.Any()) + .Returns(new AsyncUnaryCall(Task.FromResult(new FooResponse() {Message = "uuuuuuuuu"}), null, null, null, null)); + // + + + // Act + var response = await _fixture.SendAsync(command); + + // Assert + response.Should(); + } + + + // [Fact] + // public async Task should_retrive_a_passenger_by_id_currectly() + // { + // // Arrange + // var userCreated = new FakeUserCreated().Generate(); + // await _testHarness.Bus.Publish(userCreated); + // await _testHarness.Consumed.Any(); + // var passengerEntity = FakePassengerCreated.Generate(userCreated); + // await _fixture.InsertAsync(passengerEntity); + // + // var query = new GetPassengerQueryById(passengerEntity.Id); + // + // // Act + // var response = await _fixture.SendAsync(query); + // + // // Assert + // 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(); + // var passengerEntity = FakePassengerCreated.Generate(userCreated); + // await _fixture.InsertAsync(passengerEntity); + // + // var passengerGrpcClient = MagicOnionClient.Create(_channel); + // + // // Act + // var response = await passengerGrpcClient.GetById(1111111111); + // + // // Assert + // response?.Should().NotBeNull(); + // response?.Id.Should().Be(passengerEntity.Id); + // } +} diff --git a/src/Services/Booking/tests/IntegrationTest/Fakes/FakeCreateBookingCommand.cs b/src/Services/Booking/tests/IntegrationTest/Fakes/FakeCreateBookingCommand.cs new file mode 100644 index 0000000..97ba209 --- /dev/null +++ b/src/Services/Booking/tests/IntegrationTest/Fakes/FakeCreateBookingCommand.cs @@ -0,0 +1,15 @@ +using AutoBogus; +using Booking.Booking.Features.CreateBooking; +using BuildingBlocks.IdsGenerator; + +namespace Integration.Test.Fakes; + +public sealed class FakeCreateBookingCommand : AutoFaker +{ + public FakeCreateBookingCommand() + { + RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId()); + RuleFor(r => r.FlightId, _ => 1); + RuleFor(r => r.PassengerId, _ => 1); + } +} diff --git a/src/Services/Flight/src/Flight.Api/Program.cs b/src/Services/Flight/src/Flight.Api/Program.cs index 0891ca6..3e7bbd2 100644 --- a/src/Services/Flight/src/Flight.Api/Program.cs +++ b/src/Services/Flight/src/Flight.Api/Program.cs @@ -77,14 +77,12 @@ app.UseSerilogRequestLogging(); app.UseCorrelationId(); app.UseRouting(); app.UseHttpMetrics(); -app.UseMigrations(env); +app.UseMigrations(); app.UseProblemDetails(); app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); - - app.UseEndpoints(endpoints => { endpoints.MapControllers(); diff --git a/src/Services/Flight/src/Flight/Extensions/MigrationsExtensions.cs b/src/Services/Flight/src/Flight/Extensions/MigrationsExtensions.cs index 5fce346..61d5235 100644 --- a/src/Services/Flight/src/Flight/Extensions/MigrationsExtensions.cs +++ b/src/Services/Flight/src/Flight/Extensions/MigrationsExtensions.cs @@ -11,13 +11,10 @@ namespace Flight.Extensions; public static class MigrationsExtensions { - public static IApplicationBuilder UseMigrations(this IApplicationBuilder app, IWebHostEnvironment env) + public static IApplicationBuilder UseMigrations(this IApplicationBuilder app) { - if (!env.IsEnvironment("test")) - { - MigrateDatabase(app.ApplicationServices); - SeedData(app.ApplicationServices); - } + MigrateDatabase(app.ApplicationServices); + SeedData(app.ApplicationServices); return app; } diff --git a/src/Services/Flight/tests/IntegrationTest/Aircraft/Features/CreateAircraftTests.cs b/src/Services/Flight/tests/IntegrationTest/Aircraft/Features/CreateAircraftTests.cs index bae7504..b96aa6c 100644 --- a/src/Services/Flight/tests/IntegrationTest/Aircraft/Features/CreateAircraftTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Aircraft/Features/CreateAircraftTests.cs @@ -8,13 +8,13 @@ using Xunit; namespace Integration.Test.Aircraft.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class CreateAircraftTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly ITestHarness _testHarness; - public CreateAircraftTests(TestFixture fixture) + public CreateAircraftTests(IntegrationTestFixture fixture) { _fixture = fixture; _testHarness = fixture.TestHarness; diff --git a/src/Services/Flight/tests/IntegrationTest/Airport/Features/CreateAirportTests.cs b/src/Services/Flight/tests/IntegrationTest/Airport/Features/CreateAirportTests.cs index 65cf55b..5232c4c 100644 --- a/src/Services/Flight/tests/IntegrationTest/Airport/Features/CreateAirportTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Airport/Features/CreateAirportTests.cs @@ -8,13 +8,13 @@ using Xunit; namespace Integration.Test.Airport.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class CreateAirportTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly ITestHarness _testHarness; - public CreateAirportTests(TestFixture fixture) + public CreateAirportTests(IntegrationTestFixture fixture) { _fixture = fixture; _testHarness = fixture.TestHarness; diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/CreateFlightTests.cs b/src/Services/Flight/tests/IntegrationTest/Flight/Features/CreateFlightTests.cs index 926edca..ab8124a 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/CreateFlightTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Flight/Features/CreateFlightTests.cs @@ -8,13 +8,13 @@ using Xunit; namespace Integration.Test.Flight.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class CreateFlightTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly ITestHarness _testHarness; - public CreateFlightTests(TestFixture fixture) + public CreateFlightTests(IntegrationTestFixture fixture) { _fixture = fixture; _testHarness = _fixture.TestHarness; diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/DeleteFlightTests.cs b/src/Services/Flight/tests/IntegrationTest/Flight/Features/DeleteFlightTests.cs index 530ec0d..60cbee8 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/DeleteFlightTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Flight/Features/DeleteFlightTests.cs @@ -11,13 +11,13 @@ using Xunit; namespace Integration.Test.Flight.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class DeleteFlightTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly ITestHarness _testHarness; - public DeleteFlightTests(TestFixture fixture) + public DeleteFlightTests(IntegrationTestFixture fixture) { _fixture = fixture; _testHarness = fixture.TestHarness; diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableFlightsTests.cs b/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableFlightsTests.cs index 5d08d1d..55a2c37 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableFlightsTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableFlightsTests.cs @@ -10,13 +10,13 @@ using Xunit; namespace Integration.Test.Flight.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class GetAvailableFlightsTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly GrpcChannel _channel; - public GetAvailableFlightsTests(TestFixture fixture) + public GetAvailableFlightsTests(IntegrationTestFixture fixture) { _fixture = fixture; _channel = fixture.Channel; diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableSeatsTests.cs b/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableSeatsTests.cs index 6f7fda0..4f99b77 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableSeatsTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableSeatsTests.cs @@ -10,13 +10,13 @@ using Xunit; namespace Integration.Test.Flight.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class GetAvailableSeatsTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly GrpcChannel _channel; - public GetAvailableSeatsTests(TestFixture fixture) + public GetAvailableSeatsTests(IntegrationTestFixture fixture) { _fixture = fixture; _channel = fixture.Channel; diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetFlightByIdTests.cs b/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetFlightByIdTests.cs index e639559..ecee2d1 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetFlightByIdTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetFlightByIdTests.cs @@ -9,13 +9,13 @@ using Xunit; namespace Integration.Test.Flight.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class GetFlightByIdTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly GrpcChannel _channel; - public GetFlightByIdTests(TestFixture fixture) + public GetFlightByIdTests(IntegrationTestFixture fixture) { _fixture = fixture; _channel = fixture.Channel; diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/ReserveSeatTests.cs b/src/Services/Flight/tests/IntegrationTest/Flight/Features/ReserveSeatTests.cs index 8ca2505..9201513 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/ReserveSeatTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Flight/Features/ReserveSeatTests.cs @@ -9,13 +9,13 @@ using Xunit; namespace Integration.Test.Flight.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class ReserveSeatTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly GrpcChannel _channel; - public ReserveSeatTests(TestFixture fixture) + public ReserveSeatTests(IntegrationTestFixture fixture) { _fixture = fixture; _channel = fixture.Channel; diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/UpdateFlightTests.cs b/src/Services/Flight/tests/IntegrationTest/Flight/Features/UpdateFlightTests.cs index 1a03c7c..74883ba 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/UpdateFlightTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Flight/Features/UpdateFlightTests.cs @@ -8,13 +8,13 @@ using Xunit; namespace Integration.Test.Flight.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class UpdateFlightTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly ITestHarness _testHarness; - public UpdateFlightTests(TestFixture fixture) + public UpdateFlightTests(IntegrationTestFixture fixture) { _fixture = fixture; _testHarness = _fixture.TestHarness; diff --git a/src/Services/Flight/tests/IntegrationTest/TestFixture.cs b/src/Services/Flight/tests/IntegrationTest/TestFixture.cs deleted file mode 100644 index 34c5761..0000000 --- a/src/Services/Flight/tests/IntegrationTest/TestFixture.cs +++ /dev/null @@ -1,282 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading.Channels; -using System.Threading.Tasks; -using BuildingBlocks.Domain.Model; -using BuildingBlocks.EFCore; -using BuildingBlocks.MassTransit; -using BuildingBlocks.Web; -using Flight.Data; -using Flight.Data.Seed; -using Flight.GrpcServer; -using Grpc.Net.Client; -using MassTransit; -using MassTransit.Testing; -using MediatR; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.VisualStudio.TestPlatform.TestHost; -using NSubstitute; -using Respawn; -using Serilog; -using Xunit; -using Xunit.Abstractions; - -namespace Integration.Test; - -[CollectionDefinition(nameof(TestFixture))] -public class TestFixtureCollection : ICollectionFixture -{ -} - -// ref: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0 -// ref: https://github.com/jbogard/ContosoUniversityDotNetCore-Pages/blob/master/ContosoUniversity.IntegrationTests/SliceFixture.cs -// ref: https://github.com/jasontaylordev/CleanArchitecture/blob/main/tests/Application.IntegrationTests/Testing.cs -// ref: https://github.com/MassTransit/MassTransit/blob/00d6992286911a437b63b93c89a56e920b053c11/src/MassTransit.TestFramework/InMemoryTestFixture.cs -public class TestFixture : IAsyncLifetime -{ - private Checkpoint _checkpoint; - private IConfiguration _configuration; - private WebApplicationFactory _factory; - private IServiceScopeFactory _scopeFactory; - private HttpClient _httpClient; - private ITestHarness _testHarness; - private GrpcChannel _channel; - - public ITestHarness TestHarness => _testHarness; - public GrpcChannel Channel => _channel; - - public async Task InitializeAsync() - { - Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "test"); - - _factory = new WebApplicationFactory() - .WithWebHostBuilder(builder => builder.ConfigureServices(services => - { - services.RemoveAll(typeof(IHostedService)); - services.ReplaceSingleton(AddHttpContextAccessorMock); - services.ReplaceScoped(); - services.AddMassTransitTestHarness(x => - { - x.UsingRabbitMq((context, cfg) => - { - var rabbitMqOptions = services.GetOptions("RabbitMq"); - var host = rabbitMqOptions.HostName; - - cfg.Host(host, h => - { - h.Username(rabbitMqOptions.UserName); - h.Password(rabbitMqOptions.Password); - }); - cfg.ConfigureEndpoints(context); - }); - }); - })); - - _testHarness = _factory.Services.GetTestHarness(); - - await _testHarness.Start(); - - _configuration = _factory.Services.GetRequiredService(); - _scopeFactory = _factory.Services.GetRequiredService(); - - _httpClient = _factory.CreateClient(new WebApplicationFactoryClientOptions {AllowAutoRedirect = false}); - - _channel = GrpcChannel.ForAddress(_httpClient.BaseAddress!, new GrpcChannelOptions - { - HttpClient = _httpClient - }); - - _checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}}; - - await EnsureDatabaseAsync(); - } - - public async Task DisposeAsync() - { - _testHarness.Cancel(); - await _factory.DisposeAsync(); - await _checkpoint.Reset(_configuration.GetConnectionString("DefaultConnection")); - } - - // 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 HttpClient CreateClient() => _httpClient; - - public async Task ExecuteScopeAsync(Func action) - { - using var scope = _scopeFactory.CreateScope(); - await action(scope.ServiceProvider); - } - - public async Task ExecuteScopeAsync(Func> action) - { - using var scope = _scopeFactory.CreateScope(); - - var result = await action(scope.ServiceProvider); - - return result; - } - - public Task ExecuteDbContextAsync(Func action) - { - return ExecuteScopeAsync(sp => action(sp.GetService())); - } - - public Task ExecuteDbContextAsync(Func action) - { - return ExecuteScopeAsync(sp => action(sp.GetService()).AsTask()); - } - - public Task ExecuteDbContextAsync(Func action) - { - return ExecuteScopeAsync(sp => action(sp.GetService(), sp.GetService())); - } - - public Task ExecuteDbContextAsync(Func> action) - { - return ExecuteScopeAsync(sp => action(sp.GetService())); - } - - public Task ExecuteDbContextAsync(Func> action) - { - return ExecuteScopeAsync(sp => action(sp.GetService()).AsTask()); - } - - public Task ExecuteDbContextAsync(Func> action) - { - return ExecuteScopeAsync(sp => action(sp.GetService(), sp.GetService())); - } - - public Task InsertAsync(params T[] entities) where T : class - { - return ExecuteDbContextAsync(db => - { - foreach (var entity in entities) db.Set().Add(entity); - - return db.SaveChangesAsync(); - }); - } - - public Task InsertAsync(TEntity entity) where TEntity : class - { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - - return db.SaveChangesAsync(); - }); - } - - public Task InsertAsync(TEntity entity, TEntity2 entity2) - where TEntity : class - where TEntity2 : class - { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - db.Set().Add(entity2); - - return db.SaveChangesAsync(); - }); - } - - public Task InsertAsync(TEntity entity, TEntity2 entity2, TEntity3 entity3) - where TEntity : class - where TEntity2 : class - where TEntity3 : class - { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - db.Set().Add(entity2); - db.Set().Add(entity3); - - return db.SaveChangesAsync(); - }); - } - - public Task InsertAsync(TEntity entity, TEntity2 entity2, TEntity3 entity3, - TEntity4 entity4) - where TEntity : class - where TEntity2 : class - where TEntity3 : class - where TEntity4 : class - { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - db.Set().Add(entity2); - db.Set().Add(entity3); - db.Set().Add(entity4); - - return db.SaveChangesAsync(); - }); - } - - public Task FindAsync(long id) - where T : class, IEntity - { - return ExecuteDbContextAsync(db => db.Set().FindAsync(id).AsTask()); - } - - public Task SendAsync(IRequest request) - { - return ExecuteScopeAsync(sp => - { - var mediator = sp.GetRequiredService(); - - return mediator.Send(request); - }); - } - - public Task SendAsync(IRequest request) - { - return ExecuteScopeAsync(sp => - { - var mediator = sp.GetRequiredService(); - - return mediator.Send(request); - }); - } - - private async Task EnsureDatabaseAsync() - { - using var scope = _scopeFactory.CreateScope(); - - var context = scope.ServiceProvider.GetRequiredService(); - var seeders = scope.ServiceProvider.GetServices(); - - await context.Database.MigrateAsync(); - - foreach (var seeder in seeders) await seeder.SeedAllAsync(); - } - - private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider) - { - var httpContextAccessorMock = Substitute.For(); - using var scope = serviceProvider.CreateScope(); - httpContextAccessorMock.HttpContext = new DefaultHttpContext {RequestServices = scope.ServiceProvider}; - - httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 5004); - httpContextAccessorMock.HttpContext.Request.Scheme = "http"; - - return httpContextAccessorMock; - } -} diff --git a/src/Services/Identity/src/Identity.Api/Program.cs b/src/Services/Identity/src/Identity.Api/Program.cs index aed3eb7..6fe8095 100644 --- a/src/Services/Identity/src/Identity.Api/Program.cs +++ b/src/Services/Identity/src/Identity.Api/Program.cs @@ -60,7 +60,7 @@ if (app.Environment.IsDevelopment()) } app.UseSerilogRequestLogging(); -app.UseMigrations(env); +app.UseMigrations(); app.UseCorrelationId(); app.UseRouting(); app.UseHttpMetrics(); diff --git a/src/Services/Identity/src/Identity/Extensions/MigrationsExtensions.cs b/src/Services/Identity/src/Identity/Extensions/MigrationsExtensions.cs index 5962f4e..90477c7 100644 --- a/src/Services/Identity/src/Identity/Extensions/MigrationsExtensions.cs +++ b/src/Services/Identity/src/Identity/Extensions/MigrationsExtensions.cs @@ -11,13 +11,10 @@ namespace Identity.Extensions; public static class MigrationsExtensions { - public static IApplicationBuilder UseMigrations(this IApplicationBuilder app, IWebHostEnvironment env) + public static IApplicationBuilder UseMigrations(this IApplicationBuilder app) { - if (!env.IsEnvironment("test")) - { - MigrateDatabase(app.ApplicationServices); - SeedData(app.ApplicationServices); - } + MigrateDatabase(app.ApplicationServices); + SeedData(app.ApplicationServices); return app; } diff --git a/src/Services/Identity/tests/IntegrationTest/Identity/Features/RegisterNewUserTests.cs b/src/Services/Identity/tests/IntegrationTest/Identity/Features/RegisterNewUserTests.cs index 5c2c6e3..9456de1 100644 --- a/src/Services/Identity/tests/IntegrationTest/Identity/Features/RegisterNewUserTests.cs +++ b/src/Services/Identity/tests/IntegrationTest/Identity/Features/RegisterNewUserTests.cs @@ -8,13 +8,13 @@ using Xunit; namespace Integration.Test.Identity.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class RegisterNewUserTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly ITestHarness _testHarness; - public RegisterNewUserTests(TestFixture fixture) + public RegisterNewUserTests(IntegrationTestFixture fixture) { _fixture = fixture; _testHarness = _fixture.TestHarness; diff --git a/src/Services/Identity/tests/IntegrationTest/Integration.Test.csproj b/src/Services/Identity/tests/IntegrationTest/Integration.Test.csproj index 959edd3..2f07dab 100644 --- a/src/Services/Identity/tests/IntegrationTest/Integration.Test.csproj +++ b/src/Services/Identity/tests/IntegrationTest/Integration.Test.csproj @@ -2,8 +2,6 @@ net6.0 - enable - false diff --git a/src/Services/Identity/tests/IntegrationTest/TestFixture.cs b/src/Services/Identity/tests/IntegrationTest/TestFixture.cs deleted file mode 100644 index 759214f..0000000 --- a/src/Services/Identity/tests/IntegrationTest/TestFixture.cs +++ /dev/null @@ -1,274 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -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; -using MediatR; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using NSubstitute; -using Respawn; -using Serilog; -using Xunit; -using Xunit.Abstractions; - -namespace Integration.Test; - -[CollectionDefinition(nameof(TestFixture))] -public class TestFixtureCollection : ICollectionFixture -{ -} - -// ref: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0 -// ref: https://github.com/jbogard/ContosoUniversityDotNetCore-Pages/blob/master/ContosoUniversity.IntegrationTests/SliceFixture.cs -// ref: https://github.com/jasontaylordev/CleanArchitecture/blob/main/tests/Application.IntegrationTests/Testing.cs -// ref: https://github.com/MassTransit/MassTransit/blob/00d6992286911a437b63b93c89a56e920b053c11/src/MassTransit.TestFramework/InMemoryTestFixture.cs -public class TestFixture : IAsyncLifetime -{ - private Checkpoint _checkpoint; - private IConfiguration _configuration; - private WebApplicationFactory _factory; - private HttpClient _httpClient; - private IServiceScopeFactory _scopeFactory; - private GrpcChannel _channel; - public ITestHarness TestHarness { get; private set; } - public GrpcChannel Channel => _channel; - - public async Task InitializeAsync() - { - Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "test"); - - _factory = new WebApplicationFactory() - .WithWebHostBuilder(builder => builder.ConfigureServices(services => - { - services.RemoveAll(typeof(IHostedService)); - services.ReplaceSingleton(AddHttpContextAccessorMock); - services.ReplaceScoped(); - services.AddMassTransitTestHarness(x => - { - x.UsingRabbitMq((context, cfg) => - { - var rabbitMqOptions = services.GetOptions("RabbitMq"); - var host = rabbitMqOptions.HostName; - - cfg.Host(host, h => - { - h.Username(rabbitMqOptions.UserName); - h.Password(rabbitMqOptions.Password); - }); - cfg.ConfigureEndpoints(context); - }); - }); - })); - - TestHarness = _factory.Services.GetTestHarness(); - - await TestHarness.Start(); - - _configuration = _factory.Services.GetRequiredService(); - _scopeFactory = _factory.Services.GetRequiredService(); - - _httpClient = _factory.CreateClient(new WebApplicationFactoryClientOptions {AllowAutoRedirect = false}); - - _channel = GrpcChannel.ForAddress(_httpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = _httpClient}); - - _checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}}; - - await EnsureDatabaseAsync(); - } - - public async Task DisposeAsync() - { - TestHarness.Cancel(); - await _factory.DisposeAsync(); - await _checkpoint.Reset(_configuration.GetConnectionString("DefaultConnection")); - } - - // 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 HttpClient CreateClient() - { - return _httpClient; - } - - public async Task ExecuteScopeAsync(Func action) - { - using var scope = _scopeFactory.CreateScope(); - await action(scope.ServiceProvider); - } - - public async Task ExecuteScopeAsync(Func> action) - { - using var scope = _scopeFactory.CreateScope(); - - var result = await action(scope.ServiceProvider); - - return result; - } - - public Task ExecuteDbContextAsync(Func action) - { - return ExecuteScopeAsync(sp => action(sp.GetService())); - } - - public Task ExecuteDbContextAsync(Func action) - { - return ExecuteScopeAsync(sp => action(sp.GetService()).AsTask()); - } - - public Task ExecuteDbContextAsync(Func action) - { - return ExecuteScopeAsync(sp => action(sp.GetService(), sp.GetService())); - } - - public Task ExecuteDbContextAsync(Func> action) - { - return ExecuteScopeAsync(sp => action(sp.GetService())); - } - - public Task ExecuteDbContextAsync(Func> action) - { - return ExecuteScopeAsync(sp => action(sp.GetService()).AsTask()); - } - - public Task ExecuteDbContextAsync(Func> action) - { - return ExecuteScopeAsync(sp => action(sp.GetService(), sp.GetService())); - } - - public Task InsertAsync(params T[] entities) where T : class - { - return ExecuteDbContextAsync(db => - { - foreach (var entity in entities) db.Set().Add(entity); - - return db.SaveChangesAsync(); - }); - } - - public Task InsertAsync(TEntity entity) where TEntity : class - { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - - return db.SaveChangesAsync(); - }); - } - - public Task InsertAsync(TEntity entity, TEntity2 entity2) - where TEntity : class - where TEntity2 : class - { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - db.Set().Add(entity2); - - return db.SaveChangesAsync(); - }); - } - - public Task InsertAsync(TEntity entity, TEntity2 entity2, TEntity3 entity3) - where TEntity : class - where TEntity2 : class - where TEntity3 : class - { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - db.Set().Add(entity2); - db.Set().Add(entity3); - - return db.SaveChangesAsync(); - }); - } - - public Task InsertAsync(TEntity entity, TEntity2 entity2, TEntity3 entity3, - TEntity4 entity4) - where TEntity : class - where TEntity2 : class - where TEntity3 : class - where TEntity4 : class - { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - db.Set().Add(entity2); - db.Set().Add(entity3); - db.Set().Add(entity4); - - return db.SaveChangesAsync(); - }); - } - - public Task FindAsync(long id) - where T : class, IEntity - { - return ExecuteDbContextAsync(db => db.Set().FindAsync(id).AsTask()); - } - - public Task SendAsync(IRequest request) - { - return ExecuteScopeAsync(sp => - { - var mediator = sp.GetRequiredService(); - - return mediator.Send(request); - }); - } - - public Task SendAsync(IRequest request) - { - return ExecuteScopeAsync(sp => - { - var mediator = sp.GetRequiredService(); - - return mediator.Send(request); - }); - } - - private async Task EnsureDatabaseAsync() - { - using var scope = _scopeFactory.CreateScope(); - - var context = scope.ServiceProvider.GetRequiredService(); - var seeders = scope.ServiceProvider.GetServices(); - - await context.Database.MigrateAsync(); - - foreach (var seeder in seeders) await seeder.SeedAllAsync(); - } - - private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider) - { - var httpContextAccessorMock = Substitute.For(); - using var scope = serviceProvider.CreateScope(); - httpContextAccessorMock.HttpContext = new DefaultHttpContext {RequestServices = scope.ServiceProvider}; - - httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 6005); - httpContextAccessorMock.HttpContext.Request.Scheme = "http"; - - return httpContextAccessorMock; - } -} diff --git a/src/Services/Passenger/src/Passenger.Api/Program.cs b/src/Services/Passenger/src/Passenger.Api/Program.cs index a27aeb0..e680c6d 100644 --- a/src/Services/Passenger/src/Passenger.Api/Program.cs +++ b/src/Services/Passenger/src/Passenger.Api/Program.cs @@ -17,8 +17,10 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer; using Passenger; using Passenger.Data; using Passenger.Extensions; +using Passenger.Services; using Prometheus; using Serilog; +using Server; var builder = WebApplication.CreateBuilder(args); var configuration = builder.Configuration; @@ -39,6 +41,7 @@ builder.Services.AddValidatorsFromAssembly(typeof(PassengerRoot).Assembly); builder.Services.AddCustomProblemDetails(); builder.Services.AddCustomMapster(typeof(PassengerRoot).Assembly); builder.Services.AddHttpContextAccessor(); +builder.Services.AddScoped(); builder.Services.AddTransient(); @@ -61,7 +64,7 @@ if (app.Environment.IsDevelopment()) } app.UseSerilogRequestLogging(); -app.UseMigrations(env); +app.UseMigrations(); app.UseCorrelationId(); app.UseRouting(); app.UseHttpMetrics(); @@ -74,6 +77,7 @@ app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapMetrics(); + endpoints.MapGrpcService(); endpoints.MapMagicOnionService(); }); diff --git a/src/Services/Passenger/src/Passenger/Extensions/MigrationsExtensions.cs b/src/Services/Passenger/src/Passenger/Extensions/MigrationsExtensions.cs index a3cf2ac..5ddfd01 100644 --- a/src/Services/Passenger/src/Passenger/Extensions/MigrationsExtensions.cs +++ b/src/Services/Passenger/src/Passenger/Extensions/MigrationsExtensions.cs @@ -10,13 +10,10 @@ namespace Passenger.Extensions; public static class MigrationsExtensions { - public static IApplicationBuilder UseMigrations(this IApplicationBuilder app, IWebHostEnvironment env) + public static IApplicationBuilder UseMigrations(this IApplicationBuilder app) { - if (!env.IsEnvironment("test")) - { - MigrateDatabase(app.ApplicationServices); - SeedData(app.ApplicationServices); - } + MigrateDatabase(app.ApplicationServices); + SeedData(app.ApplicationServices); return app; } diff --git a/src/Services/Passenger/src/Passenger/GrpcServer/PassengerGrpcService.cs b/src/Services/Passenger/src/Passenger/GrpcServer/PassengerGrpcService.cs index 3edd30f..82b813b 100644 --- a/src/Services/Passenger/src/Passenger/GrpcServer/PassengerGrpcService.cs +++ b/src/Services/Passenger/src/Passenger/GrpcServer/PassengerGrpcService.cs @@ -21,4 +21,9 @@ public class PassengerGrpcService : ServiceBase, IPasseng var result = await _mediator.Send(new GetPassengerQueryById(id)); return result.Adapt(); } + + public UnaryResult TTT(long id) + { + return new UnaryResult("hiiiiiiiiiiiiiiii"); + } } diff --git a/src/Services/Passenger/src/Passenger/Passenger.csproj b/src/Services/Passenger/src/Passenger/Passenger.csproj index 487d10d..4c8dce7 100644 --- a/src/Services/Passenger/src/Passenger/Passenger.csproj +++ b/src/Services/Passenger/src/Passenger/Passenger.csproj @@ -6,6 +6,13 @@ + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -15,10 +22,18 @@ + + + + + Protos\test.proto + + + diff --git a/src/Services/Passenger/src/Passenger/Services/TesterService.cs b/src/Services/Passenger/src/Passenger/Services/TesterService.cs new file mode 100644 index 0000000..296d2f8 --- /dev/null +++ b/src/Services/Passenger/src/Passenger/Services/TesterService.cs @@ -0,0 +1,61 @@ +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 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 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 SayHelloClientStreaming( + IAsyncStreamReader requestStream, ServerCallContext context) + { + var names = new List(); + + 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 requestStream, + IServerStreamWriter responseStream, + ServerCallContext context) + { + await foreach (var request in requestStream.ReadAllAsync()) + { + await responseStream.WriteAsync( + new HelloReply { Message = _greeter.Greet(request.Name) }); + } + } +} diff --git a/src/Services/Passenger/tests/IntegrationTest/CustomWebApplicationFactory.cs b/src/Services/Passenger/tests/IntegrationTest/CustomWebApplicationFactory.cs new file mode 100644 index 0000000..fbb8936 --- /dev/null +++ b/src/Services/Passenger/tests/IntegrationTest/CustomWebApplicationFactory.cs @@ -0,0 +1,78 @@ +using System; +using BuildingBlocks.MassTransit; +using BuildingBlocks.Web; +using MassTransit; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using NSubstitute; +using Respawn; + +public class CustomWebApplicationFactory : WebApplicationFactory +{ + public Checkpoint Checkpoint { get; set; } + public IConfiguration Configuration => Services.GetRequiredService(); + public Action? TestRegistrationServices { get; set; } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + //https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests#set-the-environment + //https://stackoverflow.com/questions/43927955/should-getenvironmentvariable-work-in-xunit-test/43951218 + + //we could read env from our test launch setting or we can set it directly here + builder.UseEnvironment("test"); + + //The test app's builder.ConfigureTestServices callback is executed after the app's Startup.ConfigureServices code is executed. + builder.ConfigureTestServices(services => + { + services.RemoveAll(typeof(IHostedService)); + services.ReplaceSingleton(AddHttpContextAccessorMock); + TestRegistrationServices?.Invoke(services); + services.AddMassTransitTestHarness(x => + { + x.UsingRabbitMq((context, cfg) => + { + var rabbitMqOptions = services.GetOptions("RabbitMq"); + var host = rabbitMqOptions.HostName; + + cfg.Host(host, h => + { + h.Username(rabbitMqOptions.UserName); + h.Password(rabbitMqOptions.Password); + }); + cfg.ConfigureEndpoints(context); + }); + }); + + Checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}}; + + 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) + { + var httpContextAccessorMock = Substitute.For(); + using var scope = serviceProvider.CreateScope(); + httpContextAccessorMock.HttpContext = new DefaultHttpContext {RequestServices = scope.ServiceProvider}; + + httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 6012); + httpContextAccessorMock.HttpContext.Request.Scheme = "http"; + + return httpContextAccessorMock; + } +} diff --git a/src/Services/Passenger/tests/IntegrationTest/Fakes/FakePassengerCreated.cs b/src/Services/Passenger/tests/IntegrationTest/Fakes/FakePassengerCreated.cs index 925d566..1c04b6d 100644 --- a/src/Services/Passenger/tests/IntegrationTest/Fakes/FakePassengerCreated.cs +++ b/src/Services/Passenger/tests/IntegrationTest/Fakes/FakePassengerCreated.cs @@ -7,6 +7,6 @@ public static class FakePassengerCreated { public static global::Passenger.Passengers.Models.Passenger Generate(UserCreated userCreated) { - return global::Passenger.Passengers.Models.Passenger.Create(SnowFlakIdGenerator.NewId(), userCreated.Name, userCreated.PassportNumber); + return global::Passenger.Passengers.Models.Passenger.Create(1000, userCreated.Name, userCreated.PassportNumber); } } diff --git a/src/Services/Passenger/tests/IntegrationTest/Fakes/FakeUserCreated.cs b/src/Services/Passenger/tests/IntegrationTest/Fakes/FakeUserCreated.cs index b885954..a0bc996 100644 --- a/src/Services/Passenger/tests/IntegrationTest/Fakes/FakeUserCreated.cs +++ b/src/Services/Passenger/tests/IntegrationTest/Fakes/FakeUserCreated.cs @@ -8,7 +8,7 @@ public class FakeUserCreated : AutoFaker { public FakeUserCreated() { - RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId()); + RuleFor(r => r.Id, _ => _.Random.Number(50, 100000)); RuleFor(r => r.Name, _ => "Meysam"); RuleFor(r => r.PassportNumber, _ => "1299878"); } diff --git a/src/Services/Passenger/tests/IntegrationTest/Identity/Features/RegisterNewUserConsumerTests.cs b/src/Services/Passenger/tests/IntegrationTest/Identity/Features/RegisterNewUserConsumerTests.cs index fc065ce..cea2042 100644 --- a/src/Services/Passenger/tests/IntegrationTest/Identity/Features/RegisterNewUserConsumerTests.cs +++ b/src/Services/Passenger/tests/IntegrationTest/Identity/Features/RegisterNewUserConsumerTests.cs @@ -1,33 +1,33 @@ -using System.Threading.Tasks; -using BuildingBlocks.Contracts.EventBus.Messages; -using Integration.Test.Fakes; -using MassTransit.Testing; -using Xunit; - -namespace Integration.Test.Identity.Features; - -[Collection(nameof(TestFixture))] -public class RegisterNewUserConsumerTests -{ - private readonly TestFixture _fixture; - private readonly ITestHarness _testHarness; - - public RegisterNewUserConsumerTests(TestFixture fixture) - { - _fixture = fixture; - _testHarness = _fixture.TestHarness; - } - - [Fact] - public async Task should_register_new_user_consume_handler_consume_user_created_message() - { - // // Arrange - var message = new FakeUserCreated().Generate(); - - // // Act - await _testHarness.Bus.Publish(message); - - // // Assert - await _testHarness.Consumed.Any(); - } -} +// using System.Threading.Tasks; +// using BuildingBlocks.Contracts.EventBus.Messages; +// using Integration.Test.Fakes; +// using MassTransit.Testing; +// using Xunit; +// +// namespace Integration.Test.Identity.Features; +// +// [Collection(nameof(TestFixture))] +// public class RegisterNewUserConsumerTests +// { +// private readonly TestFixture _fixture; +// private readonly ITestHarness _testHarness; +// +// public RegisterNewUserConsumerTests(TestFixture fixture) +// { +// _fixture = fixture; +// _testHarness = _fixture.TestHarness; +// } +// +// [Fact] +// public async Task should_register_new_user_consume_handler_consume_user_created_message() +// { +// // // Arrange +// var message = new FakeUserCreated().Generate(); +// +// // // Act +// await _testHarness.Bus.Publish(message); +// +// // // Assert +// await _testHarness.Consumed.Any(); +// } +// } diff --git a/src/Services/Passenger/tests/IntegrationTest/Integration.Test.csproj b/src/Services/Passenger/tests/IntegrationTest/Integration.Test.csproj index 91447ef..7144cd8 100644 --- a/src/Services/Passenger/tests/IntegrationTest/Integration.Test.csproj +++ b/src/Services/Passenger/tests/IntegrationTest/Integration.Test.csproj @@ -2,8 +2,6 @@ net6.0 - enable - false @@ -21,7 +19,7 @@ all - + diff --git a/src/Services/Passenger/tests/IntegrationTest/TestFixture.cs b/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs similarity index 63% rename from src/Services/Passenger/tests/IntegrationTest/TestFixture.cs rename to src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs index 18f52cb..1fadc44 100644 --- a/src/Services/Passenger/tests/IntegrationTest/TestFixture.cs +++ b/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs @@ -1,124 +1,79 @@ -using System; +using System; using System.Net.Http; using System.Threading.Tasks; using BuildingBlocks.Domain.Model; using BuildingBlocks.EFCore; -using BuildingBlocks.MassTransit; -using BuildingBlocks.Web; using Grpc.Net.Client; -using MassTransit; using MassTransit.Testing; using MediatR; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; using NSubstitute; using Passenger.Data; -using Respawn; using Serilog; using Xunit; using Xunit.Abstractions; namespace Integration.Test; -[CollectionDefinition(nameof(TestFixture))] -public class TestFixtureCollection : ICollectionFixture -{ -} +[CollectionDefinition(nameof(IntegrationTestFixture))] +public class SliceFixtureCollection : ICollectionFixture { } -// ref: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0 -// ref: https://github.com/jbogard/ContosoUniversityDotNetCore-Pages/blob/master/ContosoUniversity.IntegrationTests/SliceFixture.cs -// ref: https://github.com/jasontaylordev/CleanArchitecture/blob/main/tests/Application.IntegrationTests/Testing.cs -// ref: https://github.com/MassTransit/MassTransit/blob/00d6992286911a437b63b93c89a56e920b053c11/src/MassTransit.TestFramework/InMemoryTestFixture.cs -public class TestFixture : IAsyncLifetime +public class IntegrationTestFixture : IAsyncLifetime { - private Checkpoint _checkpoint; - private IConfiguration _configuration; - private WebApplicationFactory _factory; - private HttpClient _httpClient; - private IServiceScopeFactory _scopeFactory; - private GrpcChannel _channel; - public ITestHarness TestHarness { get; private set; } - public GrpcChannel Channel => _channel; + private readonly CustomWebApplicationFactory _factory; - public async Task InitializeAsync() + public IntegrationTestFixture() { - Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "test"); - - _factory = new WebApplicationFactory() - .WithWebHostBuilder(builder => builder.ConfigureServices(services => - { - services.RemoveAll(typeof(IHostedService)); - services.ReplaceSingleton(AddHttpContextAccessorMock); - services.AddMassTransitTestHarness(x => - { - x.UsingRabbitMq((context, cfg) => - { - var rabbitMqOptions = services.GetOptions("RabbitMq"); - var host = rabbitMqOptions.HostName; - - cfg.Host(host, h => - { - h.Username(rabbitMqOptions.UserName); - h.Password(rabbitMqOptions.Password); - }); - cfg.ConfigureEndpoints(context); - }); - }); - })); - - TestHarness = _factory.Services.GetTestHarness(); - - await TestHarness.Start(); - - _configuration = _factory.Services.GetRequiredService(); - _scopeFactory = _factory.Services.GetRequiredService(); - - _httpClient = _factory.CreateClient(new WebApplicationFactoryClientOptions {AllowAutoRedirect = false}); - - _channel = GrpcChannel.ForAddress(_httpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = _httpClient}); - - _checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}}; - - await EnsureDatabaseAsync(); + // Ref: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0#basic-tests-with-the-default-webapplicationfactory + _factory = new CustomWebApplicationFactory(); } - public async Task DisposeAsync() - { - TestHarness.Cancel(); - await _factory.DisposeAsync(); - await _checkpoint.Reset(_configuration.GetConnectionString("DefaultConnection")); - } + public IServiceProvider ServiceProvider => _factory.Services; + public IConfiguration Configuration => _factory.Configuration; + public HttpClient HttpClient => _factory.CreateClient(); + public ITestHarness TestHarness => CreateHarness(); + 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 HttpClient CreateClient() + public void RegisterTestServices(Action services) => _factory.TestRegistrationServices = services; + + public virtual Task InitializeAsync() { - return _httpClient; + return Task.CompletedTask; + } + + public virtual async Task DisposeAsync() + { + if (!string.IsNullOrEmpty(Configuration?.GetConnectionString("DefaultConnection"))) + await _factory.Checkpoint.Reset(Configuration?.GetConnectionString("DefaultConnection")); + + await _factory.DisposeAsync(); } public async Task ExecuteScopeAsync(Func action) { - using var scope = _scopeFactory.CreateScope(); + using var scope = ServiceProvider.CreateScope(); await action(scope.ServiceProvider); } public async Task ExecuteScopeAsync(Func> action) { - using var scope = _scopeFactory.CreateScope(); + using var scope = ServiceProvider.CreateScope(); var result = await action(scope.ServiceProvider); @@ -247,9 +202,33 @@ public class TestFixture : IAsyncLifetime }); } + private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider) + { + var httpContextAccessorMock = Substitute.For(); + 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() + { + var harness = ServiceProvider.GetTestHarness(); + harness.Start().GetAwaiter().GetResult(); + return harness; + } + + private GrpcChannel CreateChannel() + { + return GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions {HttpClient = HttpClient}); + } + private async Task EnsureDatabaseAsync() { - using var scope = _scopeFactory.CreateScope(); + using var scope = ServiceProvider.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); var seeders = scope.ServiceProvider.GetServices(); @@ -258,16 +237,4 @@ public class TestFixture : IAsyncLifetime foreach (var seeder in seeders) await seeder.SeedAllAsync(); } - - private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider) - { - var httpContextAccessorMock = Substitute.For(); - using var scope = serviceProvider.CreateScope(); - httpContextAccessorMock.HttpContext = new DefaultHttpContext {RequestServices = scope.ServiceProvider}; - - httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 6012); - httpContextAccessorMock.HttpContext.Request.Scheme = "http"; - - return httpContextAccessorMock; - } } diff --git a/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/CompleteRegisterPassengerTests.cs b/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/CompleteRegisterPassengerTests.cs index a611663..d91c523 100644 --- a/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/CompleteRegisterPassengerTests.cs +++ b/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/CompleteRegisterPassengerTests.cs @@ -1,21 +1,19 @@ using System.Threading.Tasks; using BuildingBlocks.Contracts.EventBus.Messages; -using BuildingBlocks.IdsGenerator; using FluentAssertions; using Integration.Test.Fakes; -using MassTransit; using MassTransit.Testing; using Xunit; namespace Integration.Test.Passenger.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class CompleteRegisterPassengerTests { - private readonly TestFixture _fixture; + private readonly IntegrationTestFixture _fixture; private readonly ITestHarness _testHarness; - public CompleteRegisterPassengerTests(TestFixture fixture) + public CompleteRegisterPassengerTests(IntegrationTestFixture fixture) { _fixture = fixture; _testHarness = _fixture.TestHarness; diff --git a/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/GetPassengerByIdTests.cs b/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/GetPassengerByIdTests.cs index 39b8e7f..4bb534d 100644 --- a/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/GetPassengerByIdTests.cs +++ b/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/GetPassengerByIdTests.cs @@ -1,26 +1,48 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Channels; +using System.Threading.Tasks; using BuildingBlocks.Contracts.EventBus.Messages; using BuildingBlocks.Contracts.Grpc; +using BuildingBlocks.IdsGenerator; using FluentAssertions; +using Grpc.Core; using Grpc.Net.Client; using Integration.Test.Fakes; +using MagicOnion; using MagicOnion.Client; using MassTransit.Testing; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NSubstitute; using Passenger.Passengers.Features.GetPassengerById; +using Server; +using Test; using Xunit; namespace Integration.Test.Passenger.Features; -[Collection(nameof(TestFixture))] +[Collection(nameof(IntegrationTestFixture))] public class GetPassengerByIdTests { - private readonly TestFixture _fixture; - private readonly ITestHarness _testHarness; + private readonly IntegrationTestFixture _fixture; private readonly GrpcChannel _channel; + private readonly ITestHarness _testHarness; + private IPassengerGrpcService _passengerGrpcService; - public GetPassengerByIdTests(TestFixture fixture) + public GetPassengerByIdTests(IntegrationTestFixture fixture) { _fixture = fixture; + var mockGreeter = Substitute.For(); + + mockGreeter.Greet(Arg.Any()) + .Returns("heyyyyyyyyyyyyyyy"); + + + _fixture.RegisterTestServices(services => + { + services.AddSingleton(mockGreeter); + }); + _testHarness = _fixture.TestHarness; _channel = _fixture.Channel; } @@ -45,6 +67,7 @@ public class GetPassengerByIdTests response?.Id.Should().Be(passengerEntity.Id); } + [Fact] public async Task should_retrive_a_passenger_by_id_from_grpc_service() { @@ -64,4 +87,18 @@ 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_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); + } }