forked from knative/func
-
Notifications
You must be signed in to change notification settings - Fork 0
/
root_test.go
336 lines (297 loc) · 8.23 KB
/
root_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
package cmd
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/ory/viper"
"knative.dev/client-pkg/pkg/util"
fn "knative.dev/func/pkg/functions"
. "knative.dev/func/pkg/testing"
)
const TestRegistry = "example.com/alice"
func TestRoot_mergeEnvMaps(t *testing.T) {
a := "A"
b := "B"
v1 := "x"
v2 := "y"
type args struct {
envs []fn.Env
toUpdate *util.OrderedMap
toRemove []string
}
tests := []struct {
name string
args args
want []fn.Env
}{
{
"add new var to empty list",
args{
[]fn.Env{},
util.NewOrderedMapWithKVStrings([][]string{{a, v1}}),
[]string{},
},
[]fn.Env{{Name: &a, Value: &v1}},
},
{
"add new var",
args{
[]fn.Env{{Name: &b, Value: &v2}},
util.NewOrderedMapWithKVStrings([][]string{{a, v1}}),
[]string{},
},
[]fn.Env{{Name: &b, Value: &v2}, {Name: &a, Value: &v1}},
},
{
"update var",
args{
[]fn.Env{{Name: &a, Value: &v1}},
util.NewOrderedMapWithKVStrings([][]string{{a, v2}}),
[]string{},
},
[]fn.Env{{Name: &a, Value: &v2}},
},
{
"update multiple vars",
args{
[]fn.Env{{Name: &a, Value: &v1}, {Name: &b, Value: &v2}},
util.NewOrderedMapWithKVStrings([][]string{{a, v2}, {b, v1}}),
[]string{},
},
[]fn.Env{{Name: &a, Value: &v2}, {Name: &b, Value: &v1}},
},
{
"remove var",
args{
[]fn.Env{{Name: &a, Value: &v1}},
util.NewOrderedMap(),
[]string{a},
},
[]fn.Env{},
},
{
"remove multiple vars",
args{
[]fn.Env{{Name: &a, Value: &v1}, {Name: &b, Value: &v2}},
util.NewOrderedMap(),
[]string{a, b},
},
[]fn.Env{},
},
{
"update and remove vars",
args{
[]fn.Env{{Name: &a, Value: &v1}, {Name: &b, Value: &v2}},
util.NewOrderedMapWithKVStrings([][]string{{a, v2}}),
[]string{b},
},
[]fn.Env{{Name: &a, Value: &v2}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, _, err := mergeEnvs(tt.args.envs, tt.args.toUpdate, tt.args.toRemove)
if err != nil {
t.Errorf("mergeEnvs() for initial vars %v and toUpdate %v and toRemove %v got error %v",
tt.args.envs, tt.args.toUpdate, tt.args.toRemove, err)
}
if !reflect.DeepEqual(got, tt.want) {
gotString := "{ "
for _, e := range got {
gotString += fmt.Sprintf("{ %s: %s } ", *e.Name, *e.Value)
}
gotString += "}"
wantString := "{ "
for _, e := range tt.want {
wantString += fmt.Sprintf("{ %s: %s } ", *e.Name, *e.Value)
}
wantString += "}"
t.Errorf("mergeEnvs() = got: %s, want %s", gotString, wantString)
}
})
}
}
// TestRoot_CommandNameParameterized confirmst that the command name, as
// printed in help text, is parameterized based on the constructor parameters
// of the root command. This allows, for example, to have help text correct
// when both embedded as a plugin or standalone.
func TestRoot_CommandNameParameterized(t *testing.T) {
expectedSynopsis := "%v is the command line interface for"
tests := []string{
"func", // standalone
"kn func", // kn plugin
}
for _, testName := range tests {
var (
cmd = NewRootCmd(RootCommandConfig{Name: testName})
out = strings.Builder{}
)
cmd.SetArgs([]string{}) // Do not use test command args
cmd.SetOut(&out)
if err := cmd.Help(); err != nil {
t.Fatal(err)
}
if cmd.Use != testName {
t.Fatalf("expected command Use '%v', got '%v'", testName, cmd.Use)
}
if !strings.HasPrefix(out.String(), fmt.Sprintf(expectedSynopsis, testName)) {
t.Logf("Testing '%v'\n", testName)
t.Log(out.String())
t.Fatalf("Help text does not include substituted name '%v'", testName)
}
}
}
func TestVerbose(t *testing.T) {
tests := []struct {
name string
args []string
want string
wantLF int
}{
{
name: "verbose as version's flag",
args: []string{"version", "-v"},
want: "Version: v0.42.0-cafe-1970-01-01",
wantLF: 3,
},
{
name: "no verbose",
args: []string{"version"},
want: "v0.42.0",
wantLF: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
viper.Reset()
var out bytes.Buffer
cmd := NewRootCmd(RootCommandConfig{
Name: "func",
Version: Version{
Date: "1970-01-01",
Vers: "v0.42.0",
Hash: "cafe",
}})
cmd.SetArgs(tt.args)
cmd.SetOut(&out)
if err := cmd.Execute(); err != nil {
t.Fatal(err)
}
outLines := strings.Split(out.String(), "\n")
if len(outLines)-1 != tt.wantLF {
t.Errorf("expected output with %v line breaks but got %v:", tt.wantLF, len(outLines)-1)
}
if outLines[0] != tt.want {
t.Errorf("expected output: %q but got: %q", tt.want, outLines[0])
}
})
}
}
// TestRoot_effectivePath ensures that the path method returns the effective path
// to use with the following precedence: empty by default, then FUNC_PATH
// environment variable, -p flag, or finally --path with the highest precedence.
func TestRoot_effectivePath(t *testing.T) {
args := os.Args
t.Cleanup(func() { os.Args = args })
t.Run("default", func(t *testing.T) {
if effectivePath() != "" {
t.Fatalf("the default path should be '.', got '%v'", effectivePath())
}
})
t.Run("FUNC_PATH", func(t *testing.T) {
t.Setenv("FUNC_PATH", "p1")
if effectivePath() != "p1" {
t.Fatalf("the effetive path did not load the environment variable. Expected 'p1', got '%v'", effectivePath())
}
})
t.Run("--path", func(t *testing.T) {
os.Args = []string{"test", "--path=p2"}
if effectivePath() != "p2" {
t.Fatalf("the effective path did not load the --path flag. Expected 'p2', got '%v'", effectivePath())
}
})
t.Run("-p", func(t *testing.T) {
os.Args = []string{"test", "-p=p3"}
if effectivePath() != "p3" {
t.Fatalf("the effective path did not load the -p flag. Expected 'p3', got '%v'", effectivePath())
}
})
t.Run("short flag precedence", func(t *testing.T) {
t.Setenv("FUNC_PATH", "p1")
os.Args = []string{"test", "-p=p3"}
if effectivePath() != "p3" {
t.Fatalf("the effective path did not load the -p flag with precedence over FUNC_PATH. Expected 'p3', got '%v'", effectivePath())
}
})
t.Run("-p highest precedence", func(t *testing.T) {
t.Setenv("FUNC_PATH", "p1")
os.Args = []string{"test", "--path=p2", "-p=p3"}
if effectivePath() != "p3" {
t.Fatalf("the effective path did not take -p with highest precedence over --path and FUNC_PATH. Expected 'p3', got '%v'", effectivePath())
}
})
t.Run("continues on unrecognized flags", func(t *testing.T) {
os.Args = []string{"test", "-r=repo.example.com/bob", "-p=p3"}
if effectivePath() != "p3" {
t.Fatalf("the effective path did not evaluate when unexpected flags were present")
}
})
}
// Helpers
// -------
// pipe the output of stdout to a buffer whose value is returned
// from the returned function. Call pipe() to start piping output
// to the buffer, call the returned function to access the data in
// the buffer.
func piped(t *testing.T) func() string {
t.Helper()
var (
o = os.Stdout
c = make(chan error, 1)
b strings.Builder
)
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
os.Stdout = w
go func() {
_, err := io.Copy(&b, r)
r.Close()
c <- err
}()
return func() string {
os.Stdout = o
w.Close()
err := <-c
if err != nil {
t.Fatal(err)
}
return strings.TrimSpace(b.String())
}
}
// fromTempDirectory is a test helper which endeavors to create
// an environment clean of developer's settings for use during CLI testing.
func fromTempDirectory(t *testing.T) string {
t.Helper()
ClearEnvs(t)
// We have to define KUBECONFIG, or the file at ~/.kube/config (if extant)
// will be used (disrupting tests by using the current user's environment).
// The test kubeconfig set below has the current namespace set to 'func'
// NOTE: the below settings affect unit tests only, and we do explicitly
// want all unit tests to start in an empty environment with tests "opting in"
// to config, not opting out.
t.Setenv("KUBECONFIG", filepath.Join(cwd(), "testdata", "default_kubeconfig"))
// By default unit tests presum no config exists unless provided in testdata.
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
// creates and CDs to a temp directory
d, done := Mktemp(t)
// Return to original directory and resets viper.
t.Cleanup(func() { done(); viper.Reset() })
return d
}