Ctrl K
ComunidadGuías…

internal/helm/detail_test.go

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

Archivo original ↗Preguntar a KiBot
package helm

import (
	"context"
	"encoding/base64"
	"errors"
	"math"
	"strconv"
	"strings"
	"testing"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime"
	k8sfake "k8s.io/client-go/kubernetes/fake"
	k8stesting "k8s.io/client-go/testing"
)

const manifest = `---
# Source: live-check/templates/cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: live-check
  namespace: demo
data:
  hello: world
---
# Source: live-check/templates/deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: live-check
  namespace: demo
spec:
  replicas: 1
`

func TestRevisionOrderingDoesNotOverflow(t *testing.T) {
	newest := stored{revision: math.MaxInt64}
	oldest := stored{revision: math.MinInt64}

	if newerRevision(newest, oldest) >= 0 {
		t.Fatal("the largest revision was not ordered first")
	}
	if newerRevision(oldest, newest) <= 0 {
		t.Fatal("the smallest revision was not ordered last")
	}
}

func detailPayload(spec release) string {
	return `{
	  "name": "` + spec.name + `",
	  "namespace": "` + spec.namespace + `",
	  "version": ` + strconv.FormatInt(spec.revision, 10) + `,
	  "info": {
	    "status": "` + spec.status + `",
	    "first_deployed": "2026-08-01T09:00:00Z",
	    "last_deployed": "` + deployedAt + `",
	    "description": "Upgrade complete",
	    "notes": "Thank you for installing live-check."
	  },
	  "chart": {"metadata": {"name": "` + spec.chart + `", "version": "` + spec.version + `", "appVersion": "` + spec.appVersion + `"}},
	  "config": {"replicaCount": 2, "image": {"tag": "1.2.3"}},
	  "manifest": ` + quote(manifest) + `
	}`
}

func quote(body string) string {
	replaced := strings.ReplaceAll(body, "\\", "\\\\")
	replaced = strings.ReplaceAll(replaced, "\"", "\\\"")
	replaced = strings.ReplaceAll(replaced, "\n", "\\n")
	return "\"" + replaced + "\""
}

func detailSecret(spec release) *corev1.Secret {
	body := []byte(base64.StdEncoding.EncodeToString(gzipped(detailPayload(spec))))
	return storedSecret(spec, body)
}

func resolver(apiVersion, kind string) (Kind, bool) {
	switch {
	case apiVersion == "v1" && kind == "ConfigMap":
		return Kind{Version: "v1", Resource: "configmaps", Namespaced: true}, true
	case apiVersion == "apps/v1" && kind == "Deployment":
		return Kind{Group: "apps", Version: "v1", Resource: "deployments", Namespaced: true}, true
	case apiVersion == "v1" && kind == "Namespace":
		return Kind{Version: "v1", Resource: "namespaces"}, true
	}
	return Kind{}, false
}

func TestDetailCarriesValuesNotesAndManifest(t *testing.T) {
	spec := sampleRelease()
	service := newService(k8sfake.NewClientset(detailSecret(spec)), nil, nil)

	got, err := service.Detail(context.Background(), "demo", "podinfo", 0, resolver)
	if err != nil {
		t.Fatalf("detail: %v", err)
	}

	if got.Release.ChartVersion != "6.9.2" {
		t.Fatalf("chart version = %q, want 6.9.2", got.Release.ChartVersion)
	}
	if !strings.Contains(got.Values, "replicaCount: 2") {
		t.Fatalf("values = %q, want the supplied values as yaml", got.Values)
	}
	if !strings.Contains(got.Values, "tag: 1.2.3") {
		t.Fatalf("values = %q, want nested values kept", got.Values)
	}
	if got.Notes != "Thank you for installing live-check." {
		t.Fatalf("notes = %q, want the release notes", got.Notes)
	}
	if !strings.Contains(got.Manifest, "kind: Deployment") {
		t.Fatalf("manifest = %q, want the rendered manifest", got.Manifest)
	}
	if got.FirstDeployed != "2026-08-01T09:00:00Z" {
		t.Fatalf("first deployed = %q, want the install time", got.FirstDeployed)
	}
	if got.Driver != DriverSecret {
		t.Fatalf("driver = %q, want secret", got.Driver)
	}
}

func TestDetailListsTheResourcesTheManifestRenders(t *testing.T) {
	service := newService(k8sfake.NewClientset(detailSecret(sampleRelease())), nil, nil)

	got, err := service.Detail(context.Background(), "demo", "podinfo", 0, resolver)
	if err != nil {
		t.Fatalf("detail: %v", err)
	}

	if len(got.Resources) != 2 {
		t.Fatalf("resources = %d, want 2", len(got.Resources))
	}
	first := got.Resources[0]
	if first.Kind != "ConfigMap" || first.Name != "live-check" {
		t.Fatalf("first = %+v, want the ConfigMap", first)
	}
	if first.Resource != "configmaps" {
		t.Fatalf("resource = %q, want discovery to have resolved the plural", first.Resource)
	}
	second := got.Resources[1]
	if second.Group != "apps" || second.Resource != "deployments" {
		t.Fatalf("second = %+v, want the apps/v1 deployment", second)
	}
	if second.Namespace != "demo" {
		t.Fatalf("namespace = %q, want demo", second.Namespace)
	}
}

