This repository has been archived by the owner on Jul 11, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 277
/
test_helpers.go
149 lines (127 loc) · 6.64 KB
/
test_helpers.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
// Package test implements utility routes to test the functionality provided by the injector package.
package test
import (
"fmt"
"io/ioutil"
"path"
"path/filepath"
"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/reflect/protoreflect"
"gopkg.in/yaml.v2"
"github.com/openservicemesh/osm/pkg/logger"
)
// All the YAML files listed above are in this sub-directory
const directoryForExpectationsYAML = "../../tests/envoy_xds_expectations/"
var log = logger.New("sidecar-injector")
func getTempDir() string {
dir, err := ioutil.TempDir("", "osm_test_envoy")
if err != nil {
log.Fatal().Err(err).Msg("Error creating temp directory")
}
return dir
}
// LoadExpectedEnvoyYAML loads the expectation for a given test from the file system. This must run within ginkgo.It()
func LoadExpectedEnvoyYAML(expectationFilePath string) string {
// The expectationFileName will contain the name of the function by convention
log.Info().Msgf("Loading test expectation from %s", filepath.Clean(expectationFilePath))
expectedEnvoyConfig, err := ioutil.ReadFile(filepath.Clean(expectationFilePath))
if err != nil {
log.Error().Err(err).Msgf("Error reading expected Envoy bootstrap YAML from file %s", expectationFilePath)
}
gomega.Expect(err).ToNot(gomega.HaveOccurred())
return string(expectedEnvoyConfig)
}
// MarshalXdsStructAndSaveToFile converts a an xDS struct into YAML and saves it to a file. This must run within ginkgo.It()
func MarshalXdsStructAndSaveToFile(m protoreflect.ProtoMessage, filePath string) string {
marshalOptions := protojson.MarshalOptions{
UseProtoNames: true,
}
configJSON, err := marshalOptions.Marshal(m)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
// Convert the JSON to an object.
var jsonObj interface{}
// We are using yaml.Unmarshal here (instead of json.Unmarshal) because the
// Go JSON library doesn't try to pick the right number type (int, float,
// etc.) when unmarshalling to interface{}, it just picks float64
// universally. go-yaml does go through the effort of picking the right
// number type, so we can preserve number type throughout this process.
err = yaml.Unmarshal([]byte(configJSON), &jsonObj)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
// Marshal this object into YAML.
configYAML, err := yaml.Marshal(jsonObj)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
log.Info().Msgf("Saving %s...", filePath)
err = ioutil.WriteFile(filepath.Clean(filePath), configYAML, 0600)
if err != nil {
log.Error().Err(err).Msgf("Error writing actual Envoy Cluster XDS YAML to file %s", filePath)
}
gomega.Expect(err).ToNot(gomega.HaveOccurred())
return string(configYAML)
}
// MarshalAndSaveToFile converts a generic Go struct into YAML and saves it to a file. This must run within ginkgo.It()
func MarshalAndSaveToFile(someStruct interface{}, filePath string) string {
fileContent, err := yaml.Marshal(someStruct)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
log.Info().Msgf("Saving %s...", filePath)
err = ioutil.WriteFile(filepath.Clean(filePath), fileContent, 0600)
if err != nil {
log.Error().Err(err).Msgf("Error writing actual Envoy Cluster XDS YAML to file %s", filePath)
}
gomega.Expect(err).ToNot(gomega.HaveOccurred())
return string(fileContent)
}
// ThisFunction runs the given function in a ginkgo.Context(), marshals the output and compares to an expectation loaded from file.
func ThisFunction(functionName string, fn func() interface{}) {
ginkgo.Context(fmt.Sprintf("ThisFunction %s", functionName), func() {
ginkgo.It("creates Envoy config", func() {
expectationFilePath := path.Join(directoryForExpectationsYAML, fmt.Sprintf("expected_output_%s.yaml", functionName))
actualFilePath := path.Join(getTempDir(), fmt.Sprintf("actual_output_%s.yaml", functionName))
log.Info().Msgf("Actual output of %s is going to be saved in %s", functionName, actualFilePath)
actual := fn()
expectedYAML := LoadExpectedEnvoyYAML(expectationFilePath)
actualYAML := MarshalAndSaveToFile(actual, actualFilePath)
Compare(functionName, actualFilePath, expectationFilePath, actualYAML, expectedYAML)
})
})
}
// ThisXdsClusterFunction runs the given function in a ginkgo.Context(), marshals the output and compares to an expectation loaded from file.
func ThisXdsClusterFunction(functionName string, fn func() protoreflect.ProtoMessage) {
ginkgo.Context(fmt.Sprintf("ThisFunction %s", functionName), func() {
ginkgo.It("creates Envoy config", func() {
expectationFilePath := path.Join(directoryForExpectationsYAML, fmt.Sprintf("expected_output_%s.yaml", functionName))
actualFilePath := path.Join(getTempDir(), fmt.Sprintf("actual_output_%s.yaml", functionName))
log.Info().Msgf("Actual output of %s is going to be saved in %s", functionName, actualFilePath)
actual := fn()
expectedYAML := LoadExpectedEnvoyYAML(expectationFilePath)
actualYAML := MarshalXdsStructAndSaveToFile(actual, actualFilePath)
Compare(functionName, actualFilePath, expectationFilePath, actualYAML, expectedYAML)
})
})
}
// ThisXdsListenerFunction runs the given function in a ginkgo.Context(), marshals the output and compares to an expectation loaded from file.
func ThisXdsListenerFunction(functionName string, fn func() (protoreflect.ProtoMessage, error)) {
ginkgo.Context(fmt.Sprintf("ThisFunction %s", functionName), func() {
ginkgo.It("creates Envoy config", func() {
expectationFilePath := path.Join(directoryForExpectationsYAML, fmt.Sprintf("expected_output_%s.yaml", functionName))
actualFilePath := path.Join(getTempDir(), fmt.Sprintf("actual_output_%s.yaml", functionName))
log.Info().Msgf("Actual output of %s is going to be saved in %s", functionName, actualFilePath)
actual, err := fn()
gomega.Expect(err).To(gomega.BeNil())
expectedYAML := LoadExpectedEnvoyYAML(expectationFilePath)
actualYAML := MarshalXdsStructAndSaveToFile(actual, actualFilePath)
Compare(functionName, actualFilePath, expectationFilePath, actualYAML, expectedYAML)
})
})
}
// Compare is a wrapper around gomega.Expect().To(Equal()) and compares actualYAML and expectedYAML; It also provides a verbose message when things don't match with a tip on how to fix things.
func Compare(functionName, actualFilename, expectedFilename, actualYAML, expectedYAML string) {
gomega.Expect(actualYAML).To(gomega.Equal(expectedYAML),
fmt.Sprintf(`The actual output of function %s (saved in file %s) does not match the expected loaded from file %s;
Compare the contents of the files with "diff %s %s"
If you are certain the actual output is correct: "cat %s > %s"`,
functionName, actualFilename, expectedFilename,
actualFilename, expectedFilename,
actualFilename, expectedFilename))
}