package mill import ( "errors" "io" "log/slog" "testing" "tangled.org/core/api/tangled" "tangled.org/core/spindle/models" millproto "tangled.org/core/spindle/mill/proto" ) type testPlacementPlanner struct { requirements []string err error } func (p testPlacementPlanner) Requirements(tangled.Pipeline_Workflow, tangled.Pipeline) ([]string, error) { return p.requirements, p.err } func TestMillEngineStoresCanonicalPlacementRequirements(t *testing.T) { m := New(slog.New(slog.NewTextHandler(io.Discard, nil)), Config{}) eng := NewEngineWithPlanner("microvm", m, testPlacementPlanner{ requirements: []string{"runner/qemu", "image/nixos", "runner/qemu"}, }) wf, err := eng.InitWorkflow(tangled.Pipeline_Workflow{Name: "build"}, tangled.Pipeline{}) if err != nil { t.Fatalf("InitWorkflow() error = %v", err) } got := wf.Data.(*millWorkflowState).RequiredCapabilities want := []string{"image/nixos", "runner/qemu"} if len(got) != len(want) || got[0] != want[0] || got[1] != want[1] { t.Fatalf("required capabilities = %v, want %v", got, want) } } func TestMillEngineRejectsInvalidPlacementRequirements(t *testing.T) { m := New(slog.New(slog.NewTextHandler(io.Discard, nil)), Config{}) plannerErr := errors.New("invalid image") eng := NewEngineWithPlanner("microvm", m, testPlacementPlanner{err: plannerErr}) if _, err := eng.InitWorkflow(tangled.Pipeline_Workflow{}, tangled.Pipeline{}); !errors.Is(err, plannerErr) { t.Fatalf("InitWorkflow() error = %v, want wrapped %v", err, plannerErr) } eng = NewEngineWithPlanner("microvm", m, testPlacementPlanner{requirements: []string{"image/nixos arm"}}) if _, err := eng.InitWorkflow(tangled.Pipeline_Workflow{}, tangled.Pipeline{}); err == nil { t.Fatal("InitWorkflow() accepted whitespace-containing capability") } } func TestRankCandidatesIntersectsLabelsAndEngineCapabilities(t *testing.T) { m := New(slog.New(slog.NewTextHandler(io.Discard, nil)), Config{}) incapable := addCandidateSession(t, m, "low-load-incapable", []string{"region/eu"}, 0.05, nil) incapable.snapshot.Engines["dummy"].Capabilities = []string{"image/nixos-aarch64"} wrongRegion := addCandidateSession(t, m, "wrong-region", []string{"region/us"}, 0.10, nil) wrongRegion.snapshot.Engines["dummy"].Capabilities = []string{"image/nixos", "runner/qemu"} partial := addCandidateSession(t, m, "partial", []string{"region/eu"}, 0.20, nil) partial.snapshot.Engines["dummy"].Capabilities = []string{"image/nixos"} eligible := addCandidateSession(t, m, "eligible", []string{"region/eu"}, 0.70, nil) eligible.snapshot.Engines["dummy"].Capabilities = []string{"runner/qemu", "image/nixos"} got := m.rankCandidates("dummy", []string{"region/eu"}, []string{"image/nixos", "runner/qemu"}) assertRankedNodes(t, got, []string{"eligible"}) } func TestBidWithMissingEngineCapabilitySendsNoReserve(t *testing.T) { m := New(slog.New(slog.NewTextHandler(io.Discard, nil)), Config{}) reserveSent := false sess := addCandidateSession(t, m, "arm", nil, 0.05, scriptedEncoder(func(msg *millproto.Message) error { if msg.GetReserveSeat() != nil { reserveSent = true } return nil })) sess.snapshot.Engines["dummy"].Capabilities = []string{"image/nixos-aarch64"} wf := testWorkflow("build") wf.Data.(*millWorkflowState).RequiredCapabilities = []string{"image/nixos"} lease, err := m.bid(t.Context(), "dummy", wf) if err != nil { t.Fatalf("bid() error = %v", err) } if lease != nil { t.Fatalf("bid() lease = %v, want nil", lease) } if reserveSent { t.Fatal("bid() sent ReserveSeat to an engine missing a required capability") } } func TestRequiredCapabilitiesWithoutMillStateAreEmpty(t *testing.T) { if got := requiredCapabilities(&models.Workflow{}); got != nil { t.Fatalf("requiredCapabilities() = %v, want nil", got) } }