Skip to content

Commit

Permalink
refactor test types, add matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziopandini committed Sep 3, 2024
1 parent 27fb8ed commit 796b0a6
Show file tree
Hide file tree
Showing 11 changed files with 1,305 additions and 149 deletions.
412 changes: 412 additions & 0 deletions internal/test/builder/v1beta2_transition.go

Large diffs are not rendered by default.

415 changes: 415 additions & 0 deletions internal/test/builder/zz_generated.deepcopy.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions internal/test/envtest/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func init() {
utilruntime.Must(admissionv1.AddToScheme(scheme.Scheme))
utilruntime.Must(runtimev1.AddToScheme(scheme.Scheme))
utilruntime.Must(ipamv1.AddToScheme(scheme.Scheme))
utilruntime.Must(builder.AddTransitionV1beta2ToScheme(scheme.Scheme))
}

// RunInput is the input for Run.
Expand Down Expand Up @@ -230,6 +231,10 @@ func newEnvironment(uncachedObjs ...client.Object) *Environment {
builder.TestBootstrapConfigCRD.DeepCopy(),
builder.TestControlPlaneTemplateCRD.DeepCopy(),
builder.TestControlPlaneCRD.DeepCopy(),
builder.Phase0ObjCRD.DeepCopy(),
builder.Phase1ObjCRD.DeepCopy(),
builder.Phase2ObjCRD.DeepCopy(),
builder.Phase3ObjCRD.DeepCopy(),
},
// initialize webhook here to be able to test the envtest install via webhookOptions
// This should set LocalServingCertDir and LocalServingPort that are used below.
Expand Down
36 changes: 19 additions & 17 deletions util/conditions/experimental/aggregate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"

"sigs.k8s.io/cluster-api/internal/test/builder"
)

