1/*
2Copyright 2020 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package meta
18
19import (
20	"reflect"
21	"testing"
22	"time"
23
24	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25)
26
27func TestSetStatusCondition(t *testing.T) {
28	oneHourBefore := time.Now().Add(-1 * time.Hour)
29	oneHourAfter := time.Now().Add(1 * time.Hour)
30
31	tests := []struct {
32		name       string
33		conditions []metav1.Condition
34		toAdd      metav1.Condition
35		expected   []metav1.Condition
36	}{
37		{
38			name: "should-add",
39			conditions: []metav1.Condition{
40				{Type: "first"},
41				{Type: "third"},
42			},
43			toAdd: metav1.Condition{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message"},
44			expected: []metav1.Condition{
45				{Type: "first"},
46				{Type: "third"},
47				{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message"},
48			},
49		},
50		{
51			name: "use-supplied-time",
52			conditions: []metav1.Condition{
53				{Type: "first"},
54				{Type: "second", Status: metav1.ConditionFalse},
55				{Type: "third"},
56			},
57			toAdd: metav1.Condition{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message"},
58			expected: []metav1.Condition{
59				{Type: "first"},
60				{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message"},
61				{Type: "third"},
62			},
63		},
64		{
65			name: "update-fields",
66			conditions: []metav1.Condition{
67				{Type: "first"},
68				{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}},
69				{Type: "third"},
70			},
71			toAdd: metav1.Condition{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourAfter}, Reason: "reason", Message: "message", ObservedGeneration: 3},
72			expected: []metav1.Condition{
73				{Type: "first"},
74				{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message", ObservedGeneration: 3},
75				{Type: "third"},
76			},
77		},
78	}
79
80	for _, test := range tests {
81		t.Run(test.name, func(t *testing.T) {
82			SetStatusCondition(&test.conditions, test.toAdd)
83			if !reflect.DeepEqual(test.conditions, test.expected) {
84				t.Error(test.conditions)
85			}
86		})
87	}
88}
89
90func TestRemoveStatusCondition(t *testing.T) {
91	tests := []struct {
92		name          string
93		conditions    []metav1.Condition
94		conditionType string
95		expected      []metav1.Condition
96	}{
97		{
98			name: "present",
99			conditions: []metav1.Condition{
100				{Type: "first"},
101				{Type: "second"},
102				{Type: "third"},
103			},
104			conditionType: "second",
105			expected: []metav1.Condition{
106				{Type: "first"},
107				{Type: "third"},
108			},
109		},
110		{
111			name: "not-present",
112			conditions: []metav1.Condition{
113				{Type: "first"},
114				{Type: "second"},
115				{Type: "third"},
116			},
117			conditionType: "fourth",
118			expected: []metav1.Condition{
119				{Type: "first"},
120				{Type: "second"},
121				{Type: "third"},
122			},
123		},
124		{
125			name:          "empty_conditions",
126			conditions:    []metav1.Condition{},
127			conditionType: "Foo",
128			expected:      []metav1.Condition{},
129		},
130	}
131
132	for _, test := range tests {
133		t.Run(test.name, func(t *testing.T) {
134			RemoveStatusCondition(&test.conditions, test.conditionType)
135			if !reflect.DeepEqual(test.conditions, test.expected) {
136				t.Error(test.conditions)
137			}
138		})
139	}
140}
141
142func TestFindStatusCondition(t *testing.T) {
143	tests := []struct {
144		name          string
145		conditions    []metav1.Condition
146		conditionType string
147		expected      *metav1.Condition
148	}{
149		{
150			name: "not-present",
151			conditions: []metav1.Condition{
152				{Type: "first"},
153			},
154			conditionType: "second",
155			expected:      nil,
156		},
157		{
158			name: "present",
159			conditions: []metav1.Condition{
160				{Type: "first"},
161				{Type: "second"},
162			},
163			conditionType: "second",
164			expected:      &metav1.Condition{Type: "second"},
165		},
166	}
167
168	for _, test := range tests {
169		t.Run(test.name, func(t *testing.T) {
170			actual := FindStatusCondition(test.conditions, test.conditionType)
171			if !reflect.DeepEqual(actual, test.expected) {
172				t.Error(actual)
173			}
174		})
175	}
176}
177
178func TestIsStatusConditionPresentAndEqual(t *testing.T) {
179	tests := []struct {
180		name            string
181		conditions      []metav1.Condition
182		conditionType   string
183		conditionStatus metav1.ConditionStatus
184		expected        bool
185	}{
186		{
187			name: "doesnt-match-true",
188			conditions: []metav1.Condition{
189				{Type: "first", Status: metav1.ConditionUnknown},
190			},
191			conditionType:   "first",
192			conditionStatus: metav1.ConditionTrue,
193			expected:        false,
194		},
195		{
196			name: "does-match-true",
197			conditions: []metav1.Condition{
198				{Type: "first", Status: metav1.ConditionTrue},
199			},
200			conditionType:   "first",
201			conditionStatus: metav1.ConditionTrue,
202			expected:        true,
203		},
204		{
205			name: "does-match-false",
206			conditions: []metav1.Condition{
207				{Type: "first", Status: metav1.ConditionFalse},
208			},
209			conditionType:   "first",
210			conditionStatus: metav1.ConditionFalse,
211			expected:        true,
212		},
213	}
214
215	for _, test := range tests {
216		t.Run(test.name, func(t *testing.T) {
217			actual := IsStatusConditionPresentAndEqual(test.conditions, test.conditionType, test.conditionStatus)
218			if actual != test.expected {
219				t.Error(actual)
220			}
221
222		})
223	}
224}
225