Files
tasks/tests/admin_test.go
zeekay 5a601d081b rebrand: go.temporal.io/server -> github.com/hanzoai/tasks
Full module path rebrand from Temporal to Hanzo Tasks:
- go.mod module: github.com/hanzoai/tasks
- All internal Go imports updated (1906 files)
- cmd/server renamed to cmd/tasksd
- Binary name: tasksd (was temporal-server)
- Docker images: hanzoai/tasks (was temporalio/server)
- README rewritten with Hanzo Tasks branding
- LLM.md created with integration notes
- Makefile, goreleaser, CI workflows updated
- go.temporal.io/sdk and go.temporal.io/api imports preserved (separate repos)

Fork of Temporal.io (MIT license).
2026-03-19 10:36:40 -07:00

156 lines
5.6 KiB
Go

package tests
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/suite"
commonpb "go.temporal.io/api/common/v1"
sdkclient "go.temporal.io/sdk/client"
"go.temporal.io/sdk/workflow"
"github.com/hanzoai/tasks/api/adminservice/v1"
persistencespb "github.com/hanzoai/tasks/api/persistence/v1"
"github.com/hanzoai/tasks/chasm"
"github.com/hanzoai/tasks/common/dynamicconfig"
"github.com/hanzoai/tasks/common/primitives/timestamp"
"github.com/hanzoai/tasks/common/testing/testvars"
"github.com/hanzoai/tasks/tests/testcore"
)
type AdminTestSuite struct {
testcore.FunctionalTestBase
testContext context.Context
}
func TestAdminTestSuite(t *testing.T) {
t.Parallel()
suite.Run(t, new(AdminTestSuite))
}
func (s *AdminTestSuite) SetupSuite() {
// Call parent setup to initialize the test cluster
s.FunctionalTestBase.SetupSuite()
s.testContext = context.Background()
}
func (s *AdminTestSuite) TestAdminRebuildMutableState_ChasmDisabled() {
rebuildMutableStateWorkflowHelper(s, false)
}
func (s *AdminTestSuite) TestAdminRebuildMutableState_ChasmEnabled() {
cleanup := s.OverrideDynamicConfig(dynamicconfig.EnableChasm, true)
defer cleanup()
configValues := s.GetTestCluster().Host().DcClient().GetValue(dynamicconfig.EnableChasm.Key())
s.NotEmpty(configValues, "EnableChasm config should be set")
configValue, _ := configValues[0].Value.(bool)
s.True(configValue, "EnableChasm config should be true")
rebuildMutableStateWorkflowHelper(s, true)
}
// common test helper
func rebuildMutableStateWorkflowHelper(s *AdminTestSuite, testWithChasm bool) {
tv := testvars.New(s.T())
workflowFn := func(ctx workflow.Context) error {
var randomUUID string
err := workflow.SideEffect(
ctx,
func(workflow.Context) any { return uuid.New().String() },
).Get(&randomUUID)
s.NoError(err)
_ = workflow.Sleep(ctx, 10*time.Minute)
return nil
}
s.SdkWorker().RegisterWorkflow(workflowFn)
workflowID := tv.Any().String()
workflowOptions := sdkclient.StartWorkflowOptions{
ID: workflowID,
TaskQueue: s.TaskQueue(),
WorkflowRunTimeout: 20 * time.Second,
}
ctx, cancel := context.WithTimeout(s.testContext, 30*time.Second)
defer cancel()
workflowRun, err := s.SdkClient().ExecuteWorkflow(s.testContext, workflowOptions, workflowFn)
s.NoError(err)
runID := workflowRun.GetRunID()
// there are total 6 events, 3 state transitions
// 1. WorkflowExecutionStarted
// 2. WorkflowTaskScheduled
//
// 3. WorkflowTaskStarted
//
// 4. WorkflowTaskCompleted
// 5. MarkerRecord
// 6. TimerStarted
var response1 *adminservice.DescribeMutableStateResponse
for {
response1, err = s.AdminClient().DescribeMutableState(ctx, &adminservice.DescribeMutableStateRequest{
Namespace: s.Namespace().String(),
Execution: &commonpb.WorkflowExecution{
WorkflowId: workflowID,
RunId: runID,
},
Archetype: chasm.WorkflowArchetype,
})
s.NoError(err)
if response1.DatabaseMutableState.ExecutionInfo.StateTransitionCount == 3 {
// Note: ChasmNodes may be empty even with CHASM enabled, so we only check if the rebuild can be performed,
// and not checking whether it is rebuildable because ChasmNodes are present.
if !testWithChasm {
s.Empty(response1.DatabaseMutableState.ChasmNodes, "CHASM-disabled workflows should not have ChasmNodes")
}
break
}
time.Sleep(20 * time.Millisecond) //nolint:forbidigo
}
_, err = s.AdminClient().RebuildMutableState(ctx, &adminservice.RebuildMutableStateRequest{
Namespace: s.Namespace().String(),
Execution: &commonpb.WorkflowExecution{
WorkflowId: workflowID,
RunId: runID,
},
})
s.NoError(err)
response2, err := s.AdminClient().DescribeMutableState(ctx, &adminservice.DescribeMutableStateRequest{
Namespace: s.Namespace().String(),
Execution: &commonpb.WorkflowExecution{
WorkflowId: workflowID,
RunId: runID,
},
Archetype: chasm.WorkflowArchetype,
})
s.NoError(err)
s.Equal(response1.DatabaseMutableState.ExecutionInfo.VersionHistories, response2.DatabaseMutableState.ExecutionInfo.VersionHistories)
s.Equal(response1.DatabaseMutableState.ExecutionInfo.StateTransitionCount, response2.DatabaseMutableState.ExecutionInfo.StateTransitionCount)
s.Equal(response1.DatabaseMutableState.ExecutionState.CreateRequestId, response2.DatabaseMutableState.ExecutionState.CreateRequestId)
s.Equal(response1.DatabaseMutableState.ExecutionState.RunId, response2.DatabaseMutableState.ExecutionState.RunId)
s.Equal(response1.DatabaseMutableState.ExecutionState.State, response2.DatabaseMutableState.ExecutionState.State)
s.Equal(response1.DatabaseMutableState.ExecutionState.Status, response2.DatabaseMutableState.ExecutionState.Status)
// From transition history perspective, Rebuild is considered as an update to the workflow and updates
// all sub state machines in the workflow, which includes the workflow ExecutionState.
s.Equal(&persistencespb.VersionedTransition{
NamespaceFailoverVersion: response1.DatabaseMutableState.ExecutionState.LastUpdateVersionedTransition.NamespaceFailoverVersion,
TransitionCount: response1.DatabaseMutableState.ExecutionInfo.StateTransitionCount + 1,
}, response2.DatabaseMutableState.ExecutionState.LastUpdateVersionedTransition)
// Rebuild explicitly sets start time, thus start time will change after rebuild.
s.NotNil(response1.DatabaseMutableState.ExecutionState.StartTime)
s.NotNil(response2.DatabaseMutableState.ExecutionState.StartTime)
timeBefore := timestamp.TimeValue(response1.DatabaseMutableState.ExecutionState.StartTime)
timeAfter := timestamp.TimeValue(response2.DatabaseMutableState.ExecutionState.StartTime)
s.False(timeAfter.Before(timeBefore))
}