Merge pull request #372 from meysamhadeli/fix/fix-ci-field-for-tests

fix: fix ci failed for tests
This commit is contained in:
Meysam Hadeli 2026-02-20 02:11:41 +03:30 committed by GitHub
commit 043c20002c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 66 deletions

View File

@ -42,8 +42,8 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
where TEntryPoint : class
{
private readonly WebApplicationFactory<TEntryPoint> _factory;
private int Timeout => 300; // Second
private ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
private int Timeout => 120; // Second
public ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
private Action<IServiceCollection> TestRegistrationServices { get; set; }
private PostgreSqlContainer PostgresTestcontainer;
private PostgreSqlContainer PostgresPersistTestContainer;
@ -136,10 +136,14 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
{
CancellationTokenSource = new CancellationTokenSource();
await StartTestContainerAsync();
await TestHarness.Start();
}
public async Task DisposeAsync()
{
await TestHarness.Stop();
await StopTestContainerAsync();
await _factory.DisposeAsync();
await CancellationTokenSource.CancelAsync();
@ -201,83 +205,71 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
public async Task Publish<TMessage>(TMessage message, CancellationToken cancellationToken = default)
where TMessage : class, IEvent
{
// Use harness bus to ensure publish happens only after the bus is started.
await TestHarness.Bus.Publish(message, cancellationToken);
}
public async Task<bool> WaitForPublishing<TMessage>(CancellationToken cancellationToken = default)
where TMessage : class, IEvent
{
var result = await WaitUntilConditionMet(async () =>
{
var published = await TestHarness.Published.Any<TMessage>(cancellationToken);
var result = await WaitUntilConditionMet(
async () =>
{
var published = await TestHarness.Published.Any<TMessage>(cancellationToken);
return published;
});
return published;
},
cancellationToken: cancellationToken
);
return result;
}
public async Task<bool> WaitForConsuming<TMessage>(CancellationToken cancellationToken = default)
where TMessage : class, IEvent
{
var result = await WaitUntilConditionMet(async () =>
{
var consumed = await TestHarness.Consumed.Any<TMessage>(cancellationToken);
return consumed;
});
return result;
}
public async Task<bool> ShouldProcessedPersistInternalCommand<TInternalCommand>(
public Task<bool> WaitUntilAsync(
Func<Task<bool>> condition,
TimeSpan? timeout = null,
TimeSpan? pollInterval = null,
CancellationToken cancellationToken = default
)
where TInternalCommand : class, IInternalCommand
{
var result = await WaitUntilConditionMet(async () =>
{
return await ExecuteScopeAsync(async sp =>
var effectiveTimeout = timeout ?? TimeSpan.FromSeconds(Timeout);
var effectivePollInterval = pollInterval ?? TimeSpan.FromMilliseconds(200);
return WaitUntilConditionMet(
conditionToMet: async () =>
{
var persistMessageProcessor = sp.GetService<IPersistMessageProcessor>();
Guard.Against.Null(persistMessageProcessor, nameof(persistMessageProcessor));
var filter = await persistMessageProcessor.GetByFilterAsync(x =>
x.DeliveryType == MessageDeliveryType.Internal && typeof(TInternalCommand).ToString() == x.DataType
);
var res = filter.Any(x => x.MessageStatus == MessageStatus.Processed);
return res;
});
});
return result;
cancellationToken.ThrowIfCancellationRequested();
return await condition();
},
timeoutSecond: (int)Math.Ceiling(effectiveTimeout.TotalSeconds),
pollInterval: effectivePollInterval,
cancellationToken: cancellationToken
);
}
// Ref: https://tech.energyhelpline.com/in-memory-testing-with-masstransit/
private async Task<bool> WaitUntilConditionMet(Func<Task<bool>> conditionToMet, int? timeoutSecond = null)
private async Task<bool> WaitUntilConditionMet(
Func<Task<bool>> conditionToMet,
int? timeoutSecond = null,
TimeSpan? pollInterval = null,
CancellationToken cancellationToken = default
)
{
var time = timeoutSecond ?? Timeout;
var delay = pollInterval ?? TimeSpan.FromMilliseconds(100);
var startTime = DateTime.Now;
var timeoutExpired = false;
var meet = await conditionToMet.Invoke();
while (!meet)
var startTime = DateTime.UtcNow;
while (DateTime.UtcNow - startTime <= TimeSpan.FromSeconds(time))
{
if (timeoutExpired)
{
return false;
}
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(100);
meet = await conditionToMet.Invoke();
timeoutExpired = DateTime.Now - startTime > TimeSpan.FromSeconds(time);
if (await conditionToMet.Invoke())
return true;
await Task.Delay(delay, cancellationToken);
}
return true;
return false;
}
private async Task StartTestContainerAsync()
@ -288,7 +280,6 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
MongoDbTestContainer = TestContainers.MongoTestContainer();
EventStoreDbTestContainer = TestContainers.EventStoreTestContainer();
// Start containers in parallel for speed
await Task.WhenAll(
MongoDbTestContainer.StartAsync(),
PostgresTestcontainer.StartAsync(),
@ -296,9 +287,7 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
EventStoreDbTestContainer.StartAsync()
);
// Start RabbitMQ last and wait extra time
await RabbitMqTestContainer.StartAsync();
await Task.Delay(5000); // Give RabbitMQ extra time to initialize
}
private async Task StopTestContainerAsync()
@ -321,7 +310,7 @@ public class TestFixture<TEntryPoint> : IAsyncLifetime
new("PostgresOptions:ConnectionString:Identity", PostgresTestcontainer.GetConnectionString()),
new("PostgresOptions:ConnectionString:Passenger", PostgresTestcontainer.GetConnectionString()),
new("PersistMessageOptions:ConnectionString", PostgresPersistTestContainer.GetConnectionString()),
new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname),
new("RabbitMqOptions:HostName", "127.0.0.1"),
new("RabbitMqOptions:UserName", TestContainers.RabbitMqContainerConfiguration.UserName),
new("RabbitMqOptions:Password", TestContainers.RabbitMqContainerConfiguration.Password),
new(

View File

@ -10,10 +10,8 @@ namespace Integration.Test.Identity.Features;
public class RegisterNewUserTests : IdentityIntegrationTestBase
{
public RegisterNewUserTests(
TestWriteFixture<Program, IdentityContext> integrationTestFactory) : base(integrationTestFactory)
{
}
public RegisterNewUserTests(TestWriteFixture<Program, IdentityContext> integrationTestFactory)
: base(integrationTestFactory) { }
[Fact]
public async Task should_create_new_user_to_db_and_publish_message_to_broker()
@ -30,4 +28,4 @@ public class RegisterNewUserTests : IdentityIntegrationTestBase
(await Fixture.WaitForPublishing<UserCreated>()).Should().Be(true);
}
}
}

