add unit test for flight microservice

This commit is contained in:
meysamhadeli 2022-06-10 23:40:00 +04:30
parent c79be5a171
commit 112ffbc55b
37 changed files with 742 additions and 23 deletions

View File

@ -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

View File

@ -15,7 +15,7 @@ using Xunit.Abstractions;
namespace Integration.Test;
[CollectionDefinition(nameof(IntegrationTestFixture))]
public class SliceFixtureCollection : ICollectionFixture<IntegrationTestFixture>
public class FixtureCollection : ICollectionFixture<IntegrationTestFixture>
{
}

View File

@ -6,8 +6,6 @@ public class CreateAircraftCommandValidator : AbstractValidator<CreateAircraftCo
{
public CreateAircraftCommandValidator()
{
CascadeMode = CascadeMode.Stop;
RuleFor(x => 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");

View File

@ -6,8 +6,6 @@ public class CreateAirportCommandValidator : AbstractValidator<CreateAirportComm
{
public CreateAirportCommandValidator()
{
CascadeMode = CascadeMode.Stop;
RuleFor(x => 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");

View File

@ -7,8 +7,6 @@ public class CreateFlightCommandValidator : AbstractValidator<CreateFlightComman
{
public CreateFlightCommandValidator()
{
CascadeMode = CascadeMode.Stop;
RuleFor(x => x.Price).GreaterThan(0).WithMessage("Price must be greater than 0");
RuleFor(x => x.Status).Must(p => (p.GetType().IsEnum &&

View File

@ -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<Models.Flight, FlightResponseDto>();
}
config.NewConfig<Models.Flight, FlightResponseDto>();
}
}

View File

@ -40,6 +40,8 @@ public class CreateSeatCommandHandler : IRequestHandler<CreateSeatCommand, SeatR
var newSeat = await _flightDbContext.Seats.AddAsync(seatEntity, cancellationToken);
await _flightDbContext.SaveChangesAsync(cancellationToken);
return _mapper.Map<SeatResponseDto>(newSeat.Entity);
}
}

View File

@ -8,8 +8,6 @@ public class CreateSeatCommandValidator : AbstractValidator<CreateSeatCommand>
{
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 &&

View File

@ -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);
}
}

View File

@ -19,7 +19,7 @@ using Xunit.Abstractions;
namespace Integration.Test;
[CollectionDefinition(nameof(IntegrationTestFixture))]
public class SliceFixtureCollection : ICollectionFixture<IntegrationTestFixture> { }
public class FixtureCollection : ICollectionFixture<IntegrationTestFixture> { }
public class IntegrationTestFixture : IAsyncLifetime
{

View File

@ -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<global::Flight.Seats.Models.Seat, global::Flight.Seats.Models.Seat>(seatEntity1, seatEntity2);
var flightGrpcClient = MagicOnionClient.Create<IFlightGrpcService>(_channel);

View File

@ -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

View File

@ -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<AircraftResponseDto> 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<Task> act = async () => { await Act(command, CancellationToken.None); };
// Assert
await act.Should().ThrowAsync<ArgumentNullException>();
}
}

View File

@ -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);
}
}

View File

@ -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<AirportResponseDto> 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<ArgumentNullException>();
}
}

View File

@ -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);
}
}

View File

@ -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<FlightDbContext>()
.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>
{
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>
{
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>
{
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>
{
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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
using BuildingBlocks.IdsGenerator;
namespace Unit.Test.Common;
public static class SnowFlakIdGeneratorFactory
{
public static void Create()
{
SnowFlakIdGenerator.Configure(1);
}
}

View File

@ -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<UnitTestFixture> { }
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);
}
}
}

View File

@ -0,0 +1,13 @@
using AutoBogus;
using BuildingBlocks.IdsGenerator;
using Flight.Aircrafts.Features.CreateAircraft;
namespace Unit.Test.Fakes;
public class FakeCreateAircraftCommand : AutoFaker<CreateAircraftCommand>
{
public FakeCreateAircraftCommand()
{
RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId());
}
}

