From 39b7b68160571b1ed6eeaff8ceb2177d11792684 Mon Sep 17 00:00:00 2001 From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:17:41 -0500 Subject: [PATCH] fix(stack): Repair terraform stack functionality Lazy loading stack components in the provisioner. Slight cleanup to remove unnecesary blueprint handler passed through provisioner to the stack. Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> --- pkg/provisioner/provisioner.go | 94 +++++++++------- pkg/provisioner/provisioner_test.go | 71 +++++++----- pkg/provisioner/terraform/stack.go | 72 ++++-------- pkg/provisioner/terraform/stack_test.go | 140 ++---------------------- 4 files changed, 127 insertions(+), 250 deletions(-) diff --git a/pkg/provisioner/provisioner.go b/pkg/provisioner/provisioner.go index 6c157cec4..1e6a50ab4 100644 --- a/pkg/provisioner/provisioner.go +++ b/pkg/provisioner/provisioner.go @@ -37,6 +37,7 @@ type Provisioner struct { KubernetesManager kubernetes.KubernetesManager KubernetesClient k8sclient.KubernetesClient ClusterClient cluster.ClusterClient + blueprintHandler blueprint.BlueprintHandler } // ============================================================================= @@ -44,9 +45,8 @@ type Provisioner struct { // ============================================================================= // NewProvisioner creates a new Provisioner instance with the provided runtime and blueprint handler. -// It sets up all required provisioner handlers—terraform stack, kubernetes manager, kubernetes client, -// and cluster client. The cluster client is created based on the cluster driver configuration (talos/omni). -// Components are initialized lazily when needed by the Up() and Down() methods. +// It sets up kubernetes manager and kubernetes client. Terraform stack and cluster client +// are initialized lazily when needed by the Up(), Down(), and WaitForHealth() methods. // Returns a pointer to the Provisioner struct. func NewProvisioner(rt *runtime.Runtime, blueprintHandler blueprint.BlueprintHandler, opts ...*Provisioner) *Provisioner { provisioner := &Provisioner{ @@ -77,19 +77,6 @@ func NewProvisioner(rt *runtime.Runtime, blueprintHandler blueprint.BlueprintHan provisioner.KubernetesManager = kubernetes.NewKubernetesManager(provisioner.KubernetesClient) } - if provisioner.TerraformStack == nil { - if rt.ConfigHandler != nil && rt.ConfigHandler.GetBool("terraform.enabled", false) { - provisioner.TerraformStack = terraforminfra.NewWindsorStack(rt, blueprintHandler) - } - } - - if provisioner.ClusterClient == nil { - clusterDriver := rt.ConfigHandler.GetString("cluster.driver", "") - if clusterDriver == "talos" || clusterDriver == "omni" { - provisioner.ClusterClient = cluster.NewTalosClusterClient() - } - } - return provisioner } @@ -107,7 +94,16 @@ func (i *Provisioner) Up(blueprint *blueprintv1alpha1.Blueprint) error { } if i.TerraformStack == nil { - return nil + if i.Runtime != nil && i.Runtime.ConfigHandler != nil { + terraformEnabled := i.Runtime.ConfigHandler.GetBool("terraform.enabled", false) + if terraformEnabled { + i.TerraformStack = terraforminfra.NewStack(i.Runtime) + } else { + return nil + } + } else { + return nil + } } if err := i.TerraformStack.Up(blueprint); err != nil { return fmt.Errorf("failed to run terraform up: %w", err) @@ -229,37 +225,51 @@ func (i *Provisioner) CheckNodeHealth(ctx context.Context, options NodeHealthChe return fmt.Errorf("no health checks specified. Use --nodes and/or --k8s-endpoint flags to specify health checks to perform") } - if hasNodeCheck && i.ClusterClient == nil && !hasK8sCheck { - return fmt.Errorf("no health checks specified. Use --nodes and/or --k8s-endpoint flags to specify health checks to perform") - } - - if hasNodeCheck && i.ClusterClient != nil { - defer i.ClusterClient.Close() + if hasNodeCheck { + if i.ClusterClient == nil { + if i.Runtime != nil && i.Runtime.ConfigHandler != nil { + clusterDriver := i.Runtime.ConfigHandler.GetString("cluster.driver", "") + if clusterDriver == "talos" || clusterDriver == "omni" { + i.ClusterClient = cluster.NewTalosClusterClient() + } + } + } - var checkCtx context.Context - var cancel context.CancelFunc - if options.Timeout > 0 { - checkCtx, cancel = context.WithTimeout(ctx, options.Timeout) - } else { - checkCtx, cancel = context.WithCancel(ctx) + if i.ClusterClient == nil { + if !hasK8sCheck { + return fmt.Errorf("no health checks specified. Use --nodes and/or --k8s-endpoint flags to specify health checks to perform") + } + // If we have k8s check, we can continue without cluster client } - defer cancel() - if err := i.ClusterClient.WaitForNodesHealthy(checkCtx, options.Nodes, options.Version); err != nil { - if hasK8sCheck { - if outputFunc != nil { - outputFunc(fmt.Sprintf("Warning: Cluster client failed (%v), continuing with Kubernetes checks\n", err)) - } + if i.ClusterClient != nil { + defer i.ClusterClient.Close() + + var checkCtx context.Context + var cancel context.CancelFunc + if options.Timeout > 0 { + checkCtx, cancel = context.WithTimeout(ctx, options.Timeout) } else { - return fmt.Errorf("nodes failed health check: %w", err) + checkCtx, cancel = context.WithCancel(ctx) } - } else { - if outputFunc != nil { - message := fmt.Sprintf("All %d nodes are healthy", len(options.Nodes)) - if options.Version != "" { - message += fmt.Sprintf(" and running version %s", options.Version) + defer cancel() + + if err := i.ClusterClient.WaitForNodesHealthy(checkCtx, options.Nodes, options.Version); err != nil { + if hasK8sCheck { + if outputFunc != nil { + outputFunc(fmt.Sprintf("Warning: Cluster client failed (%v), continuing with Kubernetes checks\n", err)) + } + } else { + return fmt.Errorf("nodes failed health check: %w", err) + } + } else { + if outputFunc != nil { + message := fmt.Sprintf("All %d nodes are healthy", len(options.Nodes)) + if options.Version != "" { + message += fmt.Sprintf(" and running version %s", options.Version) + } + outputFunc(message) } - outputFunc(message) } } } diff --git a/pkg/provisioner/provisioner_test.go b/pkg/provisioner/provisioner_test.go index ff48f2484..9ccbe0cfb 100644 --- a/pkg/provisioner/provisioner_test.go +++ b/pkg/provisioner/provisioner_test.go @@ -56,7 +56,7 @@ func createTestBlueprint() *blueprintv1alpha1.Blueprint { type ProvisionerTestMocks struct { ConfigHandler config.ConfigHandler Shell *shell.MockShell - TerraformStack *terraforminfra.MockStack + TerraformStack terraforminfra.Stack KubernetesManager *kubernetes.MockKubernetesManager KubernetesClient k8sclient.KubernetesClient ClusterClient *cluster.MockClusterClient @@ -99,7 +99,6 @@ func setupProvisionerMocks(t *testing.T, opts ...func(*ProvisionerTestMocks)) *P return "/test/project", nil } - terraformStack := terraforminfra.NewMockStack() kubernetesManager := kubernetes.NewMockKubernetesManager() kubernetesClient := k8sclient.NewMockKubernetesClient() clusterClient := cluster.NewMockClusterClient() @@ -114,6 +113,10 @@ func setupProvisionerMocks(t *testing.T, opts ...func(*ProvisionerTestMocks)) *P Shell: mockShell, } + terraformStack := terraforminfra.NewMockStack() + terraformStack.UpFunc = func(blueprint *blueprintv1alpha1.Blueprint) error { return nil } + terraformStack.DownFunc = func(blueprint *blueprintv1alpha1.Blueprint) error { return nil } + mocks := &ProvisionerTestMocks{ ConfigHandler: configHandler, Shell: mockShell, @@ -159,8 +162,8 @@ func TestNewProvisioner(t *testing.T) { t.Error("Expected config handler to be set") } - if provisioner.TerraformStack == nil { - t.Error("Expected terraform stack to be initialized") + if provisioner.TerraformStack != nil { + t.Error("Expected terraform stack to be nil (lazy loaded)") } if provisioner.KubernetesManager == nil { @@ -185,8 +188,8 @@ func TestNewProvisioner(t *testing.T) { provisioner := NewProvisioner(mocks.Runtime, mocks.BlueprintHandler) - if provisioner.ClusterClient == nil { - t.Error("Expected cluster client to be created for talos driver") + if provisioner.ClusterClient != nil { + t.Error("Expected cluster client to be nil (lazy loaded)") } }) @@ -203,8 +206,8 @@ func TestNewProvisioner(t *testing.T) { provisioner := NewProvisioner(mocks.Runtime, mocks.BlueprintHandler) - if provisioner.ClusterClient == nil { - t.Error("Expected cluster client to be created for omni driver") + if provisioner.ClusterClient != nil { + t.Error("Expected cluster client to be nil (lazy loaded)") } }) @@ -268,7 +271,7 @@ func TestNewProvisioner(t *testing.T) { provisioner := NewProvisioner(mocks.Runtime, mocks.BlueprintHandler) if provisioner.TerraformStack != nil { - t.Error("Expected terraform stack to be nil when terraform is disabled") + t.Error("Expected terraform stack to be nil (lazy loaded, and disabled in config)") } }) @@ -286,10 +289,6 @@ func TestProvisioner_Up(t *testing.T) { } provisioner := NewProvisioner(mocks.Runtime, mocks.BlueprintHandler, opts) - mocks.TerraformStack.UpFunc = func(blueprint *blueprintv1alpha1.Blueprint) error { - return nil - } - blueprint := createTestBlueprint() err := provisioner.Up(blueprint) @@ -315,8 +314,17 @@ func TestProvisioner_Up(t *testing.T) { t.Run("SuccessSkipsTerraformWhenDisabled", func(t *testing.T) { mocks := setupProvisionerMocks(t) + mockConfigHandler := mocks.ConfigHandler.(*config.MockConfigHandler) + mockConfigHandler.GetBoolFunc = func(key string, defaultValue ...bool) bool { + if key == "terraform.enabled" { + return false + } + if len(defaultValue) > 0 { + return defaultValue[0] + } + return false + } provisioner := NewProvisioner(mocks.Runtime, mocks.BlueprintHandler) - provisioner.TerraformStack = nil blueprint := createTestBlueprint() err := provisioner.Up(blueprint) @@ -328,20 +336,21 @@ func TestProvisioner_Up(t *testing.T) { t.Run("ErrorTerraformStackUp", func(t *testing.T) { mocks := setupProvisionerMocks(t) + mockStack := terraforminfra.NewMockStack() + mockStack.UpFunc = func(blueprint *blueprintv1alpha1.Blueprint) error { + return fmt.Errorf("terraform stack up failed") + } opts := &Provisioner{ - TerraformStack: mocks.TerraformStack, + TerraformStack: mockStack, } provisioner := NewProvisioner(mocks.Runtime, mocks.BlueprintHandler, opts) - mocks.TerraformStack.UpFunc = func(blueprint *blueprintv1alpha1.Blueprint) error { - return fmt.Errorf("up failed") - } - blueprint := createTestBlueprint() err := provisioner.Up(blueprint) if err == nil { t.Error("Expected error for terraform stack up failure") + return } if !strings.Contains(err.Error(), "failed to run terraform up") { @@ -359,10 +368,6 @@ func TestProvisioner_Down(t *testing.T) { } provisioner := NewProvisioner(mocks.Runtime, mocks.BlueprintHandler, opts) - mocks.TerraformStack.DownFunc = func(blueprint *blueprintv1alpha1.Blueprint) error { - return nil - } - blueprint := createTestBlueprint() err := provisioner.Down(blueprint) @@ -401,15 +406,15 @@ func TestProvisioner_Down(t *testing.T) { t.Run("ErrorTerraformStackDown", func(t *testing.T) { mocks := setupProvisionerMocks(t) + mockStack := terraforminfra.NewMockStack() + mockStack.DownFunc = func(blueprint *blueprintv1alpha1.Blueprint) error { + return fmt.Errorf("terraform stack down failed") + } opts := &Provisioner{ - TerraformStack: mocks.TerraformStack, + TerraformStack: mockStack, } provisioner := NewProvisioner(mocks.Runtime, mocks.BlueprintHandler, opts) - mocks.TerraformStack.DownFunc = func(blueprint *blueprintv1alpha1.Blueprint) error { - return fmt.Errorf("down failed") - } - blueprint := createTestBlueprint() err := provisioner.Down(blueprint) @@ -1343,6 +1348,16 @@ func TestProvisioner_CheckNodeHealth(t *testing.T) { t.Run("ErrorNoHealthChecksWhenNodesProvidedButNoClusterClient", func(t *testing.T) { mocks := setupProvisionerMocks(t) + mockConfigHandler := mocks.ConfigHandler.(*config.MockConfigHandler) + mockConfigHandler.GetStringFunc = func(key string, defaultValue ...string) string { + if key == "cluster.driver" { + return "" // No cluster driver set, so ClusterClient won't be created + } + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } provisioner := NewProvisioner(mocks.Runtime, mocks.BlueprintHandler) provisioner.ClusterClient = nil diff --git a/pkg/provisioner/terraform/stack.go b/pkg/provisioner/terraform/stack.go index ab377da2b..f7ef1a858 100644 --- a/pkg/provisioner/terraform/stack.go +++ b/pkg/provisioner/terraform/stack.go @@ -16,16 +16,16 @@ import ( "strings" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" - "github.com/windsorcli/cli/pkg/composer/blueprint" "github.com/windsorcli/cli/pkg/runtime" envvars "github.com/windsorcli/cli/pkg/runtime/env" ) // ============================================================================= -// Interfaces +// Interface // ============================================================================= -// Stack is an interface that represents a stack of components. +// Stack defines the interface for Terraform stack operations. +// Both the Stack struct and MockStack implement this interface. type Stack interface { Up(blueprint *blueprintv1alpha1.Blueprint) error Down(blueprint *blueprintv1alpha1.Blueprint) error @@ -35,16 +35,11 @@ type Stack interface { // Types // ============================================================================= -// BaseStack is a struct that implements the Stack interface. -type BaseStack struct { - runtime *runtime.Runtime - blueprintHandler blueprint.BlueprintHandler - shims *Shims -} - -// WindsorStack is a struct that implements the Stack interface. -type WindsorStack struct { - BaseStack +// TerraformStack manages Terraform infrastructure components by initializing and applying Terraform configurations. +// It processes components in order, generating terraform arguments, running Terraform init, plan, and apply operations. +type TerraformStack struct { + runtime *runtime.Runtime + shims *Shims terraformEnv *envvars.TerraformEnvPrinter } @@ -52,23 +47,11 @@ type WindsorStack struct { // Constructors // ============================================================================= -// NewBaseStack creates a new base stack of components. -func NewBaseStack(rt *runtime.Runtime, blueprintHandler blueprint.BlueprintHandler) *BaseStack { - return &BaseStack{ - runtime: rt, - blueprintHandler: blueprintHandler, - shims: NewShims(), - } -} - -// NewWindsorStack creates a new WindsorStack. -func NewWindsorStack(rt *runtime.Runtime, blueprintHandler blueprint.BlueprintHandler, opts ...*WindsorStack) *WindsorStack { - stack := &WindsorStack{ - BaseStack: BaseStack{ - runtime: rt, - blueprintHandler: blueprintHandler, - shims: NewShims(), - }, +// NewStack creates a new stack of components. +func NewStack(rt *runtime.Runtime, opts ...*TerraformStack) Stack { + stack := &TerraformStack{ + runtime: rt, + shims: NewShims(), } if len(opts) > 0 && opts[0] != nil { @@ -87,22 +70,12 @@ func NewWindsorStack(rt *runtime.Runtime, blueprintHandler blueprint.BlueprintHa return stack } -// Up creates a new stack of components. -func (s *BaseStack) Up(blueprint *blueprintv1alpha1.Blueprint) error { - return nil -} - -// Down destroys a stack of components. -func (s *BaseStack) Down(blueprint *blueprintv1alpha1.Blueprint) error { - return nil -} - // Up creates a new stack of components by initializing and applying Terraform configurations. // It processes components in order, generating terraform arguments, running Terraform init, // plan, and apply operations, and cleaning up backend override files. // The method ensures proper directory management and terraform argument setup for each component. // The blueprint parameter is required to resolve terraform components. -func (s *WindsorStack) Up(blueprint *blueprintv1alpha1.Blueprint) error { +func (s *TerraformStack) Up(blueprint *blueprintv1alpha1.Blueprint) error { if blueprint == nil { return fmt.Errorf("blueprint not provided") } @@ -196,7 +169,7 @@ func (s *WindsorStack) Up(blueprint *blueprintv1alpha1.Blueprint) error { // creates backend override files, runs Terraform refresh, plan (with destroy flag), and destroy commands, and removes backend override files. // Components with Destroy set to false are skipped. Directory state is restored after execution. Errors are returned on any operation failure. // The blueprint parameter is required to resolve terraform components. -func (s *WindsorStack) Down(blueprint *blueprintv1alpha1.Blueprint) error { +func (s *TerraformStack) Down(blueprint *blueprintv1alpha1.Blueprint) error { if blueprint == nil { return fmt.Errorf("blueprint not provided") } @@ -284,7 +257,7 @@ func (s *WindsorStack) Down(blueprint *blueprintv1alpha1.Blueprint) error { // ============================================================================= // resolveTerraformComponents resolves terraform components from the blueprint by resolving sources and paths. -func (s *WindsorStack) resolveTerraformComponents(blueprint *blueprintv1alpha1.Blueprint, projectRoot string) []blueprintv1alpha1.TerraformComponent { +func (s *TerraformStack) resolveTerraformComponents(blueprint *blueprintv1alpha1.Blueprint, projectRoot string) []blueprintv1alpha1.TerraformComponent { blueprintCopy := *blueprint s.resolveComponentSources(&blueprintCopy) s.resolveComponentPaths(&blueprintCopy, projectRoot) @@ -292,7 +265,7 @@ func (s *WindsorStack) resolveTerraformComponents(blueprint *blueprintv1alpha1.B } // resolveComponentSources resolves component source names to full URLs using blueprint sources. -func (s *WindsorStack) resolveComponentSources(blueprint *blueprintv1alpha1.Blueprint) { +func (s *TerraformStack) resolveComponentSources(blueprint *blueprintv1alpha1.Blueprint) { resolvedComponents := make([]blueprintv1alpha1.TerraformComponent, len(blueprint.TerraformComponents)) copy(resolvedComponents, blueprint.TerraformComponents) @@ -333,7 +306,7 @@ func (s *WindsorStack) resolveComponentSources(blueprint *blueprintv1alpha1.Blue } // resolveComponentPaths determines the full filesystem path for each Terraform component. -func (s *WindsorStack) resolveComponentPaths(blueprint *blueprintv1alpha1.Blueprint, projectRoot string) { +func (s *TerraformStack) resolveComponentPaths(blueprint *blueprintv1alpha1.Blueprint, projectRoot string) { resolvedComponents := make([]blueprintv1alpha1.TerraformComponent, len(blueprint.TerraformComponents)) copy(resolvedComponents, blueprint.TerraformComponents) @@ -355,7 +328,7 @@ func (s *WindsorStack) resolveComponentPaths(blueprint *blueprintv1alpha1.Bluepr } // isValidTerraformRemoteSource checks if the source is a valid Terraform module reference. -func (s *WindsorStack) isValidTerraformRemoteSource(source string) bool { +func (s *TerraformStack) isValidTerraformRemoteSource(source string) bool { patterns := []string{ `^git::https://[^/]+/.*\.git(?:@.*)?$`, `^git@[^:]+:.*\.git(?:@.*)?$`, @@ -380,7 +353,7 @@ func (s *WindsorStack) isValidTerraformRemoteSource(source string) bool { } // isOCISource returns true if the provided source is an OCI repository reference. -func (s *WindsorStack) isOCISource(sourceNameOrURL string, blueprint *blueprintv1alpha1.Blueprint) bool { +func (s *TerraformStack) isOCISource(sourceNameOrURL string, blueprint *blueprintv1alpha1.Blueprint) bool { if strings.HasPrefix(sourceNameOrURL, "oci://") { return true } @@ -396,7 +369,7 @@ func (s *WindsorStack) isOCISource(sourceNameOrURL string, blueprint *blueprintv } // getTerraformEnv returns the terraform environment printer, checking the runtime if not set on the stack. -func (s *WindsorStack) getTerraformEnv() *envvars.TerraformEnvPrinter { +func (s *TerraformStack) getTerraformEnv() *envvars.TerraformEnvPrinter { if s.terraformEnv != nil { return s.terraformEnv } @@ -408,6 +381,3 @@ func (s *WindsorStack) getTerraformEnv() *envvars.TerraformEnvPrinter { } return nil } - -// Ensure BaseStack implements Stack -var _ Stack = (*BaseStack)(nil) diff --git a/pkg/provisioner/terraform/stack_test.go b/pkg/provisioner/terraform/stack_test.go index 53dc77444..9c2ae174b 100644 --- a/pkg/provisioner/terraform/stack_test.go +++ b/pkg/provisioner/terraform/stack_test.go @@ -1,6 +1,6 @@ package terraform -// The StackTest provides comprehensive test coverage for the Stack interface implementation. +// The StackTest provides comprehensive test coverage for the Stack implementation. // It provides validation of stack initialization, component management, and infrastructure operations, // The StackTest ensures proper dependency injection and component lifecycle management, // verifying error handling, mock interactions, and infrastructure state management. @@ -187,7 +187,7 @@ contexts: } } -// setupWindsorStackMocks creates mock components for testing the WindsorStack +// setupWindsorStackMocks creates mock components for testing the Stack func setupWindsorStackMocks(t *testing.T, opts ...*SetupOptions) *TerraformTestMocks { t.Helper() mocks := setupTerraformMocks(t, opts...) @@ -220,133 +220,15 @@ func setupWindsorStackMocks(t *testing.T, opts ...*SetupOptions) *TerraformTestM // Test Public Methods // ============================================================================= -func TestStack_NewStack(t *testing.T) { - setup := func(t *testing.T) (*BaseStack, *TerraformTestMocks) { - t.Helper() - mocks := setupTerraformMocks(t) - stack := NewBaseStack(mocks.Runtime, mocks.BlueprintHandler) - stack.shims = mocks.Shims - return stack, mocks - } - - t.Run("Success", func(t *testing.T) { - stack, _ := setup(t) - - if stack == nil { - t.Errorf("Expected stack to be non-nil") - } - }) -} - -func TestStack_Initialize(t *testing.T) { - setup := func(t *testing.T) (*BaseStack, *TerraformTestMocks) { - t.Helper() - mocks := setupTerraformMocks(t) - stack := NewBaseStack(mocks.Runtime, mocks.BlueprintHandler) - stack.shims = mocks.Shims - return stack, mocks - } - - t.Run("Success", func(t *testing.T) { - stack, _ := setup(t) - - if stack == nil { - t.Error("Expected stack to be created") - } - }) -} - -func TestStack_Up(t *testing.T) { - setup := func(t *testing.T) (*BaseStack, *TerraformTestMocks) { - t.Helper() - mocks := setupTerraformMocks(t) - stack := NewBaseStack(mocks.Runtime, mocks.BlueprintHandler) - stack.shims = mocks.Shims - return stack, mocks - } - - t.Run("Success", func(t *testing.T) { - stack, _ := setup(t) - - blueprint := createTestBlueprint() - if err := stack.Up(blueprint); err != nil { - t.Errorf("Expected Up to return nil, got %v", err) - } - }) - - t.Run("UninitializedStack", func(t *testing.T) { - stack, _ := setup(t) - - blueprint := createTestBlueprint() - if err := stack.Up(blueprint); err != nil { - t.Errorf("Expected Up to return nil even without initialization, got %v", err) - } - }) - - t.Run("NilInjector", func(t *testing.T) { - mocks := setupTerraformMocks(t) - stack := NewBaseStack(mocks.Runtime, mocks.BlueprintHandler) - - blueprint := createTestBlueprint() - if err := stack.Up(blueprint); err != nil { - t.Errorf("Expected Up to return nil even with nil injector, got %v", err) - } - }) -} - -func TestStack_Down(t *testing.T) { - setup := func(t *testing.T) (*BaseStack, *TerraformTestMocks) { - t.Helper() - mocks := setupTerraformMocks(t) - stack := NewBaseStack(mocks.Runtime, mocks.BlueprintHandler) - stack.shims = mocks.Shims - return stack, mocks - } - - t.Run("Success", func(t *testing.T) { - stack, _ := setup(t) - - blueprint := createTestBlueprint() - if err := stack.Down(blueprint); err != nil { - t.Errorf("Expected Down to return nil, got %v", err) - } - }) - - t.Run("UninitializedStack", func(t *testing.T) { - stack, _ := setup(t) - - blueprint := createTestBlueprint() - if err := stack.Down(blueprint); err != nil { - t.Errorf("Expected Down to return nil even without initialization, got %v", err) - } - }) - - t.Run("NilInjector", func(t *testing.T) { - mocks := setupTerraformMocks(t) - stack := NewBaseStack(mocks.Runtime, mocks.BlueprintHandler) - - blueprint := createTestBlueprint() - if err := stack.Down(blueprint); err != nil { - t.Errorf("Expected Down to return nil even with nil injector, got %v", err) - } - }) -} - -func TestStack_Interface(t *testing.T) { - t.Run("BaseStackImplementsStack", func(t *testing.T) { - var _ Stack = (*BaseStack)(nil) - }) -} - // ============================================================================= // Test Public Methods // ============================================================================= -func TestWindsorStack_NewWindsorStack(t *testing.T) { - setup := func(t *testing.T) (*WindsorStack, *TerraformTestMocks) { +func TestStack_NewStack(t *testing.T) { + setup := func(t *testing.T) (*TerraformStack, *TerraformTestMocks) { t.Helper() mocks := setupWindsorStackMocks(t) - stack := NewWindsorStack(mocks.Runtime, mocks.BlueprintHandler) + stack := NewStack(mocks.Runtime).(*TerraformStack) return stack, mocks } @@ -359,11 +241,11 @@ func TestWindsorStack_NewWindsorStack(t *testing.T) { }) } -func TestWindsorStack_Up(t *testing.T) { - setup := func(t *testing.T) (*WindsorStack, *TerraformTestMocks) { +func TestStack_Up(t *testing.T) { + setup := func(t *testing.T) (*TerraformStack, *TerraformTestMocks) { t.Helper() mocks := setupWindsorStackMocks(t) - stack := NewWindsorStack(mocks.Runtime, mocks.BlueprintHandler) + stack := NewStack(mocks.Runtime).(*TerraformStack) stack.shims = mocks.Shims return stack, mocks } @@ -564,11 +446,11 @@ func TestWindsorStack_Up(t *testing.T) { }) } -func TestWindsorStack_Down(t *testing.T) { - setup := func(t *testing.T) (*WindsorStack, *TerraformTestMocks) { +func TestStack_Down(t *testing.T) { + setup := func(t *testing.T) (*TerraformStack, *TerraformTestMocks) { t.Helper() mocks := setupWindsorStackMocks(t) - stack := NewWindsorStack(mocks.Runtime, mocks.BlueprintHandler) + stack := NewStack(mocks.Runtime).(*TerraformStack) stack.shims = mocks.Shims mocks.Blueprint.GetTerraformComponentsFunc = func() []blueprintv1alpha1.TerraformComponent {