autogen/dotnet/test/Microsoft.AutoGen.Integration.Tests/HelloAppHostIntegrationTests.cs
Ryan Sweet ff7f863e73
Improve e2e integration tests and isolate tests from other things; includes patch to Serializer (#5497)
* integration tests used to use the samples - now they are separate
* patch dictionary problem in serializer
* add Message Registry with dead letter queue that gets checked on new
subs.
2025-02-13 16:43:57 -08:00

230 lines
10 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved.
// HelloAppHostIntegrationTests.cs
using System.Text.Json;
using Xunit.Abstractions;
namespace Microsoft.AutoGen.Integration.Tests;
public class HelloAppHostIntegrationTests(ITestOutputHelper testOutput)
{
private const string AppHostAssemblyName = "XlangTests.AppHost";
private const string DotNetResourceName = "HelloAgentTestsDotNET";
private const string PythonResourceName = "HelloAgentTestsPython";
private const string BackendResourceName = "AgentHost";
[Fact]
public async Task AppHostRunsCleanly()
{
var appHostPath = GetAssemblyPath(AppHostAssemblyName);
var appHost = await DistributedApplicationTestFactory.CreateAsync(appHostPath, testOutput);
await using var app = await appHost.BuildAsync().WaitAsync(TimeSpan.FromSeconds(15));
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(120));
await app.WaitForResourcesAsync().WaitAsync(TimeSpan.FromSeconds(120));
app.EnsureNoErrorsLogged();
await app.StopAsync().WaitAsync(TimeSpan.FromSeconds(15));
}
[Fact]
public async Task Test_Dotnet_Sends_AgentHost_Delivers_and_Python_Receives()
{
//Prepare
var appHostPath = GetAssemblyPath(AppHostAssemblyName);
var appHost = await DistributedApplicationTestFactory.CreateAsync(appHostPath, testOutput);
await using var app = await appHost.BuildAsync().WaitAsync(TimeSpan.FromSeconds(15));
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(120));
await app.WaitForResourcesAsync(new[] { KnownResourceStates.Running }).WaitAsync(TimeSpan.FromSeconds(120));
//Act
var expectedMessage = "INFO:autogen_core:Received a message from host: cloudEvent {";
var containsExpectedMessage = false;
app.EnsureNoErrorsLogged();
containsExpectedMessage = await app.WaitForExpectedMessageInResourceLogs(PythonResourceName, expectedMessage, TimeSpan.FromSeconds(120));
expectedMessage = "Hello World!";
containsExpectedMessage = false;
containsExpectedMessage = await app.WaitForExpectedMessageInResourceLogs(PythonResourceName, expectedMessage, TimeSpan.FromSeconds(120));
await app.StopAsync();
//Assert
Assert.True(containsExpectedMessage);
}
[Fact]
public async Task Test_Python_Sends_AgentHost_Delivers_and_DotNet_Receives()
{
//Prepare
var appHostPath = GetAssemblyPath(AppHostAssemblyName);
var appHost = await DistributedApplicationTestFactory.CreateAsync(appHostPath, testOutput);
await using var app = await appHost.BuildAsync().WaitAsync(TimeSpan.FromSeconds(15));
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(120));
await app.WaitForResourcesAsync(new[] { KnownResourceStates.Running }).WaitAsync(TimeSpan.FromSeconds(120));
//Act
var expectedMessage = "from Python!";
var containsExpectedMessage = false;
app.EnsureNoErrorsLogged();
containsExpectedMessage = await app.WaitForExpectedMessageInResourceLogs(DotNetResourceName, expectedMessage, TimeSpan.FromSeconds(60));
await app.StopAsync();
//Assert
Assert.True(containsExpectedMessage);
}
[Fact]
public async Task Test_Python_Agent_Sends_And_AgentHost_Receives()
{
//Prepare
Environment.SetEnvironmentVariable("XLANG_TEST_NO_DOTNET", "true");
var appHostPath = GetAssemblyPath(AppHostAssemblyName);
var appHost = await DistributedApplicationTestFactory.CreateAsync(appHostPath, testOutput);
await using var app = await appHost.BuildAsync().WaitAsync(TimeSpan.FromSeconds(15));
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(120));
await app.WaitForResourcesAsync(new[] { KnownResourceStates.Running }).WaitAsync(TimeSpan.FromSeconds(120));
//Act
var expectedMessage = "\"source\": \"HelloAgents/python\"";
var containsExpectedMessage = false;
app.EnsureNoErrorsLogged();
containsExpectedMessage = await app.WaitForExpectedMessageInResourceLogs(BackendResourceName, expectedMessage, TimeSpan.FromSeconds(120));
await app.StopAsync();
//Assert
Assert.True(containsExpectedMessage);
}
[Fact]
public async Task Test_Dotnet_Agent_Sends_And_AgentHost_Receives()
{
//Prepare
Environment.SetEnvironmentVariable("XLANG_TEST_NO_PYTHON", "true");
var appHostPath = GetAssemblyPath(AppHostAssemblyName);
var appHost = await DistributedApplicationTestFactory.CreateAsync(appHostPath, testOutput);
await using var app = await appHost.BuildAsync().WaitAsync(TimeSpan.FromSeconds(15));
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(120));
await app.WaitForResourcesAsync(new[] { KnownResourceStates.Running }).WaitAsync(TimeSpan.FromSeconds(120));
//Act
var expectedMessage = "\"source\": \"HelloAgents/dotnet\"";
var containsExpectedMessage = false;
app.EnsureNoErrorsLogged();
containsExpectedMessage = await app.WaitForExpectedMessageInResourceLogs(BackendResourceName, expectedMessage, TimeSpan.FromSeconds(120));
await app.StopAsync();
//Assert
Assert.True(containsExpectedMessage);
}
[Fact]
public async Task Test_Dotnet_Agent_Sends_And_AgentHost_Delivers_Back_To_It()
{
//Prepare
Environment.SetEnvironmentVariable("XLANG_TEST_NO_PYTHON", "true");
var appHostPath = GetAssemblyPath(AppHostAssemblyName);
var appHost = await DistributedApplicationTestFactory.CreateAsync(appHostPath, testOutput);
await using var app = await appHost.BuildAsync().WaitAsync(TimeSpan.FromSeconds(15));
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(120));
await app.WaitForResourcesAsync(new[] { KnownResourceStates.Running }).WaitAsync(TimeSpan.FromSeconds(120));
//Act
var expectedMessage = "Hello World!";
var containsExpectedMessage = false;
app.EnsureNoErrorsLogged();
containsExpectedMessage = await app.WaitForExpectedMessageInResourceLogs(DotNetResourceName, expectedMessage, TimeSpan.FromSeconds(120));
expectedMessage = "HelloAgent said Goodbye";
containsExpectedMessage = false;
containsExpectedMessage = await app.WaitForExpectedMessageInResourceLogs(DotNetResourceName, expectedMessage, TimeSpan.FromSeconds(120));
await app.StopAsync();
//Assert
Assert.True(containsExpectedMessage);
}
[Fact]
public async Task Test_Python_Agent_Sends_And_AgentHost_Delivers_Back_To_It()
{
//Prepare
Environment.SetEnvironmentVariable("XLANG_TEST_NO_DOTNET", "true");
var appHostPath = GetAssemblyPath(AppHostAssemblyName);
var appHost = await DistributedApplicationTestFactory.CreateAsync(appHostPath, testOutput);
await using var app = await appHost.BuildAsync().WaitAsync(TimeSpan.FromSeconds(15));
await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(120));
await app.WaitForResourcesAsync(new[] { KnownResourceStates.Running }).WaitAsync(TimeSpan.FromSeconds(120));
//Act
var expectedMessage = "INFO:autogen_core:Received a message from host: cloudEvent {";
var containsExpectedMessage = false;
app.EnsureNoErrorsLogged();
containsExpectedMessage = await app.WaitForExpectedMessageInResourceLogs(PythonResourceName, expectedMessage, TimeSpan.FromSeconds(120));
await app.StopAsync();
//Assert
Assert.True(containsExpectedMessage);
}
private static string GetAssemblyPath(string assemblyName)
{
var parentDir = Directory.GetParent(AppContext.BaseDirectory)?.FullName;
var grandParentDir = parentDir is not null ? Directory.GetParent(parentDir)?.FullName : null;
var greatGrandParentDir = grandParentDir is not null ? Directory.GetParent(grandParentDir)?.FullName : null
?? AppContext.BaseDirectory;
var options = new EnumerationOptions { RecurseSubdirectories = true, MatchCasing = MatchCasing.CaseInsensitive };
if (greatGrandParentDir is not null)
{
var foundFile = Directory.GetFiles(greatGrandParentDir, $"{assemblyName}.dll", options).FirstOrDefault();
if (foundFile is not null)
{
return foundFile;
}
}
throw new FileNotFoundException($"Could not find {assemblyName}.dll in {grandParentDir ?? "unknown"} directory");
}
}
public class TestEndpoints : IXunitSerializable
{
// Required for deserialization
public TestEndpoints() { }
public TestEndpoints(string appHost, Dictionary<string, List<string>> resourceEndpoints)
{
AppHost = appHost;
ResourceEndpoints = resourceEndpoints;
}
public string? AppHost { get; set; }
public List<ResourceWait>? WaitForResources { get; set; }
public Dictionary<string, List<string>>? ResourceEndpoints { get; set; }
public void Deserialize(IXunitSerializationInfo info)
{
AppHost = info.GetValue<string>(nameof(AppHost));
WaitForResources = JsonSerializer.Deserialize<List<ResourceWait>>(info.GetValue<string>(nameof(WaitForResources)));
ResourceEndpoints = JsonSerializer.Deserialize<Dictionary<string, List<string>>>(info.GetValue<string>(nameof(ResourceEndpoints)));
}
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue(nameof(AppHost), AppHost);
info.AddValue(nameof(WaitForResources), JsonSerializer.Serialize(WaitForResources));
info.AddValue(nameof(ResourceEndpoints), JsonSerializer.Serialize(ResourceEndpoints));
}
public override string? ToString() => $"{AppHost} ({ResourceEndpoints?.Count ?? 0} resources)";
public class ResourceWait(string resourceName, string targetState)
{
public string ResourceName { get; } = resourceName;
public string TargetState { get; } = targetState;
public void Deconstruct(out string resourceName, out string targetState)
{
resourceName = ResourceName;
targetState = TargetState;
}
}
}