Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 13 additions & 15 deletions cmd/bundle.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package cmd

import (
"context"
"fmt"

"github.com/spf13/cobra"
"github.com/windsorcli/cli/pkg/di"
"github.com/windsorcli/cli/pkg/pipelines"
"github.com/windsorcli/cli/pkg/runtime"
)

// bundleCmd represents the bundle command
Expand Down Expand Up @@ -39,19 +38,18 @@ Examples:
tag, _ := cmd.Flags().GetString("tag")
outputPath, _ := cmd.Flags().GetString("output")

// Set up the artifact pipeline
artifactPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "artifactPipeline")
if err != nil {
return fmt.Errorf("failed to set up artifact pipeline: %w", err)
}

// Create execution context with bundle mode and parameters
ctx := context.WithValue(cmd.Context(), "artifactMode", "bundle")
ctx = context.WithValue(ctx, "outputPath", outputPath)
ctx = context.WithValue(ctx, "tag", tag)

// Execute the artifact pipeline in bundle mode
if err := artifactPipeline.Execute(ctx); err != nil {
if err := runtime.NewRuntime(&runtime.Dependencies{
Injector: injector,
}).
LoadShell().
ProcessArtifacts(runtime.ArtifactOptions{
OutputPath: outputPath,
Tag: tag,
OutputFunc: func(actualOutputPath string) {
fmt.Printf("Blueprint bundled successfully: %s\n", actualOutputPath)
},
}).
Do(); err != nil {
return fmt.Errorf("failed to bundle artifacts: %w", err)
}

Expand Down
136 changes: 87 additions & 49 deletions cmd/bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"testing"

"github.com/spf13/cobra"
"github.com/windsorcli/cli/pkg/artifact"
"github.com/windsorcli/cli/pkg/blueprint"
"github.com/windsorcli/cli/pkg/config"
"github.com/windsorcli/cli/pkg/di"
"github.com/windsorcli/cli/pkg/pipelines"
"github.com/windsorcli/cli/pkg/kubernetes"
"github.com/windsorcli/cli/pkg/shell"
)

// =============================================================================
// Pipeline-based Tests
// Runtime-based Tests
// =============================================================================

