Skip to content

Add integration tests for enumerables with projection to arrays#7290

Merged
sfmskywalker merged 2 commits intorelease/3.6.0from
bug/7289
Feb 16, 2026
Merged

Add integration tests for enumerables with projection to arrays#7290
sfmskywalker merged 2 commits intorelease/3.6.0from
bug/7289

Conversation

@sfmskywalker
Copy link
Member

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.

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.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an end-to-end integration test scenario that reproduces and validates JavaScript-triggered conversion of a Select-projected IEnumerable into an array, along with a small update to the enumerable-to-array conversion heuristic in core expression execution.

Changes:

  • Added a new integration test scenario (ProjectedEnumerableToArray) with a custom activity + workflow to reproduce the projected-enumerable conversion case.
  • Added an integration test asserting the workflow finishes and that the projected enumerable is materialized as a string[].
  • Updated ConvertIEnumerableToArray to use the last generic argument when inferring the element type (to support Select iterators).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/unit/Elsa.Workflows.Core.UnitTests/Extensions/ExpressionExecutionContextExtensions/ExpressionExecutionContextExtensionsTests.cs Minor unit test refactor (target-typed new), plus new (currently unused) usings.
test/integration/Elsa.Workflows.IntegrationTests/Scenarios/ProjectedEnumerableToArray/TestOutputItem.cs Adds a simple DTO used to build projected strings in the test scenario.
test/integration/Elsa.Workflows.IntegrationTests/Scenarios/ProjectedEnumerableToArray/TestEnumerableActivity.cs Adds a custom activity that produces a Select iterator to reproduce the conversion issue.
test/integration/Elsa.Workflows.IntegrationTests/Scenarios/ProjectedEnumerableToArray/EnumerableProjectionTests.cs Adds an integration test asserting JS-driven enumerable-to-array conversion.
test/integration/Elsa.Workflows.IntegrationTests/Scenarios/ProjectedEnumerableToArray/EnumerableProjectionTestWorkflow.cs Adds a code-first workflow wiring the custom activity into RunJavaScript to trigger conversion.
src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs Adjusts element type inference for enumerable-to-array conversion to handle projection iterators.
Comments suppressed due to low confidence (2)

src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs:566

  • ConvertIEnumerableToArray infers the element type via obj.GetType().GetGenericArguments().LastOrDefault(). This is still a brittle heuristic: for many IEnumerable<T> implementations the enumerated element type is not the last generic argument of the concrete type (e.g., types like Lookup<TKey, TElement> enumerate IGrouping<TKey,TElement>). Consider deriving the element type from the implemented IEnumerable<T> interface instead (there is already TypeExtensions.GetEnumerableElementType in Elsa.Extensions) and only calling Enumerable.ToArray<T> when a real IEnumerable<T> element type can be determined.
        // 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;

        var toArrayMethod = typeof(Enumerable).GetMethod("ToArray")!.MakeGenericMethod(elementType);

test/unit/Elsa.Workflows.Core.UnitTests/Extensions/ExpressionExecutionContextExtensions/ExpressionExecutionContextExtensionsTests.cs:5

  • The newly added using directives appear to be unused in this test file (e.g., Elsa.Expressions.JavaScript.Activities, Elsa.Testing.Shared, Elsa.Workflows.Activities). Consider removing them to keep the unit test file warning-free and focused.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 12, 2026

Greptile Overview

Greptile Summary

This PR fixes a bug in ConvertIEnumerableToArray where LINQ projection operators (like Select) with multiple generic arguments were incorrectly extracting the element type using FirstOrDefault() instead of LastOrDefault().

Key Changes:

  • Fixed type inference in ExpressionExecutionContextExtensions.cs:561 by changing from FirstOrDefault() to LastOrDefault() when extracting element types from generic arguments
  • For projection operators like ListSelectIterator<TSource, TResult>, the element type is the LAST generic argument (TResult), not the first
  • Added comprehensive integration tests that reproduce the bug scenario end-to-end with JavaScript execution
  • Moved unit test file to a better organized directory structure

Impact:
This fix ensures that Select-projected IEnumerables can be correctly converted to arrays when accessed through JavaScript expressions in workflows, preventing runtime faults that previously occurred with multi-generic-argument LINQ iterators.

Confidence Score: 5/5

  • This PR is safe to merge with no risk
  • The change is a targeted bug fix with comprehensive test coverage. The fix changes FirstOrDefault() to LastOrDefault() which is safe for all IEnumerable types: for single-generic collections (List, Array) both methods return the same element type, while for multi-generic LINQ iterators (Select, SelectMany) LastOrDefault() correctly returns the result type. The PR includes both integration and unit tests verifying the fix works correctly.
  • No files require special attention

Important Files Changed

Filename Overview
src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs Fixed type inference bug in ConvertIEnumerableToArray by using LastOrDefault() instead of FirstOrDefault() to correctly handle LINQ projection operators like Select
test/integration/Elsa.Workflows.IntegrationTests/Scenarios/ProjectedEnumerableToArray/EnumerableProjectionTests.cs Added integration test verifying JavaScript can convert Select-projected IEnumerable variables to arrays without faulting
test/integration/Elsa.Workflows.IntegrationTests/Scenarios/ProjectedEnumerableToArray/TestEnumerableActivity.cs Added test activity that produces Select-projected IEnumerable with two generic arguments to reproduce the conversion bug

Sequence Diagram

sequenceDiagram
    participant Workflow
    participant TestEnumerableActivity
    participant Variable as Messages Variable
    participant RunJavaScript
    participant ExpressionContext
    participant ConvertToArray as ConvertIEnumerableToArray

    Workflow->>TestEnumerableActivity: Execute activity
    TestEnumerableActivity->>TestEnumerableActivity: Create items with Select projection
    Note over TestEnumerableActivity: Creates ListSelectIterator<TestOutputItem, string>
    TestEnumerableActivity->>Variable: Set EnumerableResult (IEnumerable)
    
    Workflow->>RunJavaScript: Execute JavaScript
    RunJavaScript->>ExpressionContext: getMessages()
    ExpressionContext->>ExpressionContext: GetVariableInScope("Messages")
    ExpressionContext->>ConvertToArray: Convert IEnumerable to array
    
    Note over ConvertToArray: GetGenericArguments().LastOrDefault()<br/>Gets string (TResult) instead of TestOutputItem (TSource)
    
    ConvertToArray->>ConvertToArray: Enumerable.ToArray<string>()
    ConvertToArray->>ExpressionContext: Return string[]
    ExpressionContext->>RunJavaScript: Return array
    RunJavaScript->>Variable: Set Result
    RunJavaScript->>Workflow: Complete
Loading

Copy link
Contributor

@j03y-nxxbz j03y-nxxbz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simple effective solution. Proper testing

@sfmskywalker sfmskywalker merged commit af1f1b9 into release/3.6.0 Feb 16, 2026
6 of 7 checks passed
@sfmskywalker sfmskywalker deleted the bug/7289 branch February 16, 2026 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JavaScript variable resolution crashes for LINQ-projected IEnumerable values (SelectIterator)

3 participants