func TestAggregate(t *testing.T) {
Expand Down Expand Up @@ -79,9 +81,9 @@ func TestAggregate(t *testing.T) {
options: []AggregateOption{},
want: &metav1.Condition{
Type: AvailableCondition,
Status: metav1.ConditionFalse, // False because there is one issue
Reason: ManyIssuesReason, // Using a generic reason
Message: "(False): Message-1 from default/obj0, default/obj1, default/obj2 and 2 other V1Beta2ResourceWithConditions", // messages from all the issues & unknown conditions (info dropped)
Status: metav1.ConditionFalse, // False because there is one issue
Reason: ManyIssuesReason, // Using a generic reason
Message: "(False): Message-1 from default/obj0, default/obj1, default/obj2 and 2 other Phase3Objs", // messages from all the issues & unknown conditions (info dropped)
},
},
{
Expand Down Expand Up @@ -119,9 +121,9 @@ func TestAggregate(t *testing.T) {
options: []AggregateOption{},
want: &metav1.Condition{
Type: AvailableCondition,
Status: metav1.ConditionFalse, // False because there is one issue
Reason: ManyIssuesReason, // Using a generic reason
Message: "(False): Message-1 from default/obj0, default/obj4; (False): Message-2 from default/obj1; (False): Message-3 from default/obj5; other 2 V1Beta2ResourceWithConditions with issues", // messages from all the issues & unknown conditions (info dropped)
Status: metav1.ConditionFalse, // False because there is one issue
Reason: ManyIssuesReason, // Using a generic reason
Message: "(False): Message-1 from default/obj0, default/obj4; (False): Message-2 from default/obj1; (False): Message-3 from default/obj5; other 2 Phase3Objs with issues", // messages from all the issues & unknown conditions (info dropped)
},
},
{
Expand Down Expand Up @@ -154,9 +156,9 @@ func TestAggregate(t *testing.T) {
options: []AggregateOption{},
want: &metav1.Condition{
Type: AvailableCondition,
Status: metav1.ConditionFalse, // False because there is one issue
Reason: ManyIssuesReason, // Using a generic reason
Message: "(False): Message-1 from default/obj0; (False): Message-2 from default/obj1; (False): Message-4 from default/obj3; other 1 V1Beta2ResourceWithConditions unknown", // messages from all the issues & unknown conditions (info dropped)
Status: metav1.ConditionFalse, // False because there is one issue
Reason: ManyIssuesReason, // Using a generic reason
Message: "(False): Message-1 from default/obj0; (False): Message-2 from default/obj1; (False): Message-4 from default/obj3; other 1 Phase3Objs unknown", // messages from all the issues & unknown conditions (info dropped)
},
},
{
Expand All @@ -174,9 +176,9 @@ func TestAggregate(t *testing.T) {
options: []AggregateOption{},
want: &metav1.Condition{
Type: AvailableCondition,
Status: metav1.ConditionUnknown, // False because there is one issue
Reason: ManyUnknownsReason, // Using a generic reason
Message: "(Unknown): Message-1 from default/obj0, default/obj4; (Unknown): Message-2 from default/obj1; (Unknown): Message-3 from default/obj5; other 2 V1Beta2ResourceWithConditions unknown", // messages from all the issues & unknown conditions (info dropped)
Status: metav1.ConditionUnknown, // False because there is one issue
Reason: ManyUnknownsReason, // Using a generic reason
Message: "(Unknown): Message-1 from default/obj0, default/obj4; (Unknown): Message-2 from default/obj1; (Unknown): Message-3 from default/obj5; other 2 Phase3Objs unknown", // messages from all the issues & unknown conditions (info dropped)
},
},
{
Expand All @@ -193,9 +195,9 @@ func TestAggregate(t *testing.T) {
options: []AggregateOption{},
want: &metav1.Condition{
Type: AvailableCondition,
Status: metav1.ConditionTrue, // False because there is one issue
Reason: ManyInfoReason, // Using a generic reason
Message: "(True): Message-1 from default/obj0, default/obj4; (True): Message-2 from default/obj1; (True): Message-3 from default/obj5; other 1 V1Beta2ResourceWithConditions with info messages", // messages from all the issues & unknown conditions (info dropped)
Status: metav1.ConditionTrue, // False because there is one issue
Reason: ManyInfoReason, // Using a generic reason
Message: "(True): Message-1 from default/obj0, default/obj4; (True): Message-2 from default/obj1; (True): Message-3 from default/obj5; other 1 Phase3Objs with info messages", // messages from all the issues & unknown conditions (info dropped)
},
},
}
Expand All @@ -206,12 +208,12 @@ func TestAggregate(t *testing.T) {

objs := make([]runtime.Object, 0, len(tt.conditions))
for i := range tt.conditions {
objs = append(objs, &V1Beta2ResourceWithConditions{
objs = append(objs, &builder.Phase3Obj{
ObjectMeta: metav1.ObjectMeta{
Namespace: metav1.NamespaceDefault,
Name: fmt.Sprintf("obj%d", i),
},
Status: struct{ Conditions []metav1.Condition }{
Status: builder.Phase3ObjStatus{
Conditions: tt.conditions[i],
},
})
Expand Down
107 changes: 25 additions & 82 deletions util/conditions/experimental/getter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"

clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/internal/test/builder"
)

type ObjWithoutStatus struct {
Expand Down Expand Up @@ -74,58 +75,6 @@ func (f *ObjWithWrongExperimentalConditionType) DeepCopyObject() runtime.Object
panic("implement me")
}

type V1Beta1ResourceWithLegacyConditions struct {
metav1.TypeMeta
metav1.ObjectMeta
Status struct {
Conditions clusterv1.Conditions
}
}

func (f *V1Beta1ResourceWithLegacyConditions) DeepCopyObject() runtime.Object {
panic("implement me")
}

type V1Beta1ResourceWithLegacyAndExperimentalConditionsV1 struct {
metav1.TypeMeta
metav1.ObjectMeta
Status struct {
Conditions clusterv1.Conditions
ExperimentalConditions []metav1.Condition
}
}

func (f *V1Beta1ResourceWithLegacyAndExperimentalConditionsV1) DeepCopyObject() runtime.Object {
panic("implement me")
}

type V1Beta2ResourceWithConditionsAndBackwardCompatibleConditions struct {
metav1.TypeMeta
metav1.ObjectMeta
Status struct {
Conditions []metav1.Condition
BackwardCompatibility struct {
Conditions clusterv1.Conditions
}
}
}

func (f *V1Beta2ResourceWithConditionsAndBackwardCompatibleConditions) DeepCopyObject() runtime.Object {
panic("implement me")
}

type V1Beta2ResourceWithConditions struct {
metav1.TypeMeta
metav1.ObjectMeta
Status struct {
Conditions []metav1.Condition
}
}

func (f *V1Beta2ResourceWithConditions) DeepCopyObject() runtime.Object {
panic("implement me")
}

func TestGetAll(t *testing.T) {
now := metav1.Now().Rfc3339Copy()

Expand All @@ -138,7 +87,7 @@ func TestGetAll(t *testing.T) {

t.Run("fails for nil object", func(t *testing.T) {
g := NewWithT(t)
var foo *V1Beta1ResourceWithLegacyConditions
var foo *builder.Phase0Obj

_, err := GetAll(foo)
g.Expect(err).To(HaveOccurred())
Expand Down Expand Up @@ -199,21 +148,23 @@ func TestGetAll(t *testing.T) {

t.Run("v1beta object with legacy conditions", func(t *testing.T) {
g := NewWithT(t)
foo := &V1Beta1ResourceWithLegacyConditions{
Status: struct{ Conditions clusterv1.Conditions }{Conditions: clusterv1.Conditions{
{
Type: "fooCondition",
Status: corev1.ConditionTrue,
LastTransitionTime: now,
},
{
Type: "fooCondition",
Status: corev1.ConditionFalse,
LastTransitionTime: now,
Reason: "FooReason",
Message: "FooMessage",
foo := &builder.Phase0Obj{
Status: builder.Phase0ObjStatus{
Conditions: clusterv1.Conditions{
{
Type: "fooCondition",
Status: corev1.ConditionTrue,
LastTransitionTime: now,
},
{
Type: "fooCondition",
Status: corev1.ConditionFalse,
LastTransitionTime: now,
Reason: "FooReason",
Message: "FooMessage",
},
},
}},
},
}

expect := []metav1.Condition{
Expand Down Expand Up @@ -245,11 +196,8 @@ func TestGetAll(t *testing.T) {

t.Run("v1beta1 object with both legacy and experimental conditions", func(t *testing.T) {
g := NewWithT(t)
foo := &V1Beta1ResourceWithLegacyAndExperimentalConditionsV1{
Status: struct {
Conditions clusterv1.Conditions
ExperimentalConditions []metav1.Condition
}{
foo := &builder.Phase1Obj{
Status: builder.Phase1ObjStatus{
Conditions: clusterv1.Conditions{
{
Type: "barCondition",
Expand Down Expand Up @@ -295,19 +243,16 @@ func TestGetAll(t *testing.T) {

t.Run("v1beta2 object with conditions and backward compatible conditions", func(t *testing.T) {
g := NewWithT(t)
foo := &V1Beta2ResourceWithConditionsAndBackwardCompatibleConditions{
Status: struct {
Conditions []metav1.Condition
BackwardCompatibility struct{ Conditions clusterv1.Conditions }
}{
foo := &builder.Phase2Obj{
Status: builder.Phase2ObjStatus{
Conditions: []metav1.Condition{
{
Type: "fooCondition",
Status: metav1.ConditionTrue,
LastTransitionTime: now,
},
},
BackwardCompatibility: struct{ Conditions clusterv1.Conditions }{
BackCompatibility: builder.Phase2ObjStatusBackCompatibility{
Conditions: clusterv1.Conditions{
{
Type: "barCondition",
Expand Down Expand Up @@ -341,10 +286,8 @@ func TestGetAll(t *testing.T) {

t.Run("v1beta2 object with conditions (end state)", func(t *testing.T) {
g := NewWithT(t)
foo := &V1Beta2ResourceWithConditions{
Status: struct {
Conditions []metav1.Condition
}{
foo := &builder.Phase3Obj{
Status: builder.Phase3ObjStatus{
Conditions: []metav1.Condition{
{
Type: "fooCondition",
Expand Down
102 changes: 102 additions & 0 deletions util/conditions/experimental/matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
Copyright 2024 The Kubernetes Authors.
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 experimental

import (
"fmt"

"github.com/onsi/gomega"
"github.com/onsi/gomega/types"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// MatchConditions returns a custom matcher to check equality of clusterv1.Conditions.
func MatchConditions(expected []metav1.Condition) types.GomegaMatcher {
return &matchConditions{
expected: expected,
}
}

type matchConditions struct {
expected []metav1.Condition
}

func (m matchConditions) Match(actual interface{}) (success bool, err error) {
elems := []interface{}{}
for _, condition := range m.expected {
elems = append(elems, MatchCondition(condition))
}

return gomega.ConsistOf(elems...).Match(actual)
}

func (m matchConditions) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("expected\n\t%#v\nto match\n\t%#v\n", actual, m.expected)
}

func (m matchConditions) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("expected\n\t%#v\nto not match\n\t%#v\n", actual, m.expected)
}

// MatchCondition returns a custom matcher to check equality of clusterv1.Condition.
func MatchCondition(expected metav1.Condition) types.GomegaMatcher {
return &matchCondition{
expected: expected,
}
}

type matchCondition struct {
expected metav1.Condition
}

func (m matchCondition) Match(actual interface{}) (success bool, err error) {
actualCondition, ok := actual.(metav1.Condition)
if !ok {
return false, fmt.Errorf("actual should be of type metav1.Condition")
}

ok, err = gomega.Equal(m.expected.Type).Match(actualCondition.Type)
if !ok {
return ok, err
}
ok, err = gomega.Equal(m.expected.Status).Match(actualCondition.Status)
if !ok {
return ok, err
}
ok, err = gomega.Equal(m.expected.ObservedGeneration).Match(actualCondition.ObservedGeneration)
if !ok {
return ok, err
}
ok, err = gomega.Equal(m.expected.Reason).Match(actualCondition.Reason)
if !ok {
return ok, err
}
ok, err = gomega.Equal(m.expected.Message).Match(actualCondition.Message)
if !ok {
return ok, err
}

return ok, err
}

func (m matchCondition) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("expected\n\t%#v\nto match\n\t%#v\n", actual, m.expected)
}

func (m matchCondition) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("expected\n\t%#v\nto not match\n\t%#v\n", actual, m.expected)
}
Loading

0 comments on commit 796b0a6

Please sign in to comment.