Ctrl K
ComunidadGuías…

internal/checks/facts_test.go

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

Archivo original ↗Preguntar a KiBot
package checks

import (
	"maps"
	"strings"
	"testing"

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

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

func node(name string, labels, spec, allocatable map[string]any) *unstructured.Unstructured {
	meta := map[string]any{"name": name}
	if labels != nil {
		meta["labels"] = labels
	}
	if spec == nil {
		spec = map[string]any{}
	}
	return &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "v1",
		"kind":       "Node",
		"metadata":   meta,
		"spec":       spec,
		"status":     map[string]any{"allocatable": allocatable},
	}}
}

func plainNode(name string, labels map[string]any) *unstructured.Unstructured {
	return node(name, labels, nil, map[string]any{"cpu": "4", "memory": "8Gi"})
}

func namespaceObj(name string, labels map[string]any) *unstructured.Unstructured {
	meta := map[string]any{"name": name}
	if labels != nil {
		meta["labels"] = labels
	}
	return &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "v1",
		"kind":       "Namespace",
		"metadata":   meta,
	}}
}

func limitRange(namespace string, bounds map[string]any) *unstructured.Unstructured {
	return &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "v1",
		"kind":       "LimitRange",
		"metadata":   map[string]any{"name": "limits", "namespace": namespace},
		"spec": map[string]any{
			"limits": []any{mergeInto(map[string]any{"type": containerLimits}, bounds)},
		},
	}}
}

func mergeInto(base, extra map[string]any) map[string]any {
	maps.Copy(base, extra)
	return base
}

func resourceQuota(namespace string, hard, used map[string]any) *unstructured.Unstructured {
	return &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "v1",
		"kind":       "ResourceQuota",
		"metadata":   map[string]any{"name": "team", "namespace": namespace},
		"status":     map[string]any{"hard": hard, "used": used},
	}}
}

func onNodeSelector(spec, selector map[string]any) map[string]any {
	spec["nodeSelector"] = selector
	return spec
}

