// Copyright (c) Microsoft. All rights reserved. using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Agents.AI.Workflows.Declarative.Extensions; using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; using Microsoft.Agents.ObjectModel; using Microsoft.Extensions.AI; using Microsoft.PowerFx.Types; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; /// /// Tests for . /// public sealed class CopyConversationMessagesExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output) { [Fact] public async Task CopyMessagesWithSingleStringMessageAsync() { // Arrange, Act, Assert await this.ExecuteTestAsync( displayName: nameof(CopyMessagesWithSingleStringMessageAsync), conversationId: "TestConversationId", messages: ValueExpression.Literal(StringDataValue.Create("Hello, how can I help you?")), expectedMessageCount: 1); } [Fact] public async Task CopyMessagesWithSingleRecordMessageAsync() { // Arrange ChatMessage testMessage = new(ChatRole.User, "Test message content"); DataValue messageDataValue = testMessage.ToRecord().ToDataValue(); Assert.IsType(messageDataValue); RecordDataValue messageRecord = (RecordDataValue)messageDataValue; // Act, Assert await this.ExecuteTestAsync( displayName: nameof(CopyMessagesWithSingleRecordMessageAsync), conversationId: "TestConversationId", messages: ValueExpression.Literal(messageRecord), expectedMessageCount: 1); } [Fact] public async Task CopyMessagesWithMultipleMessagesAsync() { // Arrange List testMessages = [ new ChatMessage(ChatRole.User, "First message"), new ChatMessage(ChatRole.Assistant, "Second message"), new ChatMessage(ChatRole.User, "Third message") ]; DataValue messagesDataValue = testMessages.ToTable().ToDataValue(); Assert.IsType(messagesDataValue); TableDataValue messagesTable = (TableDataValue)messagesDataValue; // Act, Assert await this.ExecuteTestAsync( displayName: nameof(CopyMessagesWithMultipleMessagesAsync), conversationId: "TestConversationId", messages: ValueExpression.Literal(messagesTable), expectedMessageCount: 3); } [Fact] public async Task CopyMessagesWithVariableExpressionAsync() { // Arrange List testMessages = [ new ChatMessage(ChatRole.User, "Message from variable") ]; TableValue messagesTable = testMessages.ToTable(); this.State.Set("SourceMessages", messagesTable); // Act, Assert await this.ExecuteTestAsync( displayName: nameof(CopyMessagesWithVariableExpressionAsync), conversationId: "TestConversationId", messages: ValueExpression.Variable(PropertyPath.TopicVariable("SourceMessages")), expectedMessageCount: 1); } [Fact] public async Task CopyMessagesToWorkflowConversationAsync() { // Arrange this.State.Set(SystemScope.Names.ConversationId, FormulaValue.New("WorkflowConversationId"), VariableScopeNames.System); List testMessages = [ new ChatMessage(ChatRole.User, "Message to workflow conversation") ]; DataValue messagesDataValue = testMessages.ToTable().ToDataValue(); Assert.IsType(messagesDataValue); TableDataValue messagesTable = (TableDataValue)messagesDataValue; // Act, Assert await this.ExecuteTestAsync( displayName: nameof(CopyMessagesToWorkflowConversationAsync), conversationId: "WorkflowConversationId", messages: ValueExpression.Literal(messagesTable), expectedMessageCount: 1, expectWorkflowEvent: true); } [Fact] public async Task CopyMessagesToNonWorkflowConversationAsync() { // Arrange this.State.Set(SystemScope.Names.ConversationId, FormulaValue.New("WorkflowConversationId"), VariableScopeNames.System); List testMessages = [ new ChatMessage(ChatRole.User, "Message to non-workflow conversation") ]; DataValue messagesDataValue = testMessages.ToTable().ToDataValue(); Assert.IsType(messagesDataValue); TableDataValue messagesTable = (TableDataValue)messagesDataValue; // Act, Assert await this.ExecuteTestAsync( displayName: nameof(CopyMessagesToNonWorkflowConversationAsync), conversationId: "DifferentConversationId", messages: ValueExpression.Literal(messagesTable), expectedMessageCount: 1, expectWorkflowEvent: false); } [Fact] public async Task CopyMessagesWithBlankDataValueAsync() { // Arrange, Act, Assert await this.ExecuteTestAsync( displayName: nameof(CopyMessagesWithBlankDataValueAsync), conversationId: "TestConversationId", messages: ValueExpression.Literal(DataValue.Blank()), expectedMessageCount: 0); } private async Task ExecuteTestAsync( string displayName, string conversationId, ValueExpression messages, int expectedMessageCount, bool expectWorkflowEvent = false) { // Arrange MockAgentProvider mockAgentProvider = new(); mockAgentProvider.TestMessages.Clear(); CopyConversationMessages model = this.CreateModel( this.FormatDisplayName(displayName), conversationId, messages); CopyConversationMessagesExecutor action = new(model, mockAgentProvider.Object, this.State); // Act WorkflowEvent[] events = await this.ExecuteAsync(action); // Assert Assert.Equal(expectedMessageCount, mockAgentProvider.TestMessages.Count); VerifyModel(model, action); AgentResponseEvent[] responseEvents = events.OfType().ToArray(); if (expectWorkflowEvent && expectedMessageCount > 0) { Assert.NotEmpty(responseEvents); AgentResponseEvent responseEvent = responseEvents.First(); Assert.Equal(action.Id, responseEvent.ExecutorId); Assert.NotNull(responseEvent.Response); Assert.Equal(expectedMessageCount, responseEvent.Response.Messages.Count); } else { Assert.Empty(responseEvents); } } private CopyConversationMessages CreateModel( string displayName, string conversationId, ValueExpression messages) { CopyConversationMessages.Builder actionBuilder = new() { Id = this.CreateActionId(), DisplayName = this.FormatDisplayName(displayName), ConversationId = StringExpression.Literal(conversationId), Messages = messages }; return AssignParent(actionBuilder); } }