Ctrl K
ComunidadGuías…

internal/actions/workload_test.go

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

Archivo original ↗Preguntar a KiBot
package actions

import (
	"context"
	"strings"
	"testing"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	dynamicfake "k8s.io/client-go/dynamic/fake"
	k8sfake "k8s.io/client-go/kubernetes/fake"

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

func readDeployment(t *testing.T, client *dynamicfake.FakeDynamicClient) *unstructured.Unstructured {
	t.Helper()
	got, err := client.Resource(deploymentGVR).
		Namespace("shop").
		Get(context.Background(), "web", metav1.GetOptions{})
	if err != nil {
		t.Fatalf("read back: %v", err)
	}
	return got
}

func TestScalePatchesTheScaleSubresource(t *testing.T) {
	client := dynClient(newDeployment(1))
	seen := recordPatches(client)
	service := serviceFor(client, k8sfake.NewClientset())

	_, err := service.Do(context.Background(), Request{Ref: deploymentRef(), Action: Scale, Replicas: 3}, stamp)
	if err != nil {
		t.Fatalf("scale: %v", err)
	}

	if len(*seen) != 1 {
		t.Fatalf("sent %d patches, want 1", len(*seen))
	}
	if (*seen)[0].subresource != "scale" {
		t.Fatalf("subresource = %q, want scale", (*seen)[0].subresource)
	}
	if (*seen)[0].body != `{"spec":{"replicas":3}}` {
		t.Fatalf("patch = %s", (*seen)[0].body)
	}
}

func TestScaleReachesZero(t *testing.T) {
	client := dynClient(newDeployment(2))
	seen := recordPatches(client)
	service := serviceFor(client, k8sfake.NewClientset())

	result, err := service.Do(context.Background(), Request{Ref: deploymentRef(), Action: Scale, Replicas: 0}, stamp)
	if err != nil {
		t.Fatalf("scale: %v", err)
	}

	if (*seen)[0].body != `{"spec":{"replicas":0}}` {
		t.Fatalf("patch = %s", (*seen)[0].body)
	}
	if !strings.Contains(result.Message, "to 0 replicas") {
		t.Fatalf("message = %q", result.Message)
	}
}

func TestScaleNamesASingleReplicaInTheSingular(t *testing.T) {
	client := dynClient(newDeployment(3))
	service := serviceFor(client, k8sfake.NewClientset())

	result, err := service.Do(context.Background(), Request{Ref: deploymentRef(), Action: Scale, Replicas: 1}, stamp)
	if err != nil {
		t.Fatalf("scale: %v", err)
	}

	if !strings.Contains(result.Message, "to 1 replica.") {
		t.Fatalf("message = %q", result.Message)
	}
}

func TestScaleRejectsANegativeCount(t *testing.T) {
	client := dynClient(newDeployment(1))
	seen := recordPatches(client)
	service := serviceFor(client, k8sfake.NewClientset())

	_, err := service.Do(context.Background(), Request{Ref: deploymentRef(), Action: Scale, Replicas: -1}, stamp)

	if err == nil {
		t.Fatal("expected a negative replica count to be rejected")
	}
	if len(*seen) != 0 {
		t.Fatalf("sent %d patches, want none", len(*seen))
	}
}

func TestScaleRejectsAResourceWithoutAScaleSubresource(t *testing.T) {
	client := dynClient()
	service := serviceFor(client, k8sfake.NewClientset())
	ref := api.ObjectRef{Version: "v1", Resource: "configmaps", Namespace: "shop", Name: "settings"}

	_, err := service.Do(context.Background(), Request{Ref: ref, Action: Scale, Replicas: 2}, stamp)

	if err == nil {
		t.Fatal("expected configmaps to reject scale")
	}
}

func TestScalePropagatesAnAPIError(t *testing.T) {
	service := serviceFor(dynClient(), k8sfake.NewClientset())

	_, err := service.Do(context.Background(), Request{Ref: deploymentRef(), Action: Scale, Replicas: 2}, stamp)

	if err == nil {
		t.Fatal("expected scaling a missing deployment to fail")
	}
}

func TestRestartStampsThePodTemplate(t *testing.T) {
	client := dynClient(newDeployment(1))
	seen := recordPatches(client)
	service := serviceFor(client, k8sfake.NewClientset())

	result, err := service.Do(context.Background(), Request{Ref: deploymentRef(), Action: Restart}, stamp)
	if err != nil {
		t.Fatalf("restart: %v", err)
	}

	want := `{"spec":{"template":{"metadata":{"annotations":{"kubectl.kubernetes.io/restartedAt":"2026-07-31T12:00:00Z"}}}}}`
	if (*seen)[0].body != want {
		t.Fatalf("patch = %s", (*seen)[0].body)
	}
	if (*seen)[0].subresource != "" {
		t.Fatalf("subresource = %q, want the main resource", (*seen)[0].subresource)
	}
	if !strings.Contains(result.Message, "2026-07-31T12:00:00Z") {
		t.Fatalf("message = %q", result.Message)
	}
}

func TestRestartKeepsTheOtherTemplateAnnotations(t *testing.T) {
	client := dynClient(newDeployment(1))
	service := serviceFor(client, k8sfake.NewClientset())

	_, err := service.Do(context.Background(), Request{Ref: deploymentRef(), Action: Restart}, stamp)
	if err != nil {
		t.Fatalf("restart: %v", err)
	}

	got := readDeployment(t, client)
	annotations, _, _ := unstructured.NestedStringMap(got.Object, "spec", "template", "metadata", "annotations")
	if annotations["owner"] != "platform" {
		t.Fatalf("restart dropped the owner annotation: %v", annotations)
	}
	if annotations[restartAnnotation] == "" {
		t.Fatalf("restart annotation missing: %v", annotations)
	}
}

func TestRestartLeavesTheReplicaCountAlone(t *testing.T) {
	client := dynClient(newDeployment(4))
	service := serviceFor(client, k8sfake.NewClientset())

	_, err := service.Do(context.Background(), Request{Ref: deploymentRef(), Action: Restart}, stamp)
	if err != nil {
		t.Fatalf("restart: %v", err)
	}

	got := readDeployment(t, client)
	replicas, _, _ := unstructured.NestedInt64(got.Object, "spec", "replicas")
	if replicas != 4 {
		t.Fatalf("spec.replicas = %d, want 4", replicas)
	}
}

func TestRestartRejectsAResourceWithoutAPodTemplate(t *testing.T) {
	service := serviceFor(dynClient(), k8sfake.NewClientset())
	ref := api.ObjectRef{Group: "apps", Version: "v1", Resource: "replicasets", Namespace: "shop", Name: "web-1"}

	_, err := service.Do(context.Background(), Request{Ref: ref, Action: Restart}, stamp)

	if err == nil {
		t.Fatal("expected replicasets to reject restart")
	}
}

func TestRestartPropagatesAnAPIError(t *testing.T) {
	service := serviceFor(dynClient(), k8sfake.NewClientset())

	_, err := service.Do(context.Background(), Request{Ref: deploymentRef(), Action: Restart}, stamp)

	if err == nil {
		t.Fatal("expected restarting a missing deployment to fail")
	}
}

func TestScaleTargetsAClusterScopedResourceWithoutANamespace(t *testing.T) {
	client := dynClient(newNode(false))
	service := serviceFor(client, k8sfake.NewClientset())

	_, err := service.Do(context.Background(), Request{Ref: nodeRef(), Action: Cordon}, stamp)
	if err != nil {
		t.Fatalf("cordon: %v", err)
	}
}