func TestEveryFactCheckFiresOnItsOwnFaultAndOnNothingElse(t *testing.T) {
	cases := []struct {
		id      string
		objects []*unstructured.Unstructured
		clean   []*unstructured.Unstructured
	}{
		{
			id: "node-selector-matches-nothing",
			objects: []*unstructured.Unstructured{
				plainNode("worker", map[string]any{hostnameKey: "worker"}),
				deployment("api", onNodeSelector(podSpec(container("app", nil)), map[string]any{"disk": "nvme"})),
			},
			clean: []*unstructured.Unstructured{
				plainNode("worker", map[string]any{hostnameKey: "worker", "disk": "nvme"}),
				deployment("api", onNodeSelector(podSpec(container("app", nil)), map[string]any{"disk": "nvme"})),
			},
		},
		{
			id: "tolerations-miss-the-taints",
			objects: []*unstructured.Unstructured{
				node("worker", map[string]any{hostnameKey: "worker"}, map[string]any{
					"taints": []any{map[string]any{"key": "gpu", "value": "yes", "effect": "NoSchedule"}},
				}, map[string]any{"cpu": "4", "memory": "8Gi"}),
				deployment("api", podSpec(container("app", nil))),
			},
			clean: []*unstructured.Unstructured{
				plainNode("worker", map[string]any{hostnameKey: "worker"}),
				deployment("api", podSpec(container("app", nil))),
			},
		},
		{
			id: "request-exceeds-largest-node",
			objects: []*unstructured.Unstructured{
				plainNode("worker", map[string]any{hostnameKey: "worker"}),
				deployment("api", podSpec(container("app", resources(requests, map[string]any{"cpu": "16"})))),
			},
			clean: []*unstructured.Unstructured{
				plainNode("worker", map[string]any{hostnameKey: "worker"}),
				deployment("api", podSpec(container("app", resources(requests, map[string]any{"cpu": "1"})))),
			},
		},
		{
			id: "spread-needs-more-domains",
			objects: []*unstructured.Unstructured{
				plainNode("worker", map[string]any{hostnameKey: "worker", "zone": "a"}),
				replicas(deployment("api", podSpecWith(map[string]any{
					"topologySpreadConstraints": []any{map[string]any{
						"topologyKey": "zone", "whenUnsatisfiable": doNotSchedule, "maxSkew": int64(1),
					}},
				}, container("app", nil))), 4),
			},
			clean: []*unstructured.Unstructured{
				plainNode("worker", map[string]any{hostnameKey: "worker", "zone": "a"}),
				replicas(deployment("api", podSpecWith(map[string]any{
					"topologySpreadConstraints": []any{map[string]any{
						"topologyKey": "zone", "whenUnsatisfiable": "ScheduleAnyway", "maxSkew": int64(1),
					}},
				}, container("app", nil))), 4),
			},
		},
		{
			id: "anti-affinity-exceeds-nodes",
			objects: []*unstructured.Unstructured{
				plainNode("worker", map[string]any{hostnameKey: "worker"}),
				replicas(deployment("api", podSpecWith(map[string]any{
					"affinity": map[string]any{"podAntiAffinity": map[string]any{
						"requiredDuringSchedulingIgnoredDuringExecution": []any{
							map[string]any{"topologyKey": hostnameKey},
						},
					}},
				}, container("app", nil))), 3),
			},
			clean: []*unstructured.Unstructured{
				plainNode("worker", map[string]any{hostnameKey: "worker"}),
				replicas(deployment("api", podSpecWith(map[string]any{
					"affinity": map[string]any{"podAntiAffinity": map[string]any{
						"preferredDuringSchedulingIgnoredDuringExecution": []any{
							map[string]any{"topologyKey": hostnameKey},
						},
					}},
				}, container("app", nil))), 3),
			},
		},
		{
			id: "outside-limit-range",
			objects: []*unstructured.Unstructured{
				limitRange(testNamespace, map[string]any{"min": map[string]any{"memory": "128Mi"}}),
				deployment("api", podSpec(container("app", resources(requests, map[string]any{"memory": "16Mi"})))),
			},
			clean: []*unstructured.Unstructured{
				limitRange(testNamespace, map[string]any{"min": map[string]any{"memory": "128Mi"}}),
				deployment("api", podSpec(container("app", resources(requests, map[string]any{"memory": "256Mi"})))),
			},
		},
		{
			id: "quota-nearly-exhausted",
			objects: []*unstructured.Unstructured{
				resourceQuota(testNamespace,
					map[string]any{"requests.memory": "10Gi"},
					map[string]any{"requests.memory": "10Gi"}),
				deployment("api", podSpec(container("app", nil))),
			},
			clean: []*unstructured.Unstructured{
				resourceQuota(testNamespace,
					map[string]any{"requests.memory": "10Gi"},
					map[string]any{"requests.memory": "1Gi"}),
				deployment("api", podSpec(container("app", nil))),
			},
		},
		{
			id: "pod-security-would-reject",
			objects: []*unstructured.Unstructured{
				namespaceObj(testNamespace, map[string]any{enforceLabel: profileBaseline}),
				deployment("api", podSpecWith(map[string]any{"hostPID": true}, container("app", nil))),
			},
			clean: []*unstructured.Unstructured{
				namespaceObj(testNamespace, map[string]any{enforceLabel: profileBaseline}),
				deployment("api", podSpec(container("app", nil))),
			},
		},
	}

	registered := map[string]bool{}
	for _, entry := range factChecks() {
		registered[entry.id] = true
	}
	if len(cases) != len(registered) {
		t.Fatalf("%d cases cover %d registered fact checks", len(cases), len(registered))
	}

	for _, tc := range cases {
		t.Run(tc.id, func(t *testing.T) {
			if findingCount(t, report(t, tc.objects...), tc.id) == 0 {
				t.Fatalf("%s did not fire on the cluster written to trip it", tc.id)
			}
			if findingCount(t, report(t, tc.clean...), tc.id) != 0 {
				t.Fatalf("%s fired on a cluster that satisfies it", tc.id)
			}
		})
	}
}

func TestACheckSaysSoWhenTheClusterNeverReportedWhatItNeeds(t *testing.T) {
	descs := descriptors()
	delete(descs, "/v1/nodes")

	found := Run(t.Context(), newLister(), descs, api.Metrics{}, wholeCluster(), 0)

	group := groupNamed(t, found, "node-selector-matches-nothing")
	if group.Skipped == "" {
		t.Fatal("a check needing nodes ran on a cluster that reported none")
	}
	if !strings.Contains(group.Skipped, "nodes") {
		t.Fatalf("skipped said %q, want it to name nodes", group.Skipped)
	}
}

func TestAChecksNeedsAreNamedForEveryFactCheck(t *testing.T) {
	for _, entry := range factChecks() {
		if len(entry.needs) == 0 {
			t.Fatalf("%s reads the cluster's shape but declares no needs", entry.id)
		}
	}
}

