Skip to content

Commit a98ccc2

Browse files
authored
mysql test timeouts (#8536)
* mysql test timeouts * 'dispose' * cleaner
1 parent d0140d2 commit a98ccc2

File tree

4 files changed

+46
-29
lines changed

4 files changed

+46
-29
lines changed

tests/Aspire.Hosting.MySql.Tests/Aspire.Hosting.MySql.Tests.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@
1212
<ProjectReference Include="..\..\src\Components\Aspire.Pomelo.EntityFrameworkCore.MySql\Aspire.Pomelo.EntityFrameworkCore.MySql.csproj" />
1313
</ItemGroup>
1414

15+
<ItemGroup>
16+
<Compile Include="$(TestsSharedDir)AsyncTestHelpers.cs" Link="shared/AsyncTestHelpers.cs" />
17+
</ItemGroup>
18+
1519
</Project>

tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Aspire.Hosting.ApplicationModel;
77
using Aspire.Hosting.Tests.Utils;
88
using Aspire.Hosting.Utils;
9+
using Microsoft.AspNetCore.InternalTesting;
910
using Microsoft.EntityFrameworkCore;
1011
using Microsoft.EntityFrameworkCore.Infrastructure;
1112
using Microsoft.EntityFrameworkCore.Storage;
@@ -27,7 +28,7 @@ public class MySqlFunctionalTests(ITestOutputHelper testOutputHelper)
2728
[RequiresDocker]
2829
public async Task VerifyWaitForOnMySqlBlocksDependentResources()
2930
{
30-
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
31+
using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan);
3132
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(testOutputHelper);
3233

3334
var healthCheckTcs = new TaskCompletionSource<HealthCheckResult>();
@@ -58,14 +59,14 @@ public async Task VerifyWaitForOnMySqlBlocksDependentResources()
5859

5960
await pendingStart;
6061

61-
await app.StopAsync();
62+
await app.StopAsync(cts.Token);
6263
}
6364

