// 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);
}
}