View File

@ -1,4 +1,3 @@
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.TestBase;
using FluentAssertions;
using Integration.Test.Fakes;
@ -21,8 +20,8 @@ public class CompleteRegisterPassengerTests : PassengerIntegrationTestBase
var userCreated = new FakeUserCreated().Generate();
await Fixture.Publish(userCreated);
(await Fixture.WaitForPublishing<UserCreated>()).Should().Be(true);
(await Fixture.WaitForConsuming<UserCreated>()).Should().Be(true);
(await WaitUntilPassengerCreatedAsync(userCreated.PassportNumber)).Should().BeTrue();
var command = new FakeCompleteRegisterPassengerCommand(userCreated.PassportNumber).Generate();
@ -36,4 +35,14 @@ public class CompleteRegisterPassengerTests : PassengerIntegrationTestBase
response?.PassengerDto?.PassengerType.ToString().Should().Be(command.PassengerType.ToString());
response?.PassengerDto?.Age.Should().Be(command.Age);
}
private Task<bool> WaitUntilPassengerCreatedAsync(string passportNumber)
{
return Fixture.WaitUntilAsync(async () =>
{
return await Fixture.ExecuteDbContextAsync(db =>
ValueTask.FromResult(db.Passengers.Any(p => p.PassportNumber.Value == passportNumber))
);
});
}
}