Skip to content

Commit 02b7b5c

Browse files
committed
Add e2e test to verify config changes are observed
1 parent a852bdb commit 02b7b5c

File tree

2 files changed

+92
-17
lines changed

2 files changed

+92
-17
lines changed

src/DefaultBuilder/test/Microsoft.AspNetCore.FunctionalTests/WebApplicationFunctionalTests.cs

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.IO;
5-
using System.Threading.Tasks;
64
using Microsoft.AspNetCore.Builder;
75
using Microsoft.AspNetCore.Testing;
86
using Microsoft.Extensions.Logging;
9-
using Xunit;
7+
using Microsoft.Extensions.Logging.EventLog;
108

119
namespace Microsoft.AspNetCore.Tests
1210
{
@@ -15,9 +13,12 @@ public class WebApplicationFunctionalTests : LoggedTest
1513
[Fact]
1614
public async Task LoggingConfigurationSectionPassedToLoggerByDefault()
1715
{
16+
var contentRootPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
17+
Directory.CreateDirectory(contentRootPath);
18+
1819
try
1920
{
20-
await File.WriteAllTextAsync("appsettings.json", @"
21+
await File.WriteAllTextAsync(Path.Combine(contentRootPath, "appsettings.json"), @"
2122
{
2223
""Logging"": {
2324
""LogLevel"": {
@@ -26,7 +27,7 @@ await File.WriteAllTextAsync("appsettings.json", @"
2627
}
2728
}");
2829

29-
await using var app = WebApplication.Create();
30+
await using var app = WebApplication.Create(new[] { "--contentRoot", contentRootPath });
3031

3132
var factory = (ILoggerFactory)app.Services.GetService(typeof(ILoggerFactory));
3233
var logger = factory.CreateLogger("Test");
@@ -48,16 +49,19 @@ await File.WriteAllTextAsync("appsettings.json", @"
4849
}
4950
finally
5051
{
51-
File.Delete("appsettings.json");
52+
Directory.Delete(contentRootPath, recursive: true);
5253
}
5354
}
5455

5556
[Fact]
5657
public async Task EnvironmentSpecificLoggingConfigurationSectionPassedToLoggerByDefault()
5758
{
59+
var contentRootPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
60+
Directory.CreateDirectory(contentRootPath);
61+
5862
try
5963
{
60-
await File.WriteAllTextAsync("appsettings.Development.json", @"
64+
await File.WriteAllTextAsync(Path.Combine(contentRootPath, "appsettings.Development.json"), @"
6165
{
6266
""Logging"": {
6367
""LogLevel"": {
@@ -66,13 +70,7 @@ await File.WriteAllTextAsync("appsettings.Development.json", @"
6670
}
6771
}");
6872

69-
var app = WebApplication.Create(new[] { "--environment", "Development" });
70-
71-
// TODO: Make this work! I think it should be possible if we register our Configuration
72-
// as a ChainedConfigurationSource instead of copying over the bootstrapped IConfigurationSources.
73-
//var builder = WebApplication.CreateBuilder();
74-
//builder.Environment.EnvironmentName = "Development";
75-
//await using var app = builder.Build();
73+
var app = WebApplication.Create(new[] { "--environment", "Development", "--contentRoot", contentRootPath });
7674

7775
var factory = (ILoggerFactory)app.Services.GetService(typeof(ILoggerFactory));
7876
var logger = factory.CreateLogger("Test");
@@ -94,9 +92,86 @@ await File.WriteAllTextAsync("appsettings.Development.json", @"
9492
}
9593
finally
9694
{
97-
File.Delete("appsettings.json");
95+
Directory.Delete(contentRootPath, recursive: true);
9896
}
9997
}
10098

99+
[Fact]
100+
public async Task LoggingConfigurationReactsToRuntimeChanges()
101+
{
102+
var contentRootPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
103+
Directory.CreateDirectory(contentRootPath);
104+
105+
try
106+
{
107+
await File.WriteAllTextAsync(Path.Combine(contentRootPath, "appsettings.json"), @"
108+
{
109+
""Logging"": {
110+
""LogLevel"": {
111+
""Default"": ""Error""
112+
}
113+
}
114+
}");
115+
116+
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
117+
{
118+
ContentRootPath = contentRootPath,
119+
});
120+
121+
// Disable the EventLogLoggerProvider because HostBuilder.ConfigureDefaults() configures it to log everything warning and higher which overrides non-provider-specific config.
122+
// https://github.com/dotnet/runtime/blob/8048fe613933a1cd91e3fad6d571c74f726143ef/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs#L238
123+
builder.Logging.AddFilter<EventLogLoggerProvider>(_ => false);
124+
125+
await using var app = builder.Build();
126+
127+
var factory = (ILoggerFactory)app.Services.GetService(typeof(ILoggerFactory));
128+
var logger = factory.CreateLogger("Test");
129+
130+
Assert.False(logger.IsEnabled(LogLevel.Warning));
131+
132+
logger.Log(LogLevel.Warning, 0, "Message", null, (s, e) =>
133+
{
134+
Assert.True(false);
135+
return string.Empty;
136+
});
137+
138+
// Lower log level from Error to Warning and wait for logging to react to the config changes.
139+
var configChangedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
140+
using var registration = app.Configuration.GetReloadToken().RegisterChangeCallback(
141+
tcs => ((TaskCompletionSource)tcs).SetResult(), configChangedTcs);
142+
143+
await File.WriteAllTextAsync(Path.Combine(contentRootPath, "appsettings.json"), @"
144+
{
145+
""Logging"": {
146+
""LogLevel"": {
147+
""Default"": ""Warning""
148+
}
149+
}
150+
}");
151+
152+
// Wait for a config change notification because logging will not react until this is fired. Even then, it won't react immediately
153+
// so we loop until success or a timeout.
154+
await configChangedTcs.Task.DefaultTimeout();
155+
156+
var timeoutTicks = Environment.TickCount64 + Testing.TaskExtensions.DefaultTimeoutDuration;
157+
var logWritten = false;
158+
159+
while (!logWritten && Environment.TickCount < timeoutTicks)
160+
{
161+
logger.Log(LogLevel.Warning, 0, "Message", null, (s, e) =>
162+
{
163+
logWritten = true;
164+
return string.Empty;
165+
});
166+
}
167+
168+
Assert.True(logWritten);
169+
Assert.True(logger.IsEnabled(LogLevel.Warning));
170+
}
171+
finally
172+
{
173+
Directory.Delete(contentRootPath, recursive: true);
174+
}
175+
}
101176
}
102177
}

src/Shared/TaskExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ static class TaskExtensions
2525
#if DEBUG
2626
// Shorter duration when running tests with debug.
2727
// Less time waiting for hang unit tests to fail in aspnetcore solution.
28-
private const int DefaultTimeoutDuration = 5 * 1000;
28+
public const int DefaultTimeoutDuration = 5 * 1000;
2929
#else
30-
private const int DefaultTimeoutDuration = 30 * 1000;
30+
public const int DefaultTimeoutDuration = 30 * 1000;
3131
#endif
3232

3333
public static TimeSpan DefaultTimeoutTimeSpan { get; } = TimeSpan.FromMilliseconds(DefaultTimeoutDuration);

0 commit comments

Comments
 (0)