forked from knative/func
-
Notifications
You must be signed in to change notification settings - Fork 0
/
function_test.go
226 lines (197 loc) · 6.5 KB
/
function_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
//go:build !integration
// +build !integration
package function_test
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
fn "knative.dev/func"
. "knative.dev/func/testing"
)
// TestFunction_PathDefault ensures that the default path when instantiating
// a NewFunciton is to use the current working directory.
func TestFunction_PathDefault(t *testing.T) {
root, rm := Mktemp(t)
defer rm()
var f fn.Function
var err error
if f, err = fn.NewFunction(root); err != nil {
t.Fatal(err)
}
f.Name = "f"
f.Runtime = "go"
if err := f.Write(); err != nil {
t.Fatal(err)
}
if f, err = fn.NewFunction(""); err != nil {
t.Fatal(err)
}
if f.Name != "f" {
t.Fatalf("expected function 'f', got '%v'", f.Name)
}
}
// TestFunction_PathErrors ensures that instantiating a function errors if
// the path does not exist or is not a directory, but does not require the
// path contain an initialized function.
func TestFunction_PathErrors(t *testing.T) {
root, rm := Mktemp(t)
defer rm()
_, err := fn.NewFunction(root)
if err != nil {
t.Fatalf("an empty but valid directory path should not error. got '%v'", err)
}
_, err = fn.NewFunction(filepath.Join(root, "nonexistent"))
if err == nil {
t.Fatalf("a nonexistent path should error")
}
if err := os.WriteFile("filepath", []byte{}, os.ModePerm); err != nil {
t.Fatal(err)
}
_, err = fn.NewFunction(filepath.Join(root, "filepath"))
if err == nil {
t.Fatalf("an invalid path (non-directory) should error")
}
}
// TestFunction_WriteIdempotency ensures that a function can be written repeatedly
// without change.
func TestFunction_WriteIdempotency(t *testing.T) {
root, rm := Mktemp(t)
defer rm()
client := fn.New(fn.WithRegistry(TestRegistry))
// Create a function
f := fn.Function{
Runtime: TestRuntime,
Root: root,
}
if err := client.Create(f); err != nil {
t.Fatal(err)
}
// Load the function and write it again
f1, err := fn.NewFunction(root)
if err != nil {
t.Fatal(err)
}
if err := f1.Write(); err != nil {
t.Fatal(err)
}
// Load it again and compare
f2, err := fn.NewFunction(root)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(f1, f2); diff != "" {
t.Error("function differs after reload (-before, +after):", diff)
}
}
// TestFunction_NameDefault ensures that a function's name is defaulted to that
// which can be derived from the last part of its path.
// Creating a new function from a path will error if there is no function at
// that path. Creating using the client initializes the default.
func TestFunction_NameDefault(t *testing.T) {
// A path at which there is no function currently
root := "testdata/testFunctionNameDefault"
defer Using(t, root)()
f, err := fn.NewFunction(root)
if err != nil {
t.Fatal(err)
}
if f.Initialized() {
t.Fatal("a function about an empty, but valid path, shold not be initialized")
}
// Create the function at the path
client := fn.New(fn.WithRegistry(TestRegistry))
f = fn.Function{
Runtime: TestRuntime,
Root: root,
}
if err := client.Create(f); err != nil {
t.Fatal(err)
}
// Load the (now extant) function
f, err = fn.NewFunction(root)
if err != nil {
t.Fatal(err)
}
// Verify the name was defaulted as expected
if f.Name != "testFunctionNameDefault" {
t.Fatalf("expected name 'testFunctionNameDefault', got '%v'", f.Name)
}
}
// Test_Interpolate ensures environment variable interpolation processes
// environment variables by interpolating properly formatted references to
// local environment variables, returning a final simple map structure.
// Also ensures that nil value references are interpreted as meaning the
// environment is not to be included in the resultant map, rather than included
// with an empty value.
// TODO: Perhaps referring to a nonexistent local env var should be treated
// as a "leave as is" (do not set) rather than "required" resulting in error?
// TODO: What use case does a nil pointer in the Env struct serve? Add it
// explicitly here ore get rid of the nils.
func Test_Interpolate(t *testing.T) {
t.Setenv("INTERPOLATE", "interpolated")
cases := []struct {
Value string
Expected string
Error bool
}{
// Simple values are kept unchanged
{Value: "simple value", Expected: "simple value"},
// Properly referenced environment variables are interpolated
{Value: "{{ env:INTERPOLATE }}", Expected: "interpolated"},
// Other interpolation types other than "env" are left unchanged
{Value: "{{ other:TYPE }}", Expected: "{{ other:TYPE }}", Error: false},
// Properly formatted references to missing variables error
{Value: "{{ env:MISSING }}", Expected: "", Error: true},
}
name := "NAME" // default name for all tests
for _, c := range cases {
t.Logf("Value: %v\n", c.Value)
var (
envs = []fn.Env{{Name: &name, Value: &c.Value}} // pre-interpolated
vv, err = fn.Interpolate(envs) // interpolated
v = vv[name] // final value
)
if c.Error && err == nil {
t.Fatal("expected error in Envs interpolation not received")
}
if v != c.Expected {
t.Fatalf("expected env value '%v' to be interpolated as '%v', but got '%v'", c.Value, c.Expected, v)
}
}
// Nil value should be treated as being disincluded from the resultant map.
envs := []fn.Env{{Name: &name}} // has a nil *Value ptr
vv, err := fn.Interpolate(envs)
if err != nil {
t.Fatal(err)
}
if len(vv) != 0 {
t.Fatalf("expected envs with a nil value to not be included in interpolation result")
}
}
// TestFunction_MarshallingError check that the correct error gets reported back to the
// user if the function that is being loaded is failing marshalling and cannot be migrated
func TestFunction_MarshallingError(t *testing.T) {
root := "testdata/testFunctionMarshallingError"
// Load the function to see it fail with a marshalling error
_, err := fn.NewFunction(root)
if err != nil {
if !strings.Contains(err.Error(), "Marshalling: 'func.yaml' is not valid:") {
t.Fatalf("expected unmarshalling error")
}
}
}
// TestFunction_MigrationError check that the correct error gets reported back to the
// user if the function that is being loaded is failing marshalling and cannot be migrated
func TestFunction_MigrationError(t *testing.T) {
root := "testdata/testFunctionMigrationError"
// Load the function to see it fail with a migration error
_, err := fn.NewFunction(root)
if err != nil {
// This function makes the migration fails
if !strings.Contains(err.Error(), "migration 'migrateToBuilderImages' error") {
t.Fatalf("expected migration error")
}
}
}