From b9b3c26edcbf35965b791e37fc489e2c6b31e427 Mon Sep 17 00:00:00 2001 From: Meysam Hadeli Date: Thu, 19 Feb 2026 23:17:14 +0330 Subject: [PATCH] fix: fix ci failed --- src/BuildingBlocks/TestBase/TestBase.cs | 28 +++- src/BuildingBlocks/TestBase/TestContainers.cs | 19 +-- .../CompleteRegisterPassengerTests.cs | 147 ++++++++++++++++-- 3 files changed, 158 insertions(+), 36 deletions(-) diff --git a/src/BuildingBlocks/TestBase/TestBase.cs b/src/BuildingBlocks/TestBase/TestBase.cs index 8dca556..5bbb337 100644 --- a/src/BuildingBlocks/TestBase/TestBase.cs +++ b/src/BuildingBlocks/TestBase/TestBase.cs @@ -42,7 +42,7 @@ public class TestFixture : IAsyncLifetime where TEntryPoint : class { private readonly WebApplicationFactory _factory; - private int Timeout => 120; // Second + private int Timeout => 300; // Second private ITestHarness TestHarness => ServiceProvider?.GetTestHarness(); private Action TestRegistrationServices { get; set; } private PostgreSqlContainer PostgresTestcontainer; @@ -140,6 +140,9 @@ public class TestFixture : IAsyncLifetime if (ServiceProvider.GetService() is { } harness) { await harness.Start(); + + // Add a small delay to ensure harness is ready + await Task.Delay(1000); } } @@ -298,11 +301,24 @@ public class TestFixture : IAsyncLifetime MongoDbTestContainer = TestContainers.MongoTestContainer(); EventStoreDbTestContainer = TestContainers.EventStoreTestContainer(); - await MongoDbTestContainer.StartAsync(); - await PostgresTestcontainer.StartAsync(); - await PostgresPersistTestContainer.StartAsync(); + // Start containers in parallel for speed + await Task.WhenAll( + MongoDbTestContainer.StartAsync(), + PostgresTestcontainer.StartAsync(), + PostgresPersistTestContainer.StartAsync(), + EventStoreDbTestContainer.StartAsync() + ); + + // Start RabbitMQ last and wait extra time await RabbitMqTestContainer.StartAsync(); - await EventStoreDbTestContainer.StartAsync(); + await Task.Delay(5000); // Give RabbitMQ extra time to initialize + + // Verify RabbitMQ is healthy + var healthCheck = await RabbitMqTestContainer.ExecAsync(new[] { "rabbitmq-diagnostics", "ping" }); + if (healthCheck.ExitCode != 0) + { + await Task.Delay(5000); // Wait more if not healthy + } } private async Task StopTestContainerAsync() @@ -727,4 +743,4 @@ public abstract class TestBase : TestFixtureC } public TestFixture Fixture { get; } -} \ No newline at end of file +} diff --git a/src/BuildingBlocks/TestBase/TestContainers.cs b/src/BuildingBlocks/TestBase/TestContainers.cs index ff4a724..8c22b2e 100644 --- a/src/BuildingBlocks/TestBase/TestContainers.cs +++ b/src/BuildingBlocks/TestBase/TestContainers.cs @@ -87,25 +87,16 @@ public static class TestContainers public static RabbitMqContainer RabbitMqTestContainer() { - var builder = new RabbitMqBuilder() + var baseBuilder = new RabbitMqBuilder() .WithUsername(RabbitMqContainerConfiguration.UserName) .WithPassword(RabbitMqContainerConfiguration.Password) + .WithLabel("Key", "Value"); + + var builder = baseBuilder .WithImage(RabbitMqContainerConfiguration.ImageName) .WithName(RabbitMqContainerConfiguration.Name) .WithPortBinding(RabbitMqContainerConfiguration.ApiPort, true) .WithPortBinding(RabbitMqContainerConfiguration.Port, true) - .WithWaitStrategy( - Wait.ForUnixContainer() - .UntilHttpRequestIsSucceeded(request => - request - .ForPort((ushort)RabbitMqContainerConfiguration.ApiPort) - .ForPath("/api/overview") - .WithBasicAuthentication( - RabbitMqContainerConfiguration.UserName, - RabbitMqContainerConfiguration.Password - ) - ) - ) .Build(); return builder; @@ -166,4 +157,4 @@ public static class TestContainers public int Port { get; set; } = 2113; public string ImageName { get; set; } = "eventstore/eventstore:latest"; } -} \ No newline at end of file +} diff --git a/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/CompleteRegisterPassengerTests.cs b/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/CompleteRegisterPassengerTests.cs index b0a4cbc..bfd5a79 100644 --- a/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/CompleteRegisterPassengerTests.cs +++ b/src/Services/Passenger/tests/IntegrationTest/Passenger/Features/CompleteRegisterPassengerTests.cs @@ -2,39 +2,154 @@ using BuildingBlocks.Contracts.EventBus.Messages; using BuildingBlocks.TestBase; using FluentAssertions; using Integration.Test.Fakes; +using Microsoft.Extensions.Logging; using Passenger.Data; using Xunit; +using Xunit.Abstractions; namespace Integration.Test.Passenger.Features; public class CompleteRegisterPassengerTests : PassengerIntegrationTestBase { - public CompleteRegisterPassengerTests( - TestFixture integrationTestFactory) : base(integrationTestFactory) + TestFixture integrationTestFactory, + ITestOutputHelper outputHelper + ) + : base(integrationTestFactory) { + Fixture.Logger = Fixture.CreateLogger(outputHelper); } [Fact] public async Task should_complete_register_passenger_and_update_to_db() { // Arrange - var userCreated = new FakeUserCreated().Generate(); + Fixture.Logger?.LogInformation("Starting CompleteRegisterPassenger test at {Time}", DateTime.UtcNow); - await Fixture.Publish(userCreated); - (await Fixture.WaitForPublishing()).Should().Be(true); - (await Fixture.WaitForConsuming()).Should().Be(true); + try + { + // Generate and publish UserCreated event + var userCreated = new FakeUserCreated().Generate(); + Fixture.Logger?.LogInformation( + "Generated UserCreated event with PassportNumber: {PassportNumber}", + userCreated.PassportNumber + ); - var command = new FakeCompleteRegisterPassengerCommand(userCreated.PassportNumber).Generate(); + await Fixture.Publish(userCreated); + Fixture.Logger?.LogInformation("Published UserCreated event"); - // Act - var response = await Fixture.SendAsync(command); + // Wait for publishing with retry logic + var published = await WaitForWithRetry( + async () => await Fixture.WaitForPublishing(), + "publishing", + maxRetries: 3 + ); - // Assert - response.Should().NotBeNull(); - response?.PassengerDto?.Name.Should().Be(userCreated.Name); - response?.PassengerDto?.PassportNumber.Should().Be(command.PassportNumber); - response?.PassengerDto?.PassengerType.ToString().Should().Be(command.PassengerType.ToString()); - response?.PassengerDto?.Age.Should().Be(command.Age); + published.Should().BeTrue("UserCreated event should be published to message broker"); + Fixture.Logger?.LogInformation("UserCreated event published successfully"); + + // Wait for consuming with retry logic + var consumed = await WaitForWithRetry( + async () => await Fixture.WaitForConsuming(), + "consuming", + maxRetries: 5 + ); + + consumed.Should().BeTrue("UserCreated event should be consumed by the passenger service"); + Fixture.Logger?.LogInformation("UserCreated event consumed successfully"); + + // Small delay to ensure event processing is complete + await Task.Delay(1000); + + // Generate and send complete registration command + var command = new FakeCompleteRegisterPassengerCommand(userCreated.PassportNumber).Generate(); + Fixture.Logger?.LogInformation( + "Sending CompleteRegisterPassenger command for PassportNumber: {PassportNumber}", + command.PassportNumber + ); + + // Act + var response = await Fixture.SendAsync(command); + Fixture.Logger?.LogInformation("Received response for CompleteRegisterPassenger command"); + + // Assert with detailed logging + response.Should().NotBeNull("Response should not be null"); + Fixture.Logger?.LogInformation("Response is not null"); + + response?.PassengerDto.Should().NotBeNull("PassengerDto should not be null"); + + response + ?.PassengerDto?.Name.Should() + .Be( + userCreated.Name, + $"Passenger name should be '{userCreated.Name}' but was '{response?.PassengerDto?.Name}'" + ); + + response + ?.PassengerDto?.PassportNumber.Should() + .Be( + command.PassportNumber, + $"Passport number should be '{command.PassportNumber}' but was '{response?.PassengerDto?.PassportNumber}'" + ); + + response + ?.PassengerDto?.PassengerType.ToString() + .Should() + .Be( + command.PassengerType.ToString(), + $"Passenger type should be '{command.PassengerType}' but was '{response?.PassengerDto?.PassengerType}'" + ); + + response + ?.PassengerDto?.Age.Should() + .Be(command.Age, $"Age should be {command.Age} but was {response?.PassengerDto?.Age}"); + + Fixture.Logger?.LogInformation("All assertions passed successfully"); + } + catch (Exception ex) + { + Fixture.Logger?.LogError(ex, "Test failed with exception: {Message}", ex.Message); + throw; + } + finally + { + Fixture.Logger?.LogInformation("Test completed at {Time}", DateTime.UtcNow); + } } -} \ No newline at end of file + + private async Task WaitForWithRetry(Func> waitCondition, string operation, int maxRetries = 3) + { + for (int i = 0; i < maxRetries; i++) + { + Fixture.Logger?.LogInformation( + "Attempt {Attempt}/{MaxRetries} for {Operation}", + i + 1, + maxRetries, + operation + ); + + var result = await waitCondition(); + + if (result) + { + Fixture.Logger?.LogInformation("{Operation} successful on attempt {Attempt}", operation, i + 1); + return true; + } + + if (i < maxRetries - 1) + { + var delaySeconds = (i + 1) * 2; // Exponential backoff: 2, 4, 6 seconds + Fixture.Logger?.LogWarning( + "{Operation} failed on attempt {Attempt}, waiting {Delay}s before retry", + operation, + i + 1, + delaySeconds + ); + await Task.Delay(TimeSpan.FromSeconds(delaySeconds)); + } + } + + Fixture.Logger?.LogError("{Operation} failed after {MaxRetries} attempts", operation, maxRetries); + return false; + } +}