func TestATolerationCoversTheTaintItNames(t *testing.T) {
	tainted := node("worker", map[string]any{hostnameKey: "worker"}, map[string]any{
		"taints": []any{map[string]any{"key": "gpu", "value": "yes", "effect": "NoSchedule"}},
	}, map[string]any{"cpu": "4", "memory": "8Gi"})

	tolerated := deployment("api", podSpecWith(map[string]any{
		"tolerations": []any{map[string]any{
			"key": "gpu", "value": "yes", "effect": "NoSchedule", "operator": "Equal",
		}},
	}, container("app", nil)))
	if findingCount(t, report(t, tainted, tolerated), "tolerations-miss-the-taints") != 0 {
		t.Fatal("a matching toleration was not accepted")
	}

	exists := deployment("api", podSpecWith(map[string]any{
		"tolerations": []any{map[string]any{"operator": "Exists"}},
	}, container("app", nil)))
	if findingCount(t, report(t, tainted, exists), "tolerations-miss-the-taints") != 0 {
		t.Fatal("a blanket Exists toleration was not accepted")
	}
}

func TestTolerationsSkipMalformedEntriesBeforeAMatch(t *testing.T) {
	tainted := node("worker", map[string]any{hostnameKey: "worker"}, map[string]any{
		"taints": []any{map[string]any{"key": "gpu", "value": "yes", "effect": "NoSchedule"}},
	}, map[string]any{"cpu": "4", "memory": "8Gi"})
	tolerated := deployment("api", podSpecWith(map[string]any{
		"tolerations": []any{
			"not a toleration",
			map[string]any{"key": "other", "value": "yes", "effect": "NoSchedule"},
			map[string]any{"key": "gpu", "value": "yes", "effect": "NoSchedule"},
		},
	}, container("app", nil)))

	if findingCount(t, report(t, tainted, tolerated), "tolerations-miss-the-taints") != 0 {
		t.Fatal("a valid toleration after malformed entries was missed")
	}
}

func TestAMalformedTolerationDoesNotHideALaterMatch(t *testing.T) {
	tainted := node("worker", map[string]any{hostnameKey: "worker"}, map[string]any{
		"taints": []any{map[string]any{"key": "gpu", "value": "yes", "effect": "NoSchedule"}},
	}, map[string]any{"cpu": "4", "memory": "8Gi"})
	tolerated := deployment("api", podSpecWith(map[string]any{
		"tolerations": []any{
			"not-an-object",
			map[string]any{"key": "gpu", "value": "yes", "effect": "NoSchedule", "operator": "Equal"},
		},
	}, container("app", nil)))

	if findingCount(t, report(t, tainted, tolerated), "tolerations-miss-the-taints") != 0 {
		t.Fatal("a malformed toleration hid a later matching one")
	}
}

func TestAPreferNoScheduleTaintDoesNotBlockAnything(t *testing.T) {
	soft := node("worker", map[string]any{hostnameKey: "worker"}, map[string]any{
		"taints": []any{map[string]any{"key": "spot", "effect": "PreferNoSchedule"}},
	}, map[string]any{"cpu": "4", "memory": "8Gi"})

	found := report(t, soft, deployment("api", podSpec(container("app", nil))))

	if findingCount(t, found, "tolerations-miss-the-taints") != 0 {
		t.Fatal("a PreferNoSchedule taint was treated as blocking")
	}
}

func TestOneUntaintedNodeIsEnough(t *testing.T) {
	tainted := node("gpu", map[string]any{hostnameKey: "gpu"}, map[string]any{
		"taints": []any{map[string]any{"key": "gpu", "effect": "NoSchedule"}},
	}, map[string]any{"cpu": "4", "memory": "8Gi"})
	free := plainNode("worker", map[string]any{hostnameKey: "worker"})

	found := report(t, tainted, free, deployment("api", podSpec(container("app", nil))))

	if findingCount(t, found, "tolerations-miss-the-taints") != 0 {
		t.Fatal("one free node did not satisfy the check")
	}
}

