package main import ( "net/http" "net/http/httptest" "strings" "testing" "time" "github.com/kami/maven/internal/config" "github.com/kami/maven/internal/llm" ) // No `workstation` block is the shipping deploy. The seam must then be the // resident client itself, with nothing probing anything. func TestModelSeamUnconfiguredIsResidentOnly(t *testing.T) { resident := llm.New("http://127.0.0.1:1", time.Second) hot, pair := modelSeam(&config.Config{}, resident) if pair != nil { t.Error("built a pair with no workstation configured") } if hot == nil { t.Fatal("no seam at all, so the cascade would route with the classifier") } } // A workstation with no resident model behind it has no floor, and a Pair with // no floor is a configuration mistake rather than a degraded mode. func TestModelSeamWithoutResidentIsNil(t *testing.T) { cfg := &config.Config{Workstation: &config.WorkstationConfig{URL: "http://127.0.0.1:1"}} cfg.Workstation.Health = strings.TrimRight(cfg.Workstation.URL, "/") + "/health" hot, pair := modelSeam(cfg, nil) if hot != nil || pair != nil { t.Errorf("built a seam with no floor: hot=%v pair=%v", hot, pair) } } // The configured case: the seam is the pair, and the pair notices a workstation // that answers /health. func TestModelSeamPrefersAnAnsweringWorkstation(t *testing.T) { up := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) defer up.Close() cfg := &config.Config{Workstation: &config.WorkstationConfig{ URL: up.URL, Probe: config.Duration(10 * time.Millisecond), }} cfg.Workstation.Health = strings.TrimRight(cfg.Workstation.URL, "/") + "/health" hot, pair := modelSeam(cfg, llm.New("http://127.0.0.1:1", time.Second)) if pair == nil || hot == nil { t.Fatal("no pair built for a configured workstation") } defer pair.Stop() deadline := time.Now().Add(2 * time.Second) for !pair.Available() && time.Now().Before(deadline) { time.Sleep(5 * time.Millisecond) } if !pair.Available() { t.Fatal("the pair never saw a workstation that answers /health") } } // A card held by a CPT run answers 503, and that must read as unavailable // rather than as an error a turn has to handle. func TestModelSeamHeldCardIsUnavailable(t *testing.T) { busy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "model not loaded", http.StatusServiceUnavailable) })) defer busy.Close() cfg := &config.Config{Workstation: &config.WorkstationConfig{ URL: busy.URL, Probe: config.Duration(10 * time.Millisecond), }} cfg.Workstation.Health = strings.TrimRight(cfg.Workstation.URL, "/") + "/health" _, pair := modelSeam(cfg, llm.New("http://127.0.0.1:1", time.Second)) if pair == nil { t.Fatal("no pair built for a configured workstation") } defer pair.Stop() time.Sleep(50 * time.Millisecond) if pair.Available() { t.Error("a 503 from the supervisor read as available") } }