package conga import ( "context" "errors" "io" "testing" "git.golem15.com/golem15/summercms/modules/diary" "github.com/riverqueue/river/rivertype" ) // TestWorkerStats: runAttempt counts every attempt of the running worker: // done on nil, failed on an error or a panic, in flight while it runs, and // handed back when Stop cancelled it; a nil worker has no counters. func TestWorkerStats(t *testing.T) { // The panicking attempt logs its stack; keep it out of the test output. t.Cleanup(diary.Reset) diary.Bootstrap(io.Discard, diary.Options{}) m := &Manager{} counters := &workerCounters{} m.counters.Store(counters) w := &Worker{counters: counters} row := &rivertype.JobRow{ID: 1, Kind: "acme_probe", Attempt: 1, MaxAttempts: 3} ctx := context.Background() for range 3 { if err := m.runAttempt(ctx, row, func(context.Context) error { return nil }); err != nil { t.Fatal(err) } } boom := errors.New("boom") if err := m.runAttempt(ctx, row, func(context.Context) error { return boom }); !errors.Is(err, boom) { t.Fatalf("failing attempt returned %v", err) } if err := m.runAttempt(ctx, row, func(context.Context) error { panic("kaboom") }); err == nil { t.Fatal("a panic did not become an error") } started, release := make(chan struct{}), make(chan struct{}) done := make(chan error, 1) go func() { done <- m.runAttempt(ctx, row, func(context.Context) error { close(started) <-release return nil }) }() <-started if got := w.Stats(); got.InFlight != 1 || got.Done != 3 || got.Failed != 2 { t.Fatalf("while running: %+v, want 1 in flight, 3 done, 2 failed", got) } close(release) if err := <-done; err != nil { t.Fatal(err) } // Stop cancels the running attempts: they are handed back, not failed. counters.stopping.Store(true) cctx, cancel := context.WithCancel(ctx) cancel() if err := m.runAttempt(cctx, row, func(c context.Context) error { return c.Err() }); err == nil { t.Fatal("cancelled attempt returned nil") } // An error unrelated to the cancellation while stopping still fails. if err := m.runAttempt(ctx, row, func(context.Context) error { return boom }); err == nil { t.Fatal("failing attempt returned nil") } want := WorkerStats{Done: 4, Failed: 3, InFlight: 0, HandedBack: 1} if got := w.Stats(); got != want { t.Fatalf("Stats() = %+v, want %+v", got, want) } var none *Worker if got := none.Stats(); got != (WorkerStats{}) { t.Fatalf("nil worker Stats() = %+v", got) } if got := (&Worker{}).Stats(); got != (WorkerStats{}) { t.Fatalf("worker without counters Stats() = %+v", got) } // Without a running worker, attempts run and nothing is counted. idle := &Manager{} if err := idle.runAttempt(ctx, row, func(context.Context) error { return nil }); err != nil { t.Fatal(err) } }