// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Microsoft.Agents.AI.Hosting.AzureStorage.Blob;
using Microsoft.Extensions.AI;
using Xunit.Abstractions;
namespace Hosting.AgentThreadStore.IntegrationTests;
///
/// Tests for .
///
public sealed class AzureBlobAgentThreadStoreTests(ITestOutputHelper output) : IAsyncLifetime
{
private const string AzuriteConnectionString = "UseDevelopmentStorage=true";
private const string TestContainerName = "agent-threads-test";
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
private BlobServiceClient _blobServiceClient;
private BlobContainerClient _containerClient;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
public async Task InitializeAsync()
{
await AzureStorageEmulatorAvailabilityHelper.SkipIfNotAvailableAsync();
this._blobServiceClient = new BlobServiceClient(AzuriteConnectionString);
this._containerClient = this._blobServiceClient.GetBlobContainerClient(TestContainerName);
// Clean up any existing test container
await this._containerClient.DeleteIfExistsAsync();
}
public async Task DisposeAsync()
{
if (this._containerClient is not null)
{
await this._containerClient.DeleteIfExistsAsync();
}
}
[SkippableFact]
public async Task AIHostAgent_SavesAndRetrievesThread_UsingAzureBlobStoreAsync()
{
AzureBlobAgentThreadStore threadStore = new(this._containerClient!);
var testRunner = TestRunner.Initialize(output, threadStore);
var blobName = GetBlobContainerName(threadStore, testRunner);
var runResult = await testRunner.RunAgentAsync("hello agent");
Assert.Single(runResult.ResponseMessages);
Assert.Equal(2, runResult.ThreadMessages.Count);
await this.AssertBlobHasTextAsync(blobName, runResult.ThreadMessages);
var runResult2 = await testRunner.RunAgentAsync("hello again");
Assert.Single(runResult2.ResponseMessages);
Assert.Equal(4, runResult2.ThreadMessages.Count);
await this.AssertBlobHasTextAsync(blobName, runResult2.ThreadMessages);
}
private Task AssertBlobHasTextAsync(string blobName, IList chatMessages)
{
var texts = chatMessages.SelectMany(x => x.Contents).OfType().Select(x => x.Text).ToArray();
return this.AssertBlobHasTextAsync(blobName, texts);
}
private async Task AssertBlobHasTextAsync(string blobName, params string[] expectedTexts)
{
var blobClient = this._containerClient.GetBlobClient(blobName);
var exists = await blobClient.ExistsAsync();
Assert.True(exists, $"Blob '{blobName}' should exist.");
var downloadResponse = await blobClient.DownloadContentAsync();
var blobJson = downloadResponse.Value.Content.ToString();
output.WriteLine($"Actual blob json: {blobJson}");
foreach (var text in expectedTexts)
{
Assert.Contains(text, blobJson);
}
}
private static string GetBlobContainerName(AzureBlobAgentThreadStore threadStore, TestRunner testRunner)
=> threadStore.GetBlobName(testRunner.HostAgent.Id, testRunner.ConversationId);
}