sdk-go
sdk-go copied to clipboard
Getting a strange error when writing tests which does not occur in real temporal deployment
Expected Behavior
should not get the illegalContext error
Actual Behavior
Running tool: /opt/homebrew/bin/go test -timeout 30s -run ^Test_Subscription_Workflow_Tests$ -testify.m ^(Test_ExampleWorkflow)$ example.com -timeout=5m
2025/05/29 11:13:56 INFO Received activation signal
2025/05/29 11:13:56 INFO ExecuteChildWorkflow WorkflowType ActivationWorkflow
2025/05/29 11:13:56 INFO Activation workflow started
2025/05/29 11:13:56 DEBUG Auto fire timer TimerID 0 TimerDuration 1s TimeSkipped 1s
2025/05/29 11:13:56 INFO Received activation signal
2025/05/29 11:13:56 INFO ExecuteChildWorkflow WorkflowType ActivationWorkflow
--- FAIL: Test_Subscription_Workflow_Tests (0.00s)
--- FAIL: Test_Subscription_Workflow_Tests/Test_ExampleWorkflow (0.00s)
/Users/johansvedlundnordstrom/dev/reproduce/workflow_testsuite.go:1236: FAIL: DummyActivity(string,workflows.DummyActivityInput)
at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/workflow_testsuite.go:414 /Users/johansvedlundnordstrom/dev/reproduce/some_test.go:110]
/Users/johansvedlundnordstrom/dev/reproduce/workflow_testsuite.go:1236: FAIL: 0 out of 1 expectation(s) were met.
The code you are testing needs to make 1 more call(s).
at: [/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/workflow_testsuite.go:1236 /Users/johansvedlundnordstrom/dev/reproduce/some_test.go:27 /Users/johansvedlundnordstrom/go/pkg/mod/github.com/stretchr/[email protected]/suite/suite.go:180 /opt/homebrew/Cellar/go/1.24.1/libexec/src/runtime/panic.go:792 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow.go:782 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/workflow.go:1490 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow.go:1029 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/context.go:336 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/context.go:206 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow.go:593 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:2288 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:2921 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:896 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:836 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:626 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:570 /Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/workflow_testsuite.go:832 /Users/johansvedlundnordstrom/dev/reproduce/some_test.go:119]
/Users/johansvedlundnordstrom/dev/reproduce/internal_workflow.go:782: test panicked: getState: illegal access from outside of workflow context
goroutine 36 [running]:
runtime/debug.Stack()
/opt/homebrew/Cellar/go/1.24.1/libexec/src/runtime/debug/stack.go:26 +0x64
github.com/stretchr/testify/suite.failOnPanic(0x1400047c540, {0x104d33640, 0x104f15540})
/Users/johansvedlundnordstrom/go/pkg/mod/github.com/stretchr/[email protected]/suite/suite.go:89 +0x38
github.com/stretchr/testify/suite.Run.func1.1()
/Users/johansvedlundnordstrom/go/pkg/mod/github.com/stretchr/[email protected]/suite/suite.go:188 +0x22c
panic({0x104d33640?, 0x104f15540?})
/opt/homebrew/Cellar/go/1.24.1/libexec/src/runtime/panic.go:792 +0x124
go.temporal.io/sdk/internal.assertNotInReadOnlyStateCancellation({0x104f23af0?, 0x140000a2060?})
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow.go:782 +0xa4
go.temporal.io/sdk/internal.(*workflowEnvironmentInterceptor).NewTimerWithOptions.func2({0x1400007c018?, 0x104a6fdd8?}, 0xb8?)
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/workflow.go:1490 +0x44
go.temporal.io/sdk/internal.(*channelImpl).Close(0x140001141c0?)
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow.go:1029 +0xb4
go.temporal.io/sdk/internal.(*cancelCtx).cancel(0x14000114180, 0x1, {0x104f18580, 0x1400035d470})
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/context.go:336 +0xb0
go.temporal.io/sdk/internal.WithCancel.func1()
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/context.go:206 +0x34
go.temporal.io/sdk/internal.(*syncWorkflowDefinition).Execute.func2()
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow.go:593 +0x28
go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).RequestCancelExternalWorkflow(0x14000146008, {0x104b6e6fd, 0x16}, {0x14000152000, 0x15}, {0x14000130060, 0x1b}, 0x1400000c180)
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:2288 +0x2c8
go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).RequestCancelExternalWorkflow.(*testWorkflowEnvironmentImpl).cancelWorkflow.(*testWorkflowEnvironmentImpl).cancelWorkflowByID.func4()
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:2921 +0x40
go.temporal.io/sdk/internal.(*testCallbackHandle).processCallback(0x140000e7448)
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:896 +0xc4
go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).startMainLoop(0x14000291188)
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:836 +0x110
go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).executeWorkflowInternal(0x14000291188, 0x0, {0x1050e298c, 0xe}, 0x0)
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:626 +0x3a8
go.temporal.io/sdk/internal.(*testWorkflowEnvironmentImpl).executeWorkflow(0x14000291188, {0x104d4f880, 0x104f10748}, {0x0, 0x0, 0x0})
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/internal_workflow_testsuite.go:570 +0x118
go.temporal.io/sdk/internal.(*TestWorkflowEnvironment).ExecuteWorkflow(0x14000409960?, {0x104d4f880?, 0x104f10748?}, {0x0?, 0x2?, 0x2?})
/Users/johansvedlundnordstrom/go/pkg/mod/go.temporal.io/[email protected]/internal/workflow_testsuite.go:832 +0xb8
example%2ecom.(*WorkflowTestSuite).Test_ExampleWorkflow(0x140002b05a0)
/Users/johansvedlundnordstrom/dev/reproduce/some_test.go:119 +0x194
reflect.Value.call({0x14000444800?, 0x1400018f328?, 0x14000471001?}, {0x104b5e4cd, 0x4}, {0x1400018af20, 0x1, 0x104714b44?})
/opt/homebrew/Cellar/go/1.24.1/libexec/src/reflect/value.go:584 +0x978
reflect.Value.Call({0x14000444800?, 0x1400018f328?, 0x66a?}, {0x1400018af20?, 0x105375c88?, 0x105578e40?})
/opt/homebrew/Cellar/go/1.24.1/libexec/src/reflect/value.go:368 +0x94
github.com/stretchr/testify/suite.Run.func1(0x1400047c540)
/Users/johansvedlundnordstrom/go/pkg/mod/github.com/stretchr/[email protected]/suite/suite.go:202 +0x394
testing.tRunner(0x1400047c540, 0x140002b06c0)
/opt/homebrew/Cellar/go/1.24.1/libexec/src/testing/testing.go:1792 +0xe4
created by testing.(*T).Run in goroutine 35
/opt/homebrew/Cellar/go/1.24.1/libexec/src/testing/testing.go:1851 +0x374
FAIL
exit status 1
FAIL example.com 0.208s
Steps to Reproduce the Problem
run the following test
package workflows
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"go.temporal.io/sdk/testsuite"
"go.temporal.io/sdk/workflow"
)
type WorkflowTestSuite struct {
suite.Suite
testsuite.WorkflowTestSuite
testEnv *testsuite.TestWorkflowEnvironment
}
func (s *WorkflowTestSuite) SetupTest() {
s.testEnv = s.NewTestWorkflowEnvironment()
}
func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) {
s.testEnv.AssertExpectations(s.T())
}
func Test_Subscription_Workflow_Tests(t *testing.T) {
suite.Run(t, new(WorkflowTestSuite))
}
func MyCoolWorkflow(ctx workflow.Context) error {
selector := workflow.NewSelector(ctx)
var activationWorkflow *workflow.Execution
selector.AddReceive(workflow.GetSignalChannel(ctx, "activate"), func(c workflow.ReceiveChannel, more bool) {
c.Receive(ctx, nil)
workflow.GetLogger(ctx).Info("Received activation signal")
if activationWorkflow != nil {
workflow.RequestCancelExternalWorkflow(ctx, activationWorkflow.ID, activationWorkflow.RunID)
}
cwf := workflow.ExecuteChildWorkflow(
ctx,
ActivationWorkflow,
)
var res workflow.Execution
if err := cwf.GetChildWorkflowExecution().Get(ctx, &res); err != nil {
workflow.GetLogger(ctx).Error("Failed to start child workflow", "error", err)
return
}
activationWorkflow = &res
selector.AddFuture(cwf, func(f workflow.Future) {
if err := f.Get(ctx, nil); err != nil {
workflow.GetLogger(ctx).Error("Child workflow failed", "error", err)
} else {
workflow.GetLogger(ctx).Info("Child workflow completed successfully")
}
activationWorkflow = nil
})
})
for selector.HasPending() || activationWorkflow != nil {
selector.Select(ctx)
}
return nil
}
type DummyActivityInput struct{}
func DummyActivity(ctx context.Context, d DummyActivityInput) error {
return nil
}
func ActivationWorkflow(ctx workflow.Context) error {
ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
StartToCloseTimeout: time.Minute,
})
workflow.GetLogger(ctx).Info("Activation workflow started")
if err := workflow.Sleep(ctx, time.Hour); err != nil {
workflow.GetLogger(ctx).Error("Activation workflow failed", "error", err)
return err
}
workflow.GetLogger(ctx).Info("Activation workflow completed")
var err error
if errors.Is(ctx.Err(), workflow.ErrCanceled) {
newCtx, _ := workflow.NewDisconnectedContext(ctx)
err = workflow.ExecuteActivity(newCtx, DummyActivity, DummyActivityInput{}).Get(newCtx, nil)
workflow.GetLogger(ctx).Info("Activation workflow canceled, running dummy activity in disconnected context")
} else {
err = workflow.ExecuteActivity(ctx, DummyActivity, DummyActivityInput{}).Get(ctx, nil)
}
return err
}
func (s *WorkflowTestSuite) Test_ExampleWorkflow() {
s.testEnv.OnActivity(DummyActivity, mock.Anything, DummyActivityInput{}).Return(nil).Once()
s.testEnv.RegisterWorkflow(ActivationWorkflow)
s.testEnv.RegisterDelayedCallback(func() {
s.testEnv.SignalWorkflow("activate", nil)
}, 0)
s.testEnv.RegisterDelayedCallback(func() {
s.testEnv.SignalWorkflow("activate", nil)
}, time.Second)
s.testEnv.ExecuteWorkflow(MyCoolWorkflow)
s.NoError(s.testEnv.GetWorkflowError())
s.testEnv.IsWorkflowCompleted()
}
Specifications
running on Mac silicon but it does not really matter where you run it
Thanks for the bug report! Looks like there's an issue with the test environment propagating ctx from inside the selector.AddReceive
I think this is still an issue
Yes, I unfortunately haven't been able to prioritize this issue, hoping I can get to it soon