github.com/codefresh-io/kcfi@v0.0.0-20230301195427-c1578715cc46/cmd/kcfi/main_test.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  package main
    18  
    19  import (
    20  	"bytes"
    21  	"io/ioutil"
    22  	"os"
    23  	"os/exec"
    24  	"runtime"
    25  	"strings"
    26  	"testing"
    27  
    28  	shellwords "github.com/mattn/go-shellwords"
    29  	"github.com/spf13/cobra"
    30  
    31  	"github.com/codefresh-io/kcfi/pkg/helm-internal/test"
    32  	"helm.sh/helm/v3/pkg/action"
    33  	"helm.sh/helm/v3/pkg/chartutil"
    34  	"helm.sh/helm/v3/pkg/cli"
    35  	kubefake "helm.sh/helm/v3/pkg/kube/fake"
    36  	"helm.sh/helm/v3/pkg/release"
    37  	"helm.sh/helm/v3/pkg/storage"
    38  	"helm.sh/helm/v3/pkg/storage/driver"
    39  	"helm.sh/helm/v3/pkg/time"
    40  )
    41  
    42  func testTimestamper() time.Time { return time.Unix(242085845, 0).UTC() }
    43  
    44  func init() {
    45  	action.Timestamper = testTimestamper
    46  }
    47  
    48  func runTestCmd(t *testing.T, tests []cmdTestCase) {
    49  	t.Helper()
    50  	for _, tt := range tests {
    51  		for i := 0; i <= tt.repeat; i++ {
    52  			t.Run(tt.name, func(t *testing.T) {
    53  				defer resetEnv()()
    54  
    55  				storage := storageFixture()
    56  				for _, rel := range tt.rels {
    57  					if err := storage.Create(rel); err != nil {
    58  						t.Fatal(err)
    59  					}
    60  				}
    61  				t.Logf("running cmd (attempt %d): %s", i+1, tt.cmd)
    62  				_, out, err := executeActionCommandC(storage, tt.cmd)
    63  				if (err != nil) != tt.wantError {
    64  					t.Errorf("expected error, got '%v'", err)
    65  				}
    66  				if tt.golden != "" {
    67  					test.AssertGoldenString(t, out, tt.golden)
    68  				}
    69  			})
    70  		}
    71  	}
    72  }
    73  
    74  func runTestActionCmd(t *testing.T, tests []cmdTestCase) {
    75  	t.Helper()
    76  	for _, tt := range tests {
    77  		t.Run(tt.name, func(t *testing.T) {
    78  			defer resetEnv()()
    79  
    80  			store := storageFixture()
    81  			for _, rel := range tt.rels {
    82  				store.Create(rel)
    83  			}
    84  			_, out, err := executeActionCommandC(store, tt.cmd)
    85  			if (err != nil) != tt.wantError {
    86  				t.Errorf("expected error, got '%v'", err)
    87  			}
    88  			if tt.golden != "" {
    89  				test.AssertGoldenString(t, out, tt.golden)
    90  			}
    91  		})
    92  	}
    93  }
    94  
    95  func storageFixture() *storage.Storage {
    96  	return storage.Init(driver.NewMemory())
    97  }
    98  
    99  func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) {
   100  	return executeActionCommandStdinC(store, nil, cmd)
   101  }
   102  
   103  func executeActionCommandStdinC(store *storage.Storage, in *os.File, cmd string) (*cobra.Command, string, error) {
   104  	args, err := shellwords.Parse(cmd)
   105  	if err != nil {
   106  		return nil, "", err
   107  	}
   108  
   109  	buf := new(bytes.Buffer)
   110  
   111  	actionConfig := &action.Configuration{
   112  		Releases:     store,
   113  		KubeClient:   &kubefake.PrintingKubeClient{Out: ioutil.Discard},
   114  		Capabilities: chartutil.DefaultCapabilities,
   115  		Log:          func(format string, v ...interface{}) {},
   116  	}
   117  
   118  	root := newRootCmd(actionConfig, buf, args)
   119  	root.SetOut(buf)
   120  	root.SetErr(buf)
   121  	root.SetArgs(args)
   122  
   123  	oldStdin := os.Stdin
   124  	if in != nil {
   125  		root.SetIn(in)
   126  		os.Stdin = in
   127  	}
   128  
   129  	if mem, ok := store.Driver.(*driver.Memory); ok {
   130  		mem.SetNamespace(settings.Namespace())
   131  	}
   132  	c, err := root.ExecuteC()
   133  
   134  	result := buf.String()
   135  
   136  	os.Stdin = oldStdin
   137  
   138  	return c, result, err
   139  }
   140  
   141  // cmdTestCase describes a test case that works with releases.
   142  type cmdTestCase struct {
   143  	name      string
   144  	cmd       string
   145  	golden    string
   146  	wantError bool
   147  	// Rels are the available releases at the start of the test.
   148  	rels []*release.Release
   149  	// Number of repeats (in case a feature was previously flaky and the test checks
   150  	// it's now stably producing identical results). 0 means test is run exactly once.
   151  	repeat int
   152  }
   153  
   154  func executeActionCommand(cmd string) (*cobra.Command, string, error) {
   155  	return executeActionCommandC(storageFixture(), cmd)
   156  }
   157  
   158  func resetEnv() func() {
   159  	origEnv := os.Environ()
   160  	return func() {
   161  		os.Clearenv()
   162  		for _, pair := range origEnv {
   163  			kv := strings.SplitN(pair, "=", 2)
   164  			os.Setenv(kv[0], kv[1])
   165  		}
   166  		settings = cli.New()
   167  	}
   168  }
   169  
   170  func testChdir(t *testing.T, dir string) func() {
   171  	t.Helper()
   172  	old, err := os.Getwd()
   173  	if err != nil {
   174  		t.Fatal(err)
   175  	}
   176  	if err := os.Chdir(dir); err != nil {
   177  		t.Fatal(err)
   178  	}
   179  	return func() { os.Chdir(old) }
   180  }
   181  
   182  func TestPluginExitCode(t *testing.T) {
   183  	if os.Getenv("RUN_MAIN_FOR_TESTING") == "1" {
   184  		os.Args = []string{"helm", "exitwith", "2"}
   185  
   186  		// We DO call helm's main() here. So this looks like a normal `helm` process.
   187  		main()
   188  
   189  		// As main calls os.Exit, we never reach this line.
   190  		// But the test called this block of code catches and verifies the exit code.
   191  		return
   192  	}
   193  
   194  	// Currently, plugins assume a Linux subsystem. Skip the execution
   195  	// tests until this is fixed
   196  	if runtime.GOOS != "windows" {
   197  		// Do a second run of this specific test(TestPluginExitCode) with RUN_MAIN_FOR_TESTING=1 set,
   198  		// So that the second run is able to run main() and this first run can verify the exit status returned by that.
   199  		//
   200  		// This technique originates from https://talks.golang.org/2014/testing.slide#23.
   201  		cmd := exec.Command(os.Args[0], "-test.run=TestPluginExitCode")
   202  		cmd.Env = append(
   203  			os.Environ(),
   204  			"RUN_MAIN_FOR_TESTING=1",
   205  			// See pkg/cli/environment.go for which envvars can be used for configuring these passes
   206  			// and also see plugin_test.go for how a plugin env can be set up.
   207  			// We just does the same setup as plugin_test.go via envvars
   208  			"HELM_PLUGINS=testdata/helmhome/helm/plugins",
   209  			"HELM_REPOSITORY_CONFIG=testdata/helmhome/helm/repositories.yaml",
   210  			"HELM_REPOSITORY_CACHE=testdata/helmhome/helm/repository",
   211  		)
   212  		stdout := &bytes.Buffer{}
   213  		stderr := &bytes.Buffer{}
   214  		cmd.Stdout = stdout
   215  		cmd.Stderr = stderr
   216  		err := cmd.Run()
   217  		exiterr, ok := err.(*exec.ExitError)
   218  
   219  		if !ok {
   220  			t.Fatalf("Unexpected error returned by os.Exit: %T", err)
   221  		}
   222  
   223  		if stdout.String() != "" {
   224  			t.Errorf("Expected no write to stdout: Got %q", stdout.String())
   225  		}
   226  
   227  		expectedStderr := "Error: plugin \"exitwith\" exited with error\n"
   228  		if stderr.String() != expectedStderr {
   229  			t.Errorf("Expected %q written to stderr: Got %q", expectedStderr, stderr.String())
   230  		}
   231  
   232  		if exiterr.ExitCode() != 2 {
   233  			t.Errorf("Expected exit code 2: Got %d", exiterr.ExitCode())
   234  		}
   235  	}
   236  }