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 17, 2024
1 parent fddbee3 commit 91d7c5f
Show file tree
Hide file tree
Showing 4 changed files with 440 additions and 0 deletions.
132 changes: 132 additions & 0 deletions internal/testing/shoot/matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
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 ShootMatcher struct {
toMatch interface{}
fails []string
}

func Matcher(i interface{}) types.GomegaMatcher {
return &ShootMatcher{
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 *ShootMatcher) 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 *ShootMatcher) NegatedFailureMessage(actual interface{}) string {
return "expected should not equal actual"
}

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

import (
"os"

"github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/kyma-project/infrastructure-manager/internal/testing/shoot"
"gopkg.in/yaml.v2"
corev1 "k8s.io/apimachinery/pkg/apis/meta/v1"

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

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
}
}

func deepCp(s v1beta1.Shoot, opts ...deepCpOpts) v1beta1.Shoot {
cp := s.DeepCopy()

for _, opt := range opts {
opt(cp)
}

return *cp
}

func loadAsString(path string, out *string) error {
data, err := os.ReadFile(path)
if err != nil {
return err
}

(*out) = string(data)
return nil
}

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

func testResults(actual, expected interface{}, expectedMatch bool) {
matcher := shoot.Matcher(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

var azure string
Expect(loadAsString("testdata/dev_azure.yaml", &azure)).
ShouldNot(HaveOccurred())

var shoot3 v1beta1.Shoot
Expect(yaml.Unmarshal([]byte(azure), &shoot3)).NotTo(HaveOccurred())

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")
}
Loading

0 comments on commit 91d7c5f

Please sign in to comment.