Refactor: improve code quality and worker flow
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"moku-backend/internal/jobs"
|
||||
)
|
||||
|
||||
func TestRunnerProcessesRegisteredJob(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
store := &fakeJobStore{
|
||||
job: &jobs.Job{
|
||||
ID: "job-1",
|
||||
Kind: jobs.KindBootstrapStructureMaterialize,
|
||||
Payload: []byte(`{"installationId":"installation-1"}`),
|
||||
},
|
||||
cancel: cancel,
|
||||
}
|
||||
runner := NewRunner(store, slog.New(slog.NewTextHandler(io.Discard, nil)), 0)
|
||||
|
||||
handlerCalled := false
|
||||
runner.Register(jobs.KindBootstrapStructureMaterialize, func(ctx context.Context, job jobs.Job) error {
|
||||
handlerCalled = true
|
||||
if job.ID != "job-1" {
|
||||
t.Fatalf("expected job id job-1, got %s", job.ID)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := runner.Run(ctx); err != nil {
|
||||
t.Fatalf("runner returned error: %v", err)
|
||||
}
|
||||
|
||||
if !handlerCalled {
|
||||
t.Fatal("expected handler to be called")
|
||||
}
|
||||
if len(store.succeeded) != 1 || store.succeeded[0] != "job-1" {
|
||||
t.Fatalf("expected job to be marked succeeded once, got %#v", store.succeeded)
|
||||
}
|
||||
if len(store.failed) != 0 {
|
||||
t.Fatalf("expected no failed jobs, got %#v", store.failed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerMarksFailedWhenHandlerErrors(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
store := &fakeJobStore{
|
||||
job: &jobs.Job{
|
||||
ID: "job-2",
|
||||
Kind: jobs.KindBootstrapStructureMaterialize,
|
||||
},
|
||||
cancel: cancel,
|
||||
}
|
||||
runner := NewRunner(store, slog.New(slog.NewTextHandler(io.Discard, nil)), 0)
|
||||
runner.Register(jobs.KindBootstrapStructureMaterialize, func(ctx context.Context, job jobs.Job) error {
|
||||
return errors.New("boom")
|
||||
})
|
||||
|
||||
if err := runner.Run(ctx); err != nil {
|
||||
t.Fatalf("runner returned error: %v", err)
|
||||
}
|
||||
|
||||
if len(store.succeeded) != 0 {
|
||||
t.Fatalf("expected no succeeded jobs, got %#v", store.succeeded)
|
||||
}
|
||||
if len(store.failed) != 1 {
|
||||
t.Fatalf("expected one failed job, got %#v", store.failed)
|
||||
}
|
||||
if store.failed[0].jobID != "job-2" {
|
||||
t.Fatalf("expected failed job id job-2, got %#v", store.failed[0])
|
||||
}
|
||||
if !strings.Contains(store.failed[0].failure, "boom") {
|
||||
t.Fatalf("expected failure to mention handler error, got %#v", store.failed[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerMarksFailedWhenHandlerMissing(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
store := &fakeJobStore{
|
||||
job: &jobs.Job{
|
||||
ID: "job-3",
|
||||
Kind: "unknown.kind",
|
||||
},
|
||||
cancel: cancel,
|
||||
}
|
||||
runner := NewRunner(store, slog.New(slog.NewTextHandler(io.Discard, nil)), 0)
|
||||
|
||||
if err := runner.Run(ctx); err != nil {
|
||||
t.Fatalf("runner returned error: %v", err)
|
||||
}
|
||||
|
||||
if len(store.failed) != 1 {
|
||||
t.Fatalf("expected one failed job, got %#v", store.failed)
|
||||
}
|
||||
if !strings.Contains(store.failed[0].failure, "no handler registered") {
|
||||
t.Fatalf("expected missing handler failure, got %#v", store.failed[0])
|
||||
}
|
||||
}
|
||||
|
||||
type fakeJobStore struct {
|
||||
mu sync.Mutex
|
||||
job *jobs.Job
|
||||
claimed bool
|
||||
succeeded []string
|
||||
failed []fakeFailure
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
type fakeFailure struct {
|
||||
jobID string
|
||||
failure string
|
||||
}
|
||||
|
||||
func (store *fakeJobStore) ClaimNext(ctx context.Context) (*jobs.Job, error) {
|
||||
store.mu.Lock()
|
||||
defer store.mu.Unlock()
|
||||
|
||||
if store.claimed || store.job == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
store.claimed = true
|
||||
job := *store.job
|
||||
return &job, nil
|
||||
}
|
||||
|
||||
func (store *fakeJobStore) MarkSucceeded(ctx context.Context, jobID string) error {
|
||||
store.mu.Lock()
|
||||
store.succeeded = append(store.succeeded, jobID)
|
||||
store.mu.Unlock()
|
||||
|
||||
if store.cancel != nil {
|
||||
store.cancel()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *fakeJobStore) MarkFailed(ctx context.Context, jobID, failure string) error {
|
||||
store.mu.Lock()
|
||||
store.failed = append(store.failed, fakeFailure{jobID: jobID, failure: failure})
|
||||
store.mu.Unlock()
|
||||
|
||||
if store.cancel != nil {
|
||||
store.cancel()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user