func TestTheLargestNodeIsTheOneCompared(t *testing.T) {
	small := node("small", map[string]any{hostnameKey: "small"}, nil, map[string]any{"cpu": "2", "memory": "4Gi"})
	big := node("big", map[string]any{hostnameKey: "big"}, nil, map[string]any{"cpu": "32", "memory": "128Gi"})
	greedy := deployment("api", podSpec(container("app", resources(requests, map[string]any{"cpu": "8"}))))

	if findingCount(t, report(t, small, big, greedy), "request-exceeds-largest-node") != 0 {
		t.Fatal("a request the big node can hold was compared against the small one")
	}
	if findingCount(t, report(t, small, greedy), "request-exceeds-largest-node") != 1 {
		t.Fatal("a request no node can hold was not reported")
	}
}

func TestTheSpreadCheckCountsDomainsNotNodes(t *testing.T) {
	spread := func(count int64) *unstructured.Unstructured {
		return replicas(deployment("api", podSpecWith(map[string]any{
			"topologySpreadConstraints": []any{map[string]any{
				"topologyKey": "zone", "whenUnsatisfiable": doNotSchedule, "maxSkew": int64(1),
			}},
		}, container("app", nil))), count)
	}
	one := plainNode("a1", map[string]any{hostnameKey: "a1", "zone": "a"})
	two := plainNode("a2", map[string]any{hostnameKey: "a2", "zone": "a"})
	three := plainNode("b1", map[string]any{hostnameKey: "b1", "zone": "b"})
	joining := plainNode("joining", map[string]any{hostnameKey: "joining"})

	if findingCount(t, report(t, joining, one, two, spread(2)), "spread-needs-more-domains") != 1 {
		t.Fatal("two nodes in one zone were counted as two domains")
	}
	if findingCount(t, report(t, one, two, three, spread(2)), "spread-needs-more-domains") != 0 {
		t.Fatal("two zones were not enough for two replicas")
	}
}

func TestSpreadConstraintsSkipMalformedEntriesAndDefaultTheSkew(t *testing.T) {
	spread := replicas(deployment("api", podSpecWith(map[string]any{
		"topologySpreadConstraints": []any{
			"not a constraint",
			map[string]any{"whenUnsatisfiable": doNotSchedule},
			map[string]any{"topologyKey": "zone", "whenUnsatisfiable": doNotSchedule},
		},
	}, container("app", nil))), 3)
	objects := []*unstructured.Unstructured{
		plainNode("unlabelled", map[string]any{hostnameKey: "unlabelled"}),
		plainNode("a", map[string]any{hostnameKey: "a", "zone": "a"}),
		plainNode("b", map[string]any{hostnameKey: "b", "zone": "b"}),
		spread,
	}

	if findingCount(t, report(t, objects...), "spread-needs-more-domains") != 1 {
		t.Fatal("the default maxSkew did not catch three replicas across two domains")
	}
}

func TestMalformedRequiredAntiAffinityIsIgnored(t *testing.T) {
	cases := []Subject{
		{Pod: map[string]any{"affinity": map[string]any{"podAntiAffinity": "not an object"}}},
		{Pod: map[string]any{"affinity": map[string]any{"podAntiAffinity": map[string]any{
			"requiredDuringSchedulingIgnoredDuringExecution": []any{"not a rule"},
		}}}},
	}
	for _, subject := range cases {
		if got := requiredAntiAffinity(subject); got != nil {
			t.Fatalf("anti-affinity = %v, want malformed state ignored", got)
		}
	}
}

func TestAntiAffinityOnlyCountsHostnameDomainsWithUsableNodes(t *testing.T) {
	empty := newCorpus(nil, nil, nil, allTargets(), nil, nil)
	wrongTopology := Subject{Replicas: 3, Pod: map[string]any{"affinity": map[string]any{
		"podAntiAffinity": map[string]any{"requiredDuringSchedulingIgnoredDuringExecution": []any{
			map[string]any{"topologyKey": "zone"},
		}},
	}}}
	if detail, _ := antiAffinityExceedsNodes(wrongTopology, empty); detail != "" {
		t.Fatalf("wrong topology detail = %q, want none", detail)
	}
	wrongTopology.Pod = map[string]any{"affinity": map[string]any{
		"podAntiAffinity": map[string]any{"requiredDuringSchedulingIgnoredDuringExecution": []any{
			map[string]any{"topologyKey": hostnameKey},
		}},
	}}
	if detail, _ := antiAffinityExceedsNodes(wrongTopology, empty); detail != "" {
		t.Fatalf("empty cluster detail = %q, want none", detail)
	}
}

