Merge pull request #343 from AyobKafrashian/Add-tests-modular-monolith

Add tests modular monolith(flight module)
This commit is contained in:
Meysam Hadeli 2025-05-11 13:42:22 +03:30 committed by GitHub
commit 774866e9ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
65 changed files with 1860 additions and 59 deletions

View File

@ -9,10 +9,10 @@ var builder = WebApplication.CreateBuilder(args);
builder.AddSharedInfrastructure();
builder.AddFlightModules();
builder.AddIdentityModules();
builder.AddPassengerModules();
builder.AddBookingModules();
builder.AddFlightModules();
var app = builder.Build();

View File

@ -1,6 +1,7 @@
namespace Booking.Booking.Features.CreatingBook.V1;
using Ardalis.GuardClauses;
using BookingFlight;
using BuildingBlocks.Core;
using BuildingBlocks.Core.CQRS;
using BuildingBlocks.Core.Event;
@ -9,7 +10,6 @@ using BuildingBlocks.EventStoreDB.Repository;
using BuildingBlocks.Web;
using Duende.IdentityServer.EntityFramework.Entities;
using Exceptions;
using Flight;
using FluentValidation;
using Mapster;
using MapsterMapper;
@ -99,7 +99,7 @@ internal class CreateBookingCommandHandler : ICommandHandler<CreateBooking, Crea
Guard.Against.Null(command, nameof(command));
var flight =
await _flightGrpcServiceClient.GetByIdAsync(new Flight.GetByIdRequest { Id = command.FlightId.ToString() }, cancellationToken: cancellationToken);
await _flightGrpcServiceClient.GetByIdAsync(new BookingFlight.GetByIdRequest { Id = command.FlightId.ToString() }, cancellationToken: cancellationToken);
if (flight is null)
{

View File

@ -1,6 +1,6 @@
using Booking.Configuration;
using BookingFlight;
using BuildingBlocks.Web;
using Flight;
using Microsoft.Extensions.DependencyInjection;
using Passenger;

View File

@ -1,6 +1,6 @@
syntax = "proto3";
package flight;
package bookingFlight;
import "google/protobuf/timestamp.proto";
service FlightGrpcService {

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
@ -21,6 +21,22 @@
<Protobuf Include="GrpcServer\Protos\flight.proto" GrpcServices="Both" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Unit.Test</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Integration.Test</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>EndToEnd.Test</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\building-blocks\BuildingBlocks.csproj" />
</ItemGroup>

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Api\src\Api.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
using AutoBogus;
using Flight.Flights.Enums;
namespace EndToEnd.Test.Fakes;
using global::Flight.Data.Seed;
using global::Flight.Flights.Features.CreatingFlight.V1;
using MassTransit;
public sealed class FakeCreateFlightCommand : AutoFaker<CreateFlight>
{
public FakeCreateFlightCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.FlightNumber, r => "12FF");
RuleFor(r => r.DepartureAirportId, _ => InitialData.Airports.First().Id);
RuleFor(r => r.ArriveAirportId, _ => InitialData.Airports.Last().Id);
RuleFor(r => r.Status, _ => FlightStatus.Flying);
RuleFor(r => r.AircraftId, _ => InitialData.Aircrafts.First().Id);
}
}

View File

@ -0,0 +1,22 @@
namespace EndToEnd.Test.Fakes;
using AutoBogus;
using global::Flight.Data.Seed;
using global::Flight.Flights.Enums;
using global::Flight.Flights.Features.CreatingFlight.V1;
using MassTransit;
public sealed class FakeCreateFlightMongoCommand : AutoFaker<CreateFlightMongo>
{
public FakeCreateFlightMongoCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.FlightNumber, r => "12FF");
RuleFor(r => r.DepartureAirportId, _ => InitialData.Airports.First().Id);
RuleFor(r => r.ArriveAirportId, _ => InitialData.Airports.Last().Id);
RuleFor(r => r.Status, _ => FlightStatus.Flying);
RuleFor(r => r.AircraftId, _ => InitialData.Aircrafts.First().Id);
RuleFor(r => r.IsDeleted, _ => false);
}
}

View File

@ -0,0 +1,33 @@
using System.Net;
using System.Net.Http.Json;
using Api;
using BuildingBlocks.TestBase;
using EndToEnd.Test.Fakes;
using EndToEnd.Test.Routes;
using Flight.Data;
using FluentAssertions;
using Xunit;
namespace EndToEnd.Test.Flight.Features;
public class CreateFlightTests : FlightEndToEndTestBase
{
public CreateFlightTests(TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base(integrationTestFixture)
{
}
[Fact]
public async Task should_create_new_flight_to_db_and_publish_message_to_broker()
{
//Arrange
var command = new FakeCreateFlightCommand().Generate();
// Act
var route = ApiRoutes.Flight.CreateFlight;
var result = await Fixture.HttpClient.PostAsJsonAsync(route, command);
// Assert
result.StatusCode.Should().Be(HttpStatusCode.Created);
}
}

View File

@ -0,0 +1,34 @@
using System.Net;
using Api;
using BuildingBlocks.TestBase;
using EndToEnd.Test.Fakes;
using EndToEnd.Test.Routes;
using Flight.Data;
using FluentAssertions;
using Xunit;
namespace EndToEnd.Test.Flight.Features;
public class GetFlightByIdTests : FlightEndToEndTestBase
{
public GetFlightByIdTests(TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base(integrationTestFixture)
{
}
[Fact]
public async Task should_retrive_a_flight_by_id_currectly()
{
//Arrange
var command = new FakeCreateFlightMongoCommand().Generate();
await Fixture.SendAsync(command);
// Act
var route = ApiRoutes.Flight.GetFlightById.Replace(ApiRoutes.Flight.Id, command.Id.ToString(), StringComparison.CurrentCulture);
var result = await Fixture.HttpClient.GetAsync(route);
// Assert
result.StatusCode.Should().Be(HttpStatusCode.OK);
}
}

View File

@ -0,0 +1,20 @@
using Api;
using BuildingBlocks.TestBase;
using Flight.Data;
using Xunit;
namespace EndToEnd.Test;
[Collection(EndToEndTestCollection.Name)]
public class FlightEndToEndTestBase : TestBase<Program, FlightDbContext, FlightReadDbContext>
{
public FlightEndToEndTestBase(TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base(integrationTestFixture)
{
}
}
[CollectionDefinition(Name)]
public class EndToEndTestCollection : ICollectionFixture<TestFixture<Program, FlightDbContext, FlightReadDbContext>>
{
public const string Name = "Flight EndToEnd Test";
}

View File

@ -0,0 +1,85 @@
using BuildingBlocks.EFCore;
using Flight.Aircrafts.Models;
using Flight.Airports.Models;
using Flight.Data;
using Flight.Data.Seed;
using Flight.Flights.Models;
using Flight.Seats.Models;
using MapsterMapper;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace EndToEnd.Test;
public class FlightTestDataSeeder(
FlightDbContext flightDbContext,
FlightReadDbContext flightReadDbContext,
IMapper mapper
) : ITestDataSeeder
{
public async Task SeedAllAsync()
{
await SeedAirportAsync();
await SeedAircraftAsync();
await SeedFlightAsync();
await SeedSeatAsync();
}
private async Task SeedAirportAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Airports))
{
await flightDbContext.Airports.AddRangeAsync(InitialData.Airports);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Airport.AsQueryable()))
{
await flightReadDbContext.Airport.InsertManyAsync(mapper.Map<List<AirportReadModel>>(InitialData.Airports));
}
}
}
private async Task SeedAircraftAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Aircraft))
{
await flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Aircraft.AsQueryable()))
{
await flightReadDbContext.Aircraft.InsertManyAsync(mapper.Map<List<AircraftReadModel>>(InitialData.Aircrafts));
}
}
}
private async Task SeedSeatAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Seats))
{
await flightDbContext.Seats.AddRangeAsync(InitialData.Seats);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Seat.AsQueryable()))
{
await flightReadDbContext.Seat.InsertManyAsync(mapper.Map<List<SeatReadModel>>(InitialData.Seats));
}
}
}
private async Task SeedFlightAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Flights))
{
await flightDbContext.Flights.AddRangeAsync(InitialData.Flights);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Flight.AsQueryable()))
{
await flightReadDbContext.Flight.InsertManyAsync(mapper.Map<List<FlightReadModel>>(InitialData.Flights));
}
}
}
}

