Server-Sent Events
Alba can test server-sent event endpoints two ways: finite streams through an ordinary scenario, and live streams through a dedicated streaming API. Events are surfaced as SseItem<T> from System.Net.ServerSentEvents.
Finite Streams
If the endpoint writes a bounded number of events and completes, a regular scenario works — the response is fully buffered like any other. Parse the buffered body with ReadAsServerSentEvents():
public async Task read_a_finite_event_stream(IAlbaHost host)
{
// The endpoint writes a bounded number of events and completes,
// so an ordinary scenario buffers the whole response
var result = await host.Scenario(x =>
{
x.Get.Url("/sse/finite");
});
// Parse the buffered body as server-sent events
var events = result.ReadAsServerSentEvents();
// Or deserialize each data payload with the application's
// JSON options
var typed = result.ReadAsServerSentEvents<CounterEvent>();
}The typed overload ReadAsServerSentEvents<T>() deserializes each data: payload as JSON using the application's own JsonSerializerOptions, exactly like ReadAsJson<T>() does. Both reads are repeatable.
Live Streams
Endpoints that stream indefinitely (or that you want to observe mid-stream) can't be buffered. Use StreamServerSentEvents instead of Scenario:
public async Task stream_live_events(IAlbaHost host)
{
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
// Returns as soon as the response headers arrive;
// the response body is never buffered
await using var stream = await host.StreamServerSentEvents(x =>
{
x.Get.Url("/sse/infinite");
}, timeout.Token);
// Events are yielded as the application writes them
await foreach (var item in stream.ReadEvents(timeout.Token))
{
if (item.Data == "done") break;
}
// Disposing the stream aborts the request, cancelling the
// endpoint's HttpContext.RequestAborted token
}The call returns as soon as the application flushes its response headers, and ReadEvents() / ReadEvents<T>() yield each event as the application writes it. The event stream can only be consumed once.
Disposing the SseStreamResult aborts the in-flight request, which cancels the endpoint's HttpContext.RequestAborted token — a well-behaved streaming endpoint will observe that and stop. Any AfterEach/AfterEachAsync actions run at disposal with a null HttpContext.
All BeforeEach/BeforeEachAsync actions and Alba's security extensions (such as JwtSecurityStub, including per-scenario claims via WithClaim) apply to streams exactly as they do to scenarios.
Limitations
- Response assertions (
ContentShouldContain, header assertions, custom assertions) are not supported withStreamServerSentEventsand are rejected up front — assert on the streamed events instead. The expected status code is honored: a mismatch (for example a 401 from a missed auth setup) throws with the actual response body instead of presenting an empty stream. - The response must have the
text/event-streamcontent type. - The
CancellationTokenargument toStreamServerSentEventsis the escape hatch for an endpoint that never starts its response.
