package engine import ( "context" "path/filepath" "testing" "time" "github.com/google/uuid" "git.cloudinit.dev/coreci/orca/internal/model" "git.cloudinit.dev/coreci/orca/internal/store" ) func newTestExecutor(t *testing.T) (*Executor, func()) { t.Helper() path := filepath.Join(t.TempDir(), "test.db") db, err := store.Open(path) if err != nil { t.Fatalf("open db: %v", err) } ex := NewExecutor(store.NewJobRepo(db), store.NewTaskRepo(db), nil) return ex, func() { _ = db.Close() } } func TestExecutor_Submit_Success(t *testing.T) { ex, cleanup := newTestExecutor(t) defer cleanup() ctx := context.Background() spec := []byte(`{"command":"/bin/echo","args":["hello"]}`) jobID, err := ex.Submit(ctx, spec) if err != nil { t.Fatalf("Submit: %v", err) } if jobID == "" { t.Fatal("Submit: empty jobID") } status, err := ex.Status(ctx, jobID) if err != nil { t.Fatalf("Status: %v", err) } if status != string(model.JobStatusComplete) { t.Errorf("Status: got %q, want %q", status, model.JobStatusComplete) } } func TestExecutor_Submit_MissingCommand(t *testing.T) { ex, cleanup := newTestExecutor(t) defer cleanup() _, err := ex.Submit(context.Background(), []byte(`{"name":"x"}`)) if err == nil { t.Fatal("Submit: expected error for missing command, got nil") } } func TestExecutor_Submit_MalformedJSON(t *testing.T) { ex, cleanup := newTestExecutor(t) defer cleanup() _, err := ex.Submit(context.Background(), []byte(`{bad json`)) if err == nil { t.Fatal("Submit: expected error for malformed JSON, got nil") } } func TestExecutor_Submit_FailingCommand(t *testing.T) { ex, cleanup := newTestExecutor(t) defer cleanup() ctx := context.Background() jobID, err := ex.Submit(ctx, []byte(`{"command":"/bin/false"}`)) if err == nil { t.Fatal("Submit failing command: expected error, got nil") } if jobID == "" { t.Fatal("Submit failing command: empty jobID") } status, err := ex.Status(ctx, jobID) if err != nil { t.Fatalf("Status: %v", err) } if status != string(model.JobStatusFailed) { t.Errorf("Status: got %q, want %q", status, model.JobStatusFailed) } } func TestExecutor_Status_NotFound(t *testing.T) { ex, cleanup := newTestExecutor(t) defer cleanup() _, err := ex.Status(context.Background(), "nonexistent-job-id") if err == nil { t.Fatal("Status: expected error for missing job, got nil") } } func TestExecutor_Run_Success(t *testing.T) { ex, cleanup := newTestExecutor(t) defer cleanup() ctx := context.Background() job := &model.Job{ ID: uuid.NewString(), Name: "run-success", Spec: "{}", Status: model.JobStatusPending, } specs := []TaskSpec{{Name: "echo", Command: "/bin/echo", Args: []string{"hi"}}} if err := ex.Run(ctx, job, specs); err != nil { t.Fatalf("Run: %v", err) } got, err := ex.Status(ctx, job.ID) if err != nil { t.Fatalf("Status: %v", err) } if got != string(model.JobStatusComplete) { t.Errorf("Status: got %q, want %q", got, model.JobStatusComplete) } } func TestExecutor_Run_ContextCancel(t *testing.T) { ex, cleanup := newTestExecutor(t) defer cleanup() ctx, cancel := context.WithCancel(context.Background()) job := &model.Job{ ID: uuid.NewString(), Name: "run-cancel", Spec: "{}", Status: model.JobStatusPending, } specs := []TaskSpec{{Name: "sleep", Command: "/bin/sleep", Args: []string{"10"}}} go func() { time.Sleep(100 * time.Millisecond) cancel() }() err := ex.Run(ctx, job, specs) if err == nil { t.Fatal("Run: expected error after context cancel, got nil") } status, sErr := ex.Status(context.Background(), job.ID) if sErr != nil { t.Fatalf("Status after cancel: %v", sErr) } if status == string(model.JobStatusComplete) { t.Errorf("Status: got %q, want not complete (task should have been killed)", status) } }