Ctrl K
ComunidadGuías…

internal/argocd/diagnose_test.go

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

Archivo original ↗Preguntar a KiBot
package argocd

import (
	"strings"
	"testing"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

	"github.com/sophotechlabs/spinoza/internal/api"
)

func issueTitled(issues []api.GitopsIssue, title string) (api.GitopsIssue, bool) {
	for _, one := range issues {
		if one.Title == title {
			return one, true
		}
	}
	return api.GitopsIssue{}, false
}

func issueAbout(issues []api.GitopsIssue, subject string) (api.GitopsIssue, bool) {
	for _, one := range issues {
		if one.Subject == subject {
			return one, true
		}
	}
	return api.GitopsIssue{}, false
}

func TestAHealthyApplicationHasNoIssues(t *testing.T) {
	if got := Detail(detailed()); got.Issues != nil {
		t.Fatalf("issues = %+v, want none", got.Issues)
	}
}

func TestConditionsBecomeTypedIssues(t *testing.T) {
	app := detailed()
	_ = unstructured.SetNestedSlice(app.Object, []any{
		map[string]any{"type": "ComparisonError", "message": "authentication required"},
		map[string]any{"type": "OrphanedResourceWarning", "message": "Secret/db is orphaned"},
		map[string]any{"type": "SomethingElse", "message": "who knows"},
	}, "status", "conditions")

	issues := Detail(app).Issues

	comparison, found := issueAbout(issues, "ComparisonError")
	if !found || comparison.Severity != api.SeverityMedium {
		t.Fatalf("comparison issue = %+v, want it degraded", comparison)
	}
	if !strings.Contains(comparison.Detail, "repository credentials") {
		t.Fatalf("detail = %q, want it to say what to do", comparison.Detail)
	}
	orphaned, found := issueAbout(issues, "OrphanedResourceWarning")
	if !found || orphaned.Severity != api.SeverityLow {
		t.Fatalf("orphan issue = %+v, want a warning", orphaned)
	}
	other, found := issueAbout(issues, "SomethingElse")
	if !found || other.Severity != api.SeverityLow {
		t.Fatalf("unclassified issue = %+v, want info", other)
	}
	if other.Detail != "" {
		t.Fatalf("detail = %q, want nothing invented for a type we do not know", other.Detail)
	}
}

func TestConditionsWithoutATypeAreSkipped(t *testing.T) {
	app := detailed()
	_ = unstructured.SetNestedSlice(app.Object, []any{
		map[string]any{"message": "no type here"},
		"not a map",
	}, "status", "conditions")

	if got := Detail(app); got.Issues != nil {
		t.Fatalf("issues = %+v, want none", got.Issues)
	}
}

func TestAFailedOperationBecomesAnIssueWithItsCause(t *testing.T) {
	app := detailed()
	_ = unstructured.SetNestedField(app.Object, "Failed", "status", "operationState", "phase")
	_ = unstructured.SetNestedField(app.Object, "Operation terminated", "status", "operationState", "message")

	issue, found := issueAbout(Detail(app).Issues, "operation")

	if !found {
		t.Fatal("a failed operation raised no issue")
	}
	if issue.Title != "The last operation failed" {
		t.Fatalf("title = %q", issue.Title)
	}
	if !strings.HasPrefix(issue.Detail, "the operation was stopped") {
		t.Fatalf("detail = %q, want the cause first", issue.Detail)
	}
	if !strings.HasSuffix(issue.Detail, "Operation terminated") {
		t.Fatalf("detail = %q, want the raw message kept", issue.Detail)
	}
}

func TestAFailureWithNoKnownCauseKeepsTheRawMessage(t *testing.T) {
	app := detailed()
	_ = unstructured.SetNestedField(app.Object, "Error", "status", "operationState", "phase")
	_ = unstructured.SetNestedField(app.Object, "something odd happened", "status", "operationState", "message")

	issue, _ := issueAbout(Detail(app).Issues, "operation")

	if issue.Detail != "something odd happened" {
		t.Fatalf("detail = %q, want the message as it came", issue.Detail)
	}
}

