Files
wehub-resource-sync db620d33df
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:25 +08:00

154 lines
5.5 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
#if !NET10_0_OR_GREATER
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using AGUI.Abstractions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests;
/// <summary>
/// Unit tests for the <see cref="AGUIServerSentEventsResult"/> class.
/// </summary>
public sealed class AGUIServerSentEventsResultTests
{
[Fact]
public async Task ExecuteAsync_SetsCorrectResponseHeaders_ContentTypeAndCacheControlAsync()
{
// Arrange
List<BaseEvent> events = [];
ILogger<AGUIServerSentEventsResult> logger = NullLogger<AGUIServerSentEventsResult>.Instance;
AGUIServerSentEventsResult result = new(events.ToAsyncEnumerableAsync(), logger);
DefaultHttpContext httpContext = new();
httpContext.Response.Body = new MemoryStream();
// Act
await result.ExecuteAsync(httpContext);
// Assert
Assert.Equal("text/event-stream", httpContext.Response.ContentType);
Assert.Equal("no-cache,no-store", httpContext.Response.Headers.CacheControl.ToString());
Assert.Equal("no-cache", httpContext.Response.Headers.Pragma.ToString());
}
[Fact]
public async Task ExecuteAsync_SerializesEventsInSSEFormat_WithDataPrefixAndNewlinesAsync()
{
// Arrange
List<BaseEvent> events =
[
new RunStartedEvent { ThreadId = "thread1", RunId = "run1" },
new RunFinishedEvent { ThreadId = "thread1", RunId = "run1" }
];
ILogger<AGUIServerSentEventsResult> logger = NullLogger<AGUIServerSentEventsResult>.Instance;
AGUIServerSentEventsResult result = new(events.ToAsyncEnumerableAsync(), logger);
DefaultHttpContext httpContext = new();
MemoryStream responseStream = new();
httpContext.Response.Body = responseStream;
// Act
await result.ExecuteAsync(httpContext);
// Assert
string responseContent = Encoding.UTF8.GetString(responseStream.ToArray());
Assert.Contains("data: ", responseContent);
Assert.Contains("\n\n", responseContent);
string[] eventStrings = responseContent.Split("\n\n", StringSplitOptions.RemoveEmptyEntries);
Assert.Equal(2, eventStrings.Length);
}
[Fact]
public async Task ExecuteAsync_FlushesResponse_AfterEachEventAsync()
{
// Arrange
List<BaseEvent> events =
[
new RunStartedEvent { ThreadId = "thread1", RunId = "run1" },
new TextMessageContentEvent { MessageId = "msg1", Delta = "Hello" },
new RunFinishedEvent { ThreadId = "thread1", RunId = "run1" }
];
ILogger<AGUIServerSentEventsResult> logger = NullLogger<AGUIServerSentEventsResult>.Instance;
AGUIServerSentEventsResult result = new(events.ToAsyncEnumerableAsync(), logger);
DefaultHttpContext httpContext = new();
MemoryStream responseStream = new();
httpContext.Response.Body = responseStream;
// Act
await result.ExecuteAsync(httpContext);
// Assert
string responseContent = Encoding.UTF8.GetString(responseStream.ToArray());
string[] eventStrings = responseContent.Split("\n\n", StringSplitOptions.RemoveEmptyEntries);
Assert.Equal(3, eventStrings.Length);
}
[Fact]
public async Task ExecuteAsync_WithEmptyEventStream_CompletesSuccessfullyAsync()
{
// Arrange
List<BaseEvent> events = [];
ILogger<AGUIServerSentEventsResult> logger = NullLogger<AGUIServerSentEventsResult>.Instance;
AGUIServerSentEventsResult result = new(events.ToAsyncEnumerableAsync(), logger);
DefaultHttpContext httpContext = new();
httpContext.Response.Body = new MemoryStream();
// Act
await result.ExecuteAsync(httpContext);
}
[Fact]
public async Task ExecuteAsync_RespectsCancellationToken_WhenCancelledAsync()
{
// Arrange
using CancellationTokenSource cts = new();
List<BaseEvent> events =
[
new RunStartedEvent { ThreadId = "thread1", RunId = "run1" },
new TextMessageContentEvent { MessageId = "msg1", Delta = "Hello" }
];
async IAsyncEnumerable<BaseEvent> GetEventsWithCancellationAsync()
{
foreach (BaseEvent evt in events)
{
yield return evt;
await Task.Delay(10);
}
}
ILogger<AGUIServerSentEventsResult> logger = NullLogger<AGUIServerSentEventsResult>.Instance;
AGUIServerSentEventsResult result = new(GetEventsWithCancellationAsync(), logger);
DefaultHttpContext httpContext = new();
httpContext.Response.Body = new MemoryStream();
httpContext.RequestAborted = cts.Token;
// Act
cts.Cancel();
// Assert
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => result.ExecuteAsync(httpContext));
}
[Fact]
public async Task ExecuteAsync_WithNullHttpContext_ThrowsArgumentNullExceptionAsync()
{
// Arrange
List<BaseEvent> events = [];
ILogger<AGUIServerSentEventsResult> logger = NullLogger<AGUIServerSentEventsResult>.Instance;
AGUIServerSentEventsResult result = new(events.ToAsyncEnumerableAsync(), logger);
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => result.ExecuteAsync(null!));
}
}
#endif