View File

@ -0,0 +1,13 @@
namespace EndToEnd.Test.Routes;
public static class ApiRoutes
{
private const string BaseApiPath = "api/v1.0";
public static class Flight
{
public const string Id = "{id}";
public const string GetFlightById = $"{BaseApiPath}/flight/{Id}";
public const string CreateFlight = $"{BaseApiPath}/flight";
}
}

View File

@ -0,0 +1,4 @@
{
"parallelizeAssembly": false,
"parallelizeTestCollections": false
}

View File

@ -0,0 +1,33 @@
using System.Threading.Tasks;
using Api;
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.TestBase;
using Flight.Data;
using FluentAssertions;
using Integration.Test.Fakes;
using Xunit;
namespace Integration.Test.Aircraft.Features;
public class CreateAircraftTests : FlightIntegrationTestBase
{
public CreateAircraftTests(
TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
{
}
[Fact]
public async Task should_create_new_aircraft_to_db_and_publish_message_to_broker()
{
// Arrange
var command = new FakeCreateAircraftCommand().Generate();
// Act
var response = await Fixture.SendAsync(command);
// Assert
response?.Id.Value.Should().Be(command.Id);
(await Fixture.WaitForPublishing<AircraftCreated>()).Should().Be(true);
}
}

View File

@ -0,0 +1,33 @@
using System.Threading.Tasks;
using Api;
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.TestBase;
using Flight.Data;
using FluentAssertions;
using Integration.Test.Fakes;
using Xunit;
namespace Integration.Test.Airport.Features;
public class CreateAirportTests : FlightIntegrationTestBase
{
public CreateAirportTests(
TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
{
}
[Fact]
public async Task should_create_new_airport_to_db_and_publish_message_to_broker()
{
// Arrange
var command = new FakeCreateAirportCommand().Generate();
// Act
var response = await Fixture.SendAsync(command);
// Assert
response?.Id.Should().Be(command.Id);
(await Fixture.WaitForPublishing<AirportCreated>()).Should().Be(true);
}
}

View File

@ -0,0 +1,15 @@
using AutoBogus;
namespace Integration.Test.Fakes;
using global::Flight.Aircrafts.Features.CreatingAircraft.V1;
using MassTransit;
public class FakeCreateAircraftCommand : AutoFaker<CreateAircraft>
{
public FakeCreateAircraftCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.ManufacturingYear, _ => 2000);
}
}

View File

@ -0,0 +1,14 @@
using AutoBogus;
namespace Integration.Test.Fakes;
using global::Flight.Airports.Features.CreatingAirport.V1;
using MassTransit;
public class FakeCreateAirportCommand : AutoFaker<CreateAirport>
{
public FakeCreateAirportCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
}
}

View File

@ -0,0 +1,22 @@
using AutoBogus;
using Flight.Flights.Enums;
namespace Integration.Test.Fakes;
using System.Linq;
using global::Flight.Data.Seed;
using global::Flight.Flights.Features.CreatingFlight.V1;
using MassTransit;
public sealed class FakeCreateFlightCommand : AutoFaker<CreateFlight>
{
public FakeCreateFlightCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.FlightNumber, r => r.Random.Number(1000, 2000).ToString());
RuleFor(r => r.DepartureAirportId, _ => InitialData.Airports.First().Id);
RuleFor(r => r.ArriveAirportId, _ => InitialData.Airports.Last().Id);
RuleFor(r => r.Status, _ => FlightStatus.Flying);
RuleFor(r => r.AircraftId, _ => InitialData.Aircrafts.First().Id);
}
}

View File

@ -0,0 +1,23 @@
namespace Integration.Test.Fakes;
using System.Linq;
using AutoBogus;
using global::Flight.Data.Seed;
using global::Flight.Flights.Enums;
using global::Flight.Flights.Features.CreatingFlight.V1;
using MassTransit;
public sealed class FakeCreateFlightMongoCommand : AutoFaker<CreateFlightMongo>
{
public FakeCreateFlightMongoCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.FlightNumber, r => "12FF");
RuleFor(r => r.DepartureAirportId, _ => InitialData.Airports.First().Id);
RuleFor(r => r.ArriveAirportId, _ => InitialData.Airports.Last().Id);
RuleFor(r => r.Status, _ => FlightStatus.Flying);
RuleFor(r => r.AircraftId, _ => InitialData.Aircrafts.First().Id);
RuleFor(r => r.IsDeleted, _ => false);
}
}

View File

@ -0,0 +1,19 @@
using AutoBogus;
using Flight.Seats.Enums;
namespace Integration.Test.Fakes;
using System;
using global::Flight.Seats.Features.CreatingSeat.V1;
using MassTransit;
public class FakeCreateSeatCommand : AutoFaker<CreateSeat>
{
public FakeCreateSeatCommand(Guid flightId)
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.FlightId, _ => flightId);
RuleFor(r => r.Class, _ => SeatClass.Economy);
RuleFor(r => r.Type, _ => SeatType.Middle);
}
}

View File

@ -0,0 +1,19 @@
namespace Integration.Test.Fakes;
using System;
using AutoBogus;
using global::Flight.Seats.Enums;
using global::Flight.Seats.Features.CreatingSeat.V1;
using MassTransit;
public class FakeCreateSeatMongoCommand : AutoFaker<CreateSeatMongo>
{
public FakeCreateSeatMongoCommand(Guid flightId)
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.FlightId, _ => flightId);
RuleFor(r => r.Class, _ => SeatClass.Economy);
RuleFor(r => r.Type, _ => SeatType.Middle);
RuleFor(r => r.IsDeleted, _ => false);
}
}

View File

@ -0,0 +1,20 @@
using AutoBogus;
namespace Integration.Test.Fakes;
using global::Flight.Flights.Features.UpdatingFlight.V1;
public class FakeUpdateFlightCommand : AutoFaker<UpdateFlight>
{
public FakeUpdateFlightCommand(global::Flight.Flights.Models.Flight flight)
{
RuleFor(r => r.Id, _ => flight.Id);
RuleFor(r => r.DepartureAirportId, _ => flight.DepartureAirportId);
RuleFor(r => r.ArriveAirportId, _ => flight.ArriveAirportId);
RuleFor(r => r.AircraftId, _ => flight.AircraftId);
RuleFor(r => r.FlightNumber, r => r.Random.Number(1000, 2000).ToString());
RuleFor(r => r.Price, _ => 800);
RuleFor(r => r.Status, _ => flight.Status);
RuleFor(r => r.ArriveDate, _ => flight.ArriveDate);
}
}

View File

