// Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package resolve import ( "testing" "github.com/google/go-cmp/cmp" "gopkg.in/yaml.v3" "k8s.io/apimachinery/pkg/labels" ) var ( webSelector = selector(`app=web`) notWebSelector = selector(`app!=web`) nopSelector = selector(`foo!=bark`) ) const ( webPod = `apiVersion: v1 kind: Pod metadata: labels: # comments should be preserved app: web name: rss-site ` dbPod = `apiVersion: v1 kind: Pod metadata: labels: # comments should be preserved app: db name: rss-db ` podList = `apiVersion: v1 kind: List metadata: resourceVersion: "" selfLink: "" items: - apiVersion: v1 kind: Pod metadata: labels: app: web name: rss-site - apiVersion: v1 kind: Pod metadata: labels: app: db name: rss-db ` webPodList = `apiVersion: v1 kind: List metadata: resourceVersion: "" selfLink: "" items: - apiVersion: v1 kind: Pod metadata: labels: app: web name: rss-site ` dbPodList = `apiVersion: v1 kind: List metadata: resourceVersion: "" selfLink: "" items: - apiVersion: v1 kind: Pod metadata: labels: app: db name: rss-db ` ) func TestMatchesSelector(t *testing.T) { tests := []struct { desc string input string selector labels.Selector output string matches bool }{{ desc: "single object with matching selector", input: webPod, selector: webSelector, output: webPod, matches: true, }, { desc: "single object with non-matching selector", input: webPod, selector: notWebSelector, matches: false, }, { desc: "single object with noop selector", input: dbPod, selector: nopSelector, output: dbPod, matches: true, }, { desc: "selector matching elements of list object", input: podList, selector: webSelector, output: webPodList, matches: true, }, { desc: "selector matching other elements of list object", input: podList, selector: notWebSelector, output: dbPodList, matches: true, }, { desc: "selector matching all elements of list object", input: podList, selector: labels.Everything(), output: podList, matches: true, }, { desc: "selector matching no elements of list object", input: podList, selector: labels.Nothing(), matches: false, }, { desc: "null node", input: "!!null", selector: labels.Everything(), matches: false, }} for _, test := range tests { t.Run(test.desc, func(t *testing.T) { doc := strToYAML(t, test.input) matches, err := MatchesSelector(doc, test.selector) if err != nil { t.Fatalf("unexpected error: %v", err) } if matches != test.matches { t.Errorf("unexpected result: got %v - want %v", matches, test.matches) } // assert doc is mutated correctly if test.output != "" { // Normalize whitespace formatting output := normalizeYAML(t, test.output) if diff := cmp.Diff(output, yamlToStr(t, doc)); diff != "" { t.Errorf("unexpected diff (-want, +got) %v", diff) } } }) } } func TestSelectorFailure(t *testing.T) { tests := []struct { desc string input string }{ { desc: "not an object", input: "image: some.go/package", }, { desc: "not an object in a list", input: `apiVersion: v1 kind: List metadata: resourceVersion: "" selfLink: "" items: - blah: ha `, }, { desc: "not a valid list", input: `apiVersion: v1 kind: List `, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { _, err := MatchesSelector(strToYAML(t, test.input), labels.Everything()) if err == nil { t.Error("expected error") } }) } } func selector(s string) labels.Selector { selector, err := labels.Parse(s) if err != nil { panic("unable to parse selector " + s) } return selector } func normalizeYAML(t *testing.T, yuml string) string { t.Helper() return yamlToStr(t, strToYAML(t, yuml)) } func yamlToStr(t *testing.T, node *yaml.Node) string { result, err := yaml.Marshal(node) if err != nil { t.Fatalf("error marshalling yaml: %v", err) } return string(result) } func strToYAML(t *testing.T, yuml string) *yaml.Node { t.Helper() var node yaml.Node if err := yaml.Unmarshal([]byte(yuml), &node); err != nil { t.Fatalf("error unmarshalling yaml: %v\n%v", err, yuml) } return &node }