func TestAFailureWithNoMessageAtAllStatesTheCauseAlone(t *testing.T) {
	if got := detailOf(""); got != "" {
		t.Fatalf("detail = %q, want nothing", got)
	}
}

func TestARunningOperationIsNotAnIssue(t *testing.T) {
	app := operating(detailed(), runningPhase)

	if _, found := issueAbout(Detail(app).Issues, "operation"); found {
		t.Fatal("an operation still running was reported as a problem")
	}
}

func TestDriftWithAutoSyncOffSaysNothingWillReconcileIt(t *testing.T) {
	app := automating(detailed(), map[string]any{"enabled": false})
	_ = unstructured.SetNestedField(app.Object, outOfSync, "status", "sync", "status")

	issue, found := issueAbout(Detail(app).Issues, "drift")

	if !found {
		t.Fatal("drift with auto-sync off raised no issue")
	}
	if issue.Title != "Nothing will reconcile this" {
		t.Fatalf("title = %q", issue.Title)
	}
}

func TestDriftThatKeepsComingBackIsCalledOut(t *testing.T) {
	app := detailed()
	_ = unstructured.SetNestedField(app.Object, outOfSync, "status", "sync", "status")

	issue, found := issueAbout(Detail(app).Issues, "drift")

	if !found {
		t.Fatal("a synced-then-out-of-sync loop raised no issue")
	}
	if issue.Title != "Synced, and out of sync again" {
		t.Fatalf("title = %q", issue.Title)
	}
	if !strings.Contains(issue.Detail, "mutating webhook") {
		t.Fatalf("detail = %q, want the likely culprits named", issue.Detail)
	}
}

func TestDriftIsNotCalledALoopWhileTheLastSyncFailed(t *testing.T) {
	app := detailed()
	_ = unstructured.SetNestedField(app.Object, outOfSync, "status", "sync", "status")
	_ = unstructured.SetNestedField(app.Object, "Failed", "status", "operationState", "phase")

	if _, found := issueAbout(Detail(app).Issues, "drift"); found {
		t.Fatal("a failed sync was reported as a drift loop")
	}
}

func TestASyncedApplicationRaisesNoDriftIssue(t *testing.T) {
	if _, found := issueAbout(Detail(detailed()).Issues, "drift"); found {
		t.Fatal("a synced application was reported as drifting")
	}
}

func TestBrokenResourcesEachGetAnIssue(t *testing.T) {
	app := detailed()
	_ = unstructured.SetNestedSlice(app.Object, []any{
		map[string]any{
			"group": "apps", "version": "v1", "kind": "Deployment", "name": "podinfo", "namespace": "web",
			"status": "Synced", "health": map[string]any{"status": "Degraded", "message": "0/3 ready"},
		},
		map[string]any{
			"version": "v1", "kind": "Service", "name": "podinfo", "namespace": "web",
			"status": "OutOfSync", "health": map[string]any{"status": "Missing"},
		},
	}, "status", "resources")

	issues := Detail(app).Issues

	broken, found := issueAbout(issues, "Deployment/podinfo")
	if !found || broken.Severity != api.SeverityHigh {
		t.Fatalf("degraded issue = %+v, want the same word the issues queue uses", broken)
	}
	if broken.Detail != "0/3 ready" {
		t.Fatalf("detail = %q, want the health message", broken.Detail)
	}
	gone, found := issueAbout(issues, "Service/podinfo")
	if !found || gone.Severity != api.SeverityMedium {
		t.Fatalf("missing issue = %+v, want it ranked under a degraded one", gone)
	}
}

func TestAResourceTheOperationAlreadyBlamedIsNotRepeated(t *testing.T) {
	app := detailed()
	_ = unstructured.SetNestedField(app.Object, "Failed", "status", "operationState", "phase")
	_ = unstructured.SetNestedField(app.Object, `Deployment "podinfo" failed to apply`, "status", "operationState", "message")
	_ = unstructured.SetNestedSlice(app.Object, []any{
		map[string]any{
			"group": "apps", "version": "v1", "kind": "Deployment", "name": "podinfo", "namespace": "web",
			"health": map[string]any{"status": "Degraded"},
		},
	}, "status", "resources")

	if _, found := issueAbout(Detail(app).Issues, "Deployment/podinfo"); found {
		t.Fatal("the resource the operation already named appeared a second time")
	}
}

