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 }