diff --git a/2-modular-monolith-architecture-style/src/Api/src/Program.cs b/2-modular-monolith-architecture-style/src/Api/src/Program.cs index 5fb7504..8fffce4 100644 --- a/2-modular-monolith-architecture-style/src/Api/src/Program.cs +++ b/2-modular-monolith-architecture-style/src/Api/src/Program.cs @@ -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(); diff --git a/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/EndToEnd.Test.csproj b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/EndToEnd.Test.csproj new file mode 100644 index 0000000..a0c6e9f --- /dev/null +++ b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/EndToEnd.Test.csproj @@ -0,0 +1,22 @@ + + + + + PreserveNewest + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Fakes/FakeCreateFlightCommand.cs b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Fakes/FakeCreateFlightCommand.cs new file mode 100644 index 0000000..83df003 --- /dev/null +++ b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Fakes/FakeCreateFlightCommand.cs @@ -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 +{ + 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); + } +} diff --git a/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Fakes/FakeCreateFlightMongoCommand.cs b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Fakes/FakeCreateFlightMongoCommand.cs new file mode 100644 index 0000000..603c455 --- /dev/null +++ b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Fakes/FakeCreateFlightMongoCommand.cs @@ -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 +{ + 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); + } +} + diff --git a/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Flight/Features/CreateFlightTests.cs b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Flight/Features/CreateFlightTests.cs new file mode 100644 index 0000000..9e64a7c --- /dev/null +++ b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Flight/Features/CreateFlightTests.cs @@ -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 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); + } +} diff --git a/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Flight/Features/GetFlightByIdTests.cs b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Flight/Features/GetFlightByIdTests.cs new file mode 100644 index 0000000..609b039 --- /dev/null +++ b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Flight/Features/GetFlightByIdTests.cs @@ -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 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); + } +} diff --git a/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/FlightEndToEndTestBase.cs b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/FlightEndToEndTestBase.cs new file mode 100644 index 0000000..ba8dad5 --- /dev/null +++ b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/FlightEndToEndTestBase.cs @@ -0,0 +1,20 @@ +using Api; +using BuildingBlocks.TestBase; +using Flight.Data; +using Xunit; + +namespace EndToEnd.Test; + +[Collection(EndToEndTestCollection.Name)] +public class FlightEndToEndTestBase : TestBase +{ + public FlightEndToEndTestBase(TestFixture integrationTestFixture) : base(integrationTestFixture) + { + } +} + +[CollectionDefinition(Name)] +public class EndToEndTestCollection : ICollectionFixture> +{ + public const string Name = "Flight EndToEnd Test"; +} diff --git a/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/FlightTestDataSeeder.cs b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/FlightTestDataSeeder.cs new file mode 100644 index 0000000..862356f --- /dev/null +++ b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/FlightTestDataSeeder.cs @@ -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>(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>(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>(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>(InitialData.Flights)); + } + } + } +} diff --git a/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Routes/ApiRoutes.cs b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Routes/ApiRoutes.cs new file mode 100644 index 0000000..0517f29 --- /dev/null +++ b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/Routes/ApiRoutes.cs @@ -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"; + } +} diff --git a/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/xunit.runner.json b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/xunit.runner.json new file mode 100644 index 0000000..9db029b --- /dev/null +++ b/2-modular-monolith-architecture-style/src/Modules/Flight/tests/EndToEnd.Test/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "parallelizeAssembly": false, + "parallelizeTestCollections": false +} diff --git a/monolith-to-cloud-architecture.sln b/monolith-to-cloud-architecture.sln index dbd5d44..df06619 100644 --- a/monolith-to-cloud-architecture.sln +++ b/monolith-to-cloud-architecture.sln @@ -109,6 +109,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unit.Test", "2-modular-mono 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 @@ -215,6 +217,10 @@ Global {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 @@ -269,6 +275,7 @@ Global {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}