@ -0,0 +1,33 @@
using Api;
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.TestBase;
using Flight.Data;
using FluentAssertions;
using Integration.Test.Fakes;
using Xunit;
namespace Integration.Test.Flight.Features;
public class CreateFlightTests : FlightIntegrationTestBase
{
public CreateFlightTests(
TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
{
}
[Fact]
public async Task should_create_new_flight_to_db_and_publish_message_to_broker()
{
//Arrange
var command = new FakeCreateFlightCommand().Generate();
// Act
var response = await Fixture.SendAsync(command);
// Assert
response.Should().NotBeNull();
response?.Id.Should().Be(command.Id);
(await Fixture.WaitForPublishing<FlightCreated>()).Should().Be(true);
}
}

View File

@ -0,0 +1,45 @@
using System.Linq;
using System.Threading.Tasks;
using Api;
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.TestBase;
using Flight.Data;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Xunit;
namespace Integration.Test.Flight.Features;
using global::Flight.Data.Seed;
using global::Flight.Flights.Features.DeletingFlight.V1;
using global::Flight.Flights.Models;
using global::Flight.Flights.ValueObjects;
public class DeleteFlightTests : FlightIntegrationTestBase
{
public DeleteFlightTests(
TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
{
}
[Fact]
public async Task should_delete_flight_from_db()
{
// Arrange
var flightEntity = await Fixture.FindAsync<Flight, FlightId>(InitialData.Flights.First().Id);
var command = new DeleteFlight(flightEntity.Id.Value);
// Act
await Fixture.SendAsync(command);
var deletedFlight = (await Fixture.ExecuteDbContextAsync(db => db.Flights
.Where(x => x.Id == FlightId.Of(command.Id))
.IgnoreQueryFilters()
.ToListAsync())
).FirstOrDefault();
// Assert
deletedFlight?.IsDeleted.Should().BeTrue();
(await Fixture.WaitForPublishing<FlightDeleted>()).Should().Be(true);
}
}

View File

@ -0,0 +1,39 @@
using System.Linq;
using System.Threading.Tasks;
using Api;
using BuildingBlocks.TestBase;
using Flight.Data;
using FluentAssertions;
using Integration.Test.Fakes;
using Xunit;
namespace Integration.Test.Flight.Features;
using global::Flight.Flights.Features.CreatingFlight.V1;
using global::Flight.Flights.Features.GettingAvailableFlights.V1;
public class GetAvailableFlightsTests : FlightIntegrationTestBase
{
public GetAvailableFlightsTests(
TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
{
}
[Fact]
public async Task should_return_available_flights()
{
// Arrange
var command = new FakeCreateFlightMongoCommand().Generate();
await Fixture.SendAsync(command);
var query = new GetAvailableFlights();
// Act
var response = (await Fixture.SendAsync(query))?.FlightDtos?.ToList();
// Assert
response?.Should().NotBeNull();
response?.Count.Should().BeGreaterOrEqualTo(2);
}
}

View File

@ -0,0 +1,56 @@
using System.Threading.Tasks;
using Api;
using BuildingBlocks.TestBase;
using Flight;
using Flight.Data;
using FluentAssertions;
using Integration.Test.Fakes;
using Xunit;
namespace Integration.Test.Flight.Features;
using global::Flight.Flights.Features.GettingFlightById.V1;
public class GetFlightByIdTests : FlightIntegrationTestBase
{
public GetFlightByIdTests(
TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
{
}
[Fact]
public async Task should_retrive_a_flight_by_id_currectly()
{
//Arrange
var command = new FakeCreateFlightMongoCommand().Generate();
await Fixture.SendAsync(command);
var query = new GetFlightById(command.Id);
// Act
var response = await Fixture.SendAsync(query);
// Assert
response.Should().NotBeNull();
response?.FlightDto?.Id.Should().Be(command.Id);
}
[Fact]
public async Task should_retrive_a_flight_by_id_from_grpc_service()
{
//Arrange
var command = new FakeCreateFlightMongoCommand().Generate();
await Fixture.SendAsync(command);
var flightGrpcClient = new FlightGrpcService.FlightGrpcServiceClient(Fixture.Channel);
// Act
var response = await flightGrpcClient.GetByIdAsync(new GetByIdRequest { Id = command.Id.ToString() }).ResponseAsync;
// Assert
response?.Should().NotBeNull();
response?.FlightDto.Id.Should().Be(command.Id.ToString());
}
}

View File

@ -0,0 +1,40 @@
using System.Threading.Tasks;
using Api;
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.TestBase;
using Flight.Data;
using FluentAssertions;
using Integration.Test.Fakes;
using Xunit;
namespace Integration.Test.Flight.Features;
using System.Linq;
using global::Flight.Data.Seed;
using global::Flight.Flights.Models;
using global::Flight.Flights.ValueObjects;
public class UpdateFlightTests : FlightIntegrationTestBase
{
public UpdateFlightTests(
TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
{
}
[Fact]
public async Task should_update_flight_to_db_and_publish_message_to_broker()
{
// Arrange
var flightEntity = await Fixture.FindAsync<Flight, FlightId>(InitialData.Flights.First().Id);
var command = new FakeUpdateFlightCommand(flightEntity).Generate();
// Act
var response = await Fixture.SendAsync(command);
// Assert
response.Should().NotBeNull();
response?.Id.Should().Be(flightEntity.Id);
(await Fixture.WaitForPublishing<FlightUpdated>()).Should().Be(true);
}
}

View File

@ -0,0 +1,20 @@
using Api;
using BuildingBlocks.TestBase;
using Flight.Data;
using Xunit;
namespace Integration.Test;
[Collection(IntegrationTestCollection.Name)]
public class FlightIntegrationTestBase : TestBase<Program, FlightDbContext, FlightReadDbContext>
{
public FlightIntegrationTestBase(TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFixture) : base(integrationTestFixture)
{
}
}
[CollectionDefinition(Name)]
public class IntegrationTestCollection : ICollectionFixture<TestFixture<Program, FlightDbContext, FlightReadDbContext>>
{
public const string Name = "Flight Integration Test";
}

View File

@ -0,0 +1,85 @@
using BuildingBlocks.EFCore;
using Flight.Aircrafts.Models;
using Flight.Airports.Models;
using Flight.Data;
using Flight.Data.Seed;
using Flight.Flights.Models;
using Flight.Seats.Models;
using MapsterMapper;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace Integration.Test;
public class FlightTestDataSeeder(
FlightDbContext flightDbContext,
FlightReadDbContext flightReadDbContext,
IMapper mapper
) : ITestDataSeeder
{
public async Task SeedAllAsync()
{
await SeedAirportAsync();
await SeedAircraftAsync();
await SeedFlightAsync();
await SeedSeatAsync();
}
private async Task SeedAirportAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Airports))
{
await flightDbContext.Airports.AddRangeAsync(InitialData.Airports);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Airport.AsQueryable()))
{
await flightReadDbContext.Airport.InsertManyAsync(mapper.Map<List<AirportReadModel>>(InitialData.Airports));
}
}
}
private async Task SeedAircraftAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Aircraft))
{
await flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Aircraft.AsQueryable()))
{
await flightReadDbContext.Aircraft.InsertManyAsync(mapper.Map<List<AircraftReadModel>>(InitialData.Aircrafts));
}
}
}
private async Task SeedSeatAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Seats))
{
await flightDbContext.Seats.AddRangeAsync(InitialData.Seats);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Seat.AsQueryable()))
{
await flightReadDbContext.Seat.InsertManyAsync(mapper.Map<List<SeatReadModel>>(InitialData.Seats));
}
}
}
private async Task SeedFlightAsync()
{
if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Flights))
{
await flightDbContext.Flights.AddRangeAsync(InitialData.Flights);
await flightDbContext.SaveChangesAsync();
if (!await MongoQueryable.AnyAsync(flightReadDbContext.Flight.AsQueryable()))
{
await flightReadDbContext.Flight.InsertManyAsync(mapper.Map<List<FlightReadModel>>(InitialData.Flights));
}
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Api\src\Api.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,40 @@
using BuildingBlocks.TestBase;
using Flight;
using Flight.Data;
using FluentAssertions;
using Integration.Test.Fakes;
using Xunit;
namespace Integration.Test.Seat.Features;
using Api;
public class GetAvailableSeatsTests : FlightIntegrationTestBase
{
public GetAvailableSeatsTests(
TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
{
}
[Fact]
public async Task should_return_available_seats_from_grpc_service()
{
// Arrange
var flightCommand = new FakeCreateFlightMongoCommand().Generate();
await Fixture.SendAsync(flightCommand);
var seatCommand = new FakeCreateSeatMongoCommand(flightCommand.Id).Generate();
await Fixture.SendAsync(seatCommand);
var flightGrpcClient = new FlightGrpcService.FlightGrpcServiceClient(Fixture.Channel);
// Act
var response = await flightGrpcClient.GetAvailableSeatsAsync(new GetAvailableSeatsRequest { FlightId = flightCommand.Id.ToString() });
// Assert
response?.Should().NotBeNull();
response?.SeatDtos?.Count.Should().BeGreaterOrEqualTo(1);
}
}

View File

@ -0,0 +1,42 @@
using Api;
using BuildingBlocks.TestBase;
using Flight;
using Flight.Data;
using FluentAssertions;
using Integration.Test.Fakes;
using Xunit;
namespace Integration.Test.Seat.Features;
public class ReserveSeatTests : FlightIntegrationTestBase
{
public ReserveSeatTests(
TestFixture<Program, FlightDbContext, FlightReadDbContext> integrationTestFactory) : base(integrationTestFactory)
{
}
[Fact]
public async Task should_return_valid_reserve_seat_from_grpc_service()
{
// Arrange
var flightCommand = new FakeCreateFlightCommand().Generate();
await Fixture.SendAsync(flightCommand);
var seatCommand = new FakeCreateSeatCommand(flightCommand.Id).Generate();
await Fixture.SendAsync(seatCommand);
var flightGrpcClient = new FlightGrpcService.FlightGrpcServiceClient(Fixture.Channel);
// Act
var response = await flightGrpcClient.ReserveSeatAsync(new ReserveSeatRequest()
{
FlightId = seatCommand.FlightId.ToString(),
SeatNumber = seatCommand.SeatNumber
});
// Assert
response?.Should().NotBeNull();
response?.Id.Should().Be(seatCommand?.Id.ToString());
}
}

View File

@ -0,0 +1,4 @@
{
"parallelizeAssembly": false,
"parallelizeTestCollections": false
}

View File

@ -0,0 +1,53 @@
using FluentAssertions;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
namespace Unit.Test.Aircraft.Features.CreateAircraftTests;
using global::Flight.Aircrafts.Features.CreatingAircraft.V1;
[Collection(nameof(UnitTestFixture))]
public class CreateAircraftCommandHandlerTests
{
private readonly UnitTestFixture _fixture;
private readonly CreateAircraftHandler _handler;
public Task<CreateAircraftResult> Act(CreateAircraft command, CancellationToken cancellationToken) =>
_handler.Handle(command, cancellationToken);
public CreateAircraftCommandHandlerTests(UnitTestFixture fixture)
{
_fixture = fixture;
_handler = new CreateAircraftHandler(_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.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
CreateAircraft command = null;
// Act
Func<Task> act = async () => { await Act(command, CancellationToken.None); };
// Assert
await act.Should().ThrowAsync<ArgumentNullException>();
}
}

View File

@ -0,0 +1,30 @@
using FluentAssertions;
using FluentValidation.TestHelper;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
namespace Unit.Test.Aircraft.Features.CreateAircraftTests;
using global::Flight.Aircrafts.Features.CreatingAircraft.V1;
[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 CreateAircraftValidator();
// 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,55 @@
using FluentAssertions;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
namespace Unit.Test.Airport.Features.CreateAirportTests;
using global::Flight.Airports.Features.CreatingAirport.V1;
using global::Flight.Airports.ValueObjects;
[Collection(nameof(UnitTestFixture))]
public class CreateAirportCommandHandlerTests
{
private readonly UnitTestFixture _fixture;
private readonly CreateAirportHandler _handler;
public CreateAirportCommandHandlerTests(UnitTestFixture fixture)
{
_fixture = fixture;
_handler = new CreateAirportHandler(_fixture.DbContext);
}
public Task<CreateAirportResult> Act(CreateAirport 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.FindAsync(AirportId.Of(response.Id));
entity?.Should().NotBeNull();
response?.Id.Should().Be(entity.Id);
}
[Fact]
public async Task handler_with_null_command_should_throw_argument_exception()
{
// Arrange
CreateAirport command = null;
// Act
var act = async () => { await Act(command, CancellationToken.None); };
// Assert
await act.Should().ThrowAsync<ArgumentNullException>();
}
}

View File

@ -0,0 +1,30 @@
using FluentAssertions;
using FluentValidation.TestHelper;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
namespace Unit.Test.Airport.Features.CreateAirportTests;
using global::Flight.Airports.Features.CreatingAirport.V1;
[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 CreateAirportValidator();
// 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,96 @@
using Flight.Data;
using Flight.Flights.Enums;
using Flight.Seats.Enums;
using Microsoft.EntityFrameworkCore;
namespace Unit.Test.Common;
using global::Flight.Aircrafts.ValueObjects;
using global::Flight.Airports.ValueObjects;
using global::Flight.Flights.ValueObjects;
using global::Flight.Seats.ValueObjects;
using MassTransit;
using AirportName = global::Flight.Airports.ValueObjects.Name;
using Name = global::Flight.Aircrafts.ValueObjects.Name;
public static class DbContextFactory
{
private static readonly Guid _airportId1 = NewId.NextGuid();
private static readonly Guid _airportId2 = NewId.NextGuid();
private static readonly Guid _aircraft1 = NewId.NextGuid();
private static readonly Guid _aircraft2 = NewId.NextGuid();
private static readonly Guid _aircraft3 = NewId.NextGuid();
private static readonly Guid _flightId1 = NewId.NextGuid();
public static FlightDbContext Create()
{
var options = new DbContextOptionsBuilder<FlightDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options;
var context = new FlightDbContext(options, currentUserProvider: null, 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(AirportId.Of(_airportId1), AirportName.Of("Lisbon International Airport"), Address.Of("LIS"),
Code.Of("12988")),
global::Flight.Airports.Models.Airport.Create(AirportId.Of(_airportId2), AirportName.Of("Sao Paulo International Airport"), Address.Of("BRZ"),
Code.Of("11200"))
};
context.Airports.AddRange(airports);
var aircrafts = new List<global::Flight.Aircrafts.Models.Aircraft>
{
global::Flight.Aircrafts.Models.Aircraft.Create(AircraftId.Of(_aircraft1), Name.Of("Boeing 737"), Model.Of("B737"), ManufacturingYear.Of(2005)),
global::Flight.Aircrafts.Models.Aircraft.Create(AircraftId.Of(_aircraft2), Name.Of("Airbus 300"), Model.Of("A300"), ManufacturingYear.Of(2000)),
global::Flight.Aircrafts.Models.Aircraft.Create(AircraftId.Of(_aircraft3), Name.Of("Airbus 320"), Model.Of("A320"), ManufacturingYear.Of(2003))
};
context.Aircraft.AddRange(aircrafts);
var flights = new List<global::Flight.Flights.Models.Flight>
{
global::Flight.Flights.Models.Flight.Create(FlightId.Of(_flightId1), FlightNumber.Of( "BD467"), AircraftId.Of(_aircraft1), AirportId.Of( _airportId1),
DepartureDate.Of( new DateTime(2022, 1, 31, 12, 0, 0)),
ArriveDate.Of( new DateTime(2022, 1, 31, 14, 0, 0)),
AirportId.Of( _airportId2), DurationMinutes.Of(120m),
FlightDate.Of( new DateTime(2022, 1, 31)), FlightStatus.Completed,
Price.Of(8000))
};
context.Flights.AddRange(flights);
var seats = new List<global::Flight.Seats.Models.Seat>
{
global::Flight.Seats.Models.Seat.Create(SeatId.Of( NewId.NextGuid()), SeatNumber.Of("12A"), SeatType.Window, SeatClass.Economy,
FlightId.Of(_flightId1)),
global::Flight.Seats.Models.Seat.Create(SeatId.Of(NewId.NextGuid()), SeatNumber.Of("12B"), SeatType.Window, SeatClass.Economy,
FlightId.Of(_flightId1)),
global::Flight.Seats.Models.Seat.Create(SeatId.Of(NewId.NextGuid()), SeatNumber.Of("12C"), SeatType.Middle, SeatClass.Economy,
FlightId.Of(_flightId1)),
global::Flight.Seats.Models.Seat.Create(SeatId.Of(NewId.NextGuid()), SeatNumber.Of("12D"), SeatType.Middle, SeatClass.Economy,
FlightId.Of(_flightId1)),
global::Flight.Seats.Models.Seat.Create(SeatId.Of(NewId.NextGuid()), SeatNumber.Of("12E"), SeatType.Aisle, SeatClass.Economy,
FlightId.Of(_flightId1)),
global::Flight.Seats.Models.Seat.Create(SeatId.Of(NewId.NextGuid()), SeatNumber.Of("12F"), SeatType.Aisle, SeatClass.Economy,
FlightId.Of(_flightId1))
};
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,26 @@
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()
{
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,15 @@
using AutoBogus;
namespace Unit.Test.Fakes;
using global::Flight.Aircrafts.Features.CreatingAircraft.V1;
using MassTransit;
public class FakeCreateAircraftCommand : AutoFaker<CreateAircraft>
{
public FakeCreateAircraftCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.ManufacturingYear, _ => 2000);
}
}

View File

@ -0,0 +1,14 @@
using AutoBogus;
namespace Unit.Test.Fakes;
using global::Flight.Airports.Features.CreatingAirport.V1;
using MassTransit;
public class FakeCreateAirportCommand : AutoFaker<CreateAirport>
{
public FakeCreateAirportCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
}
}

View File

@ -0,0 +1,20 @@
using AutoBogus;
namespace Unit.Test.Fakes;
using System.Linq;
using global::Flight.Data.Seed;
using global::Flight.Flights.Features.CreatingFlight.V1;
using MassTransit;
public sealed class FakeCreateFlightCommand : AutoFaker<CreateFlight>
{
public FakeCreateFlightCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.FlightNumber, r => r.Random.Number(1000, 2000).ToString());
RuleFor(r => r.DepartureAirportId, _ => InitialData.Airports.First().Id);
RuleFor(r => r.ArriveAirportId, _ => InitialData.Airports.Last().Id);
RuleFor(r => r.AircraftId, _ => InitialData.Aircrafts.First().Id);
}
}

View File

@ -0,0 +1,21 @@
using AutoBogus;
using Flight.Seats.Enums;
namespace Unit.Test.Fakes;
using System.Linq;
using global::Flight.Data.Seed;
using global::Flight.Seats.Features.CreatingSeat.V1;
using MassTransit;
public class FakeCreateSeatCommand : AutoFaker<CreateSeat>
{
public FakeCreateSeatCommand()
{
RuleFor(r => r.Id, _ => NewId.NextGuid());
RuleFor(r => r.FlightId, _ => InitialData.Flights.First().Id);
RuleFor(r => r.SeatNumber, _ => "F99");
RuleFor(r => r.Type, _ => SeatType.Window);
RuleFor(r => r.Class, _ => SeatClass.Economy);
}
}

View File

@ -0,0 +1,18 @@
namespace Unit.Test.Fakes;
using global::Flight.Aircrafts.ValueObjects;
using global::Flight.Airports.ValueObjects;
using global::Flight.Flights.ValueObjects;
public static class FakeFlightCreate
{
public static global::Flight.Flights.Models.Flight Generate()
{
var command = new FakeCreateFlightCommand().Generate();
return global::Flight.Flights.Models.Flight.Create(FlightId.Of(command.Id), FlightNumber.Of(command.FlightNumber),
AircraftId.Of(command.AircraftId), AirportId.Of(command.DepartureAirportId), DepartureDate.Of(command.DepartureDate),
ArriveDate.Of(command.ArriveDate), AirportId.Of(command.ArriveAirportId), DurationMinutes.Of(command.DurationMinutes),
FlightDate.Of(command.FlightDate), command.Status, Price.Of(command.Price));
}
}

View File

@ -0,0 +1,13 @@
namespace Unit.Test.Fakes;
using global::Flight.Flights.Models;
using global::Flight.Flights.ValueObjects;
public static class FakeFlightUpdate
{
public static void Generate(Flight flight)
{
flight.Update(flight.Id, flight.FlightNumber, flight.AircraftId, flight.DepartureAirportId, flight.DepartureDate,
flight.ArriveDate, flight.ArriveAirportId, flight.DurationMinutes, flight.FlightDate, flight.Status, Price.Of(1000), flight.IsDeleted);
}
}

View File

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

View File

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

View File

@ -0,0 +1,21 @@
using System;
using AutoBogus;
using Flight.Flights.Enums;
namespace Unit.Test.Fakes;
using global::Flight.Flights.Features.CreatingFlight.V1;
public class FakeValidateCreateFlightCommand : AutoFaker<CreateFlight>
{
public FakeValidateCreateFlightCommand()
{
RuleFor(r => r.Price, _ => -10);
RuleFor(r => r.Status, _ => (FlightStatus)10);
RuleFor(r => r.AircraftId, _ => Guid.Empty);
RuleFor(r => r.DepartureAirportId, _ => Guid.Empty);
RuleFor(r => r.ArriveAirportId, _ => Guid.Empty);
RuleFor(r => r.DurationMinutes, _ => 0);
RuleFor(r => r.FlightDate, _ => new DateTime());
}
}

View File

@ -0,0 +1,17 @@
using AutoBogus;
using Flight.Seats.Enums;
namespace Unit.Test.Fakes;
using System;
using global::Flight.Seats.Features.CreatingSeat.V1;
public class FakeValidateCreateSeatCommand : AutoFaker<CreateSeat>
{
public FakeValidateCreateSeatCommand()
{
RuleFor(r => r.SeatNumber, _ => null);
RuleFor(r => r.FlightId, _ => Guid.Empty);
RuleFor(r => r.Class, _ => (SeatClass)10);
}
}

View File

@ -0,0 +1,34 @@
namespace Unit.Test.Flight.Features.Domains
{
using System.Linq;
using FluentAssertions;
using global::Flight.Flights.Features.CreatingFlight.V1;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
[Collection(nameof(UnitTestFixture))]
public class CreateFlightTests
{
[Fact]
public void can_create_valid_flight()
{
// Arrange + Act
var fakeFlight = FakeFlightCreate.Generate();
// Assert
fakeFlight.Should().NotBeNull();
}
[Fact]
public void queue_domain_event_on_create()
{
// Arrange + Act
var fakeFlight = FakeFlightCreate.Generate();
// Assert
fakeFlight.DomainEvents.Count.Should().Be(1);
fakeFlight.DomainEvents.FirstOrDefault().Should().BeOfType(typeof(FlightCreatedDomainEvent));
}
}
}

View File

@ -0,0 +1,40 @@
namespace Unit.Test.Flight.Features.Domains;
using System.Linq;
using FluentAssertions;
using global::Flight.Flights.Features.UpdatingFlight.V1;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
[Collection(nameof(UnitTestFixture))]
public class UpdateFlightTests
{
[Fact]
public void can_update_valid_flight()
{
// Arrange
var fakeFlight = FakeFlightCreate.Generate();
// Act
FakeFlightUpdate.Generate(fakeFlight);
// Assert
fakeFlight.Price.Value.Should().Be(1000);
}
[Fact]
public void queue_domain_event_on_update()
{
// Arrange
var fakeFlight = FakeFlightCreate.Generate();
fakeFlight.ClearDomainEvents();
// Act
FakeFlightUpdate.Generate(fakeFlight);
// Assert
fakeFlight.DomainEvents.Count.Should().Be(1);
fakeFlight.DomainEvents.FirstOrDefault().Should().BeOfType(typeof(FlightUpdatedDomainEvent));
}
}

View File

@ -0,0 +1,56 @@
namespace Unit.Test.Flight.Features.Handlers.CreateFlight;
using System;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using global::Flight.Flights.Features.CreatingFlight.V1;
using global::Flight.Flights.ValueObjects;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
[Collection(nameof(UnitTestFixture))]
public class CreateFlightCommandHandlerTests
{
private readonly UnitTestFixture _fixture;
private readonly CreateFlightHandler _handler;
public Task<CreateFlightResult> Act(CreateFlight command, CancellationToken cancellationToken) =>
_handler.Handle(command, cancellationToken);
public CreateFlightCommandHandlerTests(UnitTestFixture fixture)
{
_fixture = fixture;
_handler = new CreateFlightHandler(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(FlightId.Of(response.Id));
entity?.Should().NotBeNull();
response?.Id.Should().Be(entity.Id);
}
[Fact]
public async Task handler_with_null_command_should_throw_argument_exception()
{
// Arrange
CreateFlight command = null;
// Act
Func<Task> act = async () => { await Act(command, CancellationToken.None); };
// Assert
await act.Should().ThrowAsync<ArgumentNullException>();
}
}

View File

@ -0,0 +1,33 @@
namespace Unit.Test.Flight.Features.Handlers.CreateFlight;
using FluentAssertions;
using FluentValidation.TestHelper;
using global::Flight.Flights.Features.CreatingFlight.V1;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
[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 CreateFlightValidator();
// 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,39 @@
using Flight.Flights.Dtos;
using MapsterMapper;
using Unit.Test.Common;
using Xunit;
namespace Unit.Test.Flight;
[Collection(nameof(UnitTestFixture))]
public class FlightMappingTests
{
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.FlightReadModel), typeof(FlightDto)
};
}
}
[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,57 @@
using FluentAssertions;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
namespace Unit.Test.Seat.Features;
using global::Flight.Seats.Features.CreatingSeat.V1;
using global::Flight.Seats.ValueObjects;
[Collection(nameof(UnitTestFixture))]
public class CreateSeatCommandHandlerTests
{
private readonly UnitTestFixture _fixture;
private readonly CreateSeatCommandHandler _handler;
public CreateSeatCommandHandlerTests(UnitTestFixture fixture)
{
_fixture = fixture;
_handler = new CreateSeatCommandHandler(_fixture.DbContext);
}
public Task<CreateSeatResult> Act(CreateSeat 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.FindAsync(SeatId.Of(response.Id));
entity?.Should().NotBeNull();
response?.Id.Should().Be(entity.Id);
}
[Fact]
public async Task handler_with_null_command_should_throw_argument_exception()
{
// Arrange
CreateSeat command = null;
// Act
var act = async () => { await Act(command, CancellationToken.None); };
// Assert
await act.Should().ThrowAsync<ArgumentNullException>();
}
}

View File

@ -0,0 +1,30 @@
using FluentAssertions;
using FluentValidation.TestHelper;
using Unit.Test.Common;
using Unit.Test.Fakes;
using Xunit;
namespace Unit.Test.Seat.Features;
using global::Flight.Seats.Features.CreatingSeat.V1;
[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 CreateSeatValidator();
// 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,40 @@
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.SeatReadModel), typeof(SeatDto)
};
}
}
[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,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Flight.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,4 @@
{
"parallelizeAssembly": false,
"parallelizeTestCollections": false
}

View File

@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35931.197
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unit.Test", "Unit.Test\Unit.Test.csproj", "{9FF9D3A3-D316-461F-90D8-A51226796AB8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "Integration.Test\Integration.Test.csproj", "{7CD2A0EF-7505-43D5-A428-8F8EB4C8004F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EndToEnd.Test", "EndToEnd.Test\EndToEnd.Test.csproj", "{52AB66AF-F024-F2C2-5B1B-29218D5BB25E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9FF9D3A3-D316-461F-90D8-A51226796AB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9FF9D3A3-D316-461F-90D8-A51226796AB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FF9D3A3-D316-461F-90D8-A51226796AB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9FF9D3A3-D316-461F-90D8-A51226796AB8}.Release|Any CPU.Build.0 = Release|Any CPU
{7CD2A0EF-7505-43D5-A428-8F8EB4C8004F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CD2A0EF-7505-43D5-A428-8F8EB4C8004F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CD2A0EF-7505-43D5-A428-8F8EB4C8004F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CD2A0EF-7505-43D5-A428-8F8EB4C8004F}.Release|Any CPU.Build.0 = Release|Any CPU
{52AB66AF-F024-F2C2-5B1B-29218D5BB25E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52AB66AF-F024-F2C2-5B1B-29218D5BB25E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52AB66AF-F024-F2C2-5B1B-29218D5BB25E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52AB66AF-F024-F2C2-5B1B-29218D5BB25E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4EB6EA31-EAFC-4770-A32B-52E5EFF62E05}
EndGlobalSection
EndGlobal

View File

@ -333,12 +333,22 @@ where TEntryPoint : class
private void AddCustomAppSettings(IConfigurationBuilder configuration)
{
//todo: provide better approach for reading `PostgresOptions`
configuration.AddInMemoryCollection(
new KeyValuePair<string, string>[]
{
new(
"PostgresOptions:ConnectionString",
PostgresTestcontainer.GetConnectionString()),
new(
"PostgresOptions:ConnectionString:Flight",
PostgresTestcontainer.GetConnectionString()),
new(
"PostgresOptions:ConnectionString:Identity",
PostgresTestcontainer.GetConnectionString()),
new(
"PostgresOptions:ConnectionString:Passenger",
PostgresTestcontainer.GetConnectionString()),
new(
"PersistMessageOptions:ConnectionString",
PostgresPersistTestContainer.GetConnectionString()),

View File

@ -103,62 +103,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Api", "Api", "{EAAC4A89-D71
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api", "1-monolith-architecture-style\src\Api\src\Api.csproj", "{E7BA185C-B26D-4ACB-A24A-70AB730DD2A0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Flight", "Flight", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unit.Test", "2-modular-monolith-architecture-style\src\Modules\Flight\tests\Unit.Test\Unit.Test.csproj", "{E7B7E65D-DB14-494C-A748-EF90666FB0B1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "2-modular-monolith-architecture-style\src\Modules\Flight\tests\Integration.Test\Integration.Test.csproj", "{AE7E4AE8-4A5C-44AE-B1FC-2A04824EE29B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EndToEnd.Test", "2-modular-monolith-architecture-style\src\Modules\Flight\tests\EndToEnd.Test\EndToEnd.Test.csproj", "{7CBA4E35-64EA-BFB3-0507-B7DE1E60CD54}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{57366225-A26A-4715-A690-DA641B0C0450} = {33F5E6F6-FACB-4D34-8E48-B80E0497D239}
{39BAB0A0-3C16-49B1-B817-9EC5C600BF5E} = {33F5E6F6-FACB-4D34-8E48-B80E0497D239}
{570274DD-E84A-4F22-9079-D60B59EC9ED2} = {F1BEC66C-B321-45D8-95C1-8DCD4743275D}
{AD2FB7C1-8641-47E9-B62D-B3A2D74147D8} = {F1BEC66C-B321-45D8-95C1-8DCD4743275D}
{DBAE70CC-011A-4997-9612-58AFAFF73291} = {96913068-5EFE-4D13-8B00-924AFED16439}
{FB529C7A-F884-42D1-BE41-2A6B7CC4101B} = {96913068-5EFE-4D13-8B00-924AFED16439}
{5A2A9F1C-245A-4978-93A0-E00120FEE765} = {57366225-A26A-4715-A690-DA641B0C0450}
{D0A6E1E7-DFB6-4940-A254-F2E24897C4AC} = {57366225-A26A-4715-A690-DA641B0C0450}
{F77CA77C-49CE-49B5-BE5A-FCCF6C63D65A} = {57366225-A26A-4715-A690-DA641B0C0450}
{A1786514-35EB-4AFA-87E1-2FB89D748C02} = {57366225-A26A-4715-A690-DA641B0C0450}
{A5E49049-0DFF-4D87-9188-2B0ACBC0D59B} = {57366225-A26A-4715-A690-DA641B0C0450}
{6538BDF3-A741-46E9-8988-C859ABB2FBB2} = {A5E49049-0DFF-4D87-9188-2B0ACBC0D59B}
{B851799B-A328-4E40-9095-C56C11A6235E} = {D0A6E1E7-DFB6-4940-A254-F2E24897C4AC}
{674C0974-11C4-4BF7-B2DF-8ED753919224} = {D0A6E1E7-DFB6-4940-A254-F2E24897C4AC}
{FB8AA20B-0D31-4241-A126-07992BCF7E2A} = {5A2A9F1C-245A-4978-93A0-E00120FEE765}
{0EB6B262-197D-450C-A56E-634D2D428FCB} = {5A2A9F1C-245A-4978-93A0-E00120FEE765}
{694E763C-E076-4F36-A3CE-3A55D794C871} = {A1786514-35EB-4AFA-87E1-2FB89D748C02}
{E18BC5D2-5A32-469A-9E0B-5ACFE3B6639B} = {A1786514-35EB-4AFA-87E1-2FB89D748C02}
{1F2CE508-42D0-437B-83AB-0D5FEBE324F8} = {F77CA77C-49CE-49B5-BE5A-FCCF6C63D65A}
{77410EA7-739C-475B-B460-CCEF5CEE1AD1} = {F77CA77C-49CE-49B5-BE5A-FCCF6C63D65A}
{7D81CAA6-6DEF-43C1-AE1B-D0F0B7B09CE7} = {39BAB0A0-3C16-49B1-B817-9EC5C600BF5E}
{D2F19D7F-A9DA-47D2-A445-F9ED8D4479C1} = {39BAB0A0-3C16-49B1-B817-9EC5C600BF5E}
{51EE0C7A-6D1A-4538-957A-34B6906FC932} = {39BAB0A0-3C16-49B1-B817-9EC5C600BF5E}
{3FD37B50-3C7D-49E9-9456-A3E82675227D} = {39BAB0A0-3C16-49B1-B817-9EC5C600BF5E}
{5D3D2499-0732-4545-87F5-C26AF1FE827B} = {51EE0C7A-6D1A-4538-957A-34B6906FC932}
{EE5ECF6A-0D4F-4737-BAD3-AE66D1B53D83} = {7D81CAA6-6DEF-43C1-AE1B-D0F0B7B09CE7}
{0A74D3F2-7814-4FB4-890D-4899749B67A2} = {7D81CAA6-6DEF-43C1-AE1B-D0F0B7B09CE7}
{FDA41108-8194-49B2-8F78-9F8AECABB7BC} = {7D81CAA6-6DEF-43C1-AE1B-D0F0B7B09CE7}
{B6D11E8B-CAAE-4452-B3AE-A49DA7E68FE0} = {3FD37B50-3C7D-49E9-9456-A3E82675227D}
{6D17EFCC-63DB-4E51-8073-25D5E59B2170} = {D2F19D7F-A9DA-47D2-A445-F9ED8D4479C1}
{5ED78466-4114-48ED-9A6E-02143984E7A1} = {B85B39B5-B341-4117-8626-C5DD4F375569}
{D1907049-C23E-47CB-9DF1-0D9EDB7CE117} = {570274DD-E84A-4F22-9079-D60B59EC9ED2}
{5F943131-E273-474E-891E-6386C4B10D00} = {570274DD-E84A-4F22-9079-D60B59EC9ED2}
{183FF15D-1B24-4FA4-A5E4-505825919113} = {5F943131-E273-474E-891E-6386C4B10D00}
{3CB44FE8-8DC1-49BD-864A-72FB6A8229C5} = {5F943131-E273-474E-891E-6386C4B10D00}
{254C235E-7E2D-4FEE-9EB4-50E48BDB1295} = {5F943131-E273-474E-891E-6386C4B10D00}
{6C250353-B112-42F5-BBE9-FA2A725870FD} = {5F943131-E273-474E-891E-6386C4B10D00}
{99914C87-B2FC-4DB2-9BFC-AA2D63B3024F} = {183FF15D-1B24-4FA4-A5E4-505825919113}
{18FAA2C9-5B3B-41D4-83F4-F91B0F2355A0} = {D1907049-C23E-47CB-9DF1-0D9EDB7CE117}
{301AB091-1BBB-4D95-9A54-AA7A8EE928EF} = {3CB44FE8-8DC1-49BD-864A-72FB6A8229C5}
{3020E2CD-C6E5-4489-914E-96083705AF0E} = {6C250353-B112-42F5-BBE9-FA2A725870FD}
{1CD81080-9F44-49AA-94F9-EFEBFD8073E4} = {254C235E-7E2D-4FEE-9EB4-50E48BDB1295}
{DB31E41A-D441-4BB8-B96C-F70395FBFB95} = {DBAE70CC-011A-4997-9612-58AFAFF73291}
{ECBE72AF-7F47-4086-A3F8-AF011085E253} = {DB31E41A-D441-4BB8-B96C-F70395FBFB95}
{EAAC4A89-D71D-426F-ABB0-127C3E777E54} = {DBAE70CC-011A-4997-9612-58AFAFF73291}
{E7BA185C-B26D-4ACB-A24A-70AB730DD2A0} = {EAAC4A89-D71D-426F-ABB0-127C3E777E54}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6538BDF3-A741-46E9-8988-C859ABB2FBB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6538BDF3-A741-46E9-8988-C859ABB2FBB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
@ -252,5 +209,75 @@ Global
{E7BA185C-B26D-4ACB-A24A-70AB730DD2A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7BA185C-B26D-4ACB-A24A-70AB730DD2A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7BA185C-B26D-4ACB-A24A-70AB730DD2A0}.Release|Any CPU.Build.0 = Release|Any CPU
{E7B7E65D-DB14-494C-A748-EF90666FB0B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7B7E65D-DB14-494C-A748-EF90666FB0B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7B7E65D-DB14-494C-A748-EF90666FB0B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7B7E65D-DB14-494C-A748-EF90666FB0B1}.Release|Any CPU.Build.0 = Release|Any CPU
{AE7E4AE8-4A5C-44AE-B1FC-2A04824EE29B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE7E4AE8-4A5C-44AE-B1FC-2A04824EE29B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE7E4AE8-4A5C-44AE-B1FC-2A04824EE29B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE7E4AE8-4A5C-44AE-B1FC-2A04824EE29B}.Release|Any CPU.Build.0 = Release|Any CPU
{7CBA4E35-64EA-BFB3-0507-B7DE1E60CD54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CBA4E35-64EA-BFB3-0507-B7DE1E60CD54}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CBA4E35-64EA-BFB3-0507-B7DE1E60CD54}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CBA4E35-64EA-BFB3-0507-B7DE1E60CD54}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{57366225-A26A-4715-A690-DA641B0C0450} = {33F5E6F6-FACB-4D34-8E48-B80E0497D239}
{39BAB0A0-3C16-49B1-B817-9EC5C600BF5E} = {33F5E6F6-FACB-4D34-8E48-B80E0497D239}
{570274DD-E84A-4F22-9079-D60B59EC9ED2} = {F1BEC66C-B321-45D8-95C1-8DCD4743275D}
{AD2FB7C1-8641-47E9-B62D-B3A2D74147D8} = {F1BEC66C-B321-45D8-95C1-8DCD4743275D}
{DBAE70CC-011A-4997-9612-58AFAFF73291} = {96913068-5EFE-4D13-8B00-924AFED16439}
{FB529C7A-F884-42D1-BE41-2A6B7CC4101B} = {96913068-5EFE-4D13-8B00-924AFED16439}
{5A2A9F1C-245A-4978-93A0-E00120FEE765} = {57366225-A26A-4715-A690-DA641B0C0450}
{D0A6E1E7-DFB6-4940-A254-F2E24897C4AC} = {57366225-A26A-4715-A690-DA641B0C0450}
{F77CA77C-49CE-49B5-BE5A-FCCF6C63D65A} = {57366225-A26A-4715-A690-DA641B0C0450}
{A1786514-35EB-4AFA-87E1-2FB89D748C02} = {57366225-A26A-4715-A690-DA641B0C0450}
{A5E49049-0DFF-4D87-9188-2B0ACBC0D59B} = {57366225-A26A-4715-A690-DA641B0C0450}
{6538BDF3-A741-46E9-8988-C859ABB2FBB2} = {A5E49049-0DFF-4D87-9188-2B0ACBC0D59B}
{B851799B-A328-4E40-9095-C56C11A6235E} = {D0A6E1E7-DFB6-4940-A254-F2E24897C4AC}
{674C0974-11C4-4BF7-B2DF-8ED753919224} = {D0A6E1E7-DFB6-4940-A254-F2E24897C4AC}
{FB8AA20B-0D31-4241-A126-07992BCF7E2A} = {5A2A9F1C-245A-4978-93A0-E00120FEE765}
{0EB6B262-197D-450C-A56E-634D2D428FCB} = {5A2A9F1C-245A-4978-93A0-E00120FEE765}
{694E763C-E076-4F36-A3CE-3A55D794C871} = {A1786514-35EB-4AFA-87E1-2FB89D748C02}
{E18BC5D2-5A32-469A-9E0B-5ACFE3B6639B} = {A1786514-35EB-4AFA-87E1-2FB89D748C02}
{1F2CE508-42D0-437B-83AB-0D5FEBE324F8} = {F77CA77C-49CE-49B5-BE5A-FCCF6C63D65A}
{77410EA7-739C-475B-B460-CCEF5CEE1AD1} = {F77CA77C-49CE-49B5-BE5A-FCCF6C63D65A}
{7D81CAA6-6DEF-43C1-AE1B-D0F0B7B09CE7} = {39BAB0A0-3C16-49B1-B817-9EC5C600BF5E}
{D2F19D7F-A9DA-47D2-A445-F9ED8D4479C1} = {39BAB0A0-3C16-49B1-B817-9EC5C600BF5E}
{51EE0C7A-6D1A-4538-957A-34B6906FC932} = {39BAB0A0-3C16-49B1-B817-9EC5C600BF5E}
{3FD37B50-3C7D-49E9-9456-A3E82675227D} = {39BAB0A0-3C16-49B1-B817-9EC5C600BF5E}
{5D3D2499-0732-4545-87F5-C26AF1FE827B} = {51EE0C7A-6D1A-4538-957A-34B6906FC932}
{EE5ECF6A-0D4F-4737-BAD3-AE66D1B53D83} = {7D81CAA6-6DEF-43C1-AE1B-D0F0B7B09CE7}
{0A74D3F2-7814-4FB4-890D-4899749B67A2} = {7D81CAA6-6DEF-43C1-AE1B-D0F0B7B09CE7}
{FDA41108-8194-49B2-8F78-9F8AECABB7BC} = {7D81CAA6-6DEF-43C1-AE1B-D0F0B7B09CE7}
{B6D11E8B-CAAE-4452-B3AE-A49DA7E68FE0} = {3FD37B50-3C7D-49E9-9456-A3E82675227D}
{6D17EFCC-63DB-4E51-8073-25D5E59B2170} = {D2F19D7F-A9DA-47D2-A445-F9ED8D4479C1}
{5ED78466-4114-48ED-9A6E-02143984E7A1} = {B85B39B5-B341-4117-8626-C5DD4F375569}
{D1907049-C23E-47CB-9DF1-0D9EDB7CE117} = {570274DD-E84A-4F22-9079-D60B59EC9ED2}
{5F943131-E273-474E-891E-6386C4B10D00} = {570274DD-E84A-4F22-9079-D60B59EC9ED2}
{183FF15D-1B24-4FA4-A5E4-505825919113} = {5F943131-E273-474E-891E-6386C4B10D00}
{3CB44FE8-8DC1-49BD-864A-72FB6A8229C5} = {5F943131-E273-474E-891E-6386C4B10D00}
{254C235E-7E2D-4FEE-9EB4-50E48BDB1295} = {5F943131-E273-474E-891E-6386C4B10D00}
{6C250353-B112-42F5-BBE9-FA2A725870FD} = {5F943131-E273-474E-891E-6386C4B10D00}
{99914C87-B2FC-4DB2-9BFC-AA2D63B3024F} = {183FF15D-1B24-4FA4-A5E4-505825919113}
{18FAA2C9-5B3B-41D4-83F4-F91B0F2355A0} = {D1907049-C23E-47CB-9DF1-0D9EDB7CE117}
{301AB091-1BBB-4D95-9A54-AA7A8EE928EF} = {3CB44FE8-8DC1-49BD-864A-72FB6A8229C5}
{3020E2CD-C6E5-4489-914E-96083705AF0E} = {6C250353-B112-42F5-BBE9-FA2A725870FD}
{1CD81080-9F44-49AA-94F9-EFEBFD8073E4} = {254C235E-7E2D-4FEE-9EB4-50E48BDB1295}
{DB31E41A-D441-4BB8-B96C-F70395FBFB95} = {DBAE70CC-011A-4997-9612-58AFAFF73291}
{ECBE72AF-7F47-4086-A3F8-AF011085E253} = {DB31E41A-D441-4BB8-B96C-F70395FBFB95}
{EAAC4A89-D71D-426F-ABB0-127C3E777E54} = {DBAE70CC-011A-4997-9612-58AFAFF73291}
{E7BA185C-B26D-4ACB-A24A-70AB730DD2A0} = {EAAC4A89-D71D-426F-ABB0-127C3E777E54}
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {AD2FB7C1-8641-47E9-B62D-B3A2D74147D8}
{E7B7E65D-DB14-494C-A748-EF90666FB0B1} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{AE7E4AE8-4A5C-44AE-B1FC-2A04824EE29B} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{7CBA4E35-64EA-BFB3-0507-B7DE1E60CD54} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BF29DFD4-25EC-44C4-9DA6-E3AC4B292257}
EndGlobalSection
EndGlobal

4
package-lock.json generated
View File

@ -1,11 +1,11 @@
{
"name": "booking-microservices",
"name": "monolith-to-cloud-architecture",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "booking-microservices",
"name": "monolith-to-cloud-architecture",
"version": "1.0.0",
"license": "MIT",
"devDependencies": {