Skip to content

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

@josvegit

Description

@josvegit

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions