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