Skip to content

Commit e21146b

Browse files
ANcpLuaclaude
andcommitted
Modernize SSE test hosting to remove WebHostBuilder warnings
- Add CreateSseTestServerAsync helper using modern HostBuilder + UseTestServer - Migrate SseExtensionsFallbackTests to use new helper - Migrate SseExtensionsNet10Tests to use new helper - Preserve legacy CreateSseTestServer for integration tests - Eliminate all obsolete WebHostBuilder/TestServer warnings - All tests pass: 94/94 on net9.0 and net10.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 5352f7f commit e21146b

File tree

3 files changed

+105
-118
lines changed

3 files changed

+105
-118
lines changed

SWEN3.Paperless.RabbitMq.Tests/Helpers/SseTestHelpers.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
namespace SWEN3.Paperless.RabbitMq.Tests.Helpers;
22

3+
/// <summary>
4+
/// Helpers for creating SSE test servers using modern generic host builder.
5+
/// </summary>
36
internal static class SseTestHelpers
47
{
8+
/// <summary>
9+
/// Creates a test server with a provided SSE stream instance (legacy, for integration tests).
10+
/// </summary>
511
public static TestServer CreateSseTestServer<T>(ISseStream<T> sseStream,
612
Action<IEndpointRouteBuilder> configureEndpoints) where T : class
713
{
@@ -23,4 +29,36 @@ public static TestServer CreateSseTestServer<T>(ISseStream<T> sseStream,
2329
var host = hostBuilder.StartAsync().GetAwaiter().GetResult();
2430
return host.GetTestServer();
2531
}
32+
33+
/// <summary>
34+
/// Creates a test server with SSE stream configured, using modern IHost and TestServer.
35+
/// </summary>
36+
/// <typeparam name="T">The event type to stream</typeparam>
37+
/// <param name="configureServices">Optional additional service configuration</param>
38+
/// <param name="configureEndpoints">Endpoint configuration (e.g., MapSse calls)</param>
39+
/// <returns>A tuple of (IHost, TestServer) for use in tests</returns>
40+
public static async Task<(IHost Host, TestServer Server)> CreateSseTestServerAsync<T>(
41+
Action<IServiceCollection>? configureServices,
42+
Action<IEndpointRouteBuilder> configureEndpoints) where T : class
43+
{
44+
var hostBuilder = new HostBuilder()
45+
.ConfigureWebHost(web =>
46+
{
47+
web.UseTestServer();
48+
web.ConfigureServices(services =>
49+
{
50+
services.AddRouting();
51+
services.AddSseStream<T>();
52+
configureServices?.Invoke(services);
53+
});
54+
web.Configure(app =>
55+
{
56+
app.UseRouting();
57+
app.UseEndpoints(configureEndpoints);
58+
});
59+
});
60+
61+
var host = await hostBuilder.StartAsync();
62+
return (host, host.GetTestServer());
63+
}
2664
}

SWEN3.Paperless.RabbitMq.Tests/Unit/SseExtensionsFallbackTests.cs

Lines changed: 33 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,21 @@ public class SseExtensionsFallbackTests
88
public async Task MapSse_Fallback_ShouldWriteCorrectSseFormat()
99
{
1010
// Arrange
11-
var hostBuilder = new WebHostBuilder()
12-
.ConfigureServices(services =>
13-
{
14-
services.AddRouting();
15-
services.AddSseStream<Messages.SseTestEvent>();
16-
})
17-
.Configure(app =>
18-
{
19-
app.UseRouting();
20-
app.UseEndpoints(endpoints =>
21-
{
22-
endpoints.MapSse<Messages.SseTestEvent>("/sse",
23-
e => new { id = e.Id, msg = e.Message },
24-
_ => "test-event");
25-
});
26-
});
11+
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
12+
cts.CancelAfter(TimeSpan.FromSeconds(30));
13+
14+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<Messages.SseTestEvent>(
15+
configureServices: null,
16+
configureEndpoints: e => e.MapSse<Messages.SseTestEvent>("/sse",
17+
evt => new { id = evt.Id, msg = evt.Message },
18+
_ => "test-event"));
2719

28-
using var server = new TestServer(hostBuilder);
20+
using var _ = host;
2921
var client = server.CreateClient();
3022
client.Timeout = Timeout.InfiniteTimeSpan;
31-
var sseStream = server.Host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
23+
var sseStream = host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
3224

3325
// Act
34-
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
35-
cts.CancelAfter(TimeSpan.FromSeconds(30));
36-
3726
var responseTask = client.GetAsync("/sse", HttpCompletionOption.ResponseHeadersRead, cts.Token);
3827

3928
// Publish repeatedly until the subscriber is definitely connected
@@ -72,39 +61,29 @@ public async Task MapSse_Fallback_ShouldWriteCorrectSseFormat()
7261
{
7362
await cts.CancelAsync();
7463
await publisherTask;
64+
await host.StopAsync();
7565
}
7666
}
7767

7868
[Fact]
7969
public async Task MapSse_Fallback_ShouldSetCorrectHeaders()
8070
{
8171
// Arrange
82-
var hostBuilder = new WebHostBuilder()
83-
.ConfigureServices(services =>
84-
{
85-
services.AddRouting();
86-
services.AddSseStream<Messages.SseTestEvent>();
87-
})
88-
.Configure(app =>
89-
{
90-
app.UseRouting();
91-
app.UseEndpoints(endpoints =>
92-
{
93-
endpoints.MapSse<Messages.SseTestEvent>("/sse",
94-
e => new { id = e.Id, msg = e.Message },
95-
_ => "test-event");
96-
});
97-
});
72+
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
73+
cts.CancelAfter(TimeSpan.FromSeconds(30));
9874

99-
using var server = new TestServer(hostBuilder);
75+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<Messages.SseTestEvent>(
76+
configureServices: null,
77+
configureEndpoints: e => e.MapSse<Messages.SseTestEvent>("/sse",
78+
evt => new { id = evt.Id, msg = evt.Message },
79+
_ => "test-event"));
80+
81+
using var _ = host;
10082
var client = server.CreateClient();
10183
client.Timeout = Timeout.InfiniteTimeSpan;
102-
var sseStream = server.Host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
84+
var sseStream = host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
10385

10486
// Act
105-
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
106-
cts.CancelAfter(TimeSpan.FromSeconds(30));
107-
10887
var responseTask = client.GetAsync("/sse", HttpCompletionOption.ResponseHeadersRead, cts.Token);
10988

11089
// Publish events so the connection doesn't hang
@@ -138,39 +117,29 @@ public async Task MapSse_Fallback_ShouldSetCorrectHeaders()
138117
{
139118
await cts.CancelAsync();
140119
await publisherTask;
120+
await host.StopAsync();
141121
}
142122
}
143123

144124
[Fact]
145125
public async Task MapSse_Fallback_ShouldStreamMultipleEvents()
146126
{
147127
// Arrange
148-
var hostBuilder = new WebHostBuilder()
149-
.ConfigureServices(services =>
150-
{
151-
services.AddRouting();
152-
services.AddSseStream<Messages.SseTestEvent>();
153-
})
154-
.Configure(app =>
155-
{
156-
app.UseRouting();
157-
app.UseEndpoints(endpoints =>
158-
{
159-
endpoints.MapSse<Messages.SseTestEvent>("/sse",
160-
e => new { id = e.Id, msg = e.Message },
161-
_ => "test-event");
162-
});
163-
});
128+
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
129+
cts.CancelAfter(TimeSpan.FromSeconds(30));
130+
131+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<Messages.SseTestEvent>(
132+
configureServices: null,
133+
configureEndpoints: e => e.MapSse<Messages.SseTestEvent>("/sse",
134+
evt => new { id = evt.Id, msg = evt.Message },
135+
_ => "test-event"));
164136

165-
using var server = new TestServer(hostBuilder);
137+
using var _ = host;
166138
var client = server.CreateClient();
167139
client.Timeout = Timeout.InfiniteTimeSpan;
168-
var sseStream = server.Host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
140+
var sseStream = host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
169141

170142
// Act
171-
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
172-
cts.CancelAfter(TimeSpan.FromSeconds(30));
173-
174143
var responseTask = client.GetAsync("/sse", HttpCompletionOption.ResponseHeadersRead, cts.Token);
175144

176145
var events = new[]
@@ -239,6 +208,7 @@ public async Task MapSse_Fallback_ShouldStreamMultipleEvents()
239208
{
240209
await cts.CancelAsync();
241210
await publisherTask;
211+
await host.StopAsync();
242212
}
243213
}
244214
#endif

SWEN3.Paperless.RabbitMq.Tests/Unit/SseExtensionsNet10Tests.cs

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,21 @@ public class SseExtensionsNet10Tests
88
public async Task MapSse_Net10_ShouldStreamEventsUsingNativeApi()
99
{
1010
// Arrange
11-
var hostBuilder = new WebHostBuilder()
12-
.ConfigureServices(services =>
13-
{
14-
services.AddRouting();
15-
services.AddSseStream<Messages.SseTestEvent>();
16-
})
17-
.Configure(app =>
18-
{
19-
app.UseRouting();
20-
app.UseEndpoints(endpoints =>
21-
{
22-
endpoints.MapSse<Messages.SseTestEvent>("/sse",
23-
e => new { id = e.Id, msg = e.Message },
24-
_ => "test-event");
25-
});
26-
});
11+
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
12+
cts.CancelAfter(TimeSpan.FromSeconds(30));
13+
14+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<Messages.SseTestEvent>(
15+
configureServices: null,
16+
configureEndpoints: e => e.MapSse<Messages.SseTestEvent>("/sse",
17+
evt => new { id = evt.Id, msg = evt.Message },
18+
_ => "test-event"));
2719

28-
using var server = new TestServer(hostBuilder);
20+
using var _ = host;
2921
var client = server.CreateClient();
3022
client.Timeout = Timeout.InfiniteTimeSpan;
31-
var sseStream = server.Host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
23+
var sseStream = host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
3224

3325
// Act
34-
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
35-
cts.CancelAfter(TimeSpan.FromSeconds(30));
36-
3726
var responseTask = client.GetAsync("/sse", HttpCompletionOption.ResponseHeadersRead, cts.Token);
3827

3928
// Publish repeatedly until subscriber connects
@@ -74,39 +63,29 @@ public async Task MapSse_Net10_ShouldStreamEventsUsingNativeApi()
7463
{
7564
await cts.CancelAsync();
7665
await publisherTask;
66+
await host.StopAsync();
7767
}
7868
}
7969

8070
[Fact]
8171
public async Task MapSse_Net10_ShouldStreamMultipleEvents()
8272
{
8373
// Arrange
84-
var hostBuilder = new WebHostBuilder()
85-
.ConfigureServices(services =>
86-
{
87-
services.AddRouting();
88-
services.AddSseStream<Messages.SseTestEvent>();
89-
})
90-
.Configure(app =>
91-
{
92-
app.UseRouting();
93-
app.UseEndpoints(endpoints =>
94-
{
95-
endpoints.MapSse<Messages.SseTestEvent>("/sse",
96-
e => new { id = e.Id, msg = e.Message },
97-
_ => "test-event");
98-
});
99-
});
74+
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
75+
cts.CancelAfter(TimeSpan.FromSeconds(30));
10076

101-
using var server = new TestServer(hostBuilder);
77+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<Messages.SseTestEvent>(
78+
configureServices: null,
79+
configureEndpoints: e => e.MapSse<Messages.SseTestEvent>("/sse",
80+
evt => new { id = evt.Id, msg = evt.Message },
81+
_ => "test-event"));
82+
83+
using var _ = host;
10284
var client = server.CreateClient();
10385
client.Timeout = Timeout.InfiniteTimeSpan;
104-
var sseStream = server.Host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
86+
var sseStream = host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
10587

10688
// Act
107-
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
108-
cts.CancelAfter(TimeSpan.FromSeconds(30));
109-
11089
var responseTask = client.GetAsync("/sse", HttpCompletionOption.ResponseHeadersRead, cts.Token);
11190

11291
var events = new[]
@@ -175,28 +154,27 @@ public async Task MapSse_Net10_ShouldStreamMultipleEvents()
175154
{
176155
await cts.CancelAsync();
177156
await publisherTask;
157+
await host.StopAsync();
178158
}
179159
}
180160

181161
[Fact]
182162
public async Task MapSse_Net10_CancelsAfterFirstEvent_StopsIterator()
183163
{
184-
var host = new WebHostBuilder()
185-
.ConfigureServices(s => { s.AddRouting(); s.AddSseStream<Messages.SseTestEvent>(); })
186-
.Configure(app =>
187-
{
188-
app.UseRouting();
189-
app.UseEndpoints(e =>
190-
e.MapSse<Messages.SseTestEvent>("/sse",
191-
m => new { m.Id, m.Message },
192-
_ => "evt"));
193-
});
194-
using var server = new TestServer(host);
195-
var stream = server.Host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
164+
// Arrange
196165
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
197166
cts.CancelAfter(TimeSpan.FromSeconds(30));
167+
168+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<Messages.SseTestEvent>(
169+
configureServices: null,
170+
configureEndpoints: e => e.MapSse<Messages.SseTestEvent>("/sse",
171+
m => new { m.Id, m.Message },
172+
_ => "evt"));
173+
174+
using var _ = host;
198175
var client = server.CreateClient();
199176
client.Timeout = Timeout.InfiniteTimeSpan;
177+
var sseStream = host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
200178

201179
// Publish events continuously in background
202180
var publishTask = Task.Run(async () =>
@@ -206,7 +184,7 @@ public async Task MapSse_Net10_CancelsAfterFirstEvent_StopsIterator()
206184
while (!cts.Token.IsCancellationRequested)
207185
{
208186
await Task.Delay(50, cts.Token);
209-
stream.Publish(new Messages.SseTestEvent { Id = 1, Message = "Hi" });
187+
sseStream.Publish(new Messages.SseTestEvent { Id = 1, Message = "Hi" });
210188
}
211189
}
212190
catch (OperationCanceledException)
@@ -231,6 +209,7 @@ public async Task MapSse_Net10_CancelsAfterFirstEvent_StopsIterator()
231209
{
232210
await cts.CancelAsync();
233211
await publishTask;
212+
await host.StopAsync();
234213
}
235214
}
236215
#endif

0 commit comments

Comments
 (0)