mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-04-05 18:27:23 +00:00
294 lines
7.4 KiB
Go
294 lines
7.4 KiB
Go
// Copyright 2020-2025 Project Capsule Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package meta_test
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
"github.com/projectcapsule/capsule/pkg/api/meta"
|
|
)
|
|
|
|
func TestFreezeLabel(t *testing.T) {
|
|
ns := &corev1.Namespace{}
|
|
ns.SetLabels(map[string]string{})
|
|
|
|
// absent
|
|
if meta.FreezeLabelTriggers(ns) {
|
|
t.Errorf("expected FreezeLabelTriggers to be false when label is absent")
|
|
}
|
|
|
|
// set to trigger
|
|
ns.Labels[meta.FreezeLabel] = meta.ValueTrue
|
|
if !meta.FreezeLabelTriggers(ns) {
|
|
t.Errorf("expected FreezeLabelTriggers to be true when label is set to trigger")
|
|
}
|
|
|
|
ns.Labels[meta.FreezeLabel] = "false"
|
|
if meta.FreezeLabelTriggers(ns) {
|
|
t.Errorf("expected FreezeLabelTriggers to be false when label is not set to trigger")
|
|
}
|
|
|
|
// remove
|
|
meta.FreezeLabelRemove(ns)
|
|
if _, ok := ns.Labels[meta.FreezeLabel]; ok {
|
|
t.Errorf("expected FreezeLabel to be removed")
|
|
}
|
|
}
|
|
|
|
func TestOwnerPromotionLabel(t *testing.T) {
|
|
ns := &corev1.Namespace{}
|
|
ns.SetLabels(map[string]string{})
|
|
|
|
if meta.OwnerPromotionLabelTriggers(ns) {
|
|
t.Errorf("expected OwnerPromotionLabelTriggers to be false when label is absent")
|
|
}
|
|
|
|
ns.Labels[meta.OwnerPromotionLabel] = meta.ValueTrue
|
|
if !meta.OwnerPromotionLabelTriggers(ns) {
|
|
t.Errorf("expected OwnerPromotionLabelTriggers to be true when label is set to trigger")
|
|
}
|
|
|
|
ns.Labels[meta.OwnerPromotionLabel] = "false"
|
|
if meta.OwnerPromotionLabelTriggers(ns) {
|
|
t.Errorf("expected OwnerPromotionLabelTriggers to be false when label is not set to trigger")
|
|
}
|
|
|
|
meta.OwnerPromotionLabelRemove(ns)
|
|
if _, ok := ns.Labels[meta.OwnerPromotionLabel]; ok {
|
|
t.Errorf("expected OwnerPromotionLabel to be removed")
|
|
}
|
|
}
|
|
|
|
func TestSetFilteredLabels(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
obj *unstructured.Unstructured
|
|
filter map[string]struct{}
|
|
want map[string]string
|
|
}
|
|
|
|
newObjWithLabels := func(labels map[string]string) *unstructured.Unstructured {
|
|
u := &unstructured.Unstructured{}
|
|
u.SetLabels(labels)
|
|
return u
|
|
}
|
|
|
|
tests := []testCase{
|
|
{
|
|
name: "nil obj - no panic",
|
|
obj: nil,
|
|
filter: map[string]struct{}{"a": {}},
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "empty filter - object unchanged",
|
|
obj: newObjWithLabels(map[string]string{"a": "1", "b": "2"}),
|
|
filter: map[string]struct{}{},
|
|
want: map[string]string{"a": "1", "b": "2"},
|
|
},
|
|
{
|
|
name: "nil labels - stays nil (no-op removal)",
|
|
obj: newObjWithLabels(nil),
|
|
filter: map[string]struct{}{"a": {}},
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "removes single reserved label",
|
|
obj: newObjWithLabels(map[string]string{"keep": "x", "rm": "y"}),
|
|
filter: map[string]struct{}{"rm": {}},
|
|
want: map[string]string{"keep": "x"},
|
|
},
|
|
{
|
|
name: "removes multiple reserved labels",
|
|
obj: newObjWithLabels(map[string]string{"a": "1", "b": "2", "c": "3"}),
|
|
filter: map[string]struct{}{"a": {}, "c": {}},
|
|
want: map[string]string{"b": "2"},
|
|
},
|
|
{
|
|
name: "filter contains keys not present - unchanged",
|
|
obj: newObjWithLabels(map[string]string{"a": "1"}),
|
|
filter: map[string]struct{}{"missing": {}},
|
|
want: map[string]string{"a": "1"},
|
|
},
|
|
{
|
|
name: "removes all labels -> labels becomes empty map or nil (accept either)",
|
|
obj: newObjWithLabels(map[string]string{"a": "1"}),
|
|
filter: map[string]struct{}{"a": {}},
|
|
want: map[string]string{},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
meta.SetFilteredLabels(tc.obj, tc.filter)
|
|
|
|
if tc.obj == nil {
|
|
return
|
|
}
|
|
|
|
got := tc.obj.GetLabels()
|
|
|
|
if tc.want != nil && len(tc.want) == 0 {
|
|
if got == nil || len(got) == 0 {
|
|
return
|
|
}
|
|
t.Fatalf("expected labels to be empty or nil, got: %#v", got)
|
|
}
|
|
|
|
if !reflect.DeepEqual(got, tc.want) {
|
|
t.Fatalf("labels mismatch\nwant: %#v\ngot: %#v", tc.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetFilteredLabels_DoesNotMutateFilter(t *testing.T) {
|
|
u := &unstructured.Unstructured{}
|
|
u.SetLabels(map[string]string{"a": "1", "b": "2"})
|
|
|
|
filter := map[string]struct{}{"a": {}}
|
|
filterBefore := copyStructSet(filter)
|
|
|
|
meta.SetFilteredLabels(u, filter)
|
|
|
|
if !reflect.DeepEqual(filter, filterBefore) {
|
|
t.Fatalf("filter map was mutated\nbefore: %#v\nafter: %#v", filterBefore, filter)
|
|
}
|
|
}
|
|
|
|
func copyStructSet(in map[string]struct{}) map[string]struct{} {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
out := make(map[string]struct{}, len(in))
|
|
for k := range in {
|
|
out[k] = struct{}{}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestLabelsChanged(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type tc struct {
|
|
name string
|
|
keys []string
|
|
oldLabels map[string]string
|
|
newLabels map[string]string
|
|
want bool
|
|
}
|
|
|
|
tests := []tc{
|
|
{
|
|
name: "no keys => unchanged (false)",
|
|
keys: nil,
|
|
oldLabels: map[string]string{"a": "1"},
|
|
newLabels: map[string]string{"a": "2"},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "key unchanged => false",
|
|
keys: []string{"a"},
|
|
oldLabels: map[string]string{"a": "1"},
|
|
newLabels: map[string]string{"a": "1"},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "value changed => true",
|
|
keys: []string{"a"},
|
|
oldLabels: map[string]string{"a": "1"},
|
|
newLabels: map[string]string{"a": "2"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "key added => true",
|
|
keys: []string{"a"},
|
|
oldLabels: map[string]string{},
|
|
newLabels: map[string]string{"a": "1"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "key removed => true",
|
|
keys: []string{"a"},
|
|
oldLabels: map[string]string{"a": "1"},
|
|
newLabels: map[string]string{},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "old nil new has key => true",
|
|
keys: []string{"a"},
|
|
oldLabels: nil,
|
|
newLabels: map[string]string{"a": "1"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "old has key new nil => true",
|
|
keys: []string{"a"},
|
|
oldLabels: map[string]string{"a": "1"},
|
|
newLabels: nil,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "both nil and key missing => false",
|
|
keys: []string{"a"},
|
|
oldLabels: nil,
|
|
newLabels: nil,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "multiple keys: one changed => true",
|
|
keys: []string{"a", "b"},
|
|
oldLabels: map[string]string{"a": "1", "b": "2"},
|
|
newLabels: map[string]string{"a": "1", "b": "3"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "multiple keys: only non-watched key changed => false",
|
|
keys: []string{"a"},
|
|
oldLabels: map[string]string{"a": "1", "x": "old"},
|
|
newLabels: map[string]string{"a": "1", "x": "new"},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "watched key absent in both even if other keys differ => false",
|
|
keys: []string{"a"},
|
|
oldLabels: map[string]string{"x": "1"},
|
|
newLabels: map[string]string{"x": "2"},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "duplicate keys in keys slice still behaves correctly",
|
|
keys: []string{"a", "a"},
|
|
oldLabels: map[string]string{"a": "1"},
|
|
newLabels: map[string]string{"a": "1"},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "duplicate keys in keys slice with change => true",
|
|
keys: []string{"a", "a"},
|
|
oldLabels: map[string]string{"a": "1"},
|
|
newLabels: map[string]string{"a": "2"},
|
|
want: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got := meta.LabelsChanged(tt.keys, tt.oldLabels, tt.newLabels)
|
|
if got != tt.want {
|
|
t.Fatalf("LabelsChanged(keys=%v, old=%v, new=%v) = %v, want %v",
|
|
tt.keys, tt.oldLabels, tt.newLabels, got, tt.want,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|