Files
wehub-resource-sync 1b8708893a
Security Scan / tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:12:26 +08:00

233 lines
6.9 KiB
Go

package worker
import (
"context"
"errors"
"os"
"path/filepath"
"sync"
"github.com/mudler/LocalAI/core/config"
"github.com/mudler/LocalAI/pkg/model"
"github.com/mudler/LocalAI/pkg/system"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
// fakeInstaller records every call and lets each test choose the behaviour
// for a given model name. Modelled on the real modelInstaller signature so
// the prefetch loop can't accidentally widen its dependency on the gallery
// package without breaking the contract here too.
type fakeInstaller struct {
mu sync.Mutex
calls []string
behavior map[string]error // model name -> error to return; missing key = nil (success)
}
func newFakeInstaller() *fakeInstaller {
return &fakeInstaller{behavior: map[string]error{}}
}
func (f *fakeInstaller) install(
_ context.Context,
_ []config.Gallery,
_ []config.Gallery,
_ *system.SystemState,
_ *model.ModelLoader,
name string,
) error {
f.mu.Lock()
defer f.mu.Unlock()
f.calls = append(f.calls, name)
return f.behavior[name]
}
func (f *fakeInstaller) recorded() []string {
f.mu.Lock()
defer f.mu.Unlock()
out := make([]string, len(f.calls))
copy(out, f.calls)
return out
}
// minimalSystemState yields a SystemState pointed at a fresh tempdir. The
// prefetch loop never touches it directly (the fake installer ignores it),
// but the production signature requires a non-nil value and we want the
// tests to mirror the real wiring as closely as possible.
func minimalSystemState(modelsDir string) *system.SystemState {
ss, err := system.GetSystemState(system.WithModelPath(modelsDir))
Expect(err).ToNot(HaveOccurred())
return ss
}
var _ = Describe("prefetchModels", func() {
var (
tmp string
ss *system.SystemState
galleries = `[{"name":"localai","url":"file:///dev/null"}]`
)
BeforeEach(func() {
var err error
tmp, err = os.MkdirTemp("", "worker-prefetch-test")
Expect(err).ToNot(HaveOccurred())
ss = minimalSystemState(tmp)
})
AfterEach(func() {
_ = os.RemoveAll(tmp)
})
It("installs every configured model before returning (happy path)", func() {
f := newFakeInstaller()
cfg := &Config{
PrefetchModels: []string{"llama-3.2-1b", "phi-3-mini"},
Galleries: galleries,
ModelsPath: tmp,
}
prefetchModels(context.Background(), cfg, ss, nil, nil, f.install)
Expect(f.recorded()).To(Equal([]string{"llama-3.2-1b", "phi-3-mini"}))
})
It("is a no-op when the file is already present on disk (idempotency contract)", func() {
// Drop a fake artifact where the gallery would have placed it; the
// real installer's downloader stats the path and short-circuits when
// the SHA matches (or any time SHA is unset). We simulate that here
// by having the fake installer assert the file exists before claiming
// success — exactly the invariant we rely on for restart-against-PVC.
modelFile := filepath.Join(tmp, "llama-3.2-1b.gguf")
Expect(os.WriteFile(modelFile, []byte("already-here"), 0o600)).To(Succeed())
var installerCalls int
var sawFile bool
fake := func(
_ context.Context,
_, _ []config.Gallery,
_ *system.SystemState,
_ *model.ModelLoader,
_ string,
) error {
installerCalls++
if _, err := os.Stat(modelFile); err == nil {
sawFile = true
}
// Real installer returns nil on cache hit. Mirror that.
return nil
}
cfg := &Config{
PrefetchModels: []string{"llama-3.2-1b"},
Galleries: galleries,
ModelsPath: tmp,
}
prefetchModels(context.Background(), cfg, ss, nil, nil, fake)
Expect(installerCalls).To(Equal(1), "installer is always invoked; its downloader handles the skip")
Expect(sawFile).To(BeTrue(), "fake installer saw the pre-existing artifact, matching the restart-on-warm-PVC path")
// File untouched.
data, err := os.ReadFile(modelFile)
Expect(err).ToNot(HaveOccurred())
Expect(string(data)).To(Equal("already-here"))
})
It("logs and continues when an individual install fails (non-fatal contract)", func() {
f := newFakeInstaller()
f.behavior["broken"] = errors.New("gallery unreachable")
cfg := &Config{
PrefetchModels: []string{"good-1", "broken", "good-2"},
Galleries: galleries,
ModelsPath: tmp,
}
// Function must return normally despite the middle install failing.
Expect(func() {
prefetchModels(context.Background(), cfg, ss, nil, nil, f.install)
}).ToNot(Panic())
// Subsequent models still get attempted — failures don't short-circuit.
Expect(f.recorded()).To(Equal([]string{"good-1", "broken", "good-2"}))
})
It("does nothing when PrefetchModels is empty", func() {
f := newFakeInstaller()
cfg := &Config{ModelsPath: tmp, Galleries: galleries}
prefetchModels(context.Background(), cfg, ss, nil, nil, f.install)
Expect(f.recorded()).To(BeEmpty())
})
It("trims whitespace and skips empty entries", func() {
f := newFakeInstaller()
cfg := &Config{
PrefetchModels: []string{" llama ", "", " ", "phi"},
Galleries: galleries,
ModelsPath: tmp,
}
prefetchModels(context.Background(), cfg, ss, nil, nil, f.install)
Expect(f.recorded()).To(Equal([]string{"llama", "phi"}))
})
It("skips prefetch when LOCALAI_GALLERIES is malformed (non-fatal)", func() {
f := newFakeInstaller()
cfg := &Config{
PrefetchModels: []string{"llama"},
Galleries: `not-json`,
ModelsPath: tmp,
}
prefetchModels(context.Background(), cfg, ss, nil, nil, f.install)
Expect(f.recorded()).To(BeEmpty(), "no installer call when galleries can't be parsed")
})
It("skips prefetch when no galleries are configured (non-fatal)", func() {
f := newFakeInstaller()
cfg := &Config{
PrefetchModels: []string{"llama"},
Galleries: `[]`,
ModelsPath: tmp,
}
prefetchModels(context.Background(), cfg, ss, nil, nil, f.install)
Expect(f.recorded()).To(BeEmpty())
})
})
var _ = Describe("parseModelGalleries", func() {
It("returns empty slice on empty input", func() {
g, err := parseModelGalleries("")
Expect(err).ToNot(HaveOccurred())
Expect(g).To(BeEmpty())
})
It("returns empty slice on whitespace-only input", func() {
g, err := parseModelGalleries(" \n\t ")
Expect(err).ToNot(HaveOccurred())
Expect(g).To(BeEmpty())
})
It("parses a valid JSON list", func() {
g, err := parseModelGalleries(`[{"name":"localai","url":"github:mudler/LocalAI/gallery/index.yaml@master"}]`)
Expect(err).ToNot(HaveOccurred())
Expect(g).To(HaveLen(1))
Expect(g[0].Name).To(Equal("localai"))
})
It("returns an error on malformed JSON", func() {
_, err := parseModelGalleries(`{this is not json`)
Expect(err).To(HaveOccurred())
})
})
var _ = Describe("normalizePrefetchList", func() {
It("drops empty and whitespace-only entries", func() {
got := normalizePrefetchList([]string{"a", "", " ", " b ", "\tc\n"})
Expect(got).To(Equal([]string{"a", "b", "c"}))
})
It("returns empty slice on empty input", func() {
Expect(normalizePrefetchList(nil)).To(BeEmpty())
Expect(normalizePrefetchList([]string{})).To(BeEmpty())
})
})