// 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, ) } }) } }