Files
tasks/tests/xdc/chasm_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

236 lines
7.3 KiB
Go

package xdc
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/suite"
commonpb "go.temporal.io/api/common/v1"
enumspb "go.temporal.io/api/enums/v1"
namespacepb "go.temporal.io/api/namespace/v1"
"go.temporal.io/api/serviceerror"
"go.temporal.io/api/workflowservice/v1"
"github.com/hanzoai/tasks/api/adminservice/v1"
"github.com/hanzoai/tasks/chasm"
"github.com/hanzoai/tasks/chasm/lib/tests"
"github.com/hanzoai/tasks/common/debug"
"github.com/hanzoai/tasks/common/dynamicconfig"
"github.com/hanzoai/tasks/common/namespace"
"github.com/hanzoai/tasks/common/testing/testvars"
"github.com/hanzoai/tasks/tests/testcore"
"google.golang.org/protobuf/types/known/durationpb"
)
const (
chasmTestTimeout = 30 * time.Second * debug.TimeoutMultiplier
)
type ChasmSuite struct {
xdcBaseSuite
chasmContext context.Context
}
func TestChasmSuite(t *testing.T) {
t.Parallel()
s := &ChasmSuite{}
s.enableTransitionHistory = true
suite.Run(t, s)
}
func (s *ChasmSuite) SetupSuite() {
s.dynamicConfigOverrides = map[dynamicconfig.Key]any{
dynamicconfig.EnableChasm.Key(): true,
dynamicconfig.NamespaceMinRetentionGlobal.Key(): 1 * time.Second,
}
s.setupSuite()
}
func (s *ChasmSuite) SetupTest() {
s.setupTest()
chasmEngine, err := s.clusters[0].Host().ChasmEngine()
s.Require().NoError(err)
s.Require().NotNil(chasmEngine)
chasmVisibilityMgr := s.clusters[0].Host().ChasmVisibilityManager()
s.Require().NotNil(chasmVisibilityMgr)
s.chasmContext = chasm.NewEngineContext(context.Background(), chasmEngine)
s.chasmContext = chasm.NewVisibilityManagerContext(s.chasmContext, chasmVisibilityMgr)
}
func (s *ChasmSuite) TearDownSuite() {
s.tearDownSuite()
}
func (s *ChasmSuite) TestDeleteExecution_RunningExecution() {
nsName := s.createGlobalNamespace()
nsResp, err := s.clusters[0].FrontendClient().DescribeNamespace(testcore.NewContext(), &workflowservice.DescribeNamespaceRequest{
Namespace: nsName,
})
s.NoError(err)
nsID := nsResp.NamespaceInfo.GetId()
tv := testvars.New(s.T())
storeID := tv.Any().String()
ctx, cancel := context.WithTimeout(s.chasmContext, chasmTestTimeout)
defer cancel()
_, err = tests.NewPayloadStoreHandler(
ctx,
tests.NewPayloadStoreRequest{
NamespaceID: namespace.ID(nsID),
StoreID: storeID,
IDReusePolicy: chasm.BusinessIDReusePolicyRejectDuplicate,
IDConflictPolicy: chasm.BusinessIDConflictPolicyFail,
},
)
s.NoError(err)
chasmRegistry := s.clusters[0].Host().GetCHASMRegistry()
archetypeID, ok := chasmRegistry.ComponentIDFor(&tests.PayloadStore{})
s.True(ok)
archetype, ok := chasmRegistry.ComponentFqnByID(archetypeID)
s.True(ok)
describeExecutionRequest := &adminservice.DescribeMutableStateRequest{
Namespace: nsName,
Execution: &commonpb.WorkflowExecution{
WorkflowId: storeID,
},
Archetype: archetype,
}
_, err = s.clusters[0].AdminClient().DescribeMutableState(testcore.NewContext(), describeExecutionRequest)
s.NoError(err)
s.Eventually(func() bool {
_, err = s.clusters[1].AdminClient().DescribeMutableState(testcore.NewContext(), describeExecutionRequest)
return err == nil
}, 10*time.Second, 100*time.Millisecond)
err = tests.DeletePayloadStoreHandler(
ctx,
tests.DeletePayloadStoreRequest{
NamespaceID: namespace.ID(nsID),
StoreID: storeID,
Reason: "xdc test deletion",
Identity: "test-identity",
},
)
s.NoError(err)
// Active cluster should fully delete the execution.
s.Eventually(func() bool {
_, err = s.clusters[0].AdminClient().DescribeMutableState(testcore.NewContext(), describeExecutionRequest)
return errors.As(err, new(*serviceerror.NotFound))
}, 10*time.Second, 100*time.Millisecond)
// Standby cluster receives the close via replication but won't process
// the DeleteExecutionTask itself; it will be cleaned up by the retention timer.
// Verify the execution is terminated on the standby.
s.Eventually(func() bool {
resp, err := s.clusters[1].AdminClient().DescribeMutableState(testcore.NewContext(), describeExecutionRequest)
if err != nil {
// Execution may already be gone if replication of delete happened.
return errors.As(err, new(*serviceerror.NotFound))
}
return resp.GetDatabaseMutableState().GetExecutionState().GetStatus() == enumspb.WORKFLOW_EXECUTION_STATUS_TERMINATED
}, 10*time.Second, 100*time.Millisecond)
}
func (s *ChasmSuite) TestRetentionTimer() {
nsName := s.createGlobalNamespace()
nsResp, err := s.clusters[0].FrontendClient().DescribeNamespace(testcore.NewContext(), &workflowservice.DescribeNamespaceRequest{
Namespace: nsName,
})
s.NoError(err)
nsID := nsResp.NamespaceInfo.GetId()
tv := testvars.New(s.T())
storeID := tv.Any().String()
ctx, cancel := context.WithTimeout(s.chasmContext, chasmTestTimeout)
defer cancel()
_, err = tests.NewPayloadStoreHandler(
ctx,
tests.NewPayloadStoreRequest{
NamespaceID: namespace.ID(nsID),
StoreID: storeID,
IDReusePolicy: chasm.BusinessIDReusePolicyRejectDuplicate,
IDConflictPolicy: chasm.BusinessIDConflictPolicyFail,
},
)
s.NoError(err)
chasmRegistry := s.clusters[0].Host().GetCHASMRegistry()
archetypeID, ok := chasmRegistry.ComponentIDFor(&tests.PayloadStore{})
s.True(ok)
archetype, ok := chasmRegistry.ComponentFqnByID(archetypeID)
s.True(ok)
describeExecutionRequest := &adminservice.DescribeMutableStateRequest{
Namespace: nsName,
Execution: &commonpb.WorkflowExecution{
WorkflowId: storeID,
},
Archetype: archetype,
}
_, err = s.clusters[0].AdminClient().DescribeMutableState(testcore.NewContext(), describeExecutionRequest)
s.NoError(err)
s.Eventually(func() bool {
// Wait for it to be replicated to the standby cluster
_, err = s.clusters[1].AdminClient().DescribeMutableState(testcore.NewContext(), describeExecutionRequest)
return err == nil
}, 10*time.Second, 100*time.Millisecond)
// Reduce namespace retention to trigger deletion
retention := 3 * time.Second
_, err = s.clusters[0].FrontendClient().UpdateNamespace(testcore.NewContext(), &workflowservice.UpdateNamespaceRequest{
Namespace: nsName,
Config: &namespacepb.NamespaceConfig{
WorkflowExecutionRetentionTtl: durationpb.New(retention),
},
})
s.NoError(err)
// Wait for ns update to be replicated
s.Eventually(func() bool {
// Wait for it to be replicated to the standby cluster
resp, err := s.clusters[1].FrontendClient().DescribeNamespace(testcore.NewContext(), &workflowservice.DescribeNamespaceRequest{
Namespace: nsName,
})
s.NoError(err)
return resp.GetConfig().GetWorkflowExecutionRetentionTtl().AsDuration() == retention
}, 10*time.Second, 100*time.Millisecond)
// Wait for ns registry refresh
time.Sleep(2 * testcore.NamespaceCacheRefreshInterval) //nolint:forbidigo
// Close the execution and validate it's deleted on both clusters.
_, err = tests.ClosePayloadStoreHandler(
ctx,
tests.ClosePayloadStoreRequest{
NamespaceID: namespace.ID(nsID),
StoreID: storeID,
},
)
s.NoError(err)
for _, cluster := range []*testcore.TestCluster{s.clusters[0], s.clusters[1]} {
s.Eventually(func() bool {
// Wait for replication, retention period, and retention timer task processing.
_, err = cluster.AdminClient().DescribeMutableState(testcore.NewContext(), describeExecutionRequest)
return errors.As(err, new(*serviceerror.NotFound))
}, 10*time.Second, 100*time.Millisecond)
}
}