Skip to content

Commit 9317f8a

Browse files
ANcpLuaclaude
andcommitted
refactor: tighten SSE tests, fix xUnit1051, remove redundancy
- Remove stray template files (Program.cs, Components/, Services/, appsettings, wwwroot) - Fix xUnit1051 warnings: use TestContext.Current.CancellationToken throughout - Simplify resource handling: remove explicit StopAsync calls, rely on using disposal - Remove redundant fallback tests (headers-only, mid-stream cancel variants) - Add combined headers+multi-event validation test for better coverage - Reduce test code: 262 lines removed, 109 added (153 net reduction) - Maintain 100% line coverage for SseExtensions Test Results: - .NET 9.0: 94/94 passed - .NET 10.0: 95/95 passed - Overall coverage: 100% lines (321/321), 98.39% branches (61/62) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ee482b0 commit 9317f8a

File tree

6 files changed

+109
-262
lines changed

6 files changed

+109
-262
lines changed

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

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,6 @@ namespace SWEN3.Paperless.RabbitMq.Tests.Helpers;
55
/// </summary>
66
internal static class SseTestHelpers
77
{
8-
/// <summary>
9-
/// Creates a test server with a provided SSE stream instance (legacy, for integration tests).
10-
/// </summary>
11-
public static TestServer CreateSseTestServer<T>(ISseStream<T> sseStream,
12-
Action<IEndpointRouteBuilder> configureEndpoints) where T : class
13-
{
14-
var hostBuilder = new HostBuilder().ConfigureWebHost(webHost =>
15-
{
16-
webHost.UseTestServer();
17-
webHost.ConfigureServices(services =>
18-
{
19-
services.AddSingleton(sseStream);
20-
services.AddRouting();
21-
});
22-
webHost.Configure(app =>
23-
{
24-
app.UseRouting();
25-
app.UseEndpoints(configureEndpoints);
26-
});
27-
});
28-
29-
var host = hostBuilder.StartAsync().GetAwaiter().GetResult();
30-
return host.GetTestServer();
31-
}
32-
338
/// <summary>
349
/// Creates a test server with SSE stream configured, using modern IHost and TestServer.
3510
/// </summary>

SWEN3.Paperless.RabbitMq.Tests/Integration/GenAIEventStreamIntegrationTests.cs

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,80 @@ public class GenAIEventStreamIntegrationTests
77
[InlineData("Failed", "genai-failed")]
88
public async Task MapGenAIEventStream_ShouldEmitCorrectEventType(string status, string expectedEventType)
99
{
10-
var sseStream = new SseStream<GenAIEvent>();
11-
using var server = SseTestHelpers.CreateSseTestServer(sseStream, endpoints => endpoints.MapGenAIEventStream());
10+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<GenAIEvent>(
11+
configureServices: null,
12+
configureEndpoints: endpoints => endpoints.MapGenAIEventStream());
13+
14+
using var _ = host;
15+
var sseStream = host.Services.GetRequiredService<ISseStream<GenAIEvent>>();
1216
var client = server.CreateClient();
17+
var ct = TestContext.Current.CancellationToken;
1318

1419
var readTask = Task.Run(async () =>
1520
{
1621
using var response =
17-
await client.GetAsync("/api/v1/events/genai", HttpCompletionOption.ResponseHeadersRead);
18-
await using var stream = await response.Content.ReadAsStreamAsync();
22+
await client.GetAsync("/api/v1/events/genai", HttpCompletionOption.ResponseHeadersRead, ct);
23+
await using var stream = await response.Content.ReadAsStreamAsync(ct);
1924
using var reader = new StreamReader(stream);
2025

2126
while (true)
2227
{
23-
var line = await reader.ReadLineAsync();
28+
var line = await reader.ReadLineAsync(ct);
2429
if (line == null)
2530
return null;
2631
if (line.StartsWith("event:"))
2732
return line;
2833
}
29-
});
34+
}, ct);
3035

3136
while (sseStream.ClientCount == 0)
32-
await Task.Delay(50, TestContext.Current.CancellationToken);
37+
await Task.Delay(50, ct);
3338

3439
var genAiEvent = status == "Completed"
3540
? new GenAIEvent(Guid.NewGuid(), "Test summary", DateTimeOffset.UtcNow)
3641
: new GenAIEvent(Guid.NewGuid(), string.Empty, DateTimeOffset.UtcNow, "Service error");
3742
sseStream.Publish(genAiEvent);
3843

