From 88433586fa38cd0e1dd24d177539411a22337674 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 10 Feb 2026 23:58:11 +0000
Subject: [PATCH 1/4] Initial plan
From 7a2f9d343bd099dee5bac7b3d9139c0bdb161aa6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Feb 2026 00:06:31 +0000
Subject: [PATCH 2/4] Add CopyConversationMessagesExecutorTest with 7
comprehensive test cases
Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>
---
.../CopyConversationMessagesExecutorTest.cs | 203 ++++++++++++++++++
1 file changed, 203 insertions(+)
create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs
diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs
new file mode 100644
index 0000000000..8bb005b1d0
--- /dev/null
+++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs
@@ -0,0 +1,203 @@
+// 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;
+using Xunit.Abstractions;
+
+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 ChatMessage(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);
+
+ if (expectWorkflowEvent && expectedMessageCount > 0)
+ {
+ AgentResponseEvent[] responseEvents = events.OfType().ToArray();
+ 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 if (!expectWorkflowEvent)
+ {
+ AgentResponseEvent[] responseEvents = events.OfType().ToArray();
+ 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);
+ }
+}
From 214041da70fa1006db9e43e5ca2cb43de40bdf24 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Feb 2026 00:09:15 +0000
Subject: [PATCH 3/4] Simplify ExecuteTestAsync to avoid duplicate event
filtering logic
Co-authored-by: crickman <66376200+crickman@users.noreply.github.com>
---
.../ObjectModel/CopyConversationMessagesExecutorTest.cs | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs
index 8bb005b1d0..8602cca51f 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs
@@ -169,18 +169,17 @@ private async Task ExecuteTestAsync(
Assert.Equal(expectedMessageCount, mockAgentProvider.TestMessages.Count);
VerifyModel(model, action);
+ AgentResponseEvent[] responseEvents = events.OfType().ToArray();
if (expectWorkflowEvent && expectedMessageCount > 0)
{
- AgentResponseEvent[] responseEvents = events.OfType().ToArray();
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 if (!expectWorkflowEvent)
+ else
{
- AgentResponseEvent[] responseEvents = events.OfType().ToArray();
Assert.Empty(responseEvents);
}
}
From dc79f28f153efe5cdcdc9c59e1fd37a2c19cf1d8 Mon Sep 17 00:00:00 2001
From: Chris Rickman
Date: Wed, 11 Feb 2026 08:31:03 -0800
Subject: [PATCH 4/4] Refine
---
.../ObjectModel/CopyConversationMessagesExecutor.cs | 11 ++++-------
.../CopyConversationMessagesExecutorTest.cs | 4 ++--
2 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/CopyConversationMessagesExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/CopyConversationMessagesExecutor.cs
index 6a51ce5805..bf5be5310f 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/CopyConversationMessagesExecutor.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/CopyConversationMessagesExecutor.cs
@@ -42,14 +42,11 @@ internal sealed class CopyConversationMessagesExecutor(CopyConversationMessages
private IEnumerable? GetInputMessages()
{
- DataValue? messages = null;
+ Throw.IfNull(this.Model.Messages, $"{nameof(this.Model)}.{nameof(this.Model.Messages)}");
- if (this.Model.Messages is not null)
- {
- EvaluationResult expressionResult = this.Evaluator.GetValue(this.Model.Messages);
- messages = expressionResult.Value;
- }
+ EvaluationResult expressionResult = this.Evaluator.GetValue(this.Model.Messages);
+ DataValue messages = expressionResult.Value;
- return messages?.ToChatMessages();
+ return messages.ToChatMessages();
}
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs
index 8602cca51f..cb818fec15 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Linq;
@@ -33,7 +33,7 @@ await this.ExecuteTestAsync(
public async Task CopyMessagesWithSingleRecordMessageAsync()
{
// Arrange
- ChatMessage testMessage = new ChatMessage(ChatRole.User, "Test message content");
+ ChatMessage testMessage = new(ChatRole.User, "Test message content");
DataValue messageDataValue = testMessage.ToRecord().ToDataValue();
Assert.IsType(messageDataValue);
RecordDataValue messageRecord = (RecordDataValue)messageDataValue;