Is it possible to run unit tests with RegisterDelayedCallback
that executes the same cadence workflow?
I have the following code that runs a workflow twice, the first execution saves a callback token and the second execution retrieves the saved token to complete the activity asynchronously.
workflow.go
package workflow
import (
"context"
"encoding/base64"
"fmt"
"go.uber.org/cadence/activity"
"go.uber.org/cadence/workflow"
)
type WorkflowImpl struct {
worker.Worker
client.Client
}
func (w WorkflowImpl) TActivity(ctx context.Context, action string) error {
fmt.Println("TActivity started", action)
if action != "Approved" {
activityInfo := activity.GetInfo(ctx)
callbackToken := base64.StdEncoding.EncodeToString(activityInfo.TaskToken)
fmt.Println("save callbackToken", callbackToken)
// Saves callbackToken.
return activity.ErrResultPending
}
fmt.Println("Approved")
// Do some approved things.
// Get saved callback token.
// Call w.CompleteActivity() with the saved callback token.
return nil
}
func (w WorkflowImpl) TWorkflow(ctx workflow.Context, action string) (result string, err error) {
fmt.Println("TWorkflow started", action)
waitChannel := workflow.NewChannel(ctx)
workflow.Go(ctx, func(ctx workflow.Context) {
if err := workflow.ExecuteActivity(ctx, w.TActivity, action).Get(ctx, nil); err != nil {
// Do nothing, keep workflow open.
return
}
waitChannel.Send(ctx, "OK")
})
var signal string
waitChannel.Receive(ctx, &signal)
return signal, nil
}
workflow_test.go
package workflow_test
import (
"time"
"go.uber.org/cadence/worker"
)
func (s *UnitTestSuite) Test_TWorkflow() {
env := s.NewTestWorkflowEnvironment()
worker := workflow.WorkflowImpl{
Worker: ...
Client: ...
}
s.worker = &worker
env.RegisterActivity(s.worker.TActivity)
// Delay second TWorkflow.
env.RegisterDelayedCallback(func() {
env.ExecuteWorkflow(s.worker.TWorkflow, "Approved")
}, time.Second*2)
env.ExecuteWorkflow(s.worker.TWorkflow, "Noop")
s.True(env.IsWorkflowCompleted())
s.NoError(env.GetWorkflowError())
}
The above code is not complete, it doesn't save the callback token and call CompleteActivity. For the purpose of testing the sequence I just expect to see the logs of the workflow started and the activity started twice, but I am not seeing that. After the first workflow started, and without logs for any activity, the test hanged until timeout.
What is missing or is it possible to execute the same workflow twice like this?
env.RegisterDelayedCallback(func() {
env.ExecuteWorkflow(s.worker.TWorkflow, "Approved")
}, time.Second*2)
There is a deadlock here. The env
is locked when a callback is running (see the source code). And the callback wants to execute a workflow on the same env
, which needs to acquire the same lock on the env
(see the source code).
Let's try to break the deadlock by running the callback in a new goroutine:
env.RegisterDelayedCallback(func() {
go env.ExecuteWorkflow(s.worker.TWorkflow, "Approved")
}, time.Second*2)
Now we get a panic:
panic: Current TestWorkflowEnvironment is used to execute s.worker.TWorkflow. Please create a new TestWorkflowEnvironment for s.worker.TWorkflow.
Currently, the TestWorkflowEnvironment
can not run 2 workflows that are not parent-child. See the issue that tracks the task to Make TestWorkflowEnvironment support test multiple workflows.
As the panic message suggested, you have to create a new TestWorkflowEnvironment
to execute another workflow (but I'm not sure does it work for your use case).