39-
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
40-
var eventLine = await readTask.WaitAsync(cts.Token);
44+
var eventLine = await readTask;
4145

4246
eventLine.Should().Be($"event: {expectedEventType}");
4347
}
4448

4549
[Fact]
4650
public async Task MapGenAIEventStream_WithMultipleEvents_ShouldStreamInOrder()
4751
{
48-
var sseStream = new SseStream<GenAIEvent>();
49-
using var server = SseTestHelpers.CreateSseTestServer(sseStream, endpoints => endpoints.MapGenAIEventStream());
52+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<GenAIEvent>(
53+
configureServices: null,
54+
configureEndpoints: endpoints => endpoints.MapGenAIEventStream());
5055

56+
using var _ = host;
57+
var sseStream = host.Services.GetRequiredService<ISseStream<GenAIEvent>>();
5158
var client = server.CreateClient();
52-
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
53-
var token = cts.Token;
54-
var readTask = Task.Run(() => ReadEventsAsync(client, 3, token), token);
59+
var ct = TestContext.Current.CancellationToken;
60+
61+
var readTask = Task.Run(() => ReadEventsAsync(client, 3, ct), ct);
5562

5663
// Wait for client to connect with timeout
5764
var waitStart = DateTime.UtcNow;
5865
while (sseStream.ClientCount == 0)
5966
{
6067
if (DateTime.UtcNow - waitStart > TimeSpan.FromSeconds(10))
6168
throw new TimeoutException("Client did not connect within timeout");
62-
await Task.Delay(50, TestContext.Current.CancellationToken);
69+
await Task.Delay(50, ct);
6370
}
6471

6572
// Give the HTTP connection a moment to stabilize
66-
await Task.Delay(100, TestContext.Current.CancellationToken);
73+
await Task.Delay(100, ct);
6774

6875
sseStream.Publish(new GenAIEvent(Guid.NewGuid(), "Summary 1", DateTimeOffset.UtcNow));
69-
await Task.Delay(50, TestContext.Current.CancellationToken);
76+
await Task.Delay(50, ct);
7077

7178
sseStream.Publish(new GenAIEvent(Guid.NewGuid(), "Summary 2", DateTimeOffset.UtcNow));
72-
await Task.Delay(50, TestContext.Current.CancellationToken);
79+
await Task.Delay(50, ct);
7380

7481
sseStream.Publish(new GenAIEvent(Guid.NewGuid(), string.Empty, DateTimeOffset.UtcNow, "Error occurred"));
7582

76-
var events = await readTask.WaitAsync(TimeSpan.FromSeconds(30), TestContext.Current.CancellationToken);
83+
var events = await readTask;
7784

7885
events[0].Event.Should().Be("event: genai-completed");
7986
events[0].Data.Should().Contain("Summary 1");

SWEN3.Paperless.RabbitMq.Tests/Integration/OcrEventStreamIntegrationTests.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,49 @@ public sealed class OcrEventStreamIntegrationTests
88
[InlineData("Processing", "ocr-failed")]
99
public async Task MapOcrEventStream_ShouldEmitCorrectEventType(string status, string expectedEventType)
1010
{
11-
var sseStream = new SseStream<OcrEvent>();
12-
var server = SseTestHelpers.CreateSseTestServer(sseStream, endpoints => endpoints.MapOcrEventStream());
11+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<OcrEvent>(
12+
configureServices: null,
13+
configureEndpoints: endpoints => endpoints.MapOcrEventStream());
14+
15+
using var _ = host;
16+
var sseStream = host.Services.GetRequiredService<ISseStream<OcrEvent>>();
17+
var ct = TestContext.Current.CancellationToken;
1318

14-
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
1519
var readTask = Task.Run(async () =>
1620
{
1721
using var client = server.CreateClient();
1822
using var response = await client.GetAsync("/api/v1/ocr-results", HttpCompletionOption.ResponseHeadersRead,
19-
cts.Token);
20-
await using var stream = await response.Content.ReadAsStreamAsync(cts.Token);
23+
ct);
24+
await using var stream = await response.Content.ReadAsStreamAsync(ct);
2125
using var reader = new StreamReader(stream);
2226

2327
while (true)
2428
{
25-
var line = await reader.ReadLineAsync(cts.Token);
29+
var line = await reader.ReadLineAsync(ct);
2630
if (line == null)
2731
return null;
2832
if (line.StartsWith("event:"))
2933
return line;
3034
}
31-
}, cts.Token);
35+
}, ct);
3236

