Files
tasks/chasm/lib/scheduler/scheduler_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

168 lines
5.3 KiB
Go

package scheduler_test
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
commonpb "go.temporal.io/api/common/v1"
enumspb "go.temporal.io/api/enums/v1"
schedulepb "go.temporal.io/api/schedule/v1"
schedulespb "github.com/hanzoai/tasks/api/schedule/v1"
"github.com/hanzoai/tasks/chasm"
"github.com/hanzoai/tasks/chasm/lib/scheduler"
schedulerpb "github.com/hanzoai/tasks/chasm/lib/scheduler/gen/schedulerpb/v1"
"github.com/hanzoai/tasks/common/testing/protorequire"
"google.golang.org/protobuf/types/known/timestamppb"
)
func TestListInfo(t *testing.T) {
scheduler, ctx, _ := setupSchedulerForTest(t)
// Generator maintains the FutureActionTimes list, set that up first.
generator := scheduler.Generator.Get(ctx)
expectedFutureTimes := []*timestamppb.Timestamp{timestamppb.Now(), timestamppb.Now()}
generator.FutureActionTimes = expectedFutureTimes
listInfo := scheduler.ListInfo(ctx)
// Should return a populated info block.
require.NotNil(t, listInfo)
require.NotNil(t, listInfo.Spec)
require.NotEmpty(t, listInfo.Spec.Interval)
protorequire.ProtoEqual(t, listInfo.Spec.Interval[0], scheduler.Schedule.Spec.Interval[0])
require.NotNil(t, listInfo.WorkflowType)
require.NotEmpty(t, listInfo.FutureActionTimes)
require.Equal(t, expectedFutureTimes, listInfo.FutureActionTimes)
}
func TestCreateSchedulerFromMigration(t *testing.T) {
now := time.Now().UTC()
_, _, node := setupSchedulerForTest(t)
req := &schedulerpb.CreateFromMigrationStateRequest{
NamespaceId: namespaceID,
State: &schedulerpb.SchedulerMigrationState{
SchedulerState: &schedulerpb.SchedulerState{
Schedule: defaultSchedule(),
Info: &schedulepb.ScheduleInfo{ActionCount: 10},
Namespace: namespace,
NamespaceId: namespaceID,
ScheduleId: scheduleID,
ConflictToken: 42,
},
GeneratorState: &schedulerpb.GeneratorState{
LastProcessedTime: timestamppb.New(now),
},
InvokerState: &schedulerpb.InvokerState{
BufferedStarts: []*schedulespb.BufferedStart{
{
NominalTime: timestamppb.New(now),
ActualTime: timestamppb.New(now),
OverlapPolicy: enumspb.SCHEDULE_OVERLAP_POLICY_SKIP,
RequestId: "req-1",
WorkflowId: "wf-1",
},
{
NominalTime: timestamppb.New(now.Add(time.Minute)),
ActualTime: timestamppb.New(now.Add(time.Minute)),
OverlapPolicy: enumspb.SCHEDULE_OVERLAP_POLICY_ALLOW_ALL,
RequestId: "req-2",
WorkflowId: "wf-2",
},
},
},
Backfillers: map[string]*schedulerpb.BackfillerState{
"bf-1": {
BackfillId: "bf-1",
Request: &schedulerpb.BackfillerState_BackfillRequest{
BackfillRequest: &schedulepb.BackfillRequest{
StartTime: timestamppb.New(now.Add(-time.Hour)),
EndTime: timestamppb.New(now),
OverlapPolicy: enumspb.SCHEDULE_OVERLAP_POLICY_ALLOW_ALL,
},
},
},
},
LastCompletionResult: &schedulerpb.LastCompletionResult{
Success: &commonpb.Payload{Data: []byte("result-data")},
},
SearchAttributes: map[string]*commonpb.Payload{
"CustomAttr": {Data: []byte("attr-value")},
},
Memo: map[string]*commonpb.Payload{
"MemoKey": {Data: []byte("memo-value")},
},
},
}
ctx := chasm.NewMutableContext(context.Background(), node)
sched, err := scheduler.CreateSchedulerFromMigration(ctx, req)
require.NoError(t, err)
// Scheduler state
require.Equal(t, namespace, sched.Namespace)
require.Equal(t, namespaceID, sched.NamespaceId)
require.Equal(t, scheduleID, sched.ScheduleId)
require.Equal(t, int64(42), sched.ConflictToken)
require.False(t, sched.Closed)
// Generator
generator := sched.Generator.Get(ctx)
require.Equal(t, now, generator.LastProcessedTime.AsTime())
// Invoker buffered starts
invoker := sched.Invoker.Get(ctx)
require.Len(t, invoker.BufferedStarts, 2)
require.Equal(t, "req-1", invoker.BufferedStarts[0].RequestId)
require.Equal(t, "req-2", invoker.BufferedStarts[1].RequestId)
// Backfillers
require.Len(t, sched.Backfillers, 1)
bf := sched.Backfillers["bf-1"].Get(ctx)
require.Equal(t, "bf-1", bf.BackfillId)
// Last completion result
lastResult := sched.LastCompletionResult.Get(ctx)
require.Equal(t, []byte("result-data"), lastResult.Success.Data)
require.NoError(t, node.SetRootComponent(sched))
_, err = node.CloseTransaction()
require.NoError(t, err)
}
func TestCreateSchedulerFromMigration_EmptyState(t *testing.T) {
_, _, node := setupSchedulerForTest(t)
req := &schedulerpb.CreateFromMigrationStateRequest{
NamespaceId: namespaceID,
State: &schedulerpb.SchedulerMigrationState{
SchedulerState: &schedulerpb.SchedulerState{
Schedule: defaultSchedule(),
Info: &schedulepb.ScheduleInfo{},
Namespace: namespace,
NamespaceId: namespaceID,
ScheduleId: scheduleID,
ConflictToken: 1,
},
GeneratorState: &schedulerpb.GeneratorState{},
InvokerState: &schedulerpb.InvokerState{},
},
}
ctx := chasm.NewMutableContext(context.Background(), node)
sched, err := scheduler.CreateSchedulerFromMigration(ctx, req)
require.NoError(t, err)
require.Equal(t, int64(1), sched.ConflictToken)
require.Empty(t, sched.Backfillers)
invoker := sched.Invoker.Get(ctx)
require.Empty(t, invoker.BufferedStarts)
require.NoError(t, node.SetRootComponent(sched))
_, err = node.CloseTransaction()
require.NoError(t, err)
}