internal/actions/workload_test.go
Rama main · réplica de lectura del repositorio original.
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)
}
}