mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
* Initial plan * Add comprehensive unit tests for EditTableV2Executor - Test AddItemOperation with record and scalar values - Test ClearItemsOperation - Test RemoveItemOperation - Test TakeLastItemOperation (with items and empty table) - Test TakeFirstItemOperation (with items and empty table) - Test error cases (null ItemsVariable, non-table variable) - Include ExecuteTestAsync and CreateModel helper methods - All 10 tests passing Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> * Add comprehensive unit tests for EditTableV2Executor - complete with 100% coverage - Added 13 comprehensive tests covering all code paths - Test AddItemOperation with record and scalar values - Test ClearItemsOperation - Test RemoveItemOperation (including non-table value case) - Test TakeLastItemOperation (with items and empty table) - Test TakeFirstItemOperation (with items and empty table) - Test error cases (null ItemsVariable, non-table variable, null operation values) - Include ExecuteTestAsync and CreateModel helper methods - 100% line and branch coverage achieved Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> * Update tests / refine product code * Checkpoint * Updated * Update dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetTextVariableExecutorTest.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address code review feedback - Fix typo: rename metadataExpresssion to metadataExpression - Fix test name in AddMessageWithMetadataAsync (was using wrong test name) - Fix test name in ClearGlobalScopeAsync (was using wrong test name) - Remove pre-population in SetTextVariableExecutorTest that made tests ineffective - Use explicit .Where() filter in SetMultipleVariablesExecutorTest foreach loop Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> Co-authored-by: Chris Rickman <crickman@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
95 lines
4.4 KiB
C#
95 lines
4.4 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
|
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
|
|
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
|
using Microsoft.Agents.ObjectModel;
|
|
using Microsoft.Agents.ObjectModel.Abstractions;
|
|
using Microsoft.PowerFx.Types;
|
|
using Microsoft.Shared.Diagnostics;
|
|
|
|
namespace Microsoft.Agents.AI.Workflows.Declarative.ObjectModel;
|
|
|
|
internal sealed class EditTableV2Executor(EditTableV2 model, WorkflowFormulaState state) : DeclarativeActionExecutor<EditTableV2>(model, state)
|
|
{
|
|
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken = default)
|
|
{
|
|
Throw.IfNull(this.Model.ItemsVariable, $"{nameof(this.Model)}.{nameof(this.Model.ItemsVariable)}");
|
|
|
|
FormulaValue table = context.ReadState(this.Model.ItemsVariable);
|
|
if (table is not TableValue tableValue)
|
|
{
|
|
throw this.Exception($"Require '{this.Model.ItemsVariable.Path}' to be a table, not: '{table.GetType().Name}'.");
|
|
}
|
|
|
|
EditTableOperation? changeType = this.Model.ChangeType;
|
|
if (changeType is AddItemOperation addItemOperation)
|
|
{
|
|
ValueExpression addItemValue = Throw.IfNull(addItemOperation.Value, $"{nameof(this.Model)}.{nameof(this.Model.ChangeType)}");
|
|
EvaluationResult<DataValue> expressionResult = this.Evaluator.GetValue(addItemValue);
|
|
RecordValue newRecord = BuildRecord(tableValue.Type.ToRecord(), expressionResult.Value.ToFormula());
|
|
await tableValue.AppendAsync(newRecord, cancellationToken).ConfigureAwait(false);
|
|
await this.AssignAsync(this.Model.ItemsVariable, newRecord, context).ConfigureAwait(false);
|
|
}
|
|
else if (changeType is ClearItemsOperation)
|
|
{
|
|
await tableValue.ClearAsync(cancellationToken).ConfigureAwait(false);
|
|
await this.AssignAsync(this.Model.ItemsVariable, FormulaValue.NewBlank(), context).ConfigureAwait(false);
|
|
}
|
|
else if (changeType is RemoveItemOperation removeItemOperation)
|
|
{
|
|
ValueExpression removeItemValue = Throw.IfNull(removeItemOperation.Value, $"{nameof(this.Model)}.{nameof(this.Model.ChangeType)}");
|
|
EvaluationResult<DataValue> expressionResult = this.Evaluator.GetValue(removeItemValue);
|
|
if (expressionResult.Value.ToFormula() is TableValue removeItemTable)
|
|
{
|
|
await tableValue.RemoveAsync(removeItemTable.Rows.Select(row => row.Value), all: true, cancellationToken).ConfigureAwait(false);
|
|
await this.AssignAsync(this.Model.ItemsVariable, FormulaValue.NewBlank(), context).ConfigureAwait(false);
|
|
}
|
|
}
|
|
else if (changeType is TakeLastItemOperation)
|
|
{
|
|
RecordValue? lastRow = tableValue.Rows.LastOrDefault()?.Value;
|
|
if (lastRow is not null)
|
|
{
|
|
await tableValue.RemoveAsync([lastRow], all: true, cancellationToken).ConfigureAwait(false);
|
|
await this.AssignAsync(this.Model.ItemsVariable, lastRow, context).ConfigureAwait(false);
|
|
}
|
|
}
|
|
else if (changeType is TakeFirstItemOperation)
|
|
{
|
|
RecordValue? firstRow = tableValue.Rows.FirstOrDefault()?.Value;
|
|
if (firstRow is not null)
|
|
{
|
|
await tableValue.RemoveAsync([firstRow], all: true, cancellationToken).ConfigureAwait(false);
|
|
await this.AssignAsync(this.Model.ItemsVariable, firstRow, context).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
return default;
|
|
|
|
static RecordValue BuildRecord(RecordType recordType, FormulaValue value)
|
|
{
|
|
return FormulaValue.NewRecordFromFields(recordType, GetValues());
|
|
|
|
IEnumerable<NamedValue> GetValues()
|
|
{
|
|
foreach (NamedFormulaType fieldType in recordType.GetFieldTypes())
|
|
{
|
|
if (value is RecordValue recordValue)
|
|
{
|
|
yield return new NamedValue(fieldType.Name, recordValue.GetField(fieldType.Name));
|
|
}
|
|
else
|
|
{
|
|
yield return new NamedValue(fieldType.Name, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|