Files
fn_registry/functions/ml/sdcli_test.go
T
egutierrez 8284afcba5 feat(ml): auto-commit con 14 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 01:22:02 +02:00

115 lines
3.3 KiB
Go

package ml
import (
"context"
"os"
"testing"
"time"
)
// TestSdcliResolveBinary_NotFound verifica que SdcliResolveBinary retorna error
// cuando no hay binario en PATH ni hint. Forzamos PATH="" para que LookPath
// no encuentre nada, lo que hace el test determinista independientemente del
// entorno del desarrollador.
func TestSdcliResolveBinary_NotFound(t *testing.T) {
t.Setenv("PATH", "")
_, err := SdcliResolveBinary("")
if err == nil {
t.Fatal("expected error when sd not in PATH, got nil")
}
}
// TestSdcliResolveBinary_Hint verifica que un hint valido (archivo ejecutable)
// se resuelve con Source="config" sin necesidad de PATH.
func TestSdcliResolveBinary_Hint(t *testing.T) {
// Crear archivo temporal ejecutable que simula el binario sd.
// El script simplemente sale con 0; --version devolvera string vacio
// pero eso no es error (version es best-effort).
f, err := os.CreateTemp("", "sd-test-*")
if err != nil {
t.Fatalf("creating temp file: %v", err)
}
defer os.Remove(f.Name())
f.Close()
script := []byte("#!/bin/sh\necho 'sd-test 0.1'\n")
if err := os.WriteFile(f.Name(), script, 0o755); err != nil {
t.Fatalf("writing temp file: %v", err)
}
if err := os.Chmod(f.Name(), 0o755); err != nil {
t.Fatalf("chmod temp file: %v", err)
}
bin, err := SdcliResolveBinary(f.Name())
if err != nil {
t.Fatalf("SdcliResolveBinary(hint): %v", err)
}
if bin.Source != "config" {
t.Fatalf("expected source=config, got %q", bin.Source)
}
if bin.Path != f.Name() {
t.Fatalf("expected path=%q, got %q", f.Name(), bin.Path)
}
}
// TestSdcliGenerate_RequiresBinary es un test de integracion que salta si el
// binario sd no esta instalado en PATH. Si esta disponible, tambien requiere
// el modelo SD Turbo en el vault para ejecutar una generacion real.
func TestSdcliGenerate_RequiresBinary(t *testing.T) {
bin, err := SdcliResolveBinary("")
if err != nil {
t.Skipf("sd binary not in PATH, skipping integration test: %v", err)
}
modelPath := "/home/lucas/vaults/imagegen_models/diffusers/sd-turbo/sd_turbo.safetensors"
if _, err := os.Stat(modelPath); err != nil {
t.Skipf("SD Turbo model not in vault (%s), skipping: %v", modelPath, err)
}
cfg := GenerationConfig{
Prompt: "a red apple",
Seed: 42,
Steps: 4,
CfgScale: 1.0,
Sampler: "euler_a",
Width: 512,
Height: 512,
Model: ModelRef{
Name: "sd-turbo",
ModelType: "sd15",
Quantization: "fp16",
Path: modelPath,
},
}
outPath := t.TempDir() + "/out.png"
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
var progressCalled bool
res, err := SdcliGenerate(ctx, bin, cfg, outPath, func(p SdcliProgress) {
progressCalled = true
t.Logf("progress: step %d/%d (%.1f%%) %.2fit/s",
p.Step, p.TotalSteps, p.Percent, p.ItPerSec)
})
if err != nil {
t.Fatalf("SdcliGenerate: %v", err)
}
if len(res.ImageBytes) == 0 {
t.Fatal("expected non-empty image bytes")
}
if res.Format != "png" {
t.Fatalf("expected format=png, got %q", res.Format)
}
if res.DurationMs <= 0 {
t.Fatalf("expected positive duration_ms, got %d", res.DurationMs)
}
if res.Meta["backend"] != "sdcli" {
t.Fatalf("expected meta.backend=sdcli, got %v", res.Meta["backend"])
}
t.Logf("generated %d bytes in %dms (progress_called=%v)",
len(res.ImageBytes), res.DurationMs, progressCalled)
}