3337
// Wait for client to connect with timeout
3438
var waitStart = DateTime.UtcNow;
3539
while (sseStream.ClientCount == 0)
3640
{
3741
if (DateTime.UtcNow - waitStart > TimeSpan.FromSeconds(10))
3842
throw new TimeoutException("Client did not connect within timeout");
39-
await Task.Delay(50, TestContext.Current.CancellationToken);
43+
await Task.Delay(50, ct);
4044
}
4145

4246
// Give the HTTP connection a moment to stabilize
43-
await Task.Delay(100, TestContext.Current.CancellationToken);
47+
await Task.Delay(100, ct);
4448

4549
var ocrEvent = new OcrEvent(Guid.NewGuid(), status, status is "Completed" ? "Text" : null,
4650
DateTimeOffset.UtcNow);
4751
sseStream.Publish(ocrEvent);
4852

49-
var eventLine = await readTask.WaitAsync(cts.Token);
53+
var eventLine = await readTask;
5054

5155
eventLine.Should().Be($"event: {expectedEventType}");
5256
}

SWEN3.Paperless.RabbitMq.Tests/Integration/SseExtensionsTests.cs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,21 @@ public class SseExtensionsTests
77
[Fact]
88
public async Task MapSse_WithPublishedEvent_ShouldStreamToClient()
99
{
10-
var sseStream = new SseStream<Messages.SseTestEvent>();
11-
using var server = SseTestHelpers.CreateSseTestServer(sseStream,
12-
endpoints => endpoints.MapSse<Messages.SseTestEvent>(
10+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<Messages.SseTestEvent>(
11+
configureServices: null,
12+
configureEndpoints: endpoints => endpoints.MapSse<Messages.SseTestEvent>(
1313
TestEndpoint, evt => new { evt.Id, evt.Message }, _ => "test-event"));
1414

15+
using var _ = host;
16+
var sseStream = host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
17+
1518
var clientId = Guid.NewGuid();
1619
var reader = sseStream.Subscribe(clientId);
1720

18-
var readTask = Task.Run(async () =>
19-
{
20-
await foreach (var item in reader.ReadAllAsync())
21-
return item;
22-
23-
throw new InvalidOperationException("No event received");
24-
});
25-
2621
var testEvent = new Messages.SseTestEvent { Id = 42, Message = "Hello SSE" };
2722
sseStream.Publish(testEvent);
2823

29-
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
30-
var result = await readTask.WaitAsync(cts.Token);
24+
var result = await reader.ReadAsync(TestContext.Current.CancellationToken);
3125

3226
result.Id.Should().Be(42);
3327
result.Message.Should().Be("Hello SSE");
@@ -38,11 +32,14 @@ public async Task MapSse_WithPublishedEvent_ShouldStreamToClient()
3832
[Fact]
3933
public async Task MapSse_MultipleClients_ShouldReceiveSameEvent()
4034
{
41-
var sseStream = new SseStream<Messages.SseTestEvent>();
42-
using var server = SseTestHelpers.CreateSseTestServer(sseStream,
43-
endpoints => endpoints.MapSse<Messages.SseTestEvent>(
35+
var (host, server) = await SseTestHelpers.CreateSseTestServerAsync<Messages.SseTestEvent>(
36+
configureServices: null,
37+
configureEndpoints: endpoints => endpoints.MapSse<Messages.SseTestEvent>(
4438
TestEndpoint, evt => new { evt.Id, evt.Message }, _ => "test-event"));
4539

40+
using var _ = host;
41+
var sseStream = host.Services.GetRequiredService<ISseStream<Messages.SseTestEvent>>();
42+
4643
var clientId1 = Guid.NewGuid();
4744
var clientId2 = Guid.NewGuid();
4845
var reader1 = sseStream.Subscribe(clientId1);
@@ -61,7 +58,6 @@ public async Task MapSse_MultipleClients_ShouldReceiveSameEvent()
6158
result2.Should().BeEquivalentTo(testEvent);
6259

6360
sseStream.Unsubscribe(clientId1);
64-
6561
sseStream.Unsubscribe(clientId2);
6662
}
6763
}

0 commit comments

Comments
 (0)