Ctrl K
ComunidadGuías…

internal/lineagecontrollerwebhook/config_test.go

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

Archivo original ↗Preguntar a KiBot
package lineagecontrollerwebhook

import (
	"testing"

	helmv2 "github.com/fluxcd/helm-controller/api/v2"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// TestGetApplicationLabel_NilLabelsErrors pins the explicit nil-map
// guard so a HelmRelease with no labels at all does not panic.
func TestGetApplicationLabel_NilLabelsErrors(t *testing.T) {
	hr := &helmv2.HelmRelease{
		ObjectMeta: metav1.ObjectMeta{Name: "x", Namespace: "y"},
	}
	if _, err := getApplicationLabel(hr, "any"); err == nil {
		t.Errorf("expected error on nil labels, got nil")
	}
}

// TestGetApplicationLabel_MissingKey pins the descriptive error for
// missing labels — the message should name the key that was looked up.
func TestGetApplicationLabel_MissingKey(t *testing.T) {
	hr := &helmv2.HelmRelease{
		ObjectMeta: metav1.ObjectMeta{
			Name: "rel", Namespace: "tenant-foo",
			Labels: map[string]string{"unrelated": "x"},
		},
	}
	_, err := getApplicationLabel(hr, "apps.cozystack.io/application.kind")
	if err == nil {
		t.Fatalf("expected error, got nil")
	}
	if got := err.Error(); got == "" {
		t.Errorf("expected non-empty error message")
	}
}

// TestGetApplicationLabel_Present pins the happy path.
func TestGetApplicationLabel_Present(t *testing.T) {
	hr := &helmv2.HelmRelease{
		ObjectMeta: metav1.ObjectMeta{
			Labels: map[string]string{
				"apps.cozystack.io/application.kind": "Harbor",
			},
		},
	}
	got, err := getApplicationLabel(hr, "apps.cozystack.io/application.kind")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if got != "Harbor" {
		t.Errorf("expected Harbor, got %q", got)
	}
}

// TestMap_HappyPath pins the four-tuple HelmRelease → (apiVersion,
// kind, prefix, error). prefix is derived by stripping the
// application.name suffix from the HelmRelease name.
func TestMap_HappyPath(t *testing.T) {
	hr := &helmv2.HelmRelease{
		ObjectMeta: metav1.ObjectMeta{
			Name: "myinstance-harbor",
			Labels: map[string]string{
				"apps.cozystack.io/application.kind":  "Harbor",
				"apps.cozystack.io/application.group": "apps.cozystack.io",
				"apps.cozystack.io/application.name":  "harbor",
			},
		},
	}
	l := &LineageControllerWebhook{}
	apiVersion, kind, prefix, err := l.Map(hr)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if apiVersion != "apps.cozystack.io/v1alpha1" {
		t.Errorf("apiVersion=%q, want apps.cozystack.io/v1alpha1", apiVersion)
	}
	if kind != "Harbor" {
		t.Errorf("kind=%q, want Harbor", kind)
	}
	if prefix != "myinstance-" {
		t.Errorf("prefix=%q, want myinstance-", prefix)
	}
}

// TestMap_AppNameAppearsTwiceInHRName pins the prefix-derivation guard:
// stripping the suffix once must yield "<prefix><appName>" == hr.Name.
// If the name has the appName embedded but not as suffix, return error
// rather than producing a wrong prefix.
func TestMap_AppNameAppearsTwiceInHRName(t *testing.T) {
	hr := &helmv2.HelmRelease{
		// name="harbor-staging" with appName="harbor": strip "harbor"
		// from the end yields "harbor-stagi", which when combined with
		// "harbor" doesn't reconstruct "harbor-staging" → error.
		ObjectMeta: metav1.ObjectMeta{
			Name: "harbor-staging",
			Labels: map[string]string{
				"apps.cozystack.io/application.kind":  "Harbor",
				"apps.cozystack.io/application.group": "apps.cozystack.io",
				"apps.cozystack.io/application.name":  "harbor",
			},
		},
	}
	l := &LineageControllerWebhook{}
	_, _, _, err := l.Map(hr)
	if err == nil {
		t.Errorf("expected derivation error, got nil")
	}
}

// TestMap_MissingLabel pins the failure mode when any of the three
// required application.* labels is absent.
func TestMap_MissingLabel(t *testing.T) {
	cases := []struct {
		name   string
		labels map[string]string
	}{
		{"missing kind", map[string]string{
			"apps.cozystack.io/application.group": "apps.cozystack.io",
			"apps.cozystack.io/application.name":  "harbor",
		}},
		{"missing group", map[string]string{
			"apps.cozystack.io/application.kind": "Harbor",
			"apps.cozystack.io/application.name": "harbor",
		}},
		{"missing name", map[string]string{
			"apps.cozystack.io/application.kind":  "Harbor",
			"apps.cozystack.io/application.group": "apps.cozystack.io",
		}},
		{"nil labels", nil},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			hr := &helmv2.HelmRelease{
				ObjectMeta: metav1.ObjectMeta{
					Name: "x", Namespace: "y",
					Labels: tc.labels,
				},
			}
			l := &LineageControllerWebhook{}
			_, _, _, err := l.Map(hr)
			if err == nil {
				t.Errorf("expected error, got nil")
			}
		})
	}
}

// TestInitConfig_StoresEmptyMap pins the lazy init contract: after
// initConfig the runtimeConfig must exist with an empty appCRDMap, so
// downstream lookups don't NPE on a fresh webhook instance.
func TestInitConfig_StoresEmptyMap(t *testing.T) {
	l := &LineageControllerWebhook{}
	l.initConfig()
	cfg, ok := l.config.Load().(*runtimeConfig)
	if !ok || cfg == nil {
		t.Fatalf("expected *runtimeConfig stored, got %T", l.config.Load())
	}
	if cfg.appCRDMap == nil {
		t.Errorf("expected empty appCRDMap, got nil")
	}
	if len(cfg.appCRDMap) != 0 {
		t.Errorf("expected empty appCRDMap, got %d entries", len(cfg.appCRDMap))
	}
}

// TestInitConfig_IdempotentDoesNotOverwrite pins that calling
// initConfig twice does not replace the stored runtimeConfig pointer
// — sync.Once guarantees the body runs only once, so the second call
// is observably a no-op.
func TestInitConfig_IdempotentDoesNotOverwrite(t *testing.T) {
	l := &LineageControllerWebhook{}
	l.initConfig() // first call — stores
	first := l.config.Load()
	l.initConfig() // second call — sync.Once short-circuits
	second := l.config.Load()
	if first != second {
		t.Errorf("initConfig overwrote existing config: %p → %p", first, second)
	}
}