Skip to content

Commit

Permalink
add shoot matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
m00g3n committed May 20, 2024
1 parent fddbee3 commit 5f069a8
Show file tree
Hide file tree
Showing 3 changed files with 299 additions and 0 deletions.
131 changes: 131 additions & 0 deletions internal/testing/shoot/matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package shoot

import (
"fmt"
"reflect"
"strings"

"github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/onsi/gomega"
"github.com/onsi/gomega/types"
"sigs.k8s.io/yaml"
)

var (
errInvalidType = fmt.Errorf("invalid type")
)

type Matcher struct {
toMatch interface{}
fails []string
}

func NewMatcher(i interface{}) types.GomegaMatcher {
return &Matcher{
toMatch: i,
}
}

func getShoot(i interface{}) (shoot v1beta1.Shoot, err error) {
if i == nil {
return v1beta1.Shoot{}, fmt.Errorf("invalid value nil")
}

switch v := i.(type) {
case string:
err = yaml.Unmarshal([]byte(v), &shoot)
return shoot, err

case v1beta1.Shoot:
return v, nil

case *v1beta1.Shoot:
return *v, nil

default:
return v1beta1.Shoot{}, fmt.Errorf(`%w: %s`, errInvalidType, reflect.TypeOf(v))
}
}

type matcher struct {
types.GomegaMatcher
path string
actual interface{}
}

func (m *Matcher) Match(actual interface{}) (success bool, err error) {
aShoot, err := getShoot(actual)
if err != nil {
return false, err
}

eShoot, err := getShoot(m.toMatch)
if err != nil {
return false, err
}

for _, matcher := range []matcher{
{
GomegaMatcher: gomega.Equal(eShoot.TypeMeta),
actual: aShoot.TypeMeta,
},
{
GomegaMatcher: gomega.Equal(eShoot.Name),
actual: aShoot.Name,
path: "metadata/name",
},
{
GomegaMatcher: gomega.Equal(eShoot.Namespace),
actual: aShoot.Namespace,
path: "metadata/namespace",
},
{
GomegaMatcher: gomega.Equal(eShoot.Labels),
actual: aShoot.Labels,
path: "metadata/labels",
},
{
GomegaMatcher: gomega.Equal(eShoot.Annotations),
actual: aShoot.Annotations,
path: "metadata/annotations",
},
{
GomegaMatcher: gomega.Equal(eShoot.OwnerReferences),
actual: aShoot.OwnerReferences,
path: "metadata/ownerReferences",
},
{
GomegaMatcher: gomega.Equal(eShoot.Finalizers),
actual: aShoot.Finalizers,
path: "metadata/finalizers",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec),
actual: aShoot.Spec,
path: "spec",
},
} {
ok, err := matcher.Match(matcher.actual)
if err != nil {
return false, err
}

if !ok {
msg := matcher.FailureMessage(matcher.actual)
if matcher.path != "" {
msg = fmt.Sprintf("%s: %s", matcher.path, msg)
}
m.fails = append(m.fails, msg)
}
}

return len(m.fails) == 0, nil
}

func (m *Matcher) NegatedFailureMessage(_ interface{}) string {
return "expected should not equal actual"
}

func (m *Matcher) FailureMessage(_ interface{}) string {
return strings.Join(m.fails, "\n")
}
155 changes: 155 additions & 0 deletions internal/testing/shoot/matcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package shoot_test