func TestTheSameBrokenResourceIsCountedOnce(t *testing.T) {
	app := detailed()
	entry := map[string]any{
		"group": "apps", "version": "v1", "kind": "Deployment", "name": "podinfo", "namespace": "web",
		"health": map[string]any{"status": "Degraded"},
	}
	_ = unstructured.SetNestedSlice(app.Object, []any{entry, entry}, "status", "resources")

	count := 0
	for _, one := range Detail(app).Issues {
		if one.Subject == "Deployment/podinfo" {
			count++
		}
	}
	if count != 1 {
		t.Fatalf("the same broken deployment raised %d issues, want 1", count)
	}
}

func TestHealthyResourcesRaiseNoIssue(t *testing.T) {
	if _, found := issueAbout(Detail(detailed()).Issues, "Deployment/podinfo"); found {
		t.Fatal("a healthy deployment raised an issue")
	}
}

func TestAnApplicationBeingDeletedNamesTheFinalizersHoldingIt(t *testing.T) {
	app := detailed()
	now := metav1.Now()
	app.SetDeletionTimestamp(&now)
	app.SetFinalizers([]string{"resources-finalizer.argocd.argoproj.io"})

	issue, found := issueTitled(Detail(app).Issues, "This application is being deleted")

	if !found {
		t.Fatal("an application being deleted raised no issue")
	}
	if !strings.Contains(issue.Detail, "resources-finalizer.argocd.argoproj.io") {
		t.Fatalf("detail = %q, want the finalizer named", issue.Detail)
	}
}

func TestAnApplicationBeingDeletedWithNoFinalizersSaysSo(t *testing.T) {
	app := detailed()
	now := metav1.Now()
	app.SetDeletionTimestamp(&now)
	app.SetFinalizers(nil)

	issue, _ := issueTitled(Detail(app).Issues, "This application is being deleted")

	if !strings.Contains(issue.Detail, "no finalizers left") {
		t.Fatalf("detail = %q", issue.Detail)
	}
}

func TestAFailedOperationIsAsFatalHereAsItIsInTheQueue(t *testing.T) {
	app := detailed()
	_ = unstructured.SetNestedField(app.Object, "Failed", "status", "operationState", "phase")

	issue, _ := issueAbout(Detail(app).Issues, "operation")

	if issue.Severity != api.SeverityHigh {
		t.Fatalf("severity = %q, want %q so one failure reads the same on both surfaces", issue.Severity, api.SeverityHigh)
	}
}

func TestEverySeverityComesFromTheSharedVocabulary(t *testing.T) {
	known := map[string]bool{
		api.SeverityHigh:   true,
		api.SeverityMedium: true,
		api.SeverityLow:    true,
	}
	app := detailed()
	_ = unstructured.SetNestedField(app.Object, "Failed", "status", "operationState", "phase")
	_ = unstructured.SetNestedField(app.Object, outOfSync, "status", "sync", "status")
	_ = unstructured.SetNestedSlice(app.Object, []any{
		map[string]any{"type": "ComparisonError", "message": "nope"},
		map[string]any{"type": "OrphanedResourceWarning", "message": "stray"},
		map[string]any{"type": "SomethingElse", "message": "who knows"},
	}, "status", "conditions")
	_ = unstructured.SetNestedSlice(app.Object, []any{
		map[string]any{"kind": "Service", "name": "api", "health": map[string]any{"status": "Missing"}},
	}, "status", "resources")

	issues := Detail(app).Issues

	if len(issues) < 4 {
		t.Fatalf("issues = %+v, want the whole spread", issues)
	}
	for _, one := range issues {
		if !known[one.Severity] {
			t.Fatalf("severity = %q on %q, want one the queue also uses", one.Severity, one.Title)
		}
	}
}