Files

166 lines
4.4 KiB
Go

package automations
import (
"context"
"errors"
"testing"
"time"
"git.yeij.top/AskaEth/Cyrene_v2/internal/bus"
)
func TestValidateCron(t *testing.T) {
t.Parallel()
s, err := New(Config{}, Deps{Bus: bus.New()})
if err != nil {
t.Fatalf("New: %v", err)
}
tests := []struct {
name string
expr string
cfg Config
want bool
}{
{name: "standard 5-field", expr: "*/5 * * * *", want: true},
{name: "standard 5-field with names", expr: "0 9 * * MON", want: true},
{name: "descriptor every", expr: "@every 1h30m", want: true},
{name: "descriptor midnight", expr: "@midnight", want: true},
{name: "6-field rejected by default", expr: "* * * * * *", want: false},
{name: "6-field accepted with seconds", expr: "*/10 * * * * *", cfg: Config{WithSeconds: true}, want: true},
{name: "garbage rejected", expr: "not a cron", want: false},
{name: "empty rejected", expr: "", want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ss := s
if tt.cfg.WithSeconds {
ss, err = New(tt.cfg, Deps{Bus: bus.New()})
if err != nil {
t.Fatalf("New: %v", err)
}
}
err := ss.ValidateCron(tt.expr)
if got := err == nil; got != tt.want {
t.Fatalf("ValidateCron(%q) err = %v, wantValid=%v", tt.expr, err, tt.want)
}
})
}
}
func TestAddSchedule_InvalidCronRejected(t *testing.T) {
t.Parallel()
s, err := New(Config{}, Deps{Bus: bus.New()})
if err != nil {
t.Fatalf("New: %v", err)
}
act, err := NewFuncAction("x", func(context.Context, any) (any, error) { return nil, nil })
if err != nil {
t.Fatalf("NewFuncAction: %v", err)
}
if _, err := s.AddSchedule(context.Background(), "not a cron", act); !errors.Is(err, ErrInvalidCron) {
t.Fatalf("err = %v, want ErrInvalidCron", err)
}
if _, err := s.AddSchedule(context.Background(), "* * * * * *", act); !errors.Is(err, ErrInvalidCron) {
t.Fatalf("6-field without seconds: err = %v, want ErrInvalidCron", err)
}
}
func TestScheduler_RejectsNilAction(t *testing.T) {
t.Parallel()
s, err := New(Config{}, Deps{Bus: bus.New()})
if err != nil {
t.Fatalf("New: %v", err)
}
if _, err := s.AddSchedule(context.Background(), "*/5 * * * *", nil); !errors.Is(err, ErrNilAction) {
t.Fatalf("err = %v, want ErrNilAction", err)
}
}
func TestScheduler_StartStopIdempotent(t *testing.T) {
t.Parallel()
s, err := New(Config{}, Deps{Bus: bus.New()})
if err != nil {
t.Fatalf("New: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := s.Start(ctx); err != nil {
t.Fatalf("Start: %v", err)
}
if err := s.Start(ctx); err != nil {
t.Fatalf("second Start: %v", err)
}
if err := s.Stop(ctx); err != nil {
t.Fatalf("Stop: %v", err)
}
if err := s.Stop(ctx); err != nil {
t.Fatalf("second Stop: %v", err)
}
}
func TestScheduler_FiresOnDue(t *testing.T) {
t.Parallel()
b := bus.New()
defer b.Close()
got := make(chan bus.Event, 1)
unsub, err := b.Subscribe(context.Background(), SubjectResult, func(_ context.Context, ev bus.Event) error {
got <- ev
return nil
})
if err != nil {
t.Fatalf("subscribe: %v", err)
}
defer unsub()
s, err := New(Config{}, Deps{Bus: b})
if err != nil {
t.Fatalf("New: %v", err)
}
act, err := NewFuncAction("tick", func(context.Context, any) (any, error) { return "ok", nil })
if err != nil {
t.Fatalf("NewFuncAction: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if _, err := s.AddSchedule(ctx, "@every 50ms", act); err != nil {
t.Fatalf("AddSchedule: %v", err)
}
if err := s.Start(ctx); err != nil {
t.Fatalf("Start: %v", err)
}
select {
case ev := <-got:
res, ok := ev.Payload.(Result)
if !ok {
t.Fatalf("payload type = %T, want Result", ev.Payload)
}
if res.Kind != "schedule" || res.Action != "tick" || res.Output != "ok" {
t.Fatalf("result = %+v, want {schedule tick ok}", res)
}
case <-time.After(3 * time.Second):
t.Fatal("schedule did not fire")
}
if err := s.Stop(ctx); err != nil {
t.Fatalf("Stop: %v", err)
}
}
func TestScheduler_WithSecondsAcceptsSixField(t *testing.T) {
t.Parallel()
s, err := New(Config{WithSeconds: true}, Deps{Bus: bus.New()})
if err != nil {
t.Fatalf("New: %v", err)
}
act, err := NewFuncAction("tick", func(context.Context, any) (any, error) { return nil, nil })
if err != nil {
t.Fatalf("NewFuncAction: %v", err)
}
if _, err := s.AddSchedule(context.Background(), "*/1 * * * * *", act); err != nil {
t.Fatalf("6-field with seconds rejected: %v", err)
}
}