github.com/ngicks/gokugen@v0.0.5/cron/cron_like_rescheduler_test.go (about)

     1  package cron_test
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/ngicks/gokugen"
    10  	"github.com/ngicks/gokugen/cron"
    11  	"github.com/ngicks/type-param-common/iterator"
    12  	syncparam "github.com/ngicks/type-param-common/sync-param"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  var _ cron.RowLike = fakeRowLike{}
    17  
    18  type fakeRowLike struct {
    19  	nextTime time.Time
    20  	command  []string
    21  }
    22  
    23  func (r fakeRowLike) NextSchedule(now time.Time) (time.Time, error) {
    24  	return r.nextTime, nil
    25  }
    26  
    27  func (r fakeRowLike) GetCommand() []string {
    28  	return r.command
    29  }
    30  
    31  var _ gokugen.Task = &fakeTask{}
    32  
    33  type fakeTask struct {
    34  	ctx gokugen.SchedulerContext
    35  }
    36  
    37  func (t fakeTask) Cancel() (cancelled bool) {
    38  	return true
    39  }
    40  
    41  func (t fakeTask) CancelWithReason(err error) (cancelled bool) {
    42  	return true
    43  }
    44  
    45  func (t fakeTask) GetScheduledTime() time.Time {
    46  	return t.ctx.ScheduledTime()
    47  }
    48  func (t fakeTask) IsCancelled() (cancelled bool) {
    49  	return
    50  }
    51  func (t fakeTask) IsDone() (done bool) {
    52  	return
    53  }
    54  
    55  var _ cron.Scheduler = &fakeScheduler{}
    56  
    57  type fakeScheduler struct {
    58  	mu      sync.Mutex
    59  	idx     int
    60  	ctxList []gokugen.SchedulerContext
    61  }
    62  
    63  func (s *fakeScheduler) runAllTasks() (results []error) {
    64  	var cloned []gokugen.SchedulerContext
    65  	func() {
    66  		s.mu.Lock()
    67  		defer s.mu.Unlock()
    68  		cloned = make([]gokugen.SchedulerContext, len(s.ctxList))
    69  		copy(cloned, s.ctxList)
    70  	}()
    71  
    72  	iter := iterator.Iterator[gokugen.SchedulerContext]{
    73  		DeIterator: iterator.FromSlice(cloned),
    74  	}.SkipN(s.idx)
    75  
    76  	s.idx = iter.Len()
    77  
    78  	for next, ok := iter.Next(); ok; next, ok = iter.Next() {
    79  		_, err := next.Work()(context.TODO(), next.ScheduledTime())
    80  		results = append(results, err)
    81  	}
    82  	return
    83  }
    84  
    85  func (s *fakeScheduler) Schedule(ctx gokugen.SchedulerContext) (gokugen.Task, error) {
    86  	s.mu.Lock()
    87  	defer s.mu.Unlock()
    88  	s.ctxList = append(s.ctxList, ctx)
    89  	return fakeTask{ctx: ctx}, nil
    90  }
    91  
    92  func prepareController(
    93  	shouldReschedule func(workErr error, callCount int) bool,
    94  ) (
    95  	rowLike *fakeRowLike,
    96  	scheduler *fakeScheduler,
    97  	registry *syncparam.Map[string, gokugen.WorkFnWParam],
    98  	cronLikeRescheduler *cron.CronLikeRescheduler,
    99  ) {
   100  	rowLike = &fakeRowLike{command: []string{"foo", "bar", "baz"}}
   101  	scheduler = &fakeScheduler{
   102  		ctxList: make([]gokugen.SchedulerContext, 0),
   103  	}
   104  	registry = new(syncparam.Map[string, gokugen.WorkFnWParam])
   105  	registry.LoadOrStore("foo", func(taskCtx context.Context, scheduled time.Time, param any) (any, error) {
   106  		return nil, nil
   107  	})
   108  
   109  	cronLikeRescheduler = cron.NewCronLikeRescheduler(
   110  		rowLike,
   111  		time.Now(),
   112  		shouldReschedule,
   113  		scheduler,
   114  		registry,
   115  	)
   116  	return
   117  }
   118  
   119  func TestController(t *testing.T) {
   120  	t.Parallel()
   121  	t.Run("rescheduling is controlled by shouldReschedule passed by user.", func(t *testing.T) {
   122  		_, scheduler, _, cronLikeRescheduler := prepareController(
   123  			func(workErr error, callCount int) bool { return callCount == 0 },
   124  		)
   125  
   126  		err := cronLikeRescheduler.Schedule()
   127  		if err != nil {
   128  			t.Fatal(err)
   129  		}
   130  
   131  		results := scheduler.runAllTasks()
   132  		require.Len(t, results, 1)
   133  		for _, res := range results {
   134  			if res != nil {
   135  				t.Fatal(res)
   136  			}
   137  		}
   138  
   139  		results = scheduler.runAllTasks()
   140  		require.Len(t, results, 1)
   141  		for _, res := range results {
   142  			if res != nil {
   143  				t.Fatal(res)
   144  			}
   145  		}
   146  
   147  		results = scheduler.runAllTasks()
   148  		require.Len(t, results, 0)
   149  	})
   150  
   151  }