sdk-go icon indicating copy to clipboard operation
sdk-go copied to clipboard

Getting a strange error when writing tests which does not occur in real temporal deployment

Open josvegit opened this issue 6 months ago • 1 comments

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

josvegit avatar May 29 '25 09:05 josvegit

Thanks for the bug report! Looks like there's an issue with the test environment propagating ctx from inside the selector.AddReceive

yuandrew avatar May 30 '25 21:05 yuandrew

I think this is still an issue

josvegit avatar Oct 08 '25 20:10 josvegit

Yes, I unfortunately haven't been able to prioritize this issue, hoping I can get to it soon

yuandrew avatar Oct 09 '25 16:10 yuandrew