func TestFullestQuotaEntrySkipsUnreadableValues(t *testing.T) {
	if name, share := fullestEntry("not a map", map[string]any{}); name != "" || share != 0 {
		t.Fatalf("invalid maps = %q %d, want empty", name, share)
	}
	hard := map[string]any{"bad": "not a quantity", "zero": "0", "valid": "10"}
	used := map[string]any{"valid": "not a quantity"}
	if name, share := fullestEntry(hard, used); name != "" || share != 0 {
		t.Fatalf("unreadable values = %q %d, want empty", name, share)
	}
}

func TestBaselinePodSecurityNamesPrivilegeAndHostPath(t *testing.T) {
	unsafe := deployment("api", podSpecWith(map[string]any{
		"volumes": []any{map[string]any{"name": "host", "hostPath": map[string]any{"path": "/srv/data"}}},
	}, container("app", map[string]any{"securityContext": map[string]any{"privileged": true}})))

	found := report(t, namespaceObj(testNamespace, map[string]any{enforceLabel: profileBaseline}), unsafe)
	detail := onlyFinding(t, found, "pod-security-would-reject").Detail

	if !strings.Contains(detail, "privileged") || !strings.Contains(detail, "hostPath volumes") {
		t.Fatalf("detail = %q, want both baseline violations", detail)
	}
}

func TestMalformedSpreadConstraintsDoNotHideALaterValidOne(t *testing.T) {
	workload := replicas(deployment("api", podSpecWith(map[string]any{
		"topologySpreadConstraints": []any{
			"not-an-object",
			map[string]any{"whenUnsatisfiable": doNotSchedule},
			map[string]any{
				"topologyKey": "zone", "whenUnsatisfiable": doNotSchedule, "maxSkew": int64(1),
			},
		},
	}, container("app", nil))), 2)
	one := plainNode("a1", map[string]any{hostnameKey: "a1", "zone": "a"})
	two := plainNode("a2", map[string]any{hostnameKey: "a2", "zone": "a"})

	if findingCount(t, report(t, one, two, workload), "spread-needs-more-domains") != 1 {
		t.Fatal("a malformed constraint hid a later valid one")
	}
}

func TestTheQuotaCheckNamesTheFullestEntry(t *testing.T) {
	quota := resourceQuota(testNamespace,
		map[string]any{"requests.cpu": "10", "requests.memory": "10Gi"},
		map[string]any{"requests.cpu": "1", "requests.memory": "10Gi"})

	found := report(t, quota, deployment("api", podSpec(container("app", nil))))

	if detail := onlyFinding(t, found, "quota-nearly-exhausted").Detail; !strings.Contains(detail, "requests.memory") {
		t.Fatalf("detail was %q, want the fullest entry named", detail)
	}
}

func TestMalformedQuotaEntriesDoNotHideALaterValidOne(t *testing.T) {
	quota := resourceQuota(testNamespace,
		map[string]any{
			"a-bad-hard":      true,
			"b-bad-used":      "10",
			"requests.memory": "10Gi",
		},
		map[string]any{
			"b-bad-used":      true,
			"requests.memory": "10Gi",
		})

	found := report(t, quota, deployment("api", podSpec(container("app", nil))))
	detail := onlyFinding(t, found, "quota-nearly-exhausted").Detail
	if !strings.Contains(detail, "requests.memory") {
		t.Fatalf("detail was %q, want the valid quota entry named", detail)
	}
}

func TestAQuotaWithNoStatusIsSkipped(t *testing.T) {
	bare := &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "v1",
		"kind":       "ResourceQuota",
		"metadata":   map[string]any{"name": "team", "namespace": testNamespace},
	}}

	found := report(t, bare, deployment("api", podSpec(container("app", nil))))

	if findingCount(t, found, "quota-nearly-exhausted") != 0 {
		t.Fatal("a quota the apiserver had not filled in yet was reported")
	}
}

func TestPodSecurityOnlyJudgesTheLevelTheNamespaceEnforces(t *testing.T) {
	escalating := deployment("api", podSpec(container("app", nil)))

	baseline := report(t, namespaceObj(testNamespace, map[string]any{enforceLabel: profileBaseline}), escalating)
	if findingCount(t, baseline, "pod-security-would-reject") != 0 {
		t.Fatal("baseline rejected a pod that only breaks restricted")
	}

	strict := report(t, namespaceObj(testNamespace, map[string]any{enforceLabel: profileStrict}), escalating)
	if detail := onlyFinding(t, strict, "pod-security-would-reject").Detail; !strings.Contains(detail, "allowPrivilegeEscalation != false") {
		t.Fatalf("detail was %q, want the restricted control named", detail)
	}
}

