fix(expressions): keep C# variable accessor lookup keys as real names (#8160)
* fix(expressions): keep C# variable accessor lookup keys as real names Pascalize only the generated C# property identifier. Get/Set still use the original variable.Name so camelCase names like orderId resolve the same way as JS, Liquid, and Python. Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com> * test(expressions): disambiguate Options.Create in accessor tests Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
parent
6503c0cb92
commit
7e22a12081
|
|
@ -38,7 +38,8 @@ public class GenerateWorkflowVariableAccessors(IOptions<CSharpOptions> options)
|
|||
{
|
||||
foreach (var variable in variables.Where(x => x.Name.IsValidVariableName()))
|
||||
{
|
||||
var variableName = variable.Name.Pascalize();
|
||||
var propertyName = variable.Name.Pascalize();
|
||||
var variableName = variable.Name;
|
||||
var variableType = variable.GetVariableType();
|
||||
|
||||
// Check if the variable type is ExpandoObject
|
||||
|
|
@ -48,7 +49,7 @@ public class GenerateWorkflowVariableAccessors(IOptions<CSharpOptions> options)
|
|||
var displayTypeName = isExpandoObject ? "dynamic" : variableType.GetFriendlyTypeName(Brackets.Angle);
|
||||
var retrieveTypeName = isExpandoObject ? "ExpandoObject" : displayTypeName;
|
||||
|
||||
sb.AppendLine($"\tpublic {displayTypeName} {variableName}");
|
||||
sb.AppendLine($"\tpublic {displayTypeName} {propertyName}");
|
||||
sb.AppendLine("\t{");
|
||||
sb.AppendLine($"\t\tget => Get<{retrieveTypeName}>(\"{variableName}\");");
|
||||
sb.AppendLine($"\t\tset => Set(\"{variableName}\", value);");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
using Elsa.Expressions.CSharp.Handlers;
|
||||
using Elsa.Expressions.CSharp.Notifications;
|
||||
using Elsa.Expressions.CSharp.Options;
|
||||
using Elsa.Expressions.CSharp.Services;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Mediator.Contracts;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
|
||||
namespace Elsa.Expressions.UnitTests.CSharp;
|
||||
|
||||
public class GenerateWorkflowVariableAccessorsTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task PascalizedProperty_ReadsCamelCaseVariable_ByRealName()
|
||||
{
|
||||
var context = CreateContext(new Variable<string>("orderId", "ORD-1"));
|
||||
var evaluator = CreateEvaluator();
|
||||
|
||||
var result = await evaluator.EvaluateAsync("Variables.OrderId", typeof(string), context, new ExpressionEvaluatorOptions());
|
||||
|
||||
Assert.Equal("ORD-1", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PascalizedProperty_WritesCamelCaseVariable_WithoutCreatingPascalizedShadow()
|
||||
{
|
||||
var context = CreateContext(new Variable<string>("orderId", "ORD-1"));
|
||||
var evaluator = CreateEvaluator();
|
||||
|
||||
var result = await evaluator.EvaluateAsync(
|
||||
"""
|
||||
Variables.OrderId = "ORD-2";
|
||||
Variables.OrderId
|
||||
""",
|
||||
typeof(string),
|
||||
context,
|
||||
new ExpressionEvaluatorOptions());
|
||||
|
||||
Assert.Equal("ORD-2", result);
|
||||
Assert.Equal("ORD-2", context.GetVariable<string>("orderId"));
|
||||
Assert.Null(context.GetVariable<string>("OrderId"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PascalizedProperty_ReadsAlreadyPascalCaseVariable()
|
||||
{
|
||||
var context = CreateContext(new Variable<string>("OrderId", "ORD-3"));
|
||||
var evaluator = CreateEvaluator();
|
||||
|
||||
var result = await evaluator.EvaluateAsync("Variables.OrderId", typeof(string), context, new ExpressionEvaluatorOptions());
|
||||
|
||||
Assert.Equal("ORD-3", result);
|
||||
}
|
||||
|
||||
private static ExpressionExecutionContext CreateContext(Variable variable)
|
||||
{
|
||||
var memoryRegister = new MemoryRegister();
|
||||
memoryRegister.Declare(variable);
|
||||
return new ExpressionExecutionContext(new ServiceCollection().BuildServiceProvider(), memoryRegister);
|
||||
}
|
||||
|
||||
private static CSharpEvaluator CreateEvaluator()
|
||||
{
|
||||
var csharpOptions = new CSharpOptions
|
||||
{
|
||||
AllowHostCodeExecution = true
|
||||
};
|
||||
var options = Microsoft.Extensions.Options.Options.Create(csharpOptions);
|
||||
var accessors = new GenerateWorkflowVariableAccessors(options);
|
||||
var assemblies = new AddAssembliesAndReferencesFromOptions(options);
|
||||
var notificationSender = Substitute.For<INotificationSender>();
|
||||
|
||||
notificationSender
|
||||
.SendAsync(Arg.Any<INotification>(), Arg.Any<CancellationToken>())
|
||||
.Returns(async callInfo =>
|
||||
{
|
||||
if (callInfo.Arg<INotification>() is not EvaluatingCSharp evaluating)
|
||||
return;
|
||||
|
||||
var cancellationToken = callInfo.Arg<CancellationToken>();
|
||||
await assemblies.HandleAsync(evaluating, cancellationToken);
|
||||
await accessors.HandleAsync(evaluating, cancellationToken);
|
||||
});
|
||||
|
||||
return new CSharpEvaluator(notificationSender, options, new MemoryCache(new MemoryCacheOptions()));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue