diff --git a/booking-microservices-sample.sln b/booking-microservices-sample.sln index 5599825..5754a2b 100644 --- a/booking-microservices-sample.sln +++ b/booking-microservices-sample.sln @@ -63,6 +63,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Ser EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Booking\tests\IntegrationTest\Integration.Test.csproj", "{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unit.Test", "src\Services\Flight\tests\UnitTest\Unit.Test.csproj", "{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -99,6 +101,7 @@ Global {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} + {8F78BCE2-C705-4357-A6B9-1B83B55ABBE8} = {C6EE337B-91EA-472A-87C7-E9528408CE59} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -157,5 +160,9 @@ Global {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 + {8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Services/Booking/tests/IntegrationTest/IntegrationTestFixture.cs b/src/Services/Booking/tests/IntegrationTest/IntegrationTestFixture.cs index eaa4bfd..bd35dfd 100644 --- a/src/Services/Booking/tests/IntegrationTest/IntegrationTestFixture.cs +++ b/src/Services/Booking/tests/IntegrationTest/IntegrationTestFixture.cs @@ -15,7 +15,7 @@ using Xunit.Abstractions; namespace Integration.Test; [CollectionDefinition(nameof(IntegrationTestFixture))] -public class SliceFixtureCollection : ICollectionFixture +public class FixtureCollection : ICollectionFixture { } diff --git a/src/Services/Flight/src/Flight/Aircrafts/Features/CreateAircraft/CreateAircraftCommandValidator.cs b/src/Services/Flight/src/Flight/Aircrafts/Features/CreateAircraft/CreateAircraftCommandValidator.cs index 6614e3f..da923f6 100644 --- a/src/Services/Flight/src/Flight/Aircrafts/Features/CreateAircraft/CreateAircraftCommandValidator.cs +++ b/src/Services/Flight/src/Flight/Aircrafts/Features/CreateAircraft/CreateAircraftCommandValidator.cs @@ -6,8 +6,6 @@ public class CreateAircraftCommandValidator : AbstractValidator x.Model).NotEmpty().WithMessage("Model is required"); RuleFor(x => x.Name).NotEmpty().WithMessage("Name is required"); RuleFor(x => x.ManufacturingYear).NotEmpty().WithMessage("ManufacturingYear is required"); diff --git a/src/Services/Flight/src/Flight/Airports/Features/CreateAirport/CreateAirportCommandValidator.cs b/src/Services/Flight/src/Flight/Airports/Features/CreateAirport/CreateAirportCommandValidator.cs index 51b5eb6..1d02059 100644 --- a/src/Services/Flight/src/Flight/Airports/Features/CreateAirport/CreateAirportCommandValidator.cs +++ b/src/Services/Flight/src/Flight/Airports/Features/CreateAirport/CreateAirportCommandValidator.cs @@ -6,8 +6,6 @@ public class CreateAirportCommandValidator : AbstractValidator x.Code).NotEmpty().WithMessage("Code is required"); RuleFor(x => x.Name).NotEmpty().WithMessage("Name is required"); RuleFor(x => x.Address).NotEmpty().WithMessage("Address is required"); diff --git a/src/Services/Flight/src/Flight/Flights/Features/CreateFlight/CreateFlightCommandValidator.cs b/src/Services/Flight/src/Flight/Flights/Features/CreateFlight/CreateFlightCommandValidator.cs index f55e221..f4da34a 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/CreateFlight/CreateFlightCommandValidator.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/CreateFlight/CreateFlightCommandValidator.cs @@ -7,8 +7,6 @@ public class CreateFlightCommandValidator : AbstractValidator x.Price).GreaterThan(0).WithMessage("Price must be greater than 0"); RuleFor(x => x.Status).Must(p => (p.GetType().IsEnum && diff --git a/src/Services/Flight/src/Flight/Flights/Features/FlightMappings.cs b/src/Services/Flight/src/Flight/Flights/Features/FlightMappings.cs index 979afde..306e555 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/FlightMappings.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/FlightMappings.cs @@ -1,12 +1,13 @@ +using AutoMapper; using Flight.Flights.Dtos; using Mapster; namespace Flight.Flights.Features; -public class FlightMappings : IRegister +public class FlightMappings : Profile { public void Register(TypeAdapterConfig config) { - config.NewConfig(); - } + config.NewConfig(); + } } diff --git a/src/Services/Flight/src/Flight/Seats/Features/CreateSeat/CreateSeatCommandHandler.cs b/src/Services/Flight/src/Flight/Seats/Features/CreateSeat/CreateSeatCommandHandler.cs index dd233bf..320844a 100644 --- a/src/Services/Flight/src/Flight/Seats/Features/CreateSeat/CreateSeatCommandHandler.cs +++ b/src/Services/Flight/src/Flight/Seats/Features/CreateSeat/CreateSeatCommandHandler.cs @@ -40,6 +40,8 @@ public class CreateSeatCommandHandler : IRequestHandler(newSeat.Entity); } } diff --git a/src/Services/Flight/src/Flight/Seats/Features/CreateSeat/CreateSeatCommandValidator.cs b/src/Services/Flight/src/Flight/Seats/Features/CreateSeat/CreateSeatCommandValidator.cs index 5b08ee0..96cde0e 100644 --- a/src/Services/Flight/src/Flight/Seats/Features/CreateSeat/CreateSeatCommandValidator.cs +++ b/src/Services/Flight/src/Flight/Seats/Features/CreateSeat/CreateSeatCommandValidator.cs @@ -8,8 +8,6 @@ public class CreateSeatCommandValidator : AbstractValidator { public CreateSeatCommandValidator() { - CascadeMode = CascadeMode.Stop; - RuleFor(x => x.SeatNumber).NotEmpty().WithMessage("SeatNumber is required"); RuleFor(x => x.FlightId).NotEmpty().WithMessage("FlightId is required"); RuleFor(x => x.Class).Must(p => (p.GetType().IsEnum && diff --git a/src/Services/Flight/tests/IntegrationTest/Fakes/FakeSeatCreated.cs b/src/Services/Flight/tests/IntegrationTest/Fakes/FakeSeatCreated.cs index 74af85c..8814ad1 100644 --- a/src/Services/Flight/tests/IntegrationTest/Fakes/FakeSeatCreated.cs +++ b/src/Services/Flight/tests/IntegrationTest/Fakes/FakeSeatCreated.cs @@ -5,9 +5,9 @@ namespace Integration.Test.Fakes; public static class FakeSeatCreated { - public static Seat Generate(CreateSeatCommand command) + public static global::Flight.Seats.Models.Seat Generate(CreateSeatCommand command) { - return Seat.Create(command.Id, command.SeatNumber, command.Type, command.Class, command.FlightId); + return global::Flight.Seats.Models.Seat.Create(command.Id, command.SeatNumber, command.Type, command.Class, command.FlightId); } } diff --git a/src/Services/Flight/tests/IntegrationTest/IntegrationTestFixture.cs b/src/Services/Flight/tests/IntegrationTest/IntegrationTestFixture.cs index 143318e..65e1064 100644 --- a/src/Services/Flight/tests/IntegrationTest/IntegrationTestFixture.cs +++ b/src/Services/Flight/tests/IntegrationTest/IntegrationTestFixture.cs @@ -19,7 +19,7 @@ using Xunit.Abstractions; namespace Integration.Test; [CollectionDefinition(nameof(IntegrationTestFixture))] -public class SliceFixtureCollection : ICollectionFixture { } +public class FixtureCollection : ICollectionFixture { } public class IntegrationTestFixture : IAsyncLifetime { diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableSeatsTests.cs b/src/Services/Flight/tests/IntegrationTest/Seat/Features/GetAvailableSeatsTests.cs similarity index 89% rename from src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableSeatsTests.cs rename to src/Services/Flight/tests/IntegrationTest/Seat/Features/GetAvailableSeatsTests.cs index 4f99b77..a5a2d35 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/GetAvailableSeatsTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Seat/Features/GetAvailableSeatsTests.cs @@ -1,14 +1,13 @@ 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; +namespace Integration.Test.Seat.Features; [Collection(nameof(IntegrationTestFixture))] public class GetAvailableSeatsTests @@ -36,7 +35,7 @@ public class GetAvailableSeatsTests var seatEntity1 = FakeSeatCreated.Generate(seatCommand1); var seatEntity2 = FakeSeatCreated.Generate(seatCommand2); - await _fixture.InsertAsync(seatEntity1, seatEntity2); + await _fixture.InsertAsync(seatEntity1, seatEntity2); var flightGrpcClient = MagicOnionClient.Create(_channel); diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/ReserveSeatTests.cs b/src/Services/Flight/tests/IntegrationTest/Seat/Features/ReserveSeatTests.cs similarity index 93% rename from src/Services/Flight/tests/IntegrationTest/Flight/Features/ReserveSeatTests.cs rename to src/Services/Flight/tests/IntegrationTest/Seat/Features/ReserveSeatTests.cs index 9201513..2822e32 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/ReserveSeatTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Seat/Features/ReserveSeatTests.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; using BuildingBlocks.Contracts.Grpc; using FluentAssertions; using Grpc.Net.Client; @@ -7,7 +6,7 @@ using Integration.Test.Fakes; using MagicOnion.Client; using Xunit; -namespace Integration.Test.Flight.Features; +namespace Integration.Test.Seat.Features; [Collection(nameof(IntegrationTestFixture))] public class ReserveSeatTests diff --git a/src/Services/Flight/tests/UnitTest/Aircraft/Features/CreateAircraftTests/CreateAircraftCommandHandlerTests.cs b/src/Services/Flight/tests/UnitTest/Aircraft/Features/CreateAircraftTests/CreateAircraftCommandHandlerTests.cs new file mode 100644 index 0000000..a10a47e --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Aircraft/Features/CreateAircraftTests/CreateAircraftCommandHandlerTests.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Flight.Aircrafts.Dtos; +using Flight.Aircrafts.Features.CreateAircraft; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Unit.Test.Common; +using Unit.Test.Fakes; +using Xunit; + +namespace Unit.Test.Aircraft.Features.CreateAircraftTests; + +[Collection(nameof(UnitTestFixture))] +public class CreateAircraftCommandHandlerTests +{ + private readonly UnitTestFixture _fixture; + private readonly CreateAircraftCommandHandler _handler; + + public Task Act(CreateAircraftCommand command, CancellationToken cancellationToken) => + _handler.Handle(command, cancellationToken); + + public CreateAircraftCommandHandlerTests(UnitTestFixture fixture) + { + _fixture = fixture; + _handler = new CreateAircraftCommandHandler(_fixture.Mapper, _fixture.DbContext); + } + + [Fact] + public async Task handler_with_valid_command_should_create_new_aircraft_and_return_currect_aircraft_dto() + { + // Arrange + var command = new FakeCreateAircraftCommand().Generate(); + + // Act + var response = await Act(command, CancellationToken.None); + + // Assert + var entity = await _fixture.DbContext.Aircraft.SingleOrDefaultAsync(x => x.Model == response.Model); + + entity?.Should().NotBeNull(); + response?.Model.Should().Be(entity?.Model); + } + + [Fact] + public async Task handler_with_null_command_should_throw_argument_exception() + { + // Arrange + CreateAircraftCommand command = null; + + // Act + Func act = async () => { await Act(command, CancellationToken.None); }; + + // Assert + await act.Should().ThrowAsync(); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Aircraft/Features/CreateAircraftTests/CreateAircraftCommandValidatorTests.cs b/src/Services/Flight/tests/UnitTest/Aircraft/Features/CreateAircraftTests/CreateAircraftCommandValidatorTests.cs new file mode 100644 index 0000000..dc26903 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Aircraft/Features/CreateAircraftTests/CreateAircraftCommandValidatorTests.cs @@ -0,0 +1,29 @@ +using Flight.Aircrafts.Features.CreateAircraft; +using FluentAssertions; +using FluentValidation.TestHelper; +using Unit.Test.Common; +using Unit.Test.Fakes; +using Xunit; + +namespace Unit.Test.Aircraft.Features.CreateAircraftTests; + +[Collection(nameof(UnitTestFixture))] +public class CreateAircraftCommandValidatorTests +{ + [Fact] + public void is_valid_should_be_false_when_have_invalid_parameter() + { + // Arrange + var command = new FakeValidateCreateAircraftCommand().Generate(); + var validator = new CreateAircraftCommandValidator(); + + // Act + var result = validator.TestValidate(command); + + // Assert + result.IsValid.Should().BeFalse(); + result.ShouldHaveValidationErrorFor(x => x.Model); + result.ShouldHaveValidationErrorFor(x => x.ManufacturingYear); + result.ShouldHaveValidationErrorFor(x => x.Name); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Airport/Features/CreateAirportTests/CreateAirportCommandHandlerTests.cs b/src/Services/Flight/tests/UnitTest/Airport/Features/CreateAirportTests/CreateAirportCommandHandlerTests.cs new file mode 100644 index 0000000..99213af --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Airport/Features/CreateAirportTests/CreateAirportCommandHandlerTests.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Flight.Airports.Dtos; +using Flight.Airports.Features.CreateAirport; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Unit.Test.Common; +using Unit.Test.Fakes; +using Xunit; + +namespace Unit.Test.Airport.Features.CreateAirportTests; + +[Collection(nameof(UnitTestFixture))] +public class CreateAirportCommandHandlerTests +{ + private readonly UnitTestFixture _fixture; + private readonly CreateAirportCommandHandler _handler; + + + public CreateAirportCommandHandlerTests(UnitTestFixture fixture) + { + _fixture = fixture; + _handler = new CreateAirportCommandHandler(_fixture.Mapper, _fixture.DbContext); + } + + public Task Act(CreateAirportCommand command, CancellationToken cancellationToken) => + _handler.Handle(command, cancellationToken); + + [Fact] + public async Task handler_with_valid_command_should_create_new_airport_and_return_currect_airport_dto() + { + // Arrange + var command = new FakeCreateAirportCommand().Generate(); + + // Act + var response = await Act(command, CancellationToken.None); + + // Assert + var entity = await _fixture.DbContext.Airports.SingleOrDefaultAsync(x => x.Code == response.Code); + + entity?.Should().NotBeNull(); + response?.Code.Should().Be(entity?.Code); + } + + [Fact] + public async Task handler_with_null_command_should_throw_argument_exception() + { + // Arrange + CreateAirportCommand command = null; + + // Act + var act = async () => { await Act(command, CancellationToken.None); }; + + // Assert + await act.Should().ThrowAsync(); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Airport/Features/CreateAirportTests/CreateAirportCommandValidatorTests.cs b/src/Services/Flight/tests/UnitTest/Airport/Features/CreateAirportTests/CreateAirportCommandValidatorTests.cs new file mode 100644 index 0000000..2e3d574 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Airport/Features/CreateAirportTests/CreateAirportCommandValidatorTests.cs @@ -0,0 +1,29 @@ +using Flight.Airports.Features.CreateAirport; +using FluentAssertions; +using FluentValidation.TestHelper; +using Unit.Test.Common; +using Unit.Test.Fakes; +using Xunit; + +namespace Unit.Test.Airport.Features.CreateAirportTests; + +[Collection(nameof(UnitTestFixture))] +public class CreateAirportCommandValidatorTests +{ + [Fact] + public void is_valid_should_be_false_when_have_invalid_parameter() + { + // Arrange + var command = new FakeValidateCreateAirportCommand().Generate(); + var validator = new CreateAirportCommandValidator(); + + // Act + var result = validator.TestValidate(command); + + // Assert + result.IsValid.Should().BeFalse(); + result.ShouldHaveValidationErrorFor(x => x.Code); + result.ShouldHaveValidationErrorFor(x => x.Address); + result.ShouldHaveValidationErrorFor(x => x.Name); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Common/DbContextFactory.cs b/src/Services/Flight/tests/UnitTest/Common/DbContextFactory.cs new file mode 100644 index 0000000..cb2eb93 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Common/DbContextFactory.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using Flight.Aircrafts.Models; +using Flight.Airports.Models; +using Flight.Data; +using Flight.Flights.Models; +using Flight.Seats.Models; +using Microsoft.EntityFrameworkCore; + +namespace Unit.Test.Common +{ + public static class DbContextFactory + { + public static FlightDbContext Create() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options; + + var context = new FlightDbContext(options, httpContextAccessor: null); + + // Seed our data + FlightDataSeeder(context); + + return context; + } + + private static void FlightDataSeeder(FlightDbContext context) + { + var airports = new List + { + global::Flight.Airports.Models.Airport.Create(1, "Lisbon International Airport", "LIS", "12988"), + global::Flight.Airports.Models.Airport.Create(2, "Sao Paulo International Airport", "BRZ", "11200") + }; + + context.Airports.AddRange(airports); + + var aircrafts = new List + { + global::Flight.Aircrafts.Models.Aircraft.Create(1, "Boeing 737", "B737", 2005), + global::Flight.Aircrafts.Models.Aircraft.Create(2, "Airbus 300", "A300", 2000), + global::Flight.Aircrafts.Models.Aircraft.Create(3, "Airbus 320", "A320", 2003) + }; + + context.Aircraft.AddRange(aircrafts); + + var flights = new List + { + global::Flight.Flights.Models.Flight.Create(1, "BD467", 1, 1, new DateTime(2022, 1, 31, 12, 0, 0), + new DateTime(2022, 1, 31, 14, 0, 0), + 2, 120m, + new DateTime(2022, 1, 31), FlightStatus.Completed, + 8000) + }; + context.Flights.AddRange(flights); + + var seats = new List + { + global::Flight.Seats.Models.Seat.Create(1, "12A", SeatType.Window, SeatClass.Economy, 1), + global::Flight.Seats.Models.Seat.Create(2, "12B", SeatType.Window, SeatClass.Economy, 1), + global::Flight.Seats.Models.Seat.Create(3, "12C", SeatType.Middle, SeatClass.Economy, 1), + global::Flight.Seats.Models.Seat.Create(4, "12D", SeatType.Middle, SeatClass.Economy, 1), + global::Flight.Seats.Models.Seat.Create(5, "12E", SeatType.Aisle, SeatClass.Economy, 1), + global::Flight.Seats.Models.Seat.Create(6, "12F", SeatType.Aisle, SeatClass.Economy, 1) + }; + + context.Seats.AddRange(seats); + + context.SaveChanges(); + } + + public static void Destroy(FlightDbContext context) + { + context.Database.EnsureDeleted(); + context.Dispose(); + } + } +} diff --git a/src/Services/Flight/tests/UnitTest/Common/MapperFactory.cs b/src/Services/Flight/tests/UnitTest/Common/MapperFactory.cs new file mode 100644 index 0000000..7145fa9 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Common/MapperFactory.cs @@ -0,0 +1,18 @@ +using Flight; +using Mapster; +using MapsterMapper; + +namespace Unit.Test.Common +{ + public static class MapperFactory + { + public static IMapper Create() + { + var typeAdapterConfig = TypeAdapterConfig.GlobalSettings; + typeAdapterConfig.Scan(typeof(FlightRoot).Assembly); + IMapper instance = new Mapper(typeAdapterConfig); + + return instance; + } + } +} diff --git a/src/Services/Flight/tests/UnitTest/Common/SnowFlakIdGeneratorFactory.cs b/src/Services/Flight/tests/UnitTest/Common/SnowFlakIdGeneratorFactory.cs new file mode 100644 index 0000000..55f6062 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Common/SnowFlakIdGeneratorFactory.cs @@ -0,0 +1,11 @@ +using BuildingBlocks.IdsGenerator; + +namespace Unit.Test.Common; + +public static class SnowFlakIdGeneratorFactory +{ + public static void Create() + { + SnowFlakIdGenerator.Configure(1); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Common/UnitTestFixture.cs b/src/Services/Flight/tests/UnitTest/Common/UnitTestFixture.cs new file mode 100644 index 0000000..3a8a181 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Common/UnitTestFixture.cs @@ -0,0 +1,29 @@ +using System; +using BuildingBlocks.IdsGenerator; +using Flight.Data; +using MapsterMapper; +using Xunit; + +namespace Unit.Test.Common +{ + [CollectionDefinition(nameof(UnitTestFixture))] + public class FixtureCollection : ICollectionFixture { } + + public class UnitTestFixture : IDisposable + { + public UnitTestFixture() + { + SnowFlakIdGeneratorFactory.Create(); + Mapper = MapperFactory.Create(); + DbContext = DbContextFactory.Create(); + } + + public IMapper Mapper { get; } + public FlightDbContext DbContext { get; } + + public void Dispose() + { + DbContextFactory.Destroy(DbContext); + } + } +} diff --git a/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateAircraftCommand.cs b/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateAircraftCommand.cs new file mode 100644 index 0000000..59ce6a5 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateAircraftCommand.cs @@ -0,0 +1,13 @@ +using AutoBogus; +using BuildingBlocks.IdsGenerator; +using Flight.Aircrafts.Features.CreateAircraft; + +namespace Unit.Test.Fakes; + +public class FakeCreateAircraftCommand : AutoFaker +{ + public FakeCreateAircraftCommand() + { + RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId()); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateAirportCommand.cs b/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateAirportCommand.cs new file mode 100644 index 0000000..1b19edc --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateAirportCommand.cs @@ -0,0 +1,13 @@ +using AutoBogus; +using BuildingBlocks.IdsGenerator; +using Flight.Airports.Features.CreateAirport; + +namespace Unit.Test.Fakes; + +public class FakeCreateAirportCommand : AutoFaker +{ + public FakeCreateAirportCommand() + { + RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId()); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateFlightCommand.cs b/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateFlightCommand.cs new file mode 100644 index 0000000..9533e7a --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateFlightCommand.cs @@ -0,0 +1,17 @@ +using AutoBogus; +using BuildingBlocks.IdsGenerator; +using Flight.Flights.Features.CreateFlight; + +namespace Unit.Test.Fakes; + +public sealed class FakeCreateFlightCommand : AutoFaker +{ + public FakeCreateFlightCommand() + { + RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId()); + RuleFor(r => r.FlightNumber, r => r.Random.String()); + RuleFor(r => r.DepartureAirportId, _ => 1); + RuleFor(r => r.ArriveAirportId, _ => 2); + RuleFor(r => r.AircraftId, _ => 1); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateSeatCommand.cs b/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateSeatCommand.cs new file mode 100644 index 0000000..96fd7ec --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Fakes/FakeCreateSeatCommand.cs @@ -0,0 +1,18 @@ +using AutoBogus; +using BuildingBlocks.IdsGenerator; +using Flight.Seats.Features.CreateSeat; +using Flight.Seats.Models; + +namespace Unit.Test.Fakes; + +public class FakeCreateSeatCommand : AutoFaker +{ + public FakeCreateSeatCommand() + { + RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId()); + RuleFor(r => r.FlightId, _ => 1); + RuleFor(r => r.SeatNumber, _ => "F99"); + RuleFor(r => r.Type, _ => SeatType.Window); + RuleFor(r => r.Class, _ => SeatClass.Economy); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateAircraftCommand.cs b/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateAircraftCommand.cs new file mode 100644 index 0000000..913e0d1 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateAircraftCommand.cs @@ -0,0 +1,14 @@ +using AutoBogus; +using Flight.Aircrafts.Features.CreateAircraft; + +namespace Unit.Test.Fakes; + +public class FakeValidateCreateAircraftCommand : AutoFaker +{ + public FakeValidateCreateAircraftCommand() + { + RuleFor(r => r.ManufacturingYear, _ => 0); + RuleFor(r => r.Name, _ => null); + RuleFor(r => r.Model, _ => null); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateAirportCommand.cs b/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateAirportCommand.cs new file mode 100644 index 0000000..ad578b6 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateAirportCommand.cs @@ -0,0 +1,14 @@ +using AutoBogus; +using Flight.Airports.Features.CreateAirport; + +namespace Unit.Test.Fakes; + +public class FakeValidateCreateAirportCommand : AutoFaker +{ + public FakeValidateCreateAirportCommand() + { + RuleFor(r => r.Code, _ => null); + RuleFor(r => r.Name, _ => null); + RuleFor(r => r.Address, _ => null); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateFlightCommand.cs b/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateFlightCommand.cs new file mode 100644 index 0000000..bddb9a3 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateFlightCommand.cs @@ -0,0 +1,20 @@ +using System; +using AutoBogus; +using Flight.Flights.Features.CreateFlight; +using Flight.Flights.Models; + +namespace Unit.Test.Fakes; + +public class FakeValidateCreateFlightCommand : AutoFaker +{ + public FakeValidateCreateFlightCommand() + { + RuleFor(r => r.Price, _ => -10); + RuleFor(r => r.Status, _ => (FlightStatus)10); + RuleFor(r => r.AircraftId, _ => 0); + RuleFor(r => r.DepartureAirportId, _ => 0); + RuleFor(r => r.ArriveAirportId, _ => 0); + RuleFor(r => r.DurationMinutes, _ => 0); + RuleFor(r => r.FlightDate, _ => new DateTime()); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateSeatCommand.cs b/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateSeatCommand.cs new file mode 100644 index 0000000..c62d84d --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Fakes/FakeValidateCreateSeatCommand.cs @@ -0,0 +1,15 @@ +using AutoBogus; +using Flight.Seats.Features.CreateSeat; +using Flight.Seats.Models; + +namespace Unit.Test.Fakes; + +public class FakeValidateCreateSeatCommand : AutoFaker +{ + public FakeValidateCreateSeatCommand() + { + RuleFor(r => r.SeatNumber, _ => null); + RuleFor(r => r.FlightId, _ => 0); + RuleFor(r => r.Class, _ => (SeatClass)10); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Flight/Features/CreateFlight/CreateFlightCommandHandlerTests.cs b/src/Services/Flight/tests/UnitTest/Flight/Features/CreateFlight/CreateFlightCommandHandlerTests.cs new file mode 100644 index 0000000..9da6e4e --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Flight/Features/CreateFlight/CreateFlightCommandHandlerTests.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Flight.Flights.Dtos; +using Flight.Flights.Features.CreateFlight; +using FluentAssertions; +using Unit.Test.Common; +using Unit.Test.Fakes; +using Xunit; + +namespace Unit.Test.Flight.Features.CreateFlight; + +[Collection(nameof(UnitTestFixture))] +public class CreateFlightCommandHandlerTests +{ + private readonly UnitTestFixture _fixture; + private readonly CreateFlightCommandHandler _handler; + + public Task Act(CreateFlightCommand command, CancellationToken cancellationToken) => + _handler.Handle(command, cancellationToken); + + public CreateFlightCommandHandlerTests(UnitTestFixture fixture) + { + _fixture = fixture; + _handler = new CreateFlightCommandHandler(fixture.Mapper, fixture.DbContext); + } + + [Fact] + public async Task handler_with_valid_command_should_create_new_flight_and_return_currect_flight_dto() + { + // Arrange + var command = new FakeCreateFlightCommand().Generate(); + + // Act + var response = await Act(command, CancellationToken.None); + + // Assert + var entity = await _fixture.DbContext.Flights.FindAsync(response?.Id); + + entity?.Should().NotBeNull(); + response?.Id.Should().Be(entity?.Id); + } + + [Fact] + public async Task handler_with_null_command_should_throw_argument_exception() + { + // Arrange + CreateFlightCommand command = null; + + // Act + Func act = async () => { await Act(command, CancellationToken.None); }; + + // Assert + await act.Should().ThrowAsync(); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Flight/Features/CreateFlight/CreateFlightCommandValidatorTests.cs b/src/Services/Flight/tests/UnitTest/Flight/Features/CreateFlight/CreateFlightCommandValidatorTests.cs new file mode 100644 index 0000000..2f23a69 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Flight/Features/CreateFlight/CreateFlightCommandValidatorTests.cs @@ -0,0 +1,33 @@ +using Flight.Flights.Features.CreateFlight; +using FluentAssertions; +using FluentValidation.TestHelper; +using Unit.Test.Common; +using Unit.Test.Fakes; +using Xunit; + +namespace Unit.Test.Flight.Features.CreateFlight; + +[Collection(nameof(UnitTestFixture))] +public class CreateFlightCommandValidatorTests +{ + [Fact] + public void is_valid_should_be_false_when_have_invalid_parameter() + { + // Arrange + var command = new FakeValidateCreateFlightCommand().Generate(); + var validator = new CreateFlightCommandValidator(); + + // Act + var result = validator.TestValidate(command); + + // Assert + result.IsValid.Should().BeFalse(); + result.ShouldHaveValidationErrorFor(x => x.Price); + result.ShouldHaveValidationErrorFor(x => x.Status); + result.ShouldHaveValidationErrorFor(x => x.AircraftId); + result.ShouldHaveValidationErrorFor(x => x.DepartureAirportId); + result.ShouldHaveValidationErrorFor(x => x.ArriveAirportId); + result.ShouldHaveValidationErrorFor(x => x.DurationMinutes); + result.ShouldHaveValidationErrorFor(x => x.FlightDate); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Flight/FlightMappingTests.cs b/src/Services/Flight/tests/UnitTest/Flight/FlightMappingTests.cs new file mode 100644 index 0000000..d132e05 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Flight/FlightMappingTests.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using Flight.Flights.Dtos; +using MapsterMapper; +using Unit.Test.Common; +using Xunit; + +namespace Unit.Test.Flight; + +[Collection(nameof(UnitTestFixture))] +public class FlightMappingTests +{ + private readonly UnitTestFixture _fixture; + private readonly IMapper _mapper; + + public FlightMappingTests(UnitTestFixture fixture) + { + _mapper = fixture.Mapper; + } + + public static IEnumerable Data + { + get + { + yield return new object[] + { + // these types will instantiate with reflection in the future + typeof(global::Flight.Flights.Models.Flight), typeof(FlightResponseDto) + }; + } + } + + [Theory] + [MemberData(nameof(Data))] + public void should_support_mapping_from_source_to_destination(Type source, Type destination, + params object[] parameters) + { + var instance = Activator.CreateInstance(source, parameters); + + _mapper.Map(instance, source, destination); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Seat/Features/CreateSeatCommandHandlerTests.cs b/src/Services/Flight/tests/UnitTest/Seat/Features/CreateSeatCommandHandlerTests.cs new file mode 100644 index 0000000..079bee2 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Seat/Features/CreateSeatCommandHandlerTests.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Flight.Seats.Dtos; +using Flight.Seats.Features.CreateSeat; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Unit.Test.Common; +using Unit.Test.Fakes; +using Xunit; + +namespace Unit.Test.Seat.Features; + +[Collection(nameof(UnitTestFixture))] +public class CreateSeatCommandHandlerTests +{ + private readonly UnitTestFixture _fixture; + private readonly CreateSeatCommandHandler _handler; + + + public CreateSeatCommandHandlerTests(UnitTestFixture fixture) + { + _fixture = fixture; + _handler = new CreateSeatCommandHandler(_fixture.Mapper, _fixture.DbContext); + } + + public Task Act(CreateSeatCommand command, CancellationToken cancellationToken) + { + return _handler.Handle(command, cancellationToken); + } + + [Fact] + public async Task handler_with_valid_command_should_create_new_seat_and_return_currect_seat_dto() + { + // Arrange + var command = new FakeCreateSeatCommand().Generate(); + + // Act + var response = await Act(command, CancellationToken.None); + + // Assert + var entity = await _fixture.DbContext.Seats.SingleOrDefaultAsync(x => x.SeatNumber == response.SeatNumber); + + entity?.Should().NotBeNull(); + response?.Id.Should().Be(entity?.Id); + } + + [Fact] + public async Task handler_with_null_command_should_throw_argument_exception() + { + // Arrange + CreateSeatCommand command = null; + + // Act + var act = async () => { await Act(command, CancellationToken.None); }; + + // Assert + await act.Should().ThrowAsync(); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Seat/Features/CreateSeatCommandValidatorTests.cs b/src/Services/Flight/tests/UnitTest/Seat/Features/CreateSeatCommandValidatorTests.cs new file mode 100644 index 0000000..544db18 --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Seat/Features/CreateSeatCommandValidatorTests.cs @@ -0,0 +1,29 @@ +using Flight.Seats.Features.CreateSeat; +using FluentAssertions; +using FluentValidation.TestHelper; +using Unit.Test.Common; +using Unit.Test.Fakes; +using Xunit; + +namespace Unit.Test.Seat.Features; + +[Collection(nameof(UnitTestFixture))] +public class CreateSeatCommandValidatorTests +{ + [Fact] + public void is_valid_should_be_false_when_have_invalid_parameter() + { + // Arrange + var command = new FakeValidateCreateSeatCommand().Generate(); + var validator = new CreateSeatCommandValidator(); + + // Act + var result = validator.TestValidate(command); + + // Assert + result.IsValid.Should().BeFalse(); + result.ShouldHaveValidationErrorFor(x => x.SeatNumber); + result.ShouldHaveValidationErrorFor(x => x.FlightId); + result.ShouldHaveValidationErrorFor(x => x.Class); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Seat/SeatMappingTests.cs b/src/Services/Flight/tests/UnitTest/Seat/SeatMappingTests.cs new file mode 100644 index 0000000..43f4aba --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Seat/SeatMappingTests.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using Flight.Seats.Dtos; +using MapsterMapper; +using Unit.Test.Common; +using Xunit; + +namespace Unit.Test.Seat; + +[Collection(nameof(UnitTestFixture))] +public class SeatMappingTests +{ + private readonly UnitTestFixture _fixture; + private readonly IMapper _mapper; + + public SeatMappingTests(UnitTestFixture fixture) + { + _mapper = fixture.Mapper; + } + + public static IEnumerable Data + { + get + { + yield return new object[] + { + // these types will instantiate with reflection in the future + typeof(global::Flight.Seats.Models.Seat), typeof(SeatResponseDto) + }; + } + } + + + [Theory] + [MemberData(nameof(Data))] + public void should_support_mapping_from_source_to_destination(Type source, Type destination, params object[] parameters) + { + var instance = Activator.CreateInstance(source, parameters); + + _mapper.Map(instance, source, destination); + } +} diff --git a/src/Services/Flight/tests/UnitTest/Unit.Test.csproj b/src/Services/Flight/tests/UnitTest/Unit.Test.csproj new file mode 100644 index 0000000..ee72bae --- /dev/null +++ b/src/Services/Flight/tests/UnitTest/Unit.Test.csproj @@ -0,0 +1,25 @@ + + + + net6.0 + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/Services/Identity/tests/IntegrationTest/IntegrationTestFixture.cs b/src/Services/Identity/tests/IntegrationTest/IntegrationTestFixture.cs index 53fd92f..7bb3fd9 100644 --- a/src/Services/Identity/tests/IntegrationTest/IntegrationTestFixture.cs +++ b/src/Services/Identity/tests/IntegrationTest/IntegrationTestFixture.cs @@ -19,7 +19,7 @@ using Xunit.Abstractions; namespace Integration.Test; [CollectionDefinition(nameof(IntegrationTestFixture))] -public class SliceFixtureCollection : ICollectionFixture { } +public class FixtureCollection : ICollectionFixture { } public class IntegrationTestFixture : IAsyncLifetime { diff --git a/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs b/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs index a908460..af2826d 100644 --- a/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs +++ b/src/Services/Passenger/tests/IntegrationTest/IntegrationTestFixture.cs @@ -27,7 +27,7 @@ using Xunit.Abstractions; namespace Integration.Test; [CollectionDefinition(nameof(IntegrationTestFixture))] -public class SliceFixtureCollection : ICollectionFixture { } +public class FixtureCollection : ICollectionFixture { } public class IntegrationTestFixture : IAsyncLifetime {