From d1a66a8f3d1564bb43dd29c68c85d7779809e598 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 22:02:11 +0000 Subject: [PATCH] Add NextSpeaker_Empty_Falls_Back_To_First E2E test Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/6e8bca46-448d-4f21-a7e9-240179571970 Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> --- .../MagenticOrchestrationTests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MagenticOrchestrationTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MagenticOrchestrationTests.cs index 2872af66bb..89425e6d5d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MagenticOrchestrationTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MagenticOrchestrationTests.cs @@ -259,6 +259,58 @@ public class MagenticOrchestrationTests runResult.Result![0].Text.Should().Contain("Immediate completion"); } + [Fact] + public async Task NextSpeaker_Empty_Falls_Back_To_First() + { + // Arrange: First progress ledger returns empty next_speaker, which should fall back to first participant. + // Round 1: empty speaker → fallback to Worker (first participant) → Worker echoes + // Round 2 (after Worker responds, TakeTurnAsync re-enters): new plan + satisfied ledger → final answer + List factsResponse1 = CreatePlanResponse("Facts about the task"); + List planResponse1 = CreatePlanResponse("Step 1: Execute"); + List emptyNextSpeakerLedger = CreateProgressLedgerResponse( + isRequestSatisfied: false, + isInLoop: false, + isProgressBeingMade: true, + nextSpeaker: "", // Empty - should fall back to first participant + instructionOrQuestion: "Please help with this task"); + + // Round 2 responses (after Worker echoes back, orchestrator re-enters TakeTurnAsync → UpdatePlanAndDelegateAsync) + List factsResponse2 = CreatePlanResponse("Updated facts"); + List planResponse2 = CreatePlanResponse("Updated plan"); + List satisfiedLedger = CreateProgressLedgerResponse( + isRequestSatisfied: true, + isInLoop: false, + isProgressBeingMade: true, + nextSpeaker: "Worker", + instructionOrQuestion: "Done"); + List finalAnswerResponse = CreateFinalAnswerResponse("Task completed after fallback"); + + TestReplayAgent manager = new( + [factsResponse1, planResponse1, emptyNextSpeakerLedger, + factsResponse2, planResponse2, satisfiedLedger, finalAnswerResponse], + name: "Manager"); + TestEchoAgent worker = new(name: "Worker"); + + List collectedEvents = []; + + Workflow workflow = new MagenticWorkflowBuilder(manager) + .AddParticipants(worker) + .RequirePlanSignoff(false) + .Build(); + + // Act + WorkflowRunResult runResult = await RunMagenticWorkflowAsync( + workflow, + [new ChatMessage(ChatRole.User, "Do the task")], + eventCollector: collectedEvents); + + // Assert: Warning about empty next speaker should be emitted + collectedEvents.OfType() + .Should().Contain(e => e.Data != null && e.Data.ToString()!.Contains("empty")); + runResult.Result.Should().NotBeNull(); + runResult.Result![0].Text.Should().Contain("Task completed after fallback"); + } + #region Helper Methods private sealed record WorkflowRunResult(