func TestANamespaceEnforcingNothingIsNotJudged(t *testing.T) {
	found := report(t,
		namespaceObj(testNamespace, map[string]any{enforceLabel: "privileged"}),
		deployment("api", podSpecWith(map[string]any{"hostPID": true}, container("app", nil))))

	if findingCount(t, found, "pod-security-would-reject") != 0 {
		t.Fatal("a namespace at the privileged level rejected something")
	}
}

func TestANamespaceObjectIsNotAuditedAsAWorkload(t *testing.T) {
	found := report(t, namespaceObj(testNamespace, nil), plainNode("worker", nil))

	if found.Scanned != 0 {
		t.Fatalf("scanned %d subjects, want 0 from a cluster holding only context objects", found.Scanned)
	}
}

func TestClusterObjectsOfTheWrongShapeAreSkipped(t *testing.T) {
	odd := &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "v1",
		"kind":       "Node",
		"metadata":   map[string]any{"name": "worker"},
		"spec":       map[string]any{"taints": []any{"not-an-object"}},
		"status":     map[string]any{"allocatable": map[string]any{"cpu": true}},
	}}
	workload := deployment("api", podSpecWith(map[string]any{
		"nodeSelector":              map[string]any{"disk": int64(7)},
		"tolerations":               []any{"not-an-object"},
		"topologySpreadConstraints": []any{"not-an-object"},
	}, container("app", resources(requests, map[string]any{"cpu": "1"}))))

	found := report(t, odd, workload)

	for _, id := range []string{
		"node-selector-matches-nothing",
		"tolerations-miss-the-taints",
		"request-exceeds-largest-node",
		"spread-needs-more-domains",
	} {
		if findingCount(t, found, id) != 0 {
			t.Fatalf("%s reported something from a field of the wrong shape", id)
		}
	}
}

func TestATolerationWhoseEffectDiffersDoesNotCover(t *testing.T) {
	tainted := node("worker", map[string]any{hostnameKey: "worker"}, map[string]any{
		"taints": []any{map[string]any{"key": "gpu", "effect": "NoExecute"}},
	}, map[string]any{"cpu": "4", "memory": "8Gi"})
	wrongEffect := deployment("api", podSpecWith(map[string]any{
		"tolerations": []any{map[string]any{"key": "gpu", "operator": "Exists", "effect": "NoSchedule"}},
	}, container("app", nil)))

	if findingCount(t, report(t, tainted, wrongEffect), "tolerations-miss-the-taints") != 1 {
		t.Fatal("a toleration for a different effect was accepted")
	}
}

func TestANodeWithNoAllocatableIsNotTheLargest(t *testing.T) {
	bare := &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "v1", "kind": "Node",
		"metadata": map[string]any{"name": "joining"},
	}}
	small := node("small", nil, nil, map[string]any{"cpu": "2", "memory": "4Gi"})
	greedy := deployment("api", podSpec(container("app", resources(requests, map[string]any{"cpu": "8"}))))

	if findingCount(t, report(t, bare, small, greedy), "request-exceeds-largest-node") != 1 {
		t.Fatal("a node that reports no allocatable hid a request nothing can hold")
	}
}

func TestALimitRangeForSomethingOtherThanAContainerIsIgnored(t *testing.T) {
	podRange := &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "v1", "kind": "LimitRange",
		"metadata": map[string]any{"name": "limits", "namespace": testNamespace},
		"spec": map[string]any{"limits": []any{
			map[string]any{"type": "Pod", "min": map[string]any{"memory": "128Mi"}},
		}},
	}}
	small := deployment("api", podSpec(container("app", resources(requests, map[string]any{"memory": "16Mi"}))))

	if findingCount(t, report(t, podRange, small), "outside-limit-range") != 0 {
		t.Fatal("a Pod-scoped LimitRange was applied to a container")
	}
}

func TestARequestAboveTheLimitRangeMaximumIsReported(t *testing.T) {
	capped := limitRange(testNamespace, map[string]any{"max": map[string]any{"cpu": "1"}})
	greedy := deployment("api", podSpec(container("app", resources(requests, map[string]any{"cpu": "4"}))))

	if detail := onlyFinding(t, report(t, capped, greedy), "outside-limit-range").Detail; !strings.Contains(detail, "maximum") {
		t.Fatalf("detail was %q, want the maximum named", detail)
	}
}