db620d33df
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
194 lines
7.6 KiB
C#
194 lines
7.6 KiB
C#
// 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;
|
|
|
|
/// <summary>
|
|
/// Tests targeting the static <see cref="AgentWorkflowBuilder"/> helper surface —
|
|
/// <see cref="AgentWorkflowBuilder.BuildSequential(IEnumerable{AIAgent})"/>,
|
|
/// <see cref="AgentWorkflowBuilder.BuildConcurrent(IEnumerable{AIAgent}, Func{IList{List{ChatMessage}}, List{ChatMessage}})"/>,
|
|
/// and the various <c>Create*BuilderWith</c> factories. Per-builder unit tests live in their own
|
|
/// files (<see cref="SequentialWorkflowBuilderTests"/>, <see cref="ConcurrentWorkflowBuilderTests"/>, etc.).
|
|
/// </summary>
|
|
public class AgentWorkflowBuilderTests
|
|
{
|
|
[Fact]
|
|
public void Test_AgentWorkflowBuilder_BuildSequential_InvalidArguments_Throws()
|
|
{
|
|
Assert.Throws<ArgumentNullException>("agents", () => AgentWorkflowBuilder.BuildSequential(workflowName: null!, null!));
|
|
Assert.Throws<ArgumentException>("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<ChatMessage>? result, _, _) =
|
|
await OrchestrationTestHelpers.RunWorkflowAsync(workflow, [new ChatMessage(ChatRole.User, "abc")]);
|
|
|
|
Assert.NotNull(result);
|
|
Assert.Equal(numAgents + 1, result.Count);
|
|
Assert.NotEmpty(updateText);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Test_AgentWorkflowBuilder_BuildSequential_ChainOnlyAgentResponsesAsync()
|
|
{
|
|
Workflow workflow = AgentWorkflowBuilder.BuildSequential(
|
|
chainOnlyAgentResponses: true,
|
|
new OrchestrationTestHelpers.DoubleEchoAgent("agent1"),
|
|
new OrchestrationTestHelpers.DoubleEchoAgent("agent2"));
|
|
|
|
(_, List<ChatMessage>? result, _, _) =
|
|
await OrchestrationTestHelpers.RunWorkflowAsync(workflow, [new ChatMessage(ChatRole.User, "abc")]);
|
|
|
|
result.Should().NotBeNull();
|
|
result!.Should().ContainSingle();
|
|
result[0].AuthorName.Should().Be("agent2");
|
|
}
|
|
|
|
[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<ArgumentNullException>("agents", () => AgentWorkflowBuilder.BuildConcurrent(null!));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Test_AgentWorkflowBuilder_BuildConcurrent_DelegatesToBuilderAsync()
|
|
{
|
|
StrongBox<TaskCompletionSource<bool>> barrier = new();
|
|
StrongBox<int> remaining = new();
|
|
|
|
Workflow workflow = AgentWorkflowBuilder.BuildConcurrent(
|
|
[
|
|
new OrchestrationTestHelpers.DoubleEchoAgentWithBarrier("agent1", barrier, remaining),
|
|
new OrchestrationTestHelpers.DoubleEchoAgentWithBarrier("agent2", barrier, remaining),
|
|
]);
|
|
|
|
barrier.Value = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
remaining.Value = 2;
|
|
|
|
(string updateText, List<ChatMessage>? 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<ChatMessage> sentinel = [new(ChatRole.Assistant, "custom-aggregator-result")];
|
|
|
|
Workflow workflow = AgentWorkflowBuilder.BuildConcurrent(
|
|
[new OrchestrationTestHelpers.DoubleEchoAgent("agent1")],
|
|
aggregator: _ => sentinel);
|
|
|
|
(_, List<ChatMessage>? 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<ArgumentNullException>("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<ArgumentNullException>("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<ArgumentNullException>("initialAgent", () => AgentWorkflowBuilder.CreateHandoffBuilderWith(null!));
|
|
#pragma warning restore MAAIW001
|
|
}
|
|
|
|
[Fact]
|
|
public void Test_AgentWorkflowBuilder_CreateGroupChatBuilderWith_RejectsNull()
|
|
{
|
|
Assert.Throws<ArgumentNullException>("managerFactory", () => AgentWorkflowBuilder.CreateGroupChatBuilderWith(null!));
|
|
}
|
|
|
|
[Fact]
|
|
public void Test_AgentWorkflowBuilder_CreateMagenticBuilderWith_RejectsNull()
|
|
{
|
|
#pragma warning disable MAAIW001
|
|
Assert.Throws<ArgumentNullException>("managerAgent", () => AgentWorkflowBuilder.CreateMagenticBuilderWith(null!));
|
|
#pragma warning restore MAAIW001
|
|
}
|
|
}
|