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  }