@@ -7285,3 +7285,161 @@ func (s *mutableStateSuite) TestApplyWorkflowExecutionOptionsUpdatedEvent_TimeSk
72857285 })
72867286 }
72877287}
7288+
7289+ func TestGenerateActivityCancelCommandsForClose (t * testing.T ) {
7290+ t .Parallel ()
7291+
7292+ startedClock := & clockspb.VectorClock {ShardId : 1 , Clock : 100 }
7293+
7294+ testCases := []struct {
7295+ name string
7296+ featureEnabled bool
7297+ activities map [int64 ]* persistencespb.ActivityInfo
7298+ expectedQueues map [string ]int // controlQueue -> expected command count
7299+ expectedNoTasks bool
7300+ }{
7301+ {
7302+ name : "activities with control queue and started clock" ,
7303+ featureEnabled : true ,
7304+ activities : map [int64 ]* persistencespb.ActivityInfo {
7305+ 1 : {
7306+ ScheduledEventId : 1 ,
7307+ ActivityId : "act-1" ,
7308+ ActivityType : & commonpb.ActivityType {Name : "type1" },
7309+ WorkerControlTaskQueue : "control-queue-1" ,
7310+ StartedClock : startedClock ,
7311+ Attempt : 1 ,
7312+ },
7313+ },
7314+ expectedQueues : map [string ]int {"control-queue-1" : 1 },
7315+ },
7316+ {
7317+ name : "skips activities without control queue" ,
7318+ featureEnabled : true ,
7319+ activities : map [int64 ]* persistencespb.ActivityInfo {
7320+ 1 : {
7321+ ScheduledEventId : 1 ,
7322+ ActivityId : "act-1" ,
7323+ ActivityType : & commonpb.ActivityType {Name : "type1" },
7324+ StartedClock : startedClock ,
7325+ Attempt : 1 ,
7326+ },
7327+ },
7328+ expectedNoTasks : true ,
7329+ },
7330+ {
7331+ name : "skips activities without started clock" ,
7332+ featureEnabled : true ,
7333+ activities : map [int64 ]* persistencespb.ActivityInfo {
7334+ 1 : {
7335+ ScheduledEventId : 1 ,
7336+ ActivityId : "act-1" ,
7337+ ActivityType : & commonpb.ActivityType {Name : "type1" },
7338+ WorkerControlTaskQueue : "control-queue-1" ,
7339+ Attempt : 1 ,
7340+ },
7341+ },
7342+ expectedNoTasks : true ,
7343+ },
7344+ {
7345+ name : "multiple activities batched by control queue" ,
7346+ featureEnabled : true ,
7347+ activities : map [int64 ]* persistencespb.ActivityInfo {
7348+ 1 : {
7349+ ScheduledEventId : 1 ,
7350+ ActivityId : "act-1" ,
7351+ ActivityType : & commonpb.ActivityType {Name : "type1" },
7352+ WorkerControlTaskQueue : "queue-A" ,
7353+ StartedClock : startedClock ,
7354+ Attempt : 1 ,
7355+ },
7356+ 2 : {
7357+ ScheduledEventId : 2 ,
7358+ ActivityId : "act-2" ,
7359+ ActivityType : & commonpb.ActivityType {Name : "type2" },
7360+ WorkerControlTaskQueue : "queue-A" ,
7361+ StartedClock : startedClock ,
7362+ Attempt : 1 ,
7363+ },
7364+ 3 : {
7365+ ScheduledEventId : 3 ,
7366+ ActivityId : "act-3" ,
7367+ ActivityType : & commonpb.ActivityType {Name : "type3" },
7368+ WorkerControlTaskQueue : "queue-B" ,
7369+ StartedClock : startedClock ,
7370+ Attempt : 1 ,
7371+ },
7372+ },
7373+ expectedQueues : map [string ]int {"queue-A" : 2 , "queue-B" : 1 },
7374+ },
7375+ {
7376+ name : "feature flag disabled - no tasks generated" ,
7377+ featureEnabled : false ,
7378+ activities : map [int64 ]* persistencespb.ActivityInfo {
7379+ 1 : {
7380+ ScheduledEventId : 1 ,
7381+ ActivityId : "act-1" ,
7382+ ActivityType : & commonpb.ActivityType {Name : "type1" },
7383+ WorkerControlTaskQueue : "control-queue-1" ,
7384+ StartedClock : startedClock ,
7385+ Attempt : 1 ,
7386+ },
7387+ },
7388+ expectedNoTasks : true ,
7389+ },
7390+ }
7391+
7392+ for _ , tc := range testCases {
7393+ t .Run (tc .name , func (t * testing.T ) {
7394+ ctrl := gomock .NewController (t )
7395+ mockEventsCache := events .NewMockCache (ctrl )
7396+ mockConfig := tests .NewDynamicConfig ()
7397+ mockConfig .EnableCancelActivityWorkerCommand = dynamicconfig .GetBoolPropertyFn (tc .featureEnabled )
7398+
7399+ mockShard := shard .NewTestContext (
7400+ ctrl ,
7401+ & persistencespb.ShardInfo {ShardId : 0 , RangeId : 1 },
7402+ mockConfig ,
7403+ )
7404+ defer mockShard .StopForTest ()
7405+ reg := hsm .NewRegistry ()
7406+ require .NoError (t , RegisterStateMachine (reg ))
7407+ require .NoError (t , callbacks .RegisterStateMachine (reg ))
7408+ require .NoError (t , nexusoperations .RegisterStateMachines (reg ))
7409+ mockShard .SetStateMachineRegistry (reg )
7410+ mockShard .SetEventsCacheForTesting (mockEventsCache )
7411+
7412+ namespaceEntry := tests .GlobalNamespaceEntry
7413+ mockShard .Resource .NamespaceCache .EXPECT ().GetNamespaceByID (tests .NamespaceID ).Return (namespaceEntry , nil ).AnyTimes ()
7414+ mockShard .Resource .ClusterMetadata .EXPECT ().ClusterNameForFailoverVersion (gomock .Any (), gomock .Any ()).Return (cluster .TestCurrentClusterName ).AnyTimes ()
7415+ mockShard .Resource .ClusterMetadata .EXPECT ().GetCurrentClusterName ().Return (cluster .TestCurrentClusterName ).AnyTimes ()
7416+ mockShard .Resource .ClusterMetadata .EXPECT ().GetClusterID ().Return (int64 (1 )).AnyTimes ()
7417+
7418+ ms := NewMutableState (mockShard , mockEventsCache , log .NewTestLogger (), namespaceEntry , tests .WorkflowID , tests .RunID , time .Now ().UTC ())
7419+ ms .pendingActivityInfoIDs = tc .activities
7420+
7421+ err := ms .GenerateActivityCancelCommandsForClose ()
7422+ require .NoError (t , err )
7423+
7424+ if tc .expectedNoTasks {
7425+ require .Empty (t , ms .InsertTasks [tasks .CategoryOutbound ])
7426+ return
7427+ }
7428+
7429+ // Verify tasks were generated by checking outbound task messages
7430+ var workerCommandTasks []* tasks.WorkerCommandsTask
7431+ for _ , task := range ms .InsertTasks [tasks .CategoryOutbound ] {
7432+ if wct , ok := task .(* tasks.WorkerCommandsTask ); ok {
7433+ workerCommandTasks = append (workerCommandTasks , wct )
7434+ }
7435+ }
7436+
7437+ // Verify each expected queue got the right number of commands
7438+ tasksByQueue := make (map [string ]int )
7439+ for _ , wct := range workerCommandTasks {
7440+ tasksByQueue [wct .Destination ] = len (wct .Commands )
7441+ }
7442+ require .Equal (t , tc .expectedQueues , tasksByQueue )
7443+ })
7444+ }
7445+ }
0 commit comments