1
16
17 package get
18
19 import (
20 "bytes"
21 "reflect"
22 "strings"
23 "testing"
24
25 corev1 "k8s.io/api/core/v1"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/cli-runtime/pkg/printers"
29 "k8s.io/kubectl/pkg/scheme"
30 )
31
32
33 var decoder = scheme.Codecs.UniversalDecoder(scheme.Scheme.PrioritizedVersionsAllGroups()...)
34
35 func TestMassageJSONPath(t *testing.T) {
36 tests := []struct {
37 input string
38 expectedOutput string
39 expectErr bool
40 }{
41 {input: "foo.bar", expectedOutput: "{.foo.bar}"},
42 {input: "{foo.bar}", expectedOutput: "{.foo.bar}"},
43 {input: ".foo.bar", expectedOutput: "{.foo.bar}"},
44 {input: "{.foo.bar}", expectedOutput: "{.foo.bar}"},
45 {input: "", expectedOutput: ""},
46 {input: "{foo.bar", expectErr: true},
47 {input: "foo.bar}", expectErr: true},
48 {input: "{foo.bar}}", expectErr: true},
49 {input: "{{foo.bar}", expectErr: true},
50 }
51 for _, test := range tests {
52 t.Run(test.input, func(t *testing.T) {
53 output, err := RelaxedJSONPathExpression(test.input)
54 if err != nil && !test.expectErr {
55 t.Errorf("unexpected error: %v", err)
56 return
57 }
58 if test.expectErr {
59 if err == nil {
60 t.Error("unexpected non-error")
61 }
62 return
63 }
64 if output != test.expectedOutput {
65 t.Errorf("input: %s, expected: %s, saw: %s", test.input, test.expectedOutput, output)
66 }
67 })
68 }
69 }
70
71 func TestNewColumnPrinterFromSpec(t *testing.T) {
72 tests := []struct {
73 spec string
74 expectedColumns []Column
75 expectErr bool
76 name string
77 noHeaders bool
78 }{
79 {
80 spec: "",
81 expectErr: true,
82 name: "empty",
83 },
84 {
85 spec: "invalid",
86 expectErr: true,
87 name: "invalid1",
88 },
89 {
90 spec: "invalid=foobar",
91 expectErr: true,
92 name: "invalid2",
93 },
94 {
95 spec: "invalid,foobar:blah",
96 expectErr: true,
97 name: "invalid3",
98 },
99 {
100 spec: "NAME:metadata.name,API_VERSION:apiVersion",
101 name: "ok",
102 expectedColumns: []Column{
103 {
104 Header: "NAME",
105 FieldSpec: "{.metadata.name}",
106 },
107 {
108 Header: "API_VERSION",
109 FieldSpec: "{.apiVersion}",
110 },
111 },
112 },
113 {
114 spec: "API_VERSION:apiVersion",
115 name: "no-headers",
116 noHeaders: true,
117 },
118 }
119 for _, test := range tests {
120 t.Run(test.name, func(t *testing.T) {
121 printer, err := NewCustomColumnsPrinterFromSpec(test.spec, decoder, test.noHeaders)
122 if test.expectErr {
123 if err == nil {
124 t.Errorf("[%s] unexpected non-error", test.name)
125 }
126 return
127 }
128 if !test.expectErr && err != nil {
129 t.Errorf("[%s] unexpected error: %v", test.name, err)
130 return
131 }
132 if test.noHeaders {
133 buffer := &bytes.Buffer{}
134
135 printer.PrintObj(&corev1.Pod{}, buffer)
136 if err != nil {
137 t.Fatalf("An error occurred printing Pod: %#v", err)
138 }
139
140 if contains(strings.Fields(buffer.String()), "API_VERSION") {
141 t.Errorf("unexpected header API_VERSION")
142 }
143
144 } else if !reflect.DeepEqual(test.expectedColumns, printer.Columns) {
145 t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v\n", test.name, test.expectedColumns, printer.Columns)
146 }
147 })
148 }
149 }
150
151 func contains(arr []string, s string) bool {
152 for i := range arr {
153 if arr[i] == s {
154 return true
155 }
156 }
157 return false
158 }
159
160 const exampleTemplateOne = `NAME API_VERSION
161 {metadata.name} {apiVersion}`
162
163 const exampleTemplateTwo = `NAME API_VERSION
164 {metadata.name} {apiVersion}`
165
166 func TestNewColumnPrinterFromTemplate(t *testing.T) {
167 tests := []struct {
168 spec string
169 expectedColumns []Column
170 expectErr bool
171 name string
172 }{
173 {
174 spec: "",
175 expectErr: true,
176 name: "empty",
177 },
178 {
179 spec: "invalid",
180 expectErr: true,
181 name: "invalid1",
182 },
183 {
184 spec: "invalid=foobar",
185 expectErr: true,
186 name: "invalid2",
187 },
188 {
189 spec: "invalid,foobar:blah",
190 expectErr: true,
191 name: "invalid3",
192 },
193 {
194 spec: exampleTemplateOne,
195 name: "ok",
196 expectedColumns: []Column{
197 {
198 Header: "NAME",
199 FieldSpec: "{.metadata.name}",
200 },
201 {
202 Header: "API_VERSION",
203 FieldSpec: "{.apiVersion}",
204 },
205 },
206 },
207 {
208 spec: exampleTemplateTwo,
209 name: "ok-2",
210 expectedColumns: []Column{
211 {
212 Header: "NAME",
213 FieldSpec: "{.metadata.name}",
214 },
215 {
216 Header: "API_VERSION",
217 FieldSpec: "{.apiVersion}",
218 },
219 },
220 },
221 }
222 for _, test := range tests {
223 t.Run(test.name, func(t *testing.T) {
224 reader := bytes.NewBufferString(test.spec)
225 printer, err := NewCustomColumnsPrinterFromTemplate(reader, decoder)
226 if test.expectErr {
227 if err == nil {
228 t.Errorf("[%s] unexpected non-error", test.name)
229 }
230 return
231 }
232 if !test.expectErr && err != nil {
233 t.Errorf("[%s] unexpected error: %v", test.name, err)
234 return
235 }
236
237 if !reflect.DeepEqual(test.expectedColumns, printer.Columns) {
238 t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v\n", test.name, test.expectedColumns, printer.Columns)
239 }
240 })
241 }
242 }
243
244 func TestColumnPrint(t *testing.T) {
245 tests := []struct {
246 columns []Column
247 obj runtime.Object
248 expectedOutput string
249 }{
250 {
251 columns: []Column{
252 {
253 Header: "NAME",
254 FieldSpec: "{.metadata.name}",
255 },
256 },
257 obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
258 expectedOutput: `NAME
259 foo
260 `,
261 },
262 {
263 columns: []Column{
264 {
265 Header: "NAME",
266 FieldSpec: "{.metadata.name}",
267 },
268 },
269 obj: &corev1.PodList{
270 Items: []corev1.Pod{
271 {ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
272 {ObjectMeta: metav1.ObjectMeta{Name: "bar"}},
273 },
274 },
275 expectedOutput: `NAME
276 foo
277 bar
278 `,
279 },
280 {
281 columns: []Column{
282 {
283 Header: "NAME",
284 FieldSpec: "{.metadata.name}",
285 },
286 {
287 Header: "API_VERSION",
288 FieldSpec: "{.apiVersion}",
289 },
290 },
291 obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}},
292 expectedOutput: `NAME API_VERSION
293 foo baz
294 `,
295 },
296 {
297 columns: []Column{
298 {
299 Header: "NAME",
300 FieldSpec: "{.metadata.name}",
301 },
302 {
303 Header: "API_VERSION",
304 FieldSpec: "{.apiVersion}",
305 },
306 {
307 Header: "NOT_FOUND",
308 FieldSpec: "{.notFound}",
309 },
310 },
311 obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}},
312 expectedOutput: `NAME API_VERSION NOT_FOUND
313 foo baz <none>
314 `,
315 },
316 {
317 columns: []Column{
318 {
319 Header: "NAME",
320 FieldSpec: "{.metadata.name}",
321 },
322 },
323 obj: &corev1.PodList{
324 Items: []corev1.Pod{
325 {ObjectMeta: metav1.ObjectMeta{Name: "\x1b \r"}},
326 },
327 },
328 expectedOutput: `NAME
329 ^[ \r
330 `,
331 },
332 }
333
334 for _, test := range tests {
335 t.Run(test.expectedOutput, func(t *testing.T) {
336 printer := &CustomColumnsPrinter{
337 Columns: test.columns,
338 Decoder: decoder,
339 }
340 buffer := &bytes.Buffer{}
341 if err := printer.PrintObj(test.obj, buffer); err != nil {
342 t.Errorf("unexpected error: %v", err)
343 }
344 if buffer.String() != test.expectedOutput {
345 t.Errorf("\nexpected:\n'%s'\nsaw\n'%s'\n", test.expectedOutput, buffer.String())
346 }
347 })
348 }
349 }
350
351
352 func TestIndividualPrintObjOnExistingTabWriter(t *testing.T) {
353 columns := []Column{
354 {
355 Header: "NAME",
356 FieldSpec: "{.metadata.name}",
357 },
358 {
359 Header: "LONG COLUMN NAME",
360 FieldSpec: "{.metadata.labels.label1}",
361 },
362 {
363 Header: "LABEL 2",
364 FieldSpec: "{.metadata.labels.label2}",
365 },
366 }
367 objects := []*corev1.Pod{
368 {ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"label1": "foo", "label2": "foo"}}},
369 {ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{"label1": "bar", "label2": "bar"}}},
370 }
371 expectedOutput := `NAME LONG COLUMN NAME LABEL 2
372 foo foo foo
373 bar bar bar
374 `
375
376 buffer := &bytes.Buffer{}
377 tabWriter := printers.GetNewTabWriter(buffer)
378 printer := &CustomColumnsPrinter{
379 Columns: columns,
380 Decoder: decoder,
381 }
382 for _, obj := range objects {
383 if err := printer.PrintObj(obj, tabWriter); err != nil {
384 t.Errorf("unexpected error: %v", err)
385 }
386 }
387 tabWriter.Flush()
388 if buffer.String() != expectedOutput {
389 t.Errorf("\nexpected:\n'%s'\nsaw\n'%s'\n", expectedOutput, buffer.String())
390 }
391 }
392
393 func TestSliceColumnPrint(t *testing.T) {
394 pod := &corev1.Pod{
395 ObjectMeta: metav1.ObjectMeta{
396 Name: "fake-name",
397 Namespace: "fake-namespace",
398 },
399 Spec: corev1.PodSpec{
400 Containers: []corev1.Container{
401 {
402 Name: "fake0",
403 },
404 {
405 Name: "fake1",
406 },
407 {
408 Name: "fake2",
409 },
410 {
411 Name: "fake3",
412 },
413 },
414 },
415 }
416
417 tests := []struct {
418 name string
419 spec string
420 expectedOutput string
421 expectErr bool
422 }{
423 {
424 name: "containers[0]",
425 spec: "CONTAINER:.spec.containers[0].name",
426 expectedOutput: `CONTAINER
427 fake0
428 `,
429 expectErr: false,
430 },
431 {
432 name: "containers[3]",
433 spec: "CONTAINER:.spec.containers[3].name",
434 expectedOutput: `CONTAINER
435 fake3
436 `,
437 expectErr: false,
438 },
439 {
440 name: "containers[5], illegal expression because it is out of bounds",
441 spec: "CONTAINER:.spec.containers[5].name",
442 expectedOutput: "",
443 expectErr: true,
444 },
445 {
446 name: "containers[-1], it equals containers[3]",
447 spec: "CONTAINER:.spec.containers[-1].name",
448 expectedOutput: `CONTAINER
449 fake3
450 `,
451 expectErr: false,
452 },
453 {
454 name: "containers[-2], it equals containers[2]",
455 spec: "CONTAINER:.spec.containers[-2].name",
456 expectedOutput: `CONTAINER
457 fake2
458 `,
459 expectErr: false,
460 },
461 {
462 name: "containers[-4], it equals containers[0]",
463 spec: "CONTAINER:.spec.containers[-4].name",
464 expectedOutput: `CONTAINER
465 fake0
466 `,
467 expectErr: false,
468 },
469 {
470 name: "containers[-5], illegal expression because it is out of bounds",
471 spec: "CONTAINER:.spec.containers[-5].name",
472 expectedOutput: "",
473 expectErr: true,
474 },
475 {
476 name: "containers[0:0], it equals empty set",
477 spec: "CONTAINER:.spec.containers[0:0].name",
478 expectedOutput: `CONTAINER
479 <none>
480 `,
481 expectErr: false,
482 },
483 {
484 name: "containers[0:3]",
485 spec: "CONTAINER:.spec.containers[0:3].name",
486 expectedOutput: `CONTAINER
487 fake0,fake1,fake2
488 `,
489 expectErr: false,
490 },
491 {
492 name: "containers[1:]",
493 spec: "CONTAINER:.spec.containers[1:].name",
494 expectedOutput: `CONTAINER
495 fake1,fake2,fake3
496 `,
497 expectErr: false,
498 },
499 {
500 name: "containers[3:1], illegal expression because start index is greater than end index",
501 spec: "CONTAINER:.spec.containers[3:1].name",
502 expectedOutput: "",
503 expectErr: true,
504 },
505
506 {
507 name: "containers[0:-1], it equals containers[0:3]",
508 spec: "CONTAINER:.spec.containers[0:-1].name",
509 expectedOutput: `CONTAINER
510 fake0,fake1,fake2
511 `,
512 expectErr: false,
513 },
514 {
515 name: "containers[-1:], it equals containers[3:]",
516 spec: "CONTAINER:.spec.containers[-1:].name",
517 expectedOutput: `CONTAINER
518 fake3
519 `,
520 expectErr: false,
521 },
522 {
523 name: "containers[-4:], it equals containers[0:]",
524 spec: "CONTAINER:.spec.containers[-4:].name",
525 expectedOutput: `CONTAINER
526 fake0,fake1,fake2,fake3
527 `,
528 expectErr: false,
529 },
530
531 {
532 name: "containers[-3:-1], it equasl containers[1:3]",
533 spec: "CONTAINER:.spec.containers[-3:-1].name",
534 expectedOutput: `CONTAINER
535 fake1,fake2
536 `,
537 expectErr: false,
538 },
539 {
540 name: "containers[-2:-3], it equals containers[2:1], illegal expression because start index is greater than end index",
541 spec: "CONTAINER:.spec.containers[-2:-3].name",
542 expectedOutput: "",
543 expectErr: true,
544 },
545 {
546 name: "containers[4:4], it equals empty set",
547 spec: "CONTAINER:.spec.containers[4:4].name",
548 expectedOutput: `CONTAINER
549 <none>
550 `,
551 expectErr: false,
552 },
553 {
554 name: "containers[-5:-5], it equals empty set",
555 spec: "CONTAINER:.spec.containers[-5:-5].name",
556 expectedOutput: `CONTAINER
557 <none>
558 `,
559 expectErr: false,
560 },
561 }
562
563 for _, test := range tests {
564 t.Run(test.name, func(t *testing.T) {
565 printer, err := NewCustomColumnsPrinterFromSpec(test.spec, decoder, false)
566 if err != nil {
567 t.Errorf("test %s has unexpected error: %v", test.name, err)
568 }
569
570 buffer := &bytes.Buffer{}
571 err = printer.PrintObj(pod, buffer)
572 if test.expectErr {
573 if err == nil {
574 t.Errorf("test %s has unexpected error: %v", test.name, err)
575
576 }
577 } else {
578 if err != nil {
579 t.Errorf("test %s has unexpected error: %v", test.name, err)
580 } else if buffer.String() != test.expectedOutput {
581 t.Errorf("test %s has unexpected output:\nexpected: %s\nsaw: %s", test.name, test.expectedOutput, buffer.String())
582 }
583 }
584 })
585 }
586 }
587
View as plain text