// 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) { [Theory, Trait("type", "integration")] [MemberData(nameof(AppHostAssemblies))] public async Task AppHostRunsCleanly(string appHostPath) { 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)); } [Theory, Trait("type", "integration")] [MemberData(nameof(TestEndpoints))] public async Task AppHostLogsHelloAgentE2E(TestEndpoints testEndpoints) { var appHostName = testEndpoints.AppHost!; var appHostPath = $"{appHostName}.dll"; 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)); if (testEndpoints.WaitForResources?.Count > 0) { // Wait until each resource transitions to the required state var timeout = TimeSpan.FromMinutes(5); foreach (var (ResourceName, TargetState) in testEndpoints.WaitForResources) { await app.WaitForResource(ResourceName, TargetState).WaitAsync(timeout); } } //sleep to make sure the app is running await Task.Delay(20000); app.EnsureNoErrorsLogged(); app.EnsureLogContains("HelloAgent said Goodbye"); app.EnsureLogContains("Wild Hello from Python!"); await app.StopAsync().WaitAsync(TimeSpan.FromSeconds(15)); } public static TheoryData AppHostAssemblies() { var appHostAssemblies = GetSamplesAppHostAssemblyPaths(); var theoryData = new TheoryData(); return new(appHostAssemblies.Select(p => Path.GetRelativePath(AppContext.BaseDirectory, p))); } public static TheoryData TestEndpoints() => new([ new TestEndpoints("Hello.AppHost", new() { { "backend", ["/"] } }), ]); private static IEnumerable GetSamplesAppHostAssemblyPaths() { // All the AppHost projects are referenced by this project so we can find them by looking for all their assemblies in the base directory return Directory.GetFiles(AppContext.BaseDirectory, "*.AppHost.dll") .Where(fileName => !fileName.EndsWith("Aspire.Hosting.AppHost.dll", StringComparison.OrdinalIgnoreCase)); } } public class TestEndpoints : IXunitSerializable { // Required for deserialization public TestEndpoints() { } public TestEndpoints(string appHost, Dictionary> resourceEndpoints) { AppHost = appHost; ResourceEndpoints = resourceEndpoints; } public string? AppHost { get; set; } public List? WaitForResources { get; set; } public Dictionary>? ResourceEndpoints { get; set; } public void Deserialize(IXunitSerializationInfo info) { AppHost = info.GetValue(nameof(AppHost)); WaitForResources = JsonSerializer.Deserialize>(info.GetValue(nameof(WaitForResources))); ResourceEndpoints = JsonSerializer.Deserialize>>(info.GetValue(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; } } }