func TestBundleCmdWithPipeline(t *testing.T) {
t.Run("SuccessWithPipeline", func(t *testing.T) {
func TestBundleCmdWithRuntime(t *testing.T) {
t.Run("SuccessWithRuntime", func(t *testing.T) {
// Set up temporary directory
tmpDir := t.TempDir()
originalDir, _ := os.Getwd()
Expand All @@ -26,28 +31,42 @@ func TestBundleCmdWithPipeline(t *testing.T) {
}()
os.Chdir(tmpDir)

// Create injector and mock artifact pipeline
// Create required directory structure
contextsDir := filepath.Join(tmpDir, "contexts")
templateDir := filepath.Join(contextsDir, "_template")
os.MkdirAll(templateDir, 0755)

// Create injector with required mocks
injector := di.NewInjector()
mockArtifactPipeline := pipelines.NewMockBasePipeline()
mockArtifactPipeline.ExecuteFunc = func(ctx context.Context) error {
// Verify context values
mode, ok := ctx.Value("artifactMode").(string)
if !ok || mode != "bundle" {
return fmt.Errorf("expected artifactMode 'bundle', got %v", mode)
}
outputPath, ok := ctx.Value("outputPath").(string)
if !ok || outputPath != "." {
return fmt.Errorf("expected outputPath '.', got %v", outputPath)
}
tag, ok := ctx.Value("tag").(string)
if !ok || tag != "test:v1.0.0" {
return fmt.Errorf("expected tag 'test:v1.0.0', got %v", tag)
}
return nil

// Mock shell
mockShell := shell.NewMockShell()
mockShell.GetProjectRootFunc = func() (string, error) {
return tmpDir, nil
}
injector.Register("shell", mockShell)

// Mock config handler
mockConfigHandler := config.NewMockConfigHandler()
mockConfigHandler.GetContextValuesFunc = func() (map[string]any, error) {
return map[string]any{}, nil
}
injector.Register("configHandler", mockConfigHandler)

// Mock kubernetes manager
mockK8sManager := kubernetes.NewMockKubernetesManager(injector)
injector.Register("kubernetesManager", mockK8sManager)

// Mock blueprint handler
mockBlueprintHandler := blueprint.NewMockBlueprintHandler(injector)
mockBlueprintHandler.GetLocalTemplateDataFunc = func() (map[string][]byte, error) {
return map[string][]byte{}, nil
}
injector.Register("blueprintHandler", mockBlueprintHandler)

// Register the mock pipeline
injector.Register("artifactPipeline", mockArtifactPipeline)
// Mock artifact builder
mockArtifactBuilder := artifact.NewMockArtifact()
injector.Register("artifactBuilder", mockArtifactBuilder)

// Create test command
cmd := &cobra.Command{
Expand All @@ -74,7 +93,7 @@ func TestBundleCmdWithPipeline(t *testing.T) {
}
})

t.Run("PipelineSetupError", func(t *testing.T) {
t.Run("RuntimeSetupError", func(t *testing.T) {
// Set up temporary directory
tmpDir := t.TempDir()
originalDir, _ := os.Getwd()
Expand All @@ -83,9 +102,8 @@ func TestBundleCmdWithPipeline(t *testing.T) {
}()
os.Chdir(tmpDir)

// Create injector without registering the pipeline
// This will cause WithPipeline to try to create a new one, which will fail
// because it requires the contexts/_template directory
// Create injector without required dependencies
// The runtime is now resilient and will create default dependencies
injector := di.NewInjector()

// Create test command
Expand All @@ -107,22 +125,13 @@ func TestBundleCmdWithPipeline(t *testing.T) {
// Execute command
err := cmd.Execute()

// Verify error - the pipeline setup should fail because the real artifact pipeline
// requires the contexts/_template directory which doesn't exist in the test
if err == nil {
t.Error("Expected error, got nil")
}
expectedError := "failed to bundle artifacts: bundling failed: templates directory not found: contexts"
if err.Error()[:len(expectedError)] != expectedError {
t.Errorf("Expected error to start with %q, got %q", expectedError, err.Error())
}
// Verify the path separator is correct for the platform
if !strings.Contains(err.Error(), "contexts") {
t.Errorf("Expected error to contain 'contexts', got %q", err.Error())
// Verify success - runtime is now resilient and creates default dependencies
if err != nil {
t.Errorf("Expected success, got error: %v", err)
}
})

t.Run("PipelineExecutionError", func(t *testing.T) {
t.Run("RuntimeExecutionError", func(t *testing.T) {
// Set up temporary directory
tmpDir := t.TempDir()
originalDir, _ := os.Getwd()
Expand All @@ -131,15 +140,45 @@ func TestBundleCmdWithPipeline(t *testing.T) {
}()
os.Chdir(tmpDir)

// Create injector and mock artifact pipeline that fails
// Create required directory structure
contextsDir := filepath.Join(tmpDir, "contexts")
templateDir := filepath.Join(contextsDir, "_template")
os.MkdirAll(templateDir, 0755)

// Create injector with mocks that will cause execution to fail
injector := di.NewInjector()
mockArtifactPipeline := pipelines.NewMockBasePipeline()
mockArtifactPipeline.ExecuteFunc = func(ctx context.Context) error {
return fmt.Errorf("pipeline execution failed")

// Mock shell
mockShell := shell.NewMockShell()
mockShell.GetProjectRootFunc = func() (string, error) {
return tmpDir, nil
}
injector.Register("shell", mockShell)

// Mock config handler
mockConfigHandler := config.NewMockConfigHandler()
mockConfigHandler.GetContextValuesFunc = func() (map[string]any, error) {
return map[string]any{}, nil
}
injector.Register("configHandler", mockConfigHandler)

// Mock kubernetes manager
mockK8sManager := kubernetes.NewMockKubernetesManager(injector)
injector.Register("kubernetesManager", mockK8sManager)

// Register the mock pipeline
injector.Register("artifactPipeline", mockArtifactPipeline)
// Mock blueprint handler
mockBlueprintHandler := blueprint.NewMockBlueprintHandler(injector)
mockBlueprintHandler.GetLocalTemplateDataFunc = func() (map[string][]byte, error) {
return map[string][]byte{}, nil
}
injector.Register("blueprintHandler", mockBlueprintHandler)

// Mock artifact builder that fails during bundle
mockArtifactBuilder := artifact.NewMockArtifact()
mockArtifactBuilder.WriteFunc = func(outputPath string, tag string) (string, error) {
return "", fmt.Errorf("artifact bundle failed")
}
injector.Register("artifactBuilder", mockArtifactBuilder)

// Create test command
cmd := &cobra.Command{
Expand All @@ -164,9 +203,8 @@ func TestBundleCmdWithPipeline(t *testing.T) {
if err == nil {
t.Error("Expected error, got nil")
}
expectedError := "failed to bundle artifacts: pipeline execution failed"
if err.Error() != expectedError {
t.Errorf("Expected error %q, got %q", expectedError, err.Error())
if !strings.Contains(err.Error(), "artifact bundle failed") {
t.Errorf("Expected error to contain 'artifact bundle failed', got %v", err)
}
})
}
19 changes: 19 additions & 0 deletions cmd/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"bytes"
"context"
"os"
"strings"
"testing"
Expand All @@ -13,6 +14,10 @@ func checkContains(str, substr string) bool {
}

func TestCheckCmd(t *testing.T) {
t.Cleanup(func() {
rootCmd.SetContext(context.Background())
})

setup := func(t *testing.T, withConfig bool) (*bytes.Buffer, *bytes.Buffer) {
t.Helper()

Expand Down Expand Up @@ -57,6 +62,9 @@ func TestCheckCmd(t *testing.T) {
// Given a directory with proper configuration
stdout, stderr := setup(t, true)

// Reset context for fresh test
rootCmd.SetContext(context.Background())

// When executing the command
err := Execute()

Expand All @@ -79,6 +87,9 @@ func TestCheckCmd(t *testing.T) {
// Given a directory with no configuration
_, _ = setup(t, false)

// Reset context for fresh test
rootCmd.SetContext(context.Background())

// When executing the command
err := Execute()

Expand All @@ -96,6 +107,11 @@ func TestCheckCmd(t *testing.T) {
}

func TestCheckNodeHealthCmd(t *testing.T) {
// Cleanup: reset rootCmd context after all subtests complete
t.Cleanup(func() {
rootCmd.SetContext(context.Background())
})

setup := func(t *testing.T, withConfig bool) (*bytes.Buffer, *bytes.Buffer) {
t.Helper()

Expand Down Expand Up @@ -213,6 +229,9 @@ func TestCheckNodeHealthCmd(t *testing.T) {
// Given a directory with no configuration
_, _ = setup(t, false)

// Reset context for fresh test
rootCmd.SetContext(context.Background())

// Setup command args
rootCmd.SetArgs([]string{"check", "node-health", "--nodes", "10.0.0.1"})

Expand Down
2 changes: 1 addition & 1 deletion cmd/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ var setContextCmd = &cobra.Command{
if err := runtime.NewRuntime(deps).
LoadShell().
LoadConfig().
SetContext(args[0]).
WriteResetToken().
SetContext(args[0]).
Do(); err != nil {
return fmt.Errorf("Error setting context: %w", err)
}
Expand Down
25 changes: 16 additions & 9 deletions cmd/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra"
"github.com/windsorcli/cli/pkg/di"
"github.com/windsorcli/cli/pkg/pipelines"
"github.com/windsorcli/cli/pkg/runtime"
)

var (
Expand All @@ -25,21 +26,27 @@ var downCmd = &cobra.Command{
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// First, run the env pipeline in quiet mode to set up environment variables
var envPipeline pipelines.Pipeline
envPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "envPipeline")
if err != nil {
return fmt.Errorf("failed to set up env pipeline: %w", err)
// First, set up environment variables using runtime
deps := &runtime.Dependencies{
Injector: injector,
}
envCtx := context.WithValue(cmd.Context(), "quiet", true)
envCtx = context.WithValue(envCtx, "decrypt", true)
if err := envPipeline.Execute(envCtx); err != nil {
if err := runtime.NewRuntime(deps).
LoadShell().
CheckTrustedDirectory().
LoadConfig().
LoadSecretsProviders().
LoadEnvVars(runtime.EnvVarsOptions{
Decrypt: true,
Verbose: verbose,
}).
ExecutePostEnvHook(verbose).
Do(); err != nil {
return fmt.Errorf("failed to set up environment: %w", err)
}

// Then, run the init pipeline to initialize the environment
var initPipeline pipelines.Pipeline
initPipeline, err = pipelines.WithPipeline(injector, cmd.Context(), "initPipeline")
initPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "initPipeline")
if err != nil {
return fmt.Errorf("failed to set up init pipeline: %w", err)
}
Expand Down
Loading
Loading