import (
"github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/kyma-project/infrastructure-manager/internal/testing/shoot"
. "github.com/onsi/ginkgo/v2" //nolint:revive
. "github.com/onsi/gomega" //nolint:revive
corev1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type deepCpOpts = func(*v1beta1.Shoot)

func withName(name string) deepCpOpts {
return func(s *v1beta1.Shoot) {
s.Name = name
}
}

func withNamespace(namespace string) deepCpOpts {
return func(s *v1beta1.Shoot) {
s.Namespace = namespace
}
}

func withLabels(labels map[string]string) deepCpOpts {
return func(s *v1beta1.Shoot) {
s.Labels = labels
}
}

func withFinalizers(finalizers []string) deepCpOpts {
return func(s *v1beta1.Shoot) {
s.Finalizers = finalizers
}
}

func withOwnerReferences(ownerReferences []corev1.OwnerReference) deepCpOpts {
return func(s *v1beta1.Shoot) {
s.OwnerReferences = ownerReferences
}
}

func withAnnotations(annotations map[string]string) deepCpOpts {
return func(s *v1beta1.Shoot) {
s.Annotations = annotations
}
}

func withShootSpec(spec v1beta1.ShootSpec) deepCpOpts {
return func(s *v1beta1.Shoot) {
s.Spec = spec
}
}

// nolint: unparam
func deepCp(s v1beta1.Shoot, opts ...deepCpOpts) v1beta1.Shoot {
for _, opt := range opts {
opt(&s)
}

return s
}

func testInvalidArgs(actual, expected interface{}) {
matcher := shoot.NewMatcher(expected)
_, err := matcher.Match(actual)
Expect(err).To(HaveOccurred())
}

func testResults(actual, expected interface{}, expectedMatch bool) {
matcher := shoot.NewMatcher(expected)
actualMatch, err := matcher.Match(actual)
Expect(err).ShouldNot(HaveOccurred())
Expect(actualMatch).Should(Equal(expectedMatch), matcher.FailureMessage(actual))
}

var _ = Describe(":: shoot matcher :: ", func() {
var empty v1beta1.Shoot

DescribeTable(
"checking invalid args :: ",
testInvalidArgs,
Entry("when actual is nil", nil, empty),
Entry("when expected is nil", "", nil),
)

DescribeTable(
"checking results :: ",
testResults,
Entry(
"should match empty and zero values",
"",
empty,
true,
),
Entry(
"should match copies of the same instance",
deepCp(empty),
deepCp(empty),
true,
),
Entry(
"should detect name difference",
deepCp(empty, withName("test1")),
deepCp(empty, withName("test2")),
false,
),
Entry(
"should detect namespace difference",
deepCp(empty, withNamespace("test1")),
deepCp(empty, withNamespace("test2")),
false,
),
Entry(
"should detect difference in labels",
deepCp(empty, withLabels(map[string]string{"test": "me"})),
deepCp(empty, withLabels(map[string]string{})),
false,
),
Entry(
"should detect difference in annotations",
deepCp(empty, withAnnotations(map[string]string{"test": "me"})),
deepCp(empty, withAnnotations(map[string]string{"test": "it"})),
false,
),
Entry(
"should detect differences in finalizers",
deepCp(empty, withFinalizers([]string{"test", "me"})),
deepCp(empty, withFinalizers([]string{"test", "me 2"})),
false,
),
Entry(
"should detect differences in owner references",
deepCp(empty, withOwnerReferences([]corev1.OwnerReference{
{Name: "test1", UID: "1"},
{Name: "test2", UID: "2"},
})),
deepCp(empty, withOwnerReferences([]corev1.OwnerReference{
{Name: "test1", UID: "1"},
{Name: "test3", UID: "3"},
})),
false,
),
Entry(
"should detect differences in spec",
deepCp(empty, withShootSpec(v1beta1.ShootSpec{
Region: "test1",
})),
deepCp(empty, withShootSpec(v1beta1.ShootSpec{
Region: "test2",
})),
false,
),
)
})
13 changes: 13 additions & 0 deletions internal/testing/shoot/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package shoot_test

import (
"testing"

"github.com/onsi/ginkgo/v2" //nolint:revive
"github.com/onsi/gomega" //nolint:revive
)

func TestMatcher(t *testing.T) {
gomega.RegisterFailHandler(ginkgo.Fail)
ginkgo.RunSpecs(t, "shoot matcher")
}

0 comments on commit 5f069a8

Please sign in to comment.