Ctrl K
ComunidadGuías…

pkg/ownership/catalog_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package ownership

import (
	"reflect"
	"slices"
	"testing"

	"github.com/skyhook-io/radar/pkg/resourceid"
)

func TestCatalogMergesVersionsAndKeepsTheGarbageCollectorsKinds(t *testing.T) {
	gc := []string{"create", "delete", "get", "list", "watch"}
	widget := resourceid.GroupKind{Group: "example.io", Kind: "Widget"}
	c := NewCatalog([]ServedResource{
		{Group: "example.io", Version: "v1beta1", Kind: "Widget", Resource: "widgets", Namespaced: true, Verbs: []string{"get", "list"}},
		{Group: "example.io", Version: "v1", Kind: "Widget", Resource: "widgets", Namespaced: true, Verbs: gc},
		{Group: "example.io", Version: "v1", Kind: "Gadget", Resource: "gadgets", Namespaced: true, Verbs: []string{"get", "list", "watch"}},
		{Group: "metrics.k8s.io", Version: "v1beta1", Kind: "PodMetrics", Resource: "pods", Namespaced: true, Verbs: []string{"get", "list"}},
		{Group: "", Version: "v1", Kind: "Event", Resource: "events", Namespaced: true, Verbs: gc},
		{Group: "events.k8s.io", Version: "v1", Kind: "Event", Resource: "events", Namespaced: true, Verbs: gc},
		{Group: "", Version: "v1", Kind: "Node", Resource: "nodes", Verbs: gc},
	}, true)

	got, ok := c.Kind(widget)
	want := CatalogKind{GroupKind: widget, Resource: "widgets", Namespaced: true, Versions: []string{"v1", "v1beta1"}, HasGCVerbs: true}
	if !ok || !reflect.DeepEqual(got, want) {
		t.Fatalf("Widget = %+v, %v; want %+v", got, ok, want)
	}
	if !c.Serves(widget, "v1beta1") || c.Serves(widget, "v2") || c.Serves(resourceid.GroupKind{Kind: "Widget"}, "v1") {
		t.Fatal("Serves must name exactly the group, kind and versions discovery listed")
	}
	var dependents []string
	for _, k := range c.DependentKinds() {
		dependents = append(dependents, k.GroupKind.String())
	}
	// Gadget lacks delete and PodMetrics lacks watch, so the garbage collector
	// never acts on them; both Events are left out by design.
	if want := []string{"Node", "Widget.example.io"}; !slices.Equal(dependents, want) {
		t.Fatalf("DependentKinds = %v, want %v", dependents, want)
	}
	if !c.Complete() || NewCatalog(nil, false).Complete() {
		t.Fatal("Complete must be what discovery said")
	}
}

// A reference names a kind, not a resource, so a kind two resources of one
// group serve can't be read or searched as either.
func TestCatalogMarksAKindServedByTwoResourcesAmbiguous(t *testing.T) {
	widget := resourceid.GroupKind{Group: "example.io", Kind: "Widget"}
	getOnly := ServedResource{Group: "example.io", Version: "v1", Kind: "Widget", Resource: "awidgets", Verbs: []string{"get"}}
	collected := ServedResource{Group: "example.io", Version: "v1", Kind: "Widget", Resource: "widgets", Namespaced: true, Verbs: []string{"delete", "list", "watch"}}
	for _, order := range [][]ServedResource{{getOnly, collected}, {collected, getOnly}} {
		c := NewCatalog(order, true)
		if _, found := c.Kind(widget); found || !c.Ambiguous(widget) || c.Serves(widget, "v1") {
			t.Fatalf("Widget must be ambiguous, never resolved to one resource: found=%v ambiguous=%v", found, c.Ambiguous(widget))
		}
		if got := c.AmbiguousDependentKinds(); !slices.Equal(got, []AmbiguousKind{{GroupKind: widget, Namespaced: true}}) {
			t.Fatalf("AmbiguousDependentKinds = %+v; want Widget, collected through its namespaced resource", got)
		}
		if len(c.DependentKinds()) != 0 {
			t.Fatal("an ambiguous kind must not join the dependent kinds as one of its resources")
		}
	}
	if NewCatalog([]ServedResource{collected}, true).Ambiguous(widget) {
		t.Fatal("one resource serving a kind is not ambiguous")
	}
}

