Skip to content

Commit 1fed700

Browse files
authored
Test-suite bugfix: local activity errors were not encoded correctly (#1247)
The added test hopefully reveals the issue - prior to this fix, that `err := f.Get(...)` -> `errors.Is(err, ...)` check passed, because the error value was not ever encoded into a `*GenericError`. Thankfully this was only a test environment bug, and a user discovered it due to writing tests. --- The non-test behavior can be seen split between these two locations: - encoding error reason / data: https://github.com/uber-go/cadence-client/blob/ad91fc718330cb40ff26627fa1d1a6c78c2ceaca/internal/internal_event_handlers.go#L1159-L1163 - constructing the generic error in handleLocalActivityMarker: https://github.com/uber-go/cadence-client/blob/ad91fc718330cb40ff26627fa1d1a6c78c2ceaca/internal/internal_event_handlers.go#L1132-L1135 ... which also show incorrect / pointless string/byte conversions. I've left comments there just to prevent confusion, but these fields are probably worth changing at some point.
1 parent e9ed409 commit 1fed700

File tree

5 files changed

+48
-5
lines changed

5 files changed

+48
-5
lines changed

internal/internal_event_handlers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,10 +1159,10 @@ func (weh *workflowExecutionEventHandlerImpl) ProcessLocalActivityResult(lar *lo
11591159
if lar.err != nil {
11601160
errReason, errDetails := getErrorDetails(lar.err, weh.GetDataConverter())
11611161
lamd.ErrReason = errReason
1162-
lamd.ErrJSON = string(errDetails)
1162+
lamd.ErrJSON = string(errDetails) // TODO: this is not json, nor is it necessarily a valid string
11631163
lamd.Backoff = lar.backoff
11641164
} else {
1165-
lamd.ResultJSON = string(lar.result)
1165+
lamd.ResultJSON = string(lar.result) // TODO: same, not json nor a string
11661166
}
11671167

11681168
// encode marker data

internal/internal_task_pollers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ type (
140140

141141
localActivityResult struct {
142142
result []byte
143-
err error
143+
err error // original error type, possibly an un-encoded user error
144144
task *localActivityTask
145145
backoff time.Duration
146146
}

internal/internal_worker_base.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ type (
6060
laResultHandler func(lar *localActivityResultWrapper)
6161

6262
localActivityResultWrapper struct {
63-
err error
63+
err error // internal error type, possibly containing encoded user-error data
6464
result []byte
6565
attempt int32
6666
backoff time.Duration

internal/internal_workflow_testsuite.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1301,7 +1301,16 @@ func (env *testWorkflowEnvironmentImpl) handleLocalActivityResult(result *localA
13011301
}
13021302

13031303
delete(env.localActivities, activityID)
1304-
lar := &localActivityResultWrapper{err: result.err, result: result.result, backoff: noRetryBackoff}
1304+
var encodedErr error
1305+
if result.err != nil {
1306+
errReason, errDetails := getErrorDetails(result.err, env.GetDataConverter())
1307+
encodedErr = constructError(errReason, errDetails, env.GetDataConverter())
1308+
}
1309+
lar := &localActivityResultWrapper{
1310+
err: encodedErr,
1311+
result: result.result,
1312+
backoff: noRetryBackoff,
1313+
}
13051314
if result.task.retryPolicy != nil && result.err != nil {
13061315
lar.backoff = getRetryBackoff(result, env.Now())
13071316
lar.attempt = task.attempt

internal/internal_workflow_testsuite_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ import (
3131
"testing"
3232
"time"
3333

34+
"github.com/stretchr/testify/assert"
3435
"github.com/stretchr/testify/mock"
36+
"github.com/stretchr/testify/require"
3537
"github.com/stretchr/testify/suite"
3638
"go.uber.org/zap"
3739
"go.uber.org/zap/zaptest"
@@ -3169,3 +3171,35 @@ func (s *WorkflowTestSuiteUnitTest) Test_Regression_ExecuteChildWorkflowWithCanc
31693171
check(0, true, "no err")
31703172
})
31713173
}
3174+
3175+
func TestRegression_LocalActivityErrorEncoding(t *testing.T) {
3176+
// previously not encoded correctly
3177+
s := WorkflowTestSuite{}
3178+
s.SetLogger(zaptest.NewLogger(t))
3179+
env := s.NewTestWorkflowEnvironment()
3180+
sentinel := errors.New("sentinel error value")
3181+
env.RegisterWorkflowWithOptions(func(ctx Context) error {
3182+
ctx = WithLocalActivityOptions(ctx, LocalActivityOptions{ScheduleToCloseTimeout: time.Second})
3183+
err := ExecuteLocalActivity(ctx, func(ctx context.Context) error {
3184+
return sentinel
3185+
}).Get(ctx, nil)
3186+
if errors.Is(err, sentinel) {
3187+
// incorrect path, taken through v0.19.1
3188+
return fmt.Errorf("local activity errors need to be encoded, and must not be `.Is` a specific value: %w", err)
3189+
}
3190+
// correct path
3191+
return sentinel
3192+
}, RegisterWorkflowOptions{Name: "errorsis"})
3193+
env.ExecuteWorkflow("errorsis")
3194+
err := env.GetWorkflowError()
3195+
3196+
// make sure that the right path was chosen, and that the GetWorkflowError returns the same encoded value.
3197+
// GetWorkflowError could... *possibly* return a wrapped original error value, but this seems unnecessarily
3198+
// difficult to maintain long-term, doesn't reflect actual non-test behavior, and might lead to misunderstandings.
3199+
require.Error(t, err) // stop early to avoid confusing NPEs
3200+
var generr *GenericError
3201+
assert.ErrorAs(t, err, &generr, "should be an encoded generic error")
3202+
assert.NotErrorIs(t, err, sentinel, "should not contain a specific value, as this cannot be replayed")
3203+
assert.Contains(t, err.Error(), "sentinel error value", "should contain the user error text")
3204+
assert.NotContains(t, err.Error(), "need to be encoded", "should not contain the wrong-err-type branch message")
3205+
}

0 commit comments

Comments
 (0)