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

221 lines
9.3 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Moq;
namespace Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests;
/// <summary>
/// Unit tests for the <see cref="AGUIEndpointRouteBuilderExtensions"/> class.
/// </summary>
public sealed class AGUIEndpointRouteBuilderExtensionsTests
{
[Fact]
public void MapAGUIServer_MapsEndpoint_AtSpecifiedPattern()
{
// Arrange
Mock<IEndpointRouteBuilder> endpointsMock = new();
Mock<IServiceProvider> serviceProviderMock = new();
serviceProviderMock.As<IKeyedServiceProvider>();
endpointsMock.Setup(e => e.ServiceProvider).Returns(serviceProviderMock.Object);
endpointsMock.Setup(e => e.DataSources).Returns([]);
const string Pattern = "/api/agent";
AIAgent agent = new TestAgent();
// Act
IEndpointConventionBuilder? result = endpointsMock.Object.MapAGUIServer(Pattern, agent);
// Assert
Assert.NotNull(result);
}
[Fact]
public void MapAGUIServer_WithAgentName_ResolvesKeyedAgentFromDI()
{
// Arrange
Mock<IEndpointRouteBuilder> endpointsMock = new();
Mock<IServiceProvider> serviceProviderMock = new();
AIAgent agent = new NamedTestAgent();
serviceProviderMock.As<IKeyedServiceProvider>()
.Setup(sp => sp.GetRequiredKeyedService(typeof(AIAgent), "test-agent"))
.Returns(agent);
endpointsMock.Setup(e => e.ServiceProvider).Returns(serviceProviderMock.Object);
endpointsMock.Setup(e => e.DataSources).Returns([]);
// Act
IEndpointConventionBuilder? result = endpointsMock.Object.MapAGUIServer("test-agent", "/api/agent");
// Assert
Assert.NotNull(result);
serviceProviderMock.As<IKeyedServiceProvider>()
.Verify(sp => sp.GetRequiredKeyedService(typeof(AIAgent), "test-agent"), Times.Once);
}
[Fact]
public void MapAGUIServer_WithHostedAgentBuilder_ResolvesAgentByBuilderName()
{
// Arrange
Mock<IEndpointRouteBuilder> endpointsMock = new();
Mock<IServiceProvider> serviceProviderMock = new();
Mock<IHostedAgentBuilder> agentBuilderMock = new();
AIAgent agent = new NamedTestAgent();
agentBuilderMock.Setup(b => b.Name).Returns("test-agent");
serviceProviderMock.As<IKeyedServiceProvider>()
.Setup(sp => sp.GetRequiredKeyedService(typeof(AIAgent), "test-agent"))
.Returns(agent);
endpointsMock.Setup(e => e.ServiceProvider).Returns(serviceProviderMock.Object);
endpointsMock.Setup(e => e.DataSources).Returns([]);
// Act
IEndpointConventionBuilder? result = endpointsMock.Object.MapAGUIServer(agentBuilderMock.Object, "/api/agent");
// Assert
Assert.NotNull(result);
serviceProviderMock.As<IKeyedServiceProvider>()
.Verify(sp => sp.GetRequiredKeyedService(typeof(AIAgent), "test-agent"), Times.Once);
}
[Fact]
public void MapAGUIServer_WithAgent_ResolvesSessionStoreFromDI()
{
// Arrange
Mock<IEndpointRouteBuilder> endpointsMock = new();
Mock<IServiceProvider> serviceProviderMock = new();
Mock<AgentSessionStore> sessionStoreMock = new();
AIAgent agent = new NamedTestAgent();
serviceProviderMock.As<IKeyedServiceProvider>()
.Setup(sp => sp.GetKeyedService(typeof(AgentSessionStore), "test-agent"))
.Returns(sessionStoreMock.Object);
endpointsMock.Setup(e => e.ServiceProvider).Returns(serviceProviderMock.Object);
endpointsMock.Setup(e => e.DataSources).Returns([]);
// Act
IEndpointConventionBuilder? result = endpointsMock.Object.MapAGUIServer("/api/agent", agent);
// Assert
Assert.NotNull(result);
serviceProviderMock.As<IKeyedServiceProvider>()
.Verify(sp => sp.GetKeyedService(typeof(AgentSessionStore), "test-agent"), Times.Once);
}
[Fact]
public void MapAGUIServer_WithoutSessionStore_FallsBackToNoopStore()
{
// Arrange
Mock<IEndpointRouteBuilder> endpointsMock = new();
Mock<IServiceProvider> serviceProviderMock = new();
AIAgent agent = new TestAgent();
// No session store registered - IKeyedServiceProvider returns null by default
serviceProviderMock.As<IKeyedServiceProvider>();
endpointsMock.Setup(e => e.ServiceProvider).Returns(serviceProviderMock.Object);
endpointsMock.Setup(e => e.DataSources).Returns([]);
// Act - should not throw (falls back to NoopAgentSessionStore)
IEndpointConventionBuilder? result = endpointsMock.Object.MapAGUIServer("/api/agent", agent);
// Assert
Assert.NotNull(result);
}
[Fact]
public void MapAGUIServer_WithNullEndpoints_ThrowsArgumentNullException()
{
// Arrange
AIAgent agent = new TestAgent();
// Act & Assert
Assert.Throws<ArgumentNullException>(() =>
AGUIEndpointRouteBuilderExtensions.MapAGUIServer(null!, "/api/agent", agent));
}
[Fact]
public void MapAGUIServer_WithNullAgent_ThrowsArgumentNullException()
{
// Arrange
Mock<IEndpointRouteBuilder> endpointsMock = new();
Mock<IServiceProvider> serviceProviderMock = new();
serviceProviderMock.As<IKeyedServiceProvider>();
endpointsMock.Setup(e => e.ServiceProvider).Returns(serviceProviderMock.Object);
// Act & Assert
Assert.Throws<ArgumentNullException>(() =>
endpointsMock.Object.MapAGUIServer("/api/agent", (AIAgent)null!));
}
[Fact]
public void MapAGUIServer_WithNullAgentName_ThrowsArgumentNullException()
{
// Arrange
Mock<IEndpointRouteBuilder> endpointsMock = new();
Mock<IServiceProvider> serviceProviderMock = new();
serviceProviderMock.As<IKeyedServiceProvider>();
endpointsMock.Setup(e => e.ServiceProvider).Returns(serviceProviderMock.Object);
// Act & Assert
Assert.Throws<ArgumentNullException>(() =>
endpointsMock.Object.MapAGUIServer((string)null!, "/api/agent"));
}
[Fact]
public void MapAGUIServer_WithNullAgentBuilder_ThrowsArgumentNullException()
{
// Arrange
Mock<IEndpointRouteBuilder> endpointsMock = new();
Mock<IServiceProvider> serviceProviderMock = new();
endpointsMock.Setup(e => e.ServiceProvider).Returns(serviceProviderMock.Object);
// Act & Assert
Assert.Throws<ArgumentNullException>(() =>
endpointsMock.Object.MapAGUIServer((IHostedAgentBuilder)null!, "/api/agent"));
}
private sealed class TestAgent : AIAgent
{
protected override Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
protected override IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException();
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
protected override ValueTask<JsonElement> SerializeSessionCoreAsync(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
}
private sealed class NamedTestAgent : AIAgent
{
protected override string? IdCore => "named-test-agent";
public override string? Name => "test-agent";
protected override Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
protected override IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException();
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
protected override ValueTask<JsonElement> SerializeSessionCoreAsync(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
}
}