// An aggregated API can serve one kind namespaced at one version and
// cluster-scoped at another, so which scope a reference or a search means is
// as open as which resource.
func TestCatalogMarksAKindServedAtTwoScopesAmbiguous(t *testing.T) {
	widget := resourceid.GroupKind{Group: "example.io", Kind: "Widget"}
	gc := []string{"delete", "list", "watch"}
	v1 := ServedResource{Group: "example.io", Version: "v1", Kind: "Widget", Resource: "widgets", Namespaced: true, Verbs: gc}
	v1beta1 := ServedResource{Group: "example.io", Version: "v1beta1", Kind: "Widget", Resource: "widgets", Verbs: gc}
	for _, order := range [][]ServedResource{{v1, v1beta1}, {v1beta1, v1}} {
		c := NewCatalog(order, true)
		if _, found := c.Kind(widget); found || !c.Ambiguous(widget) {
			t.Fatalf("Widget at two scopes must be ambiguous: found=%v", found)
		}
		if got := c.AmbiguousDependentKinds(); !slices.Equal(got, []AmbiguousKind{{GroupKind: widget, Namespaced: true, ClusterScoped: true}}) {
			t.Fatalf("AmbiguousDependentKinds = %+v; want Widget in both scopes", got)
		}
	}
}

// Aggregated APIs can serve one kind through several query resources that
// nothing can list, watch or delete, as Kueue's visibility API serves
// PendingWorkloadsSummary through clusterqueues and localqueues. The kind is
// ambiguous, but no object of it can be a dependent.
func TestCatalogKeepsAnAmbiguousKindNothingCollectsOutOfDependents(t *testing.T) {
	summary := resourceid.GroupKind{Group: "visibility.kueue.x-k8s.io", Kind: "PendingWorkloadsSummary"}
	c := NewCatalog([]ServedResource{
		{Group: summary.Group, Version: "v1beta2", Kind: summary.Kind, Resource: "clusterqueues"},
		{Group: summary.Group, Version: "v1beta2", Kind: summary.Kind, Resource: "localqueues", Namespaced: true},
	}, true)
	if !c.Ambiguous(summary) {
		t.Fatal("two resources serving the kind must still make it ambiguous")
	}
	if got := c.AmbiguousDependentKinds(); len(got) != 0 {
		t.Fatalf("AmbiguousDependentKinds = %+v; nothing can collect a summary", got)
	}
	scoped := NewCatalog([]ServedResource{
		{Group: "example.io", Version: "v1", Kind: "Widget", Resource: "widgets", Namespaced: true, Verbs: []string{"get"}},
		{Group: "example.io", Version: "v1", Kind: "Widget", Resource: "clusterwidgets", Verbs: []string{"delete", "list", "watch"}},
	}, true)
	if got := scoped.AmbiguousDependentKinds(); !slices.Equal(got, []AmbiguousKind{{GroupKind: resourceid.GroupKind{Group: "example.io", Kind: "Widget"}, ClusterScoped: true}}) {
		t.Fatalf("AmbiguousDependentKinds = %+v; want Widget, collected only cluster-wide", got)
	}
}

func TestIsEventNamesBothEventKinds(t *testing.T) {
	for gk, want := range map[resourceid.GroupKind]bool{
		{Kind: "Event"}:                         true,
		{Group: "events.k8s.io", Kind: "Event"}: true,
		{Group: "example.io", Kind: "Event"}:    false,
		{Kind: "Pod"}:                           false,
	} {
		if IsEvent(gk) != want {
			t.Errorf("IsEvent(%v) = %v, want %v", gk, !want, want)
		}
	}
}

func TestZeroReadIsUnavailable(t *testing.T) {
	var r ObjectRead
	if r.State != Unavailable || r.State.String() != "unavailable" {
		t.Fatalf("zero read = %v; an unset read must never claim an answer", r.State)
	}
}