// Copyright (c) Microsoft. All rights reserved. using System; using System.Threading.Tasks; using Microsoft.Extensions.AI; using Moq; namespace Microsoft.Agents.AI.UnitTests; /// /// Unit tests for the class, including its public constructor used to test custom evaluators. /// public class LoopContextTests { /// /// Verify that the constructor throws when the agent is null. /// [Fact] public void Constructor_NullAgent_Throws() { // Act & Assert Assert.Throws("agent", () => new LoopContext( null!, new ChatClientAgentSession(), [], CreateResponse())); } /// /// Verify that the constructor throws when the session is null. /// [Fact] public void Constructor_NullSession_Throws() { // Act & Assert Assert.Throws("session", () => new LoopContext( new Mock().Object, null!, [], CreateResponse())); } /// /// Verify that the constructor throws when the initial messages are null. /// [Fact] public void Constructor_NullInitialMessages_Throws() { // Act & Assert Assert.Throws("initialMessages", () => new LoopContext( new Mock().Object, new ChatClientAgentSession(), null!, CreateResponse())); } /// /// Verify that the constructor throws when the last response is null. /// [Fact] public void Constructor_NullLastResponse_Throws() { // Act & Assert Assert.Throws("lastResponse", () => new LoopContext( new Mock().Object, new ChatClientAgentSession(), [], null!)); } /// /// Verify that the constructor populates the properties and that LastResponse is never null. /// [Fact] public void Constructor_ValidArguments_SetsProperties() { // Arrange var agent = new Mock().Object; var session = new ChatClientAgentSession(); ChatMessage[] initialMessages = [new ChatMessage(ChatRole.User, "go")]; var response = CreateResponse("done"); // Act var context = new LoopContext(agent, session, initialMessages, response); // Assert Assert.Same(agent, context.Agent); Assert.Same(session, context.Session); Assert.Same(initialMessages, context.InitialMessages); Assert.Same(response, context.LastResponse); Assert.Null(context.RunOptions); Assert.NotNull(context.AdditionalProperties); Assert.Equal(0, context.Iteration); Assert.Empty(context.Feedback); } /// /// Verify that the session can be replaced through the internal setter (used by the loop for fresh contexts). /// [Fact] public void Session_IsInternallySettable() { // Arrange var context = new LoopContext( new Mock().Object, new ChatClientAgentSession(), [], CreateResponse()); var newSession = new ChatClientAgentSession(); // Act context.Session = newSession; // Assert Assert.Same(newSession, context.Session); } /// /// Verify that can be assigned through its internal setter. /// [Fact] public void Feedback_IsInternallySettable() { // Arrange var context = new LoopContext( new Mock().Object, new ChatClientAgentSession(), [], CreateResponse()); // Act context.Feedback = ["first", null]; // Assert Assert.Equal(["first", null], context.Feedback); } /// /// Verify that an evaluator can be evaluated against a publicly-constructed context (the scenario the public /// constructor exists to support). /// [Fact] public async Task PubliclyConstructedContext_CanEvaluateEvaluatorAsync() { // Arrange var context = new LoopContext( new Mock().Object, new ChatClientAgentSession(), [new ChatMessage(ChatRole.User, "go")], CreateResponse("done")); var evaluator = new DelegateLoopEvaluator((ctx, _) => new ValueTask( ctx.LastResponse.Text == "done" ? LoopEvaluation.Stop() : LoopEvaluation.Continue())); // Act LoopEvaluation evaluation = await evaluator.EvaluateAsync(context); // Assert Assert.False(evaluation.ShouldReinvoke); } private static AgentResponse CreateResponse(string text = "response") => new([new ChatMessage(ChatRole.Assistant, text)]); }