6465
[Fact]
6566
[RequiresDocker]
6667
public async Task VerifyMySqlResource()
6768
{
68-
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
69+
using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
6970
var pipeline = new ResiliencePipelineBuilder()
7071
.AddRetry(new() { MaxRetryAttempts = 10, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(2), ShouldHandle = new PredicateBuilder().Handle<MySqlException>() })
7172
.Build();
@@ -79,9 +80,9 @@ public async Task VerifyMySqlResource()
7980

8081
using var app = builder.Build();
8182

82-
await app.StartAsync();
83+
await app.StartAsync(cts.Token);
8384

84-
await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
85+
await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
8586

8687
var hb = Host.CreateApplicationBuilder();
8788

@@ -94,7 +95,7 @@ public async Task VerifyMySqlResource()
9495

9596
using var host = hb.Build();
9697

97-
await host.StartAsync();
98+
await host.StartAsync(cts.Token);
9899
await pipeline.ExecuteAsync(async token =>
99100
{
100101
using var connection = host.Services.GetRequiredService<MySqlConnection>();
@@ -119,7 +120,7 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
119120
string? volumeName = null;
120121
string? bindMountPath = null;
121122

122-
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
123+
using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
123124
var pipeline = new ResiliencePipelineBuilder()
124125
.AddRetry(new() { MaxRetryAttempts = 10, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(2) })
125126
.Build();
@@ -151,9 +152,9 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
151152

152153
using (var app = builder1.Build())
153154
{
154-
await app.StartAsync();
155+
await app.StartAsync(cts.Token);
155156

156-
await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
157+
await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
157158

158159
try
159160
{
@@ -168,7 +169,7 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
168169

169170
using (var host = hb.Build())
170171
{
171-
await host.StartAsync();
172+
await host.StartAsync(cts.Token);
172173

173174
// Wait until the database is available
174175
await pipeline.ExecuteAsync(async token =>
@@ -199,7 +200,7 @@ await pipeline.ExecuteAsync(async token =>
199200
finally
200201
{
201202
// Stops the container, or the Volume/mount would still be in use
202-
await app.StopAsync();
203+
await app.StopAsync(cts.Token);
203204
}
204205
}
205206

@@ -220,24 +221,24 @@ await pipeline.ExecuteAsync(async token =>
220221

221222
using (var app = builder2.Build())
222223
{
223-
await app.StartAsync();
224+
await app.StartAsync(cts.Token);
224225

225-
await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
226+
await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
226227

227228
try
228229
{
229230
var hb = Host.CreateApplicationBuilder();
230231

231232
hb.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
232233
{
233-
[$"ConnectionStrings:{db2.Resource.Name}"] = await db2.Resource.ConnectionStringExpression.GetValueAsync(default)
234+
[$"ConnectionStrings:{db2.Resource.Name}"] = await db2.Resource.ConnectionStringExpression.GetValueAsync(cts.Token)
234235
});
235236

236237
hb.AddMySqlDataSource(db2.Resource.Name);
237238

238239
using (var host = hb.Build())
239240
{
240-
await host.StartAsync();
241+
await host.StartAsync(cts.Token);
241242

242243
// Wait until the database is available
243244
await pipeline.ExecuteAsync(async token =>
@@ -264,7 +265,7 @@ await pipeline.ExecuteAsync(async token =>
264265
finally
265266
{
266267
// Stops the container, or the Volume/mount would still be in use
267-
await app.StopAsync();
268+
await app.StopAsync(cts.Token);
268269
}
269270
}
270271

@@ -296,7 +297,7 @@ public async Task VerifyWithInitBindMount()
296297
{
297298
// Creates a script that should be executed when the container is initialized.
298299

299-
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
300+
using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
300301
var pipeline = new ResiliencePipelineBuilder()
301302
.AddRetry(new() { MaxRetryAttempts = 10, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(2), ShouldHandle = new PredicateBuilder().Handle<MySqlException>() })
302303
.Build();
@@ -323,22 +324,22 @@ public async Task VerifyWithInitBindMount()
323324

324325
using var app = builder.Build();
325326

326-
await app.StartAsync();
327+
await app.StartAsync(cts.Token);
327328

328-
await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
329+
await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
329330

330331
var hb = Host.CreateApplicationBuilder();
331332

332333
hb.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
333334
{
334-
[$"ConnectionStrings:{db.Resource.Name}"] = await db.Resource.ConnectionStringExpression.GetValueAsync(default)
335+
[$"ConnectionStrings:{db.Resource.Name}"] = await db.Resource.ConnectionStringExpression.GetValueAsync(cts.Token)
335336
});
336337

337338
hb.AddMySqlDataSource(db.Resource.Name);
338339

339340
using var host = hb.Build();
340341

341-
await host.StartAsync();
342+
await host.StartAsync(cts.Token);
342343

343344
// Wait until the database is available
344345
await pipeline.ExecuteAsync(async token =>
@@ -380,7 +381,7 @@ await pipeline.ExecuteAsync(async token =>
380381
[QuarantinedTest("https://github.com/dotnet/aspire/issues/7340")]
381382
public async Task VerifyEfMySql()
382383
{
383-
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
384+
using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
384385
var pipeline = new ResiliencePipelineBuilder()
385386
.AddRetry(new() { MaxRetryAttempts = 10, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(1), ShouldHandle = new PredicateBuilder().Handle<MySqlException>() })
386387
.Build();
@@ -394,22 +395,22 @@ public async Task VerifyEfMySql()
394395

395396
using var app = builder.Build();
396397

397-
await app.StartAsync();
398+
await app.StartAsync(cts.Token);
398399

399-
await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
400+
await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
400401

401402
var hb = Host.CreateApplicationBuilder();
402403

403404
hb.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
404405
{
405-
[$"ConnectionStrings:{db.Resource.Name}"] = await db.Resource.ConnectionStringExpression.GetValueAsync(default)
406+
[$"ConnectionStrings:{db.Resource.Name}"] = await db.Resource.ConnectionStringExpression.GetValueAsync(cts.Token)
406407
});
407408

408409
hb.AddMySqlDbContext<TestDbContext>(db.Resource.Name);
409410

410411
using var host = hb.Build();
411412

412-
await host.StartAsync();
413+
await host.StartAsync(cts.Token);
413414

414415
// Wait until the database is available
415416
await pipeline.ExecuteAsync(async token =>
@@ -453,7 +454,7 @@ public async Task MySql_WithPersistentLifetime_ReusesContainers(bool useMultiple
453454
// it generates and mounts a config.user.inc.php file instead of using environment variables.
454455
// For this reason we need to test with and without multiple instances to cover both scenarios.
455456

456-
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10));
457+
using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
457458

458459
// Use the same path for both runs
459460
var aspireStorePath = Directory.CreateTempSubdirectory().FullName;
@@ -511,7 +512,7 @@ public async Task MySql_WithPersistentLifetime_ReusesContainers(bool useMultiple
511512
resourceEvent = await rns.WaitForResourceHealthyAsync("resource-phpmyadmin", cts.Token);
512513
var phpMyAdminId = GetContainerId(resourceEvent);
513514

514-
await app.StopAsync(cts.Token).WaitAsync(TimeSpan.FromMinutes(1), cts.Token);
515+
await app.StopAsync(cts.Token).WaitAsync(cts.Token);
515516

516517
return [mySqlId, mySqlId2, phpMyAdminId];
517518
}

tests/Aspire.Hosting.Tests/Utils/LoggerNotificationExtensions.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public static Task WaitForTextAsync(this DistributedApplication app, string logT
2828
return WaitForTextAsync(app, (log) => log.Contains(logText), resourceName, cancellationToken);
2929
}
3030

31-
public static async Task WaitForHealthyAsync<T>(this DistributedApplication app, IResourceBuilder<T> resource, CancellationToken cancellationToken = default) where T: IResource
31+
public static async Task WaitForHealthyAsync<T>(this DistributedApplication app, IResourceBuilder<T> resource, CancellationToken cancellationToken = default) where T : IResource
3232
{
3333
ArgumentNullException.ThrowIfNull(app);
3434
ArgumentNullException.ThrowIfNull(resource);
@@ -52,6 +52,16 @@ public static Task WaitForTextAsync(this DistributedApplication app, IEnumerable
5252
return app.WaitForTextAsync((log) => logTexts.Any(x => log.Contains(x)), resourceName, cancellationToken);
5353
}
5454

55+
/// <summary>
56+
/// Waits for the specified text to be logged.
57+
/// </summary>
58+
/// <param name="app">The <see cref="DistributedApplication" /> instance to watch.</param>
59+
/// <param name="predicate">A predicate checking the text to wait for.</param>
60+
/// <param name="cancellationToken">The cancellation token.</param>
61+
/// <returns></returns>
62+
public static Task WaitForTextAsync(this DistributedApplication app, Predicate<string> predicate, CancellationToken cancellationToken = default)
63+
=> app.WaitForTextAsync(predicate, resourceName: null, cancellationToken);
64+
5565
/// <summary>
5666
/// Waits for the specified text to be logged.
5767
/// </summary>

tests/Shared/AsyncTestHelpers.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ internal static class TestConstants
2020
// Less time waiting for hang unit tests to fail in aspnetcore solution.
2121
public static readonly int DefaultTimeoutDuration = 5 * 1000 * (PlatformDetection.IsRunningOnCI ? 6 : 1); // 5 sec, 30 sec in CI
2222
public static readonly int LongTimeoutDuration = 60 * 1000 * (PlatformDetection.IsRunningOnCI ? 3 : 1); // 60 sec, 180 sec in CI
23+
public static readonly int ExtraLongTimeoutDuration = 60 * 1000 * 3 * (PlatformDetection.IsRunningOnCI ? 2 : 1); // 180 sec, 360 sec in CI -- useful when a docker image might need pulling
2324
public static readonly int DefaultOrchestratorTestTimeout = 15 * 1000 * (PlatformDetection.IsRunningOnCI ? 2 : 1); // 15 sec, 30 sec in CI
2425
public static readonly int DefaultOrchestratorTestLongTimeout = 45 * 1000 * (PlatformDetection.IsRunningOnCI ? 4 : 1); // 45 sec, 180 sec in CI
2526

2627
public static TimeSpan DefaultTimeoutTimeSpan { get; } = TimeSpan.FromMilliseconds(DefaultTimeoutDuration);
2728
public static TimeSpan LongTimeoutTimeSpan { get; } = TimeSpan.FromMilliseconds(LongTimeoutDuration);
29+
public static TimeSpan ExtraLongTimeoutTimeSpan { get; } = TimeSpan.FromMilliseconds(ExtraLongTimeoutDuration);
2830
}
2931

3032
internal static class AsyncTestHelpers

0 commit comments

Comments
 (0)