Ctrl K
ComunidadGuías…

pkg/health/golden_crosslang_test.go

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

Archivo original ↗Preguntar a KiBot
package health

import (
	"encoding/json"
	"os"
	"testing"
	"time"

	appsv1 "k8s.io/api/apps/v1"
	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
)

// goldenVector is one (object, expected-level) pair in the shared cross-language
// contract. The same testdata/golden_vectors.json is loaded by the frontend's
// vitest (packages/k8s-ui/src/__tests__/health-golden.test.ts) so the TS table
// classifiers can't drift from pkg/health, which is the source of truth.
type goldenVector struct {
	Name   string          `json:"name"`
	Kind   string          `json:"kind"`
	Level  string          `json:"level"`
	Object json.RawMessage `json:"object"`
}

// TestGoldenVectorsCrossLang pins that pkg/health produces the level recorded in
// the shared fixture for each object. The display/wire level is what the frontend
// renders, so pods go through PodDisplayLevel (folds unschedulable/terminating)
// and workloads through Workload. Keep this in lockstep with the vitest mirror.
func TestGoldenVectorsCrossLang(t *testing.T) {
	raw, err := os.ReadFile("testdata/golden_vectors.json")
	if err != nil {
		t.Fatalf("read golden vectors: %v", err)
	}
	var file struct {
		Vectors []goldenVector `json:"vectors"`
	}
	if err := json.Unmarshal(raw, &file); err != nil {
		t.Fatalf("parse golden vectors: %v", err)
	}
	if len(file.Vectors) == 0 {
		t.Fatal("no golden vectors loaded")
	}

	// Fixed clock — every vector is time-independent (see the fixture comment), so
	// the exact value is irrelevant, but pinning it keeps the run reproducible.
	now := time.Date(2026, 6, 25, 12, 0, 0, 0, time.UTC)

	for _, v := range file.Vectors {
		t.Run(v.Name, func(t *testing.T) {
			got := classifyGolden(t, v.Kind, v.Object, now)
			if string(got) != v.Level {
				t.Errorf("%s [%s]: got level %q, want %q", v.Name, v.Kind, got, v.Level)
			}
		})
	}
}

func classifyGolden(t *testing.T, kind string, obj json.RawMessage, now time.Time) Level {
	t.Helper()
	switch kind {
	case "Pod":
		var pod corev1.Pod
		mustUnmarshal(t, obj, &pod)
		return PodDisplayLevel(&pod, now)
	case "Deployment":
		var d appsv1.Deployment
		mustUnmarshal(t, obj, &d)
		return Workload(&d, now).Level
	case "StatefulSet":
		var s appsv1.StatefulSet
		mustUnmarshal(t, obj, &s)
		return Workload(&s, now).Level
	case "DaemonSet":
		var ds appsv1.DaemonSet
		mustUnmarshal(t, obj, &ds)
		return Workload(&ds, now).Level
	case "Job":
		var j batchv1.Job
		mustUnmarshal(t, obj, &j)
		return Workload(&j, now).Level
	case "CronJob":
		var cj batchv1.CronJob
		mustUnmarshal(t, obj, &cj)
		return Workload(&cj, now).Level
	case "PersistentVolumeClaim":
		var pvc corev1.PersistentVolumeClaim
		mustUnmarshal(t, obj, &pvc)
		return Workload(&pvc, now).Level
	default:
		t.Fatalf("golden vector kind %q has no Go classifier mapping", kind)
		return LevelUnknown
	}
}

func mustUnmarshal(t *testing.T, raw json.RawMessage, into any) {
	t.Helper()
	if err := json.Unmarshal(raw, into); err != nil {
		t.Fatalf("unmarshal object: %v", err)
	}
}