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

134 lines
5.4 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Options;
using Moq;
namespace Microsoft.Agents.AI.Foundry.Hosting.UnitTests;
[Collection(FoundryProjectEndpointEnvFixture.Name)]
public class FoundryToolboxHealthCheckTests
{
[Fact]
public async Task CheckHealthAsync_PendingStatus_ReturnsConfiguredFailureAsync()
{
// Arrange: a fresh FoundryToolboxService whose StartAsync has never run reports
// Pending. The health check must surface that as the registration's failure
// status so the platform waits before sending traffic.
var service = CreateServiceWithoutStarting();
var check = new FoundryToolboxHealthCheck(service);
var context = NewContext(failureStatus: HealthStatus.Unhealthy);
// Act
var result = await check.CheckHealthAsync(context);
// Assert
Assert.Equal(HealthStatus.Unhealthy, result.Status);
Assert.Contains("startup has not completed", result.Description, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task CheckHealthAsync_NoEndpointStatus_ReturnsHealthyAsync()
{
// Arrange: no FOUNDRY_PROJECT_ENDPOINT / AZURE_AI_PROJECT_ENDPOINT is normal local-dev.
// The container must still pass readiness because the rest of the agent is functional.
var savedFoundry = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT");
var savedAzure = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT");
Environment.SetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT", null);
Environment.SetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT", null);
try
{
var service = CreateServiceWithoutStarting(toolbox: "any");
await service.StartAsync(CancellationToken.None);
var check = new FoundryToolboxHealthCheck(service);
var context = NewContext(failureStatus: HealthStatus.Unhealthy);
// Act
var result = await check.CheckHealthAsync(context);
// Assert
Assert.Equal(HealthStatus.Healthy, result.Status);
Assert.Equal(FoundryToolboxStartupStatus.NoEndpoint, service.StartupStatus);
}
finally
{
Environment.SetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT", savedFoundry);
Environment.SetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT", savedAzure);
}
}
[Fact]
public async Task CheckHealthAsync_DegradedStatus_StaysRoutableForDeferredToolboxAsync()
{
// Arrange: a pre-registered toolbox at an unreachable endpoint forces StartAsync to defer
// the toolbox (non-consent failure). The container must stay routable so the toolbox can be
// retried per-request, where the platform injects the per-user isolation key on egress.
var options = new FoundryToolboxOptions
{
EndpointOverride = "http://127.0.0.1:1/unreachable",
};
options.ToolboxNames.Add("broken-toolbox");
var service = new FoundryToolboxService(Options.Create(options), Mock.Of<TokenCredential>());
await service.StartAsync(CancellationToken.None);
var check = new FoundryToolboxHealthCheck(service);
var context = NewContext(failureStatus: HealthStatus.Unhealthy);
// Act
var result = await check.CheckHealthAsync(context);
// Assert: deferred toolbox keeps the container Healthy (routable), not bricked.
Assert.Equal(FoundryToolboxStartupStatus.Degraded, service.StartupStatus);
Assert.Equal(HealthStatus.Healthy, result.Status);
Assert.Equal("broken-toolbox", Assert.Single(service.DeferredToolboxNames));
Assert.Contains("deferred", result.Description, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task CheckHealthAsync_HealthyStatus_ReturnsHealthyAsync()
{
// Arrange: an endpoint set but no pre-registered toolboxes is the legitimate
// lazy-only setup. StartAsync reports Healthy and the check must agree.
var options = new FoundryToolboxOptions
{
EndpointOverride = "http://127.0.0.1:1/unused",
};
var service = new FoundryToolboxService(Options.Create(options), Mock.Of<TokenCredential>());
await service.StartAsync(CancellationToken.None);
var check = new FoundryToolboxHealthCheck(service);
var context = NewContext(failureStatus: HealthStatus.Unhealthy);
// Act
var result = await check.CheckHealthAsync(context);
// Assert
Assert.Equal(HealthStatus.Healthy, result.Status);
}
private static FoundryToolboxService CreateServiceWithoutStarting(string? toolbox = null)
{
var options = new FoundryToolboxOptions();
if (toolbox is not null)
{
options.ToolboxNames.Add(toolbox);
}
return new FoundryToolboxService(Options.Create(options), Mock.Of<TokenCredential>());
}
private static HealthCheckContext NewContext(HealthStatus failureStatus) =>
new()
{
Registration = new HealthCheckRegistration(
name: "foundry-toolbox",
instance: Mock.Of<IHealthCheck>(),
failureStatus: failureStatus,
tags: null),
};
}