-
-
Notifications
You must be signed in to change notification settings - Fork 130
Expand file tree
/
Copy pathExecutionOrderReproductionTests.cs
More file actions
143 lines (118 loc) · 4.98 KB
/
Copy pathExecutionOrderReproductionTests.cs
File metadata and controls
143 lines (118 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using TUnit.AspNetCore;
namespace TUnit.Example.Asp.Net.TestProject;
/// <summary>
/// Reproduction test for issue #4431 - API starting before test configuration.
///
/// User reports: "the second breakpoint (WebApi startup) gets hit first,
/// then the first one (config.AddInMemoryCollection) that basically overrides the connection string"
///
/// Expected: ConfigureTestConfiguration should run BEFORE the API starts.
/// </summary>
public class ExecutionOrderReproductionTests : WebApplicationTest<WebApplicationFactory, Program>
{
private static readonly List<string> ExecutionLog = new();
private static readonly object Lock = new();
private void Log(string message)
{
lock (Lock)
{
var entry = $"{DateTime.UtcNow:HH:mm:ss.fff} - {message}";
ExecutionLog.Add(entry);
Console.WriteLine(entry);
}
}
protected override void ConfigureTestOptions(WebApplicationTestOptions options)
{
Log("1. ConfigureTestOptions called");
base.ConfigureTestOptions(options);
}
protected override Task SetupAsync()
{
Log("2. SetupAsync called");
return base.SetupAsync();
}
protected override void ConfigureTestConfiguration(IConfigurationBuilder config)
{
Log("5. ConfigureTestConfiguration called - THIS SHOULD BE BEFORE API STARTS");
// This is what the user does - add configuration that should override defaults
config.AddInMemoryCollection(new Dictionary<string, string?>
{
{ "TestConfigApplied", "true" },
{ "Database:ConnectionString", "test-connection-string-from-config" }
});
base.ConfigureTestConfiguration(config);
}
protected override void ConfigureWebHostBuilder(IWebHostBuilder builder)
{
Log("6. ConfigureWebHostBuilder called");
base.ConfigureWebHostBuilder(builder);
}
protected override void ConfigureTestServices(IServiceCollection services)
{
Log("7. ConfigureTestServices called");
// Register a service that logs when the API actually starts
services.AddHostedService<StartupLoggerService>();
base.ConfigureTestServices(services);
}
[Test]
[DisplayName("Issue #4431: Verify API starts AFTER ConfigureTestConfiguration")]
public async Task Api_Should_Start_After_ConfigureTestConfiguration()
{
Log("9. Test method starting - about to call CreateClient()");
// This should trigger the API to start
var client = Factory.CreateClient();
Log("10. CreateClient() returned");
// Verify the configuration was applied
var config = Factory.Services.GetRequiredService<IConfiguration>();
var testConfigApplied = config["TestConfigApplied"];
await Assert.That(testConfigApplied).IsEqualTo("true")
.Because("Test configuration should have been applied before API started");
// Print the execution log. Snapshot under the lock first: ExecutionLog is a
// shared static list and parallel test instances may Add() to it via their
// lifecycle hooks while we enumerate, which would throw "Collection was modified".
List<string> executionLogSnapshot;
lock (Lock)
{
executionLogSnapshot = ExecutionLog.ToList();
}
Console.WriteLine("\n=== FULL EXECUTION LOG ===");
foreach (var entry in executionLogSnapshot)
{
Console.WriteLine(entry);
}
Console.WriteLine("===========================\n");
}
[Test]
[DisplayName("Issue #4431: Verify connection string override works")]
public async Task Connection_String_Should_Be_Overridden()
{
Log("Test: Connection string override test starting");
var client = Factory.CreateClient();
var config = Factory.Services.GetRequiredService<IConfiguration>();
var connectionString = config["Database:ConnectionString"];
// The test's ConfigureTestConfiguration should have overridden any default
await Assert.That(connectionString).IsEqualTo("test-connection-string-from-config")
.Because("Test configuration should override factory defaults");
}
}
/// <summary>
/// A hosted service that logs when the application actually starts.
/// This helps us verify the startup timing.
/// </summary>
public class StartupLoggerService : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss.fff} - 8. API STARTUP - StartupLoggerService.StartAsync called");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss.fff} - API SHUTDOWN - StartupLoggerService.StopAsync called");
return Task.CompletedTask;
}
}