// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using Azure.Identity; using Microsoft.Agents.Workflows.Declarative.IntegrationTests.Framework; using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; using Shared.IntegrationTests; using Xunit.Abstractions; namespace Microsoft.Agents.Workflows.Declarative.IntegrationTests; /// /// Tests execution of workflow created by . /// [Collection("Global")] public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : WorkflowTest(output) { [Theory] [InlineData("SendActivity.yaml", "SendActivity.json")] [InlineData("InvokeAgent.yaml", "InvokeAgent.json")] public Task ValidateAsync(string workflowFileName, string testcaseFileName) => this.RunWorkflowAsync(workflowFileName, testcaseFileName); private Task RunWorkflowAsync(string workflowFileName, string testcaseFileName) { this.Output.WriteLine($"WORKFLOW: {workflowFileName}"); this.Output.WriteLine($"TESTCASE: {testcaseFileName}"); Testcase testcase = ReadTestcase(testcaseFileName); IConfiguration configuration = InitializeConfig(); string workflowPath = Path.Combine("Workflows", workflowFileName); this.Output.WriteLine($" {testcase.Description}"); return testcase.Setup.Input.Type switch { nameof(ChatMessage) => this.RunWorkflowAsync(testcase, workflowPath, configuration), nameof(String) => this.RunWorkflowAsync(testcase, workflowPath, configuration), _ => throw new NotSupportedException($"Input type '{testcase.Setup.Input.Type}' is not supported."), }; } private async Task RunWorkflowAsync( Testcase testcase, string workflowPath, IConfiguration configuration) where TInput : notnull { this.Output.WriteLine($"INPUT: {testcase.Setup.Input.Value}"); AzureAIConfiguration? foundryConfig = configuration.GetSection("AzureAI").Get(); Assert.NotNull(foundryConfig); IReadOnlyDictionary agentMap = await AgentFixture.GetAgentsAsync(foundryConfig); IConfiguration workflowConfig = new ConfigurationBuilder() .AddInMemoryCollection(agentMap) .Build(); DeclarativeWorkflowOptions workflowOptions = new(new AzureAgentProvider(foundryConfig.Endpoint, new AzureCliCredential())) { Configuration = workflowConfig, LoggerFactory = this.Output }; Workflow workflow = DeclarativeWorkflowBuilder.Build(workflowPath, workflowOptions); WorkflowEvents workflowEvents = await WorkflowHarness.RunAsync(workflow, (TInput)GetInput(testcase)); foreach (DeclarativeActionInvokedEvent actionInvokeEvent in workflowEvents.ActionInvokeEvents) { this.Output.WriteLine($"ACTION: {actionInvokeEvent.ActionId} [{actionInvokeEvent.ActionType}]"); } Assert.Equal(testcase.Validation.ActionCount, workflowEvents.ActionInvokeEvents.Count); Assert.Equal(testcase.Validation.ActionCount, workflowEvents.ActionCompleteEvents.Count); } private static object GetInput(Testcase testcase) where TInput : notnull => testcase.Setup.Input.Type switch { nameof(ChatMessage) => new ChatMessage(ChatRole.User, testcase.Setup.Input.Value), nameof(String) => testcase.Setup.Input.Value, _ => throw new NotSupportedException($"Input type '{testcase.Setup.Input.Type}' is not supported."), }; private static Testcase ReadTestcase(string testcaseFileName) { using Stream testcaseStream = File.Open(Path.Combine("Testcases", testcaseFileName), FileMode.Open); Testcase? testcase = JsonSerializer.Deserialize(testcaseStream, s_jsonSerializerOptions); Assert.NotNull(testcase); return testcase; } private static readonly JsonSerializerOptions s_jsonSerializerOptions = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, WriteIndented = true, }; }