// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Threading.Tasks; using FluentAssertions; using Microsoft.Extensions.AI; #pragma warning disable SYSLIB1045 // Use GeneratedRegex #pragma warning disable RCS1186 // Use Regex instance instead of static method namespace Microsoft.Agents.AI.Workflows.UnitTests; /// /// Tests targeting the static helper surface — /// , /// , /// and the various Create*BuilderWith factories. Per-builder unit tests live in their own /// files (, , etc.). /// public class AgentWorkflowBuilderTests { [Fact] public void Test_AgentWorkflowBuilder_BuildSequential_InvalidArguments_Throws() { Assert.Throws("agents", () => AgentWorkflowBuilder.BuildSequential(workflowName: null!, null!)); Assert.Throws("agents", () => AgentWorkflowBuilder.BuildSequential()); } [Theory] [InlineData(1)] [InlineData(2)] [InlineData(3)] public async Task Test_AgentWorkflowBuilder_BuildSequential_DelegatesToBuilderAsync(int numAgents) { Workflow workflow = AgentWorkflowBuilder.BuildSequential( from i in Enumerable.Range(1, numAgents) select new OrchestrationTestHelpers.DoubleEchoAgent($"agent{i}")); // Smoke: end-to-end run produces a non-empty result. Detailed pipeline-ordering // assertions live in SequentialWorkflowBuilderTests. (string updateText, List? result, _, _) = await OrchestrationTestHelpers.RunWorkflowAsync(workflow, [new ChatMessage(ChatRole.User, "abc")]); Assert.NotNull(result); Assert.Equal(numAgents + 1, result.Count); Assert.NotEmpty(updateText); } [Fact] public void Test_AgentWorkflowBuilder_BuildSequential_WithWorkflowNameSetsNameOnWorkflow() { Workflow workflow = AgentWorkflowBuilder.BuildSequential( "static-sequential", new OrchestrationTestHelpers.DoubleEchoAgent("agent1")); workflow.Name.Should().Be("static-sequential"); } [Fact] public void Test_AgentWorkflowBuilder_BuildConcurrent_InvalidArguments_Throws() { Assert.Throws("agents", () => AgentWorkflowBuilder.BuildConcurrent(null!)); } [Fact] public async Task Test_AgentWorkflowBuilder_BuildConcurrent_DelegatesToBuilderAsync() { StrongBox> barrier = new(); StrongBox remaining = new(); Workflow workflow = AgentWorkflowBuilder.BuildConcurrent( [ new OrchestrationTestHelpers.DoubleEchoAgentWithBarrier("agent1", barrier, remaining), new OrchestrationTestHelpers.DoubleEchoAgentWithBarrier("agent2", barrier, remaining), ]); barrier.Value = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); remaining.Value = 2; (string updateText, List? result, _, _) = await OrchestrationTestHelpers.RunWorkflowAsync(workflow, [new ChatMessage(ChatRole.User, "abc")]); Assert.NotEmpty(updateText); Assert.NotNull(result); Assert.Equal(2, result.Count); Assert.Single(Regex.Matches(updateText, "agent1")); Assert.Single(Regex.Matches(updateText, "agent2")); } [Fact] public void Test_AgentWorkflowBuilder_BuildConcurrent_WithWorkflowNameSetsNameOnWorkflow() { Workflow workflow = AgentWorkflowBuilder.BuildConcurrent( "static-concurrent", [new OrchestrationTestHelpers.DoubleEchoAgent("agent1")]); workflow.Name.Should().Be("static-concurrent"); } [Fact] public async Task Test_AgentWorkflowBuilder_BuildConcurrent_AggregatorIsHonoredAsync() { // Replace the default ("last message from each agent") with a custom aggregator, // and confirm the workflow yields its result. List sentinel = [new(ChatRole.Assistant, "custom-aggregator-result")]; Workflow workflow = AgentWorkflowBuilder.BuildConcurrent( [new OrchestrationTestHelpers.DoubleEchoAgent("agent1")], aggregator: _ => sentinel); (_, List? result, _, _) = await OrchestrationTestHelpers.RunWorkflowAsync(workflow, [new ChatMessage(ChatRole.User, "abc")]); result.Should().NotBeNull().And.ContainSingle(); result![0].Text.Should().Be("custom-aggregator-result"); } [Fact] public void Test_AgentWorkflowBuilder_CreateSequentialBuilderWith_RejectsNull() { Assert.Throws("agents", () => AgentWorkflowBuilder.CreateSequentialBuilderWith(null!)); } [Fact] public void Test_AgentWorkflowBuilder_CreateSequentialBuilderWith_ReturnsConfigurableBuilder() { OrchestrationTestHelpers.DoubleEchoAgent agent = new("agent1"); SequentialWorkflowBuilder builder = AgentWorkflowBuilder.CreateSequentialBuilderWith(agent); Workflow workflow = builder.WithName("via-factory").Build(); workflow.Name.Should().Be("via-factory"); } [Fact] public void Test_AgentWorkflowBuilder_CreateConcurrentBuilderWith_RejectsNull() { Assert.Throws("agents", () => AgentWorkflowBuilder.CreateConcurrentBuilderWith(null!)); } [Fact] public void Test_AgentWorkflowBuilder_CreateConcurrentBuilderWith_ReturnsConfigurableBuilder() { OrchestrationTestHelpers.DoubleEchoAgent agent = new("agent1"); ConcurrentWorkflowBuilder builder = AgentWorkflowBuilder.CreateConcurrentBuilderWith(agent); Workflow workflow = builder.WithName("via-factory").Build(); workflow.Name.Should().Be("via-factory"); } [Fact] public void Test_AgentWorkflowBuilder_CreateHandoffBuilderWith_RejectsNull() { #pragma warning disable MAAIW001 Assert.Throws("initialAgent", () => AgentWorkflowBuilder.CreateHandoffBuilderWith(null!)); #pragma warning restore MAAIW001 } [Fact] public void Test_AgentWorkflowBuilder_CreateGroupChatBuilderWith_RejectsNull() { Assert.Throws("managerFactory", () => AgentWorkflowBuilder.CreateGroupChatBuilderWith(null!)); } [Fact] public void Test_AgentWorkflowBuilder_CreateMagenticBuilderWith_RejectsNull() { #pragma warning disable MAAIW001 Assert.Throws("managerAgent", () => AgentWorkflowBuilder.CreateMagenticBuilderWith(null!)); #pragma warning restore MAAIW001 } }