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
134 lines
5.4 KiB
C#
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),
|
|
};
|
|
}
|