package cmd import ( "encoding/json" "errors" "net/http" "net/http/httptest" "testing" "go.moleculesai.app/cli/internal/client" ) // fakeOfferedModelsServer returns a server that responds to GET // /admin/llm/offered-models?runtime=... with a fixed menu. func fakeOfferedModelsServer(t *testing.T, runtime string, models []string) *httptest.Server { t.Helper() return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/admin/llm/offered-models" { http.Error(w, `{"error":"not found"}`, http.StatusNotFound) return } if r.URL.Query().Get("runtime") != runtime { http.Error(w, `{"error":"unknown runtime"}`, http.StatusNotFound) return } out := map[string]interface{}{ "runtime": runtime, "models": []map[string]string{}, } for _, m := range models { out["models"] = append(out["models"].([]map[string]string), map[string]string{ "model": m, "provider": "platform", }) } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(out) })) } func TestRequireModelCompatibleWithRuntime_AllowsKnownGood(t *testing.T) { server := fakeOfferedModelsServer(t, "claude-code", []string{"claude-sonnet-4-6", "claude-opus-4-7"}) defer server.Close() cl := client.NewWithAuth(server.URL, "token", "org") if err := requireModelCompatibleWithRuntime(cl, "claude-sonnet-4-6", "claude-code"); err != nil { t.Fatalf("expected compatible model to pass, got: %v", err) } } func TestRequireModelCompatibleWithRuntime_RejectsKnownBad(t *testing.T) { server := fakeOfferedModelsServer(t, "claude-code", []string{"claude-sonnet-4-6", "claude-opus-4-7"}) defer server.Close() cl := client.NewWithAuth(server.URL, "token", "org") err := requireModelCompatibleWithRuntime(cl, "gpt-5.5", "claude-code") if err == nil { t.Fatal("expected incompatible model to fail") } var ee *exitError if !errors.As(err, &ee) { t.Fatalf("expected exitError, got %T", err) } if ee.code != 1 { t.Errorf("expected exit code 1, got %d", ee.code) } } func TestRequireModelCompatibleWithRuntime_EmptyModelAllowed(t *testing.T) { server := fakeOfferedModelsServer(t, "claude-code", []string{"claude-sonnet-4-6"}) defer server.Close() cl := client.NewWithAuth(server.URL, "token", "org") if err := requireModelCompatibleWithRuntime(cl, "", "claude-code"); err != nil { t.Fatalf("empty model should be allowed: %v", err) } } func TestRequireModelCompatibleWithRuntime_UnknownRuntimeAllowed(t *testing.T) { // Unknown runtimes (e.g. federated / third-party) return 404 from the // offered-models endpoint. We fail-open there to match the server's // federation contract. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, `{"error":"unknown runtime"}`, http.StatusNotFound) })) defer server.Close() cl := client.NewWithAuth(server.URL, "token", "org") if err := requireModelCompatibleWithRuntime(cl, "anything", "external"); err != nil { t.Fatalf("unknown runtime should fail-open: %v", err) } } func TestRequireModelCompatibleWithRuntime_TransientErrorFailsClosed(t *testing.T) { // Any non-404 error from the offered-models endpoint must be treated as // ambiguous and fail-closed; otherwise a transient 5xx/network blip lets // an unsafe runtime switch through. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, `{"error":"internal server error"}`, http.StatusInternalServerError) })) defer server.Close() cl := client.NewWithAuth(server.URL, "token", "org") err := requireModelCompatibleWithRuntime(cl, "claude-sonnet-4-6", "claude-code") if err == nil { t.Fatal("expected transient error to fail closed") } var ee *exitError if !errors.As(err, &ee) { t.Fatalf("expected exitError, got %T", err) } if ee.code != 1 { t.Errorf("expected exit code 1, got %d", ee.code) } }