package handlers import ( "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" ) // ── ParseCapabilities ─────────────────────────────────────────────── func TestParseCapabilities_Full(t *testing.T) { manifest := map[string]any{ "id": "test-pkg", "capabilities": map[string]any{ "required": []any{"postgres", "pgvector"}, "optional": []any{"s3"}, }, } caps, ok := ParseCapabilities(manifest) if !ok { t.Fatal("expected ok=true") } if len(caps.Required) != 2 || caps.Required[0] != "postgres" || caps.Required[1] != "pgvector" { t.Errorf("required = %v, want [postgres pgvector]", caps.Required) } if len(caps.Optional) != 1 || caps.Optional[0] != "s3" { t.Errorf("optional = %v, want [s3]", caps.Optional) } } func TestParseCapabilities_Missing(t *testing.T) { manifest := map[string]any{"id": "test-pkg"} _, ok := ParseCapabilities(manifest) if ok { t.Error("expected ok=false when no capabilities block") } } func TestParseCapabilities_EmptyArrays(t *testing.T) { manifest := map[string]any{ "capabilities": map[string]any{ "required": []any{}, "optional": []any{}, }, } caps, ok := ParseCapabilities(manifest) if !ok { t.Fatal("expected ok=true") } if len(caps.Required) != 0 { t.Errorf("required should be empty, got %v", caps.Required) } if len(caps.Optional) != 0 { t.Errorf("optional should be empty, got %v", caps.Optional) } } func TestParseCapabilities_IgnoresEmpty(t *testing.T) { manifest := map[string]any{ "capabilities": map[string]any{ "required": []any{"pgvector", "", "postgres"}, }, } caps, _ := ParseCapabilities(manifest) if len(caps.Required) != 2 { t.Errorf("expected 2 entries (empty filtered), got %v", caps.Required) } } // ── CheckRequiredCapabilities ─────────────────────────────────────── func TestCheckRequired_AllMet(t *testing.T) { detected := map[string]bool{"postgres": true, "pgvector": true, "s3": true} missing := CheckRequiredCapabilities([]string{"postgres", "pgvector"}, detected) if len(missing) != 0 { t.Errorf("expected no missing, got %v", missing) } } func TestCheckRequired_SomeUnmet(t *testing.T) { detected := map[string]bool{"postgres": true, "pgvector": false, "s3": false} missing := CheckRequiredCapabilities([]string{"postgres", "pgvector", "s3"}, detected) if len(missing) != 2 { t.Errorf("expected 2 missing, got %v", missing) } } func TestCheckRequired_EmptyRequired(t *testing.T) { detected := map[string]bool{"postgres": true} missing := CheckRequiredCapabilities(nil, detected) if len(missing) != 0 { t.Errorf("expected no missing for nil required, got %v", missing) } } func TestCheckRequired_UnknownCap(t *testing.T) { detected := map[string]bool{"postgres": true} missing := CheckRequiredCapabilities([]string{"gpu"}, detected) if len(missing) != 1 || missing[0] != "gpu" { t.Errorf("expected [gpu], got %v", missing) } } // ── capabilityHelpText ────────────────────────────────────────────── func TestCapabilityHelpText_KnownCaps(t *testing.T) { hints := capabilityHelpText([]string{"pgvector", "s3"}) if len(hints) != 2 { t.Fatalf("expected 2 hints, got %d", len(hints)) } if hints["pgvector"] == "" { t.Error("pgvector hint is empty") } if hints["s3"] == "" { t.Error("s3 hint is empty") } } func TestCapabilityHelpText_UnknownCap(t *testing.T) { hints := capabilityHelpText([]string{"gpu"}) if hints["gpu"] == "" { t.Error("expected fallback hint for unknown cap") } } // ── DetectCapabilities (SQLite path — no real DB) ─────────────────── func TestDetectCapabilities_SQLite_NilStore(t *testing.T) { caps := DetectCapabilities(nil, false, nil, "") if caps["postgres"] { t.Error("postgres should be false on SQLite") } if caps["pgvector"] { t.Error("pgvector should be false on SQLite") } if caps["object_storage"] { t.Error("object_storage should be false with nil store") } if caps["s3"] { t.Error("s3 should be false with nil store") } if caps["workspace"] { t.Error("workspace should be false with empty root") } } func TestDetectCapabilities_WorkspaceWritable(t *testing.T) { tmpDir := t.TempDir() caps := DetectCapabilities(nil, false, nil, tmpDir) if !caps["workspace"] { t.Error("workspace should be true for writable temp dir") } } func TestDetectCapabilities_WorkspaceUnwritable(t *testing.T) { caps := DetectCapabilities(nil, false, nil, "/nonexistent/path/xyz") if caps["workspace"] { t.Error("workspace should be false for nonexistent path") } } // ── Admin endpoint ────────────────────────────────────────────────── func TestGetCapabilities_HTTP(t *testing.T) { gin.SetMode(gin.TestMode) h := NewCapabilitiesHandler(func() map[string]bool { return map[string]bool{ "postgres": false, "pgvector": false, "object_storage": true, "s3": false, "workspace": true, } }) r := gin.New() r.GET("/api/v1/admin/capabilities", h.GetCapabilities) req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/capabilities", nil) w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != 200 { t.Fatalf("status = %d, want 200", w.Code) } var result map[string]bool if err := json.Unmarshal(w.Body.Bytes(), &result); err != nil { t.Fatalf("unmarshal: %v", err) } if result["postgres"] { t.Error("postgres should be false") } if !result["object_storage"] { t.Error("object_storage should be true") } if !result["workspace"] { t.Error("workspace should be true") } }