Files
NianAIGC/backend/internal/jobs/loop_test.go

76 lines
1.6 KiB
Go

package jobs
import (
"context"
"sync"
"testing"
"time"
)
type tickRunner struct {
mu sync.Mutex
calls, active, max int
block chan struct{}
}
func (r *tickRunner) Tick(ctx context.Context, _ string) (TickResult, error) {
r.mu.Lock()
r.calls++
r.active++
if r.active > r.max {
r.max = r.active
}
r.mu.Unlock()
select {
case <-r.block:
case <-ctx.Done():
}
r.mu.Lock()
r.active--
r.mu.Unlock()
return TickResult{}, nil
}
func TestWorkerLoopStartStopAndPreventsOverlap(t *testing.T) {
runner := &tickRunner{block: make(chan struct{})}
loop := NewWorkerLoop(runner, LoopConfig{Interval: time.Millisecond, WorkerID: "loop"})
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
loop.Start(ctx)
deadline := time.After(time.Second)
for {
runner.mu.Lock()
calls := runner.calls
runner.mu.Unlock()
if calls > 0 {
break
}
select {
case <-deadline:
t.Fatal("loop did not tick")
default:
time.Sleep(time.Millisecond)
}
}
time.Sleep(10 * time.Millisecond)
runner.mu.Lock()
if runner.max != 1 || runner.calls != 1 {
t.Fatalf("calls=%d max=%d", runner.calls, runner.max)
}
runner.mu.Unlock()
close(runner.block)
loop.Stop()
loop.Stop()
}
func TestWorkerLoopTickNowSkipsOverlap(t *testing.T) {
r := &tickRunner{block: make(chan struct{})}
l := NewWorkerLoop(r, LoopConfig{WorkerID: "w"})
done := make(chan struct{})
go func() { _, _ = l.Tick(context.Background()); close(done) }()
time.Sleep(time.Millisecond)
if _, ok := l.Tick(context.Background()); ok {
t.Fatal("overlap was not skipped")
}
close(r.block)
<-done
}