Skip to content

Commit 4dd1ed8

Browse files
authoredMay 31, 2024··
Fix bugs when using a custom FailureConverter in tests (#1490)
Fix bugs when using a custom `FailureConverter` in tests Follow up to #1484. 1. We weren't correctly threading the failure converter down to activities and child workflows, so they were still using the default converter. 2. The `NonRetryable` flag on the protobuf failure type wasn't being honored for custom errors.
1 parent bcfa85a commit 4dd1ed8

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed
 

‎internal/internal_workflow_testsuite.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ func (env *testWorkflowEnvironmentImpl) newTestWorkflowEnvironmentForChild(param
374374
childEnv.testWorkflowEnvironmentShared = env.testWorkflowEnvironmentShared
375375
childEnv.workerOptions = env.workerOptions
376376
childEnv.dataConverter = params.DataConverter
377+
childEnv.failureConverter = env.failureConverter
377378
childEnv.registry = env.registry
378379
childEnv.detachedChildWaitDisabled = env.detachedChildWaitDisabled
379380

@@ -1403,8 +1404,14 @@ func (env *testWorkflowEnvironmentImpl) executeActivityWithRetryForTest(
14031404

14041405
// check if a retry is needed
14051406
if request, ok := result.(*workflowservice.RespondActivityTaskFailedRequest); ok && parameters.RetryPolicy != nil {
1407+
failure := request.GetFailure()
1408+
1409+
if failure.GetApplicationFailureInfo().GetNonRetryable() {
1410+
break
1411+
}
1412+
14061413
p := fromProtoRetryPolicy(parameters.RetryPolicy)
1407-
backoff := getRetryBackoffWithNowTime(p, task.GetAttempt(), env.failureConverter.FailureToError(request.GetFailure()), env.Now(), expireTime)
1414+
backoff := getRetryBackoffWithNowTime(p, task.GetAttempt(), env.failureConverter.FailureToError(failure), env.Now(), expireTime)
14081415
if backoff > 0 {
14091416
// need a retry
14101417
waitCh := make(chan struct{})
@@ -1987,6 +1994,7 @@ func (env *testWorkflowEnvironmentImpl) newTestActivityTaskHandler(taskQueue str
19871994
MetricsHandler: env.metricsHandler,
19881995
Logger: env.logger,
19891996
UserContext: env.workerOptions.BackgroundActivityContext,
1997+
FailureConverter: env.failureConverter,
19901998
DataConverter: dataConverter,
19911999
WorkerStopChannel: env.workerStopChannel,
19922000
ContextPropagators: env.contextPropagators,

‎internal/workflow_testsuite_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@ import (
2828
"context"
2929
"errors"
3030
"strings"
31+
"sync/atomic"
3132
"testing"
3233
"time"
3334

3435
"github.com/stretchr/testify/assert"
36+
failurepb "go.temporal.io/api/failure/v1"
37+
"go.temporal.io/sdk/converter"
3538

3639
"github.com/stretchr/testify/mock"
3740
"github.com/stretchr/testify/require"
@@ -533,3 +536,64 @@ func TestMockCallWrapperNotBefore(t *testing.T) {
533536
require.ErrorAs(t, env.GetWorkflowError(), &expectedErr)
534537
require.ErrorContains(t, expectedErr, "Must not be called before")
535538
}
539+
540+
func TestCustomFailureConverter(t *testing.T) {
541+
t.Parallel()
542+
543+
var suite WorkflowTestSuite
544+
env := suite.NewTestWorkflowEnvironment()
545+
env.SetFailureConverter(testFailureConverter{
546+
fallback: defaultFailureConverter,
547+
})
548+
549+
var calls atomic.Int32
550+
activity := func(context.Context) error {
551+
_ = calls.Add(1)
552+
return testCustomError{}
553+
}
554+
env.RegisterActivity(activity)
555+
556+
env.ExecuteWorkflow(func(ctx Context) error {
557+
ctx = WithActivityOptions(ctx, ActivityOptions{
558+
StartToCloseTimeout: time.Hour,
559+
})
560+
return ExecuteActivity(ctx, activity).Get(ctx, nil)
561+
})
562+
require.True(t, env.IsWorkflowCompleted())
563+
564+
// Failure converter should've reconstructed the custom error type.
565+
require.True(t, errors.As(env.GetWorkflowError(), &testCustomError{}))
566+
567+
// Activity should've only been called once because the failure converter
568+
// set the NonRetryable flag.
569+
require.Equal(t, 1, int(calls.Load()))
570+
}
571+
572+
type testCustomError struct{}
573+
574+
func (testCustomError) Error() string { return "this is a custom error type" }
575+
576+
type testFailureConverter struct {
577+
fallback converter.FailureConverter
578+
}
579+
580+
func (c testFailureConverter) ErrorToFailure(err error) *failurepb.Failure {
581+
if errors.As(err, &testCustomError{}) {
582+
return &failurepb.Failure{
583+
FailureInfo: &failurepb.Failure_ApplicationFailureInfo{
584+
ApplicationFailureInfo: &failurepb.ApplicationFailureInfo{
585+
Type: "CUSTOM ERROR",
586+
NonRetryable: true,
587+
},
588+
},
589+
}
590+
}
591+
return c.fallback.ErrorToFailure(err)
592+
}
593+
594+
func (c testFailureConverter) FailureToError(failure *failurepb.Failure) error {
595+
if failure.GetApplicationFailureInfo().GetType() == "CUSTOM ERROR" {
596+
return testCustomError{}
597+
}
598+
return c.fallback.FailureToError(failure)
599+
}

0 commit comments

Comments
 (0)
Please sign in to comment.