package client import ( "context" "encoding/json" "net/http" "testing" ) func TestGardenCanUsesEffectivePermissions(t *testing.T) { custom := Garden{Role: "garden:custom", Permissions: []string{"plants:create"}} if !custom.Can("plants:create") || custom.Can("garden:update") { t.Fatalf("custom role did not use effective permissions: %+v", custom) } deniedOverride := Garden{Role: "owner", Permissions: []string{"garden:read"}} if deniedOverride.Can("garden:update") { t.Fatal("explicit permissions must override the built-in role fallback") } legacyOwner := Garden{Role: "owner"} if !legacyOwner.Can("garden:update") { t.Fatal("legacy built-in role response should retain compatibility") } if legacyOwner.Can("unknown:permission") { t.Fatal("legacy role fallback must reject unknown permissions") } } func TestGardenClientCRUD(t *testing.T) { t.Parallel() name := "Neu" description := "Beschreibung" handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch { case r.Method == http.MethodPost && r.URL.Path == "/v1/gardens": var input CreateGardenInput if err := json.NewDecoder(r.Body).Decode(&input); err != nil { t.Error(err) } if input.Name != "Hinterhof" { t.Errorf("create name: got %q, want %q", input.Name, "Hinterhof") } w.WriteHeader(http.StatusCreated) _, _ = w.Write([]byte(`{"garden":{"id":12,"name":"Hinterhof","version":1}}`)) case r.Method == http.MethodGet && r.URL.Path == "/v1/gardens": _, _ = w.Write([]byte(`{"gardens":[{"id":12,"name":"Hinterhof","version":1}]}`)) case r.Method == http.MethodGet && r.URL.Path == "/v1/gardens/12": _, _ = w.Write([]byte(`{"garden":{"id":12,"name":"Hinterhof","version":1}}`)) case r.Method == http.MethodPatch && r.URL.Path == "/v1/gardens/12": var input UpdateGardenInput if err := json.NewDecoder(r.Body).Decode(&input); err != nil { t.Error(err) } if input.Name == nil || *input.Name != name || input.Description == nil || *input.Description != description { t.Errorf("update input: got %+v", input) } _, _ = w.Write([]byte(`{"garden":{"id":12,"name":"Neu","description":"Beschreibung","version":2}}`)) case r.Method == http.MethodDelete && r.URL.Path == "/v1/gardens/12": w.WriteHeader(http.StatusNoContent) default: http.NotFound(w, r) } }) apiClient := newTestClient(t, handler) created, response, err := apiClient.CreateGarden(context.Background(), CreateGardenInput{Name: "Hinterhof"}) if err != nil || response.StatusCode != http.StatusCreated || created.ID != 12 { t.Fatalf("CreateGarden(): garden=%+v response=%v err=%v", created, response, err) } gardens, _, err := apiClient.Gardens(context.Background()) if err != nil || len(gardens) != 1 || gardens[0].ID != 12 { t.Fatalf("Gardens(): gardens=%+v err=%v", gardens, err) } garden, _, err := apiClient.Garden(context.Background(), 12) if err != nil || garden.Name != "Hinterhof" { t.Fatalf("Garden(): garden=%+v err=%v", garden, err) } updated, _, err := apiClient.UpdateGarden(context.Background(), 12, UpdateGardenInput{Name: &name, Description: &description}) if err != nil || updated.Version != 2 || updated.Name != name { t.Fatalf("UpdateGarden(): garden=%+v err=%v", updated, err) } response, err = apiClient.DeleteGarden(context.Background(), 12) if err != nil || response.StatusCode != http.StatusNoContent { t.Fatalf("DeleteGarden(): response=%v err=%v", response, err) } }