View File

@ -0,0 +1,13 @@
using AutoBogus;
using BuildingBlocks.IdsGenerator;
using Flight.Airports.Features.CreateAirport;
namespace Unit.Test.Fakes;
public class FakeCreateAirportCommand : AutoFaker<CreateAirportCommand>
{
public FakeCreateAirportCommand()
{
RuleFor(r => r.Id, _ => SnowFlakIdGenerator.NewId());
}
}

View File

@ -0,0 +1,17 @@
using AutoBogus;
using BuildingBlocks.IdsGenerator;
using Flight.Flights.Features.CreateFlight;
namespace Unit.Test.Fakes;
public sealed class FakeCreateFlightCommand : AutoFaker<CreateFlightCommand>
{
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);
}
}

View File

@ -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<CreateSeatCommand>
{
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);
}
}

View File

@ -0,0 +1,14 @@
using AutoBogus;
using Flight.Aircrafts.Features.CreateAircraft;
namespace Unit.Test.Fakes;
public class FakeValidateCreateAircraftCommand : AutoFaker<CreateAircraftCommand>
{
public FakeValidateCreateAircraftCommand()
{
RuleFor(r => r.ManufacturingYear, _ => 0);
RuleFor(r => r.Name, _ => null);
RuleFor(r => r.Model, _ => null);
}
}

View File

@ -0,0 +1,14 @@
using AutoBogus;
using Flight.Airports.Features.CreateAirport;
namespace Unit.Test.Fakes;
public class FakeValidateCreateAirportCommand : AutoFaker<CreateAirportCommand>
{
public FakeValidateCreateAirportCommand()
{
RuleFor(r => r.Code, _ => null);
RuleFor(r => r.Name, _ => null);
RuleFor(r => r.Address, _ => null);
}
}

View File

@ -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<CreateFlightCommand>
{
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());
}
}

View File

@ -0,0 +1,15 @@
using AutoBogus;
using Flight.Seats.Features.CreateSeat;
using Flight.Seats.Models;
namespace Unit.Test.Fakes;
public class FakeValidateCreateSeatCommand : AutoFaker<CreateSeatCommand>
{
public FakeValidateCreateSeatCommand()
{
RuleFor(r => r.SeatNumber, _ => null);
RuleFor(r => r.FlightId, _ => 0);
RuleFor(r => r.Class, _ => (SeatClass)10);
}
}

View File

@ -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<FlightResponseDto> 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<Task> act = async () => { await Act(command, CancellationToken.None); };
// Assert
await act.Should().ThrowAsync<ArgumentNullException>();
}
}

View File

@ -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);
}
}

View File

@ -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<object[]> 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);
}
}

View File

@ -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<SeatResponseDto> 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<ArgumentNullException>();
}
}

View File

@ -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);
}
}

View File

@ -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<object[]> 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);
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Flight.Api\Flight.Api.csproj" />
</ItemGroup>
</Project>

View File

@ -19,7 +19,7 @@ using Xunit.Abstractions;
namespace Integration.Test;
[CollectionDefinition(nameof(IntegrationTestFixture))]
public class SliceFixtureCollection : ICollectionFixture<IntegrationTestFixture> { }
public class FixtureCollection : ICollectionFixture<IntegrationTestFixture> { }
public class IntegrationTestFixture : IAsyncLifetime
{

View File

@ -27,7 +27,7 @@ using Xunit.Abstractions;
namespace Integration.Test;
[CollectionDefinition(nameof(IntegrationTestFixture))]
public class SliceFixtureCollection : ICollectionFixture<IntegrationTestFixture> { }
public class FixtureCollection : ICollectionFixture<IntegrationTestFixture> { }
public class IntegrationTestFixture : IAsyncLifetime
{