func TestDetailLeavesAnUnknownKindUnresolved(t *testing.T) {
	service := newService(k8sfake.NewClientset(detailSecret(sampleRelease())), nil, nil)

	got, err := service.Detail(context.Background(), "demo", "podinfo", 0,
		func(string, string) (Kind, bool) {
			return Kind{}, false
		})
	if err != nil {
		t.Fatalf("detail: %v", err)
	}

	if got.Resources[0].Resource != "" {
		t.Fatalf("resource = %q, want it unresolved", got.Resources[0].Resource)
	}
	if got.Resources[0].Kind != "ConfigMap" {
		t.Fatalf("kind = %q, want it still named", got.Resources[0].Kind)
	}
}

func TestDetailWorksWithNoResolverAtAll(t *testing.T) {
	service := newService(k8sfake.NewClientset(detailSecret(sampleRelease())), nil, nil)

	got, err := service.Detail(context.Background(), "demo", "podinfo", 0, nil)
	if err != nil {
		t.Fatalf("detail: %v", err)
	}

	if len(got.Resources) != 2 {
		t.Fatalf("resources = %d, want them listed even unresolved", len(got.Resources))
	}
}

func TestHistoryOrdersARequestedPageNewestFirst(t *testing.T) {
	first := sampleRelease()
	first.revision = 1
	first.status = "superseded"
	first.version = "6.9.0"
	second := sampleRelease()
	second.revision = 2
	second.status = "superseded"
	second.version = "6.9.1"
	third := sampleRelease()
	service := newService(k8sfake.NewClientset(
		detailSecret(second), detailSecret(third), detailSecret(first),
	), nil, nil)

	got, err := service.History(context.Background(), "demo", "podinfo", 3)
	if err != nil {
		t.Fatalf("history: %v", err)
	}

	if len(got.Revisions) != 3 {
		t.Fatalf("history = %d, want the requested revisions", len(got.Revisions))
	}
	if got.Revisions[0].Revision != 3 || got.Revisions[2].Revision != 1 {
		t.Fatalf("history = %v, want newest first", got.Revisions)
	}
	if got.Revisions[1].ChartVersion != "6.9.1" {
		t.Fatalf("chart version = %q, want each revision's own", got.Revisions[1].ChartVersion)
	}
	if got.Next != 0 {
		t.Fatalf("next = %d, want the end of history", got.Next)
	}
}

func TestDetailRefusesAReleaseThatIsNotThere(t *testing.T) {
	service := newService(k8sfake.NewClientset(), nil, nil)

	_, err := service.Detail(context.Background(), "demo", "missing", 0, resolver)

	if !errors.Is(err, ErrNoRelease) {
		t.Fatalf("err = %v, want it to say there is no such release", err)
	}
}

func TestDetailRefusesNamesThatCouldBeFlags(t *testing.T) {
	service := newService(k8sfake.NewClientset(), nil, nil)

	_, err := service.Detail(context.Background(), "demo", "--kubeconfig=/etc/shadow", 0, resolver)

	if err == nil {
		t.Fatal("a flag-shaped release name was accepted")
	}
}

func TestDetailStillAnswersWhenTheNewestPayloadIsUnreadable(t *testing.T) {
	spec := sampleRelease()
	service := newService(k8sfake.NewClientset(storedSecret(spec, []byte("rubbish"))), nil, nil)

	got, err := service.Detail(context.Background(), "demo", "podinfo", 0, resolver)
	if err != nil {
		t.Fatalf("detail: %v", err)
	}

	if got.Release.Name != "podinfo" {
		t.Fatalf("name = %q, want the label's", got.Release.Name)
	}
	if !strings.Contains(got.Error, "could not be read") {
		t.Fatalf("error = %q, want it to say the payload is unreadable", got.Error)
	}
	if len(got.History) != 0 {
		t.Fatalf("history = %d, want history loaded separately", len(got.History))
	}

	history, historyErr := service.History(context.Background(), "demo", "podinfo", 3)
	if historyErr != nil {
		t.Fatalf("history: %v", historyErr)
	}
	if len(history.Revisions) != 1 {
		t.Fatalf("history = %d, want the revision listed separately", len(history.Revisions))
	}
	if history.Revisions[0].Description != "this revision's payload could not be read" {
		t.Fatalf("description = %q, want the unreadable note", history.Revisions[0].Description)
	}
}

func TestDetailReportsARefusedList(t *testing.T) {
	cs := k8sfake.NewClientset()
	cs.PrependReactor("list", "secrets", func(k8stesting.Action) (bool, runtime.Object, error) {
		return true, nil, errors.New("secrets is forbidden")
	})
	service := newService(cs, nil, nil)

	_, err := service.Detail(context.Background(), "demo", "podinfo", 0, resolver)

	if err == nil {
		t.Fatal("a refused list reported success")
	}
}

