mirror of
https://github.com/mudler/LocalAI.git
synced 2026-04-01 13:42:20 -04:00
* feat: add distributed mode (experimental) Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix data races, mutexes, transactions Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactorings Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fixups Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix events and tool stream in agent chat Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * use ginkgo Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(cron): compute correctly time boundaries avoiding re-triggering Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * enhancements, refactorings Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * do not flood of healthy checks Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * do not list obvious backends as text backends Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * tests fixups Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * Drop redundant healthcheck Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * enhancements, refactorings Signed-off-by: Ettore Di Giacinto <mudler@localai.io> --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
100 lines
2.8 KiB
Go
100 lines
2.8 KiB
Go
package gallery_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/mudler/LocalAI/core/gallery"
|
|
)
|
|
|
|
func TestDeleteStagedModelFiles(t *testing.T) {
|
|
t.Run("rejects empty model name", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
err := gallery.DeleteStagedModelFiles(dir, "")
|
|
if err == nil {
|
|
t.Fatal("expected error for empty model name")
|
|
}
|
|
})
|
|
|
|
t.Run("rejects path traversal via ..", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
err := gallery.DeleteStagedModelFiles(dir, "../../etc/passwd")
|
|
if err == nil {
|
|
t.Fatal("expected error for path traversal attempt")
|
|
}
|
|
})
|
|
|
|
t.Run("rejects path traversal via ../foo", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
err := gallery.DeleteStagedModelFiles(dir, "../foo")
|
|
if err == nil {
|
|
t.Fatal("expected error for path traversal attempt")
|
|
}
|
|
})
|
|
|
|
t.Run("removes model subdirectory with all files", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
modelDir := filepath.Join(dir, "my-model", "sd-cpp", "models")
|
|
if err := os.MkdirAll(modelDir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Create model files in subdirectory
|
|
os.WriteFile(filepath.Join(modelDir, "flux.gguf"), []byte("model"), 0o644)
|
|
os.WriteFile(filepath.Join(modelDir, "flux.gguf.mmproj"), []byte("mmproj"), 0o644)
|
|
|
|
err := gallery.DeleteStagedModelFiles(dir, "my-model")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Entire my-model directory should be gone
|
|
if _, err := os.Stat(filepath.Join(dir, "my-model")); !os.IsNotExist(err) {
|
|
t.Fatal("expected model directory to be removed")
|
|
}
|
|
})
|
|
|
|
t.Run("removes single file model", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
modelFile := filepath.Join(dir, "model.gguf")
|
|
os.WriteFile(modelFile, []byte("model"), 0o644)
|
|
|
|
err := gallery.DeleteStagedModelFiles(dir, "model.gguf")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if _, err := os.Stat(modelFile); !os.IsNotExist(err) {
|
|
t.Fatal("expected model file to be removed")
|
|
}
|
|
})
|
|
|
|
t.Run("removes sibling files via glob", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
modelFile := filepath.Join(dir, "model.gguf")
|
|
siblingFile := filepath.Join(dir, "model.gguf.mmproj")
|
|
os.WriteFile(modelFile, []byte("model"), 0o644)
|
|
os.WriteFile(siblingFile, []byte("mmproj"), 0o644)
|
|
|
|
err := gallery.DeleteStagedModelFiles(dir, "model.gguf")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if _, err := os.Stat(modelFile); !os.IsNotExist(err) {
|
|
t.Fatal("expected model file to be removed")
|
|
}
|
|
if _, err := os.Stat(siblingFile); !os.IsNotExist(err) {
|
|
t.Fatal("expected sibling file to be removed")
|
|
}
|
|
})
|
|
|
|
t.Run("no error when model does not exist", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
err := gallery.DeleteStagedModelFiles(dir, "nonexistent")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|