get.porter.sh/porter@v1.3.0/cmd/porter/main_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"strings"
     7  	"testing"
     8  
     9  	"get.porter.sh/porter/pkg"
    10  	"get.porter.sh/porter/pkg/config"
    11  	"get.porter.sh/porter/pkg/experimental"
    12  	"get.porter.sh/porter/pkg/porter"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestCommandWiring(t *testing.T) {
    18  	testcases := []string{
    19  		"build",
    20  		"create",
    21  		"install",
    22  		"uninstall",
    23  		"run",
    24  		"schema",
    25  		"bundles",
    26  		"bundle create",
    27  		"bundle build",
    28  		"installation install",
    29  		"installation uninstall",
    30  		"mixins",
    31  		"mixins list",
    32  		"plugins list",
    33  		"storage",
    34  		"storage migrate",
    35  		"version",
    36  	}
    37  
    38  	for _, tc := range testcases {
    39  		t.Run(tc, func(t *testing.T) {
    40  			osargs := strings.Split(tc, " ")
    41  
    42  			rootCmd := buildRootCommand()
    43  			cmd, _, err := rootCmd.Find(osargs)
    44  			assert.NoError(t, err)
    45  			assert.Equal(t, osargs[len(osargs)-1], cmd.Name())
    46  		})
    47  	}
    48  }
    49  
    50  func TestHelp(t *testing.T) {
    51  	t.Run("no args", func(t *testing.T) {
    52  		var output bytes.Buffer
    53  		rootCmd := buildRootCommand()
    54  		rootCmd.SetArgs([]string{})
    55  		rootCmd.SetOut(&output)
    56  
    57  		err := rootCmd.Execute()
    58  		require.NoError(t, err)
    59  		assert.Contains(t, output.String(), "Usage")
    60  	})
    61  
    62  	t.Run("help", func(t *testing.T) {
    63  		var output bytes.Buffer
    64  		rootCmd := buildRootCommand()
    65  		rootCmd.SetArgs([]string{"help"})
    66  		rootCmd.SetOut(&output)
    67  
    68  		err := rootCmd.Execute()
    69  		require.NoError(t, err)
    70  		assert.Contains(t, output.String(), "Usage")
    71  	})
    72  
    73  	t.Run("--help", func(t *testing.T) {
    74  		var output bytes.Buffer
    75  		rootCmd := buildRootCommand()
    76  		rootCmd.SetArgs([]string{"--help"})
    77  		rootCmd.SetOut(&output)
    78  
    79  		err := rootCmd.Execute()
    80  		require.NoError(t, err)
    81  		assert.Contains(t, output.String(), "Usage")
    82  	})
    83  }
    84  
    85  // Validate that porter is correctly binding experimental which is a flag on some commands AND is persisted on config.Data
    86  // This is a regression test to ensure that we are applying our configuration from viper and cobra in the proper order
    87  // such that flags defined on config.Data are persisted.
    88  // I'm testing both experimental and verbosity because honestly, I've seen both break enough that I'd rather have excessive test than see it break again.
    89  func TestExperimentalFlags(t *testing.T) {
    90  	// do not run in parallel
    91  	expEnvVar := "PORTER_EXPERIMENTAL"
    92  	os.Unsetenv(expEnvVar)
    93  
    94  	t.Run("default", func(t *testing.T) {
    95  		p := porter.NewTestPorter(t)
    96  		defer p.Close()
    97  
    98  		cmd := buildRootCommandFrom(p.Porter)
    99  		cmd.SetArgs([]string{"install"})
   100  		err := cmd.Execute()
   101  		require.Error(t, err)
   102  
   103  		assert.False(t, p.Config.IsFeatureEnabled(experimental.FlagNoopFeature))
   104  	})
   105  
   106  	t.Run("flag set", func(t *testing.T) {
   107  		p := porter.NewTestPorter(t)
   108  		defer p.Close()
   109  
   110  		cmd := buildRootCommandFrom(p.Porter)
   111  		cmd.SetArgs([]string{"install", "--experimental", experimental.NoopFeature})
   112  		err := cmd.Execute()
   113  		require.Error(t, err)
   114  
   115  		assert.True(t, p.Config.IsFeatureEnabled(experimental.FlagNoopFeature))
   116  	})
   117  
   118  	t.Run("env set", func(t *testing.T) {
   119  		os.Setenv(expEnvVar, experimental.NoopFeature)
   120  		defer os.Unsetenv(expEnvVar)
   121  
   122  		p := porter.NewTestPorter(t)
   123  		defer p.Close()
   124  
   125  		cmd := buildRootCommandFrom(p.Porter)
   126  		cmd.SetArgs([]string{"install"})
   127  		err := cmd.Execute()
   128  		require.Error(t, err)
   129  
   130  		assert.True(t, p.Config.IsFeatureEnabled(experimental.FlagNoopFeature))
   131  	})
   132  
   133  	t.Run("cfg set", func(t *testing.T) {
   134  		p := porter.NewTestPorter(t)
   135  		defer p.Close()
   136  
   137  		cfg := []byte(`experimental: [no-op]`)
   138  		require.NoError(t, p.FileSystem.WriteFile("/home/myuser/.porter/config.yaml", cfg, pkg.FileModeWritable))
   139  		cmd := buildRootCommandFrom(p.Porter)
   140  		cmd.SetArgs([]string{"install"})
   141  		err := cmd.Execute()
   142  		require.Error(t, err)
   143  
   144  		assert.True(t, p.Config.IsFeatureEnabled(experimental.FlagNoopFeature))
   145  	})
   146  
   147  	t.Run("flag set, cfg set", func(t *testing.T) {
   148  		p := porter.NewTestPorter(t)
   149  		defer p.Close()
   150  
   151  		cfg := []byte(`experimental: []`)
   152  		require.NoError(t, p.FileSystem.WriteFile("/home/myuser/.porter/config.yaml", cfg, pkg.FileModeWritable))
   153  		cmd := buildRootCommandFrom(p.Porter)
   154  		cmd.SetArgs([]string{"install", "--experimental", "no-op"})
   155  		err := cmd.Execute()
   156  		require.Error(t, err)
   157  
   158  		assert.True(t, p.Config.IsFeatureEnabled(experimental.FlagNoopFeature))
   159  	})
   160  
   161  	t.Run("flag set, env set", func(t *testing.T) {
   162  		os.Setenv(expEnvVar, "")
   163  		defer os.Unsetenv(expEnvVar)
   164  
   165  		p := porter.NewTestPorter(t)
   166  		defer p.Close()
   167  
   168  		cmd := buildRootCommandFrom(p.Porter)
   169  		cmd.SetArgs([]string{"install", "--experimental", "no-op"})
   170  		err := cmd.Execute()
   171  		require.Error(t, err)
   172  
   173  		assert.True(t, p.Config.IsFeatureEnabled(experimental.FlagNoopFeature))
   174  	})
   175  
   176  	t.Run("env set, cfg set", func(t *testing.T) {
   177  		os.Setenv(expEnvVar, "")
   178  		defer os.Unsetenv(expEnvVar)
   179  
   180  		p := porter.NewTestPorter(t)
   181  		defer p.Close()
   182  
   183  		cfg := []byte(`experimental: [no-op]`)
   184  		require.NoError(t, p.FileSystem.WriteFile("/home/myuser/.porter/config.yaml", cfg, pkg.FileModeWritable))
   185  		cmd := buildRootCommandFrom(p.Porter)
   186  		cmd.SetArgs([]string{"install"})
   187  		err := cmd.Execute()
   188  		require.Error(t, err)
   189  
   190  		assert.False(t, p.Config.IsFeatureEnabled(experimental.FlagNoopFeature))
   191  	})
   192  }
   193  
   194  // Validate that porter is correctly binding verbosity which is a flag on all commands AND is persisted on config.Data
   195  // This is a regression test to ensure that we are applying our configuration from viper and cobra in the proper order
   196  // such that flags defined on config.Data are persisted.
   197  // I'm testing both experimental and verbosity because honestly, I've seen both break enough that I'd rather have excessive test than see it break again.
   198  func TestVerbosity(t *testing.T) {
   199  	// do not run in parallel
   200  	envVar := "PORTER_VERBOSITY"
   201  	os.Unsetenv(envVar)
   202  
   203  	t.Run("default", func(t *testing.T) {
   204  		p := porter.NewTestPorter(t)
   205  		defer p.Close()
   206  
   207  		cmd := buildRootCommandFrom(p.Porter)
   208  		cmd.SetArgs([]string{"install"})
   209  		err := cmd.Execute()
   210  		require.Error(t, err)
   211  
   212  		assert.Equal(t, config.LogLevelInfo, p.Config.GetVerbosity())
   213  	})
   214  
   215  	t.Run("flag set", func(t *testing.T) {
   216  		p := porter.NewTestPorter(t)
   217  		defer p.Close()
   218  
   219  		cmd := buildRootCommandFrom(p.Porter)
   220  		cmd.SetArgs([]string{"install", "--verbosity=debug"})
   221  		err := cmd.Execute()
   222  		require.Error(t, err)
   223  
   224  		assert.Equal(t, config.LogLevelDebug, p.Config.GetVerbosity())
   225  	})
   226  
   227  	t.Run("env set", func(t *testing.T) {
   228  		os.Setenv(envVar, "error")
   229  		defer os.Unsetenv(envVar)
   230  
   231  		p := porter.NewTestPorter(t)
   232  		defer p.Close()
   233  
   234  		cmd := buildRootCommandFrom(p.Porter)
   235  		cmd.SetArgs([]string{"install"})
   236  		err := cmd.Execute()
   237  		require.Error(t, err)
   238  
   239  		assert.Equal(t, config.LogLevelError, p.Config.GetVerbosity())
   240  	})
   241  
   242  	t.Run("cfg set", func(t *testing.T) {
   243  		p := porter.NewTestPorter(t)
   244  		defer p.Close()
   245  
   246  		cfg := []byte(`verbosity: warning`)
   247  		require.NoError(t, p.FileSystem.WriteFile("/home/myuser/.porter/config.yaml", cfg, pkg.FileModeWritable))
   248  		cmd := buildRootCommandFrom(p.Porter)
   249  		cmd.SetArgs([]string{"install"})
   250  		err := cmd.Execute()
   251  		require.Error(t, err)
   252  
   253  		assert.Equal(t, config.LogLevelWarn, p.Config.GetVerbosity())
   254  	})
   255  
   256  	t.Run("flag set, cfg set", func(t *testing.T) {
   257  		p := porter.NewTestPorter(t)
   258  		defer p.Close()
   259  
   260  		cfg := []byte(`verbosity: debug`)
   261  		require.NoError(t, p.FileSystem.WriteFile("/home/myuser/.porter/config.yaml", cfg, pkg.FileModeWritable))
   262  		cmd := buildRootCommandFrom(p.Porter)
   263  		cmd.SetArgs([]string{"install", "--verbosity", "warn"})
   264  		err := cmd.Execute()
   265  		require.Error(t, err)
   266  
   267  		assert.Equal(t, config.LogLevelWarn, p.Config.GetVerbosity())
   268  	})
   269  
   270  	t.Run("flag set, env set", func(t *testing.T) {
   271  		os.Setenv(envVar, "warn")
   272  		defer os.Unsetenv(envVar)
   273  
   274  		p := porter.NewTestPorter(t)
   275  		defer p.Close()
   276  
   277  		cmd := buildRootCommandFrom(p.Porter)
   278  		cmd.SetArgs([]string{"install", "--verbosity=debug"})
   279  		err := cmd.Execute()
   280  		require.Error(t, err)
   281  
   282  		assert.Equal(t, config.LogLevelDebug, p.Config.GetVerbosity())
   283  	})
   284  
   285  	t.Run("env set, cfg set", func(t *testing.T) {
   286  		os.Setenv(envVar, "warn")
   287  		defer os.Unsetenv(envVar)
   288  
   289  		p := porter.NewTestPorter(t)
   290  		defer p.Close()
   291  
   292  		cfg := []byte(`verbosity: debug`)
   293  		require.NoError(t, p.FileSystem.WriteFile("/home/myuser/.porter/config.yaml", cfg, pkg.FileModeWritable))
   294  		cmd := buildRootCommandFrom(p.Porter)
   295  		cmd.SetArgs([]string{"install"})
   296  		err := cmd.Execute()
   297  		require.Error(t, err)
   298  
   299  		assert.Equal(t, config.LogLevelWarn, p.Config.GetVerbosity())
   300  	})
   301  }
   302  
   303  // Validate that porter is correctly binding porter explain --output which is a flag that is NOT bound to config.Data
   304  // This is a regression test to ensure that we are applying our configuration from viper and cobra in the proper order
   305  // such that flags defined on a separate data structure from config.Data are persisted.
   306  func TestExplainOutput(t *testing.T) {
   307  	// do not run in parallel
   308  	envVar := "PORTER_OUTPUT"
   309  	os.Unsetenv(envVar)
   310  
   311  	const ref = "ghcr.io/getporter/examples/porter-hello:v0.2.0"
   312  
   313  	assertPlainOutput := func(t *testing.T, output string) {
   314  		t.Helper()
   315  		assert.Contains(t, "Name: examples/porter-hello", output, "explain should have output plain text")
   316  	}
   317  
   318  	assertJsonOutput := func(t *testing.T, output string) {
   319  		t.Helper()
   320  		assert.Contains(t, `"name": "examples/porter-hello",`, output, "explain should have output JSON")
   321  	}
   322  
   323  	assertYamlOutput := func(t *testing.T, output string) {
   324  		t.Helper()
   325  		assert.Contains(t, `- name: name`, output, "explain should have output YAML")
   326  	}
   327  
   328  	t.Run("default", func(t *testing.T) {
   329  		p := porter.NewTestPorter(t)
   330  		defer p.Close()
   331  
   332  		cmd := buildRootCommandFrom(p.Porter)
   333  		cmd.SetArgs([]string{"explain", ref})
   334  		require.NoError(t, cmd.Execute(), "explain failed")
   335  
   336  		assertPlainOutput(t, p.TestConfig.TestContext.GetOutput())
   337  	})
   338  
   339  	t.Run("flag set", func(t *testing.T) {
   340  		p := porter.NewTestPorter(t)
   341  		defer p.Close()
   342  
   343  		cmd := buildRootCommandFrom(p.Porter)
   344  		cmd.SetArgs([]string{"explain", ref, "--output=json"})
   345  		require.NoError(t, cmd.Execute(), "explain failed")
   346  
   347  		assertJsonOutput(t, p.TestConfig.TestContext.GetOutput())
   348  	})
   349  
   350  	t.Run("env set", func(t *testing.T) {
   351  		os.Setenv(envVar, "json")
   352  		defer os.Unsetenv(envVar)
   353  
   354  		p := porter.NewTestPorter(t)
   355  		defer p.Close()
   356  
   357  		cmd := buildRootCommandFrom(p.Porter)
   358  		cmd.SetArgs([]string{"explain", ref})
   359  		require.NoError(t, cmd.Execute(), "explain failed")
   360  
   361  		assertJsonOutput(t, p.TestConfig.TestContext.GetOutput())
   362  	})
   363  
   364  	t.Run("cfg set", func(t *testing.T) {
   365  		p := porter.NewTestPorter(t)
   366  		defer p.Close()
   367  
   368  		cfg := []byte(`output: json`)
   369  		require.NoError(t, p.FileSystem.WriteFile("/home/myuser/.porter/config.yaml", cfg, pkg.FileModeWritable))
   370  		cmd := buildRootCommandFrom(p.Porter)
   371  		cmd.SetArgs([]string{"explain", ref})
   372  		require.NoError(t, cmd.Execute(), "explain failed")
   373  
   374  		assertJsonOutput(t, p.TestConfig.TestContext.GetOutput())
   375  	})
   376  
   377  	t.Run("flag set, cfg set", func(t *testing.T) {
   378  		p := porter.NewTestPorter(t)
   379  		defer p.Close()
   380  
   381  		cfg := []byte(`output: json`)
   382  		require.NoError(t, p.FileSystem.WriteFile("/home/myuser/.porter/config.yaml", cfg, pkg.FileModeWritable))
   383  		cmd := buildRootCommandFrom(p.Porter)
   384  		cmd.SetArgs([]string{"explain", ref, "--output=yaml"})
   385  		require.NoError(t, cmd.Execute(), "explain failed")
   386  
   387  		assertYamlOutput(t, p.TestConfig.TestContext.GetOutput())
   388  	})
   389  
   390  	t.Run("flag set, env set", func(t *testing.T) {
   391  		os.Setenv(envVar, "json")
   392  		defer os.Unsetenv(envVar)
   393  
   394  		p := porter.NewTestPorter(t)
   395  		defer p.Close()
   396  
   397  		cmd := buildRootCommandFrom(p.Porter)
   398  		cmd.SetArgs([]string{"explain", ref, "--output=yaml"})
   399  		require.NoError(t, cmd.Execute(), "explain failed")
   400  
   401  		assertYamlOutput(t, p.TestConfig.TestContext.GetOutput())
   402  	})
   403  
   404  	t.Run("env set, cfg set", func(t *testing.T) {
   405  		os.Setenv(envVar, "yaml")
   406  		defer os.Unsetenv(envVar)
   407  
   408  		p := porter.NewTestPorter(t)
   409  		defer p.Close()
   410  
   411  		cfg := []byte(`output: json`)
   412  		require.NoError(t, p.FileSystem.WriteFile("/home/myuser/.porter/config.yaml", cfg, pkg.FileModeWritable))
   413  		cmd := buildRootCommandFrom(p.Porter)
   414  		cmd.SetArgs([]string{"explain", ref})
   415  		require.NoError(t, cmd.Execute(), "explain failed")
   416  
   417  		assertYamlOutput(t, p.TestConfig.TestContext.GetOutput())
   418  	})
   419  }