1
16
17 package get
18
19 import (
20 "bytes"
21 "fmt"
22 "regexp"
23 "strings"
24 "testing"
25
26 corev1 "k8s.io/api/core/v1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/runtime"
29 "k8s.io/apimachinery/pkg/runtime/schema"
30 "k8s.io/cli-runtime/pkg/genericclioptions"
31 )
32
33 func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
34 testTable := &metav1.Table{
35 ColumnDefinitions: []metav1.TableColumnDefinition{
36 {Name: "Name", Type: "string", Format: "name"},
37 {Name: "Ready", Type: "string", Format: ""},
38 {Name: "Status", Type: "string", Format: ""},
39 {Name: "Restarts", Type: "integer", Format: ""},
40 {Name: "Age", Type: "string", Format: ""},
41 {Name: "IP", Type: "string", Format: "", Priority: 1},
42 {Name: "Node", Type: "string", Format: "", Priority: 1},
43 {Name: "Nominated Node", Type: "string", Format: "", Priority: 1},
44 {Name: "Readiness Gates", Type: "string", Format: "", Priority: 1},
45 },
46 Rows: []metav1.TableRow{{
47 Object: runtime.RawExtension{
48 Object: &corev1.Pod{
49 TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Pod"},
50 ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foons", Labels: map[string]string{"l1": "value"}},
51 },
52 },
53 Cells: []interface{}{"foo", "0/0", "", int64(0), "<unknown>", "<none>", "<none>", "<none>", "<none>"},
54 }},
55 }
56 testPod := &corev1.Pod{
57 TypeMeta: metav1.TypeMeta{
58 APIVersion: "v1",
59 Kind: "Pod",
60 },
61 ObjectMeta: metav1.ObjectMeta{
62 Name: "foo",
63 Namespace: "foons",
64 Labels: map[string]string{
65 "l1": "value",
66 },
67 },
68 }
69
70 testCases := []struct {
71 name string
72 testObject runtime.Object
73 showKind bool
74 showLabels bool
75
76
77 sortBy string
78 columnLabels []string
79
80 noHeaders bool
81 withNamespace bool
82
83 outputFormat string
84
85 expectedError string
86 expectedOutput string
87 expectNoMatch bool
88 }{
89 {
90 name: "empty output format matches a humanreadable printer",
91 testObject: testPod.DeepCopy(),
92 expectedOutput: "NAME\\ +AGE\nfoo\\ +<unknown>\n",
93 },
94 {
95 name: "empty output format matches a humanreadable printer",
96 testObject: testTable.DeepCopy(),
97 expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\nfoo\\ +0/0\\ +0\\ +<unknown>\n",
98 },
99 {
100 name: "\"wide\" output format prints",
101 testObject: testPod.DeepCopy(),
102 outputFormat: "wide",
103 expectedOutput: "NAME\\ +AGE\nfoo\\ +<unknown>\n",
104 },
105 {
106 name: "\"wide\" output format prints",
107 testObject: testTable.DeepCopy(),
108 outputFormat: "wide",
109 expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +IP\\ +NODE\\ +NOMINATED NODE\\ +READINESS GATES\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
110 },
111 {
112 name: "no-headers prints output with no headers",
113 testObject: testPod.DeepCopy(),
114 noHeaders: true,
115 expectedOutput: "foo\\ +<unknown>\n",
116 },
117 {
118 name: "no-headers prints output with no headers",
119 testObject: testTable.DeepCopy(),
120 noHeaders: true,
121 expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\n",
122 },
123 {
124 name: "no-headers and a \"wide\" output format prints output with no headers and additional columns",
125 testObject: testPod.DeepCopy(),
126 outputFormat: "wide",
127 noHeaders: true,
128 expectedOutput: "foo\\ +<unknown>\n",
129 },
130 {
131 name: "no-headers and a \"wide\" output format prints output with no headers and additional columns",
132 testObject: testTable.DeepCopy(),
133 outputFormat: "wide",
134 noHeaders: true,
135 expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
136 },
137 {
138 name: "show-kind displays the resource's kind, even when printing a single type of resource",
139 testObject: testPod.DeepCopy(),
140 showKind: true,
141 expectedOutput: "NAME\\ +AGE\npod/foo\\ +<unknown>\n",
142 },
143 {
144 name: "show-kind displays the resource's kind, even when printing a single type of resource",
145 testObject: testTable.DeepCopy(),
146 showKind: true,
147 expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\npod/foo\\ +0/0\\ +0\\ +<unknown>\n",
148 },
149 {
150 name: "label-columns prints specified label values in new column",
151 testObject: testPod.DeepCopy(),
152 columnLabels: []string{"l1"},
153 expectedOutput: "NAME\\ +AGE\\ +L1\nfoo\\ +<unknown>\\ +value\n",
154 },
155 {
156 name: "label-columns prints specified label values in new column",
157 testObject: testTable.DeepCopy(),
158 columnLabels: []string{"l1"},
159 expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +L1\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +value\n",
160 },
161 {
162 name: "withNamespace displays an additional NAMESPACE column",
163 testObject: testPod.DeepCopy(),
164 withNamespace: true,
165 expectedOutput: "NAMESPACE\\ +NAME\\ +AGE\nfoons\\ +foo\\ +<unknown>\n",
166 },
167 {
168 name: "withNamespace displays an additional NAMESPACE column",
169 testObject: testTable.DeepCopy(),
170 withNamespace: true,
171 expectedOutput: "NAMESPACE\\ +NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\nfoons\\ +foo\\ +0/0\\ +0\\ +<unknown>\n",
172 },
173 {
174 name: "no printer is matched on an invalid outputFormat",
175 testObject: testPod.DeepCopy(),
176 outputFormat: "invalid",
177 expectNoMatch: true,
178 },
179 {
180 name: "printer should not match on any other format supported by another printer",
181 testObject: testPod.DeepCopy(),
182 outputFormat: "go-template",
183 expectNoMatch: true,
184 },
185 }
186
187 for _, tc := range testCases {
188 t.Run(fmt.Sprintf("%s %T", tc.name, tc.testObject), func(t *testing.T) {
189 printFlags := HumanPrintFlags{
190 ShowKind: &tc.showKind,
191 ShowLabels: &tc.showLabels,
192 SortBy: &tc.sortBy,
193 ColumnLabels: &tc.columnLabels,
194
195 NoHeaders: tc.noHeaders,
196 WithNamespace: tc.withNamespace,
197 }
198
199 if tc.showKind {
200 printFlags.Kind = schema.GroupKind{Kind: "pod"}
201 }
202
203 p, err := printFlags.ToPrinter(tc.outputFormat)
204 if tc.expectNoMatch {
205 if !genericclioptions.IsNoCompatiblePrinterError(err) {
206 t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
207 }
208 return
209 }
210 if genericclioptions.IsNoCompatiblePrinterError(err) {
211 t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
212 }
213
214 if len(tc.expectedError) > 0 {
215 if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
216 t.Errorf("expecting error %q, got %v", tc.expectedError, err)
217 }
218 return
219 }
220 if err != nil {
221 t.Fatalf("unexpected error: %v", err)
222 }
223
224 out := bytes.NewBuffer([]byte{})
225 err = p.PrintObj(tc.testObject, out)
226 if err != nil {
227 t.Errorf("unexpected error: %v", err)
228 }
229
230 match, err := regexp.Match(tc.expectedOutput, out.Bytes())
231 if err != nil {
232 t.Errorf("unexpected error: %v", err)
233 }
234 if !match {
235 t.Errorf("unexpected output: expecting\n%s\ngot\n%s", tc.expectedOutput, out.String())
236 }
237 })
238 }
239 }
240
View as plain text