func TestAReleaseWithNoSuppliedValuesReadsEmpty(t *testing.T) {
	spec := sampleRelease()
	body := []byte(base64.StdEncoding.EncodeToString(gzipped(`{"name":"podinfo","namespace":"demo","version":3,"info":{"status":"deployed"}}`)))
	service := newService(k8sfake.NewClientset(storedSecret(spec, body)), nil, nil)

	got, err := service.Detail(context.Background(), "demo", "podinfo", 0, resolver)
	if err != nil {
		t.Fatalf("detail: %v", err)
	}

	if got.Values != "" {
		t.Fatalf("values = %q, want nothing", got.Values)
	}
	if len(got.Resources) != 0 {
		t.Fatalf("resources = %v, want none", got.Resources)
	}
}

func TestAManifestDocumentThatIsNotAnObjectIsSkipped(t *testing.T) {
	got := resourcesOf("---\njust a string\n---\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: real\n", "demo", nil)

	if len(got) != 1 {
		t.Fatalf("resources = %v, want only the real object", got)
	}
	if got[0].Name != "real" {
		t.Fatalf("name = %q, want real", got[0].Name)
	}
}

func TestAManifestDocumentWithNoNameIsSkipped(t *testing.T) {
	got := resourcesOf("apiVersion: v1\nkind: ConfigMap\nmetadata: {}\n", "demo", nil)

	if len(got) != 0 {
		t.Fatalf("resources = %v, want none", got)
	}
}

func TestDetailReadsAReleaseStoredInAConfigMap(t *testing.T) {
	spec := sampleRelease()
	entry := &corev1.ConfigMap{
		Name:      "sh.helm.release.v1.podinfo.v3",
		Namespace: "demo",
		Labels: map[string]string{
			"owner": "helm", "name": "podinfo", "version": "3", "status": "deployed",
		},
		Data: map[string]string{releaseKey: base64.StdEncoding.EncodeToString(gzipped(detailPayload(spec)))},
	}
	service := newService(k8sfake.NewClientset(entry), nil, nil)

	got, err := service.Detail(context.Background(), "demo", "podinfo", 0, resolver)
	if err != nil {
		t.Fatalf("detail: %v", err)
	}

	if got.Driver != DriverConfigMap {
		t.Fatalf("driver = %q, want configmap", got.Driver)
	}
	if got.Release.ChartVersion != "6.9.2" {
		t.Fatalf("chart version = %q, want the configmap payload decoded", got.Release.ChartVersion)
	}
}

func TestDetailUsesTheNewestRevisionAcrossStorageDrivers(t *testing.T) {
	old := sampleRelease()
	old.revision = 1
	newest := sampleRelease()
	newest.revision = 2
	newest.version = "6.9.3"
	newestSecret := detailSecret(newest)
	entry := &corev1.ConfigMap{
		ObjectMeta: newestSecret.ObjectMeta,
		Data:       map[string]string{releaseKey: string(newestSecret.Data[releaseKey])},
	}
	service := newService(k8sfake.NewClientset(detailSecret(old), entry), nil, nil)

	got, err := service.Detail(context.Background(), "demo", "podinfo", 0, resolver)
	if err != nil {
		t.Fatalf("detail: %v", err)
	}

	if got.Driver != DriverConfigMap || got.Release.Revision != 2 {
		t.Fatalf("detail = %+v, want configmap revision 2", got)
	}
	if got.Release.ChartVersion != "6.9.3" {
		t.Fatalf("chart version = %q, want the newest payload", got.Release.ChartVersion)
	}
}

func TestANamespacedResourceInheritsTheReleaseNamespace(t *testing.T) {
	manifest := "apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: live-check\n"

	got := resourcesOf(manifest, "helm-live", resolver)

	if len(got) != 1 {
		t.Fatalf("resources = %v, want one", got)
	}
	if got[0].Namespace != "helm-live" {
		t.Fatalf("namespace = %q, want the release's, since helm renders none", got[0].Namespace)
	}
}

func TestAResourceKeepsTheNamespaceTheManifestGave(t *testing.T) {
	manifest := "apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: cm\n  namespace: elsewhere\n"

	got := resourcesOf(manifest, "helm-live", resolver)

	if got[0].Namespace != "elsewhere" {
		t.Fatalf("namespace = %q, want the manifest's own", got[0].Namespace)
	}
}

func TestAClusterScopedResourceGetsNoNamespace(t *testing.T) {
	manifest := "apiVersion: v1\nkind: Namespace\nmetadata:\n  name: made-by-chart\n"

	got := resourcesOf(manifest, "helm-live", resolver)

	if got[0].Namespace != "" {
		t.Fatalf("namespace = %q, want none for a cluster-scoped kind", got[0].Namespace)
	}
}

func TestAnUnresolvedKindIsLeftWithoutANamespace(t *testing.T) {
	manifest := "apiVersion: acme.io/v1\nkind: Widget\nmetadata:\n  name: thing\n"

	got := resourcesOf(manifest, "helm-live", resolver)

	if got[0].Namespace != "" {
		t.Fatalf("namespace = %q, want none when the kind is unknown", got[0].Namespace)
	}
}