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