From 60b0a6297ec1f52a9c9b95711f172efc5de878a3 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 12 May 2026 21:07:59 -0700 Subject: [PATCH] fix: preserve empty parallel executor context - hoist the empty executor guard out of the inner closure to match NewPipelineExecutor - preserve canceled context semantics when no executors are provided - add coverage for the empty executor case --- act/common/executor.go | 6 ++++++ act/common/executor_test.go | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/act/common/executor.go b/act/common/executor.go index 08de6faa..90fed4d1 100644 --- a/act/common/executor.go +++ b/act/common/executor.go @@ -97,6 +97,12 @@ func NewErrorExecutor(err error) Executor { // NewParallelExecutor creates a new executor from a parallel of other executors func NewParallelExecutor(parallel int, executors ...Executor) Executor { + if len(executors) == 0 { + return func(ctx context.Context) error { + return ctx.Err() + } + } + return func(ctx context.Context) error { work := make(chan Executor, len(executors)) errs := make(chan error, len(executors)) diff --git a/act/common/executor_test.go b/act/common/executor_test.go index b0771863..41b988c9 100644 --- a/act/common/executor_test.go +++ b/act/common/executor_test.go @@ -12,6 +12,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNewWorkflow(t *testing.T) { @@ -119,6 +120,19 @@ func TestNewParallelExecutor(t *testing.T) { assert.NoError(errSingle) } +func TestNewParallelExecutorEmpty(t *testing.T) { + assert := assert.New(t) + + ctx := context.Background() + require.NoError(t, NewParallelExecutor(2)(ctx)) + + canceledCtx, cancel := context.WithCancel(context.Background()) + cancel() + + err := NewParallelExecutor(2)(canceledCtx) + assert.ErrorIs(err, context.Canceled) +} + func TestNewParallelExecutorFailed(t *testing.T) { assert := assert.New(t)