Skip to content

Commit 4d993d9

Browse files
committed
WIP: Add golden test for function output
1 parent f21fbb5 commit 4d993d9

File tree

12 files changed

+604
-4
lines changed

12 files changed

+604
-4
lines changed

functions/go/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.18
44

55
require (
66
github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0-20221007213718-5fa523b306fe
7+
github.com/google/go-cmp v0.5.9
78
k8s.io/apimachinery v0.25.2
89
)
910

functions/go/go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
33
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
44
github.com/GoogleContainerTools/kpt-functions-sdk/go/api v0.0.0-20221007213718-5fa523b306fe h1:8PNHR5cev3FqOduItCH59KZdabDwz9I5xNcB8Woc6es=
55
github.com/GoogleContainerTools/kpt-functions-sdk/go/api v0.0.0-20221007213718-5fa523b306fe/go.mod h1:prNhhUAODrB2VqHVead9tB8nLU9ffY4e4jjBwLMNO1M=
6-
github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0-20220929172014-8888fc9691d1 h1:DFLHb51Av8m6ETGvd+J4Ok0pvfo735OW0sP+wbJLQyg=
7-
github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0-20220929172014-8888fc9691d1/go.mod h1:TZd94D9b8alIDuF3mW9g+ofbl0RfwT0JewXmXm7phDI=
86
github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0-20221007213718-5fa523b306fe h1:BjbTCT1mkPf0s4X3osOleEcaa89psOlcEJGUcLc+YPo=
97
github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0-20221007213718-5fa523b306fe/go.mod h1:mqc5jH6i0Ll6T4wCmTXsVNVxhwBGlVtrtmoF/g3E9lE=
108
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
@@ -67,7 +65,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
6765
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
6866
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
6967
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
70-
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
68+
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
69+
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
7170
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
7271
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
7372
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
@@ -76,8 +75,8 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
7675
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
7776
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
7877
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
79-
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
8078
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
79+
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
8180
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
8281
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
8382
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -92,6 +91,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
9291
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9392
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
9493
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
94+
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
9595
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
9696
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
9797
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"testing"
19+
20+
"github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/set-name-prefix/pkg/krmfunctiontests"
21+
"github.com/GoogleContainerTools/kpt-functions-sdk/go/fn"
22+
)
23+
24+
func TestFunctions(t *testing.T) {
25+
krmfunctiontests.RunFunctionTests(t, "testdata", fn.ResourceListProcessorFunc(Run))
26+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package krmfunctiontests
16+
17+
import (
18+
"io/ioutil"
19+
"path/filepath"
20+
"sort"
21+
"strings"
22+
"testing"
23+
24+
"github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/set-name-prefix/pkg/testhelpers"
25+
"github.com/GoogleContainerTools/kpt-functions-sdk/go/fn"
26+
)
27+
28+
func RunFunctionTests(t *testing.T, testdata string, krmFunction fn.ResourceListProcessorFunc) {
29+
dirs, err := ioutil.ReadDir(testdata)
30+
if err != nil {
31+
t.Fatalf("failed to read directory %q: %v", testdata, err)
32+
}
33+
34+
for _, d := range dirs {
35+
dir := filepath.Join(testdata, d.Name())
36+
if !d.IsDir() {
37+
t.Errorf("expected directory, found %s", dir)
38+
continue
39+
}
40+
41+
t.Run(dir, func(t *testing.T) {
42+
files, err := ioutil.ReadDir(dir)
43+
if err != nil {
44+
t.Fatalf("failed to read directory %q: %v", testdata, err)
45+
}
46+
sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() })
47+
var items []*fn.KubeObject
48+
for _, f := range files {
49+
if strings.HasPrefix(f.Name(), "_") {
50+
continue
51+
}
52+
fileItems := mustParseFile(t, filepath.Join(dir, f.Name()))
53+
items = append(items, fileItems...)
54+
}
55+
56+
config := mustParseFile(t, filepath.Join(dir, "_fnconfig.yaml"))
57+
58+
var functionConfig *fn.KubeObject
59+
if len(config) == 0 {
60+
functionConfig = nil
61+
} else if len(config) == 1 {
62+
functionConfig = config[0]
63+
} else {
64+
t.Fatalf("found multiple config objects in %s", filepath.Join(dir, "_fnconfig.yaml"))
65+
}
66+
67+
rl, err := buildResourceList(items, functionConfig)
68+
if err != nil {
69+
t.Fatalf("failed to build resource list: %v", err)
70+
}
71+
72+
success, err := krmFunction(rl)
73+
if err != nil {
74+
t.Fatalf("Run failed unexpectedly: %v", err)
75+
}
76+
if !success {
77+
t.Fatalf("Run did not succeed")
78+
}
79+
80+
rlYAML, err := rl.ToYAML()
81+
if err != nil {
82+
t.Fatalf("failed to convert resource list to yaml: %v", err)
83+
}
84+
85+
p := filepath.Join(dir, "_expected.yaml")
86+
testhelpers.CompareGoldenFile(t, p, rlYAML)
87+
})
88+
}
89+
}
90+
91+
func mustParseFile(t *testing.T, p string) []*fn.KubeObject {
92+
b := testhelpers.MustReadFile(t, p)
93+
objects, err := fn.ParseKubeObjects(b)
94+
if err != nil {
95+
t.Fatalf("failed to parse objects from file %q: %v", p, err)
96+
}
97+
return objects
98+
}
99+
100+
func buildResourceList(items []*fn.KubeObject, functionConfig *fn.KubeObject) (*fn.ResourceList, error) {
101+
rl := &fn.ResourceList{}
102+
rl.Items = items
103+
rl.FunctionConfig = functionConfig
104+
105+
return rl, nil
106+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package testhelpers
16+
17+
import (
18+
"bytes"
19+
"fmt"
20+
"io"
21+
"io/fs"
22+
"io/ioutil"
23+
"os"
24+
"path/filepath"
25+
"testing"
26+
27+
"github.com/google/go-cmp/cmp"
28+
)
29+
30+
func RunGoldenTests(t *testing.T, basedir string, fn func(t *testing.T, dir string)) {
31+
files, err := ioutil.ReadDir(basedir)
32+
if err != nil {
33+
t.Fatalf("ReadDir(%q) failed: %v", basedir, err)
34+
}
35+
for _, file := range files {
36+
name := file.Name()
37+
absPath := filepath.Join(basedir, name)
38+
t.Run(name, func(t *testing.T) {
39+
fn(t, absPath)
40+
})
41+
}
42+
}
43+
44+
func MustReadFile(t *testing.T, p string) []byte {
45+
b, err := ioutil.ReadFile(p)
46+
if err != nil {
47+
t.Fatalf("failed to read file %q: %v", p, err)
48+
}
49+
return b
50+
}
51+
52+
func CompareGoldenFile(t *testing.T, p string, got []byte) {
53+
if os.Getenv("WRITE_GOLDEN_OUTPUT") != "" {
54+
// Short-circuit when the output is correct
55+
b, err := ioutil.ReadFile(p)
56+
if err == nil && bytes.Equal(b, got) {
57+
return
58+
}
59+
60+
if err := ioutil.WriteFile(p, got, 0644); err != nil {
61+
t.Fatalf("failed to write golden output %s: %v", p, err)
62+
}
63+
t.Errorf("wrote output to %s", p)
64+
} else {
65+
want := MustReadFile(t, p)
66+
if diff := cmp.Diff(string(want), string(got)); diff != "" {
67+
t.Errorf("unexpected diff in %s: %s", p, diff)
68+
}
69+
}
70+
}
71+
72+
func CopyDir(src, dest string) error {
73+
srcFiles, err := ioutil.ReadDir(src)
74+
if err != nil {
75+
return fmt.Errorf("ReadDir(%q) failed: %w", src, err)
76+
}
77+
for _, srcFile := range srcFiles {
78+
srcPath := filepath.Join(src, srcFile.Name())
79+
destPath := filepath.Join(dest, srcFile.Name())
80+
if srcFile.IsDir() {
81+
if err := CopyDir(srcPath, destPath); err != nil {
82+
return err
83+
}
84+
} else {
85+
if err := CopyFile(srcPath, destPath); err != nil {
86+
return err
87+
}
88+
}
89+
}
90+
return nil
91+
}
92+
93+
func CopyFile(src, dest string) error {
94+
in, err := os.Open(src)
95+
if err != nil {
96+
return fmt.Errorf("OpenFile(%q) failed: %w", src, err)
97+
}
98+
defer in.Close()
99+
100+
out, err := os.Create(dest)
101+
if err != nil {
102+
return fmt.Errorf("Create(%q) failed: %w", dest, err)
103+
}
104+
105+
if _, err := io.Copy(out, in); err != nil {
106+
out.Close()
107+
return fmt.Errorf("byte copy from %s to %s failed: %w", src, dest, err)
108+
}
109+
110+
if err := out.Close(); err != nil {
111+
return fmt.Errorf("Close(%q) failed: %w", dest, err)
112+
}
113+
114+
return nil
115+
}
116+
117+
func CompareDir(t *testing.T, expectDir, actualDir string) {
118+
expectFiles, err := ioutil.ReadDir(expectDir)
119+
if err != nil {
120+
t.Fatalf("failed to read expectation directory %q: %v", expectDir, err)
121+
}
122+
expectFileMap := make(map[string]fs.FileInfo)
123+
for _, expectFile := range expectFiles {
124+
expectFileMap[expectFile.Name()] = expectFile
125+
}
126+
127+
actualFiles, err := ioutil.ReadDir(actualDir)
128+
if err != nil {
129+
t.Fatalf("failed to read actual directory %q: %v", actualDir, err)
130+
}
131+
actualFileMap := make(map[string]fs.FileInfo)
132+
for _, actualFile := range actualFiles {
133+
actualFileMap[actualFile.Name()] = actualFile
134+
}
135+
136+
for _, expectFile := range expectFiles {
137+
name := expectFile.Name()
138+
actualFile := actualFileMap[name]
139+
if actualFile == nil {
140+
t.Errorf("expected file %s not found", name)
141+
continue
142+
}
143+
144+
if expectFile.IsDir() {
145+
if !actualFile.IsDir() {
146+
t.Errorf("expected file %s was not a directory", name)
147+
continue
148+
}
149+
CompareDir(t, filepath.Join(expectDir, name), filepath.Join(actualDir, name))
150+
} else {
151+
if actualFile.IsDir() {
152+
t.Errorf("expected file %s was not a file", name)
153+
continue
154+
}
155+
CompareFile(t, expectDir, actualDir, name)
156+
}
157+
}
158+
159+
for _, actualFile := range actualFiles {
160+
name := actualFile.Name()
161+
expectFile := expectFileMap[name]
162+
if expectFile == nil {
163+
t.Errorf("additional file %s found in output", name)
164+
continue
165+
}
166+
}
167+
}
168+
169+
func CompareFile(t *testing.T, expectDir, actualDir string, relPath string) {
170+
expectAbs := filepath.Join(expectDir, relPath)
171+
172+
actualAbs := filepath.Join(actualDir, relPath)
173+
actualBytes, err := ioutil.ReadFile(actualAbs)
174+
if err != nil {
175+
if os.IsNotExist(err) {
176+
t.Errorf("expected file %s not found", relPath)
177+
} else {
178+
t.Fatalf("error reading file %s: %v", actualAbs, err)
179+
}
180+
}
181+
182+
CompareGoldenFile(t, expectAbs, actualBytes)
183+
}

0 commit comments

Comments
 (0)