github.com/iter8-tools/iter8@v1.1.2/cmd/test_helpers.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Credit: this file is composed by modifying the following two files from Helm in minor ways 18 // https://raw.githubusercontent.com/helm/helm/974a6030c8514591ab0b0f0c898d37f816f698f6/cmd/helm/helm_test.go 19 // https://github.com/helm/helm/blob/974a6030c8514591ab0b0f0c898d37f816f698f6/internal/test/test.go#L53 20 21 package cmd 22 23 import ( 24 "bytes" 25 "flag" 26 "os" 27 "path/filepath" 28 "strings" 29 "testing" 30 "time" 31 32 "github.com/iter8-tools/iter8/base" 33 "github.com/iter8-tools/iter8/base/log" 34 shellwords "github.com/mattn/go-shellwords" 35 "github.com/pkg/errors" 36 "github.com/sirupsen/logrus" 37 "github.com/spf13/cobra" 38 "helm.sh/helm/v3/pkg/storage" 39 "helm.sh/helm/v3/pkg/storage/driver" 40 ) 41 42 func testTimestamper() time.Time { return time.Unix(242085845, 0).UTC() } 43 44 type testFormatter struct { 45 logrus.Formatter 46 } 47 48 func (u testFormatter) Format(e *logrus.Entry) ([]byte, error) { 49 e.Time = testTimestamper() 50 return u.Formatter.Format(e) 51 } 52 53 func runTestActionCmd(t *testing.T, tests []cmdTestCase) { 54 // fixed time 55 log.Logger.SetFormatter(testFormatter{log.Logger.Formatter}) 56 t.Helper() 57 t.Cleanup(resetEnv()) 58 59 for _, tt := range tests { 60 t.Run(tt.name, func(t *testing.T) { 61 store := storageFixture() 62 _, out, err := executeActionCommandC(store, tt.cmd) 63 if (err != nil) != tt.wantError { 64 t.Errorf("want error = %v, got '%v'", tt.wantError, err) 65 } 66 67 if tt.golden != "" { 68 AssertGoldenString(t, out, tt.golden) 69 } 70 }) 71 } 72 } 73 74 func storageFixture() *storage.Storage { 75 return storage.Init(driver.NewMemory()) 76 } 77 78 func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) { 79 return executeActionCommandStdinC(store, nil, cmd) 80 } 81 82 func executeActionCommandStdinC(store *storage.Storage, in *os.File, cmd string) (*cobra.Command, string, error) { 83 args, err := shellwords.Parse(cmd) 84 if err != nil { 85 return nil, "", err 86 } 87 88 buf := new(bytes.Buffer) 89 90 rootCmd.SetOut(buf) 91 rootCmd.SetErr(buf) 92 rootCmd.SetArgs(args) 93 log.Logger.Out = buf 94 95 oldStdin := os.Stdin 96 if in != nil { 97 rootCmd.SetIn(in) 98 os.Stdin = in 99 } 100 101 if mem, ok := store.Driver.(*driver.Memory); ok { 102 mem.SetNamespace(settings.Namespace()) 103 } 104 105 c, err := rootCmd.ExecuteC() 106 107 result := buf.String() 108 109 os.Stdin = oldStdin 110 111 return c, result, err 112 } 113 114 // cmdTestCase describes a test case that works with releases. 115 type cmdTestCase struct { 116 name string 117 cmd string 118 golden string 119 wantError bool 120 } 121 122 // func executeActionCommand(cmd string) (*cobra.Command, string, error) { 123 // return executeActionCommandC(storageFixture(), cmd) 124 // } 125 126 func resetEnv() func() { 127 origEnv := os.Environ() 128 return func() { 129 os.Clearenv() 130 for _, pair := range origEnv { 131 kv := strings.SplitN(pair, "=", 2) 132 _ = os.Setenv(kv[0], kv[1]) 133 } 134 logLevel = "info" 135 log.Logger.Out = os.Stderr 136 } 137 } 138 139 // the following test utils are from 140 // https://github.com/helm/helm/blob/974a6030c8514591ab0b0f0c898d37f816f698f6/internal/test/test.go#L53 141 142 // UpdateGolden writes out the golden files with the latest values, rather than failing the test. 143 var updateGolden = flag.Bool("update", false, "update golden files") 144 145 // TestingT describes a testing object compatible with the critical functions from the testing.T type 146 type TestingT interface { 147 Fatal(...interface{}) 148 Fatalf(string, ...interface{}) 149 HelperT 150 } 151 152 // HelperT describes a test with a helper function 153 type HelperT interface { 154 Helper() 155 } 156 157 // AssertGoldenBytes asserts that the give actual content matches the contents of the given filename 158 func AssertGoldenBytes(t TestingT, actual []byte, filename string) { 159 t.Helper() 160 161 if err := compare(actual, aPath(filename)); err != nil { 162 t.Fatalf("%v", err) 163 } 164 } 165 166 // AssertGoldenString asserts that the given string matches the contents of the given file. 167 func AssertGoldenString(t TestingT, actual, filename string) { 168 t.Helper() 169 170 if err := compare([]byte(actual), aPath(filename)); err != nil { 171 t.Fatalf("%v", err) 172 } 173 } 174 175 // AssertGoldenFile asserts that the content of the actual file matches the contents of the expected file 176 func AssertGoldenFile(t TestingT, actualFileName string, expectedFilename string) { 177 t.Helper() 178 179 afn := filepath.Clean(actualFileName) 180 actual, err := os.ReadFile(afn) 181 if err != nil { 182 t.Fatalf("%v", err) 183 } 184 AssertGoldenBytes(t, actual, expectedFilename) 185 } 186 187 func aPath(filename string) string { 188 if filepath.IsAbs(filename) { 189 return filename 190 } 191 return base.CompletePath("../", "testdata/"+filename) 192 } 193 194 func compare(actual []byte, filename string) error { 195 actual = normalize(actual) 196 if err := update(filename, actual); err != nil { 197 return err 198 } 199 200 fn := filepath.Clean(filename) 201 expected, err := os.ReadFile(fn) 202 if err != nil { 203 return errors.Wrapf(err, "unable to read testdata %s", filename) 204 } 205 expected = normalize(expected) 206 if !bytes.Equal(expected, actual) { 207 return errors.Errorf("does not match golden file %s WANT: '%s'\nGOT: '%s'", filename, expected, actual) 208 } 209 return nil 210 } 211 212 func update(filename string, in []byte) error { 213 if !*updateGolden { 214 return nil 215 } 216 return os.WriteFile(filename, normalize(in), 0600) 217 } 218 219 func normalize(in []byte) []byte { 220 return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1) 221 }