mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-05-01 02:38:51 +08:00
Merge pull request #370 from meysamhadeli/fix/fix-ci-failed
fix: fix ci failed
This commit is contained in:
commit
786fbb121f
@ -42,7 +42,7 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
where TEntryPoint : class
|
where TEntryPoint : class
|
||||||
{
|
{
|
||||||
private readonly WebApplicationFactory<TEntryPoint> _factory;
|
private readonly WebApplicationFactory<TEntryPoint> _factory;
|
||||||
private int Timeout => 120; // Second
|
private int Timeout => 300; // Second
|
||||||
private ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
|
private ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
|
||||||
private Action<IServiceCollection> TestRegistrationServices { get; set; }
|
private Action<IServiceCollection> TestRegistrationServices { get; set; }
|
||||||
private PostgreSqlContainer PostgresTestcontainer;
|
private PostgreSqlContainer PostgresTestcontainer;
|
||||||
@ -140,6 +140,9 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
if (ServiceProvider.GetService<ITestHarness>() is { } harness)
|
if (ServiceProvider.GetService<ITestHarness>() is { } harness)
|
||||||
{
|
{
|
||||||
await harness.Start();
|
await harness.Start();
|
||||||
|
|
||||||
|
// Add a small delay to ensure harness is ready
|
||||||
|
await Task.Delay(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,11 +301,24 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
|
|||||||
MongoDbTestContainer = TestContainers.MongoTestContainer();
|
MongoDbTestContainer = TestContainers.MongoTestContainer();
|
||||||
EventStoreDbTestContainer = TestContainers.EventStoreTestContainer();
|
EventStoreDbTestContainer = TestContainers.EventStoreTestContainer();
|
||||||
|
|
||||||
await MongoDbTestContainer.StartAsync();
|
// Start containers in parallel for speed
|
||||||
await PostgresTestcontainer.StartAsync();
|
await Task.WhenAll(
|
||||||
await PostgresPersistTestContainer.StartAsync();
|
MongoDbTestContainer.StartAsync(),
|
||||||
|
PostgresTestcontainer.StartAsync(),
|
||||||
|
PostgresPersistTestContainer.StartAsync(),
|
||||||
|
EventStoreDbTestContainer.StartAsync()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Start RabbitMQ last and wait extra time
|
||||||
await RabbitMqTestContainer.StartAsync();
|
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()
|
private async Task StopTestContainerAsync()
|
||||||
|
|||||||
@ -87,25 +87,16 @@ public static class TestContainers
|
|||||||
|
|
||||||
public static RabbitMqContainer RabbitMqTestContainer()
|
public static RabbitMqContainer RabbitMqTestContainer()
|
||||||
{
|
{
|
||||||
var builder = new RabbitMqBuilder()
|
var baseBuilder = new RabbitMqBuilder()
|
||||||
.WithUsername(RabbitMqContainerConfiguration.UserName)
|
.WithUsername(RabbitMqContainerConfiguration.UserName)
|
||||||
.WithPassword(RabbitMqContainerConfiguration.Password)
|
.WithPassword(RabbitMqContainerConfiguration.Password)
|
||||||
|
.WithLabel("Key", "Value");
|
||||||
|
|
||||||
|
var builder = baseBuilder
|
||||||
.WithImage(RabbitMqContainerConfiguration.ImageName)
|
.WithImage(RabbitMqContainerConfiguration.ImageName)
|
||||||
.WithName(RabbitMqContainerConfiguration.Name)
|
.WithName(RabbitMqContainerConfiguration.Name)
|
||||||
.WithPortBinding(RabbitMqContainerConfiguration.ApiPort, true)
|
.WithPortBinding(RabbitMqContainerConfiguration.ApiPort, true)
|
||||||
.WithPortBinding(RabbitMqContainerConfiguration.Port, true)
|
.WithPortBinding(RabbitMqContainerConfiguration.Port, true)
|
||||||
.WithWaitStrategy(
|
|
||||||
Wait.ForUnixContainer()
|
|
||||||
.UntilHttpRequestIsSucceeded(request =>
|
|
||||||
request
|
|
||||||
.ForPort((ushort)RabbitMqContainerConfiguration.ApiPort)
|
|
||||||
.ForPath("/api/overview")
|
|
||||||
.WithBasicAuthentication(
|
|
||||||
RabbitMqContainerConfiguration.UserName,
|
|
||||||
RabbitMqContainerConfiguration.Password
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
|
|||||||
@ -2,39 +2,154 @@ using BuildingBlocks.Contracts.EventBus.Messages;
|
|||||||
using BuildingBlocks.TestBase;
|
using BuildingBlocks.TestBase;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Integration.Test.Fakes;
|
using Integration.Test.Fakes;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Passenger.Data;
|
using Passenger.Data;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace Integration.Test.Passenger.Features;
|
namespace Integration.Test.Passenger.Features;
|
||||||
|
|
||||||
public class CompleteRegisterPassengerTests : PassengerIntegrationTestBase
|
public class CompleteRegisterPassengerTests : PassengerIntegrationTestBase
|
||||||
{
|
{
|
||||||
|
|
||||||
public CompleteRegisterPassengerTests(
|
public CompleteRegisterPassengerTests(
|
||||||
TestFixture<Program, PassengerDbContext, PassengerReadDbContext> integrationTestFactory) : base(integrationTestFactory)
|
TestFixture<Program, PassengerDbContext, PassengerReadDbContext> integrationTestFactory,
|
||||||
|
ITestOutputHelper outputHelper
|
||||||
|
)
|
||||||
|
: base(integrationTestFactory)
|
||||||
{
|
{
|
||||||
|
Fixture.Logger = Fixture.CreateLogger(outputHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task should_complete_register_passenger_and_update_to_db()
|
public async Task should_complete_register_passenger_and_update_to_db()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
Fixture.Logger?.LogInformation("Starting CompleteRegisterPassenger test at {Time}", DateTime.UtcNow);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Generate and publish UserCreated event
|
||||||
var userCreated = new FakeUserCreated().Generate();
|
var userCreated = new FakeUserCreated().Generate();
|
||||||
|
Fixture.Logger?.LogInformation(
|
||||||
|
"Generated UserCreated event with PassportNumber: {PassportNumber}",
|
||||||
|
userCreated.PassportNumber
|
||||||
|
);
|
||||||
|
|
||||||
await Fixture.Publish(userCreated);
|
await Fixture.Publish(userCreated);
|
||||||
(await Fixture.WaitForPublishing<UserCreated>()).Should().Be(true);
|
Fixture.Logger?.LogInformation("Published UserCreated event");
|
||||||
(await Fixture.WaitForConsuming<UserCreated>()).Should().Be(true);
|
|
||||||
|
|
||||||
|
// Wait for publishing with retry logic
|
||||||
|
var published = await WaitForWithRetry(
|
||||||
|
async () => await Fixture.WaitForPublishing<UserCreated>(),
|
||||||
|
"publishing",
|
||||||
|
maxRetries: 3
|
||||||
|
);
|
||||||
|
|
||||||
|
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<UserCreated>(),
|
||||||
|
"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();
|
var command = new FakeCompleteRegisterPassengerCommand(userCreated.PassportNumber).Generate();
|
||||||
|
Fixture.Logger?.LogInformation(
|
||||||
|
"Sending CompleteRegisterPassenger command for PassportNumber: {PassportNumber}",
|
||||||
|
command.PassportNumber
|
||||||
|
);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var response = await Fixture.SendAsync(command);
|
var response = await Fixture.SendAsync(command);
|
||||||
|
Fixture.Logger?.LogInformation("Received response for CompleteRegisterPassenger command");
|
||||||
|
|
||||||
// Assert
|
// Assert with detailed logging
|
||||||
response.Should().NotBeNull();
|
response.Should().NotBeNull("Response should not be null");
|
||||||
response?.PassengerDto?.Name.Should().Be(userCreated.Name);
|
Fixture.Logger?.LogInformation("Response is not null");
|
||||||
response?.PassengerDto?.PassportNumber.Should().Be(command.PassportNumber);
|
|
||||||
response?.PassengerDto?.PassengerType.ToString().Should().Be(command.PassengerType.ToString());
|
response?.PassengerDto.Should().NotBeNull("PassengerDto should not be null");
|
||||||
response?.PassengerDto?.Age.Should().Be(command.Age);
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> WaitForWithRetry(Func<Task<bool>> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user