...

Source file src/edge-infra.dev/pkg/k8s/runtime/conditions/patch_test.go

Documentation: edge-infra.dev/pkg/k8s/runtime/conditions

     1  package conditions
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	. "github.com/onsi/gomega" //nolint:revive // TODO(aw185176): remove
     8  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     9  
    10  	"edge-infra.dev/pkg/k8s/object/fobject"
    11  )
    12  
    13  func TestNewPatch(t *testing.T) {
    14  	fooTrue := TrueCondition("foo", "reason true", "message true")
    15  	fooFalse := FalseCondition("foo", "reason false", "message false")
    16  
    17  	tests := []struct {
    18  		name   string
    19  		before Getter
    20  		after  Getter
    21  		want   Patch
    22  	}{
    23  		{
    24  			name:   "No changes return empty patch",
    25  			before: getterWithConditions(),
    26  			after:  getterWithConditions(),
    27  			want:   nil,
    28  		},
    29  		{
    30  			name:   "No changes return empty patch",
    31  			before: getterWithConditions(fooTrue),
    32  			after:  getterWithConditions(fooTrue),
    33  			want:   nil,
    34  		},
    35  		{
    36  			name:   "Detects AddConditionPatch",
    37  			before: getterWithConditions(),
    38  			after:  getterWithConditions(fooTrue),
    39  			want: Patch{
    40  				{
    41  					Before: nil,
    42  					After:  fooTrue,
    43  					Op:     AddConditionPatch,
    44  				},
    45  			},
    46  		},
    47  		{
    48  			name:   "Detects ChangeConditionPatch",
    49  			before: getterWithConditions(fooTrue),
    50  			after:  getterWithConditions(fooFalse),
    51  			want: Patch{
    52  				{
    53  					Before: fooTrue,
    54  					After:  fooFalse,
    55  					Op:     ChangeConditionPatch,
    56  				},
    57  			},
    58  		},
    59  		{
    60  			name:   "Detects RemoveConditionPatch",
    61  			before: getterWithConditions(fooTrue),
    62  			after:  getterWithConditions(),
    63  			want: Patch{
    64  				{
    65  					Before: fooTrue,
    66  					After:  nil,
    67  					Op:     RemoveConditionPatch,
    68  				},
    69  			},
    70  		},
    71  	}
    72  
    73  	for _, tt := range tests {
    74  		t.Run(tt.name, func(t *testing.T) {
    75  			g := NewWithT(t)
    76  
    77  			got := NewPatch(tt.before, tt.after)
    78  
    79  			g.Expect(got).To(Equal(tt.want))
    80  		})
    81  	}
    82  }
    83  
    84  func TestApply(t *testing.T) {
    85  	fooTrue := TrueCondition("foo", "reason true", "message true")
    86  	fooFalse := FalseCondition("foo", "reason false", "message false")
    87  	fooUnknown := UnknownCondition("foo", "reason unknown", "message unknown")
    88  
    89  	tests := []struct {
    90  		name    string
    91  		before  Getter
    92  		after   Getter
    93  		latest  Setter
    94  		options []ApplyOption
    95  		want    []metav1.Condition
    96  		wantErr bool
    97  	}{
    98  		{
    99  			name:    "No patch return same list",
   100  			before:  getterWithConditions(fooTrue),
   101  			after:   getterWithConditions(fooTrue),
   102  			latest:  setterWithConditions(fooTrue),
   103  			want:    conditionList(fooTrue),
   104  			wantErr: false,
   105  		},
   106  		{
   107  			name:    "Add: When a condition does not exists, it should add",
   108  			before:  getterWithConditions(),
   109  			after:   getterWithConditions(fooTrue),
   110  			latest:  setterWithConditions(),
   111  			want:    conditionList(fooTrue),
   112  			wantErr: false,
   113  		},
   114  		{
   115  			name:    "Add: When a condition already exists but without conflicts, it should add",
   116  			before:  getterWithConditions(),
   117  			after:   getterWithConditions(fooTrue),
   118  			latest:  setterWithConditions(fooTrue),
   119  			want:    conditionList(fooTrue),
   120  			wantErr: false,
   121  		},
   122  		{
   123  			name:    "Add: When a condition already exists but with conflicts, it should error",
   124  			before:  getterWithConditions(),
   125  			after:   getterWithConditions(fooTrue),
   126  			latest:  setterWithConditions(fooFalse),
   127  			want:    nil,
   128  			wantErr: true,
   129  		},
   130  		{
   131  			name:    "Add: When a condition already exists but with conflicts, it should not error if the condition is owned",
   132  			before:  getterWithConditions(),
   133  			after:   getterWithConditions(fooTrue),
   134  			latest:  setterWithConditions(fooFalse),
   135  			options: []ApplyOption{WithOwnedConditions("foo")},
   136  			want:    conditionList(fooTrue), // after condition should be kept in case of error
   137  			wantErr: false,
   138  		},
   139  		{
   140  			name:    "Remove: When a condition was already deleted, it should pass",
   141  			before:  getterWithConditions(fooTrue),
   142  			after:   getterWithConditions(),
   143  			latest:  setterWithConditions(),
   144  			want:    conditionList(),
   145  			wantErr: false,
   146  		},
   147  		{
   148  			name:    "Remove: When a condition already exists but without conflicts, it should delete",
   149  			before:  getterWithConditions(fooTrue),
   150  			after:   getterWithConditions(),
   151  			latest:  setterWithConditions(fooTrue),
   152  			want:    conditionList(),
   153  			wantErr: false,
   154  		},
   155  		{
   156  			name:    "Remove: When a condition already exists but with conflicts, it should error",
   157  			before:  getterWithConditions(fooTrue),
   158  			after:   getterWithConditions(),
   159  			latest:  setterWithConditions(fooFalse),
   160  			want:    nil,
   161  			wantErr: true,
   162  		},
   163  		{
   164  			name:    "Remove: When a condition already exists but with conflicts, it should not error if the condition is owned",
   165  			before:  getterWithConditions(fooTrue),
   166  			after:   getterWithConditions(),
   167  			latest:  setterWithConditions(fooFalse),
   168  			options: []ApplyOption{WithOwnedConditions("foo")},
   169  			want:    conditionList(), // after condition should be kept in case of error
   170  			wantErr: false,
   171  		},
   172  		{
   173  			name:    "Change: When a condition exists without conflicts, it should change",
   174  			before:  getterWithConditions(fooTrue),
   175  			after:   getterWithConditions(fooFalse),
   176  			latest:  setterWithConditions(fooTrue),
   177  			want:    conditionList(fooFalse),
   178  			wantErr: false,
   179  		},
   180  		{
   181  			name:    "Change: When a condition exists with conflicts but there is agreement on the final state, it should change",
   182  			before:  getterWithConditions(fooFalse),
   183  			after:   getterWithConditions(fooTrue),
   184  			latest:  setterWithConditions(fooTrue),
   185  			want:    conditionList(fooTrue),
   186  			wantErr: false,
   187  		},
   188  		{
   189  			name:    "Change: When a condition exists with conflicts but there is no agreement on the final state, it should error",
   190  			before:  getterWithConditions(fooUnknown),
   191  			after:   getterWithConditions(fooFalse),
   192  			latest:  setterWithConditions(fooTrue),
   193  			want:    nil,
   194  			wantErr: true,
   195  		},
   196  		{
   197  			name:    "Change: When a condition exists with conflicts but there is no agreement on the final state, it should not error if the condition is owned",
   198  			before:  getterWithConditions(fooUnknown),
   199  			after:   getterWithConditions(fooFalse),
   200  			latest:  setterWithConditions(fooTrue),
   201  			options: []ApplyOption{WithOwnedConditions("foo")},
   202  			want:    conditionList(fooFalse), // after condition should be kept in case of error
   203  			wantErr: false,
   204  		},
   205  		{
   206  			name:    "Change: When a condition was deleted, it should error",
   207  			before:  getterWithConditions(fooTrue),
   208  			after:   getterWithConditions(fooFalse),
   209  			latest:  setterWithConditions(),
   210  			want:    nil,
   211  			wantErr: true,
   212  		},
   213  		{
   214  			name:    "Change: When a condition was deleted, it should not error if the condition is owned",
   215  			before:  getterWithConditions(fooTrue),
   216  			after:   getterWithConditions(fooFalse),
   217  			latest:  setterWithConditions(),
   218  			options: []ApplyOption{WithOwnedConditions("foo")},
   219  			want:    conditionList(fooFalse), // after condition should be kept in case of error
   220  			wantErr: false,
   221  		},
   222  	}
   223  
   224  	for _, tt := range tests {
   225  		t.Run(tt.name, func(t *testing.T) {
   226  			g := NewWithT(t)
   227  
   228  			patch := NewPatch(tt.before, tt.after)
   229  
   230  			err := patch.Apply(tt.latest, tt.options...)
   231  			if tt.wantErr {
   232  				g.Expect(err).To(HaveOccurred())
   233  				return
   234  			}
   235  			g.Expect(err).ToNot(HaveOccurred())
   236  
   237  			g.Expect(tt.latest.GetConditions()).To(haveSameConditionsOf(tt.want))
   238  		})
   239  	}
   240  }
   241  
   242  func TestApplyDoesNotAlterLastTransitionTime(t *testing.T) {
   243  	g := NewWithT(t)
   244  
   245  	before := &fobject.Fake{}
   246  	after := &fobject.Fake{
   247  		Status: fobject.FakeStatus{
   248  			Conditions: []metav1.Condition{
   249  				{
   250  					Type:               "foo",
   251  					Status:             metav1.ConditionTrue,
   252  					LastTransitionTime: metav1.NewTime(time.Now().UTC().Truncate(time.Second)),
   253  				},
   254  			},
   255  		},
   256  	}
   257  	latest := &fobject.Fake{}
   258  
   259  	// latest has no conditions, so we are actually adding the
   260  	// condition but in this case we should not set the LastTransitionTime
   261  	// but we should preserve the LastTransition set in after
   262  
   263  	diff := NewPatch(before, after)
   264  	err := diff.Apply(latest)
   265  
   266  	g.Expect(err).ToNot(HaveOccurred())
   267  	g.Expect(latest.GetConditions()).To(Equal(after.GetConditions()))
   268  }
   269  

View as plain text