1/*
2Copyright 2018 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 dynamiclister_test
18
19import (
20	"reflect"
21	"testing"
22
23	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24	"k8s.io/apimachinery/pkg/labels"
25	"k8s.io/apimachinery/pkg/runtime"
26	"k8s.io/apimachinery/pkg/runtime/schema"
27	"k8s.io/apimachinery/pkg/util/diff"
28	"k8s.io/client-go/dynamic/dynamiclister"
29	"k8s.io/client-go/tools/cache"
30)
31
32func TestNamespaceGetMethod(t *testing.T) {
33	tests := []struct {
34		name            string
35		existingObjects []runtime.Object
36		namespaceToSync string
37		gvrToSync       schema.GroupVersionResource
38		objectToGet     string
39		expectedObject  *unstructured.Unstructured
40		expectError     bool
41	}{
42		{
43			name: "scenario 1: gets name-foo1 resource from the indexer from ns-foo namespace",
44			existingObjects: []runtime.Object{
45				newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
46				newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"),
47				newUnstructured("group/version", "TheKind", "ns-bar", "name-bar"),
48			},
49			namespaceToSync: "ns-foo",
50			gvrToSync:       schema.GroupVersionResource{Group: "group", Version: "version", Resource: "TheKinds"},
51			objectToGet:     "name-foo1",
52			expectedObject:  newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"),
53		},
54		{
55			name: "scenario 2: gets name-foo-non-existing resource from the indexer from ns-foo namespace",
56			existingObjects: []runtime.Object{
57				newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
58				newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"),
59				newUnstructured("group/version", "TheKind", "ns-bar", "name-bar"),
60			},
61			namespaceToSync: "ns-foo",
62			gvrToSync:       schema.GroupVersionResource{Group: "group", Version: "version", Resource: "TheKinds"},
63			objectToGet:     "name-foo-non-existing",
64			expectError:     true,
65		},
66	}
67
68	for _, test := range tests {
69		t.Run(test.name, func(t *testing.T) {
70			// test data
71			indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
72			for _, obj := range test.existingObjects {
73				err := indexer.Add(obj)
74				if err != nil {
75					t.Fatal(err)
76				}
77			}
78			// act
79			target := dynamiclister.New(indexer, test.gvrToSync).Namespace(test.namespaceToSync)
80			actualObject, err := target.Get(test.objectToGet)
81
82			// validate
83			if test.expectError {
84				if err == nil {
85					t.Fatal("expected to get an error but non was returned")
86				}
87				return
88			}
89			if err != nil {
90				t.Fatal(err)
91			}
92			if !reflect.DeepEqual(test.expectedObject, actualObject) {
93				t.Fatalf("unexpected object has been returned expected = %v actual = %v, diff = %v", test.expectedObject, actualObject, diff.ObjectDiff(test.expectedObject, actualObject))
94			}
95		})
96	}
97}
98
99func TestNamespaceListMethod(t *testing.T) {
100	// test data
101	objs := []runtime.Object{
102		newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
103		newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"),
104		newUnstructured("group/version", "TheKind", "ns-bar", "name-bar"),
105	}
106	indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
107	for _, obj := range objs {
108		err := indexer.Add(obj)
109		if err != nil {
110			t.Fatal(err)
111		}
112	}
113	expectedOutput := []*unstructured.Unstructured{
114		newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
115		newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"),
116	}
117	namespaceToList := "ns-foo"
118
119	// act
120	target := dynamiclister.New(indexer, schema.GroupVersionResource{Group: "group", Version: "version", Resource: "TheKinds"}).Namespace(namespaceToList)
121	actualOutput, err := target.List(labels.Everything())
122
123	// validate
124	if err != nil {
125		t.Fatal(err)
126	}
127	assertListOrDie(expectedOutput, actualOutput, t)
128}
129
130func TestListerGetMethod(t *testing.T) {
131	tests := []struct {
132		name            string
133		existingObjects []runtime.Object
134		namespaceToSync string
135		gvrToSync       schema.GroupVersionResource
136		objectToGet     string
137		expectedObject  *unstructured.Unstructured
138		expectError     bool
139	}{
140		{
141			name: "scenario 1: gets name-foo1 resource from the indexer",
142			existingObjects: []runtime.Object{
143				newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
144				newUnstructured("group/version", "TheKind", "", "name-foo1"),
145				newUnstructured("group/version", "TheKind", "ns-bar", "name-bar"),
146			},
147			namespaceToSync: "",
148			gvrToSync:       schema.GroupVersionResource{Group: "group", Version: "version", Resource: "TheKinds"},
149			objectToGet:     "name-foo1",
150			expectedObject:  newUnstructured("group/version", "TheKind", "", "name-foo1"),
151		},
152		{
153			name: "scenario 2: doesn't get name-foo resource from the indexer from ns-foo namespace",
154			existingObjects: []runtime.Object{
155				newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
156				newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"),
157				newUnstructured("group/version", "TheKind", "ns-bar", "name-bar"),
158			},
159			namespaceToSync: "ns-foo",
160			gvrToSync:       schema.GroupVersionResource{Group: "group", Version: "version", Resource: "TheKinds"},
161			objectToGet:     "name-foo",
162			expectError:     true,
163		},
164	}
165
166	for _, test := range tests {
167		t.Run(test.name, func(t *testing.T) {
168			// test data
169			indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
170			for _, obj := range test.existingObjects {
171				err := indexer.Add(obj)
172				if err != nil {
173					t.Fatal(err)
174				}
175			}
176			// act
177			target := dynamiclister.New(indexer, test.gvrToSync)
178			actualObject, err := target.Get(test.objectToGet)
179
180			// validate
181			if test.expectError {
182				if err == nil {
183					t.Fatal("expected to get an error but non was returned")
184				}
185				return
186			}
187			if err != nil {
188				t.Fatal(err)
189			}
190			if !reflect.DeepEqual(test.expectedObject, actualObject) {
191				t.Fatalf("unexpected object has been returned expected = %v actual = %v, diff = %v", test.expectedObject, actualObject, diff.ObjectDiff(test.expectedObject, actualObject))
192			}
193		})
194	}
195}
196
197func TestListerListMethod(t *testing.T) {
198	// test data
199	objs := []runtime.Object{
200		newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
201		newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
202	}
203	indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
204	for _, obj := range objs {
205		err := indexer.Add(obj)
206		if err != nil {
207			t.Fatal(err)
208		}
209	}
210	expectedOutput := []*unstructured.Unstructured{
211		newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
212		newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
213	}
214
215	// act
216	target := dynamiclister.New(indexer, schema.GroupVersionResource{Group: "group", Version: "version", Resource: "TheKinds"})
217	actualOutput, err := target.List(labels.Everything())
218
219	// validate
220	if err != nil {
221		t.Fatal(err)
222	}
223	assertListOrDie(expectedOutput, actualOutput, t)
224}
225
226func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured {
227	return &unstructured.Unstructured{
228		Object: map[string]interface{}{
229			"apiVersion": apiVersion,
230			"kind":       kind,
231			"metadata": map[string]interface{}{
232				"namespace": namespace,
233				"name":      name,
234			},
235		},
236	}
237}
238
239func assertListOrDie(expected, actual []*unstructured.Unstructured, t *testing.T) {
240	if len(actual) != len(expected) {
241		t.Fatalf("unexpected number of items returned, expected = %d, actual = %d", len(expected), len(actual))
242	}
243	for _, expectedObject := range expected {
244		found := false
245		for _, actualObject := range actual {
246			if actualObject.GetName() == expectedObject.GetName() {
247				if !reflect.DeepEqual(expectedObject, actualObject) {
248					t.Fatalf("unexpected object has been returned expected = %v actual = %v, diff = %v", expectedObject, actualObject, diff.ObjectDiff(expectedObject, actualObject))
249				}
250				found = true
251			}
252		}
253		if !found {
254			t.Fatalf("the resource with the name = %s was not found in the returned output", expectedObject.GetName())
255		}
256	}
257}
258