|
1 | 1 | package exec |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "path/filepath" |
4 | 5 | "testing" |
5 | 6 |
|
| 7 | + "github.com/spf13/cobra" |
6 | 8 | "github.com/stretchr/testify/assert" |
| 9 | + "github.com/stretchr/testify/require" |
7 | 10 |
|
8 | 11 | cfg "github.com/cloudposse/atmos/pkg/config" |
9 | 12 | "github.com/cloudposse/atmos/pkg/schema" |
@@ -335,3 +338,188 @@ func TestCheckAndGenerateWorkflowStepNames(t *testing.T) { |
335 | 338 | }) |
336 | 339 | } |
337 | 340 | } |
| 341 | + |
| 342 | +// TestExecuteWorkflowCmd tests the ExecuteWorkflowCmd function. |
| 343 | +func TestExecuteWorkflowCmd(t *testing.T) { |
| 344 | + // Create a helper to set up cobra command with workflow flags. |
| 345 | + createWorkflowCmd := func() *cobra.Command { |
| 346 | + cmd := &cobra.Command{ |
| 347 | + Use: "workflow", |
| 348 | + } |
| 349 | + // Workflow-specific flags. |
| 350 | + cmd.PersistentFlags().StringP("file", "f", "", "Workflow file") |
| 351 | + cmd.PersistentFlags().Bool("dry-run", false, "Dry run") |
| 352 | + cmd.PersistentFlags().StringP("stack", "s", "", "Stack") |
| 353 | + cmd.PersistentFlags().String("from-step", "", "From step") |
| 354 | + cmd.PersistentFlags().String("identity", "", "Identity") |
| 355 | + |
| 356 | + // Flags expected by ProcessCommandLineArgs. |
| 357 | + cmd.PersistentFlags().String("base-path", "", "Base path") |
| 358 | + cmd.PersistentFlags().StringSlice("config", []string{}, "Config files") |
| 359 | + cmd.PersistentFlags().StringSlice("config-path", []string{}, "Config paths") |
| 360 | + |
| 361 | + return cmd |
| 362 | + } |
| 363 | + |
| 364 | + t.Run("successful workflow execution", func(t *testing.T) { |
| 365 | + // Note: These tests are run from the module root, so use paths relative to module root. |
| 366 | + stacksPath := "../../tests/fixtures/scenarios/workflows" |
| 367 | + |
| 368 | + t.Setenv("ATMOS_CLI_CONFIG_PATH", stacksPath) |
| 369 | + t.Setenv("ATMOS_BASE_PATH", stacksPath) |
| 370 | + |
| 371 | + cmd := createWorkflowCmd() |
| 372 | + err := cmd.ParseFlags([]string{"--file", "test.yaml"}) |
| 373 | + require.NoError(t, err) |
| 374 | + |
| 375 | + // Execute with workflow name. |
| 376 | + args := []string{"shell-pass"} |
| 377 | + err = ExecuteWorkflowCmd(cmd, args) |
| 378 | + |
| 379 | + // Should succeed. |
| 380 | + assert.NoError(t, err) |
| 381 | + }) |
| 382 | + |
| 383 | + t.Run("missing file flag", func(t *testing.T) { |
| 384 | + stacksPath := "../../tests/fixtures/scenarios/workflows" |
| 385 | + |
| 386 | + t.Setenv("ATMOS_CLI_CONFIG_PATH", stacksPath) |
| 387 | + t.Setenv("ATMOS_BASE_PATH", stacksPath) |
| 388 | + |
| 389 | + cmd := createWorkflowCmd() |
| 390 | + // Don't set --file flag. |
| 391 | + |
| 392 | + args := []string{"shell-pass"} |
| 393 | + err := ExecuteWorkflowCmd(cmd, args) |
| 394 | + |
| 395 | + // Should error with missing file flag message. |
| 396 | + assert.Error(t, err) |
| 397 | + assert.Contains(t, err.Error(), "'--file' flag is required") |
| 398 | + }) |
| 399 | + |
| 400 | + t.Run("file not found", func(t *testing.T) { |
| 401 | + // ExecuteWorkflowCmd calls CheckErrorPrintAndExit which exits the process. |
| 402 | + // We can't test this directly without mocking. Skip for now or refactor. |
| 403 | + // This test would require dependency injection to avoid the exit. |
| 404 | + t.Skip("Requires refactoring to avoid CheckErrorPrintAndExit") |
| 405 | + }) |
| 406 | + |
| 407 | + t.Run("absolute file path", func(t *testing.T) { |
| 408 | + stacksPath := "../../tests/fixtures/scenarios/workflows" |
| 409 | + |
| 410 | + t.Setenv("ATMOS_CLI_CONFIG_PATH", stacksPath) |
| 411 | + t.Setenv("ATMOS_BASE_PATH", stacksPath) |
| 412 | + |
| 413 | + // Use absolute path to workflow file. |
| 414 | + absPath, err := filepath.Abs("../../tests/fixtures/scenarios/workflows/stacks/workflows/test.yaml") |
| 415 | + require.NoError(t, err) |
| 416 | + |
| 417 | + cmd := createWorkflowCmd() |
| 418 | + err = cmd.ParseFlags([]string{"--file", absPath}) |
| 419 | + require.NoError(t, err) |
| 420 | + |
| 421 | + args := []string{"shell-pass"} |
| 422 | + err = ExecuteWorkflowCmd(cmd, args) |
| 423 | + |
| 424 | + assert.NoError(t, err) |
| 425 | + }) |
| 426 | + |
| 427 | + t.Run("file without extension", func(t *testing.T) { |
| 428 | + stacksPath := "../../tests/fixtures/scenarios/workflows" |
| 429 | + |
| 430 | + t.Setenv("ATMOS_CLI_CONFIG_PATH", stacksPath) |
| 431 | + t.Setenv("ATMOS_BASE_PATH", stacksPath) |
| 432 | + |
| 433 | + cmd := createWorkflowCmd() |
| 434 | + // Specify file without .yaml extension - should auto-add it. |
| 435 | + err := cmd.ParseFlags([]string{"--file", "test"}) |
| 436 | + require.NoError(t, err) |
| 437 | + |
| 438 | + args := []string{"shell-pass"} |
| 439 | + err = ExecuteWorkflowCmd(cmd, args) |
| 440 | + |
| 441 | + assert.NoError(t, err) |
| 442 | + }) |
| 443 | + |
| 444 | + t.Run("dry-run flag", func(t *testing.T) { |
| 445 | + stacksPath := "../../tests/fixtures/scenarios/workflows" |
| 446 | + |
| 447 | + t.Setenv("ATMOS_CLI_CONFIG_PATH", stacksPath) |
| 448 | + t.Setenv("ATMOS_BASE_PATH", stacksPath) |
| 449 | + |
| 450 | + cmd := createWorkflowCmd() |
| 451 | + err := cmd.ParseFlags([]string{"--file", "test.yaml", "--dry-run"}) |
| 452 | + require.NoError(t, err) |
| 453 | + |
| 454 | + args := []string{"shell-pass"} |
| 455 | + err = ExecuteWorkflowCmd(cmd, args) |
| 456 | + |
| 457 | + // Dry run should not error. |
| 458 | + assert.NoError(t, err) |
| 459 | + }) |
| 460 | + |
| 461 | + t.Run("stack override", func(t *testing.T) { |
| 462 | + stacksPath := "../../tests/fixtures/scenarios/workflows" |
| 463 | + |
| 464 | + t.Setenv("ATMOS_CLI_CONFIG_PATH", stacksPath) |
| 465 | + t.Setenv("ATMOS_BASE_PATH", stacksPath) |
| 466 | + |
| 467 | + cmd := createWorkflowCmd() |
| 468 | + err := cmd.ParseFlags([]string{"--file", "test.yaml", "--stack", "dev"}) |
| 469 | + require.NoError(t, err) |
| 470 | + |
| 471 | + // Use a workflow. |
| 472 | + args := []string{"shell-pass"} |
| 473 | + err = ExecuteWorkflowCmd(cmd, args) |
| 474 | + |
| 475 | + // Should succeed with stack override. |
| 476 | + assert.NoError(t, err) |
| 477 | + }) |
| 478 | + |
| 479 | + t.Run("from-step flag", func(t *testing.T) { |
| 480 | + stacksPath := "../../tests/fixtures/scenarios/workflows" |
| 481 | + |
| 482 | + t.Setenv("ATMOS_CLI_CONFIG_PATH", stacksPath) |
| 483 | + t.Setenv("ATMOS_BASE_PATH", stacksPath) |
| 484 | + |
| 485 | + cmd := createWorkflowCmd() |
| 486 | + err := cmd.ParseFlags([]string{"--file", "test.yaml", "--from-step", "step1"}) |
| 487 | + require.NoError(t, err) |
| 488 | + |
| 489 | + args := []string{"shell-pass"} |
| 490 | + err = ExecuteWorkflowCmd(cmd, args) |
| 491 | + |
| 492 | + // Should start from step1 (the only step in shell-pass workflow). |
| 493 | + assert.NoError(t, err) |
| 494 | + }) |
| 495 | + |
| 496 | + t.Run("identity flag", func(t *testing.T) { |
| 497 | + stacksPath := "../../tests/fixtures/scenarios/workflows" |
| 498 | + |
| 499 | + t.Setenv("ATMOS_CLI_CONFIG_PATH", stacksPath) |
| 500 | + t.Setenv("ATMOS_BASE_PATH", stacksPath) |
| 501 | + |
| 502 | + cmd := createWorkflowCmd() |
| 503 | + err := cmd.ParseFlags([]string{"--file", "test.yaml", "--identity", "test-identity"}) |
| 504 | + require.NoError(t, err) |
| 505 | + |
| 506 | + args := []string{"shell-pass"} |
| 507 | + err = ExecuteWorkflowCmd(cmd, args) |
| 508 | + |
| 509 | + // Should error because identity doesn't exist (but flag was passed through correctly). |
| 510 | + assert.Error(t, err) |
| 511 | + assert.Contains(t, err.Error(), "test-identity") |
| 512 | + }) |
| 513 | + |
| 514 | + t.Run("invalid workflow manifest - no workflows key", func(t *testing.T) { |
| 515 | + // This will call CheckErrorPrintAndExit which exits the process. |
| 516 | + // Skip for now without dependency injection. |
| 517 | + t.Skip("Requires refactoring to avoid CheckErrorPrintAndExit") |
| 518 | + }) |
| 519 | + |
| 520 | + t.Run("workflow name not found in manifest", func(t *testing.T) { |
| 521 | + // This will call CheckErrorPrintAndExit which exits the process. |
| 522 | + // Skip for now without dependency injection. |
| 523 | + t.Skip("Requires refactoring to avoid CheckErrorPrintAndExit") |
| 524 | + }) |
| 525 | +} |
0 commit comments