Add integration tests for enumerables with projection to arrays (#7290)
* Add integration tests for enumerables with projection to arrays Introduce integration tests to verify the conversion of a Select-projected IEnumerable to an array using JavaScript execution. This includes creating new test cases, activities, and workflows to ensure the end-to-end process behaves as expected. Also refined type inference in `ExpressionExecutionContextExtensions` for projection operators. * Remove unused newline in EnumerableProjectionTests
This commit is contained in:
parent
f5505d66c9
commit
af1f1b9ec0
|
|
@ -556,7 +556,9 @@ public static class ExpressionExecutionContextExtensions
|
|||
return obj;
|
||||
|
||||
// Use LINQ to convert the IEnumerable to an array.
|
||||
var elementType = obj.GetType().GetGenericArguments().FirstOrDefault();
|
||||
// For projection operators like Select, the element type is the LAST generic argument
|
||||
// (e.g., ListSelectIterator<TSource, TResult> where TResult is the element type)
|
||||
var elementType = obj.GetType().GetGenericArguments().LastOrDefault();
|
||||
|
||||
if (elementType == null)
|
||||
return obj;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
using Elsa.Expressions.JavaScript.Activities;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Memory;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Scenarios.ProjectedEnumerableToArray;
|
||||
|
||||
/// <summary>
|
||||
/// Code-first workflow that reproduces the "projected IEnumerable to array" scenario end-to-end.
|
||||
/// </summary>
|
||||
public class EnumerableProjectionTestWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
// Store in workflow instance state to mimic real execution behavior.
|
||||
var messagesVar = new Variable<object>("Messages", Array.Empty<string>()).WithWorkflowStorage();
|
||||
|
||||
workflow.WithVariables(messagesVar);
|
||||
|
||||
workflow.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new TestEnumerableActivity
|
||||
{
|
||||
EnumerableResult = new(messagesVar)
|
||||
},
|
||||
|
||||
new RunJavaScript
|
||||
{
|
||||
// Accessing Messages through JS triggers the conversion logic.
|
||||
Script = new("return getMessages();"),
|
||||
Result = new(messagesVar)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using Elsa.Expressions.Helpers;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Management;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Scenarios.ProjectedEnumerableToArray;
|
||||
|
||||
public class EnumerableProjectionTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
private readonly WorkflowTestFixture _fixture = new(testOutputHelper);
|
||||
|
||||
[Fact(DisplayName = "JavaScript should convert a Select-projected IEnumerable variable to an array")]
|
||||
public async Task Should_Convert_Select_Projected_Enumerable_To_Array()
|
||||
{
|
||||
// Arrange
|
||||
var workflow = new EnumerableProjectionTestWorkflow();
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunWorkflowAsync(workflow);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus); // If conversion failed, workflow will have faulted.
|
||||
var variableManager = _fixture.Services.GetRequiredService<IWorkflowInstanceVariableManager>();
|
||||
var messagesVariable = (await variableManager.GetVariablesAsync(result.WorkflowExecutionContext)).FirstOrDefault(x => x.Variable.Name == "Messages");
|
||||
var messages = messagesVariable?.Value.ConvertTo<string[]>();
|
||||
|
||||
Assert.NotNull(messages);
|
||||
Assert.Equal(5, messages.Length);
|
||||
|
||||
Assert.All(messages, message =>
|
||||
{
|
||||
Assert.Contains("Name:", message);
|
||||
Assert.Contains("ID:", message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using Elsa.Workflows.Models;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Scenarios.ProjectedEnumerableToArray;
|
||||
|
||||
/// <summary>
|
||||
/// A test activity that produces an IEnumerable<string> via Select projection, which has two generic arguments and was triggering the bug in ConvertIEnumerableToArray.
|
||||
/// </summary>
|
||||
public class TestEnumerableActivity : Activity
|
||||
{
|
||||
public Output<object?> EnumerableResult { get; set; } = new();
|
||||
|
||||
protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
|
||||
{
|
||||
// Create a list of items
|
||||
var items = Enumerable
|
||||
.Range(1, 5)
|
||||
.Select(x => new TestOutputItem())
|
||||
.ToList();
|
||||
|
||||
// Use Select to project to strings - this creates a ListSelectIterator<TestOutputItem, string>
|
||||
// which has TWO generic arguments, triggering the bug
|
||||
var messages = items.Select(e => $"Name: {e.Name} ID: {e.Id}");
|
||||
|
||||
context.Set(EnumerableResult, messages);
|
||||
return context.CompleteActivityWithOutcomesAsync("Done");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace Elsa.Workflows.IntegrationTests.Scenarios.ProjectedEnumerableToArray;
|
||||
|
||||
public class TestOutputItem
|
||||
{
|
||||
public string Name { get; } = Guid.NewGuid().ToString();
|
||||
public string Id { get; } = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
using Elsa.Expressions.JavaScript.Activities;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Memory;
|
||||
|
||||
namespace Elsa.Workflows.Core.UnitTests;
|
||||
|
|
@ -13,7 +16,7 @@ public class ExpressionExecutionContextExtensionsTests
|
|||
var variable = new Variable("test", 5);
|
||||
var memoryRegister = new MemoryRegister(new Dictionary<string, MemoryBlock>
|
||||
{
|
||||
{ variable.Id, new MemoryBlock(variable.Value, new VariableBlockMetadata(variable, typeof(object), true)) }
|
||||
{ variable.Id, new(variable.Value, new VariableBlockMetadata(variable, typeof(object), true)) }
|
||||
});
|
||||
|
||||
var context = new ExpressionExecutionContext(null!, memoryRegister);
|
||||
|
|
@ -46,7 +49,7 @@ public class ExpressionExecutionContextExtensionsTests
|
|||
var variable = new Variable("test", 5);
|
||||
var memoryRegister = new MemoryRegister(new Dictionary<string, MemoryBlock>
|
||||
{
|
||||
{ variable.Id, new MemoryBlock(variable.Value, new VariableBlockMetadata(variable, typeof(object), true)) }
|
||||
{ variable.Id, new(variable.Value, new VariableBlockMetadata(variable, typeof(object), true)) }
|
||||
});
|
||||
|
||||
var context = new ExpressionExecutionContext(null!, memoryRegister);
|
||||
|
|
@ -92,7 +95,7 @@ public class ExpressionExecutionContextExtensionsTests
|
|||
var variable = new Variable("test", 5);
|
||||
var memoryRegister = new MemoryRegister(new Dictionary<string, MemoryBlock>
|
||||
{
|
||||
{ variable.Id, new MemoryBlock(variable.Value, new VariableBlockMetadata(variable, typeof(object), true)) }
|
||||
{ variable.Id, new(variable.Value, new VariableBlockMetadata(variable, typeof(object), true)) }
|
||||
});
|
||||
|
||||
var context = new ExpressionExecutionContext(null!, memoryRegister);
|
||||
Loading…
Reference in a new issue