Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel;
using Microsoft.Bot.ObjectModel;
using Microsoft.PowerFx.Types;
using Xunit.Abstractions;

namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel;

/// <summary>
/// Tests for <see cref="SetMultipleVariablesExecutor"/>.
/// </summary>
public sealed class SetMultipleVariablesExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output)
{
[Fact]
public async Task SetMultipleVariablesAsync()
{
// Arrange, Act, Assert
await this.ExecuteTestAsync(
displayName: nameof(SetMultipleVariablesAsync),
assignments: [
new AssignmentCase("Variable1", new NumberDataValue(42), FormulaValue.New(42)),
new AssignmentCase("Variable2", new StringDataValue("Test"), FormulaValue.New("Test")),
new AssignmentCase("Variable3", new BooleanDataValue(true), FormulaValue.New(true))
]);
}

[Fact]
public async Task SetMultipleVariablesWithExpressionsAsync()
{
// Arrange
this.State.Set("SourceNumber", FormulaValue.New(10));
this.State.Set("SourceText", FormulaValue.New("Hello"));
this.State.Bind();

// Act, Assert
await this.ExecuteTestAsync(
displayName: nameof(SetMultipleVariablesWithExpressionsAsync),
assignments: [
new AssignmentCase("CalcVariable", ValueExpression.Expression("Local.SourceNumber * 2"), FormulaValue.New(20)),
new AssignmentCase("ConcatVariable", ValueExpression.Expression(@"Concatenate(Local.SourceText, "" World"")"), FormulaValue.New("Hello World")),
new AssignmentCase("BoolVariable", ValueExpression.Expression("Local.SourceNumber > 5"), FormulaValue.New(true))
]);
}

[Fact]
public async Task SetMultipleVariablesWithVariableReferencesAsync()
{
// Arrange
this.State.Set("Source1", FormulaValue.New(123));
this.State.Set("Source2", FormulaValue.New("Reference"));
this.State.Bind();

// Act, Assert
await this.ExecuteTestAsync(
displayName: nameof(SetMultipleVariablesWithVariableReferencesAsync),
assignments: [
new AssignmentCase("Target1", ValueExpression.Variable(PropertyPath.TopicVariable("Source1")), FormulaValue.New(123)),
new AssignmentCase("Target2", ValueExpression.Variable(PropertyPath.TopicVariable("Source2")), FormulaValue.New("Reference"))
]);
}

[Fact]
public async Task SetMultipleVariablesWithNullValuesAsync()
{
// Arrange, Act, Assert
await this.ExecuteTestAsync(
displayName: nameof(SetMultipleVariablesWithNullValuesAsync),
assignments: [
new AssignmentCase("NullVar1", null, FormulaValue.NewBlank()),
new AssignmentCase("NormalVar", new StringDataValue("NotNull"), FormulaValue.New("NotNull")),
new AssignmentCase("NullVar2", null, FormulaValue.NewBlank())
]);
}

[Fact]
public async Task SetMultipleVariablesUpdateExistingAsync()
{
// Arrange
this.State.Set("ExistingVar1", FormulaValue.New(999));
this.State.Set("ExistingVar2", FormulaValue.New("OldValue"));

// Act, Assert
await this.ExecuteTestAsync(
displayName: nameof(SetMultipleVariablesUpdateExistingAsync),
assignments: [
new AssignmentCase("ExistingVar1", new NumberDataValue(111), FormulaValue.New(111)),
new AssignmentCase("ExistingVar2", new StringDataValue("NewValue"), FormulaValue.New("NewValue")),
new AssignmentCase("NewVar", new BooleanDataValue(false), FormulaValue.New(false))
]);
}

[Fact]
public async Task SetMultipleVariablesEmptyAssignmentsAsync()
{
// Arrange
SetMultipleVariables model = this.CreateModel(nameof(SetMultipleVariablesEmptyAssignmentsAsync), []);

// Arrange, Act, Assert
Assert.Throws<DeclarativeModelException>(() =>
{
// Empty variables assignment should fail RequiredProperties validation.
_ = new SetMultipleVariablesExecutor(model, this.State);
});
}

private async Task ExecuteTestAsync(string displayName, AssignmentCase[] assignments)
{
// Arrange
SetMultipleVariables model = this.CreateModel(displayName, assignments);

// Act
SetMultipleVariablesExecutor action = new(model, this.State);
await this.ExecuteAsync(action);

// Assert
VerifyModel(model, action);
foreach (AssignmentCase assignment in assignments)
{
this.VerifyState(assignment.VariableName, assignment.ExpectedValue);
}
}

private SetMultipleVariables CreateModel(string displayName, AssignmentCase[] assignments)
{
SetMultipleVariables.Builder actionBuilder = new()
{
Id = this.CreateActionId(),
DisplayName = this.FormatDisplayName(displayName),
};

foreach (AssignmentCase assignment in assignments)
{
ValueExpression.Builder? valueExpressionBuilder = assignment.ValueExpression switch
{
null => null,
DataValue dataValue => new ValueExpression.Builder(ValueExpression.Literal(dataValue)),
ValueExpression valueExpression => new ValueExpression.Builder(valueExpression),
_ => throw new System.ArgumentException($"Unsupported value type: {assignment.ValueExpression?.GetType().Name}")
};

actionBuilder.Assignments.Add(new VariableAssignment.Builder()
{
Variable = PropertyPath.Create(FormatVariablePath(assignment.VariableName)),
Value = valueExpressionBuilder,
});
}

return AssignParent<SetMultipleVariables>(actionBuilder);
}

private sealed record AssignmentCase(string VariableName, object? ValueExpression, FormulaValue ExpectedValue);
}
Loading